Commit in lcsim/src/org/lcsim/contrib/uiowa/mst on MAIN
ClusterNotEmptyDecisionMaker.java+33added 1.1
GeometricalDistance.java+36added 1.1
MSTClusterBuilder.java+139added 1.1
MSTClusterDriver.java+102added 1.1
Metrics.java+12added 1.1
MinimumHitToHitDistance.java+35added 1.1
+357
6 added files
Preliminary version of a clustering package that uses a Minimum Spanning Tree approach

lcsim/src/org/lcsim/contrib/uiowa/mst
ClusterNotEmptyDecisionMaker.java added at 1.1
diff -N ClusterNotEmptyDecisionMaker.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ClusterNotEmptyDecisionMaker.java	29 Sep 2005 21:03:47 -0000	1.1
@@ -0,0 +1,33 @@
+package mst; //package org.lcsim.recon.cluster.mst; 
+
+import util.decision.*; //import org.lcsim.util.decision.*;
+
+import java.util.List; 
+import org.lcsim.event.Cluster;
+
+/**
+  * A simple decision maker which always returns valid
+  * for clusters that have at least one hit.
+  */
+public class ClusterNotEmptyDecisionMaker implements DecisionMakerSingle<Cluster>, DecisionMakerPair<Cluster,Cluster> {
+
+    /**
+      * Simple constructor
+      */
+    public ClusterNotEmptyDecisionMaker() {}
+
+    /**
+      * Returns true provided the cluster has at least one hit
+      */
+    public boolean valid (Cluster clus){
+	return (clus.getCalorimeterHits().size() > 0);
+    }
+
+    /**
+      * Returns true provided each cluster has at least one hit
+      */
+    public boolean valid (Cluster clus1, Cluster clus2) {
+	return valid(clus1) && valid(clus2);
+    }
+
+}

lcsim/src/org/lcsim/contrib/uiowa/mst
GeometricalDistance.java added at 1.1
diff -N GeometricalDistance.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ GeometricalDistance.java	29 Sep 2005 21:03:47 -0000	1.1
@@ -0,0 +1,36 @@
+package mst; //package org.lcsim.recon.cluster.mst; 
+
+import java.io.*; 
+import java.util.*; 
+
+import org.lcsim.event.Cluster;
+
+/**
+  * A distance metric that computes the 3D distance
+  * between two clusters.
+  */
+public class GeometricalDistance implements Metrics {
+
+    /**
+      * Simple constructor
+      */
+    public GeometricalDistance(){}
+
+    /**
+      * Compute the 3D distance between two clusters. Each cluster's
+      * position is taken as Cluster.getPosition(), which is usually
+      * the center of energy. The method then returns the distance
+      * between the two position points.
+      */
+    public double getDistance (Cluster cluster1, Cluster cluster2){
+        double[] coOne = cluster1.getPosition();
+        double[] coTwo = cluster2.getPosition();
+
+        double distance = Math.sqrt((coOne[0]-coTwo[0])*(coOne[0]-coTwo[0])
+                                  + (coOne[1]-coTwo[1])*(coOne[1]-coTwo[1])
+                                  + (coOne[2]-coTwo[2])*(coOne[2]-coTwo[2]));
+
+        return distance;
+    }
+
+}

