Package org.osm2world.core.map_elevation.creation

Source Code of org.osm2world.core.map_elevation.creation.LPEleConstraintEnforcer

package org.osm2world.core.map_elevation.creation;

import static org.osm2world.core.map_elevation.data.GroundState.ON;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import net.sf.javailp.Linear;
import net.sf.javailp.OptType;
import net.sf.javailp.Problem;
import net.sf.javailp.Result;
import net.sf.javailp.Solver;
import net.sf.javailp.SolverFactory;
import net.sf.javailp.SolverFactoryLpSolve;

import org.osm2world.core.map_elevation.data.EleConnector;
import org.osm2world.core.map_elevation.data.LPVariablePair;
import org.osm2world.core.math.VectorXYZ;
import org.osm2world.core.math.VectorXZ;
import org.osm2world.core.util.FaultTolerantIterationUtil;
import org.osm2world.core.util.FaultTolerantIterationUtil.Operation;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;

/**
* enforces constraints using linear programming
*/
public class LPEleConstraintEnforcer implements EleConstraintEnforcer {

  private final Problem problem;
 
  private final List<LPVariablePair> variables;
  private final Map<EleConnector, LPVariablePair> variableMap;

 
  public LPEleConstraintEnforcer() {
   
    problem = new Problem();
   
    variables = new ArrayList<LPVariablePair>();
    variableMap = new HashMap<EleConnector, LPVariablePair>();
   
  }
 
  @Override
  public void addConnectors(Iterable<EleConnector> connectors) {
   
    //TODO: calling this multiple times does not work yet; connectors from different invocations would not be joined
   
    for (LPVariablePair v : createVariables(connectors)) {
     
      variables.add(v);
     
      for (EleConnector c : v.getConnectors()) {
        variableMap.put(c, v);
      }
     
    }
   
  }
 
  /**
   * creates variables for connectors. Joins connected connectors by
   * representing their elevation with the same {@link LPVariablePair}.
   */
  private static Collection<LPVariablePair> createVariables(
      Iterable<EleConnector> connectors) {
   
    Multimap<VectorXZ, LPVariablePair> variablePositionMap =
        HashMultimap.create();
   
    for (EleConnector c : connectors) {
     
      Collection<LPVariablePair> existingVariables =
          variablePositionMap.get(c.pos);
     
      List<LPVariablePair> matchingVariables =
          new ArrayList<LPVariablePair>();
     
      if (existingVariables != null) {
        for (LPVariablePair v : existingVariables) {
          if (v.connectsTo(c)) {
            matchingVariables.add(v);
          }
        }
      }
     
      if (matchingVariables.isEmpty()) {
       
        /* create a new variable because no existing one fits */
       
        variablePositionMap.put(c.pos, new LPVariablePair(c));
       
      } else {
       
        /* add connector to one of the matching variables */
       
        LPVariablePair vHead = matchingVariables.get(0);
        vHead.add(c);
       
        /* merge other matching variables into that one */
       
        for (int i = 1; i < matchingVariables.size(); i++) {
         
          LPVariablePair v = matchingVariables.get(i);
         
          vHead.addAll(v);
          variablePositionMap.remove(c.pos, v);
         
        }
       
      }
     
    }
   
    return variablePositionMap.values();
   
  }
 
  @Override
  public void requireSameEle(EleConnector c1, EleConnector c2) {
   
    //FIXME: this doesn't work because of the different interpolated elevations
    // - the pos/neg variables are different!
   
    /* instead of adding an actual constraint, the variables are merged.
     * This reduces the total number of variables and constraints. */
    /*
    LPVariablePair v1 = variableMap.get(c1);
    LPVariablePair v2 = variableMap.get(c2);
   
    if (v1 == v2) { return; }
   
    // move all connectors from v2 onto v1
   
    for (EleConnector connector : v2.getConnectors()) {
      variableMap.put(connector, v1);
    }
   
    v1.addAll(v2);
    variables.remove(v1);
   
    // replace previous occurrences of v2
   
    for (Constraint constraint : problem.getConstraints()) {
     
      List<Object> variables = constraint.getLhs().getVariables();
     
      for (int i = 0; i < variables.size(); i++) {
        if (variables.get(i) == v2.posVar()) {
          variables.set(i, v1.posVar());
        }
        if (variables.get(i) == v2.negVar()) {
          variables.set(i, v1.negVar());
        }
      }
     
    }
    */
    //TODO: merge joinedConnectors instead? Problem: different xz, and possibly terrain ele
    //TODO: but add appropriate weighting to the objective term!!
   
    addConstraint(
         1, c1,
        -1, c2,
        "=", 0);
   
  }
 
  @Override
  public void requireSameEle(Iterable<EleConnector> cs) {
   
    Iterator<EleConnector> csIterator = cs.iterator();
   
    if (csIterator.hasNext()) {
   
      EleConnector c = csIterator.next();
   
      while (csIterator.hasNext()) {
        requireSameEle(c, csIterator.next());
      }
     
    }
   
  }
 
