Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update ortools solvers #144

Merged
merged 7 commits into from
Jan 23, 2024
Merged

Update ortools solvers #144

merged 7 commits into from
Jan 23, 2024

Conversation

nhuet
Copy link
Contributor

@nhuet nhuet commented Jan 19, 2024

  • upgrade ortools dependency to >=9.8
  • upgrade python minimal version to >=3.8 (ortools 9.8 does not exist for python<3.8 and python 3.7 is not maintained anymore)
  • update knapsack solver based on ortools to adapt to the api changes
  • update pickup_vrp solver based on ortools
    • construct firstsolutionstrategy and localsearchmetaheuristic enum from corresponding ortools pseudo enum
    • construct mapping from integer values to ortools solver status from ortools code
    • adapt tests and plot functions to work directly with ResultStorage and GPDPSolution instead of specific tuple
    • merge solve_intern into solve and construct directly resultstorage on the fly (could be useful to connect logging d-o callbacks or callbacks for hyperparamaters tuning with optuna)
    • fix ResultStorage.add_solution (needed by the previous item)

nhuet added 7 commits January 23, 2024 10:21
…ortools

We can create dynamically the enumeration corresponding to
routing_enums_pb2.LocalSearchMetaheuristic and routing_enums_pb2.FirstSolutionStrategy.

We use them to feed init_model() so that the hyperparameter will be more
readable as a mere integer. We do not forget to cast them to the
corresponding integer when feeding the ortools routing model.

To be compatible with previous code, we allow the hyperparmeter to take
directly the integer value.
…blem

Between different versions of ortools, the meaning of the integer
returned by routing_model.status() changes.

So it is better to extract the description from the class RoutingModel
directly.

For instance, in ortools 9.5 we have (extracted from c++ code):

class RoutingModel {
 public:
  /// Status of the search.
  enum Status {
    /// Problem not solved yet (before calling RoutingModel::Solve()).
    ROUTING_NOT_SOLVED,
    /// Problem solved successfully after calling RoutingModel::Solve().
    ROUTING_SUCCESS,
    /// No solution found to the problem after calling RoutingModel::Solve().
    ROUTING_FAIL,
    /// Time limit reached before finding a solution with RoutingModel::Solve().
    ROUTING_FAIL_TIMEOUT,
    /// Model, model parameters or flags are not valid.
    ROUTING_INVALID,
    /// Problem proven to be infeasible.
    ROUTING_INFEASIBLE
  };
...
}

But in 9.8:

class RoutingModel {
 public:
  /// Status of the search.
  enum Status {
    /// Problem not solved yet (before calling RoutingModel::Solve()).
    ROUTING_NOT_SOLVED,
    /// Problem solved successfully after calling RoutingModel::Solve().
    ROUTING_SUCCESS,
    /// Problem solved successfully after calling RoutingModel::Solve(), except
    /// that a local optimum has not been reached. Leaving more time would allow
    /// improving the solution.
    ROUTING_PARTIAL_SUCCESS_LOCAL_OPTIMUM_NOT_REACHED,
    /// No solution found to the problem after calling RoutingModel::Solve().
    ROUTING_FAIL,
    /// Time limit reached before finding a solution with RoutingModel::Solve().
    ROUTING_FAIL_TIMEOUT,
    /// Model, model parameters or flags are not valid.
    ROUTING_INVALID,ROUTING_FAIL,
    /// Time limit reached before finding a solution with RoutingModel::Solve().
    ROUTING_FAIL_TIMEOUT,
    /// Model, model parameters or flags are not valid.
    ROUTING_INVALID,
    /// Problem proven to be infeasible.
    ROUTING_INFEASIBLE
  };
...

thus status==2 meant ROUTING_FAIL, and now ROUTING_PARTIAL_SUCCESS_LOCAL_OPTIMUM_NOT_REACHED
- was adding twice the solution sometimes
- was crashing if no solution already stored (self.max and min not
  existing yet)
It could happen if at the same time nb_solutions % 100 == 0 (in
particular for first solution)
The ortools solver solve() is now returning a ResultStorage filled with
GPDPSolution's.

- We adapt plot_ortools_solution() to GPDPSolution
- We replace check_solution() in
  tests/pickup_vrp/builders/test_instance_builders.py by a new method
  check_pickup_deliverable() in GPDPSolution
- We can now only use soler.solve() in tests
We also merge solve_intern() into solve().
The result_storage will be useful when implementing intermediate
callbacks (on d-o side), e.g. for efficient hyperparameters tuning with
optuna.
We update ortools frozen at 9.5 to 9.8

ortools api changes:
- KnapsackSolver can now be found in
  ortools.algorithms.python.knapsack_solver, and its methods are in lower
  snake-case
@nhuet nhuet marked this pull request as ready for review January 23, 2024 10:08
Copy link
Collaborator

@g-poveda g-poveda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look good to me, worth to note that if in practice the lack of autocompletion when using the ortools "enum" reduce too much the easiness of development we may want to add some helper functionnality.

@g-poveda g-poveda merged commit 5a0d2b4 into airbus:master Jan 23, 2024
16 checks passed
@nhuet nhuet deleted the ortools branch January 23, 2024 13:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants