google / or-tools

Google's Operations Research tools:
https://developers.google.com/optimization/
Apache License 2.0
11.28k stars 2.13k forks source link

Endless loop when calling AddDimensionDependentDimensionWithVehicleCapacity #713

Closed fragilelambda closed 6 years ago

fragilelambda commented 6 years ago

I'm doing some experiments for the dependent dimension, and I change the code in example prptw.cc trying to make a dependent dimension which works similar to demand dimension. And here is what I wrote:

RoutingModel::StateDependentTransit ClientWaitTime(
  RoutingModel::NodeIndex from,
  RoutingModel::NodeIndex to) {
    std::function<int64(int64)> transit = [](int64 load)->int64 {
         return 1;
    };
    return RoutingModel::MakeStateDependentTransit(transit, 0, 100);
}

Add dependent dimension:

const RoutingDimension& demand_dimension = routing.GetDimensionOrDie("demand");
routing.AddDimensionDependentDimensionWithVehicleCapacity(
    NewPermanentCallback(&ClientWaitTime),
    &demand_dimension,
    0, 100, true, "load");

These Code compile and build successfully but then fall into endless loop. Is there an example for dependent dimension usage, or any suggestion on what I did wrong? Thank you!

Mizux commented 6 years ago

Are you sure your data problem as an easy First Solution ? Sometime solver can take time to find the first one...

I'll try to reproduce it...

NOTE FOR MYSELF

See if caching the StateDependentTransit will increase performance over creating a new one each time the callbakc is call...

In fact you must create a function of type VariableNodeEvaluator2* which is

typedef ResultCallback2<StateDependentTransit, NodeIndex, NodeIndex>  VariableNodeEvaluator2;

src: https://github.com/google/or-tools/blob/master/ortools/constraint_solver/routing.h#L244

Supposing your Foo dimension has for one part a pure transit cost (i.e. independent) and an other part is dependent of an other dimension Bar. i.e. the following prototype

  bool AddDimensionDependentDimensionWithVehicleCapacity(
      NodeEvaluator2* pure_transits, VariableNodeEvaluator2* dependent_transits,
      const RoutingDimension* base_dimension, int64 slack_max,
      int64 vehicle_capacity, bool fix_start_cumul_to_zero, const std::string& name);

The code should be:

// Pure transit callback
class FooContainer {
  public:
  FooContainer();
  int64 pureTransitCost(
    operations_research::RoutingModel::NodeIndex from,
    operations_research::RoutingModel::NodeIndex to) const {
    return 1;
   }
  };

// Bar dependent transit callback
class FooBarContainer {
  public:
  FooBarContainer();
  int64 dependentCost(int64 barCumul) { return barCumul;}
  StateDependentTransit dependentTransitCost(
    operations_research::RoutingModel::NodeIndex from,
    operations_research::RoutingModel::NodeIndex to) const {
      return RoutingModel::MakeStateDependentTransit(
        NewPermanentCallback(this, &FooBarContainer::dependentCost),
        0,
        MaxCapacity);
  }
};
...
main() {
  ...
  const RoutingDimension& bar = routing.GetDimensionOrDie("Bar");
  // Instanciate our Simple Foo Callback
  FooContainer foo();
  // Instanciate our FooBar Callback
  FooBarContainer fooobar();

  // Create our dimension using both callback
  model->AddDimension(
    NewPermanentCallback(&foo, &FooContainer::pureTransitCost),
    NewPermanentCallback(&foobar, &FooContainer::dependentTransitCost),
    &bar,
    MaxSlack,
    MaxCapacity,
    /*cumul_var_to_zero*/true,
    "Foo"
 );
fragilelambda commented 6 years ago

After I reduce the problem size, and set routing_solution_limit 1, I got one solution about five minutes, which is 10s before I add this dimension. But after all, it's not an endless loop, I will try to see if I can reduce some time. Thank you for your help!