blob: 6744e3909c72c4f978bb3770d6d8de168b79874d [file] [log] [blame]
/**
*
*/
package org.eclipse.stem.util.analysis;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.stem.analysis.impl.ReferenceScenarioDataMapImpl;
import org.eclipse.stem.analysis.impl.ReferenceScenarioDataMapImpl.ReferenceScenarioDataInstance;
import org.eclipse.stem.core.common.Identifiable;
import org.eclipse.stem.core.graph.Graph;
import org.eclipse.stem.definitions.labels.AreaLabel;
import org.eclipse.stem.definitions.labels.PopulationLabel;
import org.eclipse.stem.ui.Utility;
import org.eclipse.stem.util.analysis.views.EstimatorControl;
import org.eclipse.stem.util.analysis.views.Messages;
/*******************************************************************************
* Copyright (c) 2007, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
/**
* Estimates rate constants for a scenario
*/
public class ScenarioParameterEstimator {
/**
* The scenario data used for estimation
*/
ReferenceScenarioDataMapImpl referenceScenarioDataMap = null;
/**
* The Parameter Estimator method we use
*/
ParameterEstimatorMethod parameterEstimatorMethod = null;
/**
* This Set will contain locations with "good" data. Good data implies that at least
* the "I" state is defined and that maximum number of poeple in the "I" state at some time
* is large enough for the estimator to return an approximate beta coefficient
*/
Set<String> referenceLocations;
/**
* areas of the reference locations
* loaded from the scenario graph by ID
*/
Map<String,Double> referenceAreaDataMap;
/**
* populations of the reference locations
* loaded from the scenario graph by ID
*/
Map<String,Double> referencePopulationDataMap;
/**
* remember the max Infectious at each location
*/
Map<String,Double> maxInfectiousMap = new HashMap<String,Double>();
ParameterEstimator estimator;
/**
* Prefix for area. Format is
* stem://org.eclipse.stem/label/area/USA/2/US-GA-13307
*/
private static final String AREA_URI_PREFIX = org.eclipse.stem.definitions.labels.AreaLabel.URI_TYPE_AREA_LABEL_SEGMENT;
/**
* prefix for population. Format is
* stem://org.eclipse.stem/label/population/USA/2/US-GA-13307/human
*
*/
private static final String POPULATION_URI_PREFIX = org.eclipse.stem.definitions.labels.PopulationLabel.URI_TYPE_POPULATION_LABEL_SEGMENT;
/**
* TODO make gui item
* syntax is
* stem://org.eclipse.stem/label/population/ISR/2/human/2006/IL-05-623
* Human only for now
*/
private static final String SPECIES = "human";
/**
* analysis mode using ALL data including zeros
*/
public static final int ALL_MODE = 0;
/**
* analysis mode analyzing data only when S,E,I,R are all > 0.0;
*/
public static final int NON_ZERO_DATA_MODE = 1;
/**
* constant
*/
public static final String LEVEL_STRING = "level";
// fit all locations with max infectious populations >= FITTING_FRACTION * LARGEST Infectious Population
private static final double FITTING_FRACTION = 0.05;
/**
* The parent control
*/
protected EstimatorControl control = null;
/**
*
* @param scenarioDataMap
* @param parameterEstimatorMethod
*/
public ScenarioParameterEstimator(ReferenceScenarioDataMapImpl scenarioDataMap, ParameterEstimatorMethod parameterEstimatorMethod) {
referenceScenarioDataMap = scenarioDataMap;
this.parameterEstimatorMethod = parameterEstimatorMethod;
referenceLocations = getReferenceLocations();
getAreaDataMap();
getPopulationDataMap();
}
/**
* Estimate the rate parameters from the existing scenario data
* @param controlParamtersMap
* @param runnableContext Context for long running tasks
* @return map of parameters
*/
public Map<String,ModelParameters> estimateParameters(Map<String,Object> controlParamtersMap, IRunnableContext runnableContext) {
final Map<String,Object> finalParameterMap = controlParamtersMap;
final ParameterEstimatorMethod method = this.parameterEstimatorMethod;
IScenarioParameterEstimatorRunnableWithProgress analysisTask = new IScenarioParameterEstimatorRunnableWithProgress() {
public Map<String,ModelParameters> fittedParameters = new HashMap<String,ModelParameters>();
public void run(IProgressMonitor progress) {
Iterator<String> iter = referenceLocations.iterator();
int icount = 0;
progress.beginTask("Estimating parameters", referenceLocations.size());
// TODO START this code exists only to TEST the CycleCompressor class
// TODO START this code exists only to TEST the CycleCompressor class
// TODO SHOULD EVENTUALLY BE PART OF RONI's new VIEW
//List<String> allIDs = new ArrayList<String>();
//List<double[]> allCompressedIncidence = new ArrayList<double[]>();
// TODO END this code exists only to TEST the CycleCompressor class
// TODO END this code exists only to TEST the CycleCompressor class
while(iter.hasNext()) {
icount ++;
//if (icount%100==0) Activator.logInformation(".");
String id = iter.next();
ReferenceScenarioDataInstance locationDataMap = referenceScenarioDataMap.getLocation(id);
// TODO START this code exists only to TEST the CycleCompressor class
// TODO START this code exists only to TEST the CycleCompressor class
// TODO SHOULD EVENTUALLY BE PART OF RONI's new VIEW
//allIDs.add(id);
//CycleCompressor compressor = new CycleCompressor(locationDataMap);
//double[] result = compressor.getCompressedData();
//allCompressedIncidence.add(result);
// TODO END this code exists only to TEST the CycleCompressor class
// TODO END this code exists only to TEST the CycleCompressor class
estimator = ParameterEstimatorFactory.createEstimator(method, locationDataMap);
estimator.setAllParameters(finalParameterMap);
double localArea = getArea(id).doubleValue();
assert(localArea > 0.0);
if(localArea == 0) localArea = 1.0; // should never happen
final double localPopulation = getPopulation(id).doubleValue();
final double localDensity = localPopulation/localArea;
ModelParameters parameters = estimator.estimate(localDensity, localPopulation,id);
//Activator.logInformation("got parameters "+parameters.toString());
fittedParameters.put(id, parameters);
if (progress.isCanceled())
throw new OperationCanceledException();
progress.worked(1);
} // while
// TODO START this code exists only to TEST the CycleCompressor class
// TODO START this code exists only to TEST the CycleCompressor class
// TODO SHOULD EVENTUALLY BE PART OF RONI's new VIEW
//CSVAnalysisWriter writer = new CSVAnalysisWriter("C:/runtime-stem.product/Mecids/recordedsimulations/Influenza/cycleCompressed/","Incidence");
//writer.logData(allIDs, allCompressedIncidence);
// TODO END this code exists only to TEST the CycleCompressor class
// TODO END this code exists only to TEST the CycleCompressor class
progress.done();
}
/**
* Return the fitted parameters
* @return Map<String, ModelParameters> result
*/
public Map<String, ModelParameters> getFittedParameters() {
return this.fittedParameters;
}
};
try {
runnableContext.run(true, true, analysisTask);
} catch(InterruptedException ie) {
Activator.logError("", ie);
return null;
} catch(InvocationTargetException ite) {
Activator.logError("", ite);
return null;
}
return analysisTask.getFittedParameters();
}//estimateParameters()
/**
* Average All data THEN Estimate the rate parameters from the averaged scenario data
* @param controlParamtersMap
* @param runnableContext Context for long running tasks
* @return map of parameters
*/
public ModelParameters averageThenEstimateParameters(Map<String,Object> controlParamtersMap, IRunnableContext runnableContext) {
final Map<String,Object> finalParameterMap = controlParamtersMap;
final ParameterEstimatorMethod method = this.parameterEstimatorMethod;
IAveragedParameterEstimatorRunnableWithProgress analysisTask = new IAveragedParameterEstimatorRunnableWithProgress() {
ModelParameters modelParameters = null;
public void run(IProgressMonitor progress) {
Iterator<String> iter = referenceLocations.iterator();
progress.beginTask("Averaging data", referenceLocations.size());
ReferenceScenarioDataInstance averagedDataMap = null;
double localArea = 0.0;
double localPopulation = 0.0;
referenceScenarioDataMap.findMaxIncidence();
String idMax = referenceScenarioDataMap.getMaxIncidenceLocation();
//double max = referenceScenarioDataMap.getMaxIncidence();
//Activator.logInformation(" max incidence is "+max+" at "+idMax);
localArea += getArea(idMax).doubleValue();
localPopulation += getPopulation(idMax).doubleValue();
ReferenceScenarioDataInstance maxlocationDataMap = referenceScenarioDataMap.getLocation(idMax);
averagedDataMap = maxlocationDataMap.clone();
// PhaseSynchronizer synchronizer = new PhaseSynchronizer(averagedDataMap);
while(iter.hasNext()) {
String id = iter.next();
if(!id.equalsIgnoreCase(idMax)) {
localArea += getArea(id).doubleValue();
localPopulation += getPopulation(id).doubleValue();
ReferenceScenarioDataInstance locationDataMap = referenceScenarioDataMap.getLocation(id);
averagedDataMap.integrateData(locationDataMap.instance);
}
}
estimator = ParameterEstimatorFactory.createEstimator(method, averagedDataMap);
estimator.setAllParameters(finalParameterMap);
assert(localArea > 0.0);
if(localArea == 0) localArea = 1.0; // should never happen
final double localDensity = localPopulation/localArea;
modelParameters = estimator.estimate(localDensity, localPopulation, Messages.getString("EST.AVERAGE"));
if (progress.isCanceled())
throw new OperationCanceledException();
progress.worked(1);
progress.done();
}
/**
* Return the fitted parameters
* @return Map<String, ModelParameters> result
*/
public ModelParameters getModelParameters() {
return this.modelParameters;
}
};
try {
runnableContext.run(true, true, analysisTask);
} catch(InterruptedException ie) {
Activator.logError("", ie);
return null;
} catch(InvocationTargetException ite) {
Activator.logError("", ite);
return null;
}
return analysisTask.getModelParameters();
}//averageThenEstimateParameters()
/**
* Returns a set of locations where the max Infectious population satisfies the criterion:
* localMaxI >= FITTING_FRACTION*globalMaxI
* There is not point in analyzing locations where no-one gets sick
* @return set of referenceLocations
*/
private Set<String> getReferenceLocations() {
Set<String> locationsToFit = new HashSet<String>();
Set<String> allLocations = referenceScenarioDataMap.getLocations();
double globalMaxI = 0.0;
// 1. get the local max infectious count by region id for all regions
Iterator<String> iter = allLocations.iterator();
while((iter!=null)&&(iter.hasNext())) {
String id = iter.next();
ReferenceScenarioDataInstance locationDataMap = referenceScenarioDataMap.getLocation(id);
List<Double> iData = locationDataMap.getItotals();
double localMax = 0.0;
for(int i = 0; i < iData.size(); i ++) {
double infectious = iData.get(i).doubleValue();
if (localMax < infectious) localMax = infectious;
if (globalMaxI < localMax) globalMaxI = localMax;
}
maxInfectiousMap.put(id,new Double(localMax));
}
// 2. Select those regions with max infectious count >= FITTING_FRACTION * LARGEST Infectious Population
Iterator<String> iter2 = maxInfectiousMap.keySet().iterator();
while((iter2!=null)&&(iter2.hasNext())) {
String id = iter2.next();
double localMaxI = maxInfectiousMap.get(id).doubleValue();
if (localMaxI >= FITTING_FRACTION*globalMaxI) {
locationsToFit.add(id);
}
}
return locationsToFit;
}
/**
*
* @param ID
* @return
*/
Double getArea(String id) {
double area = 0.0;
URI uri = getAreaURI(id);
// New utility method
AreaLabel areaLabel = (AreaLabel)org.eclipse.stem.ui.Utility.getLabel(uri);
if(areaLabel==null) Activator.logError("area label is null", new NumberFormatException("null area"));
area = areaLabel.getCurrentAreaValue().getArea();
return new Double(area);
}
/**
*
* @param ID
* @return
*/
Double getPopulation(String id) {
double pop = 0.0;
URI uri = getPopulationURI(id);
// New utility method
PopulationLabel popLabel = (PopulationLabel)org.eclipse.stem.ui.Utility.getLabel(uri);
if(popLabel==null) Activator.logError("Population label is null", new NumberFormatException("null population"));
pop = popLabel.getCurrentPopulationValue().getCount();
return new Double(pop);
}
/**
*
* @return the area map
*/
Map<String,Double> getAreaDataMap() {
Map<String,Double> areaMap = new HashMap<String,Double>();
Iterator<String> iter = referenceLocations.iterator();
while(iter.hasNext()) {
String id = iter.next();
Double area = getArea(id);
areaMap.put(id, area);
}
return areaMap;
}
/**
*
* @return the population map
*/
Map<String,Double> getPopulationDataMap() {
Map<String,Double> popMap = new HashMap<String,Double>();
Iterator<String> iter = referenceLocations.iterator();
while(iter.hasNext()) {
String id = iter.next();
Double population = getPopulation(id);
popMap.put(id, population);
}
return popMap;
}
/**
* convert a List of String into and array of double
* @param dataList
* @return double[] of values
*/
public static double[] getDoubleValues(List<String> dataList) {
double[] retValue = new double[dataList.size()];
try {
for (int i = 0; i < dataList.size(); i ++) {
Double dbleObj = new Double(dataList.get(i));
retValue[i] = dbleObj.doubleValue();
}
} catch(Exception e) {
Activator.logError("String Data not valid. Expecting Double values", e);
}
return retValue;
}
/**
* @return the Estimator
*/
public ParameterEstimator getEstimator() {
return estimator;
}
/**
*
* @param id
* @return
*/
URI getAreaURI(String id) {
int level = getLevel(id);
String countryCode;
// need uri of form: stem://org.eclipse.stem/label/area/USA/2/US-GA-13307
if (level == 0) {
countryCode = id;
} else {
String twoLetterCode = id.substring(0,2);
countryCode = org.eclipse.stem.data.geography.GeographicMapper.getAlpha3(twoLetterCode);
}
String composite = "stem://org.eclipse.stem/"+AREA_URI_PREFIX + "/" + countryCode +"/"+getLevel(id)+"/"+id;
URI uri = URI.createURI(composite);
return uri;
}// getAreaURI
/**
*
* @param id
* @return
*/
URI getPopulationURI(String id) {
int level = getLevel(id);
String countryCode;
// compose the input string
if (level == 0) {
countryCode = id;
} else {
String twoLetterCode = id.substring(0,2);
countryCode = org.eclipse.stem.data.geography.GeographicMapper.getAlpha3(twoLetterCode);
}
// syntax is
// stem://org.eclipse.stem/label/population/ISR/2/human/2006/IL-05-623
// Hack to fix year problem. We don't know the year, try until we guess right
String composite = null;
for(int year=2000;year<2020;++year) {
composite = "stem://org.eclipse.stem/"+POPULATION_URI_PREFIX + "/" + countryCode +"/"+getLevel(id)+"/"+SPECIES+"/"+year+"/"+id;
try {
final URI populationGraphURI = Utility.createPopulationGraphURI(URI.createURI(composite));
// need to catch any exception here...we take the data for any year for this estimate
final ResourceSet resourceSet = new ResourceSetImpl();
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("*", new XMIResourceFactoryImpl());
resourceSet.getResourceFactoryRegistry().getProtocolToFactoryMap().put("platform", new XMIResourceFactoryImpl());
final Resource resource = resourceSet.getResource(populationGraphURI,true);
Identifiable ident = (Identifiable) resource.getContents().get(0);
final Graph populationGraph = (Graph) ident;
// did we get the data
if(populationGraph != null) break;
} catch (Exception e) {
//do nothing. not an error Activator.logInformation("No Error looking for data: "+composite);
}
}
URI uri = URI.createURI(composite);
return uri;
}// getPopulationURI
/**
* finds the admin level of the id by counting '-' characters
* @param id
* @return
*/
private int getLevel(String id) {
if(id.indexOf("-") ==-1) return 0;
int idx1 = id.indexOf("-");
String firstPart = id.substring(idx1+1,id.length());
if(firstPart.indexOf("-") ==-1) return 1;
int idx2 = firstPart.indexOf("-");
String secondPart = firstPart.substring(idx2+1,firstPart.length());
if(secondPart.indexOf("-") ==-1) return 2;
int idx3 = secondPart.indexOf("-");
String thirdPart = secondPart.substring(idx3+1,secondPart.length());
if(thirdPart.indexOf("-") ==-1) {
return 3;
}
// TODO should generalize this.
return 4; // two levels past where we now have data....
}
/**
* Internal interface for long running task
*/
private interface IScenarioParameterEstimatorRunnableWithProgress extends IRunnableWithProgress {
/**
* Return the fitted parameters
* @return Map<String, ModelParameters> result
*/
public Map<String, ModelParameters> getFittedParameters();
}
/**
* Internal interface for long running task
*/
private interface IAveragedParameterEstimatorRunnableWithProgress extends IRunnableWithProgress {
/**
* Return the fitted parameters
* @return ModelParameters result
*/
public ModelParameters getModelParameters();
}
/**
* Get the values to plot from the estimator
* @param chartIndex
* @param state
* @return the array of data to plot
*/
public double[] getValues(int chartIndex, int state) {
return estimator.getValues(chartIndex, state);
}
}// getLevel