Gurobi Notes

   关于Gurobi的Python API…

   Gurobi Notes (Python version)
   Author: 有何不可
   Reference book: GUROBI OPTIMIZER REFERENCE MANUAL,(提取码:s4kv )
   Updated time: 2019/4/4

gurobipy lib

1
2
# import gurobipy lib
from gurobipy import *

Model

   A model consists of a set of variables, a linear, quadratic, or piecewise-linear objective function on those variables, and a set of constraints.

Common Optimization Model

  • Linear Program (LP)
    A model with a linear objective function, linear constraints, and continuous variables.
  • Quadratic Program (QP)
    The objective is quadratic.
  • Quadratically-Constrained Program (QCP)
    The objective is quadratic, any of the constraints are quadratic.
  • Second-Order Cone Program (SOCP)
  • Mixed Integer Program (MIP)
    the model contains any integer variables, semi-continuous variables, semi-integer variables, Special Ordered Set (SOS) constraints, or general constraints.
  • Mixed Integer Linear Programs (MILP)
  • Mixed Integer Quadratic Programs (MIQP)
  • Mixed Integer Quadratically-Constrained Programs (MIQCP)
  • Mixed Integer Second-Order Cone Programs (MISOCP)

   The Gurobi Optimizer handles all of these models.

Infeasible Models

   You have a few options if a model is found to be infeasible. You can try to diagnose the cause of the infeasibility, attempt to repair the infeasibility, or both. To obtain information that can be useful for diagnosing the cause of an infeasibility, call GRBcomputeIIS to compute an Irreducible Inconsistent Subsystem (IIS). This routine can be used for both continuous and MIP models, but you should be aware that the MIP version can be quite expensive. This routine populates a set of IIS attributes.
   To attempt to repair an infeasibility, call GRBfeasrelax to compute a feasibility relaxation for the model. This relaxation allows you to find a solution that minimizes the magnitude of the constraint violation.

Attributes

   Most of the information associated with a Gurobi model is stored in a set of attributes. Some attributes are associated with the variables of the model, some with the constraints of the model, and some with the model itself.

Lazy Updates

   The Gurobi optimizer employs a lazy update approach, so changes to attributes don’t take effect until the next call to Model.update, Model.optimize, Model.write on the associated model.

Model File Formats

   The Gurobi optimizer works with a variety of file formats.
   The MPS, REW, LP, RLP, ILP, OPB formats are used to hold optimization models.
   The MST format is used to hold MIP start data. Importing this data into a MIP model allows the MIP model to start with a known feasible solution.
   The HNT format is used to hold MIP hints. Importing this data into a MIP model guides the MIP search towards a guess at a high-quality feasible solution.
   The ORD format is used to hold MIP variable branching priorities. Importing this data into a MIP model affects the search strategy.
   The BAS format holds simplex basis information. Importing this data into a continuous models allows the simplex algorithm to start from the given simplex basis.
   The SOL format holds a solution vector. It can be written once the model has been optimized.
   The PRM format holds parameter values. Importing this data into a model changes the values of the referenced parameters.

1
2
# Initiate a model
model = Model()
Academic license - for non-commercial use only

Var(Gurobi variable object)

   Before starting, we should point out one important thing about the variables in a mathematical programming model: their computed solution values will only satisfy bounds to tolerances, meaning that a variable may violate its stated bounds. Mathematical programming is fundamentally built on top of linear algebra and in particular on the numerical solution of systems of linear equations. These linear systems are solved using finite-precision arithmetic, which means that small errors are unavoidable. For some models, large errors are unavoidable too.

Variable Types

   In gurobi, the available variables types includes:

  • Continuous
  • General integer
  • Binary
  • Semi-continuous
  • Semi-integer.

Create Decision variables

   In Gurobi, we can create a variable object by adding a variable to a model rather than by using a Var constrcutor.
   There are two ways to create variables, as follow, and the Model.addVars refers the tupledict section

