@@ -56,7 +56,6 @@ void ExpandReservoirUsingCircuit(int64_t sum_of_positive_demand,
5656 int64_t sum_of_negative_demand,
5757 ConstraintProto* reservoir_ct,
5858 PresolveContext* context) {
59- SolutionCrush& crush = context->solution_crush ();
6059 const ReservoirConstraintProto& reservoir = reservoir_ct->reservoir ();
6160 const int num_events = reservoir.time_exprs_size ();
6261
@@ -72,73 +71,6 @@ void ExpandReservoirUsingCircuit(int64_t sum_of_positive_demand,
7271 std::vector<int > level_vars (num_events);
7372 for (int i = 0 ; i < num_events; ++i) {
7473 level_vars[i] = context->NewIntVar (Domain (var_min, var_max));
75- if (crush.HintIsLoaded ()) {
76- // The hint of active events is set later.
77- crush.SetNewVariableHint (level_vars[i], 0 );
78- }
79- }
80-
81- // The hints of the active events, in the order they should appear in the
82- // circuit. The hints are collected first, and sorted later.
83- struct ReservoirEventHint {
84- int index; // In the reservoir constraint.
85- int64_t time;
86- int64_t level_change;
87- };
88- std::vector<ReservoirEventHint> active_event_hints;
89- bool has_complete_hint = false ;
90- if (crush.HintIsLoaded ()) {
91- has_complete_hint = true ;
92- for (int i = 0 ; i < num_events && has_complete_hint; ++i) {
93- if (crush.VarHasSolutionHint (PositiveRef (reservoir.active_literals (i)))) {
94- if (crush.LiteralSolutionHint (reservoir.active_literals (i))) {
95- const std::optional<int64_t > time_hint =
96- crush.GetExpressionSolutionHint (reservoir.time_exprs (i));
97- const std::optional<int64_t > change_hint =
98- crush.GetExpressionSolutionHint (reservoir.level_changes (i));
99- if (time_hint.has_value () && change_hint.has_value ()) {
100- active_event_hints.push_back (
101- {i, time_hint.value (), change_hint.value ()});
102- } else {
103- has_complete_hint = false ;
104- }
105- }
106- } else {
107- has_complete_hint = false ;
108- }
109- }
110- }
111- // Update the `level_vars` hints by computing the level at each active event.
112- if (has_complete_hint) {
113- std::sort (active_event_hints.begin (), active_event_hints.end (),
114- [](const ReservoirEventHint& a, const ReservoirEventHint& b) {
115- return a.time < b.time ;
116- });
117- int64_t current_level = 0 ;
118- for (int i = 0 ; i < active_event_hints.size (); ++i) {
119- int j = i;
120- // Adjust the order of the events occurring at the same time, in the
121- // circuit, so that, at each node, the level is between `var_min` and
122- // `var_max`. For instance, if e1 = {t, +1} and e2 = {t, -1}, and if
123- // `current_level` = 0, `var_min` = -1 and `var_max` = 0, then e2 must
124- // occur before e1.
125- while (j < active_event_hints.size () &&
126- active_event_hints[j].time == active_event_hints[i].time &&
127- (current_level + active_event_hints[j].level_change < var_min ||
128- current_level + active_event_hints[j].level_change > var_max)) {
129- ++j;
130- }
131- if (j < active_event_hints.size () &&
132- active_event_hints[j].time == active_event_hints[i].time ) {
133- if (i != j) std::swap (active_event_hints[i], active_event_hints[j]);
134- current_level += active_event_hints[i].level_change ;
135- crush.UpdateVarSolutionHint (level_vars[active_event_hints[i].index ],
136- current_level);
137- } else {
138- has_complete_hint = false ;
139- break ;
140- }
141- }
14274 }
14375
14476 // For the corner case where all events are absent, we need a potential
@@ -148,17 +80,8 @@ void ExpandReservoirUsingCircuit(int64_t sum_of_positive_demand,
14880 circuit->add_tails (num_events);
14981 circuit->add_heads (num_events);
15082 circuit->add_literals (all_inactive);
151- if (has_complete_hint) {
152- crush.SetNewVariableHint (all_inactive, active_event_hints.empty ());
153- }
15483 }
15584
156- // The index of each event in `active_event_hints`, or -1 if the event's
157- // "active" hint is false.
158- std::vector<int > active_event_hint_index (num_events, -1 );
159- for (int i = 0 ; i < active_event_hints.size (); ++i) {
160- active_event_hint_index[active_event_hints[i].index ] = i;
161- }
16285 for (int i = 0 ; i < num_events; ++i) {
16386 if (!reservoir.active_literals ().empty ()) {
16487 // Add self arc to represent absence.
@@ -175,11 +98,6 @@ void ExpandReservoirUsingCircuit(int64_t sum_of_positive_demand,
17598 circuit->add_tails (num_events);
17699 circuit->add_heads (i);
177100 circuit->add_literals (start_var);
178- if (has_complete_hint) {
179- crush.SetNewVariableHint (start_var,
180- !active_event_hints.empty () &&
181- active_event_hints.front ().index == i);
182- }
183101
184102 // Add enforced linear for demand.
185103 {
@@ -200,11 +118,6 @@ void ExpandReservoirUsingCircuit(int64_t sum_of_positive_demand,
200118 circuit->add_tails (i);
201119 circuit->add_heads (num_events);
202120 circuit->add_literals (end_var);
203- if (has_complete_hint) {
204- crush.SetNewVariableHint (end_var,
205- !active_event_hints.empty () &&
206- active_event_hints.back ().index == i);
207- }
208121 }
209122
210123 for (int j = 0 ; j < num_events; ++j) {
@@ -224,13 +137,6 @@ void ExpandReservoirUsingCircuit(int64_t sum_of_positive_demand,
224137 circuit->add_tails (i);
225138 circuit->add_heads (j);
226139 circuit->add_literals (arc_i_j);
227- if (has_complete_hint) {
228- const int hint_i_index = active_event_hint_index[i];
229- const int hint_j_index = active_event_hint_index[j];
230- crush.SetNewVariableHint (arc_i_j, hint_i_index != -1 &&
231- hint_j_index != -1 &&
232- hint_j_index == hint_i_index + 1 );
233- }
234140
235141 // Add enforced linear for time.
236142 {
@@ -261,6 +167,8 @@ void ExpandReservoirUsingCircuit(int64_t sum_of_positive_demand,
261167 }
262168 }
263169 }
170+ context->solution_crush ().SetReservoirCircuitVars (reservoir, var_min, var_max,
171+ level_vars, *circuit);
264172
265173 reservoir_ct->Clear ();
266174 context->UpdateRuleStats (" reservoir: expanded using circuit." );
@@ -337,6 +245,9 @@ void ExpandReservoirUsingPrecedences(bool max_level_is_constraining,
337245
338246 // Canonicalize the newly created constraint.
339247 context->CanonicalizeLinearConstraint (new_cumul);
248+
249+ DCHECK (!PossibleIntegerOverflow (*context->working_model , new_linear->vars (),
250+ new_linear->coeffs ()));
340251 }
341252
342253 reservoir_ct->Clear ();
@@ -539,39 +450,6 @@ void ExpandIntMod(ConstraintProto* ct, PresolveContext* context) {
539450 return ;
540451 }
541452
542- bool has_complete_hint = false ;
543- bool enforced_hint = true ;
544- int64_t expr_hint = 0 ;
545- int64_t mod_expr_hint = 0 ;
546- int64_t target_expr_hint = 0 ;
547- SolutionCrush& crush = context->solution_crush ();
548- if (crush.HintIsLoaded ()) {
549- has_complete_hint = true ;
550- for (const int lit : ct->enforcement_literal ()) {
551- if (!crush.VarHasSolutionHint (PositiveRef (lit))) {
552- has_complete_hint = false ;
553- break ;
554- }
555- enforced_hint = enforced_hint && crush.LiteralSolutionHint (lit);
556- }
557- if (has_complete_hint && enforced_hint) {
558- has_complete_hint = false ;
559- std::optional<int64_t > hint = crush.GetExpressionSolutionHint (expr);
560- if (hint.has_value ()) {
561- expr_hint = hint.value ();
562- hint = crush.GetExpressionSolutionHint (mod_expr);
563- if (hint.has_value ()) {
564- mod_expr_hint = hint.value ();
565- hint = crush.GetExpressionSolutionHint (target_expr);
566- if (hint.has_value ()) {
567- target_expr_hint = hint.value ();
568- has_complete_hint = true ;
569- }
570- }
571- }
572- }
573- }
574-
575453 // Create a new constraint with the same enforcement as ct.
576454 auto new_enforced_constraint = [&]() {
577455 ConstraintProto* new_ct = context->working_model ->add_constraints ();
@@ -583,13 +461,6 @@ void ExpandIntMod(ConstraintProto* ct, PresolveContext* context) {
583461 const int div_var = context->NewIntVar (
584462 context->DomainSuperSetOf (expr).PositiveDivisionBySuperset (
585463 context->DomainSuperSetOf (mod_expr)));
586- if (has_complete_hint) {
587- if (enforced_hint) {
588- crush.SetNewVariableHint (div_var, expr_hint / mod_expr_hint);
589- } else {
590- crush.SetNewVariableHint (div_var, context->MinOf (div_var));
591- }
592- }
593464 LinearExpressionProto div_expr;
594465 div_expr.add_vars (div_var);
595466 div_expr.add_coeffs (1 );
@@ -607,13 +478,6 @@ void ExpandIntMod(ConstraintProto* ct, PresolveContext* context) {
607478 .IntersectionWith (context->DomainSuperSetOf (expr).AdditionWith (
608479 context->DomainSuperSetOf (target_expr).Negation ()));
609480 const int prod_var = context->NewIntVar (prod_domain);
610- if (has_complete_hint) {
611- if (enforced_hint) {
612- crush.SetNewVariableHint (prod_var, expr_hint - target_expr_hint);
613- } else {
614- crush.SetNewVariableHint (prod_var, context->MinOf (prod_var));
615- }
616- }
617481 LinearExpressionProto prod_expr;
618482 prod_expr.add_vars (prod_var);
619483 prod_expr.add_coeffs (1 );
@@ -633,41 +497,34 @@ void ExpandIntMod(ConstraintProto* ct, PresolveContext* context) {
633497 AddLinearExpressionToLinearConstraint (prod_expr, -1 , lin);
634498 AddLinearExpressionToLinearConstraint (target_expr, -1 , lin);
635499
500+ context->solution_crush ().SetIntModExpandedVars (*ct, div_var, prod_var,
501+ context->MinOf (div_var),
502+ context->MinOf (prod_var));
636503 ct->Clear ();
637504 context->UpdateRuleStats (" int_mod: expanded" );
638505}
639506
640507void ExpandNonBinaryIntProd (ConstraintProto* ct, PresolveContext* context) {
641508 CHECK_GT (ct->int_prod ().exprs_size (), 2 );
642- SolutionCrush& crush = context->solution_crush ();
643509 std::deque<LinearExpressionProto> terms (
644510 {ct->int_prod ().exprs ().begin (), ct->int_prod ().exprs ().end ()});
511+ std::vector<int > new_vars;
645512 while (terms.size () > 2 ) {
646513 const LinearExpressionProto& left = terms[0 ];
647514 const LinearExpressionProto& right = terms[1 ];
648515 const Domain new_domain =
649516 context->DomainSuperSetOf (left).ContinuousMultiplicationBy (
650517 context->DomainSuperSetOf (right));
651518 const int new_var = context->NewIntVar (new_domain);
652- if (crush.HintIsLoaded ()) {
653- const std::optional<int64_t > left_hint =
654- crush.GetExpressionSolutionHint (left);
655- const std::optional<int64_t > right_hint =
656- crush.GetExpressionSolutionHint (right);
657- if (left_hint.has_value () && right_hint.has_value ()) {
658- crush.SetNewVariableHint (new_var,
659- left_hint.value () * right_hint.value ());
660- }
661- }
519+ new_vars.push_back (new_var);
662520 LinearArgumentProto* const int_prod =
663521 context->working_model ->add_constraints ()->mutable_int_prod ();
664522 *int_prod->add_exprs () = left;
665523 *int_prod->add_exprs () = right;
666524 int_prod->mutable_target ()->add_vars (new_var);
667525 int_prod->mutable_target ()->add_coeffs (1 );
668526 terms.pop_front ();
669- terms.pop_front ();
670- terms.push_back (int_prod->target ());
527+ terms.front () = int_prod->target ();
671528 }
672529
673530 LinearArgumentProto* const final_int_prod =
@@ -676,6 +533,7 @@ void ExpandNonBinaryIntProd(ConstraintProto* ct, PresolveContext* context) {
676533 *final_int_prod->add_exprs () = terms[1 ];
677534 *final_int_prod->mutable_target () = ct->int_prod ().target ();
678535
536+ context->solution_crush ().SetIntProdExpandedVars (ct->int_prod (), new_vars);
679537 context->UpdateRuleStats (absl::StrCat (
680538 " int_prod: expanded int_prod with arity " , ct->int_prod ().exprs_size ()));
681539 ct->Clear ();
0 commit comments