Restricting one variable in my variable vector to integer in Pyomo - optimization

I have a minimization problem that currently has only continuous variables
min Cx
s.t. Ax <= b
lb <= x <= ub
Where C is my cost vector, A is my coefficient matrix and b is my fixed vector. X is my variable vector of continuous variables.
A = 24x29, x = 29x1, b = 24x1
I want to force one of the x variables to be an integer, how can that be done in Pyomo?
I am new to the package, any help is appreciated

Pyomo offers a way to set the domain of your variables. Let me prove it to you in this example that you can entirely run in a Python console with copy/paste.
Suppose you want to change x[1] to integers, you can use (1 being part of set S = {1,2,3}):
from pyomo.environ import ConcreteModel, Set, Var, Integers
# Create your model here (set params, vars, constraint...)
model = ConcreteModel()
model.S = Set(initialize={1,2,3})
model.x = Var(model.S)
Let's pause the example here and type model.x.display() in the Python console. You should see that the domain of all elements in model.x is set by default to Real. Let's continue.
# Here, we suppose your model is created.
# You can change the domain of one lement of your var using this line:
model.x[1].domain = Integers
Now, type model.x.display() in your Python console and you should see this:
x : Size=3, Index=S
Key : Lower : Value : Upper : Fixed : Stale : Domain
1 : None : None : None : False : True : Integers
2 : None : None : None : False : True : Reals
3 : None : None : None : False : True : Reals
So, only x[1] is integer.

In practice, we never mix continuous variables with integer variables. So for practical models, it is not a real restriction that we declare variables to be continuous or integer by their name. This rule is not exclusive to Pyomo. Modeling tools like AMPL and GAMS use the same paradigm.
Having said that, let's focus on your problem.
One way to tackle this, would be:
Create a single, scalar integer variable y.
Add the constraint y = x[342] (or whatever the x variable you want to be integer valued)
This is not as crazy as it sounds. A good MIP solver will presolve this away, so the performance impact should be minimal.

Related

pyomo matrix product

I would like to use pyomo to solve a multiple linear regression under constraint in pyomo.
to do so I have 3 matrices :
X (noted tour1 in the following code) My inputs (600x13)(bureaux*t1 in pyomo)
Y (noted tour2 in the following code) the matrix I want to predict (6003)(bureauxt2 inpyomo)
T (noted transfer In the code) (13x3)(t1*t2 in pyomo)
I would like to do the following
ypred = XT
minimize (ypred-y)**2
subject to
0<T<1
and Sum_i(Tij)=1
To that effect, I started the following code
from pyomo.environ import *
tour1=pd.DataFrame(np.random.random(size=(60,13)),columns=["X"+str(i) for i in range(13)],index=["B"+str(i) for i in range(60)])
tour2=pd.DataFrame(np.random.random(size=(60,3)),columns=["Y"+str(i) for i in range(3)],index=["B"+str(i) for i in range(60)])
def gettour1(model,i,j):
return tour1.loc[i,j]
def gettour2(model,i,j):
return tour2.loc[i,j]
def cost(model):
return sum((sum(model.tour1[i,k] * model.transfer[k,j] for k in model.t1) - model.tour2[i,j] )**2 for i in model.bureaux for j in model.tour2)
model = ConcreteModel()
model.bureaux = Set(initialize=tour1.index.tolist())
model.t1 = Set(initialize=tour1.columns)
model.t2 = Set(initialize=tour2.columns)
model.tour1 = Param(model.bureaux, model.t1,initialize=gettour1)
model.tour2 = Param(model.bureaux, model.t2,initialize=gettour2)
model.transfer = Var(model.t1,model.t2,bounds=[0,1])
model.obj=Objective(rule=cost, sense=minimize)
I unfortunately get an error at this stage :
KeyError: "Index '('X0', 'B0', 'Y0')' is not valid for indexed component 'transfer'"
anyone knows how I can calculate the objective ?
furthermore any help for the constrains would be appreciated :-)
A couple things...
First, the error you are getting. There is information in that error statement that should help identify the problem. The construction appears to be trying to index transfer with a 3-part index (x, b, y). That clearly is outside of t1 x t2. If you look at the sum equation you have, you are mistakenly using model.tour2 instead of model.t2.
Also, your bounds parameter needs to be a tuple.
While building the model, you should be pprint()-ing the model very frequently to look for these types of issues. That only works well if you have "small" data. 60 x 13 may be the normal problem size, but it is a total pain to troubleshoot. So, start with something tiny, maybe 3 x 4. Make a Set, pprint(). Make a Constraint, pprint()... Once the model computes/solves with "tiny" data, just pop in the real stuff.

