After a brief introduction to AIDA, its purpose and its philosophy, we present examples showing how to use each major component of the AIDA toolkit.
AIDA is a standard set of interfaces for creating and manipulating histograms, n-tuples and related data analysis objects. It has been created cooperatively by a group of developers working on high-energy physics data analysis tools. The goal of the AIDA project is to provide the user with a powerful set of interfaces which can be used regardless of which analysis tool they are using. The advantages of this approach are that:
Currently two versions of the AIDA interfaces exist, one for Java and one for C++. The two interfaces are as identical as the underlying languages will permit.
We have been careful to avoid using a "lowest common denominator" approach in the development of the AIDA interfaces. Rather than limiting ourselves by the existing capabilities of the underlying analysis tools, we have designed a rich set of interfaces, with the expectation that the tool developers will need to add new features to support the full AIDA functionality. This does mean however that the early implementations of AIDA may not completely implement all features.
Before going into details, here is a trivial example program that uses the AIDA interfaces to create a 1D histogram, and display it.
import hep.aida.*; import java.util.Random; public class Histogram { public static void main(String[] argv) { IAnalysisFactory af = IAnalysisFactory.create(); IHistogramFactory hf = af.createHistogramFactory(af.createTreeFactory().create()); IHistogram1D h1d = hf.createHistogram1D("test 1d",50,-3,3); IHistogram2D h2d = hf.createHistogram2D("test 2d",50,-3,3,50,-3,3); Random r = new Random(); for (int i=0; i<10000; i++) { h1d.fill(r.nextGaussian()); h2d.fill(r.nextGaussian(),r.nextGaussian()); } IPlotter plotter = af.createPlotterFactory().create("Plot"); plotter.createRegions(1,2,0); plotter.region(0).plot(h1d); plotter.region(1).plot(h2d); plotter.show(); } }
A few things to notice in this example, first note that AIDA objects are always created from factories. Although different implementations of AIDA may return different objects from their factories, the returned objects will all implement the same standard AIDA interfaces. In this way it is possible to change from one AIDA implementation to another just by changing which IAnalysisFactory is used.
The rest of this document describes the key features that the AIDA user will need to know about:
In AIDA instead of creating objects directly using "new" one uses factories. There is one "master" factory, IAnalysisFactory from which other factories are obtained. The IAnalysisFactory allows you to obtain factories for creating trees (ITreeFactory), histograms, clouds and profile histograms (IHistogramFactory), tuples (ITupleFactory) etc.
You will see many examples of using factories in the remainder of this guide.
AIDA supports 1D, 2D and 3D histograms. Creating histograms is quite straightforward using the methods in IHistogramFactory.
Histograms support arithmetic operations, in particular add, subtract, multiply and divide. In all cases the operation is applied bin-by-bin, and the histograms involved in the operation must have the same binning. The input histograms are unchanged, and a new histogram is created as the result of the operation. All of the arithmetic operations are methods of IHistogramFactory to reflect the fact that a new histogram is always created as a result of the operation. For multi-dimensional histograms it is also possible to create slices and projections.
The following example illustrates using histograms:
import hep.aida.*; import java.util.Random; public class HistogramArithmetic { public static void main(String[] argv) { IAnalysisFactory af = IAnalysisFactory.create(); IHistogramFactory hf = af.createHistogramFactory(af.createTreeFactory().create()); IHistogram1D h1 = hf.createHistogram1D("test 1d",50,-3,6); IHistogram1D h2 = hf.createHistogram1D("test 2d",50,-3,6); Random r = new Random(); for (int i=0; i<10000; i++) { h1.fill(r.nextGaussian()); h2.fill(3+r.nextGaussian()); } IHistogram1D plus = hf.add("h1+h2",h1,h2); IHistogram1D minus = hf.subtract("h1-h2",h1,h2); IHistogram1D mul = hf.multiply("h1*h2",h1,h2); IHistogram1D div = hf.divide("h1 over h2",h1,h2); IPlotter plotter = af.createPlotterFactory().create("Plot"); plotter.createRegions(2,2,0); plotter.region(0).plot(plus); plotter.region(1).plot(minus); plotter.region(2).plot(mul); plotter.region(3).plot(div); plotter.show(); } }
AIDA supports 1D and 2D profiles. IProfiles are created throught the IHistogramFactory that also supports arithmetic operations like sum, subtraction, multiplication, division and weighted mean. As for IHistograms, the IProfiles involved in the arithmetic operations must have the same binning and will be unchanged by the operations itself.
As for IHistograms it is possible to create IProfiles with fixed or variable bin width; the example below shows how to create them.
import hep.aida.*; public class ProfileCreate { public static void main(String[] argv) { IAnalysisFactory af = IAnalysisFactory.create(); ITree tree = af.createTreeFactory().create(); IHistogramFactory hf = af.createHistogramFactory(tree); // Create 1D and 2D IProfile with fixed bin width IProfile1D prof1DFixedBinWidth = hf.createProfile1D("prof1DFixedBinWidth","Fixed bin width 1D", 10, 0, 1); IProfile2D prof2DFixedBinWidth = hf.createProfile2D("prof2DFixedBinWidth","Fixed bin width 2D", 10, 0, 1, 10, -5, 5); double[] xBinEdges = {0,0.1,0.21,0.35,0.48,0.52,0.65,0.75,0.83,0.94,1.0}; double[] yBinEdges = {-5.0,-4.1,-3.2,-2.0,-1.1,-0.4,1.2,2.3,3.5,4.2,5.0}; // Create 1D and 2D IProfile with variable bin width IProfile1D prof1DVariableBinWidth = hf.createProfile1D("prof1DVariableBinWidth", "Variable bin width 1D", xBinEdges); IProfile2D prof2DVariableBinWidth = hf.createProfile2D("prof2DVariableBinWidth", "Variable bin width 2D", xBinEdges, yBinEdges); } }
To fill an IProfile invoke the fill method. In the example below we fill the IProfiles created in the previous example.
import hep.aida.*; import java.util.*; public class ProfileFill { public static void main(String[] argv) { IAnalysisFactory af = IAnalysisFactory.create(); ITree tree = af.createTreeFactory().create(); IHistogramFactory hf = af.createHistogramFactory(tree); // Create 1D and 2D IProfile with fixed bin width IProfile1D prof1DFixedBinWidth = hf.createProfile1D("prof1DFixedBinWidth","Fixed bin width 1D", 10, 0, 1); IProfile2D prof2DFixedBinWidth = hf.createProfile2D("prof2DFixedBinWidth","Fixed bin width 2D", 10, 0, 1, 10, -5, 5); double[] xBinEdges = {0,0.1,0.21,0.35,0.48,0.52,0.65,0.75,0.83,0.94,1.0}; double[] yBinEdges = {-5.0,-4.1,-3.2,-2.0,-1.1,-0.4,1.2,2.3,3.5,4.2,5.0}; // Create 1D and 2D IProfile with variable bin width IProfile1D prof1DVariableBinWidth = hf.createProfile1D("prof1DVariableBinWidth", "Variable bin width 1D", xBinEdges); IProfile2D prof2DVariableBinWidth = hf.createProfile2D("prof2DVariableBinWidth", "Variable bin width 2D", xBinEdges, yBinEdges); Random r = new Random(); for ( int i = 0; i < 100; i++ ) { double x = r.nextDouble(); double y = x + 0.1*r.nextGaussian(); // Fill the IProfiles with default weight. prof1DFixedBinWidth.fill(x,y); prof2DFixedBinWidth.fill(x,y, r.nextDouble()); prof1DVariableBinWidth.fill(x,y); prof2DVariableBinWidth.fill(x,y, r.nextDouble()); } } }
In the example below we show how to fit and display an IProfile.
import hep.aida.*; import java.util.*; public class ProfileFitAndPlot { public static void main(String[] argv) { IAnalysisFactory af = IAnalysisFactory.create(); ITree tree = af.createTreeFactory().create(); IHistogramFactory hf = af.createHistogramFactory(tree); // Create 1D and 2D IProfile with fixed bin width IProfile1D prof1DFixedBinWidth = hf.createProfile1D("prof1DFixedBinWidth","Fixed bin width 1D", 10, 0, 1); IProfile2D prof2DFixedBinWidth = hf.createProfile2D("prof2DFixedBinWidth","Fixed bin width 2D", 10, 0, 1, 10, -5, 5); double[] xBinEdges = {0,0.1,0.21,0.35,0.48,0.52,0.65,0.75,0.83,0.94,1.0}; double[] yBinEdges = {-5.0,-4.1,-3.2,-2.0,-1.1,-0.4,1.2,2.3,3.5,4.2,5.0}; // Create 1D and 2D IProfile with variable bin width IProfile1D prof1DVariableBinWidth = hf.createProfile1D("prof1DVariableBinWidth", "Variable bin width 1D", xBinEdges); IProfile2D prof2DVariableBinWidth = hf.createProfile2D("prof2DVariableBinWidth", "Variable bin width 2D", xBinEdges, yBinEdges); Random r = new Random(); for ( int i = 0; i < 100; i++ ) { double x = r.nextDouble(); double y = x + 0.1*r.nextGaussian(); // Fill the IProfiles with default weight. prof1DFixedBinWidth.fill(x,y); prof2DFixedBinWidth.fill(x,y, r.nextDouble()); prof1DVariableBinWidth.fill(x,y); prof2DVariableBinWidth.fill(x,y, r.nextDouble()); } // Create the Fitter and fit the profiles IFitFactory fitFactory = af.createFitFactory(); IFitter fitter = fitFactory.createFitter("Chi2","minuit"); // Perform the fits IFitResult resProf1DFix = fitter.fit(prof1DFixedBinWidth,"p1"); IFitResult resProf1DVar = fitter.fit(prof1DVariableBinWidth,"p1"); // Display the results IPlotter plotter = af.createPlotterFactory().create("Fit and Plot an IProfile"); plotter.createRegions(2,1); plotter.region(0).plot( prof1DFixedBinWidth ); plotter.region(0).plot( resProf1DFix.fittedFunction() ); plotter.region(1).plot( prof1DVariableBinWidth ); plotter.region(1).plot( resProf1DVar.fittedFunction() ); plotter.show(); } }
Clouds are one, two or three dimensional unbinned collections of data. They are used for scatter plots or dynamically rebinnable Histograms. A Cloud can be automatically converted to an Histogram when the number of entries exceeds a given threshold, or can be manually converted by the user.
IClouds are created through a IHistogramFactory as shown in the example below:
import hep.aida.*; public class CloudCreate { public static void main(String[] argv) { IAnalysisFactory af = IAnalysisFactory.create(); ITree tree = af.createTreeFactory().create(); IHistogramFactory hf = af.createHistogramFactory(tree); ICloud1D cl1D = hf.createCloud1D( "cl1D" ); ICloud2D cl2D = hf.createCloud2D( "cl2D", "2-Dimensional Cloud" ); ICloud3D cl3D = hf.createCloud3D( "cl3D", "3-Dimensional Cloud", 1500, "autoConvert = false" ); } }
In this example the IClouds cl1D and cl2D are set by default to autoconvert when the threshold is reached. (Please note that in this case the threshold depends on the particular implementation of the AIDA package you are using). For the ICloud cl3D we set the threshold at 1500 events and we choose to switch off the autoConvert option; in this case to convert the ICloud it is necessary to invoke the convert method.
In the example below we fill a 1-Dimensional and a 2-Dimensional Cloud with random numbers. For each entry we assign a random weight. We then plot the histograms produced by the Cloud auto-conversion
import hep.aida.*; import java.util.Random; public class Cloud { public static void main(String[] argv) { IAnalysisFactory af = IAnalysisFactory.create(); ITree tree = af.createTreeFactory().create(); IHistogramFactory hf = af.createHistogramFactory(tree); ICloud1D cl1D = hf.createCloud1D( "cl1D", "1-Dimensional Cloud", 1500, "" ); ICloud2D cl2D = hf.createCloud2D( "cl2D", "2-Dimensional Cloud", 1500, "" ); int entries = 20000; Random r = new Random(); for ( int i = 0; i < entries; i++ ) { double xval = r.nextGaussian(); double yval = r.nextGaussian(); double w = r.nextDouble(); cl1D.fill( xval, w ); cl2D.fill( xval, yval, w ); } IHistogram1D cl1DHist = cl1D.histogram(); IHistogram2D cl2DHist = cl2D.histogram(); IPlotter plotter = af.createPlotterFactory().create("Plot"); plotter.createRegions(1,2,0); plotter.region(0).plot(cl1DHist); plotter.region(1).plot(cl2D); plotter.show(); } }
An IDataPointSet is a collection of IDataPoints. Each IDataPoint is an n-dimensional collection of IMeasurements. Through an IDataPointSetFactory it is possible to either create empty IDataPointSets or already full sets using the data stored in IHistograms, IClouds and IProfiles. IDataPoints and IMeasurements cannot be created, they can only be accessed through the IDataPointSet.
AIDA also provides arithmetic operations among IDataPointSets that have the same size and the same dimension.
In the example below we show how to create an empty one and two dimensional IDataPointSet, how to fill it and plot it:
import hep.aida.*; public class DataPointSetCreateAndFill { public static void main(String[] argv) { IAnalysisFactory af = IAnalysisFactory.create(); ITree tree = af.createTreeFactory().create(); IDataPointSetFactory dpsf = af.createDataPointSetFactory(tree); // Create a one dimensional IDataPointSet. IDataPointSet dps1D = dpsf.create("dps1D","one dimensional IDataPointSet",1); // Fill the one dimensional IDataPointSet double[] yVals1D = { 0.32, 0.45, 0.36, 0.29, 0.34 }; double[] yErrP1D = { 0.06, 0.07, 0.03, 0.07, 0.04 }; for ( int i = 0; i<yVals1D.length; i++ ) { dps1D.addPoint(); dps1D.point(i).coordinate(0).setValue( yVals1D[i] ); dps1D.point(i).coordinate(0).setErrorPlus( yErrP1D[i] ); } // Create a two dimensional IDataPointSet. IDataPointSet dps2D = dpsf.create("dps2D","two dimensional IDataPointSet",2); // Fill the two dimensional IDataPointSet double[] yVals2D = { 0.12, 0.22, 0.35, 0.42, 0.54 , 0.61 }; double[] yErrP2D = { 0.01, 0.02, 0.03, 0.03, 0.04 , 0.04 }; double[] yErrM2D = { 0.02, 0.02, 0.02, 0.04, 0.06 , 0.05 }; double[] xVals2D = { 1.5, 2.6, 3.4, 4.6, 5.5 , 6.4 }; double[] xErrP2D = { 0.5, 0.5, 0.4, 0.4, 0.5 , 0.5 }; for ( int i = 0; i<yVals2D.length; i++ ) { dps2D.addPoint(); dps2D.point(i).coordinate(0).setValue( xVals2D[i] ); dps2D.point(i).coordinate(0).setErrorPlus( xErrP2D[i] ); dps2D.point(i).coordinate(1).setValue( yVals2D[i] ); dps2D.point(i).coordinate(1).setErrorPlus( yErrP2D[i] ); dps2D.point(i).coordinate(1).setErrorMinus( yErrM2D[i] ); } // Display the results IPlotter plotter = af.createPlotterFactory().create("Plot IDataPointSets"); plotter.createRegions(2,1); plotter.region(0).plot( dps1D ); plotter.region(1).plot( dps2D ); plotter.show(); } }
In the example below we show how to fit a two dimensional IDataPointSet, with a one dimensional, second order polynomial
import hep.aida.*; public class CreateAndFitDataPointSet { public static void main(String[] argv) { IAnalysisFactory af = IAnalysisFactory.create(); ITree tree = af.createTreeFactory().create(); IDataPointSetFactory dpsf = af.createDataPointSetFactory(tree); IFunctionFactory funcF = af.createFunctionFactory(tree); IFitFactory fitF = af.createFitFactory(); IFitter fitter = fitF.createFitter("Chi2","uncmin"); // Create a two dimensional IDataPointSet. IDataPointSet dataPointSet = dpsf.create("dataPointSet","two dimensional IDataPointSet",2); // Fill the two dimensional IDataPointSet double[] yVals2D = { 0.12, 0.22, 0.35, 0.42, 0.54 , 0.61 }; double[] yErrP2D = { 0.01, 0.02, 0.03, 0.03, 0.04 , 0.04 }; double[] yErrM2D = { 0.02, 0.02, 0.02, 0.04, 0.06 , 0.05 }; double[] xVals2D = { 1.5, 2.6, 3.4, 4.6, 5.5 , 6.4 }; double[] xErrP2D = { 0.5, 0.5, 0.4, 0.4, 0.5 , 0.5 }; for ( int i = 0; i<yVals2D.length; i++ ) { dataPointSet.addPoint(); dataPointSet.point(i).coordinate(0).setValue( xVals2D[i] ); dataPointSet.point(i).coordinate(0).setErrorPlus( xErrP2D[i] ); dataPointSet.point(i).coordinate(1).setValue( yVals2D[i] ); dataPointSet.point(i).coordinate(1).setErrorPlus( yErrP2D[i] ); dataPointSet.point(i).coordinate(1).setErrorMinus( yErrM2D[i] ); } //Create a 1d second order polynomial IFunction p2 = funcF.createFunctionFromScript("p2", 1, "a+b*x[0]+c*x[0]*x[0]", "a,b,c","",null); IFitData data = fitF.createFitData(); data.create1DConnection(dataPointSet,0,1); IFitResult fittedFunction = fitter.fit(data,p2); // Display the results IPlotter plotter = af.createPlotterFactory().create("Plot IDataPointSets"); plotter.createRegions(); plotter.region(0).plot( dataPointSet ); plotter.region(0).plot( fittedFunction.fittedFunction() ); plotter.show(); } }
Through the IDataPointSetFactory it is easy to convert an IHistogram, an ICloud or an IProfile to an IDataPointSet as shown in the example below:
import hep.aida.*; public class DataPointSetCreateFromData { public static void main(String[] argv) throws java.io.IOException { IAnalysisFactory af = IAnalysisFactory.create(); // Create a tree loading the AIDA objects stored in an AIDA file. ITree tree = af.createTreeFactory().create("aidaStore.aida"); IDataPointSetFactory dpsf = af.createDataPointSetFactory(tree); IHistogram1D h1 = (IHistogram1D) tree.find("h1"); IProfile2D p2 = (IProfile2D) tree.find("p2"); ICloud3D c3 = (ICloud3D) tree.find("c3"); // Create IDataPointSets from the the above AIDA objects. IDataPointSet dps1DFromHist = dpsf.create("dps1DFromHist",h1); IDataPointSet dps2DFromProf = dpsf.create("dps2DFromProf",p2); IDataPointSet dps3DFromCloud = dpsf.create("dps2DFromCloud",c3); } }
The AIDA ITuple interface provides a way to store and retrieve n-tuple data.
ITuples are created through a ITupleFactory by providing the name and the type of the columns within the Tuple; this can be done in two different ways:
import hep.aida.*; import java.util.Random; public class TupleCreate { public static void main(String[] argv) { IAnalysisFactory af = IAnalysisFactory.create(); ITree tree = af.createTreeFactory().create(); ITupleFactory tf = af.createTupleFactory(tree); String[] columnNames = { "iFlat = 0", " fGauss = 3.", " fFlat =-2." }; Class[] columnClasses = { Integer.TYPE, Float.TYPE, Float.TYPE }; ITuple tuple1 = tf.create( "tuple1", "tupleLabel1", columnNames, columnClasses, ""); String columnString = "int iFlat=0, float fGauss = 3.; fFlat=-2."; ITuple tuple2 = tf.create( "tuple2", "tupleLabel1", columnString, ""); } }
In the code above tuple1 is created by providing the factory two arrays specifying, respectively, the name and the type ( i.e. the Class ) of the Tuple's columns. Alternatively we create tuple2 by providing the ITupleFactory a single string with name and type for all the columns separated by either a coma (,) or by a semicolon (;). In both cases the default values are specified by following the column's name with = value . Please note that spaces are ignored.
There are all-together ten different types of columns: the eight primitive types ( int, short, long, float, double, char, boolean, byte ), String and Object.
An ITuple is filled by rows as shown below:
import hep.aida.*; import java.util.Random; public class Tuple { public static void main(String[] argv) { IAnalysisFactory af = IAnalysisFactory.create(); ITree tree = af.createTreeFactory().create(); ITupleFactory tf = af.createTupleFactory(tree); String[] columnNames = { "iFlat = 0", " fGauss = 3.", " fFlat =-2." }; Class[] columnClasses = { Integer.TYPE, Float.TYPE, Float.TYPE }; ITuple tuple = tf.create( "tuple", "tupleLabel", columnNames, columnClasses, ""); Random r = new Random(); for (int i=0; i<100000; i++) { tuple.fill(0, r.nextInt(20) ); tuple.fill(1, (float)r.nextGaussian() ); tuple.fill(2, r.nextFloat() ); tuple.addRow(); } int colG = tuple.findColumn("fGauss"); int colF = tuple.findColumn("fFlat"); int colI = tuple.findColumn("iFlat"); IHistogramFactory hf = af.createHistogramFactory(tree); IHistogram1D h1dI = hf.createHistogram1D("h1dI",50,tuple.columnMin(colI),tuple.columnMax(colI)); IHistogram1D h1dF = hf.createHistogram1D("h1dF",50,tuple.columnMin(colF),tuple.columnMax(colF)); IHistogram1D h1dG = hf.createHistogram1D("h1dG",50,tuple.columnMin(colG),tuple.columnMax(colG)); IHistogram2D h2d = hf.createHistogram2D("h2d",50,tuple.columnMin(colG),tuple.columnMax(colG), 50,tuple.columnMin(colF),tuple.columnMax(colF)); tuple.start(); while ( tuple.next() ) { h1dI.fill( tuple.getInt(colI) ); h1dF.fill( tuple.getFloat(colF) ); h1dG.fill( tuple.getFloat(colG) ); h2d.fill( tuple.getFloat(colG), tuple.getFloat(colF) ); } IPlotter plotter = af.createPlotterFactory().create("Plot"); plotter.createRegions(2,2,0); plotter.region(0).plot(h1dI); plotter.region(1).plot(h1dF); plotter.region(2).plot(h1dG); plotter.region(3).plot(h2d); plotter.show(); } }
The addRow() method commits the row to storage. If the fill method has not been invoked for a given column when addRow() is reached, that column will be filled with its default value.
Please note that for each column type there is an appropriate get method.
IFilter and IEvaluator are simple objects that can be created by ITupleFactory and help manage data in an ITuple. Corresponding "create" methods in the factory take String that can contain ITuple column names, standard arithmetical and boolean operators (like +, -, /, *, > , <, ==, ...) and standard functions from the java.lang.Math class ( sin, exp, pow, ...). The string should evaluate to boolean for IFilter and to double for IEvaluator.
Example below demonstrates how to use IFilter and IEvaluator to filter and evaluate ITuple data on a row-by-row basis (1) and how to fill histograms from an ITuple (2).
Note: IFilters and IEvaluators are created as a stand-alone objects. You must associate them with ITuple with initialize(ITuple tuple) method before you can use them.
import hep.aida.*; import java.util.Random; public class FilterAndEvaluatorExample { public static void main(String[] argv) { IAnalysisFactory af = IAnalysisFactory.create(); ITree tree = af.createTreeFactory().create(); ITupleFactory tf = af.createTupleFactory( tree ); IHistogramFactory hf = af.createHistogramFactory(tree); java.util.Random r = new java.util.Random(); ITuple tuple = tf.create("TupleTreeName","Title: Test Tuple", "int n, double x, double y, double z"); for ( int i = 0; i < 30; i++ ) { double v1 = r.nextDouble()*10.; double v2 = r.nextDouble()*10.; double v3 = 5.3*v1+2.1*v1*v2 + 10.7; tuple.fill(0,i); tuple.fill(1,v1); tuple.fill(2,v2); tuple.fill(3,v3); tuple.addRow(); } // Create IFilter and initialize it to this ITuple IFilter filter = tf.createFilter("5.3*x+2.1*y*x + 10.7 > 50."); filter.initialize(tuple); // Create IEvaluator and initialize it to this ITuple IEvaluator evaluator = tf.createEvaluator("(1.5*x*x-5.2*y*x + 4*sin(y))/85"); evaluator.initialize(tuple); // Example 1: Filter ITuple Data tuple.start(); while (tuple.next()) { if (filter.accept()) { System.out.println(tuple.getInt(0) + "\t Row passed, evaluate: " + evaluator.evaluateDouble()); } else { System.out.println(tuple.getInt(0) + "\t Row failed, do not evaluate"); } } // Use IHistogramFactory to create two empty histograms IHistogram1D h1 = hf.createHistogram1D("hist-1", "Use All Data", 50, -10, 10); IHistogram1D h2 = hf.createHistogram1D("hist-2", "Use Filtered Data", 50, -10, 10); // Example 2: Fill histograms from ITuple tuple.project(h1, evaluator); tuple.project(h2, evaluator, filter); // Plot histograms IPlotter plotter = af.createPlotterFactory().create("Filter and Evaluator Example"); plotter.createRegions(2,1,0); plotter.region(0).plot(h1); plotter.region(1).plot(h2); plotter.show(); } }
ITupleFactory has several methods to group ITuples together and to create ITuple with a reduced data set.
createChained methods create a logical chain of ITuples. All ITuples in the set must have the same structure and resulting chained ITuple can not be filled. In a sense, chained ITuple is just a view of original Ituples, so no data is copied during creation of chained ITuple.
createFiltered method creates a new reduced tuple (less rows) from an existing one by applying a filter. Data is explicitly copied to a new n-tuple. User also has ability to copy only selected subset of columns by providing array with column names
import hep.aida.*; import java.util.Random; public class ChainedAndFilteredTuplesExample { public static void main(String[] argv) { IAnalysisFactory af = IAnalysisFactory.create(); ITree tree = af.createTreeFactory().create(); ITupleFactory tf = af.createTupleFactory( tree ); java.util.Random r = new java.util.Random(); // Create and fill 4 different ITuples ITuple tup1 = tf.create("tup1","tup1","int n, double x"); ITuple tup2 = tf.create("tup2","tup2","int n, double x"); ITuple tup3 = tf.create("tup3","tup3","int n, double x"); ITuple tup4 = tf.create("tup4","tup4","int n, double x"); for ( int i = 0; i < 20; i++ ) { tup1.fill(0,i); tup2.fill(0,i+20); tup3.fill(0,i+40); tup4.fill(0,i+60); tup1.fill(1, r.nextDouble()*10.); tup2.fill(1, r.nextDouble()*10.); tup3.fill(1, r.nextDouble()*10.); tup4.fill(1, r.nextDouble()*10.); tup1.addRow(); tup2.addRow(); tup3.addRow(); tup4.addRow(); } // Create a chain ITuple[] set = new ITuple[] { tup1, tup2, tup3, tup4}; ITuple chain = tf.createChained("ChainedTuple", "New Chained Tuple", set); chain.start(); System.out.println("\n\nChained Tuple:"); while (chain.next()) System.out.println(chain.getInt(0) + "\t" + chain.getDouble(1)); // Create IFilter and filtered ITuple IFilter filter = tf.createFilter("n>14 && n<46"); filter.initialize(chain); ITuple filteredTuple = tf.createFiltered("FilteredTuple", chain, filter); filteredTuple.start(); System.out.println("\n\nFiltered Tuple:"); while (filteredTuple.next()) System.out.println(filteredTuple.getInt(0) + "\t" + filteredTuple.getDouble(1)); } }
Within AIDA it is possible to create ITuples containing columns of ITuples. This allows the user to create ITuples with complex structures. Here is an example of how to create, fill and retrieve data from such an ITuple:
import hep.aida.*; import java.util.Random; public class createAndFillTupleWithComplexStructure { public static void main(String[] argv) throws java.io.IOException { String columnString = "int event =0, tracks =0; ITuple momentums = { double px = .2, py = 3.,"+ "px = 0., ITuple hits = {int x,y,z} }; float ipx, ipy, ipz"; IAnalysisFactory analysisFactory = IAnalysisFactory.create(); ITreeFactory treeFactory = analysisFactory.createTreeFactory(); ITree tree = treeFactory.create("testTupleWithComplexStructure.aida","type=xml;compress=no"); ITupleFactory tupleFactory = analysisFactory.createTupleFactory(tree); ITuple tuple = tupleFactory.create("tuple", "label",columnString,""); Random r = new Random(); int events = 100; for ( int i=0; i<events; i++ ) { tuple.fill(0, i); int tracks = r.nextInt(10); tuple.fill(1,tracks); ITuple momentum = tuple.getTuple( 2 ); for ( int j = 0; j<tracks; j++ ) { momentum.fill(0,r.nextGaussian()); momentum.fill(1,r.nextGaussian()); momentum.fill(2,r.nextGaussian()); int nHits = r.nextInt(20); ITuple hits = momentum.getTuple( 3 ); for ( int k = 0; k<nHits; k++ ) { hits.fill(0,r.nextInt(40)); hits.fill(1,r.nextInt(40)); hits.fill(2,r.nextInt(40)); hits.addRow(); } // end of hits loop momentum.addRow(); }// end of tracks loop tuple.fill(3,r.nextGaussian()); tuple.fill(4,r.nextGaussian()); tuple.fill(5,r.nextGaussian()); tuple.addRow(); }//end of loop over events IHistogramFactory hf = analysisFactory.createHistogramFactory(tree); IHistogram1D pxHist = hf.createHistogram1D("pxHist",100,tuple.getTuple(3).columnMin(0),tuple.getTuple(3).columnMax(0)); tuple.start(); while ( tuple.next() ) { ITuple momTuple = (ITuple) tuple.getObject(3); momTuple.start(); while ( momTuple.next() ) pxHist.fill( momTuple.getDouble(0) ); } IPlotter plotter = analysisFactory.createPlotterFactory().create("Plot"); plotter.createRegions(1,1,0); plotter.region(0).plot(pxHist); plotter.show(); } }
Notice the different ways of accessing the inner ITuple when filling and when retrieving the data. The getTuple(int index) method returns the ITuple ready to be filled, while the getObject(int index) method, invoked on the same column, returns the same ITuple but with the current row pointing to the current row of the higher level ITuple. For this reason the getObject(int index) method is used to retrieve the data. Also notice that the addRow() method has to be called for each of the individual inner ITuples evey time a row is ready to be stored.
The AIDA ITree interface provide two capabilities. The ability to group analysis objects such as Histograms, Clouds, Tuples etc. into hierarchical directories (or folders), and the ability to save and restore sets of analysis objects into files or databases.
Here is an example of how to create a set of histograms in several different folders:
import hep.aida.*; import java.util.Random; public class Tree { public static void main(String[] argv) { IAnalysisFactory af = IAnalysisFactory.create(); ITree tree = af.createTreeFactory().create(); IHistogramFactory hf = af.createHistogramFactory(tree); hf.createHistogram1D("test 1d",50,-3,3); hf.createHistogram2D("test 2d",50,-3,3,50,-3,3); tree.mkdir("/folder1"); tree.cd("/folder1"); hf.createHistogram1D("test 1d",50,-3,3); hf.createHistogram2D("test 2d",50,-3,3,50,-3,3); tree.mkdir("/folder2"); tree.cd("/folder2"); hf.createHistogram1D("test 1d",50,-3,3); hf.createHistogram2D("test 2d",50,-3,3,50,-3,3); } }
Each IHistogramFactory has an ITree associated with it. The ITree is set when the IHistogramFactory is created. Whenever the IHistogramFactory creates a new IHistogram it implicitly inserts it into the current directory of the associated ITree. The ITree initially contains a single folder (the "root folder"). New folders can be added using the mkdir(path) or mkdirs(path) method. You can change the current directory of the tree using the cd(path) method. All methods that take a path as an argument interpret that path using Unix conventions, so:
Trees also have methods for removing objects or directories, for locating objects within the tree, and for listing the contents of directories.
A second use for trees is to allow objects to be stored and retrieved from files or databases. So far we have always used the ITreeFactory.create() method to create ITree's. This method creates an ITree that is not associated with any storage, so the objects associated with this type of Tree are only valid within the current process. The other methods of ITreeFactory allow ITree's to be associated with a file or database. The following example shows how to create a set of histograms and store them in a file.
import hep.aida.*; import java.util.Random; import java.io.IOException; public class Store { public static void main(String[] argv) throws IOException { IAnalysisFactory af = IAnalysisFactory.create(); ITree tree = af.createTreeFactory().create("myFile.aida","xml",false,true); IHistogramFactory hf = af.createHistogramFactory(tree); IHistogram1D h1d = hf.createHistogram1D("test 1d",50,-3,3); IHistogram2D h2d = hf.createHistogram2D("test 2d",50,-3,3,50,-3,3); Random r = new Random(); for (int i=0; i<10000; i++) { h1d.fill(r.nextGaussian()); h2d.fill(r.nextGaussian(),r.nextGaussian()); } tree.commit(); } }
In this case we are creating a compressed XML file. The precise types of files or databases which can be used will depend on which implementation of AIDA you are using, however all AIDA implementations should support reading and writing XML files to allow easy interchange of objects. The next example shows how to read an XML file back in.
import hep.aida.*; import java.util.Random; import java.io.IOException; public class Restore { public static void main(String[] argv) throws IOException { IAnalysisFactory af = IAnalysisFactory.create(); ITree tree = af.createTreeFactory().create("myFile.aida","xml"); IHistogram1D h1d = (IHistogram1D) tree.find("test 1d"); IHistogram2D h2d = (IHistogram2D) tree.find("test 2d"); IPlotter plotter = af.createPlotterFactory().create("Test"); plotter.createRegions(2,1); plotter.region(0).plot(h1d); plotter.region(1).plot(h2d); plotter.show(); } }
In designing the ITree interface we have borrowed some other concepts from Unix. In particular ITree's allow other ITree's to be mounted and unmounted at an arbitrary point in the tree. This allows a whole set of files to be opened but be viewed by the AIDA user as a single ITree. ITree's also support symbolic links.
We have already seen simple use of plots the AIDA IPlotter interface. By default the plotter contains a single plotting region that covers the entire page, however any number of regions can be defined by the user. These regions may either consist of a regular grid created using the IPlotter.createRegions() method, or an arbitrary arrangement of regions created using the IPlotter.createRegion() method. At any time there is one currentRegion, which is the region that will be used when a new item is plotted. The Plotter allows histograms, clouds, and functions to be plotted. If more than one item is added to the same region the items will be overlaid on the same plot.
The following example shows how to use these methods:
import hep.aida.*; import java.util.Random; public class PlotExample { public static void main(String[] argv) { IAnalysisFactory af = IAnalysisFactory.create(); IHistogramFactory hf = af.createHistogramFactory(af.createTreeFactory().create()); IHistogram2D h2d = hf.createHistogram2D("test 2d",50,-30,30,50,-3,3); Random r = new Random(); for (int i=0; i<10000; i++) { h2d.fill(10*r.nextGaussian(),r.nextGaussian()); } IPlotter plotter = af.createPlotterFactory().create("Plot"); plotter.destroyRegions(); plotter.createRegion(0,0,.66,1).plot(h2d); plotter.createRegion(.66,0,.33,.5).plot(hf.projectionX("X Projection",h2d)); plotter.createRegion(.66,.5,.33,.5).plot(hf.projectionY("Y Projection",h2d)); plotter.show(); } }
The following example shows how to create a simple function and use it to do a simple fit to a histogram:
import hep.aida.*; import java.util.Random; public class FitExample { public static void main(String[] args) { // Create factories IAnalysisFactory analysisFactory = IAnalysisFactory.create(); ITreeFactory treeFactory = analysisFactory.createTreeFactory(); ITree tree = treeFactory.create(); IPlotter plotter = analysisFactory.createPlotterFactory().create("Plot"); IHistogramFactory histogramFactory = analysisFactory.createHistogramFactory(tree); IFunctionFactory functionFactory = analysisFactory.createFunctionFactory(tree); IFitFactory fitFactory = analysisFactory.createFitFactory(); // Create 1D histogram IHistogram1D h1d = histogramFactory.createHistogram1D("Histogram 1D",50,-3,3); // Fill 1D histogram with Gaussian Random r = new Random(); for (int i=0; i<5000; i++) { h1d.fill(r.nextGaussian()); } // Create Gaussian fitting function IFunction f = functionFactory.createFunctionByName("Gaussian", "G"); // Do Fit IFitter fitter = fitFactory.createFitter("chi2"); IFitResult result = fitter.fit(h1d,f); // Show results plotter.createRegions(1,1,0); plotter.region(0).plot(h1d); plotter.region(0).plot(f); plotter.show(); } }
The AIDA IFitter interface provides the user the possibility to fit IFunctions to any AIDA data storage object. Binned fits can be perfomed on IHistograms, IProfiles and IDataPointSets, while unbinned fits can be performed on IClouds and ITuples. Simple fits can be perfomed directely on the data storage objects while the IFitData interface is to be used for a greater control over the data, in particular its ranges and the connection to the IFunction's variables. Through the IFitter it is also possible to change the underlying optimization engine as well as the fit method used.
A simple chi2 fit to an histogram is perfomed in the example below:
import hep.aida.*; import java.util.Random; public class Chi2FitToHistogram { public static void main(String[] args) { // Create factories IAnalysisFactory analysisFactory = IAnalysisFactory.create(); IHistogramFactory histogramFactory = analysisFactory.createHistogramFactory(analysisFactory.createTreeFactory().create()); IPlotter plotter = analysisFactory.createPlotterFactory().create("Plot"); IFitFactory fitFactory = analysisFactory.createFitFactory(); // Create 1D histogram IHistogram1D h1d = histogramFactory.createHistogram1D("Gaussian Distribution",100,-5,5); // Fill 1D histogram with Gaussian Random r = new Random(); for (int i=0; i<5000; i++) h1d.fill(r.nextGaussian()); // Do Fit IFitter fitter = fitFactory.createFitter("chi2"); IFitResult result = fitter.fit(h1d,"g"); // Show results plotter.createRegions(1,1,0); plotter.destroyRegions(); plotter.region(0).plot(h1d); plotter.region(0).plot(result.fittedFunction()); plotter.show(); } }
In the example below it is shown how to perfom a binned and an unbinned fit over the same data using different optimizers and fit methods.
In the JAIDA implementation there are currently two optimizers available: Minuit and Uncmin and it is possible to choose among the following fit methods: ChiSquared, CleverChiSquared, BinnedMaximumLikelihood, LeastSquares, UnbinnedMaximumLikelihood.
import hep.aida.*; import java.util.Random; public class SimpleFit { public static void main(String[] argv) throws java.io.IOException { IAnalysisFactory anFactory = IAnalysisFactory.create(); ITree tree = anFactory.createTreeFactory().create("storeForFittingExample"); IHistogramFactory histFactory = anFactory.createHistogramFactory( tree ); ITupleFactory tuplFactory = anFactory.createTupleFactory( tree ); IFunctionFactory funcFactory = anFactory.createFunctionFactory( tree ); IFitFactory fitFactory = anFactory.createFitFactory(); IFitter fitter = fitFactory.createFitter("Chi2","minuit"); IHistogram1D gaussHist = histFactory.createHistogram1D("gaussHist","Gaussian Histogram",100,-5,5); ICloud1D gaussCloud = histFactory.createCloud1D("gaussCloud","Gaussian Cloud"); ITuple tuple = tuplFactory.create("tuple","Tuple Example","double gaussDistr"); Random r = new Random(); for (int i=0; i<10000; i++) { double x = r.nextGaussian(); gaussHist.fill(x); gaussCloud.fill(x); tuple.fill(0,x); tuple.addRow(); } // Chi2 fit with Minuit IFitResult minuitChi2Fit = fitter.fit(gaussHist,"g"); // Least Squares fit with Minuit fitter.setFitMethod("LS"); IFitResult minuitLeastSquaresFit = fitter.fit(gaussHist,"g"); // Binned Maximum Likelihood fit with Minuit fitter.setFitMethod("binnedMaximumLikelihood"); IFitResult minuitBinnedMaxLikelihoodFit = fitter.fit(gaussHist,"g"); // Unbinned Maximum Likelihood fit with Uncmin fitter.setEngine("uncmin"); fitter.setFitMethod("uml"); IFitResult uncminUMLFitToCloud = fitter.fit(gaussCloud,"g"); String[] gaussColumn = {"gaussDistr"}; IFitData fitData = fitFactory.createFitData(); fitData.createConnection(tuple,gaussColumn); IFitResult uncminUMLFitToTuple = fitter.fit(fitData,"g"); IHistogram1D gaussProj = histFactory.createHistogram1D("gaussProj","Gaussian Histogram Projected from ITuple",100,-5,5); tuple.project( gaussProj, tuplFactory.createEvaluator("gaussDistr") ); IPlotter plotter = anFactory.createPlotterFactory().create("Plot"); plotter.createRegion(0,0,.66,1).plot(gaussHist); plotter.destroyRegions(); plotter.createRegion(0,0,.66,1).plot(gaussHist); plotter.region(0).plot( minuitChi2Fit.fittedFunction() ); plotter.region(0).plot( minuitLeastSquaresFit.fittedFunction() ); plotter.region(0).plot( minuitBinnedMaxLikelihoodFit.fittedFunction() ); gaussCloud.convert(100,gaussCloud.lowerEdge(),gaussCloud.upperEdge()); plotter.createRegion(.66,0,.33,.5).plot(gaussCloud.histogram()); IModelFunction cloudFunc = (IModelFunction)uncminUMLFitToCloud.fittedFunction(); cloudFunc.normalize(false); double gaussCloudNorm = gaussCloud.entries()*( gaussCloud.upperEdge()-gaussCloud.lowerEdge() )/gaussCloud.histogram().axis().bins(); double cloudFuncNorm = gaussCloudNorm/(Math.sqrt(2*Math.PI)*cloudFunc.parameter("sigma")); cloudFunc.setParameter("amplitude",cloudFuncNorm); plotter.region(1).plot( cloudFunc ); plotter.createRegion(.66,.5,.33,.5).plot( gaussProj ); IModelFunction tupleFunc = (IModelFunction)uncminUMLFitToTuple.fittedFunction(); tupleFunc.normalize(false); double gaussProjNorm = gaussProj.entries()*( gaussProj.axis().upperEdge()-gaussProj.axis().lowerEdge() )/gaussProj.axis().bins(); double tupleFuncNorm = gaussProjNorm/(Math.sqrt(2*Math.PI)*tupleFunc.parameter("sigma")); tupleFunc.setParameter("amplitude",tupleFuncNorm); plotter.region(2).plot( tupleFunc ); plotter.show(); } }
With the IFitter interface it is possible to have a more direct control over the fit: the parameters in the fit can be controlled with the IFitParameterSettings interface and it is also possible to set constraints among them. In the example below we create a scripted function an show how to control its parameters:
import hep.aida.*; import java.util.Random; public class ComplexFit { public static void main(String[] argv) throws java.io.IOException { IAnalysisFactory anFactory = IAnalysisFactory.create(); ITree tree = anFactory.createTreeFactory().create("storeForFittingExample"); IHistogramFactory histFactory = anFactory.createHistogramFactory( tree ); ITupleFactory tuplFactory = anFactory.createTupleFactory( tree ); IFunctionFactory funcFactory = anFactory.createFunctionFactory( tree ); IFitFactory fitFactory = anFactory.createFitFactory(); IFitter fitter = fitFactory.createFitter("Chi2","minuit"); IHistogram2D hist = histFactory.createHistogram2D("hist","Test Histogram",100,-5,15,50,-5,5); Random r = new Random(); for (int i=0; i<10000; i++) { double x = r.nextGaussian()+5; if ( r.nextDouble() > 0.8 ) x = 2.5*r.nextGaussian()+5; double y = r.nextGaussian(); hist.fill(x,y); } IFitData fitData = fitFactory.createFitData(); fitData.create2DConnection(hist); IFunction func = funcFactory.createFunctionFromScript("twoDdistr",2,"N*(a*exp( -(x[0]-mu0)*(x[0]-mu0)/(2*s0*s0) )+" +"(1-a)*exp( -(x[0]-mu1)*(x[0]-mu1)/(2*s1*s1) ))*exp( -(x[1]-mu2)*" +"(x[1]-mu2)/(2*s2*s2) )","N,a,mu0,s0,mu1,s1,mu2,s2","",null); double[] initialPars = { 1, 0.8, 5, 1, 5, 2, 0, 1}; func.setParameters( initialPars ); fitter.fitParameterSettings("mu2").setFixed(true); fitter.fitParameterSettings("a").setBounds(0.5,0.9); fitter.fitParameterSettings("a").setStepSize(0.001); fitter.fitParameterSettings("s1").setBounds(2,4); fitter.fitParameterSettings("s1").setStepSize(0.1); fitter.setConstraint("s0 = s2"); fitter.setConstraint("mu0 = mu1"); IFitResult fitResult = fitter.fit(fitData,func); System.out.println("Chi2 = "+fitResult.quality()); double[] fPars = fitResult.fittedParameters(); double[] fParErrs = fitResult.errors(); String[] fParNames = fitResult.fittedParameterNames(); for(int i=0; i< fitResult.fittedFunction().numberOfParameters(); i++ ) System.out.println(fParNames[i]+" : "+fPars[i]+" +- "+fParErrs[i]); IPlotter plotter = anFactory.createPlotterFactory().create("Plot"); plotter.destroyRegions(); plotter.createRegion(0,0,.66,1).plot(hist); plotter.createRegion(.66,0,.33,.5).plot( histFactory.projectionX("projX",hist) ); plotter.createRegion(.66,.5,.33,.5).plot( histFactory.projectionY("projY",hist) ); plotter.show(); } }
Scans and contours can be obtained as shown below:
import hep.aida.*; import java.util.Random; public class ScanAndContour { public static void main(String[] argv) throws java.io.IOException { IAnalysisFactory anFactory = IAnalysisFactory.create(); ITree tree = anFactory.createTreeFactory().create("storeForFittingExample"); IHistogramFactory histFactory = anFactory.createHistogramFactory( tree ); ITupleFactory tuplFactory = anFactory.createTupleFactory( tree ); IFunctionFactory funcFactory = anFactory.createFunctionFactory( tree ); IFitFactory fitFactory = anFactory.createFitFactory(); IFitter fitter = fitFactory.createFitter("Chi2","minuit"); IHistogram1D hist = histFactory.createHistogram1D("hist","Test Histogram",100,-5,5); Random r = new Random(); for (int i=0; i<10000; i++) { double x = r.nextGaussian(); hist.fill(x); } IFitData fitData = fitFactory.createFitData(); fitData.create1DConnection(hist); IFitResult fitResult = fitter.fit(fitData,"g"); int meanIndex = fitResult.fittedFunction().indexOfParameter("mean"); double meanVal = fitResult.fittedParameters()[meanIndex]; double meanErr = fitResult.errors()[meanIndex]; IDataPointSet meanScan = fitter.createScan1D(fitData, fitResult.fittedFunction(),"mean",20, meanVal-3*meanErr, meanVal+3*meanErr); int sigmaIndex = fitResult.fittedFunction().indexOfParameter("sigma"); double sigmaVal = fitResult.fittedParameters()[sigmaIndex]; double sigmaErr = fitResult.errors()[sigmaIndex]; IDataPointSet oneSigmaContour = fitter.createContour(fitData, fitResult, "mean", "sigma", 10, 1); IDataPointSet twoSigmaContour = fitter.createContour(fitData, fitResult, "mean", "sigma", 10, 2); IPlotter plotter = anFactory.createPlotterFactory().create("Plot"); plotter.destroyRegions(); plotter.createRegion(0,0,.66,1).plot(hist); plotter.region(0).plot(fitResult.fittedFunction()); plotter.createRegion(.66,0,.33,.5).plot( meanScan ); plotter.createRegion(.66,.5,.33,.5).plot( twoSigmaContour ); plotter.region(2).plot( oneSigmaContour ); plotter.show(); } }