Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
ModelBuilderExpr.cs
Go to the documentation of this file.
1// Copyright 2010-2024 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15{
17using System;
18using System.Collections;
19using System.Collections.Generic;
20using System.Linq;
21using System.Runtime.CompilerServices;
22using Google.Protobuf.Collections;
23
24internal static class HelperExtensions
25{
26 // [MethodImpl(MethodImplOptions.AggressiveInlining)]
27 // public static void AddOrIncrement(this SortedDictionary<int, double> dict, int key, double increment)
28 // {
29 // #if NET6_0_OR_GREATER
30 // System.Runtime.InteropServices.CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out _) += increment;
31 // #else
32 // if (dict.TryGetValue(key, out var value))
33 // {
34 // dict[key] = value + increment;
35 // }
36 // else
37 // {
38 // dict.Add(key, increment);
39 // }
40 // #endif
41 // }
42
43 [MethodImpl(MethodImplOptions.AggressiveInlining)]
44 internal static void TrySetCapacity<TField, TValues>(this RepeatedField<TField> field, IEnumerable<TValues> values)
45 {
46 if (values is ICollection<TValues> collection)
47 {
48 field.Capacity = collection.Count;
49 }
50 }
51
52 [MethodImpl(MethodImplOptions.AggressiveInlining)]
53 internal static void TryEnsureCapacity<TValue, TValues>(this List<TValue> list, IEnumerable<TValues> values)
54 {
55 // Check for ICollection as the generic version is not covariant and TValues could be LinearExpr, Variable, ...
56 if (values is ICollection collection)
57 {
58 list.Capacity = Math.Max(list.Count + collection.Count, list.Capacity);
59 }
60 }
61
62#if NETFRAMEWORK
63 [MethodImpl(MethodImplOptions.AggressiveInlining)]
64 internal static bool TryDequeue<T>(this Queue<T> queue, out T value)
65 {
66 if (queue.Count > 0)
67 {
68 value = queue.Dequeue();
69 return true;
70 }
71
72 value = default;
73 return false;
74 }
75#endif
76}
77
78// Holds a term (expression * coefficient)
79public struct Term
80{
82 public double coefficient;
83
84 public Term(LinearExpr e, double c)
85 {
86 this.expr = e;
87 this.coefficient = c;
88 }
89}
90
96public class LinearExpr
97{
99 public static LinearExpr Sum(IEnumerable<LinearExpr> exprs)
100 {
101 return NewBuilder(0).AddSum(exprs);
102 }
103
105 public static LinearExpr WeightedSum(IEnumerable<LinearExpr> exprs, IEnumerable<int> coeffs)
106 {
107 return NewBuilder(0).AddWeightedSum(exprs, coeffs);
108 }
109
111 public static LinearExpr WeightedSum(IEnumerable<LinearExpr> exprs, IEnumerable<double> coeffs)
112 {
113 return NewBuilder(0).AddWeightedSum(exprs, coeffs);
114 }
115
117 public static LinearExpr Term(LinearExpr expr, double coeff)
118 {
119 return Prod(expr, coeff);
120 }
121
123 public static LinearExpr Affine(LinearExpr expr, double coeff, double offset)
124 {
125 if (offset == 0)
126 {
127 return Prod(expr, coeff);
128 }
129 return NewBuilder().AddTerm(expr, coeff).Add(offset);
130 }
131
133 public static LinearExpr Constant(double value)
134 {
135 return NewBuilder(0).Add(value);
136 }
137
139 public static LinearExprBuilder NewBuilder(int sizeHint = 2)
140 {
141 return new LinearExprBuilder(sizeHint);
142 }
143
145 {
146 return NewBuilder().Add(a).Add(b);
147 }
148
149 public static LinearExpr operator +(LinearExpr a, double v)
150 {
151 return NewBuilder().Add(a).Add(v);
152 }
153
154 public static LinearExpr operator +(double v, LinearExpr a)
155 {
156 return NewBuilder().Add(a).Add(v);
157 }
158
160 {
161 return NewBuilder().Add(a).AddTerm(b, -1);
162 }
163
164 public static LinearExpr operator -(LinearExpr a, double v)
165 {
166 return NewBuilder().Add(a).Add(-v);
167 }
168
169 public static LinearExpr operator -(double v, LinearExpr a)
170 {
171 return NewBuilder().AddTerm(a, -1).Add(v);
172 }
173
174 public static LinearExpr operator *(LinearExpr a, double v)
175 {
176 return Prod(a, v);
177 }
178
179 public static LinearExpr operator *(double v, LinearExpr a)
180 {
181 return Prod(a, v);
182 }
183
185 {
186 return Prod(a, -1);
187 }
188
190 {
191 return new BoundedLinearExpression(a, b, true);
192 }
193
195 {
196 return new BoundedLinearExpression(a, b, false);
197 }
198
200 {
201 return new BoundedLinearExpression(a, v, true);
202 }
203
205 {
206 return new BoundedLinearExpression(a, v, false);
207 }
208
210 {
211 return new BoundedLinearExpression(v, a, Double.PositiveInfinity);
212 }
213
215 {
216 return a <= v;
217 }
218
220 {
221 return new BoundedLinearExpression(Double.NegativeInfinity, a, v);
222 }
223
225 {
226 return a >= v;
227 }
228
230 {
231 return new BoundedLinearExpression(0, a - b, Double.PositiveInfinity);
232 }
233
235 {
236 return new BoundedLinearExpression(Double.NegativeInfinity, a - b, 0);
237 }
238
239 internal static LinearExpr Prod(LinearExpr e, double v)
240 {
241 if (v == 0)
242 {
243 return NewBuilder(0);
244 }
245 else if (v == 1)
246 {
247 return e;
248 }
249 else
250 {
251 return NewBuilder(1).AddTerm(e, v);
252 }
253 }
254
255 internal static double GetVarValueMap(LinearExpr e, SortedDictionary<int, double> dict, Queue<Term> terms)
256 {
257 double constant = 0;
258 double coefficient = 1;
259 LinearExpr expr = e;
260 terms.Clear();
261
262 do
263 {
264 switch (expr)
265 {
266 case LinearExprBuilder builder:
267 constant += coefficient * builder.Offset;
268 if (coefficient == 1)
269 {
270 foreach (Term sub in builder.Terms)
271 {
272 terms.Enqueue(sub);
273 }
274 }
275 else
276 {
277 foreach (Term sub in builder.Terms)
278 {
279 terms.Enqueue(new Term(sub.expr, sub.coefficient * coefficient));
280 }
281 }
282 break;
283 case Variable var:
284 if (dict.TryGetValue(var.Index, out var c))
285 {
286 dict[var.Index] = c + coefficient;
287 }
288 else
289 {
290 dict.Add(var.Index, coefficient);
291 }
292 break;
293 default:
294 throw new ArgumentException("Cannot evaluate '" + expr + "' in an expression");
295 }
296
297 if (!terms.TryDequeue(out var term))
298 {
299 break;
300 }
301 expr = term.expr;
302 coefficient = term.coefficient;
303 } while (true);
304
305 return constant;
306 }
307}
308
310public sealed class LinearExprBuilder : LinearExpr
311{
312 public LinearExprBuilder(int sizeHint = 2)
313 {
314 terms_ = new List<Term>(sizeHint);
315 offset_ = 0;
316 }
317
320 {
321 return AddTerm(expr, 1);
322 }
323
325 public LinearExprBuilder Add(double constant)
326 {
327 offset_ += constant;
328 return this;
329 }
330
332 public LinearExprBuilder AddTerm(LinearExpr expr, double coefficient)
333 {
334 terms_.Add(new Term(expr, coefficient));
335 return this;
336 }
337
339 public LinearExprBuilder AddSum(IEnumerable<LinearExpr> exprs)
340 {
341 terms_.TryEnsureCapacity(exprs);
342 foreach (LinearExpr expr in exprs)
343 {
344 AddTerm(expr, 1);
345 }
346 return this;
347 }
348
350 public LinearExprBuilder AddWeightedSum(IEnumerable<LinearExpr> exprs, IEnumerable<double> coefficients)
351 {
352 terms_.TryEnsureCapacity(exprs);
353 foreach (var p in exprs.Zip(coefficients, (e, c) => new { Expr = e, Coeff = c }))
354 {
355 AddTerm(p.Expr, p.Coeff);
356 }
357 return this;
358 }
359
361 public LinearExprBuilder AddWeightedSum(IEnumerable<LinearExpr> exprs, IEnumerable<long> coefficients)
362 {
363 terms_.TryEnsureCapacity(exprs);
364 foreach (var p in exprs.Zip(coefficients, (e, c) => new { Expr = e, Coeff = c }))
365 {
366 AddTerm(p.Expr, p.Coeff);
367 }
368 return this;
369 }
370
372 public LinearExprBuilder AddWeightedSum(IEnumerable<LinearExpr> exprs, IEnumerable<int> coefficients)
373 {
374 terms_.TryEnsureCapacity(exprs);
375 foreach (var p in exprs.Zip(coefficients, (e, c) => new { Expr = e, Coeff = c }))
376 {
377 AddTerm(p.Expr, p.Coeff);
378 }
379 return this;
380 }
381 public override string ToString()
382 {
383 string result = "";
384 foreach (Term term in terms_)
385 {
386 bool first = String.IsNullOrEmpty(result);
387 if (term.expr is null || term.coefficient == 0)
388 {
389 continue;
390 }
391 if (term.coefficient == 1)
392 {
393 if (!first)
394 {
395 result += " + ";
396 }
397
398 result += term.expr.ToString();
399 }
400 else if (term.coefficient > 0)
401 {
402 if (!first)
403 {
404 result += " + ";
405 }
406
407 result += String.Format("{0} * {1}", term.coefficient, term.expr.ToString());
408 }
409 else if (term.coefficient == -1)
410 {
411 if (!first)
412 {
413 result += String.Format(" - {0}", term.expr.ToString());
414 }
415 else
416 {
417 result += String.Format("-{0}", term.expr.ToString());
418 }
419 }
420 else
421 {
422 if (!first)
423 {
424 result += String.Format(" - {0} * {1}", -term.coefficient, term.expr.ToString());
425 }
426 else
427 {
428 result += String.Format("{0} * {1}", term.coefficient, term.expr.ToString());
429 }
430 }
431 }
432 if (offset_ > 0)
433 {
434 if (!String.IsNullOrEmpty(result))
435 {
436 result += String.Format(" + {0}", offset_);
437 }
438 else
439 {
440 result += String.Format("{0}", offset_);
441 }
442 }
443 else if (offset_ < 0)
444 {
445 if (!String.IsNullOrEmpty(result))
446 {
447 result += String.Format(" - {0}", -offset_);
448 }
449 else
450 {
451 result += String.Format("{0}", offset_);
452 }
453 }
454 return result;
455 }
456
457 public double Offset
458 {
459 get {
460 return offset_;
461 }
462 }
463
464 public List<Term> Terms
465 {
466 get {
467 return terms_;
468 }
469 }
470
471 private double offset_;
472 private List<Term> terms_;
473}
474
483public class Variable : LinearExpr
484{
485 public Variable(ModelBuilderHelper helper, double lb, double ub, bool isIntegral, string name)
486 {
487 helper_ = helper;
491 helper_.SetVarIntegrality(index_, isIntegral);
492 if (!string.IsNullOrEmpty(name))
493 {
495 }
496 }
497
498 public Variable(ModelBuilderHelper helper, int index)
499 {
500 helper_ = helper;
501 index_ = index;
502 }
503
505 public int Index
506 {
507 get {
508 return index_;
509 }
510 }
511
514 {
515 get {
516 return helper_;
517 }
518 }
519
521 public double LowerBound
522 {
523 get {
525 }
526 set {
528 }
529 }
530
531 public double UpperBound
532 {
533 get {
535 }
536 set {
538 }
539 }
540
541 public override string ToString()
542 {
543 string name = helper_.VarName(index_);
544 return string.IsNullOrEmpty(name) ? String.Format("var_{0}", index_) : name;
545 }
546
548 public string Name
549 {
550 get {
551 return helper_.VarName(index_);
552 }
553 }
554
555 protected readonly int index_;
557}
558
567public sealed class BoundedLinearExpression
568{
569 public enum Type
570 {
572 VarEqVar,
574 VarEqCst,
576 }
577
578 public BoundedLinearExpression(double lb, LinearExpr expr, double ub)
579 {
580 left_ = expr;
581 right_ = null;
582 lb_ = lb;
583 ub_ = ub;
584 type_ = Type.BoundExpression;
585 }
586
587 public BoundedLinearExpression(LinearExpr left, LinearExpr right, bool equality)
588 {
589 left_ = left;
590 right_ = right;
591 lb_ = 0;
592 ub_ = 0;
593 type_ = equality ? Type.VarEqVar : Type.VarDiffVar;
594 }
595
596 public BoundedLinearExpression(LinearExpr left, double v, bool equality)
597 {
598 left_ = left;
599 right_ = null;
600 lb_ = v;
601 ub_ = 0;
602 type_ = equality ? Type.VarEqCst : Type.VarDiffCst;
603 }
604
605 bool IsTrue()
606 {
607 if (type_ == Type.VarEqVar)
608 {
609 return (object)left_ == (object)right_;
610 }
611 else if (type_ == Type.VarDiffVar)
612 {
613 return (object)left_ != (object)right_;
614 }
615 return false;
616 }
617
618 public static bool operator true(BoundedLinearExpression ble)
619 {
620 return ble.IsTrue();
621 }
622
623 public static bool operator false(BoundedLinearExpression ble)
624 {
625 return !ble.IsTrue();
626 }
627
628 public override string ToString()
629 {
630 switch (type_)
631 {
632 case Type.BoundExpression:
633 return String.Format("{0} <= {1} <= {2}", lb_, left_, ub_);
634 case Type.VarEqVar:
635 return String.Format("{0} == {1}", left_, right_);
636 case Type.VarDiffVar:
637 return String.Format("{0} != {1}", left_, right_);
638 case Type.VarEqCst:
639 return String.Format("{0} == {1}", left_, lb_);
640 case Type.VarDiffCst:
641 return String.Format("{0} != {1}", left_, lb_);
642 default:
643 throw new ArgumentException("Wrong mode in BoundedLinearExpression.");
644 }
645 }
646
648 {
649 if (a.CtType != Type.BoundExpression || a.Ub != Double.PositiveInfinity)
650 {
651 throw new ArgumentException("Operator <= not supported for this BoundedLinearExpression");
652 }
653 return new BoundedLinearExpression(a.Lb, a.Left, v);
654 }
655
657 {
658 if (a.CtType != Type.BoundExpression || a.Lb != Double.NegativeInfinity)
659 {
660 throw new ArgumentException("Operator >= not supported for this BoundedLinearExpression");
661 }
662 return new BoundedLinearExpression(v, a.Left, a.Ub);
663 }
664
666 {
667 get {
668 return left_;
669 }
670 }
671
673 {
674 get {
675 return right_;
676 }
677 }
678
679 public double Lb
680 {
681 get {
682 return lb_;
683 }
684 }
685
686 public double Ub
687 {
688 get {
689 return ub_;
690 }
691 }
692
694 {
695 get {
696 return type_;
697 }
698 }
699
700 private LinearExpr left_;
701 private LinearExpr right_;
702 private double lb_;
703 private double ub_;
704 private Type type_;
705}
706
707} // namespace Google.OrTools.ModelBuilder
Holds a linear constraint: expression ∈ domain
BoundedLinearExpression(double lb, LinearExpr expr, double ub)
static BoundedLinearExpression operator<=(BoundedLinearExpression a, double v)
static BoundedLinearExpression operator>=(BoundedLinearExpression a, double v)
BoundedLinearExpression(LinearExpr left, double v, bool equality)
BoundedLinearExpression(LinearExpr left, LinearExpr right, bool equality)
A builder class for linear expressions.
LinearExprBuilder AddWeightedSum(IEnumerable< LinearExpr > exprs, IEnumerable< double > coefficients)
Adds sum(exprs[i] * coeffs[i]) to the builder.
LinearExprBuilder AddWeightedSum(IEnumerable< LinearExpr > exprs, IEnumerable< int > coefficients)
Adds sum(exprs[i] * coeffs[i]) to the builder.
LinearExprBuilder AddTerm(LinearExpr expr, double coefficient)
Adds expr * coefficient to the builder.
LinearExprBuilder AddSum(IEnumerable< LinearExpr > exprs)
Adds sum(exprs) to the builder.
LinearExprBuilder Add(double constant)
Adds constant to the builder.
LinearExprBuilder Add(LinearExpr expr)
Adds expr to the builder.
LinearExprBuilder AddWeightedSum(IEnumerable< LinearExpr > exprs, IEnumerable< long > coefficients)
Adds sum(exprs[i] * coeffs[i]) to the builder.
Holds a linear expression: sum (ai * xi) + b.
static LinearExpr operator*(LinearExpr a, double v)
static LinearExpr operator+(LinearExpr a, LinearExpr b)
static LinearExpr Sum(IEnumerable< LinearExpr > exprs)
Creates Sum(exprs).
static LinearExpr WeightedSum(IEnumerable< LinearExpr > exprs, IEnumerable< int > coeffs)
Creates Sum(exprs[i] * coeffs[i]).
static LinearExpr operator-(LinearExpr a, LinearExpr b)
static LinearExpr Constant(double value)
Creates a constant expression.
static LinearExprBuilder NewBuilder(int sizeHint=2)
Creates a builder class for linear expression.
static BoundedLinearExpression operator>=(LinearExpr a, double v)
static BoundedLinearExpression operator<=(LinearExpr a, double v)
static BoundedLinearExpression operator!=(LinearExpr a, LinearExpr b)
static LinearExpr Affine(LinearExpr expr, double coeff, double offset)
Creates expr * coeff + offset.
static BoundedLinearExpression operator==(LinearExpr a, LinearExpr b)
static LinearExpr WeightedSum(IEnumerable< LinearExpr > exprs, IEnumerable< double > coeffs)
Creates Sum(exprs[i] * coeffs[i]).
static LinearExpr Term(LinearExpr expr, double coeff)
Creates expr * coeff.
void SetVarIntegrality(int var_index, bool is_integer)
Variable(ModelBuilderHelper helper, int index)
Variable(ModelBuilderHelper helper, double lb, double ub, bool isIntegral, string name)
Holds a term (expression * coefficient)