I use gurobipy to solve a simple problem,but after adding constraints containing exponential terms and solving them, I got a wrong result

I had trouble solving a simple problem with gurobi:
e^x+x=lnP
x=1
In Gurobipy,it transforms into this form:
x+y=temp
y=e^x
lnP=temp
x=1
The result is here:
Variable X
x 1
P 749.103
y 2.71828
Temp 3.71828
The code is as follows:
from gurobipy import *
model = Model('Antoine')
P = model.addVar(vtype=GRB.CONTINUOUS, name='P',lb=0)
x = model.addVar(vtype=GRB.CONTINUOUS, name='x',lb=0)
y = model.addVar(vtype=GRB.CONTINUOUS, name='y',lb=-GRB.INFINITY)
temp = model.addVar(vtype=GRB.CONTINUOUS, name='Temp1',lb=-GRB.INFINITY)
model.addConstr(x == 1)
model.addGenConstrExp(x,y)
model.addConstr(x+y == temp)
model.addGenConstrLog(P,temp)
model.setObjective(P, GRB.MINIMIZE)
model.write("test.lp")
model.optimize()
I don't know why the result of P is wrong
Gurobi represents nonlinear functions by piecewise linear approximations. When I solve the original model on my computer using Gurobi Optimizer 9.5.2, I get the following warning:
Warning: max constraint violation (2.9006e+00) exceeds tolerance
Warning: max general constraint violation (2.9006e+00) exceeds tolerance
Piecewise linearization of function constraints often causes big violation.
Try to adjust the settings of the related parameters, such as FuncPieces.
This means the default automatic linearization is not sufficiently accurate for this model. As suggested in the warning message, adjust the FuncPieces parameter to get a more accurate representation for this model. For example, with model.Params.FuncPieces=-1 on my computer, I get this more accurate result:
Variable X
-------------------------
P 41.29
x 1
y 2.71828
Temp1 3.71828

In AMPL, how to refer to part of the result, and use them in multiple places

I'm learning AMPL to speed up a model currently in excel spreadsheet with excel solver. It basically based on the matrix multiplication result of a 1 x m variables and an m x n parameters. And it would find the variables to maximize the minimum of certain values in the result while keeping some other values in the same result satisfying a few constraints. How to do so in AMPL?
Given: P= m x n parameters
Variable: X= 1 x m variable we tried to solve
Calculate: R= X x P , result of matrix multiplication of X and P
Maximize: min(R[1..3]), the minimum value of the first 3 values in the result
Subject to: R[2]<R[4]
min(R[6..8])>20
R[5]-20>R[7]
X are all integers
I read several tutorials and look up the manual but can't find the solution to this seemingly straightforward problem. All I found is maximize a single value, which is the calculation result. And it was used only once and does not appear again in the constraint.
The usual approach for "maximize the minimum" problems in products like AMPL is to define an auxiliary variable and set linear constraints that effectively define it as the minimum, converting a nonlinear function (min) into linear rules.
For instance, suppose I have a bunch of decision variables x[i] with i ranging over an index set S, and I want to maximize the minimum over x[i]. AMPL syntax for that would be:
var x_min;
s.t. DefineMinimum{i in S}: x_min <= x[i];
maximize ObjectiveFunction: x_min;
The constraint only requires that x_min be less than or equal to the minimum of x[i]. However, since you're trying to maximize x_min and there are no other constraints on it, it should always end up exactly equal to that minimum (give or take machine-arithmetic epsilon considerations).
If you have parameters (i.e. values are known before you run the optimisation) and want to refer to their minimum, AMPL lets you do that more directly:
param p_min := min{j in IndexSet_P} p[j];
While AMPL also supports this syntax for variables, not all of the solvers used with AMPL are capable of accepting this type of constraint. For instance:
reset;
option solver gecode;
set S := {1,2,3};
var x{S} integer;
var x_min = min{s in S} x[s];
minimize OF: sum{s in S} x[s];
s.t. c1: x_min >= 5;
solve;
This will run and do what you'd expect it to do, because Gecode is programmed to recognise and deal with min-type constraints. However, if you switch the solver option to gurobi or cplex it will fail, since these only accept linear or quadratic constraints. To apply a minimum constraint with those solvers, you need to use something like the linearization trick I discussed above.

