add ModelBuilder class in C#
This commit is contained in:
336
ortools/linear_solver/csharp/ModelBuilder.cs
Normal file
336
ortools/linear_solver/csharp/ModelBuilder.cs
Normal file
@@ -0,0 +1,336 @@
|
||||
// Copyright 2010-2022 Google LLC
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
namespace Google.OrTools.ModelBuilder
|
||||
{
|
||||
|
||||
using Google.OrTools.Util;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Google.Protobuf.Collections;
|
||||
|
||||
/// <summary>
|
||||
/// Main modeling class.
|
||||
/// </summary>
|
||||
///
|
||||
/// Proposes a factory to create all modeling objects understood by the ModelSolver.
|
||||
public class ModelBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Main constructor.
|
||||
/// </summary>
|
||||
public ModelBuilder()
|
||||
{
|
||||
helper_ = new ModelBuilderHelper();
|
||||
constantMap_ = new Dictionary<double, int>();
|
||||
var_value_map_ = new SortedDictionary<int, double>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a cloned model.
|
||||
/// </summary>
|
||||
/// <returns>A deep copy of the model.</returns>
|
||||
public ModelBuilder Clone()
|
||||
{
|
||||
ModelBuilder clonedModel = new ModelBuilder();
|
||||
clonedModel.Helper.OverwriteModel(Helper);
|
||||
foreach (KeyValuePair<double, int> entry in constantMap_)
|
||||
{
|
||||
clonedModel.constantMap_[entry.Key] = entry.Value;
|
||||
}
|
||||
return clonedModel;
|
||||
}
|
||||
|
||||
// Integer variables.
|
||||
|
||||
/// <summary>
|
||||
/// reates a variable with domain [lb, ub].
|
||||
/// </summary>
|
||||
/// <param name="lb">The lower bound of the variable</param>
|
||||
/// <param name="ub">The upper bound of the variable</param>
|
||||
/// <param name="isIntegral">Indicates if the variable is restricted to take only integral values</param>
|
||||
/// <param name="name">The name of the variable</param>
|
||||
/// <returns>The new variable</returns>
|
||||
public Variable NewVar(double lb, double ub, bool isIntegral, String name)
|
||||
{
|
||||
return new Variable(helper_, lb, ub, isIntegral, name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a continuous variable with domain [lb, ub].
|
||||
/// </summary>
|
||||
/// <param name="lb">The lower bound of the variable</param>
|
||||
/// <param name="ub">The upper bound of the variable</param>
|
||||
/// <param name="name">The name of the variable</param>
|
||||
/// <returns>The new continuous variable</returns>
|
||||
public Variable NewNumVar(double lb, double ub, String name)
|
||||
{
|
||||
return new Variable(helper_, lb, ub, false, name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an integer variable with domain [lb, ub].
|
||||
/// </summary>
|
||||
/// <param name="lb">The lower bound of the variable</param>
|
||||
/// <param name="ub">The upper bound of the variable</param>
|
||||
/// <param name="name">The name of the variable</param>
|
||||
/// <returns>The new integer variable</returns>
|
||||
public Variable NewIntVar(double lb, double ub, String name)
|
||||
{
|
||||
return new Variable(helper_, lb, ub, true, name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a bool variable with the given name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the variable</param>
|
||||
/// <returns>The new Boolean variable</returns>
|
||||
public Variable NewBoolVar(String name)
|
||||
{
|
||||
return new Variable(helper_, 0, 1, true, name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a constant variable.
|
||||
/// </summary>
|
||||
/// <param name="value">the value of the constant variable</param>
|
||||
/// <returns>A new variable with a fixed value</returns>
|
||||
public Variable NewConstant(double value)
|
||||
{
|
||||
if (constantMap_.TryGetValue(value, out int index))
|
||||
{
|
||||
return new Variable(helper_, index);
|
||||
}
|
||||
Variable cste = new Variable(helper_, value, value, false, ""); // bounds and name.
|
||||
constantMap_.Add(value, cste.Index);
|
||||
return cste;
|
||||
}
|
||||
|
||||
/// Rebuilds a variable from its index.
|
||||
public Variable VarFromIndex(int index)
|
||||
{
|
||||
return new Variable(helper_, index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a Linear constraint to the model.
|
||||
/// </summary>
|
||||
/// <param name="lin">A bounded linear expression</param>
|
||||
/// <returns>A linear expression</returns>
|
||||
/// <exception cref="ArgumentException">Throw when the constraint is not supported by the linear solver</exception>
|
||||
public LinearConstraint Add(BoundedLinearExpression lin)
|
||||
{
|
||||
switch (lin.CtType)
|
||||
{
|
||||
case BoundedLinearExpression.Type.BoundExpression: {
|
||||
return AddLinearConstraint(lin.Left, lin.Lb, lin.Ub);
|
||||
}
|
||||
case BoundedLinearExpression.Type.VarEqVar: {
|
||||
return AddLinearConstraint(lin.Left - lin.Right, 0, 0);
|
||||
}
|
||||
case BoundedLinearExpression.Type.VarEqCst: {
|
||||
return AddLinearConstraint(lin.Left, lin.Lb, lin.Lb);
|
||||
}
|
||||
default: {
|
||||
throw new ArgumentException("Cannot use '" + lin.ToString() + "' as a linear constraint.");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the constraint expr in [lb, ub].
|
||||
/// </summary>
|
||||
/// <param name="expr">The constrained expression</param>
|
||||
/// <param name="lb">the constrained lower bound of the expression</param>
|
||||
/// <param name="ub">the constrained upper bound of the expression</param>
|
||||
/// <returns>the linear constraint</returns>
|
||||
public LinearConstraint AddLinearConstraint(LinearExpr expr, double lb, double ub)
|
||||
{
|
||||
var dict = var_value_map_;
|
||||
dict.Clear();
|
||||
double offset = LinearExpr.GetVarValueMap(expr, dict, terms_);
|
||||
LinearConstraint lin = new LinearConstraint(helper_);
|
||||
foreach (KeyValuePair<int, double> term in dict)
|
||||
{
|
||||
helper_.AddConstraintTerm(lin.Index, term.Key, term.Value);
|
||||
}
|
||||
if (lb == Double.NegativeInfinity || lb == Double.PositiveInfinity)
|
||||
{
|
||||
lin.LowerBound = lb;
|
||||
}
|
||||
else
|
||||
{
|
||||
lin.LowerBound = lb - offset;
|
||||
}
|
||||
if (ub == Double.NegativeInfinity || ub == Double.PositiveInfinity)
|
||||
{
|
||||
lin.UpperBound = ub;
|
||||
}
|
||||
else
|
||||
{
|
||||
lin.UpperBound = ub - offset;
|
||||
}
|
||||
return lin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of variables in the model.
|
||||
/// </summary>
|
||||
public int VariablesCount()
|
||||
{
|
||||
return helper_.VariablesCount();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of constraints in the model.
|
||||
/// </summary>
|
||||
public int ConstraintsCount()
|
||||
{
|
||||
return helper_.ConstraintsCount();
|
||||
}
|
||||
|
||||
/// Rebuilds a linear constraint from its index.
|
||||
public LinearConstraint ConstraintFromIndex(int index)
|
||||
{
|
||||
return new LinearConstraint(helper_, index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Minimize expression.
|
||||
/// </summary>
|
||||
/// <param name="obj">the linear expression to minimize</param>
|
||||
public void Minimize(LinearExpr obj)
|
||||
{
|
||||
Optimize(obj, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximize expression.
|
||||
/// </summary>
|
||||
/// <param name="obj">the linear expression to maximize</param>
|
||||
public void Maximize(LinearExpr obj)
|
||||
{
|
||||
Optimize(obj, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the objective expression.
|
||||
/// </summary>
|
||||
/// <param name="obj">the linear expression to optimize</param>
|
||||
/// <param name="maximize">the direction of the optimization</param>
|
||||
public void Optimize(LinearExpr obj, bool maximize)
|
||||
{
|
||||
helper_.ClearObjective();
|
||||
double offset = LinearExpr.GetVarValueMap(expr, dict, terms_);
|
||||
LinearConstraint lin = new LinearConstraint(helper_);
|
||||
foreach (KeyValuePair<int, double> term in dict)
|
||||
{
|
||||
if (term.Value != 0.0)
|
||||
{
|
||||
helper_.setVarObjectiveCoefficient(term.Key, term.Value);
|
||||
}
|
||||
}
|
||||
helper_.SetObjectiveOffset(e.getOffset());
|
||||
helper_.SetMaximize(maximize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The offset of the objective.
|
||||
/// </summary>
|
||||
public double ObjectiveOffset
|
||||
{
|
||||
get {
|
||||
return helper_.ObjectiveOffset();
|
||||
}
|
||||
set {
|
||||
helper_.SetObjectiveOffset(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The name of the model.
|
||||
/// </summary>
|
||||
public String Name
|
||||
{
|
||||
get {
|
||||
return helper_.Name();
|
||||
}
|
||||
set {
|
||||
helper_.SetName(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the model as a protocol buffer to 'file'.
|
||||
/// </summary>
|
||||
/// @param file file to write the model to. If the filename ends with 'txt', the model will be
|
||||
/// written as a text file, otherwise, the binary format will be used.
|
||||
///@return true if the model was correctly written.
|
||||
public bool ExportToFile(String file)
|
||||
{
|
||||
return helper_.WriteModelToFile(file);
|
||||
}
|
||||
|
||||
public String ExportToMpsString(bool obfuscate)
|
||||
{
|
||||
return helper_.ExportToMpsString(obfuscate);
|
||||
}
|
||||
|
||||
public String ExportToLpString(bool obfuscate)
|
||||
{
|
||||
return helper_.ExportToLpString(obfuscate);
|
||||
}
|
||||
|
||||
public bool ImportFromMpsString(String mpsString)
|
||||
{
|
||||
return helper_.ImportFromMpsString(mpsString);
|
||||
}
|
||||
|
||||
public bool ImportFromMpsFile(String mpsFile)
|
||||
{
|
||||
return helper_.ImportFromMpsString(mpsFile);
|
||||
}
|
||||
|
||||
public bool ImportFromLpString(String lpString)
|
||||
{
|
||||
return helper_.ImportFromLpString(lpString);
|
||||
}
|
||||
|
||||
public bool ImportFromLpFile(String lpFile)
|
||||
{
|
||||
return helper_.ImportFromMpsString(lpFile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The model builder helper.
|
||||
/// </summary>
|
||||
public ModelBuilderHelper Helper
|
||||
{
|
||||
get {
|
||||
return helper_;
|
||||
}
|
||||
}
|
||||
|
||||
private ModelBuilderHelper helper_;
|
||||
private Dictionary<double, int> constantMap_;
|
||||
|
||||
// Used to process linear exppressions.
|
||||
private SortedDictionary<int, double> var_value_map_;
|
||||
private Queue<Term> terms_;
|
||||
}
|
||||
|
||||
} // namespace Google.OrTools.ModelBuilder
|
||||
@@ -13,77 +13,93 @@
|
||||
|
||||
namespace Google.OrTools.ModelBuilder
|
||||
{
|
||||
using Google.OrTools.Util;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Google.Protobuf.Collections;
|
||||
using Google.OrTools.Util;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Google.Protobuf.Collections;
|
||||
|
||||
/** Wrapper around a linear constraint stored in the ModelBuilderHelper instance. */
|
||||
public class LinearConstraint
|
||||
{
|
||||
/** Wrapper around a linear constraint stored in the ModelBuilderHelper instance. */
|
||||
public class LinearConstraint
|
||||
{
|
||||
public LinearConstraint(ModelBuilderHelper helper)
|
||||
{
|
||||
helper_ = helper;
|
||||
index_ = helper_.AddLinearConstraint();
|
||||
helper_ = helper;
|
||||
index_ = helper_.AddLinearConstraint();
|
||||
}
|
||||
|
||||
LinearConstraint(ModelBuilderHelper helper, int index)
|
||||
public LinearConstraint(ModelBuilderHelper helper, int index)
|
||||
{
|
||||
helper_ = helper;
|
||||
index_ = index;
|
||||
helper_ = helper;
|
||||
index_ = index;
|
||||
}
|
||||
|
||||
/** Returns the index of the constraint in the model. */
|
||||
public int Index
|
||||
{
|
||||
get { return index_; }
|
||||
get {
|
||||
return index_;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the constraint builder. */
|
||||
public ModelBuilderHelper Helper
|
||||
{
|
||||
get { return helper_; }
|
||||
get {
|
||||
return helper_;
|
||||
}
|
||||
}
|
||||
|
||||
/** The lower bound of the constraint. */
|
||||
public double LowerBound
|
||||
{
|
||||
get { return helper_.ConstraintLowerBound(index_); }
|
||||
set { helper_.SetConstraintLowerBound(index_, value); }
|
||||
get {
|
||||
return helper_.ConstraintLowerBound(index_);
|
||||
}
|
||||
set {
|
||||
helper_.SetConstraintLowerBound(index_, value);
|
||||
}
|
||||
}
|
||||
|
||||
/** The upper bound of the constraint. */
|
||||
public double UpperBound
|
||||
{
|
||||
get { return helper_.ConstraintUpperBound(index_); }
|
||||
set { helper_.SetConstraintUpperBound(index_, value); }
|
||||
get {
|
||||
return helper_.ConstraintUpperBound(index_);
|
||||
}
|
||||
set {
|
||||
helper_.SetConstraintUpperBound(index_, value);
|
||||
}
|
||||
}
|
||||
|
||||
/** The name of the variable given upon creation. */
|
||||
public String Name
|
||||
{
|
||||
get { return helper_.ConstraintName(index_); }
|
||||
set { helper_.SetConstraintName(index_, value); }
|
||||
get {
|
||||
return helper_.ConstraintName(index_);
|
||||
}
|
||||
set {
|
||||
helper_.SetConstraintName(index_, value);
|
||||
}
|
||||
}
|
||||
|
||||
// Adds var * coeff to the constraint.
|
||||
public void AddTerm(Variable var, double coeff)
|
||||
{
|
||||
helper_.AddConstraintTerm(index_, var.Index, coeff);
|
||||
helper_.AddConstraintTerm(index_, var.Index, coeff);
|
||||
}
|
||||
|
||||
/** Inline setter */
|
||||
public LinearConstraint WithName(String name)
|
||||
{
|
||||
Name = name;
|
||||
return this;
|
||||
Name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
private readonly int index_;
|
||||
private ModelBuilderHelper helper_;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Google.OrTools.ModelBuilder
|
||||
@@ -23,22 +23,22 @@ using Google.Protobuf.Collections;
|
||||
|
||||
internal static class HelperExtensions
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void AddOrIncrement(this Dictionary<int, double> dict, int key, double increment)
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
System.Runtime.InteropServices.CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out _) += increment;
|
||||
#else
|
||||
if (dict.TryGetValue(key, out var value))
|
||||
{
|
||||
dict[key] = value + increment;
|
||||
}
|
||||
else
|
||||
{
|
||||
dict.Add(key, increment);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// public static void AddOrIncrement(this SortedDictionary<int, double> dict, int key, double increment)
|
||||
// {
|
||||
// #if NET6_0_OR_GREATER
|
||||
// System.Runtime.InteropServices.CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out _) += increment;
|
||||
// #else
|
||||
// if (dict.TryGetValue(key, out var value))
|
||||
// {
|
||||
// dict[key] = value + increment;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// dict.Add(key, increment);
|
||||
// }
|
||||
// #endif
|
||||
// }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void TrySetCapacity<TField, TValues>(this RepeatedField<TField> field, IEnumerable<TValues> values)
|
||||
@@ -194,7 +194,7 @@ public class LinearExpr
|
||||
public static BoundedLinearExpression operator !=(LinearExpr a, LinearExpr b)
|
||||
{
|
||||
return new BoundedLinearExpression(a, b, false);
|
||||
}
|
||||
}
|
||||
|
||||
public static BoundedLinearExpression operator ==(LinearExpr a, double v)
|
||||
{
|
||||
@@ -252,7 +252,7 @@ public class LinearExpr
|
||||
}
|
||||
}
|
||||
|
||||
internal static double GetVarValueMap(LinearExpr e, Dictionary<int, double> dict, Queue<Term> terms)
|
||||
internal static double GetVarValueMap(LinearExpr e, SortedDictionary<int, double> dict, Queue<Term> terms)
|
||||
{
|
||||
double constant = 0;
|
||||
double coefficient = 1;
|
||||
@@ -281,7 +281,11 @@ public class LinearExpr
|
||||
}
|
||||
break;
|
||||
case Variable var:
|
||||
dict.AddOrIncrement(var.Index, coefficient);
|
||||
if (dict.TryGetValue(var.Index, out var c)) {
|
||||
dict[var.Index] = c + coefficient;
|
||||
} else {
|
||||
dict.Add(var.Index, coefficient);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("Cannot evaluate '" + expr + "' in an expression");
|
||||
@@ -475,14 +479,15 @@ public sealed class LinearExprBuilder : LinearExpr
|
||||
*/
|
||||
public class Variable : LinearExpr
|
||||
{
|
||||
public Variable(ModelBuilderHelper helper, double lb, double ub, bool isIntegral, string name)
|
||||
public Variable(ModelBuilderHelper helper, double lb, double ub, bool isIntegral, string name)
|
||||
{
|
||||
helper_ = helper;
|
||||
index_ = helper_.AddVar();
|
||||
helper_.SetVarLowerBound(index_, lb);
|
||||
helper_.SetVarUpperBound(index_, ub);
|
||||
helper_.SetVarIntegrality(index_, isIntegral);
|
||||
if (!string.IsNullOrEmpty(name)) {
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
helper_.SetVarName(index_, name);
|
||||
}
|
||||
}
|
||||
@@ -528,7 +533,7 @@ public class Variable : LinearExpr
|
||||
set {
|
||||
helper_.SetVarUpperBound(index_, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
@@ -600,7 +605,7 @@ public sealed class BoundedLinearExpression
|
||||
{
|
||||
return (object)left_ == (object)right_;
|
||||
}
|
||||
else if (type_ == Type.VarDiffVar)
|
||||
else if (type_ == Type.VarDiffVar)
|
||||
{
|
||||
return (object)left_ != (object)right_;
|
||||
}
|
||||
@@ -638,7 +643,7 @@ public sealed class BoundedLinearExpression
|
||||
|
||||
public static BoundedLinearExpression operator <=(BoundedLinearExpression a, double v)
|
||||
{
|
||||
if (a.CtType != Type.BoundExpression || a.Ub !=Double.PositiveInfinity)
|
||||
if (a.CtType != Type.BoundExpression || a.Ub != Double.PositiveInfinity)
|
||||
{
|
||||
throw new ArgumentException("Operator <= not supported for this BoundedLinearExpression");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user