lcsim/src/org/lcsim/contrib/uiowa/mst
MSTClusterBuilder.java added at 1.1
diff -N MSTClusterBuilder.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ MSTClusterBuilder.java	29 Sep 2005 21:03:47 -0000	1.1
@@ -0,0 +1,139 @@
+package mst; //package org.lcsim.recon.cluster.mst; 
+
+import util.decision.*; //import org.lcsim.util.decision.*;
+
+import java.io.*; 
+import java.util.*; 
+
+import org.lcsim.event.EventHeader;
+import org.lcsim.event.Cluster;
+import org.lcsim.recon.cluster.util.BasicCluster;
+
+/**
+  * A class to build clusters using the Minimum Spanning Tree (MST)
+  * algorithm.
+  *
+  * NB! Why are a bunch of things private instead of protected?
+  * If I want to extend this class, shouldn't I be allowed to know
+  * what the threshold is?
+  */
+public class MSTClusterBuilder {
+
+    /**
+      * Constructor.
+      *  @param list The objects that will be clustered. If these are hits, they must be pre-wrapped into Cluster format.
+      */
+    public MSTClusterBuilder (List<Cluster> list, EventHeader event){
+	inputList = list;
+        thisEvent = event;
+    }
+
+    /**
+      * Main clustering routine. Any two objects where metric(object1,object2)<threshold
+      * will be placed in the same output cluster (though the converse is not necessarily true).
+      * In addition, three decision makers are checked for validity (seedDecision, pairDecision, outputDecision).
+      */
+    public List<Cluster> doClustering(){
+	List<Cluster> outputList = new ArrayList<Cluster>();
+	usedList.clear();
+
+	if (inputList == null ) throw new AssertionError ("empty inputList");
+
+	if (theMetrics == null ) throw new AssertionError ("no metrics");
+
+	if ( !thresholdSet ) throw new AssertionError ("no threshold");
+
+	Iterator iter = inputList.iterator();
+	while (iter.hasNext()){
+	    Cluster inOne = (Cluster) iter.next();
+	    if (!usedList.contains(inOne) && seedDecision.valid(inOne)){
+		BasicCluster out = new BasicCluster();
+		recursiveClusterer(inOne, out);
+		if (outputDecision.valid(out)) {
+		    outputList.add(out);
+		}
+	    }
+	}
+	return outputList;
+    }
+
+    /**
+      * An internal routine for clustering
+      */
+    private void recursiveClusterer(Cluster inOne, BasicCluster out){
+	out.addCluster(inOne);
+	usedList.add(inOne);
+	Iterator iter = inputList.iterator();
+	while (iter.hasNext()){
+	    Cluster inTwo = (Cluster) iter.next();
+	    if (!usedList.contains(inTwo)){
+		if (pairDecision.valid(inOne, inTwo)){
+		    double d = theMetrics.getDistance(inOne, inTwo);
+		    if (d < threshold){
+			recursiveClusterer(inTwo, out);
+		    }
+		}
+	    }
+	}
+	return;
+    }
+
+    private List<Cluster> inputList;                //  was protected
+    private Collection<Cluster> usedList = new HashSet<Cluster>();    //  was protected
+
+    private Metrics theMetrics = null;
+    private double threshold;
+    private boolean thresholdSet = false;
+    private DecisionMakerSingle<Cluster> seedDecision = new ClusterNotEmptyDecisionMaker();
+    private DecisionMakerPair<Cluster,Cluster> pairDecision = new ClusterNotEmptyDecisionMaker();
+    private DecisionMakerSingle<Cluster> outputDecision = new ClusterNotEmptyDecisionMaker();
+
+    protected EventHeader thisEvent = null;
+
+    /**
+      * Specify the metric to be used. This must be done
+      * before doClustering() can be called.
+      */
+    public void registerMetrics(Metrics m){
+	theMetrics = m;
+    }
+
+    /**
+      * Specify the threshold to be used. This must be done
+      * before doClustering() can be called.
+      */
+    public void setThreshold(double t){
+	threshold = t;
+	thresholdSet = true;
+    }
+
+    /**
+      * Specify the seed decision. For an object to be the first
+      * (or only) object in a cluster, it must pass the seed decision.
+      * Objects do not pass the seed decision will either be picked up
+      * in a cluster as it spreads, or will not appear in any cluster.
+      */
+    public void setSeedDecision(DecisionMakerSingle<Cluster> d){
+	seedDecision = d;
+    }
+
+    /**
+      * Specify the pair decision. To link two objects, the pair decision
+      * must be valid AND metric(object1,object2)<threshold. If either
+      * condition fails there will not be a direct link, though it is still
+      * possible for both to end up in the same cluster (e.g. if both are
+      * successfully linked to a third object).
+      */
+    public void setPairDecision(DecisionMakerPair<Cluster,Cluster> d){
+	pairDecision = d;
+    }
+
+    /**
+      * Specify the output decision. For a cluster to be written out to the
+      * output list, this decision must be valid.
+      */
+    public void setOutputDecision(DecisionMakerSingle<Cluster> d){
+	outputDecision = d;
+    }
+
+}

