001package com.astrolabsoftware.FinkBrowser.Januser;
002
003import com.Lomikel.HBaser.HBaseClient;
004import com.Lomikel.Utils.LomikelException;
005import com.astrolabsoftware.FinkBrowser.FinkPortalClient.FPC;
006import com.astrolabsoftware.FinkBrowser.HBaser.Clusteriser.ClusterFinder;
007
008// org.json
009import org.json.JSONArray;
010import org.json.JSONObject;
011
012// Java
013import java.util.Arrays;
014import java.util.Set;
015import java.util.TreeSet;
016import java.util.Map;
017import java.util.TreeMap;
018import java.util.List;
019import java.util.ArrayList;
020import java.io.IOException;
021
022// Log4J
023import org.apache.logging.log4j.Logger;
024import org.apache.logging.log4j.LogManager;
025
026/** <code>FeaturesClassifier</code> classifies sources according to
027  * HBase <tt>lc_features_*</tt> field.
028  * @opt attributes
029  * @opt operations
030  * @opt types
031  * @opt visibility
032  * @author <a href="mailto:Julius.Hrivnac@cern.ch">J.Hrivnac</a> */
033// BUG: jd should be String or long
034public class LightCurvesClassifier extends Classifier {
035  
036  @Override
037  public void classify(FinkGremlinRecipies recipies,
038                       String              oid) throws LomikelException {
039    String jd;
040    String cl;
041    Map<String, String> value;
042    String[] featuresS;
043    double[] featuresD;
044    String fg;
045    String fr;
046    Map<String, Set<String>> allInstances; // cl -> [jd]
047    Map<String, Double>      allWeights;   // jd -> w
048    Set<String> jds;
049    String key;
050    Map<String, Map<String, String>> alerts = recipies.fhclient().scan(null,
051                                                                       "key:key:" + oid + ":prefix",
052                                                                       "i:jd,d:lc_features_g,d:lc_features_r",
053                                                                       0,
054                                                                       0,
055                                                                       false,
056                                                                       false);
057    allInstances = new TreeMap<>();
058    allWeights   = new TreeMap<>();
059    // get all alerts (jd) and their classses
060    boolean isClassified = false;
061    for (Map.Entry<String, Map<String, String>> entry : alerts.entrySet()) {
062      value = entry.getValue();
063      jd = value.get("i:jd");
064      if (value.containsKey("d:lc_features_g") &&
065          value.containsKey("d:lc_features_r")) {
066        fg = value.get("d:lc_features_g").replaceFirst("\\[", "").replaceAll("]$", "");
067        fr = value.get("d:lc_features_r").replaceFirst("\\[", "").replaceAll("]$", "");
068        // BUG: some models replace by mean
069        featuresS = (fg + "," + fr).replaceAll("null", "0.0").
070                                    replaceAll("NaN",  "0.0").
071                                    split(",");
072        featuresD = Arrays.stream(featuresS).
073                           mapToDouble(Double::parseDouble).
074                           toArray();
075        cl = String.valueOf(finder().transformAndPredict(featuresD));                  
076        if (!cl.equals("-1")) {
077          if (allInstances.containsKey(cl)) {
078            jds = allInstances.get(cl);
079            jds.add(jd);
080            }
081          else {
082            jds = new TreeSet<String>();
083            jds.add(jd);
084            allInstances.put(cl, jds);
085            }
086          allWeights.put(cl, 1.0);
087          }
088        isClassified = true;
089        }
090      else {
091        //log.warn("Alert " + entry.getKey() + " has no features");
092        }
093      }
094    // rearrange instances and weights and register
095    double weight;
096    double totalWeight;
097    double w;
098    totalWeight = 0;
099    List<String> instancesL;
100    List<Double> weightsL;
101    for (Map.Entry<String, Set<String>> cls : allInstances.entrySet()) {
102      for (String instance : cls.getValue()) {
103        totalWeight += allWeights.get(instance);
104        }
105      }
106    for (Map.Entry<String, Set<String>> cls : allInstances.entrySet()) {
107      key = "FC-" + cls.getKey();
108      instancesL = new ArrayList<String>(cls.getValue());
109      weightsL   = new ArrayList<Double>();
110      w = 0;
111      for (String instance : instancesL) {
112        weightsL.add(allWeights.get(instance));
113        w += allWeights.get(instance);
114        }
115      weight = w / totalWeight;
116      recipies.registerSoI(this, key, oid, weight, instancesL, weightsL);
117      }
118    if (!isClassified) {
119      log.warn("Source " + oid + " cannot be classified because his alerts have no LC features");
120      }
121    }
122    
123  /** Give {@link ClusterFinder} to current database. Singleton.
124    * @return The corresponding {@link ClusterFinder}. 
125    * @throws LomikelExceltion If {@link ClusterFinder} cannot be created. */
126  private ClusterFinder finder() throws LomikelException {
127    if (_finder == null) {
128      if (_dirName == null) {
129        _dirName = "/tmp";
130        }
131      try {
132        _finder = new ClusterFinder(_dirName + "/scaler_params.json",
133                                    _dirName + "/pca_params.json",
134                                    _dirName + "/cluster_centers.json");
135        }
136      catch (IOException e) {
137        throw new LomikelException("Cannot create Cluster Finder", e);
138        }
139      }
140    return _finder;
141    }
142    
143  /** Set the directory for model json files
144    * <tt>scaler_params.json, pca_params.json, cluster_centers.json</tt>.
145    * If not set, <tt>/tmp</tt> will be used.
146    * @param dirName The directory for model json files. */
147  public void setModelDirectory(String dirName) {
148    _dirName = dirName;
149    }
150  
151  private static ClusterFinder _finder;
152  
153  private static String _dirName;
154
155  /** Logging . */
156  private static Logger log = LogManager.getLogger(LightCurvesClassifier.class);
157  
158  }
159           
160