revert last change on the routing API; report new best bound of the SAT solver in the log

This commit is contained in:
Laurent Perron
2018-12-14 14:25:52 +01:00
parent 887b150207
commit 39f30fb178
21 changed files with 122 additions and 94 deletions

View File

@@ -148,13 +148,13 @@ int main(int argc, char** argv) {
const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1);
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
order < manager.num_nodes(); ++order) {
std::vector<int> orders(1, manager.NodeToIndex(order));
std::vector<int64> orders(1, manager.NodeToIndex(order));
routing.AddDisjunction(orders, kPenalty);
}
// Adding same vehicle constraint costs for consecutive nodes.
if (FLAGS_vrp_use_same_vehicle_costs) {
std::vector<int> group;
std::vector<int64> group;
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
order < manager.num_nodes(); ++order) {
group.push_back(manager.NodeToIndex(order));

View File

@@ -134,13 +134,13 @@ int main(int argc, char** argv) {
const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1);
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
order < manager.num_nodes(); ++order) {
std::vector<int> orders(1, manager.NodeToIndex(order));
std::vector<int64> orders(1, manager.NodeToIndex(order));
routing.AddDisjunction(orders, kPenalty);
}
// Adding same vehicle constraint costs for consecutive nodes.
if (FLAGS_vrp_use_same_vehicle_costs) {
std::vector<int> group;
std::vector<int64> group;
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
order < manager.num_nodes(); ++order) {
group.push_back(manager.NodeToIndex(order));

View File

@@ -26,6 +26,9 @@
namespace operations_research {
typedef std::function<int64(RoutingNodeIndex, RoutingNodeIndex)>
RoutingNodeEvaluator2;
// Random seed generator.
int32 GetSeed(bool deterministic);

View File

@@ -194,7 +194,7 @@ int main(int argc, char** argv) {
const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1);
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
order < routing.nodes(); ++order) {
std::vector<int> orders(1, manager.NodeToIndex(order));
std::vector<int64> orders(1, manager.NodeToIndex(order));
routing.AddDisjunction(orders, kPenalty);
}

View File

@@ -156,7 +156,7 @@ int main(int argc, char** argv) {
const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1);
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
order < routing.nodes(); ++order) {
std::vector<int> orders(1, manager.NodeToIndex(order));
std::vector<int64> orders(1, manager.NodeToIndex(order));
routing.AddDisjunction(orders, kPenalty);
}

View File

@@ -156,7 +156,7 @@ int main(int argc, char** argv) {
const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1);
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
order < manager.num_nodes(); ++order) {
std::vector<int> orders(1, manager.NodeToIndex(order));
std::vector<int64> orders(1, manager.NodeToIndex(order));
routing.AddDisjunction(orders, kPenalty);
}

View File

@@ -183,7 +183,7 @@ int main(int argc, char** argv) {
const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1);
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
order < routing.nodes(); ++order) {
std::vector<int> orders(1, manager.NodeToIndex(order));
std::vector<int64> orders(1, manager.NodeToIndex(order));
routing.AddDisjunction(orders, kPenalty);
}

View File

