package org.chesmapper.view.cluster; import java.awt.Color; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Set; import java.util.Vector; import javax.vecmath.Vector3f; import org.chesmapper.map.alg.embed3d.CorrelationProperty; import org.chesmapper.map.alg.embed3d.EqualPositionProperty; import org.chesmapper.map.alg.embed3d.Random3DEmbedder; import org.chesmapper.map.appdomain.AppDomainComputer; import org.chesmapper.map.data.ClusteringData; import org.chesmapper.map.data.CompoundDataImpl; import org.chesmapper.map.dataInterface.ClusterData; import org.chesmapper.map.dataInterface.CompoundData; import org.chesmapper.map.dataInterface.CompoundProperty; import org.chesmapper.map.dataInterface.CompoundPropertyOwner; import org.chesmapper.map.dataInterface.CompoundPropertyUtil; import org.chesmapper.map.dataInterface.DefaultNumericProperty; import org.chesmapper.map.dataInterface.NominalProperty; import org.chesmapper.map.dataInterface.NumericProperty; import org.chesmapper.map.dataInterface.SubstructureSmartsType; import org.chesmapper.map.main.Settings; import org.chesmapper.map.main.TaskProvider; import org.chesmapper.map.weka.Predictor; import org.chesmapper.map.weka.Predictor.PredictionResult; import org.chesmapper.view.gui.View; import org.chesmapper.view.gui.Zoomable; import org.mg.javalib.gui.CheckBoxSelectDialog; import org.mg.javalib.util.ArrayUtil; import org.mg.javalib.util.CountedSet; import org.mg.javalib.util.ListUtil; import org.mg.javalib.util.SelectionModel; import org.mg.javalib.util.Vector3fUtil; import org.mg.javalib.util.VectorUtil; public class ClusteringImpl implements Zoomable, Clustering { private Vector<Cluster> clusters; private ClusteringData clusteringData; SelectionModel clusterActive; SelectionModel clusterWatched; SelectionModel compoundActive; SelectionModel compoundWatched; boolean suppresAddEvent = false; Vector<PropertyChangeListener> listeners; public static final String CLUSTER_ADDED = "cluster_added"; public static final String CLUSTER_REMOVED = "cluster_removed"; public static final String CLUSTER_MODIFIED = "cluster_modified"; public static final String CLUSTER_NEW = "cluster_new"; public static final String CLUSTER_CLEAR = "cluster_clear"; public static final String PROPERTY_ADDED = "property_added"; boolean dirty = true; int numCompounds = -1; private boolean superimposed = false; float superimposedDiameter; float nonSuperimposedDiameter; BitSet bitSetAll; HashMap<Cluster, Integer> clusterIndices; HashMap<Integer, Compound> jmolCompoundIndexToCompound; HashMap<Integer, Cluster> jmolCompoundIndexToCluster; HashMap<CompoundProperty, Integer> numMissingValues; HashMap<CompoundProperty, Integer> numDistinctValues; ClusteringValues clusteringValues = new ClusteringValues(this); List<Compound> filteredCompoundList; List<Compound> filteredCompoundListIncludingMultiClusteredCompounds; Vector3f superimposedCenter; Vector3f nonSuperimposedCenter; Boolean endpointDataset; Boolean filledEndpointDataset; CompoundProperty highlightProperty; Color highlightColorText; JitteringProvider jittering; public ClusteringImpl() { listeners = new Vector<PropertyChangeListener>(); init(); } public void init() { clusterActive = new SelectionModel(); clusterWatched = new SelectionModel(); compoundActive = new SelectionModel(true); compoundWatched = new SelectionModel(true); clusters = new Vector<Cluster>(); numMissingValues = new HashMap<CompoundProperty, Integer>(); numDistinctValues = new HashMap<CompoundProperty, Integer>(); } public void addListener(PropertyChangeListener l) { listeners.add(l); } public void addListenerFirst(PropertyChangeListener l) { listeners.insertElementAt(l, 0); } public void fire(String event, Object oldValue, Object newValue) { if (!suppresAddEvent) for (PropertyChangeListener l : listeners) l.propertyChange(new PropertyChangeEvent(this, event, oldValue, newValue)); } private Cluster addSingleCluster(ClusterData clusterData) { Cluster c = new Cluster(clusterData); @SuppressWarnings("unchecked") Vector<Cluster> old = (Vector<Cluster>) VectorUtil.clone(ClusteringImpl.this.clusters); clusters.add(c); dirty = true; fire(CLUSTER_ADDED, old, clusters); return c; } public synchronized void update() { if (!dirty) return; numCompounds = 0; for (Cluster c : clusters) numCompounds += c.getNumCompounds(); if (View.instance != null) // for export without graphics { bitSetAll = new BitSet(); for (Cluster c : clusters) bitSetAll.or(c.getBitSet()); } int count = 0; clusterIndices = new HashMap<Cluster, Integer>(); jmolCompoundIndexToCluster = new HashMap<Integer, Cluster>(); jmolCompoundIndexToCompound = new HashMap<Integer, Compound>(); for (Cluster c : clusters) { clusterIndices.put(c, count++); for (Compound m : c.getCompounds()) { if (jmolCompoundIndexToCompound.get(m.getJmolIndex()) != null) throw new Error("duplicate compund index! " + m.getJmolIndex()); jmolCompoundIndexToCompound.put(m.getJmolIndex(), m); jmolCompoundIndexToCluster.put(m.getJmolIndex(), c); } } filteredCompoundListIncludingMultiClusteredCompounds = new ArrayList<Compound>(); for (Cluster c : clusters) for (Compound mm : c.getCompounds()) filteredCompoundListIncludingMultiClusteredCompounds.add(mm); filteredCompoundList = new ArrayList<Compound>(); HashSet<Integer> compoundIndex = new HashSet<Integer>(); for (Cluster c : clusters) for (Compound mm : c.getCompounds()) { if (!compoundIndex.contains(mm.getOrigIndex())) { filteredCompoundList.add(mm); compoundIndex.add(mm.getOrigIndex()); } } numMissingValues.clear(); numDistinctValues.clear(); clusteringValues.clear(); compoundSelections.clear(); endpointDataset = null; filledEndpointDataset = null; dirty = false; } public boolean isClusterActive() { return clusterActive.getSelected() != -1; } public boolean isClusterWatched() { return clusterWatched.getSelected() != -1; } public boolean isCompoundWatched() { return compoundWatched.getSelected() != -1; } public boolean isCompoundActiveFromCluster(int cluster) { int sel[] = compoundActive.getSelectedIndices(); if (sel.length == 0) return false; for (int compoundJmolIndex : sel) if (getClusterIndexForJmolIndex(compoundJmolIndex) == cluster) return true; return false; } public boolean isCompoundWatchedFromCluster(int cluster) { int sel[] = compoundWatched.getSelectedIndices(); if (sel.length == 0) return false; for (int compoundJmolIndex : sel) if (getClusterIndexForJmolIndex(compoundJmolIndex) == cluster) return true; return false; } public SelectionModel getClusterActive() { return clusterActive; } public SelectionModel getClusterWatched() { return clusterWatched; } public SelectionModel getCompoundActive() { return compoundActive; } public SelectionModel getCompoundWatched() { return compoundWatched; } public Cluster getClusterForCompound(Compound compound) { update(); return jmolCompoundIndexToCluster.get(compound.getJmolIndex()); } public Cluster getClusterForJmolIndex(int jmolIndex) { update(); return jmolCompoundIndexToCluster.get(jmolIndex); } public int indexOf(Cluster cluster) { update(); return clusterIndices.get(cluster); } public int getClusterIndexForCompound(Compound compound) { return indexOf(getClusterForCompound(compound)); } public int getClusterIndexForJmolIndex(int jmolIndex) { return indexOf(getClusterForJmolIndex(jmolIndex)); } public Compound getCompoundWithJmolIndex(int jmolIndex) { update(); return jmolCompoundIndexToCompound.get(jmolIndex); } public Compound[] getCompoundsWithJmolIndices(int[] idx) { Compound c[] = new Compound[idx.length]; for (int i = 0; i < c.length; i++) c[i] = getCompoundWithJmolIndex(idx[i]); return c; } public int[] getJmolIndicesWithCompounds(Compound[] c) { int idx[] = new int[c.length]; for (int i = 0; i < c.length; i++) idx[i] = c[i].getJmolIndex(); return idx; } public synchronized int numMissingValues(CompoundProperty p) { update(); if (!numMissingValues.containsKey(p)) { int num = 0; for (Cluster c : clusters) num += c.numMissingValues(p); numMissingValues.put(p, num); } return numMissingValues.get(p); } public synchronized int numDistinctValues(CompoundProperty p) { update(); if (!numDistinctValues.containsKey(p)) { int numDistinct; if (p instanceof NumericProperty) numDistinct = CompoundPropertyUtil.computeNumDistinct(getDoubleValues((NumericProperty) p)); else numDistinct = CompoundPropertyUtil.computeNumDistinct(getStringValues((NominalProperty) p, null)); numDistinctValues.put(p, numDistinct); } return numDistinctValues.get(p); } @Override public int getNumCompounds() { return numCompounds(); } public int numCompounds() { update(); return numCompounds; } @Override public CountedSet<String> getNominalSummary(NominalProperty p) { return clusteringValues.getNominalSummary(p); } public int numClusters() { return clusters.size(); } public BitSet getBitSetAll() { update(); return bitSetAll; } public synchronized void clear() { checkNoSelection(); @SuppressWarnings("unchecked") final Vector<Cluster> old = (Vector<Cluster>) VectorUtil.clone(ClusteringImpl.this.clusters); clusters.removeAllElements(); clusteringData = null; View.instance.zap(true, true, true); filteredCompoundList.clear(); filteredCompoundListIncludingMultiClusteredCompounds.clear(); clusteringValues.clear(); compoundSelections.clear(); dirty = true; jittering = null; getClusterActive().clearSelection(); fire(CLUSTER_CLEAR, old, clusters); } public void removeCluster(final Cluster... clusters) { checkNoSelection(); @SuppressWarnings("unchecked") Vector<Cluster> old = (Vector<Cluster>) VectorUtil.clone(ClusteringImpl.this.clusters); for (Cluster c : clusters) { View.instance.hide(c.getBitSet()); ClusteringImpl.this.clusters.remove(c); } dirty = true; updatePositions(); if (getNumClusters() == 1) getClusterActive().setSelected(0); fire(CLUSTER_REMOVED, old, clusters); } public Cluster getCluster(int clusterIndex) { if (clusterIndex < 0) return null; return clusters.get(clusterIndex); } public Vector<Cluster> getClusters() { return clusters; } @SuppressWarnings("unchecked") public void newClustering(ClusteringData d) { // @SuppressWarnings("unchecked") Vector<Cluster> old = (Vector<Cluster>) VectorUtil.clone(ClusteringImpl.this.clusters); suppresAddEvent = true; clusteringData = d; { String filename = d.getSDF(); TaskProvider.debug("Load dataset into Jmol"); if (View.instance != null) // for export without graphics { View.instance.loadCompoundFromFile(null, filename, null, null, false, null, null, 0); if (d.getNumCompounds(true) != View.instance.getCompoundCount()) throw new Error("illegal num compounds, loaded by Jmol: " + View.instance.getCompoundCount() + " != from wizard: " + d.getNumCompounds(true)); } } for (int i = 0; i < d.getNumClusters(); i++) addSingleCluster(d.getCluster(i)); { //data sanity checks List<Integer> jmol = new ArrayList<Integer>(); List<Integer> orig = new ArrayList<Integer>(); for (int i = 0; i < d.getNumClusters(); i++) { jmol = ListUtil.concat(jmol, d.getCluster(i).getCompoundClusterIndices()); orig = ListUtil.concat(orig, d.getCluster(i).getCompoundOrigIndices()); } int num_mult = clusteringData.getNumCompounds(true); int num_uniq = clusteringData.getNumCompounds(false); if (jmol.size() != orig.size()) throw new IllegalStateException("internal error, num compounds #1 " + jmol.size() + ", " + orig.size()); if (jmol.size() != num_mult) throw new IllegalStateException("internal error, num compounds #2 " + jmol.size() + ", " + num_mult); if (isClusterAlgorithmDisjoint()) { if (num_mult != num_uniq) throw new IllegalStateException("internal error, num compounds #3 " + num_mult + ", " + num_uniq); } else { if (num_mult <= num_uniq) throw new IllegalStateException("internal error, num compounds #4 " + num_mult + ", " + num_uniq); orig = ListUtil.uniqValue(orig); if (orig.size() != num_uniq) throw new IllegalStateException("internal error, num compounds #5 " + orig.size() + ", " + num_uniq); jmol = ListUtil.uniqValue(jmol); if (jmol.size() != num_mult) throw new IllegalStateException("internal error, num compounds #6 " + jmol.size() + ", " + num_mult); } Collections.sort(jmol); Collections.sort(orig); int i; for (i = 0; i < num_uniq; i++) if (i != jmol.get(i) || i != orig.get(i)) throw new IllegalStateException("internal error, num compounds #7 " + i + ", " + jmol.get(i) + ", " + orig.get(i)); if (!isClusterAlgorithmDisjoint()) for (; i < num_mult; i++) if (i != jmol.get(i)) throw new IllegalStateException("internal error, num compounds #8 " + i + ", " + jmol.get(i)); } update(); if (View.instance != null) // for export without graphics { TaskProvider.update(90, "Loading graphics"); updatePositions(); } suppresAddEvent = false; fire(CLUSTER_ADDED, old, clusters); fire(CLUSTER_NEW, old, clusters); if (View.instance != null) // for export without graphics View.instance.scriptWait("hover off"); } public synchronized void updatePositions() { update(); ClusteringUtil.updateScaleFactor(this); // SwingUtil.invokeAndWait(new Runnable() // { // public void run() // { getClusterWatched().clearSelection(); getCompoundWatched().clearSelection(); // } // }); View.instance.suspendAnimation("updating clustering positions"); for (Cluster c : clusters) { if (!superimposed) setClusterOverlap(c, true, null); c.updatePositions(); if (!superimposed) setClusterOverlap(c, false, null); } View.instance.proceedAnimation("updating clustering positions"); Vector3f[] positions = ArrayUtil.toArray(ClusteringUtil.getClusterPositions(this)); superimposedCenter = Vector3fUtil.centerConvexHull(positions); // take only cluster points into account (ignore cluster sizes) superimposedDiameter = Vector3fUtil.maxDist(positions); // needed for very small distances / only one cluster : diameter should be at least as big as cluster diameter for (Cluster c : clusters) superimposedDiameter = Math.max(superimposedDiameter, c.getDiameter(true)); // take only compound positions into account (ignore compound sizes) positions = ClusteringUtil.getCompoundPositions(this); nonSuperimposedCenter = Vector3fUtil.centerConvexHull(positions); nonSuperimposedDiameter = Vector3fUtil.maxDist(positions); //if all is filtered apart from compounds that share a single position nonSuperimposeDiameter would be 0 nonSuperimposedDiameter = Math.max(nonSuperimposedDiameter, superimposedDiameter); } /** * toggles compound positions between compound position (overlap=false) and cluster position (overlap=true) * * @param clusters * @param overlap * @param anim */ public void setClusterOverlap(List<Cluster> clusters, boolean overlap, View.AnimationSpeed anim) { List<Vector3f> compoundPositions = new ArrayList<Vector3f>(); List<BitSet> bitsets = new ArrayList<BitSet>(); for (Cluster cluster : clusters) { if (cluster.isSuperimposed() != overlap) { for (int i = 0; i < cluster.getNumCompounds(); i++) { bitsets.add(cluster.getCompound(i).getBitSet()); // destination is compound position Vector3f pos = cluster.getCompound(i).getPosition(); // compound is already at cluster position, sub to get relative vector pos.sub(cluster.getCenter(true)); if (overlap) pos.scale(-1); compoundPositions.add(pos); } } cluster.setSuperimposed(overlap); } View.instance.setAtomCoordRelative(compoundPositions, bitsets, anim); } /** * not animated * * @param comp * @param enable */ public void moveForDotMode(Compound comp, boolean enable) { // move to compound center ... Vector3f pos = new Vector3f(comp.origCenter); // ... from single atom position pos.sub(comp.origDotPosition); // reverse if neccessary if (!enable) pos.scale(-1); View.instance.setAtomCoordRelative(pos, comp.getDotModeDisplayBitSet()); } public void setClusterOverlap(Cluster cluster, boolean overlap, View.AnimationSpeed anim) { List<Cluster> l = new ArrayList<Cluster>(); l.add(cluster); setClusterOverlap(l, overlap, anim); } public int[] clusterChooser(String title, String description) { int clusterIndex = getClusterActive().getSelected(); if (clusterIndex == -1) clusterIndex = getClusterWatched().getSelected(); Cluster c[] = new Cluster[numClusters()]; for (int i = 0; i < c.length; i++) c[i] = getCluster(i); boolean b[] = new boolean[numClusters()]; if (clusterIndex != -1) b[clusterIndex] = true; return CheckBoxSelectDialog.selectIndices(Settings.TOP_LEVEL_FRAME, title, description, c, b); } public int[] selectJmolIndicesWithCompoundChooser(String title, String description) { int clusterIndex = getClusterActive().getSelected(); if (clusterIndex == -1) clusterIndex = getClusterWatched().getSelected(); List<Compound> l = new ArrayList<Compound>(); List<Boolean> lb = new ArrayList<Boolean>(); for (int i = 0; i < numClusters(); i++) { Cluster c = getCluster(i); for (int j = 0; j < c.getNumCompounds(); j++) { l.add(c.getCompound(j)); lb.add(clusterIndex == -1 || clusterIndex == i); } } Compound m[] = new Compound[l.size()]; int selectedIndices[] = CheckBoxSelectDialog.selectIndices(Settings.TOP_LEVEL_FRAME, title, description, l.toArray(m), ArrayUtil.toPrimitiveBooleanArray(lb)); return selectedIndices; } public void chooseClustersToExport(CompoundProperty compoundDescProp) { int[] indices = clusterChooser("Export Cluster/s", "Select the clusters you want to export. The compounds will be stored in a single SDF/CSV file."); if (indices != null) ExportData.exportClusters(this, indices, compoundDescProp); } public void chooseCompoundsToExport(CompoundProperty compoundDescProp) { int indices[] = selectJmolIndicesWithCompoundChooser("Export Compounds/s", "Select the compounds you want to export. The compounds will be stored in a single SDF/CSV file."); if (indices == null) return; List<Integer> l = new ArrayList<Integer>(); for (int i = 0; i < indices.length; i++) l.add(getCompoundWithJmolIndex(indices[i]).getOrigIndex()); ExportData.exportCompoundsWithOrigIndices(this, l, compoundDescProp); } public String getName() { if (clusteringData != null) return clusteringData.getName(); else return null; } public String getFullName() { if (clusteringData != null) return clusteringData.getFullName(); else return null; } public String getOrigSDFile() { return clusteringData.getOrigSDF(); } public String getSDFile() { return clusteringData.getSDF(); } private void checkNoSelection() { if (compoundActive.getSelected() != -1 || compoundWatched.getSelected() != -1 || clusterWatched.getSelected() != -1 || (clusterActive.getSelected() != -1 && getNumClusters() > 1)) throw new IllegalStateException("clear selection before"); } public void removeCompoundsWithJmolIndices(int jmolIndices[]) { checkNoSelection(); LinkedHashMap<Cluster, List<Integer>> toDel = new LinkedHashMap<Cluster, List<Integer>>(); // assign indices to clusters for (int i = 0; i < jmolIndices.length; i++) { Cluster c = getCluster(getClusterIndexForJmolIndex(jmolIndices[i])); List<Integer> l = toDel.get(c); if (l == null) { l = new ArrayList<Integer>(); toDel.put(c, l); } l.add(jmolIndices[i]); } // delete clusterwise boolean clusterModified = false; Cluster clusToDel[] = new Cluster[0]; for (Cluster c : toDel.keySet()) { int indices[] = ArrayUtil.toPrimitiveIntArray(toDel.get(c)); if (indices.length == c.getNumCompounds()) clusToDel = ArrayUtil.concat(Cluster.class, clusToDel, new Cluster[] { c }); else { c.removeWithJmolIndices(indices); dirty = true; clusterModified = true; } } if (clusToDel.length > 0) removeCluster(clusToDel); if (clusterModified) { updatePositions(); fire(CLUSTER_MODIFIED, null, null); } } public SubstructureSmartsType getSubstructureSmartsType() { return clusteringData.getThreeDAligner().getSubstructureSmartsType(); } public List<CompoundProperty> getFeatures() { return clusteringData.getFeatures(); } public List<CompoundProperty> getProperties() { return clusteringData.getProperties(); } public List<CompoundData> getCompounds() { return clusteringData.getCompounds(); } public int getNumClusters() { return clusters.size(); } public int getNumCompounds(boolean includingMultiClusteredCompounds) { return getCompounds(includingMultiClusteredCompounds).size(); } @Override public int getNumUnfilteredCompounds(boolean includingMultiClusteredCompounds) { return clusteringData.getNumCompounds(includingMultiClusteredCompounds); } public List<Compound> getCompounds(boolean includingMultiClusteredCompounds) { update(); if (includingMultiClusteredCompounds) return filteredCompoundListIncludingMultiClusteredCompounds; else return filteredCompoundList; } public String[] getStringValues(NominalProperty property, Compound excludeCompound) { return getStringValues(property, excludeCompound, false); } public String[] getStringValues(NominalProperty property, Compound excludeCompound, boolean formatted) { List<String> l = new ArrayList<String>(); for (Compound m : getCompounds(false)) if (m != excludeCompound && m.getStringValue(property) != null) l.add(formatted ? m.getFormattedValue(property) : m.getStringValue(property)); String v[] = new String[l.size()]; return l.toArray(v); } public Double[] getDoubleValues(NumericProperty property) { Double v[] = new Double[getNumCompounds(false)]; int i = 0; for (Compound m : getCompounds(false)) v[i++] = m.getDoubleValue(property); return v; } public String getClusterAlgorithm() { return clusteringData.getDatasetClusterer().getName(); } public boolean isClusterAlgorithmDisjoint() { return clusteringData.getDatasetClusterer().isDisjointClusterer(); } public String getEmbedAlgorithm() { return clusteringData.getThreeDEmbedder().getName(); } public String getEmbedQuality() { return clusteringData.getEmbedQuality(); } @Override public boolean isRandomEmbedding() { return clusteringData != null && clusteringData.getThreeDEmbedder() instanceof Random3DEmbedder; } @Override public Vector3f getCenter(boolean superimposed) { if (superimposed) return superimposedCenter; else return nonSuperimposedCenter; } @Override public float getDiameter(boolean superimposed) { if (superimposed) return superimposedDiameter; else return nonSuperimposedDiameter; } @Override public boolean isSuperimposed() { return superimposed; } public void setSuperimposed(boolean superimposed) { this.superimposed = superimposed; } public synchronized Double getNormalizedDoubleValue(CompoundPropertyOwner m, NumericProperty p) { return clusteringValues.getNormalizedDoubleValue(m, p); } public synchronized Double getNormalizedLogDoubleValue(CompoundPropertyOwner m, NumericProperty p) { return clusteringValues.getNormalizedLogDoubleValue(m, p); } @Override public Double getDoubleValue(NumericProperty p) { return clusteringValues.getDoubleValue(p); } @Override public String getFormattedValue(CompoundProperty p) { return getSummaryStringValue(p, false); } public String getOrigLocalPath() { return clusteringData.getOrigLocalPath(); } @Override public List<CompoundProperty> getAdditionalProperties() { return clusteringData.getAdditionalProperties(); } public CorrelationProperty getEmbeddingQualityProperty() { return clusteringData.getEmbeddingQualityProperty(); } @Override public EqualPositionProperty getEqualPosProperty() { return clusteringData.getEqualPosProperty(); } // public CompoundProperty[] getAppDomainProperties() // { // return clusteringData.getAppDomainProperties(); // } // // public List<CompoundProperty> getDistanceToProperties() // { // return clusteringData.getDistanceToProperties(); // } @Override public double getSpecificity(CompoundSelection c, CompoundProperty p) { return clusteringValues.getSpecificity(c, p); } @Override public double getSpecificity(Cluster c, CompoundProperty p) { return clusteringValues.getSpecificity(c, p); } public double getSpecificity(Compound m, CompoundProperty p) { return clusteringValues.getSpecificity(m, p); } public String getSummaryStringValue(CompoundProperty p, boolean html) { return clusteringValues.getSummaryStringValue(p, html); } public void initFeatureNormalization() { clusteringValues.initFeatureNormalization(); } public Cluster getUniqueClusterForCompounds(Compound[] c) { Cluster c1 = null; for (int i = 0; i < c.length; i++) { Cluster c2 = getClusterForCompound(c[i]); if (c1 == null || c1 == c2) c1 = c2; else return null; } return c1; } public Cluster getUniqueClusterForJmolIndices(int[] c) { Cluster c1 = null; for (int i = 0; i < c.length; i++) { Cluster c2 = getClusterForJmolIndex(c[i]); if (c1 == null || c1 == c2) c1 = c2; else return null; } return c1; } public Cluster getExactClusterForCompounds(List<Compound> compounds) { int idx[] = new int[compounds.size()]; for (int i = 0; i < idx.length; i++) idx[i] = compounds.get(i).getJmolIndex(); return getExactClusterForJmolIndices(idx); } public Cluster getExactClusterForJmolIndices(int[] jmolIndices) { Cluster c1 = null; for (int i = 0; i < jmolIndices.length; i++) { Cluster c2 = getClusterForJmolIndex(jmolIndices[i]); if (c1 == null || c1 == c2) c1 = c2; else return null; } if (c1.getNumCompounds() == jmolIndices.length) return c1; else return null; } /** * warning: requires to update compound positions after compounds are made visible again * * @param filter */ public void setCompoundFilter(CompoundFilter filter) { for (Cluster c : clusters) c.setFilter(filter); clusteringValues.clear(); dirty = true; // updatePositions(); } public int getNumOrigCompounds(boolean includingMultiClusteredCompounds) { return clusteringData.getNumCompounds(includingMultiClusteredCompounds); } @Override public Cluster getActiveCluster() { return getCluster(getClusterActive().getSelected()); } @Override public int getActiveClusterIdx() { return getClusterActive().getSelected(); } @Override public Cluster getWatchedCluster() { return getCluster(getClusterWatched().getSelected()); } @Override public int getWatchedClusterIdx() { return getClusterWatched().getSelected(); } @Override public boolean isCompoundActive() { return getCompoundActive().getSelected() != -1; } @Override public boolean isCompoundActive(Compound c) { return getCompoundActive().isSelected(c.getJmolIndex()); } @Override public Compound[] getActiveCompounds() { return getCompoundsWithJmolIndices(getCompoundActive().getSelectedIndices()); } @Override public Compound getActiveCompound() { return getCompoundWithJmolIndex(getCompoundActive().getSelected()); } @Override public int[] getActiveCompoundsJmolIdx() { return getCompoundActive().getSelectedIndices(); } @Override public Compound getWatchedCompound() { return getCompoundWithJmolIndex(getCompoundWatched().getSelected()); } @Override public Compound[] getWatchedCompounds() { return getCompoundsWithJmolIndices(getCompoundWatched().getSelectedIndices()); } @Override public int[] getWatchedCompoundsJmolIdx() { return getCompoundWatched().getSelectedIndices(); } @Override public List<CompoundProperty> getPropertiesAndFeatures() { return ListUtil.concat(getProperties(), getFeatures()); } @Override public List<CompoundProperty> selectPropertiesAndFeaturesWithDialog(String title, CompoundProperty preselected, boolean addSmiles, boolean addEmbeddingStress, boolean addActivityCliffs, boolean addDistanceTo) { List<CompoundProperty> props = new ArrayList<CompoundProperty>(); for (CompoundProperty p : getAdditionalProperties()) { if (addEmbeddingStress && p instanceof CorrelationProperty) props.add(p); else if (addActivityCliffs && p instanceof SALIProperty) props.add(p); else if (addDistanceTo && p instanceof DistanceToProperty) props.add(p); } for (CompoundProperty p : getProperties()) if (addSmiles || (p.getCompoundPropertySet() == null || !p.getCompoundPropertySet().isSmiles())) props.add(p); for (CompoundProperty p : getFeatures()) props.add(p); if (props.size() == 0) return props; else { boolean selection[] = new boolean[props.size()]; if (preselected == null) Arrays.fill(selection, true); else { for (int i = 0; i < props.size(); i++) if (preselected == props.get(i)) selection[i] = true; } Object sel[] = CheckBoxSelectDialog.select(Settings.TOP_LEVEL_FRAME, title, null, ArrayUtil.toArray(CompoundProperty.class, props), selection); if (sel == null) return null; return ArrayUtil.toList(ArrayUtil.cast(CompoundProperty.class, sel)); } } @Override public void addSelectionListener(final SelectionListener l) { getClusterActive().addListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { l.clusterActiveChanged(getCluster(getClusterActive().getSelected())); } }); getClusterWatched().addListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { l.clusterWatchedChanged(getCluster(getClusterWatched().getSelected())); } }); getCompoundActive().addListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { l.compoundActiveChanged(getCompoundsWithJmolIndices(getCompoundActive().getSelectedIndices())); } }); getCompoundWatched().addListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { l.compoundWatchedChanged(getCompoundsWithJmolIndices(getCompoundWatched().getSelectedIndices())); } }); } // hack for BMBF datasets @Override public boolean isBMBFRealEndpointDataset(boolean filtered) { if (endpointDataset == null) { Set<String> realProps = new HashSet<String>(); Set<String> filledProps = new HashSet<String>(); Set<String> otherProps = new HashSet<String>(); for (CompoundProperty p : getProperties()) { String n = p.getName(); if (n.endsWith("_real")) realProps.add(n.substring(0, n.length() - 5)); else if (n.endsWith("_filled")) filledProps.add(n.substring(0, n.length() - 7)); else otherProps.add(n); } endpointDataset = realProps.size() > 0; filledEndpointDataset = filledProps.size() > 0 && filledProps.size() == realProps.size(); for (String n : realProps) { if (!otherProps.contains(n)) { endpointDataset = false; filledEndpointDataset = false; break; } if (!filledProps.contains(n)) filledEndpointDataset = false; } } if (filtered) return filledEndpointDataset; else return endpointDataset; } @Override public CompoundProperty addSALIFeatures(CompoundProperty p) { CompoundProperty oldProps[] = new CompoundProperty[0]; for (CompoundProperty prop : getAdditionalProperties()) if (prop instanceof SALIProperty) oldProps = ArrayUtil.push(CompoundProperty.class, oldProps, prop); Double d[] = new Double[getNumCompounds(true)]; String domain[] = null; if (p instanceof NominalProperty) { domain = ((NominalProperty) p).getDomain(); if (domain.length != 2) throw new IllegalArgumentException("not yet implemented"); } for (int i = 0; i < getNumCompounds(false); i++) { Compound c = null; for (Compound comp : getCompounds(false)) { if (comp.getOrigIndex() == i) { c = comp; break; } } if (p instanceof NumericProperty) { if (c.getDoubleValue((NumericProperty) p) != null) d[i] = getNormalizedDoubleValue(c, (NumericProperty) p); } else { if (c.getStringValue((NominalProperty) p) != null) d[i] = (double) ArrayUtil.indexOf(domain, c.getStringValue((NominalProperty) p)); } } List<SALIProperty> l = SALIProperty.create(d, clusteringData.getFeatureDistanceMatrix().getValues(), p.toString()); addNewAdditionalProperties(ListUtil.toArray(l), oldProps); return l.get(0); } @Override public Double getFeatureDistance(int origIndex, int origIndex2) { return clusteringData.getFeatureDistance(origIndex, origIndex2); } @Override public CompoundProperty addDistanceToCompoundFeature(Compound comp) { for (CompoundProperty prop : getAdditionalProperties()) { if (prop instanceof DistanceToProperty && ((DistanceToProperty) prop).getCompound() == comp) return prop; } Double d[] = new Double[getNumUnfilteredCompounds(true)]; for (int i = 0; i < d.length; i++) d[i] = clusteringData.getFeatureDistance(comp.getOrigIndex(), getCompounds().get(i).getOrigIndex()); DistanceToProperty dp = new DistanceToProperty(comp, clusteringData.getEmbeddingDistanceMeasure(), d); addNewAdditionalProperty(dp, null); return dp; } HashMap<NumericProperty, NumericProperty> logProps = new HashMap<NumericProperty, NumericProperty>(); public static class LogProperty extends DefaultNumericProperty { public LogProperty(NumericProperty p) { super("Log(" + p.getName() + ")", "Log conversion (to base 10) of " + p.getName(), ArrayUtil.log(p .getDoubleValues())); } } @Override public NumericProperty addLogFeature(NumericProperty p) { if (!logProps.containsKey(p)) { NumericProperty l = new LogProperty(p); addNewAdditionalProperty(l, null); logProps.put(p, l); } return logProps.get(p); } CompoundProperty pred; CompoundProperty predMis; @Override public void predict() { CompoundProperty clazz = null; boolean classification = true; for (CompoundProperty p : getProperties()) { if (p.toString().matches("(?i).*activity.*")) clazz = p; else if (p.toString().equals("LC50_mmol_log")) { clazz = p; classification = false; } } if (clazz == null) throw new Error(); addPredictionFeature(clazz, Predictor.predict(getCompounds(), getFeatures(), clazz, classification)); } @Override public void computeAppDomain() { List<NumericProperty> feats = new ArrayList<NumericProperty>(); for (CompoundProperty p : getFeatures()) if (p instanceof NumericProperty && p.numMissingValues() == 0 && p.numDistinctValues() >= 2) feats.add((NumericProperty) p); //AppDomainComputer appDomain[] = new AppDomainComputer[] { AppDomainHelper.select() }; List<CompoundProperty> props = new ArrayList<CompoundProperty>(); AppDomainComputer appDomain[] = AppDomainComputer.APP_DOMAIN_COMPUTERS; if (appDomain != null && appDomain.length > 0) { for (AppDomainComputer ad : appDomain) { ad.computeAppDomain(getCompounds(), feats, clusteringData.getFeatureDistanceMatrix().getValues()); props.add(ad.getInsideAppDomainProperty()); props.add(ad.getPropabilityAppDomainProperty()); } } addNewAdditionalProperties(ListUtil.toArray(CompoundProperty.class, props), null); } @Override public void addPredictionFeature(CompoundProperty clazz, PredictionResult p) { CompoundProperty pred = p.createFeature(); addNewAdditionalProperty(pred, this.pred); this.pred = pred; CompoundProperty predMis = p.createMissclassifiedFeature(); addNewAdditionalProperty(predMis, this.predMis); this.predMis = predMis; } private void addNewAdditionalProperty(CompoundProperty p, CompoundProperty old) { addNewAdditionalProperties(new CompoundProperty[] { p }, new CompoundProperty[] { old }); } private void addNewAdditionalProperties(CompoundProperty props[], CompoundProperty oldProps[]) { for (CompoundProperty p : props) { int i = 0; for (CompoundData cc : getCompounds()) { CompoundDataImpl c = (CompoundDataImpl) cc; if (p instanceof NumericProperty) { c.setDoubleValue(p, ((NumericProperty) p).getDoubleValues()[i]); c.setNormalizedValueCompleteDataset(p, ((NumericProperty) p).getNormalizedValues()[i]); } else if (p instanceof NominalProperty) { c.setStringValue(p, ((NominalProperty) p).getStringValues()[i]); } i++; } } if (oldProps != null) for (CompoundProperty old : oldProps) clusteringData.getAdditionalProperties().remove(old); for (CompoundProperty p : props) clusteringData.getAdditionalProperties().add(p); dirty = true; if (View.instance != null) // for export fire(PROPERTY_ADDED, true, false); } private HashMap<String, CompoundSelection> compoundSelections = new HashMap<String, CompoundSelection>(); public CompoundSelection getCompoundSelection(Compound[] c) { StringBuffer key = new StringBuffer(); for (Compound compound : c) key.append(compound.getJmolIndex()); String k = key.toString(); if (!compoundSelections.containsKey(k)) { CompoundSelection sel = new CompoundSelection(c); clusteringValues.initSelectionNormalization(sel); compoundSelections.put(k, sel); } return compoundSelections.get(k); } @Override public CompoundProperty getHighlightProperty() { return highlightProperty; } public void setHighlighProperty(CompoundProperty prop, Color highlightColorText) { this.highlightProperty = prop; this.highlightColorText = highlightColorText; } @Override public Color getHighlightColorText() { return highlightColorText; } @Override public boolean isSkippingRedundantFeatures() { return clusteringData.isSkippingRedundantFeatures(); } @Override public boolean isBigDataMode() { return Settings.BIG_DATA; } public void updateJittering(int level, Set<Compound> compounds) { if (jittering == null) jittering = new JitteringProvider(this); jittering.updateJittering(level, compounds); } public int getJitteringResetLevel(Set<Compound> compounds) { if (jittering == null) return -1; return jittering.getJitteringResetLevel(compounds); } @Override public boolean doCheSMappingWarningsExist() { return clusteringData != null && clusteringData.doCheSMappingWarningsExist(); } @Override public void showCheSMappingWarnings() { clusteringData.showCheSMappingWarnings(); } }