Commit eb486382 authored by BZoennchen's avatar BZoennchen

introduce our own Nelder-Mead implementation into the master branch.

parent 9b1a4add
......@@ -121,6 +121,11 @@ public class AVertex<P extends IPoint> implements IVertex<P>, Cloneable {
return id+"";
}
@Override
public IPoint add(double x, double y) {
return point.add(x, y);
}
@Override
public IPoint norm(double len) {
return point.norm(len);
......
......@@ -72,6 +72,11 @@ public class PVertex<P extends IPoint> implements IVertex<P> {
// TODO: make it protected since it is a non-deep copy. Therefore the IVertex should maybe not be a IPoint!?
@Override
public IPoint add(double x, double y) {
return point.add(x, y);
}
@Override
public IPoint norm(double len) {
return point.norm(len);
......
......@@ -26,7 +26,7 @@
"navigationCluster" : false,
"navigationFollower" : false,
"directionWallDistance" : false,
"tangentialEvasion" : true,
"tangentialEvasion" : false,
"sidewaysEvasion" : false,
"onlyEvadeContraFlow" : false,
"makeSmallSteps" : false,
......@@ -69,7 +69,7 @@
"queueWidthLoading" : 1.0,
"pedestrianDynamicWeight" : 6.0,
"loadingType" : "CONSTANT",
"width" : 0.4,
"width" : 1.0,
"height" : 5.0
}
}
......
......@@ -33,8 +33,8 @@
"org.vadere.state.attributes.models.AttributesOSM" : {
"stepCircleResolution" : 4,
"numberOfCircles" : 1,
"optimizationType" : "NELDER_MEAD",
"varyStepDirection" : true,
"optimizationType" : "NELDER_MEAD_CIRCLE",
"varyStepDirection" : false,
"movementType" : "ARBITRARY",
"stepLengthIntercept" : 0.4625,
"stepLengthSlopeSpeed" : 0.2345,
......@@ -88,8 +88,8 @@
},
"obstacles" : [ {
"shape" : {
"x" : 0.5,
"y" : 11.0,
"x" : 0.2,
"y" : 10.7,
"width" : 33.5,
"height" : 1.0,
"type" : "RECTANGLE"
......@@ -97,7 +97,7 @@
"id" : -1
}, {
"shape" : {
"x" : 1.0,
"x" : 1.3,
"y" : 12.5,
"width" : 33.6,
"height" : 1.0,
......@@ -106,7 +106,7 @@
"id" : -1
}, {
"shape" : {
"x" : 1.0,
"x" : 1.3,
"y" : 12.5,
"width" : 1.0,
"height" : 19.5,
......@@ -115,55 +115,55 @@
"id" : -1
}, {
"shape" : {
"x" : 1.0,
"x" : 1.3,
"y" : 31.0,
"width" : 33.0,
"width" : 32.4,
"height" : 1.0,
"type" : "RECTANGLE"
},
"id" : -1
}, {
"shape" : {
"x" : 33.0,
"y" : 14.0,
"x" : 32.5,
"y" : 14.3,
"width" : 1.0,
"height" : 18.0,
"height" : 13.8,
"type" : "RECTANGLE"
},
"id" : -1
}, {
"shape" : {
"x" : 2.5,
"y" : 14.0,
"width" : 31.4,
"x" : 3.1,
"y" : 14.3,
"width" : 30.4,
"height" : 1.0,
"type" : "RECTANGLE"
},
"id" : -1
}, {
"shape" : {
"x" : 2.5,
"y" : 14.0,
"x" : 3.1,
"y" : 14.3,
"width" : 0.9,
"height" : 16.4,
"height" : 15.8,
"type" : "RECTANGLE"
},
"id" : -1
}, {
"shape" : {
"x" : 2.5,
"y" : 29.5,
"width" : 30.0,
"x" : 3.1,
"y" : 29.2,
"width" : 32.0,
"height" : 1.0,
"type" : "RECTANGLE"
},
"id" : -1
}, {
"shape" : {
"x" : 24.0,
"y" : 16.0,
"x" : 24.1,
"y" : 16.7,
"width" : 7.1,
"height" : 12.5,
"height" : 11.0,
"type" : "RECTANGLE"
},
"id" : -1
......
......@@ -16,8 +16,10 @@ import org.vadere.simulator.models.groups.cgm.CentroidGroupStepSizeAdjuster;
import org.vadere.simulator.models.groups.cgm.CentroidGroupModel;
import org.vadere.simulator.models.groups.cgm.CentroidGroupPotential;
import org.vadere.simulator.models.osm.optimization.ParticleSwarmOptimizer;
import org.vadere.simulator.models.osm.optimization.PatternSearchOptimizer;
import org.vadere.simulator.models.osm.optimization.StepCircleOptimizer;
import org.vadere.simulator.models.osm.optimization.StepCircleOptimizerBrent;
import org.vadere.simulator.models.osm.optimization.StepCircleOptimizerCircleNelderMead;
import org.vadere.simulator.models.osm.optimization.StepCircleOptimizerDiscrete;
import org.vadere.simulator.models.osm.optimization.StepCircleOptimizerEvolStrat;
import org.vadere.simulator.models.osm.optimization.StepCircleOptimizerGradient;
......@@ -250,12 +252,18 @@ public class OptimalStepsModel implements MainModel, PotentialFieldModel {
case NELDER_MEAD:
result = new StepCircleOptimizerNelderMead(random);
break;
case NELDER_MEAD_CIRCLE:
result = new StepCircleOptimizerCircleNelderMead(random, attributesOSM);
break;
case POWELL:
result = new StepCircleOptimizerPowell(random);
break;
case PSO:
result = new ParticleSwarmOptimizer(movementThreshold, random);
break;
case PATTERN_SEARCH:
result = new PatternSearchOptimizer(movementThreshold, attributesOSM, random);
break;
case GRADIENT:
result = new StepCircleOptimizerGradient(topography,
potentialFieldTarget, attributesOSM);
......
......@@ -9,13 +9,15 @@ import org.vadere.util.geometry.shapes.VCircle;
import org.vadere.util.geometry.shapes.VCircleSector;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.math.MathUtil;
import org.vadere.util.math.pso.PSO;
import org.vadere.util.math.optimization.pso.PSO;
import java.awt.Shape;
import java.util.*;
import java.util.List;
/**
* This class implements a particle swarm optimization which is not jet tested extensively.
*
* @author Benedikt Zoennchen
*/
public class ParticleSwarmOptimizer implements StepCircleOptimizer {
......
package org.vadere.simulator.models.osm.optimization;
import org.jetbrains.annotations.NotNull;
import org.vadere.simulator.models.osm.PedestrianOSM;
import org.vadere.state.attributes.models.AttributesOSM;
import org.vadere.util.geometry.GeometryUtils;
import org.vadere.util.geometry.shapes.VCircle;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.math.optimization.PatternSearch;
import java.awt.*;
import java.util.List;
import java.util.Random;
/**
* This is a implementation of https://en.wikipedia.org/wiki/Pattern_search_(optimization).
* It is not jet tested extensively!
*
* @author Benedikt Zoennchen
*/
public class PatternSearchOptimizer implements StepCircleOptimizer {
private final double distanceThreshold;
private final AttributesOSM attributesOSM;
private final List<VPoint> basePoint;
private final double edgeLen = 0.3;
public PatternSearchOptimizer(final double distanceThreshold, @NotNull final AttributesOSM attributesOSM, @NotNull final Random random) {
this.distanceThreshold = distanceThreshold;
this.attributesOSM = attributesOSM;
this.basePoint = GeometryUtils.getDiscDiscretizationGridPoints(new VCircle(new VPoint(0,0), 1.0 ), 1.0 / 2);
//this.basePoint = GeometryUtils.getDiscDiscretizationPoints(random, false, new VCircle(new VPoint(0,0), 1.0), attributesOSM.getNumberOfCircles(), attributesOSM.getStepCircleResolution(), 0, 2*Math.PI);
}
@Override
public VPoint getNextPosition(PedestrianOSM ped, Shape reachableArea) {
assert reachableArea instanceof VCircle;
VCircle circle = (VCircle) reachableArea;
//PatternSearch patternSearch = new PatternSearch(circle, pos -> ped.getPotential(pos), 0.01, basePoint, attributesOSM.getNumberOfCircles(), attributesOSM.getStepCircleResolution());
PatternSearch patternSearch = new PatternSearch(circle, pos -> ped.getPotential(pos), distanceThreshold, basePoint, circle.getRadius());
patternSearch.optimize();
return patternSearch.getArgMin();
}
@Override
public StepCircleOptimizer clone() {
return this;
}
}
package org.vadere.simulator.models.osm.optimization;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.NotNull;
import org.vadere.simulator.models.osm.PedestrianOSM;
import org.vadere.state.attributes.models.AttributesOSM;
import org.vadere.util.geometry.GeometryUtils;
import org.vadere.util.geometry.shapes.IPoint;
import org.vadere.util.geometry.shapes.VCircle;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.VTriangle;
import org.vadere.util.logging.Logger;
import org.vadere.util.math.optimization.neldermead.NelderMead1D;
import org.vadere.util.math.optimization.neldermead.NelderMead2D;
import java.awt.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import java.util.function.Function;
public class StepCircleOptimizerCircleNelderMead implements StepCircleOptimizer {
private static Logger logger = Logger
.getLogger(StepCircleOptimizerCircleNelderMead.class);
private static double threshold = 0.01;
private final Random random;
private final AttributesOSM attributesOSM;
public StepCircleOptimizerCircleNelderMead(@NotNull final Random random, @NotNull final AttributesOSM attributesOSM) {
this.random = random;
this.attributesOSM = attributesOSM;
}
private VPoint getNextPosition1D(@NotNull final PedestrianOSM ped, @NotNull final Shape reachableArea) {
final VCircle stepCircle = (VCircle)reachableArea;
Function<IPoint, Double> eval = pos -> ped.getPotential(pos);
Collection<Pair<Double, Double>> simplices = generate1DSimplexes(ped, stepCircle);
NelderMead1D nelderMead = new NelderMead1D(stepCircle, eval, threshold, simplices);
nelderMead.optimize();
return nelderMead.getArg();
}
@Override
public VPoint getNextPosition(@NotNull final PedestrianOSM ped, @NotNull final Shape reachableArea) {
VPoint point1D = getNextPosition1D(ped, reachableArea);
VPoint point2D = getNextPosition2D(ped, reachableArea);
double oneDimResult = ped.getPotential(point1D);
double twoDimResult = ped.getPotential(point2D);
double currentPotential = ped.getPotential(ped.getPosition());
if(oneDimResult < twoDimResult && oneDimResult < currentPotential) {
return point1D;
} else if(twoDimResult < currentPotential) {
return point2D;
} else {
return ped.getPosition();
}
//return point2D;
}
private List<Pair<Double, Double>> generate1DSimplexes(@NotNull final PedestrianOSM ped, @NotNull final VCircle stepCircle) {
int numberOfCircles = ped.getAttributesOSM().getStepCircleResolution();
double alpha = 2*Math.PI / (2.0*numberOfCircles);
List<Pair<Double, Double>> simplexes = new ArrayList<>(numberOfCircles);
for(int i = 0; i < ped.getAttributesOSM().getStepCircleResolution(); i++) {
simplexes.add(Pair.of(alpha*(2*i), alpha*(2*i+1)));
}
return simplexes;
}
private List<VTriangle> generate2DSimplexes(@NotNull final PedestrianOSM ped, @NotNull final VCircle stepCircle) {
double step = stepCircle.getRadius() / 3.0;
assert ped.getAttributesOSM().getNumberOfCircles() == 1;
List<VPoint> positions = StepCircleOptimizerDiscrete.getReachablePositions(ped, stepCircle, random);
List<VTriangle> simplices = new ArrayList<>(positions.size());
VPoint pos = ped.getPosition();
double angle = 2.0/3.0*Math.PI;
VPoint dir = new VPoint(0, ped.getRadius() / 2);
VPoint p1 = ped.getPosition().add(dir);
VPoint p2 = ped.getPosition().add(dir.rotate(angle));
VPoint p3 = ped.getPosition().add(dir.rotate(-angle));
simplices.add(new VTriangle(p1, p2, p3));
for(int i = 0; i < positions.size(); i++) {
double innerDistance = ped.getPosition().distance((positions.get(i)));
VPoint innerDirection = positions.get(i).subtract(pos).scalarMultiply(1.0 / innerDistance);
int j = (i+1) % positions.size();
VPoint anotherPoint = positions.get(j);
double outerDistance = anotherPoint.distance((ped.getPosition()));
VPoint outerDirection = positions.get(j).subtract(pos).scalarMultiply(1.0 / outerDistance);
double x1 = Math.min(step, innerDistance) * innerDirection.getX();
double y1 = Math.min(step, innerDistance) * innerDirection.getY();
double x2 = Math.min(step, outerDistance) * outerDirection.getX();
double y2 = Math.min(step, outerDistance) * outerDirection.getY();
VPoint d = new VPoint(x1+x2, y1+y2).setMagnitude(step);
p1 = pos.add(d);
p2 = p1.add(x1, y1);
p3 = p1.add(x2, y2);
simplices.add(new VTriangle(p1, p2, p3));
}
return simplices;
}
//List<VPoint> positions = StepCircleOptimizerDiscrete.getReachablePositions(pedestrian, (VCircle)reachableArea, random);
private VPoint getNextPosition2D(@NotNull final PedestrianOSM ped, @NotNull final Shape reachableArea) {
final VCircle stepCircle = (VCircle)reachableArea;
Function<IPoint, Double> eval = pos -> {
if(stepCircle.contains(pos)) {
return ped.getPotential(pos);
}
else {
return NelderMead2D.MAX_VAL;
}
};
/*double radius = stepCircle.getRadius() / 2;
List<VPoint> positions = StepCircleOptimizerDiscrete.getReachablePositions(ped, new VCircle(stepCircle.getCenter(), radius), random);
List<VTriangle> simplices = new ArrayList<>(positions.size());
int numberOfCircles = ped.getAttributesOSM().getNumberOfCircles();
double radDist = radius / numberOfCircles;
for(int i = 0; i < positions.size(); i++) {
VPoint p1 = positions.get(i);
VPoint p2 = positions.get((i+1) % positions.size());
double dist = p1.distance(stepCircle.getCenter());
VPoint midPoint = new VLine(p1, p2).midPoint();
VPoint p3;
double circRadius = ped.getPosition().distance(p1);
if(Math.abs(circRadius-radDist) > GeometryUtils.DOUBLE_EPS) {
VPoint e = midPoint.subtract(ped.getPosition()).setMagnitude(circRadius-radDist);
p3 = ped.getPosition().add(e);
} else {
p3 = ped.getPosition();
}
simplices.add(new VTriangle(p1, p2, p3));
}
double angle = 2.0/3.0*Math.PI;
VPoint dir = new VPoint(0, ped.getRadius());
VPoint p1 = ped.getPosition().add(dir);
VPoint p2 = ped.getPosition().add(dir.rotate(angle));
VPoint p3 = ped.getPosition().add(dir.rotate(-angle));
simplices.add(new VTriangle(p1, p2, p3));*/
List<VTriangle> simplicies = generate2DSimplexes(ped, (VCircle) reachableArea);
NelderMead2D nelderMead = new NelderMead2D(stepCircle, eval, threshold, simplicies);
String string = nelderMead.toString();
//System.out.println(string);
nelderMead.optimize();
assert stepCircle.signedDistance(nelderMead.getArg()) < GeometryUtils.DOUBLE_EPS;
return nelderMead.getArg();
}
/*private VPoint getNextPosition2D(@NotNull final PedestrianOSM ped, @NotNull final Shape reachableArea) {
final VCircle stepCircle = (VCircle)reachableArea;
Function<IPoint, Double> eval = pos -> {
if(stepCircle.contains(pos)) {
return ped.getPotential(pos);
}
else {
return 10000.0;
}
};
// generate start simplices
double l = stepCircle.getRadius() * 0.5;
double s = l * Math.sqrt(3);
VPoint pos = ped.getPosition();
double h = Math.sqrt(3) * 0.5 * s;
VPoint p1 = new VPoint(0, l);
VPoint p2 = p1.subtract(new VPoint(s * 0.5, h));
VPoint p3 = p1.subtract(new VPoint(-s * 0.5, h));
if(attributesOSM.isVaryStepDirection()) {
double delta = random.nextDouble() * 2 * Math.PI;
p1 = p1.rotate(delta);
p2 = p2.rotate(delta);
p3 = p3.rotate(delta);
}
p1 = p1.add(pos);
p2 = p2.add(pos);
p3 = p3.add(pos);
Collection<VTriangle> simplices = Collections.singletonList(new VTriangle(p1, p2, p3));
NelderMead2D nelderMead = new NelderMead2D(stepCircle, eval, attributesOSM.getMovementThreshold(), simplices);
nelderMead.optimize();
assert stepCircle.contains(nelderMead.getArg());
return nelderMead.getArg();
}*/
@Override
public StepCircleOptimizerCircleNelderMead clone() {
return new StepCircleOptimizerCircleNelderMead(random, attributesOSM);
}
}
......@@ -195,6 +195,11 @@ public class UpdateSchemeEventDrivenParallel extends UpdateSchemeEventDriven {
return this.point.add(point);
}
@Override
public IPoint add(double x, double y) {
return point.add(x, y);
}
@Override
public IPoint addPrecise(IPoint point) {
return this.point.addPrecise(point);
......
package org.vadere.state.types;
public enum OptimizationType {
PSO, NELDER_MEAD, POWELL, EVOLUTION_STRATEGY, BRENT, GRADIENT, DISCRETE, NONE
PATTERN_SEARCH, PSO, NELDER_MEAD, NELDER_MEAD_CIRCLE, POWELL, EVOLUTION_STRATEGY, BRENT, GRADIENT, DISCRETE, NONE
}
......@@ -167,6 +167,31 @@ public class GeometryUtils {
return circumcenter.distance(point) < circumcenter.distance(p1);
}
public static List<VPoint> getDiscDiscretizationPoints(
@NotNull final VCircle circle,
final int numberOfCircles,
final int numberOfPointsOfLargestCircle,
final double anchorAngle,
final double angle) {
return getDiscDiscretizationPoints(null, false, circle, numberOfCircles, numberOfPointsOfLargestCircle, anchorAngle, angle);
}
public static List<VPoint> getDiscDiscretizationGridPoints(@Nullable final VCircle circle, double edgeLen) {
int n = (int)(circle.getRadius() * 2 / edgeLen) + 1;
List<VPoint> points = new ArrayList<>(n * n);
double x = circle.getCenter().x - circle.getRadius();
double y = circle.getCenter().y - circle.getRadius();
for (int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
VPoint point = new VPoint(x+i * edgeLen, y+j * edgeLen);
if(circle.contains(point)) {
points.add(point);
}
}
}
return points;
}
/**
* Generates a set of points which are positioned inside a disc segment. The points are placed equidistant on one or multiple circles
* with the center at the center of the disc and the radius smaller or equals the radius of the disc.
......@@ -188,7 +213,8 @@ public class GeometryUtils {
final int numberOfPointsOfLargestCircle,
final double anchorAngle,
final double angle) {
assert !varyDirection || random != null;
assert random != null || !varyDirection;
double randOffset = varyDirection ? random.nextDouble() : 0;
List<VPoint> reachablePositions = new ArrayList<>();
......@@ -221,14 +247,6 @@ public class GeometryUtils {
return reachablePositions;
}
public static List<VPoint> getDiscDiscretizationPoints(
@NotNull final VCircle circle,
final int numberOfCircles,
final int numberOfPointsOfLargestCircle,
final double anchorAngle,
final double angle) {
return getDiscDiscretizationPoints(null, false, circle, numberOfCircles, numberOfPointsOfLargestCircle, anchorAngle, angle);
}
/**
* Computes the point on the line segment that is closest to the given point
......
......@@ -14,6 +14,8 @@ public interface IPoint extends Cloneable {
IPoint add(final IPoint point);
IPoint add(final double x, final double y);
IPoint addPrecise(final IPoint point);
IPoint subtract(final IPoint point);
......
......@@ -34,7 +34,14 @@ public class MPoint implements org.vadere.util.geometry.shapes.IPoint, Cloneable
return this;
}
@Override
@Override
public IPoint add(double x, double y) {
this.point = this.point.add(x, y);
hashCode = -1;
return this;
}
@Override
public MPoint addPrecise(final IPoint point) {
this.point = this.point.addPrecise(point);
hashCode = -1;
......
......@@ -56,6 +56,10 @@ public class VCircle implements VShape, ICircleSector {
return Math.abs(this.center.distance(pos) - this.radius);
}
public double signedDistance(IPoint pos) {
return this.center.distance(pos) - this.radius;
}
public VPoint getCenter() {
return this.center;
}
......@@ -139,6 +143,39 @@ public class VCircle implements VShape, ICircleSector {
return intersectionPoints.stream().min(Comparator.comparingDouble(point -> point.distance(r)));
}
/**
* Assumption: there exist a intersection point of the line segment.
*
* @param x11
* @param y11
* @param x22
* @param y22
* @return
*/
public Optional<VPoint> getSegmentIntersectionPoints(final double x11, final double y11, final double x22, final double y22) {
ImmutableList<VPoint> list = getIntersectionPoints(x11, y11, x22, y22);
assert !list.isEmpty();
if(x11 == x22) {
for (VPoint point : list) {
if(point.y < y11 && point.y > y22 || point.y > y11 && point.y < y22) {
return Optional.of(point);
}
}
} else {
for (VPoint point : list) {
if(point.x < x11 && point.x > x22 || point.x > x11 && point.x < x22) {
return Optional.of(point);
}
}
}
return Optional.empty();
// throw new IllegalArgumentException("line segment ("+x11+","+y11+") - ("+x22+","+y22+") does not intersect circle " + this + ".");
}
@Override
public boolean intersectsLine(double x1, double y1, double x2, double y2) {
return intersects(new VLine(x1, y1, x2, y2));
......
......@@ -137,6 +137,11 @@ public class VPoint implements Cloneable, IPoint {
return new VPoint(x + point.getX(), y + point.getY());
}
@Override
public VPoint add(double x, double y) {
return new VPoint(this.x + x, this.y + y);
}
@Override
public VPoint addPrecise(final IPoint point) {
return VPoint.addPrecise(this, point);
......@@ -217,7 +222,7 @@ public class VPoint implements Cloneable, IPoint {
@Override
public String toString() {
return "(" + x + "," + y + ")";
return "[" + x + "," + y + "]";
}
public static VPoint addPrecise(final IPoint p1, final IPoint p2) {
......
......@@ -216,6 +216,6 @@ public class VTriangle extends VPolygon {
@Override
public String toString() {
return p1 + "-" + p2 + "-" + p3;
return "["+p1 + "," + p2 + "," + p3 + "]";
}
}
package org.vadere.util.math.optimization;
import org.apache.commons.math3.util.Pair;
import org.jetbrains.annotations.NotNull;
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;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.PriorityQueue;
import java.util.function.Function;