Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1473,6 +1473,20 @@ routing.solve(
)
```

Set a native time limit for route restoration after closing the model. The limit
is in seconds and accepts fractional values:

```ruby
routing.close_model
routing.update_time_limit(0.5)
assignment = routing.read_assignment_from_routes(routes, true)
```

The limit is checked cooperatively, so the call can exceed it. Restoration can
return a valid assignment found before the limit. A `nil` result does not
distinguish an invalid route from a limit reached before finding a solution.
A subsequent solve uses the time limit in its own search parameters.

Native improvement-rate stopping is available through search parameters:

```ruby
Expand Down
9 changes: 9 additions & 0 deletions ext/or-tools/routing.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <algorithm>
#include <cmath>
#include <memory>
#include <mutex>
#include <optional>
Expand Down Expand Up @@ -714,6 +715,14 @@ void init_routing(Rice::Module& m) {
self.CloseModel();
}
})
.define_method(
"update_time_limit",
[](RoutingModel& self, double seconds) {
if (!std::isfinite(seconds) || seconds < 0) {
throw std::invalid_argument{"time limit must be finite and nonnegative"};
}
self.UpdateTimeLimit(absl::Seconds(seconds));
})
.define_method(
"_enable_solution_trace",
[](RoutingModel& self, int64_t max_samples, int64_t sample_interval_ms) {
Expand Down
55 changes: 55 additions & 0 deletions test/routing_time_limit_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
require_relative "test_helper"

class RoutingTimeLimitTest < Minitest::Test
def test_restoration_limit_can_be_reset_after_timeout
routing = build_routing
routing.close_model
routing.update_time_limit(0)

assert_nil routing.read_assignment_from_routes([[1, 2]], true)
assert_equal :fail, routing.status

routing.update_time_limit(0.5)
assignment = routing.read_assignment_from_routes([[1, 2]], true)

refute_nil assignment
assert_equal 4, assignment.objective_value
assert_equal :success, routing.status
end

def test_solve_replaces_restoration_time_limit
routing = build_routing
routing.close_model
routing.update_time_limit(0.5)
assignment = routing.read_assignment_from_routes([[1, 2]], true)
refute_nil assignment

routing.update_time_limit(0)
parameters = ORTools.default_routing_search_parameters
parameters.time_limit = 1
parameters.solution_limit = 1
solution = routing.solve_from_assignment_with_parameters(assignment, parameters)

refute_nil solution
assert_equal 4, solution.objective_value
end

def test_rejects_invalid_time_limits
routing = build_routing

[-1, Float::INFINITY, -Float::INFINITY, Float::NAN].each do |seconds|
error = assert_raises(ArgumentError) { routing.update_time_limit(seconds) }
assert_equal "time limit must be finite and nonnegative", error.message
end
end

private

def build_routing
manager = ORTools::RoutingIndexManager.new(3, 1, 0)
routing = ORTools::RoutingModel.new(manager)
transit = routing.register_transit_matrix([[0, 1, 2], [1, 0, 1], [2, 1, 0]])
routing.set_arc_cost_evaluator_of_all_vehicles(transit)
routing
end
end
Loading