lcsim/src/org/lcsim/contrib/uiowa/mst
MSTClusterDriver.java added at 1.1
diff -N MSTClusterDriver.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ MSTClusterDriver.java	29 Sep 2005 21:03:47 -0000	1.1
@@ -0,0 +1,102 @@
+package mst; //package org.lcsim.recon.cluster.mst; 
+
+import util.*; //import org.lcsim.recon.cluster.util.*;
+import util.decision.*; //import org.lcsim.util.decision.*;
+
+import java.io.*; 
+import java.util.*; 
+
+import org.lcsim.util.Driver;
+import org.lcsim.event.EventHeader;
+import org.lcsim.event.Cluster;
+import org.lcsim.event.CalorimeterHit;
+
+public class MSTClusterDriver extends Driver {
+
+    private double threshold = 3.0;
+    private String calType = "EMCal";
+    private String userTag;
+    private String clusterName = "MSTCluster "+calType;
+    private Metrics metrics = new GeometricalDistance();
+    private boolean useEndcap = false;
+    private DecisionMakerSingle inputDecision = new ClusterNotEmptyDecisionMaker();
+    private DecisionMakerSingle seedDecision = new ClusterNotEmptyDecisionMaker();
+    private DecisionMakerPair pairDecision = new ClusterNotEmptyDecisionMaker();
+    private DecisionMakerSingle outputDecision = new ClusterNotEmptyDecisionMaker();
+    private List<String> listToGet;
+
+    public MSTClusterDriver(String inString){
+        calType = inString;
+        listToGet = new ArrayList();
+        if ( calType == "EMCal" || calType == "all") {
+            listToGet.add("EcalBarrHits");
+	    if (useEndcap) listToGet.add("EcalEndcapHits");
+        } else if ( calType == "HCal" || calType == "all") {
+	    listToGet.add("HcalBarrHits");
+            if (useEndcap) listToGet .add("HcalEndcapHits");
+        } else if ( calType == "User" ) {
+        } else {
+            throw new AssertionError("Calorimeter type '"+calType+"' not recognized.");
+        }
+    }
+
+    public void process(EventHeader event){
+        List<Cluster> inputList = new ArrayList<Cluster> ();
+
+        for (String s : listToGet) {
+          ObjectToClusterWrapper wrap = new BasicObjectToClusterWrapper();
+          List l = event.get(Object.class, s);
+          try {
+            List<Cluster> wrappedList = wrap.wrapListOfObjects(l);
+            inputList.addAll(wrappedList);
+          } catch (UnwrappableObjectException x) {
+            // We fed it an  object it didn't understand (not a Cluster or CalorimeterHit)
+            throw new AssertionError(x);
+          }
+	}
+        ListFilter<Cluster> filter = new ListFilter<Cluster> (inputDecision);
+	List<Cluster> list = filter.filterList(inputList);
+
+        MSTClusterBuilder clusterBuilder = new MSTClusterBuilder( list, event );
+        clusterBuilder.registerMetrics(metrics);
+        clusterBuilder.setThreshold(threshold);
+        clusterBuilder.setSeedDecision(seedDecision);
+        clusterBuilder.setPairDecision(pairDecision);
+        clusterBuilder.setOutputDecision(outputDecision);
+        List<Cluster> outputList = clusterBuilder.doClustering();
+        event.put(clusterName, outputList );
+    }
+
+    public void setThreshold(double value){
+        threshold = value;
+        System.out.println("Threshold: "+threshold);
+    }
+
+    public void setClusterName(String string){
+        clusterName = string;
+        System.out.println("ClusterName: "+clusterName);
+    }
+
+    public void registerMetrics(Metrics inMetrics){
+        metrics = inMetrics;
+    }
+
+    public void setInputDecision(DecisionMakerSingle decision){
+        inputDecision = decision;}
+
+    public void setSeedDecision(DecisionMakerSingle decision){
+        seedDecision = decision;}
+
+    public void setPairDecision(DecisionMakerPair decision){
+        pairDecision = decision;}
+
+    public void setOutputDecision(DecisionMakerSingle decision){
+        outputDecision = decision;}
+
+    public void addUserInputList(String tag){
+        listToGet.add(tag);
+    }
+    public void resetUserInputList(){listToGet.clear();}
+
+    public void useEndcap(boolean use){useEndcap = use;}
+}

lcsim/src/org/lcsim/contrib/uiowa/mst
Metrics.java added at 1.1
diff -N Metrics.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Metrics.java	29 Sep 2005 21:03:48 -0000	1.1
@@ -0,0 +1,12 @@
+package mst; //package org.lcsim.recon.cluster.mst; 
+
+import java.io.*; 
+import java.util.*; 
+
+import org.lcsim.event.Cluster;
+
+public interface Metrics {
+
+    public double getDistance(Cluster c1, Cluster c2);
+
+}

lcsim/src/org/lcsim/contrib/uiowa/mst
MinimumHitToHitDistance.java added at 1.1
diff -N MinimumHitToHitDistance.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ MinimumHitToHitDistance.java	29 Sep 2005 21:03:48 -0000	1.1
@@ -0,0 +1,35 @@
+package mst; //package org.lcsim.recon.cluster.mst; 
+
+import java.util.List; 
+import org.lcsim.event.CalorimeterHit;
+import org.lcsim.event.Cluster;
+import hep.physics.vec.BasicHep3Vector;
+import hep.physics.vec.Hep3Vector;
+import hep.physics.vec.VecOp;
+
+import org.lcsim.event.Cluster;
+
+public class MinimumHitToHitDistance implements Metrics {
+
+    public MinimumHitToHitDistance(){}
+
+    public double getDistance (Cluster cluster1, Cluster cluster2){
+      // get min hit-hit distance
+      double minDist = Double.NaN;
+      boolean firstCheck = true;
+      for (CalorimeterHit hit1 : cluster1.getCalorimeterHits()) {
+        for (CalorimeterHit hit2 : cluster2.getCalorimeterHits()) {
+          Hep3Vector vect1 = new BasicHep3Vector(hit1.getPosition());
+          Hep3Vector vect2 = new BasicHep3Vector(hit2.getPosition());
+          Hep3Vector displacement = VecOp.sub(vect1, vect2);
+          double dist = displacement.magnitude();
+          if (firstCheck || dist<minDist) {
+            minDist = dist;
+            firstCheck = false;
+          }
+        }
+      }
+      return minDist;
+    }
+
+}
CVSspam 0.2.8