Finding out reason of Pyomo model infeasibility

I got a pyomo concrete model with lots of variables and constraints.
Somehow, one of the variable inside my model violates one constraint, which makes my model infeasible:
WARNING: Loading a SolverResults object with a warning status into model=xxxx;
message from solver=Model was proven to be infeasible.
Is there a way to ask the solver, the reason of the infeasibility?
So for example, lets assume I got a variable called x, and if I define following 2 constraints, model will be ofc. infeasible.
const1:
x >= 10
const2:
x <= 5
And what I want to achieve that pointing out the constraints and variable which causes this infeasibility, so that I can fix it. Otherwise with my big model it is kinda hard to get what causing this infeasibility.
IN: write_some_comment
OUT: variable "x" cannot fulfill "const1" and "const2" at the same time.
Many solvers (including IPOPT) will hand you back the value of the variables at solver termination, even if the problem was found infeasible. At that point, you do have some options.
There is contributed code in pyomo.util.infeasible that might help you out. https://github.com/Pyomo/pyomo/blob/master/pyomo/util/infeasible.py
Usage:
from pyomo.util.infeasible import log_infeasible_constraints
...
SolverFactory('your_solver').solve(model)
...
log_infeasible_constraints(model)
I would not trust any numbers that the solver loads into the model after reporting "infeasible." I don't think any solvers come w/ guarantees on the validity of those numbers. Further, unless a package can divine the modeler's intent, it isn't clear how it would list the infeasible constraints. Consider 2 constraints:
C1: x <= 5
C2: x >= 10
X ∈ Reals, or Integers, ...
Which is the invalid constraint? Well, it depends! Point being, it seems an impossible task to unwind the mystery based on values the solver tries.
A possible alternate strategy: Load the model with what you believe to be a valid solution, and test the slack on the constraints. This "loaded solution" could even be a null case where everything is zero'ed out (if that makes sense in the context of the model). It could also be a set of known feasible solutions tried via unit test code.
If you can construct what you believe to be a valid solution (forget about optimal, just something valid), you can (1) load those values, (2) iterate through the constraints in the model, (3) evaluate the constraint and look for negative slack, and (4) report the culprits with values and expressions
An example:
import pyomo.environ as pe
test_null_case = True
m = pe.ConcreteModel('sour constraints')
# SETS
m.T = pe.Set(initialize=['foo', 'bar'])
# VARS
m.X = pe.Var(m.T)
m.Y = pe.Var()
# OBJ
m.obj = pe.Objective(expr = sum(m.X[t] for t in m.T) + m.Y)
# Constraints
m.C1 = pe.Constraint(expr=sum(m.X[t] for t in m.T) <= 5)
m.C2 = pe.Constraint(expr=sum(m.X[t] for t in m.T) >= 10)
m.C3 = pe.Constraint(expr=m.Y >= 7)
m.C4 = pe.Constraint(expr=m.Y <= sum(m.X[t] for t in m.T))
if test_null_case:
# set values of all variables to a "known good" solution...
m.X.set_values({'foo':1, 'bar':3}) # index:value
m.Y.set_value(2) # scalar
for c in m.component_objects(ctype=pe.Constraint):
if c.slack() < 0: # constraint is not met
print(f'Constraint {c.name} is not satisfied')
c.display() # show the evaluation of c
c.pprint() # show the construction of c
print()
else:
pass
# instantiate solver & solve, etc...
Reports:
Constraint C2 is not satisfied
C2 : Size=1
Key : Lower : Body : Upper
None : 10.0 : 4 : None
C2 : Size=1, Index=None, Active=True
Key : Lower : Body : Upper : Active
None : 10.0 : X[foo] + X[bar] : +Inf : True
Constraint C3 is not satisfied
C3 : Size=1
Key : Lower : Body : Upper
None : 7.0 : 2 : None
C3 : Size=1, Index=None, Active=True
Key : Lower : Body : Upper : Active
None : 7.0 : Y : +Inf : True