@@ -351,7 +351,7 @@ bool LoadAndSolve(const std::string& pdp_file,
const int64 kPenalty = 10000000;
for (RoutingIndexManager::NodeIndex order(1); order < routing.nodes();
++order) {
std::vector<int> orders(1, manager.NodeToIndex(order));
std::vector<int64> orders(1, manager.NodeToIndex(order));
routing.AddDisjunction(orders, kPenalty);
}

View File

@@ -269,7 +269,7 @@ public class CapacitatedVehicleRoutingProblemWithTimeWindows {
for (int order = 0; order < number_of_orders; ++order) {
time_dimension.CumulVar(order).SetRange(order_time_windows_[order].start_,
order_time_windows_[order].end_);
int[] orders = {order};
long[] orders = {manager.NodeToIndex(order)};
model.AddDisjunction(orders, order_penalties_[order]);
}

View File

@@ -221,7 +221,7 @@ public class CapacitatedVehicleRoutingProblemWithTimeWindows {
timeDimension
.cumulVar(order)
.setRange(orderTimeWindows.get(order).first, orderTimeWindows.get(order).second);
int[] orderIndices = {manager.nodeToIndex(order)};
long[] orderIndices = {manager.nodeToIndex(order)};
model.addDisjunction(orderIndices, orderPenalties.get(order));
}

View File

@@ -1353,7 +1353,7 @@ void RoutingModel::ComputeVehicleClasses() {
}
RoutingModel::DisjunctionIndex RoutingModel::AddDisjunction(
const std::vector<int>& indices, int64 penalty, int64 max_cardinality) {
const std::vector<int64>& indices, int64 penalty, int64 max_cardinality) {
CHECK_GE(max_cardinality, 1);
for (int i = 0; i < indices.size(); ++i) {
CHECK_NE(kUnassigned, indices[i]);
@@ -1361,7 +1361,7 @@ RoutingModel::DisjunctionIndex RoutingModel::AddDisjunction(
const DisjunctionIndex disjunction_index(disjunctions_.size());
disjunctions_.push_back({indices, {penalty, max_cardinality}});
for (const int index : indices) {
for (const int64 index : indices) {
index_to_disjunctions_[index].push_back(disjunction_index);
}
return disjunction_index;
@@ -1371,7 +1371,7 @@ std::vector<std::pair<int64, int64>>
RoutingModel::GetPerfectBinaryDisjunctions() const {
std::vector<std::pair<int64, int64>> var_index_pairs;
for (const Disjunction& disjunction : disjunctions_) {
const std::vector<int>& var_indices = disjunction.indices;
const std::vector<int64>& var_indices = disjunction.indices;
if (var_indices.size() != 2) continue;
const int64 v0 = var_indices[0];
const int64 v1 = var_indices[1];
@@ -1402,7 +1402,7 @@ void RoutingModel::IgnoreDisjunctionsAlreadyForcedToZero() {
}
IntVar* RoutingModel::CreateDisjunction(DisjunctionIndex disjunction) {
const std::vector<int>& indices = disjunctions_[disjunction].indices;
const std::vector<int64>& indices = disjunctions_[disjunction].indices;
const int indices_size = indices.size();
std::vector<IntVar*> disjunction_vars(indices_size);
for (int i = 0; i < indices_size; ++i) {
@@ -1427,11 +1427,11 @@ IntVar* RoutingModel::CreateDisjunction(DisjunctionIndex disjunction) {
}
}
void RoutingModel::AddSoftSameVehicleConstraint(const std::vector<int>& indices,
int64 cost) {
void RoutingModel::AddSoftSameVehicleConstraint(
const std::vector<int64>& indices, int64 cost) {
if (!indices.empty()) {
ValuedNodes<int64> same_vehicle_cost;
for (const int index : indices) {
for (const int64 index : indices) {
same_vehicle_cost.indices.push_back(index);
}
same_vehicle_cost.value = cost;
@@ -1448,7 +1448,7 @@ void RoutingModel::SetAllowedVehiclesForIndex(const std::vector<int>& vehicles,
}
}
void RoutingModel::AddPickupAndDelivery(int pickup, int delivery) {
void RoutingModel::AddPickupAndDelivery(int64 pickup, int64 delivery) {
AddPickupAndDeliverySetsInternal({pickup}, {delivery});
pickup_delivery_disjunctions_.push_back({kNoDisjunction, kNoDisjunction});
}
@@ -1463,7 +1463,7 @@ void RoutingModel::AddPickupAndDeliverySets(
}
void RoutingModel::AddPickupAndDeliverySetsInternal(
const std::vector<int>& pickups, const std::vector<int>& deliveries) {
const std::vector<int64>& pickups, const std::vector<int64>& deliveries) {
if (pickups.empty() || deliveries.empty()) {
return;
}
@@ -1509,7 +1509,8 @@ int RoutingModel::GetNumOfSingletonNodes() const {
}
IntVar* RoutingModel::CreateSameVehicleCost(int vehicle_index) {
const std::vector<int>& indices = same_vehicle_costs_[vehicle_index].indices;
const std::vector<int64>& indices =
same_vehicle_costs_[vehicle_index].indices;
CHECK(!indices.empty());
std::vector<IntVar*> vehicle_counts;
solver_->MakeIntVarArray(vehicle_vars_.size() + 1, 0, indices.size() + 1,

View File

@@ -536,7 +536,7 @@ class RoutingModel {
// performed, and therefore p == 0.
// Note: passing a vector with a single index will model an optional index
// with a penalty cost if it is not visited.
DisjunctionIndex AddDisjunction(const std::vector<int>& indices,
DisjunctionIndex AddDisjunction(const std::vector<int64>& indices,
int64 penalty = kNoPenalty,
int64 max_cardinality = 1);
// Returns the indices of the disjunctions to which an index belongs.
@@ -561,7 +561,8 @@ class RoutingModel {
#if !defined(SWIGPYTHON)
// Returns the variable indices of the nodes in the disjunction of index
// 'index'.
const std::vector<int>& GetDisjunctionIndices(DisjunctionIndex index) const {
const std::vector<int64>& GetDisjunctionIndices(
DisjunctionIndex index) const {
return disjunctions_[index].indices;
}
#endif // !defined(SWIGPYTHON)
@@ -591,7 +592,7 @@ class RoutingModel {
// Adds a soft contraint to force a set of variable indices to be on the same
// vehicle. If all nodes are not on the same vehicle, each extra vehicle used
// adds 'cost' to the cost function.
void AddSoftSameVehicleConstraint(const std::vector<int>& indices,
void AddSoftSameVehicleConstraint(const std::vector<int64>& indices,
int64 cost);
// Sets the vehicles which can visit a given node. If the node is in a
@@ -623,7 +624,7 @@ class RoutingModel {
// routing.AddPickupAndDelivery(index1, index2);
//
// TODO(user): Remove this when model introspection detects linked nodes.
void AddPickupAndDelivery(int pickup, int delivery);
void AddPickupAndDelivery(int64 pickup, int64 delivery);
// Same as AddPickupAndDelivery but notifying that the performed node from
// the disjunction of index 'pickup_disjunction' is on the same route as the
// performed node from the disjunction of index 'delivery_disjunction'.
@@ -1147,7 +1148,7 @@ class RoutingModel {
// when unperformed).
template <typename T>
struct ValuedNodes {
std::vector<int> indices;
std::vector<int64> indices;
T value;
};
struct DisjunctionValues {
@@ -1209,8 +1210,8 @@ class RoutingModel {
// Returns nullptr if no penalty cost, otherwise returns penalty variable.
IntVar* CreateDisjunction(DisjunctionIndex disjunction);
// Sets up pickup and delivery sets.
void AddPickupAndDeliverySetsInternal(const std::vector<int>& pickups,
const std::vector<int>& deliveries);
void AddPickupAndDeliverySetsInternal(const std::vector<int64>& pickups,
const std::vector<int64>& deliveries);
// Returns the cost variable related to the soft same vehicle constraint of
// index 'vehicle_index'.
IntVar* CreateSameVehicleCost(int vehicle_index);

View File

@@ -21,7 +21,7 @@ namespace {
// Compute set of disjunctions involved in a pickup and delivery pair.
template <typename Disjunctions>
void AddDisjunctionsFromNodes(const RoutingModel& model,
const std::vector<int>& nodes,
const std::vector<int64>& nodes,
Disjunctions* disjunctions) {
for (int64 node : nodes) {
for (const auto disjunction : model.GetDisjunctionIndices(node)) {

View File

@@ -21,7 +21,7 @@
namespace operations_research {
const int RoutingIndexManager::kUnassigned = -1;
const int64 RoutingIndexManager::kUnassigned = -1;
RoutingIndexManager::RoutingIndexManager(int num_nodes, int num_vehicles,
NodeIndex depot)
@@ -77,7 +77,7 @@ void RoutingIndexManager::Initialize(
node_to_index_.resize(num_nodes_, kUnassigned);
vehicle_to_start_.resize(num_vehicles_);
vehicle_to_end_.resize(num_vehicles_);
int index = 0;
int64 index = 0;
for (NodeIndex i = kZeroNode; i < num_nodes_; ++i) {
if (gtl::ContainsKey(starts, i) || !gtl::ContainsKey(ends, i)) {
index_to_node_[index] = i;
@@ -90,7 +90,7 @@ void RoutingIndexManager::Initialize(
const NodeIndex start = starts_ends[i].first;
if (!gtl::ContainsKey(seen_starts, start)) {
seen_starts.insert(start);
const int start_index = node_to_index_[start];
const int64 start_index = node_to_index_[start];
vehicle_to_start_[i] = start_index;
CHECK_NE(kUnassigned, start_index);
} else {
@@ -110,7 +110,7 @@ void RoutingIndexManager::Initialize(
// Logging model information.
VLOG(1) << "Number of nodes: " << num_nodes_;
VLOG(1) << "Number of vehicles: " << num_vehicles_;
for (int index = 0; index < index_to_node_.size(); ++index) {
for (int64 index = 0; index < index_to_node_.size(); ++index) {
VLOG(2) << "Variable index " << index << " -> Node index "
<< index_to_node_[index];
}
@@ -120,12 +120,12 @@ void RoutingIndexManager::Initialize(
}
}
std::vector<int> RoutingIndexManager::NodesToIndices(
std::vector<int64> RoutingIndexManager::NodesToIndices(
const std::vector<NodeIndex>& nodes) const {
std::vector<int> indices;
std::vector<int64> indices;
indices.reserve(nodes.size());
for (const NodeIndex node : nodes) {
const int index = NodeToIndex(node);
const int64 index = NodeToIndex(node);
CHECK_NE(kUnassigned, index);
indices.push_back(index);
}

View File

@@ -40,7 +40,7 @@ namespace operations_research {
class RoutingIndexManager {
public:
typedef RoutingNodeIndex NodeIndex;
static const int kUnassigned;
static const int64 kUnassigned;
// Creates a NodeIndex to variable index mapping for a problem containing
// 'num_nodes', 'num_vehicles' and the given starts and ends for each vehicle.
@@ -57,15 +57,15 @@ class RoutingIndexManager {
int num_nodes() const { return num_nodes_; }
int num_vehicles() const { return num_vehicles_; }
int num_indices() const { return index_to_node_.size(); }
int GetStartIndex(int vehicle) const { return vehicle_to_start_[vehicle]; }
int GetEndIndex(int vehicle) const { return vehicle_to_end_[vehicle]; }
int NodeToIndex(NodeIndex node) const {
int64 GetStartIndex(int vehicle) const { return vehicle_to_start_[vehicle]; }
int64 GetEndIndex(int vehicle) const { return vehicle_to_end_[vehicle]; }
int64 NodeToIndex(NodeIndex node) const {
DCHECK_GE(node.value(), 0);
DCHECK_LT(node.value(), node_to_index_.size());
return node_to_index_[node];
}
std::vector<int> NodesToIndices(const std::vector<NodeIndex>& nodes) const;
NodeIndex IndexToNode(int index) const {
std::vector<int64> NodesToIndices(const std::vector<NodeIndex>& nodes) const;
NodeIndex IndexToNode(int64 index) const {
DCHECK_GE(index, 0);
DCHECK_LT(index, index_to_node_.size());
return index_to_node_[index];
@@ -74,7 +74,7 @@ class RoutingIndexManager {
// complete.
int num_unique_depots() const { return num_unique_depots_; }
std::vector<NodeIndex> GetIndexToNodeMap() const { return index_to_node_; }
gtl::ITIVector<NodeIndex, int> GetNodeToIndexMap() const {
gtl::ITIVector<NodeIndex, int64> GetNodeToIndexMap() const {
return node_to_index_;
}
@@ -84,9 +84,9 @@ class RoutingIndexManager {
const std::vector<std::pair<NodeIndex, NodeIndex> >& starts_ends);
std::vector<NodeIndex> index_to_node_;
gtl::ITIVector<NodeIndex, int> node_to_index_;
std::vector<int> vehicle_to_start_;
std::vector<int> vehicle_to_end_;
gtl::ITIVector<NodeIndex, int64> node_to_index_;
std::vector<int64> vehicle_to_start_;
std::vector<int64> vehicle_to_end_;
int num_nodes_;
int num_vehicles_;
int num_unique_depots_;

View File

@@ -151,7 +151,7 @@ MakePairInactiveOperator::MakePairInactiveOperator(
const RoutingIndexPairs& index_pairs)
: PathWithPreviousNodesOperator(vars, secondary_vars, 1,
std::move(start_empty_path_class)) {
int max_pair_index = -1;
int64 max_pair_index = -1;
for (const auto& index_pair : index_pairs) {
max_pair_index = std::max(max_pair_index, index_pair.first[0]);
max_pair_index = std::max(max_pair_index, index_pair.second[0]);
@@ -189,7 +189,7 @@ PairRelocateOperator::PairRelocateOperator(
index_max = std::max(index_max, var->Max());
}
is_first_.resize(index_max + 1, false);
int max_pair_index = -1;
int64 max_pair_index = -1;
// TODO(user): Support pairs with disjunctions.
for (const auto& index_pair : index_pairs) {
max_pair_index = std::max(max_pair_index, index_pair.first[0]);
@@ -283,7 +283,7 @@ LightPairRelocateOperator::LightPairRelocateOperator(
const RoutingIndexPairs& index_pairs)
: PathWithPreviousNodesOperator(vars, secondary_vars, 2,
std::move(start_empty_path_class)) {
int max_pair_index = -1;
int64 max_pair_index = -1;
// TODO(user): Support pairs with disjunctions.
for (const auto& index_pair : index_pairs) {
max_pair_index = std::max(max_pair_index, index_pair.first[0]);
@@ -332,7 +332,7 @@ PairExchangeOperator::PairExchangeOperator(
index_max = std::max(index_max, var->Max());
}
is_first_.resize(index_max + 1, false);
int max_pair_index = -1;
int64 max_pair_index = -1;
// TODO(user): Support pairs with disjunctions.
for (const auto& index_pair : index_pairs) {
max_pair_index = std::max(max_pair_index, index_pair.first[0]);
@@ -415,7 +415,7 @@ PairExchangeRelocateOperator::PairExchangeRelocateOperator(
index_max = std::max(index_max, var->Max());
}
is_first_.resize(index_max + 1, false);
int max_pair_index = -1;
int64 max_pair_index = -1;
// TODO(user): Support pairs with disjunctions.
for (const auto& index_pair : index_pairs) {
max_pair_index = std::max(max_pair_index, index_pair.first[0]);
@@ -673,7 +673,7 @@ IndexPairSwapActiveOperator::IndexPairSwapActiveOperator(
: PathWithPreviousNodesOperator(vars, secondary_vars, 1,
std::move(start_empty_path_class)),
inactive_node_(0) {
int max_pair_index = -1;
int64 max_pair_index = -1;
// TODO(user): Support pairs with disjunctions.
for (const auto& index_pair : index_pairs) {
max_pair_index = std::max(max_pair_index, index_pair.first[0]);

View File

@@ -182,9 +182,9 @@ class NodeDisjunctionFilter : public IntVarLocalSearchFilter {
i < active_per_disjunction_.size(); ++i) {
active_per_disjunction_[i] = 0;
inactive_per_disjunction_[i] = 0;
const std::vector<int>& disjunction_indices =
const std::vector<int64>& disjunction_indices =
routing_model_.GetDisjunctionIndices(i);
for (const int index : disjunction_indices) {
for (const int64 index : disjunction_indices) {
const bool index_synced = IsVarSynced(index);
if (index_synced) {
if (Value(index) != index) {

View File

@@ -37,12 +37,10 @@ DEFINE_INT_TYPE(RoutingDimensionIndex, int);
DEFINE_INT_TYPE(RoutingDisjunctionIndex, int);
DEFINE_INT_TYPE(RoutingVehicleClassIndex, int);
typedef std::function<int64(RoutingNodeIndex, RoutingNodeIndex)>
RoutingNodeEvaluator2;
typedef std::function<int64(int)> RoutingTransitCallback1;
typedef std::function<int64(int, int)> RoutingTransitCallback2;
// NOTE(user): keep the "> >" for SWIG.
typedef std::pair<std::vector<int>, std::vector<int> > RoutingIndexPair;
typedef std::pair<std::vector<int64>, std::vector<int64> > RoutingIndexPair;
typedef std::vector<RoutingIndexPair> RoutingIndexPairs;
} // namespace operations_research

View File

@@ -347,6 +347,7 @@ std::string Summarize(const std::string& input) {
struct WorkerInfo {
std::string worker_name;
WallTimer* global_timer = nullptr;
bool parallel_mode = false;
};
} // namespace.
@@ -1186,9 +1187,9 @@ std::function<SatParameters(Model*)> NewSatParameters(
}
namespace {
void LogNewParallelSolution(const std::string& event_or_solution_count,
double time_in_seconds, double obj_lb,
double obj_ub, const std::string& solution_info) {
void LogNewSolution(const std::string& event_or_solution_count,
double time_in_seconds, double obj_lb, double obj_ub,
const std::string& solution_info) {
VLOG(1) << absl::StrFormat("#%-5s %6.2fs obj:[%0.0f,%0.0f] %s",
event_or_solution_count, time_in_seconds, obj_lb,
obj_ub, solution_info);
@@ -1202,8 +1203,6 @@ void RegisterObjectiveLowerBoundWatcher(
const auto broadcast_lower_bound =
[model_proto, external_solution_observer, objective_var,
model](const std::vector<IntegerVariable>& modified_vars) {
CpSolverResponse lb_response;
lb_response.set_status(CpSolverStatus::UNKNOWN);
auto* integer_trail = model->Get<IntegerTrail>();
const ObjectiveSynchronizationHelper* const helper =
model->GetOrCreate<ObjectiveSynchronizationHelper>();
@@ -1220,30 +1219,29 @@ void RegisterObjectiveLowerBoundWatcher(
new_best_bound > current_best_bound) ||
(helper->scaling_factor < 0 &&
new_best_bound < current_best_bound)) {
DCHECK_EQ(0, lb_response.solution_size());
DCHECK_EQ(0, lb_response.solution_lower_bounds_size());
DCHECK_EQ(0, lb_response.solution_upper_bounds_size());
DCHECK_EQ(lb_response.status(), CpSolverStatus::UNKNOWN);
lb_response.set_objective_value(current_objective_value);
lb_response.set_best_objective_bound(new_best_bound);
if (current_objective_value >= new_best_bound) {
LogNewParallelSolution("ObjLb", worker_info->global_timer->Get(),
new_best_bound, current_objective_value,
worker_info->worker_name);
if (new_best_bound > current_best_bound) { // minimization.
LogNewSolution("ObjLb", worker_info->global_timer->Get(),
new_best_bound, current_objective_value,
worker_info->worker_name);
} else {
LogNewParallelSolution("ObjUb", worker_info->global_timer->Get(),
current_objective_value, new_best_bound,
worker_info->worker_name);
LogNewSolution("ObjUb", worker_info->global_timer->Get(),
current_objective_value, new_best_bound,
worker_info->worker_name);
}
if (worker_info->parallel_mode) {
// Broadcast a best bound improving solution.
CpSolverResponse lb_response;
lb_response.set_status(CpSolverStatus::UNKNOWN);
lb_response.set_objective_value(current_objective_value);
lb_response.set_best_objective_bound(new_best_bound);
external_solution_observer(lb_response);
}
external_solution_observer(lb_response);
}
};
model->GetOrCreate<GenericLiteralWatcher>()
->RegisterLevelZeroModifiedVariablesCallback(broadcast_lower_bound);
}
// Because we also use this function for postsolve, we call it with
// is_real_solve set to true and avoid doing non-useful work in this case.
CpSolverResponse SolveCpModelInternal(
@@ -1350,9 +1348,9 @@ CpSolverResponse SolveCpModelInternal(
model->GetOrCreate<SatSolver>()->Propagate();
// Auto detect "at least one of" constraints in the PrecedencesPropagator.
// Note that we do that before we finish loading the problem (objective and LP
// relaxation), because propagation will be faster at this point and it should
// be enough for the purpose of this auto-detection.
// Note that we do that before we finish loading the problem (objective and
// LP relaxation), because propagation will be faster at this point and it
// should be enough for the purpose of this auto-detection.
if (model->Mutable<PrecedencesPropagator>() != nullptr &&
parameters.auto_detect_greater_than_at_least_one_of()) {
model->Mutable<PrecedencesPropagator>()
@@ -1441,8 +1439,8 @@ CpSolverResponse SolveCpModelInternal(
}
}
// Note that we do one last propagation at level zero once all the constraints
// were added.
// Note that we do one last propagation at level zero once all the
// constraints were added.
model->GetOrCreate<SatSolver>()->Propagate();
// Initialize the search strategy function.
@@ -1472,7 +1470,32 @@ CpSolverResponse SolveCpModelInternal(
external_solution_observer(response);
};
if (watch_objective_lower_bound) {
if (watch_objective_lower_bound && model_proto.has_objective()) {
// Detect sequential mode, register callbacks in that case.
if (model->Get<WorkerInfo>() == nullptr) {
model->GetOrCreate<WorkerInfo>()->global_timer = &wall_timer;
auto* integer_trail = model->Get<IntegerTrail>();
const CpObjectiveProto& obj = model_proto.objective();
const auto get_objective_value = [&response, integer_trail, &obj,
objective_var]() {
return response.status() != CpSolverStatus::MODEL_INVALID
? response.objective_value()
: ScaleObjectiveValue(
obj, integer_trail->LevelZeroUpperBound(objective_var)
.value() +
1);
};
const auto get_objective_best_bound = [&response, integer_trail, &obj,
&objective_var]() {
return response.status() != CpSolverStatus::MODEL_INVALID
? response.best_objective_bound()
: ScaleObjectiveValue(
obj, integer_trail->LevelZeroLowerBound(objective_var)
.value());
};
SetObjectiveSynchronizationFunctions(get_objective_value,
get_objective_best_bound, model);
}
RegisterObjectiveLowerBoundWatcher(&model_proto, external_solution_observer,
objective_var, model);
}
@@ -2139,6 +2162,7 @@ CpSolverResponse SolveCpModelParallel(
WorkerInfo* worker_info = local_model.GetOrCreate<WorkerInfo>();
worker_info->worker_name = worker_name;
worker_info->global_timer = global_timer;
worker_info->parallel_mode = true;
SetSynchronizationFunction(std::move(solution_synchronization),
&local_model);
@@ -2304,12 +2328,12 @@ CpSolverResponse SolveCpModel(const CpModelProto& model_proto, Model* model) {
[&model_proto, &observers, &num_solutions, &timer,
&postprocess_solution](const CpSolverResponse& response) {
const bool maximize = model_proto.objective().scaling_factor() < 0.0;
LogNewParallelSolution(absl::StrCat(++num_solutions), timer.Get(),
maximize ? response.objective_value()
: response.best_objective_bound(),
maximize ? response.best_objective_bound()
: response.objective_value(),
response.solution_info());
LogNewSolution(absl::StrCat(++num_solutions), timer.Get(),
maximize ? response.objective_value()
: response.best_objective_bound(),
maximize ? response.best_objective_bound()
: response.objective_value(),
response.solution_info());
if (observers.empty()) return;
CpSolverResponse copy = response;
@@ -2346,7 +2370,7 @@ CpSolverResponse SolveCpModel(const CpModelProto& model_proto, Model* model) {
} else {
response = SolveCpModelInternal(
new_model, /*is_real_solve=*/true, observer_function,
/*watch_objective_lower_bound=*/false, model);
/*watch_objective_lower_bound=*/true, model);
}
postprocess_solution(&response);

View File

@@ -675,9 +675,10 @@ bool LinearProgrammingConstraint::ComputeNewLinearConstraint(
*scaling = std::max(*scaling, 1e6 / lp_multipliers_norm);
}
// Make sure the scaled coeff are not too big.
// Make sure the scaled coeff are not too big so that they can fit on
// an IntegerValue. Since 1<<63 is around 9.2e18, we use 1e18 here.
if (max_cp_multi > 0.0) {
*scaling = std::min(*scaling, 1e12 / max_cp_multi);
*scaling = std::min(*scaling, 1e18 / max_cp_multi);
}
// Scale the multipliers by *scaling.

View File

@@ -29,7 +29,7 @@
// Note(user): for an unknown reason, using the (handy) method PyObjAs()
// defined in base/swig/python-swig.cc seems to cause issues, so we can't
// use a generic, templated type checker.
// Get const std::vector<std::string>& "in" typemap.
// Get const std::vector<std::string>& "in" typemap.
%define PY_LIST_OUTPUT_TYPEMAP(type, checker, py_converter)
%typecheck(SWIG_TYPECHECK_POINTER) const std::vector<type>&,