1
2
3
4
# Add a decision variable to a model
Model.addVar(lb=0.0, ub=GRB.INFINITY, obj=0.0, vtype=GRB.CONTINUOUS, name="", column=None)
# Add multiple decision variables to a model
Model.addVars(*indices, lb=0.0, ub=GRB.INFINITY, obj=0.0, vtype=GRB.CONTINUOUS, name="")

   About “name” : the variable x in Python is used to operate. In name="x", the `x’ is used to replace the CX(e.g. C0, C1…) in order to represent the expression in the way of user.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Create decision variables
# Method 1 --> Model.addVar()
x = model.addVar(lb=1.0, ub=GRB.INFINITY, obj=0.0, vtype=GRB.CONTINUOUS, name="x")
y = model.addVar(name="y")
z = model.addVar(name="z")
x_bin = model.addVar(vtype=GRB.BINARY) # Binary variable
y_bin = model.addVar(vtype=GRB.BINARY) # Binary variable

# Method 2 --> MOodel.addVars()
x_vector = [('x1'), ('x2'), ('x3')]
# Return tupledict type, the key must be a tuple type
x_tudict = model.addVars(x_vector, name= x_vector)
expr0 = x_tudict['x1'] + 2*x_tudict['x2'] + x_tudict['x3']

# Print data
print('==> Model no updated:' ,x,y,z)
# Update model
model.update()
print('==> Model updated:' ,x,y,z)
print('='*25)
print('==> x_tudict:', x_tudict)
print('==> expr0:', expr0)
==> Model no updated: <gurobi.Var *Awaiting Model Update*> <gurobi.Var *Awaiting Model Update*> <gurobi.Var *Awaiting Model Update*>
==> Model updated: <gurobi.Var x> <gurobi.Var y> <gurobi.Var z>
=========================
==> x_tudict: {'x1': <gurobi.Var x1>, 'x2': <gurobi.Var x2>, 'x3': <gurobi.Var x3>}
==> expr0: <gurobi.LinExpr: x1 + 2.0 x2 + x3>
1
2
3
4
5
6
7
8
9
10
11
12
# Get attribute of variable
print('==> Get attrs:', [(x.getAttr(GRB.Attr.LB), x.getAttr("ub")), ((y.getAttr(GRB.Attr.LB), y.getAttr("ub")))])

# Set attribute of variable
x.setAttr("lb", 5)
x.setAttr("ub", 100)
# Update model
model.update()
attr_lb, attr_ub = x.getAttr("lb"), x.getAttr("ub")
print('='*25)
print('==> attr_lb:', attr_lb)
print('==> attr_ub:', attr_ub)
==> Get attrs: [(1.0, 1e+100), (0.0, 1e+100)]
=========================
==> attr_lb: 5.0
==> attr_ub: 100.0

Gurobi constraint

Constr(Gurobi constraint object)

   In Gurobi, we can create a constraint object by adding a constraint to a model rather than by using a Constr constrcutor.
   There are two ways, as follow,

1
2
3
4
# Add a constraint to a model.
Model.addConstr(lhs, sense=None, rhs=None, name="" )
# Add multiple constraints to a model.
Model.addConstrs(generator, name="")


1
2
3
4
5
6
7
# Add a linear constraint to a model
Model.addLConstr(lhs, sense=None, rhs=None, name="")

# Add a range constraint to a model.
# A range constraint states that the value of the input expression must be between the specified lower and upper bounds.
# Para expr can be a Linear expression or a variable.
Model.addRange(expr, lower, upper, name="" )
1
2
3
4
5
6
7
8
9
10
11
# Add constraints to a model
c0 = model.addConstr(lhs=x+y, sense=GRB.EQUAL, rhs=4, name="c0")
c1 = model.addConstr(lhs=z, sense=GRB.GREATER_EQUAL, rhs=0, name="c1")
# Update model
model.update()
print('==> c0:',c0, 'c1:', c1)

# Get attributes
attr_rhs = c0.getAttr(GRB.Attr.RHS)
print('='*25)
print('==> RHS:', attr_rhs)
==> c0: <gurobi.Constr c0> c1: <gurobi.Constr c1>
=========================
==> RHS: 4.0

QConstr(Gurobi quadratic constraint object)

   Add a quadratic constraint to a model, as follow,

1
2
# Add a quadratic constraint to a model.
Model.addQConst(lhs, sense=None, rhs=None, name="")

   Important note:
  The algorithms that Gurobi uses to solve quadratically constrained problems can only handle certain types of quadratic constraints. Constraints of the following forms are always accepted:
$$ x^TQx + q^Tx <= b $$
$$ x^Tx <= y^2 $$
$$ x^Tx <= yz $$

1
2
3
4
5
# Add quadratic constraints to a model
qc0 = model.addQConstr(lhs=x*x+y*y, sense=GRB.LESS_EQUAL, rhs=z*z, name="qc0")
# Update model
model.update()
print('==> qc0:', qc0)
==> qc0: <gurobi.QConstr qc0>

GenConstr(Gurobi general constraint object)

   General constraints are always associated with a particular model. You add a general constraint to a model either by using Model.addGenConstr, or by using Model.addConstr or Model.addConstrs plus a general constraint helper function).
   Refer the TempConstr section to add a general constraint to a model.

TempConstr(Gurobi temporary constraint object)

   Objects of this class are created as intermediate results when building constraints using overloaded operators.
   The TempConstr object allows you to create several different types of constraints:

  • Linear Constraint
  • Ranged Linear Constraint
  • Quadratic Constraint
  • Absolute Value Constraint
  • Logical Constraint
  • Min or Max Constraint
  • Indicator Constraint
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Create a TempConstr object
# (Note: Expr1 and Expr2 is a LinExpr, Var, constant)
x + y <= 1 # Linear Constr --> Expr1 sense(<=,==,>=) Expr2

x + y == [0, 10] # Ranged Linear Constr --> LinExpr == [constan1, constant2]

x*x + y*y <= 3 # Quad Constr --> Expr1 sense Expr2 (Note: must have a QuadExpr)

x == abs_(y) # Absolute Value Constr --> var1 == abs_(var2)

# (Note: A is a binary Var; B is a binary Var, a list of binary Var, or a tupledict of binary Var)
x_bin == or_(y_bin) # Logical Constr --> A == or_(B)

# (Note: x is a Var; y is a Var, a list of Var and constant, or a tupledict of Var)
x == max_(y, z) # MIN/MAX Constr --> x == min_(y)

# (Note: x is a binary Var; b is 0 or 1; Expr1 and Expr2 is a LinExpr, Var, constant)
(x_bin == 1) >> (y + z <= 5) # Indicator Constr --> (x == b) >> (Expr1 sense Expr2)

# Add a general constraint to a mmodel
model.addConstr(x + y <= z)
model.update()

SOS(Gurobi SOS constraint object)

   SOS constraints are always associated with a particular model. You create an SOS object by adding an SOS constraint to a model (using Model.addSOS), rather than by using an SOS constructor. Similarly, SOS constraints are removed using the Model.remove method.
   An SOS constraint can be of type 1 or 2 (GRB.SOS_TYPE1 or GRB.SOS_TYPE2). A type 1 SOS constraint is a set of variables where at most one variable in the set may take a value other than zero. A type 2 SOS constraint is an ordered set of variables where at most two variables in the set may take non-zero values. If two take non-zero values, they must be contiguous in the ordered set.

1
2
# Add an SOS constraint to the model.  
Model.addSOS(type, vars, wts=None)

Gurobi expression

LinExpr(Gurobi linear expression object)

  Build linear expression, there are two ways, as follow,

1
2
3
4
5
6
7
8
# Method 1
LinExpr(arg1=0.0, arg2=None)
# Method 2
quicksum(list)
# Mothod 3
tupledict.sum()
# Method 4 -- overloaded operators
Lin_expr = x1 + 2*x2 + 5*x3 + 2.5

   The cost of building expressions depends heavily on the approach you use, you should be aware of a few efficiency issues when building large expressions:

  • Avoid using Python sum function.
  • Avoid using expr+= x in a loop.
  • Use the quicksum function when build a large expression.
  • The two most efficient ways to build large linear expressions are addTerms or the LinExpr() constructor.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Build linear expressions
# Method 1
expr1 = LinExpr(3) # constant
expr2 = LinExpr(2*x+y+z) # variables
expr3 = LinExpr(expr1+expr2) # expr
expr4 = LinExpr([4, 12], [x, y])
expr5 = LinExpr([(0.5, x), (1.2, y), (2.5, z)])
# method 4
expr6 = x + y + z
# 打印表达式
print('==> expr1:', expr1)
print('==> expr2:', expr2)
print('==> expr3:', expr3)
print('==> expr4:', expr4)
print('==> expr5:', expr5)
print('==> expr6:', expr6)
==> expr1: <gurobi.LinExpr: 3.0>
==> expr2: <gurobi.LinExpr: 2.0 x + y + z>
==> expr3: <gurobi.LinExpr: 3.0 + 2.0 x + y + z>
==> expr4: <gurobi.LinExpr: 4.0 x + 12.0 y>
==> expr5: <gurobi.LinExpr: 0.5 x + 1.2 y + 2.5 z>
==> expr6: <gurobi.LinExpr: x + y + z>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# LinExpr 
# LinExpr.add(expr, mult=1.0)
expr1.add(x, 3.0)
print('==> expr1:', expr1)
# LinExpr.addConstant(c)
expr1.addConstant(10)
print('==> expr1:', expr1)
# LinExpr.addTerms (coeffs, vars)
expr1.addTerms([6,9], [y,z])
print('==> expr1:', expr1)
# LinExpr.copy()
expr7 = expr1.copy()
print('==> expr7:', expr7)
# LinExpr.size()
item_num = expr7.size()
print('==> expr7 items:', item_num)
==> expr1: <gurobi.LinExpr: 3.0 + 3.0 x>
==> expr1: <gurobi.LinExpr: 13.0 + 3.0 x>
==> expr1: <gurobi.LinExpr: 13.0 + 3.0 x + 6.0 y + 9.0 z>
==> expr7: <gurobi.LinExpr: 13.0 + 3.0 x + 6.0 y + 9.0 z>
==> expr7 items: 3
1
2
3
4
5
6
7
8
9
10
# LinExpr.getConstant()
print('==> ', expr7.getConstant())
# LinExpr.getCoeff(i)
print('==> ', expr7.getCoeff(0), expr7.getCoeff(1), expr7.getCoeff(2))
# LinExpr.getVar(i)
print('==> ', expr7.getVar(0), expr7.getVar(1), expr7.getVar(2))
# LinExpr.remove(item)
expr8 = expr7.copy()
expr8.remove(y)
print('==> expr8:' ,expr8)
==>  13.0
==>  3.0 6.0 9.0
==>  <gurobi.Var x> <gurobi.Var y> <gurobi.Var z>
==> expr8: <gurobi.LinExpr: 13.0 + 3.0 x + 9.0 z>

QuadExpr(Gurobi quadratic expression object)

   A quadratic expression consists of a linear expression plus a list of coefficient-variable-variable triples that capture the quadratic terms. Quadratic expressions are used to build quadratic objective functions and quadratic constraints. Expressions can be built from constants, variables, or from other expressions.
   The cost of building expressions depends heavily on the approach you use, you should be aware of a few efficiency issues when building large expressions:

  • Avoid using the Python sum function..
  • Avoid using expr = expr + x*x in a loop.
  • Use the quicksum function when build a large expression.
  • The most efficient way to build a large quadratic expression is with a single call to addTerms.

   Build a quadratic expression, as follow,

QuadExpr(expr=None)

variables + overloadedoperators

1
2
3
4
5
6
7
8
9
10
# Build quadratic expressions
# Method 1
qexpr1 = QuadExpr(1)
qexpr2 = QuadExpr(expr=x*y+y+z)
# Method 2
qexpr3 = x*x + y*y + z*z + 3
#
print('==> qexpr1:', qexpr1, 'item_num:', qexpr1.size())
print('==> qexpr2:', qexpr2, 'item_num:', qexpr2.size())
print('==> qexpr3:', qexpr3, 'item_num:', qexpr3.size())
==> qexpr1: <gurobi.QuadExpr: 1.0> item_num: 0
==> qexpr2: <gurobi.QuadExpr: y + z + [ x * y ]> item_num: 1
==> qexpr3: <gurobi.QuadExpr: 3.0 + [ x ^ 2 + y ^ 2 + z ^ 2 ]> item_num: 3
1
2
3
# Quad.addTerms(coeffs, vars, vars2=None)
qexpr1.addTerms([2.0, 3.0, 1.5], [x, y, z], [y, z, x])
print('==> ', qexpr1)
==>  <gurobi.QuadExpr: 1.0 + [ 2.0 x * y + 3.0 y * z + 1.5 z * x ]>

GenExpr(Gurobi general expression object)

   Objects of this class are created by a set of general constraint helper functions functions(i.e. max_(),min_(),and_(),or_()).
   GenExpr object is used in conjunction with overloaded operators to build TempConstr objects, which are then passed to addConstr or addConstrs to build general constraints.

1
2
3
4
5
6
7
8
9
10
11
# Create a GenExpr 
gexpr1 = max_(x, y)
gexpr2 = min_(x, y)
gexpr3 = and_(x_bin, y_bin)
gexpr4 = or_(x_bin, y_bin)
gexpr5 = abs_(x)
print('==> ', type(gexpr1))
print('==> ', type(gexpr2))
print('==> ', type(gexpr3))
print('==> ', type(gexpr4))
print('==> ', type(gexpr5))
==>  <class 'gurobipy.GenExpr'>
==>  <class 'gurobipy.GenExpr'>
==>  <class 'gurobipy.GenExpr'>
==>  <class 'gurobipy.GenExpr'>
==>  <class 'gurobipy.GenExpr'>

Callbacks(Gurobi callback class)

   A callback is a user function that is called periodically by the Gurobi optimizer in order to allow the user to query or modify the state of the optimization.
   More precisely, if you pass a function that takes two arguments (model and where) as the argument to Model.optimize, your function will be called during the optimization. Your callback function can then call Model.cbGet to query the optimizer or details on the state of the optimization.
    The Gurobi callback class provides a set of constants that are used within the user callback function. The first set of constants in this class list the options for the where argument to the user callback function. The where argument indicates from where in the optimization process the user callback is being called. The other set of constants in this class list the options for the what argument to Model.cbGet. The what argument is used by the user callback to indicate what piece of status information it would like to retrieve.
   When solving a model using multiple threads, note that the user callback is only ever called from a single thread, so you don’t need to worry about the thread safety of your callback.

tuplelist & tupledict

tuplelist(Gurobi tuple list)

   This is a sub-class of the Python list class that is designed to efficiently support a usage pattern that is quite common when building optimization models. The tuplelist is a Python list and the elements of the list is Python tuple. All tuples in a tuplelist must be the same length.
   In particular, if a tuplelist is populated with a list of tuples, the select function on this class efficiently selects tuples whose values match specified values in specified tuple fields.
   You generally build tuplelist objects in the same way you would build standard Python lists. For example, you can use the += operator to append a new list of items to an existing tuplelist, or the + operator to concatenate a pair of tuplelist objects. You can also call the append, extend, insert, pop, and removefunctions.
   To access the members of a tuplelist, you also use standard list functions.
   Note that tuplelist objects build and maintain a set of internal data structures to support efficient select operations. If you wish to reclaim the storage associated with these data structures, you can call the clean function.
   A tuplelist is designed to store tuples containing scalar values (int, float, string, …) to a list, namely [(int, float, string), (int, float, string), (int, float, string)] rather than tuples containing tuples, namely [(int, float, string), (tuple )]

1
2
3
4
5
6
# list: Initial list of member tuples. 
tuplelist(list)
# Returns a tuplelist containing all member tuples that match the specified pattern.
tuplelist.select(pattern)
# Discards internal data structure associated with a tuplelist object.
tuplelist.clean()

1
2
3
4
5
6
7
8
9
10
11
12
13
# Create tuplelist
# tuplelist(list)
tulist1 = tuplelist([(1,2,'z'), ('z','h','f'), (1,6,'z'), ('A', 2, 5)])
# tuplelist.select(pattern)
tulist2 = tulist1.select(1, '*', 'z')

# Print tulist1
print('==>', 'tulist1 len:', len(tulist1))
print('==>', 'tulist1:' ,tulist1)
print('==>', tulist1[0], tulist1[0][1])
print('='*25)
# Print tulist2
print('==> tulist2:' ,tulist2)
==> tulist1 len: 4
==> tulist1: <gurobi.tuplelist (4 tuples, 3 values each):
 ( 1 , 2 , z )
 ( z , h , f )
 ( 1 , 6 , z )
 ( A , 2 , 5 )
>
==> (1, 2, 'z') 2
=========================
==> tulist2: <gurobi.tuplelist (2 tuples, 3 values each):
 ( 1 , 2 , z )
 ( 1 , 6 , z )
>

tupledict(Gurobi tuple dict)

   This is a sub-class of the Python dict class that is designed to efficiently support a usage pattern that is quite common when building optimization models. In particular, a tupledict is a Python dict where the keys are stored as a Gurobi tuplelist, and where the values are typically Gurobi Var objects.
   Objects of this class make it easier to build linear expressions on sets of Gurobi variables, using tuplelist.select() syntax and semantics.
   You typically build a tupledict by calling Model.addVars. Once you’ve created a tupledict object d, you can use d.sum() to create a linear expression that captures the sum of the variables in the tupledict. You can also use a command like d.sum(1, ’*’, 5) to create a sum over a subset of the variables in tupledict. You can also use d.prod(coeff) to create a linear expression where the coefficients are pulled from the argument dict.
   To access the members of a tupledict, you can use standard dict indexing.
   Note that a tupledict key must be a tuple of scalar values (int, float, string, ...). Thus, you can use (1, 2.0, ’abc’) as a key, but you can’t use ((1, 2.0), ’abc’).
   Note that tupledict objects build and maintain a set of internal data structures to support efficient select operations. If you wish to reclaim the storage associated with these data structures,
you can call the clean function.

1
2
3
4
5
6
7
8
9
10
11
12
#
tupledict(args, kwargs)
#
tupledict.sum()
tupledict.sum(pattern)
#
tupledict.sum(pattern)
# Returns a linear expression that contains one term for each tuple
# that is present in both the tupledict and in the argument dict.
tupledict.prod (coeff, pattern)
# Discards internal data structure associated with a tupledict object.
tupledict.clean()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Create tupledict
# Method1 -- tupledict(args, kwargs)
tudict1 = tupledict([((1,2),'zhf'), ((1,'z'),[2010,1218]), (('w','z'),('zhf','wjq'))])
# Method2 -- Model.addVars()
tudict2 = model.addVars([(1,2), (1,3), (2,3)], name="tudict2")
tudict3 = model.addVars([(1,2), (1,3), (2,3)])
model.update()
# Print tudic1
print('==> tudict1:', tudict1)
print('==> keys:', tudict1.keys())
print('==> values:', tudict1.values())
print('='*25)
# Print tudict2
print('==> tudict2:', tudict2)
print('==> tudict3:', tudict3)
print('==> keys:', tudict2.keys())
print('==> values:', tudict2.values())
==> tudict1: {(1, 2): 'zhf', (1, 'z'): [2010, 1218], ('w', 'z'): ('zhf', 'wjq')}
==> keys: <gurobi.tuplelist (3 tuples, 2 values each):
 ( 1 , 2 )
 ( 1 , z )
 ( w , z )
>
==> values: ['zhf', [2010, 1218], ('zhf', 'wjq')]
=========================
==> tudict2: {(1, 2): <gurobi.Var tudict2[1,2]>, (1, 3): <gurobi.Var tudict2[1,3]>, (2, 3): <gurobi.Var tudict2[2,3]>}
==> tudict3: {(1, 2): <gurobi.Var C11>, (1, 3): <gurobi.Var C12>, (2, 3): <gurobi.Var C13>}
==> keys: <gurobi.tuplelist (3 tuples, 2 values each):
 ( 1 , 2 )
 ( 1 , 3 )
 ( 2 , 3 )
>
==> values: [<gurobi.Var tudict2[1,2]>, <gurobi.Var tudict2[1,3]>, <gurobi.Var tudict2[2,3]>]
1
2
3
4
5
6
7
8
9
10
11
12
13
# Create LinExpr  
# tupledict.sum(pattern)
tudict_expr1 = tudict2.sum()
tudict_expr2 = tudict2.sum(1, '*')
tudict_expr3 = tudict2.sum('*', 3)
tudict_expr4 = tudict2.sum(2, 3)
tudict_expr5 = tudict3.sum()
# Print LinExpr
print('==> tudict_expr1:', tudict_expr1)
print('==> tudict_expr5:', tudict_expr5)
print('==> tudict_expr2:', tudict_expr2)
print('==> tudict_expr3:', tudict_expr3)
print('==> tudict_expr4:', tudict_expr4)
==> tudict_expr1: <gurobi.LinExpr: tudict2[1,2] + tudict2[1,3] + tudict2[2,3]>
==> tudict_expr5: <gurobi.LinExpr: C11 + C12 + C13>
==> tudict_expr2: <gurobi.LinExpr: tudict2[1,2] + tudict2[1,3]>
==> tudict_expr3: <gurobi.LinExpr: tudict2[1,3] + tudict2[2,3]>
==> tudict_expr4: <gurobi.LinExpr: tudict2[2,3]>
1
2
3
4
# tupledict.select(pattern)
print('==> ', tudict1.select())
print('==> ', tudict1.select(1, '*'))
print('==> ', tudict1.select('*', 'z'))
==>  ['zhf', [2010, 1218], ('zhf', 'wjq')]
==>  ['zhf', [2010, 1218]]
==>  [[2010, 1218], ('zhf', 'wjq')]
有何不可 wechat
Subscribe to my blog by scanning my wechat account~
书山有路勤为径,学海无涯苦作舟~