struggling minimizing non linear function

I am looking forward to minimize a non linear function with 3 arguments (x1,x2 and x3)
My sources of information are:
the explanation of the minimization function:
https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html
And an example they provide:
https://docs.scipy.org/doc/scipy/reference/tutorial/optimize.html
I do not belong to a mathematical area, so first off forgive me if I am using incorrect wording / expressions.
This is my code :
import numpy as np
from scipy.optimize import minimize
def rosen(x1,x2,x3):
return np.sqrt(((x1**2)*0.002)+((x2**2)*0.0035)+((x3**2)*0.0015)+(2*x1*x2*0.015)+(2*x1*x3*0.01)+(2*x2*x3*0.02))
I think that the first step is okey up to here..
Then it is required to state the:
x0 : ndarray
Initial guess. len(x0) is the dimensionality of the minimization problem.
Given that I am stating 3 args in the minimization function I shall state a 3 dim array , such like this?
x0=np.array([1,1,1])
res = minimize(rosen, x0)
print(res.x)
The undesired output is:
rosen() missing 2 required positional arguments: 'x2' and 'x3'
Which I do not really understand where shall I state the positional arguments.
Apart from that I would like to set some bounds for the outputing values for x1,x2,x3 .
Which I tried
res = minimize(rosen, x0, bounds=([0,None]),options={"disp": False})
Which outputs also that :
ValueError: length of x0 != length of bounds
How should I then express the bounds inside the res then?
The desired output would be simply to output an array for x1,x2,x3 according to the minimization of the function where each value is minimun 0, as stated in the bounds and that the sum of the args add up to 1.
Function-definition
Read the docs carefully, e.g. for your function-def:
fun : callable
The objective function to be minimized. Must be in the form f(x, *args). The
optimizing argument, x, is a 1-D array of points, and args is a tuple of any
additional fixed parameters needed to completely specify the function.
Your function should take a 1d-array, while you implement the multi-argument for multi-variables approach!
Changing:
def rosen(x1,x2,x3):
return np.sqrt(((x1**2)*0.002)+((x2**2)*0.0035)+((x3**2)*0.0015)+(2*x1*x2*0.015)+(2*x1*x3*0.01)+(2*x2*x3*0.02))
def rosen(x):
x1,x2,x3 = x # unpack vector for your kind of calculations
return np.sqrt(((x1**2)*0.002)+((x2**2)*0.0035)+((x3**2)*0.0015)+(2*x1*x2*0.015)+(2*x1*x3*0.01)+(2*x2*x3*0.02))
should work. This is a bit a repair-something-to-keep-my-other-code approach but won't hurt much in this example. Usually you implement your function-definition on the 1d-array-input assumption!
Bounds
Again from the docs:
bounds : sequence, optional
Bounds for variables (only for L-BFGS-B, TNC and SLSQP). (min, max) pairs for each
element in x, defining the bounds on that parameter. Use None for one of min or max
when there is no bound in that direction.
So you need n_vars pairs! Easily achieved by using a list-comprehension, deducing the necessary info from x0.
res = minimize(rosen, x0, bounds=[[0,None] for i in range(len(x0))],options={"disp": False})
Make variables sum up to 1 / Constraints
Your comment implies you want the variables to sum up to 1. You would need to use an equality-constraint then (only 1 solver supporting this and inequality-constraints; one other only inequality-constraints; the rest no constraints; solver will be picked automatically if none explicitly given).
It looks somewhat like:
cons = ({'type': 'eq', 'fun': lambda x: sum(x) - 1}) # read docs to understand!
# to think about:
# sum vs. np.sum
# (not much diff here)
res = minimize(rosen, x0, bounds=[[0,None] for i in range(len(x0))],options={"disp": False}, constraints=cons)
For the case of x nonnegative, the constraint is usually called the probability-simplex.
(untested code; conceptually correct!)