Commit 1652e2e5 authored by Daniel Lehmberg's avatar Daniel Lehmberg
Browse files

initial: new outputfile and datakey and processor to write out metric data

parent 3d351721
Pipeline #115829 failed with stages
in 160 minutes and 8 seconds
......@@ -3,26 +3,44 @@ package org.vadere.simulator.models.osm.optimization;
import org.vadere.util.geometry.shapes.VPoint;
/**
* @author Daniel Lehmberg
* //TODO
* Stores the values of the true solution (which can be computed analytically or by brute force) and the solution that
* is found by a optimizer algorithm. It holds both the (tru/found) point and (true/found) function value.
*
* The difference between the two quantities allow to measure the quality of an optimization algorithm.
*/
public class OptimizationMetric {
/* Meta data for the metric */
private double simTime;
private int pedId;
/* Metric data from which differences can be taken */
private VPoint optimalPoint;
private double optimalFuncValue;
private VPoint foundPoint;
private double foundFuncValue;
public OptimizationMetric(final VPoint optimalPoint, double optimalFuncValue){
public OptimizationMetric(int pedId, double simTime, final VPoint optimalPoint, double optimalFuncValue,
final VPoint foundPoint, final double foundFuncValue){
this.pedId = pedId;
this.simTime = simTime;
this.optimalPoint = optimalPoint;
this.optimalFuncValue = optimalFuncValue;
this.foundPoint = null; // Can only be set afterwards optimal point
this.foundFuncValue = -1;
this.foundPoint = foundPoint;
this.foundFuncValue = foundFuncValue;
}
public double getSimTime() {
return simTime;
}
public int getPedId() {
return pedId;
}
public VPoint getOptimalPoint() {
......@@ -41,11 +59,9 @@ public class OptimizationMetric {
return foundFuncValue;
}
public void setFoundPoint(VPoint foundPoint) {
this.foundPoint = foundPoint;
}
public void setFoundFuncValue(double foundFuncValue) {
this.foundFuncValue = foundFuncValue;
public String[] getValueString(){
String[] valueLine = {""+optimalPoint.x, ""+optimalPoint.y, ""+optimalFuncValue,
""+foundPoint.x, ""+foundPoint.y, ""+foundFuncValue};
return valueLine;
}
}
package org.vadere.simulator.models.osm.optimization;
import java.awt.Shape;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import org.vadere.simulator.models.osm.PedestrianOSM;
import org.vadere.state.scenario.Pedestrian;
import org.vadere.util.data.SortedList;
import org.vadere.util.geometry.GeometryUtils;
import org.vadere.util.geometry.shapes.VCircle;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.logging.Logger;
/**
* The Interface StepCircleOptimizer.
*
* Abstract Base Class for StepCircleOptimizer.
*
* The abstract functions need to be implemented by every StepCircleOptimizer.
* The additional functions only serve to compute a true solution (obtained via computationally expensive brute force).
* This allows to compute a metric that measures the quality of the respective concrete subclass of StepCircleOptimizer.
*
* Currently, only the StepCircleOptimizerNelderMead uses the metric functionality. To compute the brute force
* solution only the "computeAndAddBruteForceSolutionMetric" function has to be called (NOTE: depending on the setting
* in "getReachablePositions" this can be very expensive.
*/
public abstract class StepCircleOptimizer {
private ArrayList<OptimizationMetric> currentMetricValues;
private boolean computeMetric;
private ArrayList<OptimizationMetric> currentMetricValues;
protected StepCircleOptimizer(){
// TODO: read if the metric should be computed from a config file, see issue #243
boolean computeMetric = true;
this.computeMetric = true;
if(computeMetric){
if(this.computeMetric){
this.currentMetricValues = new ArrayList<>();
}else{
this.currentMetricValues = null;
......@@ -43,7 +47,22 @@ public abstract class StepCircleOptimizer {
* quality of a optimization algorithm.
*/
private OptimizationMetric bruteForceOptimalValue(PedestrianOSM pedestrian){
protected class SolutionPair {
/* Data class to store point and function value of either */
public final VPoint point;
public final double funcValue;
public SolutionPair(VPoint point, double funcValue){
this.point = point;
this.funcValue = funcValue;
}
}
protected boolean getComputeMetric(){
return computeMetric;
}
private SolutionPair bruteForceOptimalValue(PedestrianOSM pedestrian){
var reachableArea = new VCircle( pedestrian.getFreeFlowStepSize() );
var potentialEvaluationFunction = new PotentialEvaluationFunction(pedestrian);
......@@ -57,25 +76,25 @@ public abstract class StepCircleOptimizer {
try{
optimalFuncValue = potentialEvaluationFunction.getValue(optimalPoint);
}catch (Exception e) {
Logger.getLogger(StepCircleOptimizerDiscrete.class).error("Potential evaluation threw error. Setting value " +
"to invalid (-1)");
Logger.getLogger(StepCircleOptimizerDiscrete.class).error("Potential evaluation threw error. " +
"Setting value to invalid (-1)");
optimalFuncValue = -1;
}
return new OptimizationMetric(optimalPoint, optimalFuncValue);
return new SolutionPair(optimalPoint, optimalFuncValue);
}
protected void setBruteForceSolution(PedestrianOSM pedestrian){
this.currentMetricValues.add(bruteForceOptimalValue(pedestrian)); // adds at the end of the list
}
protected void computeAndAddBruteForceSolutionMetric(final PedestrianOSM pedestrian,
final SolutionPair foundSolution){
protected void setFoundSolution(VPoint foundSolution, double funcValue){
// Always add at the last, currently this is not very secure bc. there is no checking if it overwrites other
// values
OptimizationMetric metric = this.currentMetricValues.get(this.currentMetricValues.size()-1);
metric.setFoundPoint(foundSolution);
metric.setFoundFuncValue(funcValue);
}
var bruteForceSolution = bruteForceOptimalValue(pedestrian);
// TODO: maybe time of nextStep is not the actual correct one, possibly adapt
var optimizationMetric = new OptimizationMetric(pedestrian.getId(), pedestrian.getTimeOfNextStep(),
bruteForceSolution.point, bruteForceSolution.funcValue, foundSolution.point, foundSolution.funcValue);
currentMetricValues.add(optimizationMetric);
}
public ArrayList<OptimizationMetric> getCurrentMetricValues(){
return this.currentMetricValues;
......@@ -86,15 +105,15 @@ public abstract class StepCircleOptimizer {
}
private static List<VPoint> getReachablePositions(VCircle reachableArea){
// TODO: numberPointsOfLargestCircle and numberOfCircles are parameters with a trade off between runtime and
// NOTE: numberPointsOfLargestCircle and numberOfCircles are parameters with a trade off between runtime and
// precision of brute force solution
return GeometryUtils.getDiscDiscretizationPoints(
null,
false,
reachableArea,
20,
10000,
30,
5000,
0,
2 * Math.PI);
}
......
......@@ -132,6 +132,13 @@ public class StepCircleOptimizerNelderMead extends StepCircleOptimizer {
// System.out.println(potentialEvaluationFunction.counter);
//logger.info("["+(minimum[0]-pedestrian.getPosition().getX())+","+(minimum[1]-pedestrian.getPosition().getY())+"]");
//lastSolution.put(pedestrian, new VPoint(minimum[0]-pedestrian.getPosition().getX(), minimum[1]-pedestrian.getPosition().getY()));
if(getComputeMetric()){
// See merge request !65
this.computeAndAddBruteForceSolutionMetric(pedestrian,
new SolutionPair(new VPoint(minimum[0], minimum[1]), minimumValue));
}
return new VPoint(minimum[0], minimum[1]);
}
......
package org.vadere.simulator.projects.dataprocessing.datakey;
import org.vadere.simulator.projects.dataprocessing.outputfile.EventtimePedestrianIdOutputFile;
/**
* This key consists of the pedestrian id and the event time ("eventTime") which is the *exact* time at which an
* event occured (which can be any positive real value). In between simulation time steps (see parameter
* "simTimeStepLength" (currently default at 0.4) there can be multiple events. The event time therefore allows to
* have more (accurate) data (all the events in between simulation time steps).
*/
@OutputFileMap(outputFileClass = EventtimePedestrianIdOutputFile.class)
public class EventtimePedestrianIdKey implements DataKey<EventtimePedestrianIdKey> {
private final double simTime;
private final int pedestrianId;
public EventtimePedestrianIdKey(double simTime, int pedestrianId) {
this.simTime = simTime ;
this.pedestrianId = pedestrianId;
}
public Double getSimtime() {
return simTime;
}
public Integer getPedestrianId() {
return pedestrianId;
}
@Override
public int compareTo(EventtimePedestrianIdKey o) {
int result = Double.compare(simTime, o.simTime);
if (result == 0) {
return Integer.compare(pedestrianId, o.pedestrianId);
}
return result;
}
public static String[] getHeaders() {
// TODO: if there are more keys using simTime then there should be a separate key.
return new String[] { PedestrianIdKey.getHeader(), "simTime"};
}
@Override
public String toString() {
return "TimestepPedestrianIdKey{" +
"simTime=" + this.simTime +
", pedestrianId=" + pedestrianId +
'}';
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + pedestrianId;
result = prime * result + (int) (simTime *100);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
EventtimePedestrianIdKey other = (EventtimePedestrianIdKey) obj;
if (pedestrianId != other.pedestrianId)
return false;
if (simTime != other.simTime)
return false;
return true;
}
}
package org.vadere.simulator.projects.dataprocessing.outputfile;
import org.vadere.annotation.factories.outputfiles.OutputFileClass;
import org.vadere.simulator.projects.dataprocessing.datakey.EventtimePedestrianIdKey;
/**
*
*/
@OutputFileClass(dataKeyMapping = EventtimePedestrianIdKey.class)
public class EventtimePedestrianIdOutputFile extends OutputFile<EventtimePedestrianIdKey> {
public EventtimePedestrianIdOutputFile() {
super(EventtimePedestrianIdKey.getHeaders());
}
@Override
public String[] toStrings(final EventtimePedestrianIdKey key) {
return new String[] {Integer.toString(key.getPedestrianId()), Double.toString(key.getSimtime())};
}
}
package org.vadere.simulator.projects.dataprocessing.processor;
import org.vadere.annotation.factories.dataprocessors.DataProcessorClass;
import org.vadere.simulator.control.SimulationState;
import org.vadere.simulator.models.osm.PedestrianOSM;
import org.vadere.simulator.models.osm.optimization.OptimizationMetric;
import org.vadere.simulator.projects.dataprocessing.ProcessorManager;
import org.vadere.simulator.projects.dataprocessing.datakey.EventtimePedestrianIdKey;
import org.vadere.state.scenario.Pedestrian;
import java.util.ArrayList;
import java.util.Collection;
/**
*
* This processor requires the true and found solution (both point and function value).
* The solution are stored in OptimizationMetric and have to be set in StepCircleOptimizer.
*
* Note: currently only the Nelder Mead and PedestrianOSM support this feature, but it should be easy to generalize
* this to other optimizer or more general pedestrian.
*/
@DataProcessorClass()
public class PedestrianMetricOptimizationProcessor extends DataProcessor<EventtimePedestrianIdKey, OptimizationMetric>{
public PedestrianMetricOptimizationProcessor() {
super("optX", "optY", "optFunc", "foundX", "foundY", "foundFunc");
}
@Override
protected void doUpdate(final SimulationState state) {
Collection<Pedestrian> pedestrians = state.getTopography().getPedestrianDynamicElements().getElements();
for (Pedestrian pedestrian : pedestrians) {
ArrayList<OptimizationMetric> pedestrianMetrics = ((PedestrianOSM) pedestrian).getOptimizationMetricElements();
if(pedestrianMetrics == null){
throw new RuntimeException("Pedestrian OptimizationMetric is null. This means that there the " +
"configuration is not to measure the quality is not active.");
}else if (!pedestrianMetrics.isEmpty()) {
for (OptimizationMetric singleMetric : pedestrianMetrics) {
putValue(new EventtimePedestrianIdKey(singleMetric.getSimTime(), singleMetric.getPedId()), singleMetric);
}
} //else (if empty) do nothing, no event occured for this pedestrians in the last simulation step.
}
}
@Override
public void init(final ProcessorManager manager) {
super.init(manager);
}
@Override
public String[] toStrings(EventtimePedestrianIdKey key) {
OptimizationMetric metric = this.getValue(key);
return metric.getValueString();
}
}
......@@ -83,10 +83,10 @@ public class Pedestrian extends Agent {
trajectory.clear();
}
// Getter
public VTrajectory getFootSteps() {
return trajectory;
}
// Getter
public int getIdAsTarget() {
return this.idAsTarget;
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment