package org.jactr.tools.itr.ortho;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URI;
import java.net.URL;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import org.antlr.runtime.tree.CommonTree;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jactr.core.model.IModel;
import org.jactr.core.runtime.ACTRRuntime;
import org.jactr.core.utils.parameter.IParameterized;
import org.jactr.entry.iterative.IIterativeRunListener;
import org.jactr.entry.iterative.TerminateIterativeRunException;
import org.jactr.io.antlr3.misc.ASTSupport;
import org.jactr.io.generator.CodeGeneratorFactory;
import org.jactr.io.generator.ICodeGenerator;
import org.jactr.tools.itr.IParameterModifier;
import org.jactr.tools.itr.IParameterSetModifier;
import org.jactr.tools.itr.LongitudinalParameterSetModifier;
import org.jactr.tools.itr.ortho.impl.Slice;
import org.jactr.tools.itr.ortho.impl.SliceAnalysis;
import org.w3c.dom.Document;
public class OrthogonalSliceAnalyzer implements IIterativeRunListener,
IParameterized
{
/**
* Logger definition
*/
static private transient Log LOGGER = LogFactory
.getLog(OrthogonalSliceAnalyzer.class);
static public final String URI = "ConfigURL";
final private Collection<ISliceAnalyzer> _analyzers;
final private Collection<ISliceIntegrator> _integrators;
final private Map<String, IParameterModifier> _parameterModifiers;
final private Collection<SliceAnalysis> _analyses;
final private Collection<SliceAnalysis> _completedAnalyses;
final private Collection<ISliceListener> _sliceListeners = new ArrayList<ISliceListener>();
private File _reportRoot;
private PrintWriter _report;
private String _reportName = "OrthoSpaceSearch";
private LongitudinalParameterSetModifier _longitudinalModifier;
public OrthogonalSliceAnalyzer()
{
_analyzers = new ArrayList<ISliceAnalyzer>();
_parameterModifiers = new LinkedHashMap<String, IParameterModifier>();
_analyses = new ArrayList<SliceAnalysis>();
_integrators = new ArrayList<ISliceIntegrator>();
_completedAnalyses = new ArrayList<SliceAnalysis>();
}
public void add(ISliceListener listener)
{
_sliceListeners.add(listener);
}
public void add(ISliceAnalyzer analyzer)
{
_analyzers.add(analyzer);
if (analyzer instanceof ISliceListener) add((ISliceListener) analyzer);
}
public void add(ISliceIntegrator integrator)
{
_integrators.add(integrator);
if (integrator instanceof ISliceListener) add((ISliceListener) integrator);
}
public void add(IParameterModifier pModifier)
{
if (pModifier instanceof LongitudinalParameterSetModifier)
// we'll add it last at start
_longitudinalModifier = (LongitudinalParameterSetModifier) pModifier;
else
_parameterModifiers.put(pModifier.getParameterDisplayName(), pModifier);
if (pModifier instanceof ISliceListener) add((ISliceListener) pModifier);
}
public void exceptionThrown(int index, IModel model, Throwable thrown)
throws TerminateIterativeRunException
{
}
public void postRun(int currentRunIndex, int totalRuns,
Collection<IModel> models) throws TerminateIterativeRunException
{
ISlice slice = getSlice(currentRunIndex);
for (ISliceListener listener : _sliceListeners)
listener.stopIteration(slice, currentRunIndex, models);
/*
* should we run the analysis?
*/
if (currentRunIndex == slice.getLastIteration())
{
for (ISliceListener listener : _sliceListeners)
listener.stopSlice(slice);
LOGGER.debug("Finished " + slice);
runAnalyzers(currentRunIndex);
}
}
public void preBuild(int currentRunIndex, int totalRuns,
Collection<CommonTree> modelDescriptors) throws TerminateIterativeRunException
{
/*
* set the parameters
*/
ISlice slice = getSlice(currentRunIndex);
/**
* if no longitudinal, just set all the parameters. if there is a
* longitudinal, only set the population parameters if longitudinal is
* first..
*/
if (_longitudinalModifier == null)
for (Map.Entry<String, Object> parameter : slice.getParameterValues()
.entrySet())
for (CommonTree modelDescriptor : modelDescriptors)
{
IParameterModifier pm = _parameterModifiers.get(parameter.getKey());
if (pm == null) continue;
pm.setParameter(modelDescriptor, parameter.getValue().toString());
}
else
{
Map<String, Object> parameters = slice.getParameterValues();
for (Map.Entry<String, Object> parameter : parameters.entrySet())
if (parameter.getKey().equals(
_longitudinalModifier.getParameterDisplayName())
|| _longitudinalModifier.isFirstSlice(parameters.get(
_longitudinalModifier.getParameterDisplayName()).toString()))
for (CommonTree modelDescriptor : modelDescriptors)
{
IParameterModifier pm = _parameterModifiers.get(parameter.getKey());
if (pm == null) continue;
pm.setParameter(modelDescriptor, parameter.getValue().toString());
}
}
/*
* should we dump the models too?
*/
if (slice.getFirstIteration() == currentRunIndex)
{
SliceAnalysis analysis = (SliceAnalysis) getSliceAnalysis(currentRunIndex);
File sliceDir = new File(_reportRoot, "" + slice.getId());
File modelsDir = new File(sliceDir, "models");
modelsDir.mkdirs();
ICodeGenerator generator = CodeGeneratorFactory.getCodeGenerator("jactr");
for (CommonTree model : modelDescriptors)
try
{
String modelName = ASTSupport.getName(model);
File fp = File.createTempFile("model-", ".jactr", modelsDir);
PrintWriter pw = new PrintWriter(new FileWriter(fp));
for (StringBuilder line : generator.generate(model, true))
pw.println(line);
pw.close();
analysis
.addModel(modelName, modelsDir.getName() + "/" + fp.getName());
}
catch (IOException ioe)
{
}
}
}
private void runAnalyzers(int currentRun)
{
ISliceAnalysis analysis = getSliceAnalysis(currentRun);
_completedAnalyses.add((SliceAnalysis) analysis);
for (ISliceAnalyzer analyzer : _analyzers)
analyzer.analyze(analysis);
write();
/*
* notify integrators
*/
for (ISliceIntegrator integrator : _integrators)
integrator.completed(analysis);
}
private void write()
{
try
{
_report = new PrintWriter(new FileWriter(new File(_reportRoot,
"report.orthoxml")));
}
catch (IOException e)
{
throw new IllegalStateException("Cannot save report.xml ", e);
}
/*
* dump the header
*/
_report.print("<analyses date=\"");
_report.print(DateFormat.getDateTimeInstance(DateFormat.SHORT,
DateFormat.LONG).format(new Date()));
_report.print("\" name=\"");
_report.print(_reportName);
_report.println("\">");
_report.println("");
/*
* dump all the completed analyses
*/
for (SliceAnalysis analysis : _completedAnalyses)
{
analysis.write(_report);
_report.println();
}
/*
* dump the rest of the file
*/
_report.println("</analyses>");
_report.close();
}
public void preRun(int currentRunIndex, int totalRuns,
Collection<IModel> models) throws TerminateIterativeRunException
{
/*
* current run working directory, during iterative runs, this is a sub
* directory of user dir.
*/
File workingDir = ACTRRuntime.getRuntime().getWorkingDirectory();
Slice slice = (Slice) getSlice(currentRunIndex);
slice.addWorkingDirectory(workingDir.getName());
for (ISliceListener listener : _sliceListeners)
listener.startIteration(slice, currentRunIndex, models);
}
/**
* return the slice for this run iteration
*
* @param currentRun
* @return
*/
private ISlice getSlice(int currentRun)
{
return getSliceAnalysis(currentRun).getSlice();
}
private ISliceAnalysis getSliceAnalysis(int currentRun)
{
for (SliceAnalysis analysis : _analyses)
{
ISlice slice = analysis.getSlice();
if (slice.getFirstIteration() <= currentRun
&& currentRun <= slice.getLastIteration()) return analysis;
}
throw new IllegalStateException("No slice associated with " + currentRun);
}
public void start(int totalRuns) throws TerminateIterativeRunException
{
if (_longitudinalModifier != null)
_parameterModifiers.put(_longitudinalModifier.getParameterDisplayName(),
_longitudinalModifier);
long totalSize = 1;
Map<String, Long> pRanges = new LinkedHashMap<String, Long>();
for (IParameterModifier modifier : _parameterModifiers.values())
{
long size = modifier.getParameterValues().size();
pRanges.put(modifier.getParameterDisplayName(), size);
totalSize *= size;
}
long averageBlockSize = totalRuns / totalSize;
if (averageBlockSize == 0)
throw new RuntimeException(
"Insufficient trials to explore parameter space, need at least "
+ totalSize);
_reportRoot = new File(System.getProperty("user.dir"), "report");
_reportRoot.mkdirs();
/*
* we precreate all the slices..
*/
long blockCount = 0;
for (long i = 1; i <= totalRuns; i += averageBlockSize)
{
blockCount++;
long last = Math.min(i + averageBlockSize - 1, totalRuns);
if (totalRuns - last < averageBlockSize) last = totalRuns;
Slice slice = new Slice(blockCount, i, last);
/*
* compute the indicies into the parameter space
*/
Map<String, Long> currentIndicies = computeIndicies(blockCount - 1,
pRanges);
/*
* set the parameter values for that slice
*/
for (Map.Entry<String, Long> entry : currentIndicies.entrySet())
{
IParameterModifier parameterModifier = _parameterModifiers.get(entry
.getKey());
int parameterValueIndex = entry.getValue().intValue();
slice.setProperty(entry.getKey(), parameterModifier
.getParameterValues().get(parameterValueIndex));
if (parameterModifier instanceof IParameterSetModifier)
{
Map<String, String> nestedParametres = ((IParameterSetModifier) parameterModifier)
.getNestedParameterValues(parameterValueIndex);
for (Map.Entry<String, String> pV : nestedParametres.entrySet())
slice.setProperty(pV.getKey(), pV.getValue());
}
}
LOGGER.debug("Created " + slice);
// File analysisRoot = new File(_reportRoot, "" + blockCount);
SliceAnalysis analysis = new SliceAnalysis(slice, "report/" + blockCount);
_analyses.add(analysis);
if (last == totalRuns) i = totalRuns;
}
}
public void stop()
{
_longitudinalModifier = null;
_analyzers.clear();
_parameterModifiers.clear();
/*
* and notify the integrators
*/
Collection<ISliceAnalysis> analyses = Collections
.unmodifiableCollection(new ArrayList<ISliceAnalysis>(_analyses));
for (ISliceIntegrator integrator : _integrators)
integrator.integrate(analyses);
_analyses.clear();
_integrators.clear();
_completedAnalyses.clear();
}
public String getParameter(String key)
{
return null;
}
public Collection<String> getPossibleParameters()
{
return Collections.singleton(URI);
}
public Collection<String> getSetableParameters()
{
return Collections.singleton(URI);
}
public void setParameter(String key, String value)
{
if (URI.equalsIgnoreCase(key))
try
{
URI uri = null;
URL resource = getClass().getClassLoader().getResource(value);
if (resource != null)
uri = resource.toURI();
else
{
uri = new URI(value);
if (!uri.isAbsolute())
uri = new File(System.getProperty("user.dir")).toURI().resolve(
value);
}
Document doc = Parser.load(uri);
for (ISliceAnalyzer analyzer : Parser.buildAnalyzers(doc))
add(analyzer);
for (IParameterModifier modifier : Parser.buildModifiers(doc))
add(modifier);
for (ISliceIntegrator integrator : Parser.buildIntegrators(doc))
add(integrator);
_reportName = doc.getDocumentElement().getAttribute("name");
}
catch (Exception e)
{
throw new RuntimeException("Could not configure from " + value, e);
}
}
static private Map<String, Long> computeIndicies(long linearOffset,
Map<String, Long> dimensions)
{
LinkedHashMap<String, Long> indicies = new LinkedHashMap<String, Long>();
int size = 1;
/*
* first get the total linear size
*/
for (Long dim : dimensions.values())
size *= dim;
Iterator<Map.Entry<String, Long>> iterator = dimensions.entrySet()
.iterator();
while (iterator.hasNext())
{
Map.Entry<String, Long> entry = iterator.next();
size /= entry.getValue();
long index = linearOffset / size;
index = Math.min(index, entry.getValue() - 1);
linearOffset -= index * size;
indicies.put(entry.getKey(), index);
}
return Collections.unmodifiableMap(indicies);
}
public void preLoad(int currentRunIndex, int totalRuns) throws TerminateIterativeRunException
{
ISlice slice = getSlice(currentRunIndex);
if (slice.getFirstIteration() == currentRunIndex)
{
LOGGER.debug("Starting " + slice);
for (ISliceListener listener : _sliceListeners)
listener.startSlice(slice);
}
}
}