  @Override
  public void requireVerticalDistance(ConstraintType type, double distance,
      EleConnector upper, EleConnector base1, EleConnector base2) {
   
    double dist1 = base1.pos.distanceTo(upper.pos);
    double dist2 = base2.pos.distanceTo(upper.pos);
   
    addConstraint(
         1, upper,
        -(dist2 / (dist1 + dist2)), base1,
        -(dist1 / (dist1 + dist2)), base2,
        getOperator(type),
        distance);
   
  }
 
  @Override
  public void requireVerticalDistance(ConstraintType type, double distance,
      EleConnector upper, EleConnector lower) {
   
    addConstraint(
         1, upper,
        -1, lower,
        getOperator(type),
        distance);
   
  }
 
  @Override
  public void requireIncline(ConstraintType type, double incline,
      List<EleConnector> cs) {
   
    for (int i = 0; i+1 < cs.size(); i++) {
     
      addConstraint(
           1, cs.get(i+1),
          -1, cs.get(i),
          getOperator(type),
          incline * cs.get(i).pos.distanceTo(cs.get(i+1).pos));
     
    }
   
  }
 
  @Override
  public void requireSmoothness(EleConnector from,
      EleConnector via, EleConnector to) {
   
    /* TODO restore
   
    double maxInclineDiffPerMeter = 0.5 / 100;
   
    double dist12 = from.pos.distanceTo(via.pos);
    double dist23 = via.pos.distanceTo(to.pos);
   
    double maxInclineDiff = maxInclineDiffPerMeter * (dist12 + dist23);
   
    System.out.println(maxInclineDiff);
   
    //| - 1/dist12 * from + (1/dist12 + 1/dist23) * via - 1/dist23 * to |
    //   <= maxInclineDiff
   
    addConstraint(
        -1/dist12, from,
        1/dist12 + 1/dist23, via,
        -1/dist23, to,
        "<=", maxInclineDiff);
   
    addConstraint(
        -1/dist12, from,
        1/dist12 + 1/dist23, via,
        -1/dist23, to,
        ">=", -maxInclineDiff);
   
    */
   
  }
 
  private void addConstraint(
      double factor1, EleConnector var1,
      String op, double limit) {
   
    addConstraint(
        factor1, var1,
        0, null,
        0, null,
        op, limit);
   
  }
 
  private void addConstraint(
      double factor1, EleConnector var1,
      double factor2, EleConnector var2,
      String op, double limit) {
   
    addConstraint(
        factor1, var1,
        factor2, var2,
        0, null,
        op, limit);
   
  }
 
  private void addConstraint(
      double factor1, EleConnector var1,
      double factor2, EleConnector var2,
      double factor3, EleConnector var3,
      String op, double limit) {
   
    Linear linear = new Linear();
   
    double limitCorrection = 0;
   
    if (var1 != null) {
      LPVariablePair c1 = variableMap.get(var1);
      linear.add(factor1, c1.posVar());
      linear.add(-factor1, c1.negVar());
      limitCorrection += factor1 * var1.getPosXYZ().y;
    }
   
    if (var2 != null) {
      LPVariablePair c2 = variableMap.get(var2);
      linear.add(factor2, c2.posVar());
      linear.add(-factor2, c2.negVar());
      limitCorrection += factor2 * var2.getPosXYZ().y;
    }
   
    if (var3 != null) {
      LPVariablePair c3 = variableMap.get(var3);
      linear.add(factor3, c3.posVar());
      linear.add(-factor3, c3.negVar());
      limitCorrection += factor3 * var3.getPosXYZ().y;
    }
   
    problem.add(linear, op, limit - limitCorrection);
   
  }
 
  private static String getOperator(ConstraintType constraintType) {
   
    switch (constraintType) {
    case MIN: return ">=";
    case MAX: return "<=";
    case EXACT: return "=";
    default: throw new Error("unhandled constraint type");
    }
   
  }
 
  @Override
  public void enforceConstraints() {
   
    SolverFactory factory = new SolverFactoryLpSolve();
    factory.setParameter(Solver.VERBOSE, 0);
   
    problem.setObjective(constructObjective(), OptType.MIN);
   
    //TODO Relaxations relax = new Relaxations();
   
    final Solver solver = factory.get();
    final Result result = solver.solve(problem);
   
    if (result == null) {
      System.out.println("[ERROR]: cannot enforce constraints, no result for LP");
    } else {
     
      /* apply elevation values */
     
      FaultTolerantIterationUtil.iterate(variables, new Operation<LPVariablePair>() {
        @Override public void perform(LPVariablePair v) {
         
          VectorXYZ posXYZ = v.getPosXYZ().addY(
              + result.get(v.posVar()).doubleValue()
              - result.get(v.negVar()).doubleValue());
         
          v.setPosXYZ(posXYZ);
         
        }
      });
           
    }
   
  }
 
  private Linear constructObjective() {
 
    Linear objectiveLinear = new Linear();
   
    for (LPVariablePair v : variables) {
     
      if (v.getConnectors().get(0).groundState == ON) {
       
        objectiveLinear.add(1, v.posVar());
        objectiveLinear.add(1, v.negVar());
       
      }
     
    }
   
    return objectiveLinear;
   
  }
 
}
TOP

Related Classes of org.osm2world.core.map_elevation.creation.LPEleConstraintEnforcer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.