In this bit of code:
import cvxpy as cvx
# Examples: linear programming
# Create two scalar optimization variables.
x = cvx.Variable()
y = cvx.Variable()
# Create 4 constraints.
constraints = [x >= 0,
y >= 0,
x + y >= 1,
2*x + y >= 1]
# Form objective.
obj = cvx.Minimize(x+y)
# Form and solve problem.
prob = cvx.Problem(obj, constraints)
prob.solve(warm_start= True) # Returns the optimal value.
print ("status:", prob.status)
print ("optimal value", prob.value)
print ("optimal var", x.value, y.value)
I'm looking for a way to choose the warm start value myself (for example: x = 1/2 and y = 1/2), not the previous solver result.
Is there any way to give the solver this input? And if not, is there a non-commercial alternative to cvxpy?
To the 2021 readers: today is impossible (in cvxpy) to give a hand to the solver with an initial guess. Warm start right now only works when you solve the same problem with different parameter values, initializing with the previous solution (see https://github.com/cvxpy/cvxpy/issues/1355).
You can manually assign the values using x.value = 1/2, and then passing the warm_start=True parameter in the available solvers. Keep in mind not all solvers allow this, one that does is for example SCS.
More info available on: https://www.cvxpy.org/tutorial/advanced/index.html
Related
I have an optimization problem that has a condition such that if the variable value is greater than 0, a certain constraint applies.
I tried to use m.if3 to generate a binary value that assumes the value of 1 if the variable value is greater than 0, but that did not work because the m.if3 condition applies for greater than or equal to 0, and I need it to be strictly greater than 0. Here is an example of the code I had with if3 but it didn't work.
x = m.Var(lb=0)
a = m.if3(x,0,1)
m.Equation(b - c == a*f ) #b,c,f are calculated values
I also attempted to use a typical if statement condition, but that did not work with the gekko variable (example below).
if x>0:
m.Equation(b - c == f )
Is there another way for me to be able to apply this condition (strictly greater than zero)?
You can flip the variable's sign and flip the conditions to get your desired behaviour:
In a = m.if3(x,0,1) a = 0 if x < 0, and a = 1 if x >= 0.
Instead you can do a = m.if3(-x,1,0). Here a = 1 if -x < 0 (equivalent to x > 0).
Using an if statement as in the second part of your question will not work because x does not have a value. It will only have a value after the optimization. And that will be accessed with something like x.value, not directly like this, afaik.
The if3(x,0,1) is correct for the desired application but the condition that is turned off or on should have a*() on both sides of the equation. Alternatively, move all of the expression to one side so that it is:
m.Equation(a*(b-c-f)==0)
so that 0==0 when a=0. Here is a complete script that demonstrates the solution.
from gekko import GEKKO
import numpy as np
m = GEKKO()
b = 0; c=1
f = m.Var()
x = m.Param(-1)
# binary variable (a=0 if x<0, a=1 otherwise)
a = m.if3(x,0,1)
# turn off condition when a==0
m.Equation(a*(b-c-f)==0)
# initialize with IPOPT (NLP solver)
m.options.SOLVER=3
m.solve(disp=False)
# Find integer solution with APOPT (MINLP solver)
m.options.SOLVER=1
m.solve(disp=True)
print(a.value[0])
This gives a solution with a=0 and the equation deactivated. Change x = m.Param(-1) to x = m.Param(1) to observe a solution with a=1.
I'm trying to do one task, but I just can't figure it out.
This is my function:
1/(x**1/n) + 1/(y**1/n) + 1/(z**1/n) - 1
I want that sum to be as close to 1 as possible.
And these are my input variables (x,y,z):
test = np.array([1.42, 5.29, 7.75])
So n is the only decision variable.
To summarize:
I have a situation like this right now:
1/(1.42**1/1) + 1/(5.29**1/1) + 1/(7.75**1/1) = 1.02229
And I want to get the following:
1/(1.42^(1/0.972782944446024)) + 1/(5.29^(1/0.972782944446024)) + 1/(7.75^(1/0.972782944446024)) = 0.999625
So far I have roughly nothing, and any help is welcome.
import numpy as np
from scipy.optimize import minimize
def objectiv(xyz):
x = xyz[0]
y = xyz[1]
z = xyz[2]
n = 1
return 1/(x**(1/n)) + 1/(y**(1/n)) + 1/(z**(1/n))
test = np.array([1.42, 5.29, 7.75])
print(objectiv(test))
OUTPUT: 1.0222935270013889
How to properly define a constraint?
def conconstraint(xyz):
x = xyz[0]
y = xyz[1]
z = xyz[2]
n = 1
return 1/(x**(1/n)) + 1/(y**(1/n)) + 1/(z**(1/n)) - 1
And it is not at all clear to me how and what to do with n?
EDIT
I managed to do the following:
def objective(n,*args):
x = odds[0]
y = odds[1]
z = odds[2]
return abs((1/(x**(1/n)) + 1/(y**(1/n)) + 1/(z**(1/n))) - 1)
odds = [1.42,5.29,7.75]
solve = minimize(objective,1.0,args=(odds))
And my output:
fun: -0.9999999931706812
x: array([0.01864994])
And really when put in the formula:
(1/(1.42^(1/0.01864994)) + 1/(5.29^(1/0.01864994)) + 1/(7.75^(1/0.01864994))) -1 = -0.999999993171
Unfortunately I need a positive 1 and I have no idea what to change.
We want to find n that gets our result for a fixed x, y, and z as close as possible to 1. minimize tries to get the lowest possible value for something, without negative bound; -3 is better than -2, and so on.
So what we actually want is called least-squares optimization. Similar idea, though. This documentation is a bit hard to understand, so I'll try to clarify:
All these optimization functions have a common design where you pass in a callable that takes at least one parameter, the one you want to optimize for (in your case, n). Then you can have it take more parameters, whose values will be fixed according to what you pass in.
In your case, you want to be able to solve the optimization problem for different values of x, y and z. So you make your callback accept n, x, y, and z, and pass the x, y, and z values to use when you call scipy.optimize.least_squares. You pass these using the args keyword argument (notice that it is not *args). We can also supply an initial guess of 1 for the n value, which the algorithm will refine.
The rest is customization that is not relevant for our purposes.
So, first let us make the callback:
def objective(n, x, y, z):
return 1/(x**(1/n)) + 1/(y**(1/n)) + 1/(z**(1/n))
Now our call looks like:
best_n = least_squares(objective, 1.0, args=np.array([1.42, 5.29, 7.75]))
(You can call minimize the same way, and it will instead look for an n value to make the objective function return as low a value as possible. If I am thinking clearly: the guess for n should trend towards zero, making the denominators increase without bound, making the sum of the reciprocals go towards zero; negative values are not possible. However, it will stop when it gets close to zero, according to the default values for ftol, xtol and gtol. To understand this part properly is beyond the scope of this answer; please try on math.stackexchange.com.)
I can be considered pretty much new to python and coding in general so forgive me for my ignorance.
I'm trying to solve a system of trigonometric functions in python, and I'm doing so using the solve command from sympy. However, this method returns only a finite number of solutions, two in this particular case.
I've read through the documentation and it seems that to get an expression for all the solutions solveset is to be used instead. However, I do not want all the solutions to be displayed, but rather only a finite amount which is contained within a certain range.
Here's the example:
from sympy import *
x, y = symbols('x, y')
eq1 = Eq(y - sin(x), 0)
eq2 = Eq(y - cos(x), 0)
sol = solve([eq1, eq2], [x, y])
print(sol)
which only returns the first two solutions in the positive x range.
How could I do to, for example, display all the solutions within the x range [-2pi, 2pi]?
I'd want them in explicit form rather than written in term of some multiplier since I then need to convert them into numerical form.
Thank you in advance.
SymPy can really take you down rabbit holes. I agree with kampmani's solution, only if you can easily solve for y on your own. However, in more general cases and in higher dimensions, his solution does not hold.
For example, the following will be slightly more tricky:
eq1 = Eq(z - x*y, 0)
eq2 = Eq(z - cos(x) - sin(y), 0)
eq3 = Eq(z + x*y, 0)
So here I am; killing a fly with a bazooka. The problem is that one was able to simplify the set of equations into a single equation with a single variable. But what if you can't do that (for example, if it was a larger system)?
In this case, one needs to use nonlinsolve to solve the system of equations. But this does not provide numeric solutions directly and does not have a domain argument.
So the following code unpacks the solutions. It goes through each tuple in the set of solutions and finds the numeric solutions for each component in the tuple. Then in order to get the full list, you need a Cartesian Product of each of those components. Repeat this for each tuple in the set of solutions.
The following should work for almost any system of equations in any dimension greater than 1. It produces numeric solutions in the cube whose boundaries are the domains variable.
from sympy import *
import itertools # used for cartesian product
x, y, z = symbols('x y z', real=True)
domains = [Interval(-10, 10), Interval(-10, 10), Interval(-10, 10)] # The domain for each variable
eq1 = z - x*y
eq2 = z - cos(x) - sin(y)
eq3 = z + x*y
solutions = nonlinsolve([eq1, eq2, eq3], [x, y, z]) # the recommended function for this situation
print("---------Solution set----------")
print(solutions) # make sure the solution set is reasonable. If not, assertion error will occur
_n = Symbol("n", integer=True) # the solution set often seems to contain these symbols
numeric_solutions = []
assert isinstance(solutions, Set) # everything that I had tried resulted in a FiniteSet output
for solution in solutions.args: # loop through the different kinds of solutions
assert isinstance(solution, Tuple) # each solution should be a Tuple if in 2D or higher
list_of_numeric_values = [] # the list of lists of a single numerical value
for i, element in enumerate(solution):
if isinstance(element, Set):
numeric_values = list(element.intersect(domains[i]))
else: # assume it is an Expr
assert isinstance(element, Expr)
if _n.name in [s.name for s in element.free_symbols]: # if n is in the expression
# change our own _n to the solutions _n since they have different hidden
# properties and they cannot be substituted without having the same _n
_n = [s for s in element.free_symbols if s.name == _n.name][0]
numeric_values = [element.subs(_n, n)
for n in range(-10, 10) # just choose a bunch of sample values
if element.subs(_n, n) in domains[i]]
elif len(element.free_symbols) == 0: # we just have a single, numeric number
numeric_values = [element] if element in domains[i] else []
else: # otherwise we just have an Expr that depends on x or y
# we assume this numerical value is in the domain
numeric_values = [element]
# note that we may have duplicates, so we remove them with `set()`
list_of_numeric_values.append(set(numeric_values))
# find the resulting cartesian product of all our numeric_values
numeric_solutions += itertools.product(*list_of_numeric_values)
# remove duplicates again to be safe with `set()` but then retain ordering with `list()`
numeric_solutions = list(set(numeric_solutions))
print("--------`Expr` values----------")
for i in numeric_solutions:
print(list(i)) # turn it into a `list` since the output below is also a `list`.
print("--------`float` values---------")
for i in numeric_solutions:
print([N(j) for j in i]) # could have been converted into a `tuple` instead
In particular, it produces the following output for the new problem:
---------Solution set----------
FiniteSet((0, ImageSet(Lambda(_n, 2*_n*pi + 3*pi/2), Integers), 0))
--------`Expr` values----------
[0, -5*pi/2, 0]
[0, -pi/2, 0]
[0, 3*pi/2, 0]
--------`float` values---------
[0, -7.85398163397448, 0]
[0, -1.57079632679490, 0]
[0, 4.71238898038469, 0]
It was a lot of effort and probably not worth it but oh well.
By using solveset you can restrict the solutions with domain argument. For evaluating numerical results use .evalf() or another similar method.
from sympy import Interval, symbols, solveset, sin, cos, pi
x = symbols('x')
sol = solveset(cos(x) - sin(x), x, domain=Interval(-2*pi, 2*pi))
print(sol)
print(sol.evalf())
Output
FiniteSet(-7*pi/4, -3*pi/4, pi/4, 5*pi/4)
FiniteSet(-5.49778714378214, -2.35619449019234, 0.785398163397448, 3.92699081698724)
I hope this helps!
Thanks to the brilliant suggestion from #kampmani it is possible to achieve the desired result.
For start, the FiniteSet elements are not indexed and cannot be used, so the FiniteSet has to be converted into a list:
solx_array = []
#
#
#
solx = solveset(cos(x) - sin(x), x, domain=Interval(-2*pi, 2*pi))
solx_array=list(solx)
The next step is to find the y coordinate of the intersection point given its x coordinate. The final code should look somewhat similar to this:
from sympy import Interval, symbols, solveset, sin, cos, pi
sol_array = []
x = symbols('x')
solx = solveset(cos(x) - sin(x), x, domain=Interval(-2*pi, 2*pi))
solx_array=list(solx)
for i in range(len(solx_array)):
soly = cos(solx_array[i])
sol_array.append(str(solx_array[i] + soly))
for i in range(len(sol_array)):
print(sol_array[i])
Still don't know how to convert the results into numerical form though, any idea is very appreciated.
I want to solve a (convex) mixed integer program as well as its continuous relaxation using cvxpy. Is there a way to use the same implementation of the objective and the constraints for both calculations?
As an example, take a look at the MIP example problem from the cvxpy website with some added constraint 'x[0]>=2':
np.random.seed(0)
m, n= 40, 25
A = np.random.rand(m, n)
b = np.random.randn(m)
# Construct a CVXPY problem
x = cp.Variable(n, integer=True) # x is an integer variable
obj = cp.sum_squares(A#x - b)
objective = cp.Minimize(obj)
constraint = [x[0] >= 2]
prob = cp.Problem(objective, constraint)
prob.solve()
print("The optimal value is", prob.value)
print("A solution x is")
print(x.value)
x = cp.Variable(n) # Now, x is no longer an integer variable but continuous
obj = cp.sum_squares(A#x - b) # I want to leave out this line (1)
constraint = [x[0] >= 2] # I want to leave out this line (2)
objective = cp.Minimize(obj)
prob = cp.Problem(objective, constraint)
prob.solve()
print("The optimal value is", prob.value)
print("A solution x is")
print(x.value)
When leaving out line (2), the problem is solved without the constraint. When leaving out line (1), the mixed integer problem is solved (so, changing 'x' to a continuous variable did not have any effect).
I want to avoid reimplementing the objective function and constraints because a missed copy and paste may lead to weird, hard-to-find errors.
Thank you for your help!
Edit: Thank you, Sascha, for your reply. You are right, outsourcing the model building solves the problem. So
class ModelBuilder:
m, n = 40, 25
A = np.random.rand(m, n)
b = np.random.randn(m)
def __init__(self, solve_continuous):
np.random.seed(0)
if solve_continuous:
self.x = cp.Variable(self.n)
else:
self.x = cp.Variable(self.n, integer=True)
#staticmethod
def constraint_func(x):
return [x[0] >= 2]
def objective_func(self, x):
return cp.sum_squares(self.A#x - self.b)
def build_problem(self):
objective = cp.Minimize(self.objective_func(self.x))
constraint = self.constraint_func(self.x)
return cp.Problem(objective, constraint)
# Construct and solve mixed integer problem
build_cont_model = False
MIP_Model = ModelBuilder(build_cont_model)
MIP_problem = MIP_Model.build_problem()
MIP_problem.solve()
print("The optimal value is", MIP_problem.value)
print("A solution x is")
print(MIP_Model.x.value)
# Construct and solve continuous problem
build_cont_model = True
Cont_Model = ModelBuilder(build_cont_model)
Cont_problem = Cont_Model.build_problem()
Cont_problem.solve()
print("The optimal value is", Cont_problem.value)
print("A solution x is")
print(Cont_Model.x.value)
works just as expected. Since I did not have this simple idea, it shows me that I do not yet understand the concept of applying a cvxpy.Variable to an expression.
In my first attempt, I defined variable x and used it when defining obj. Then, I changed the value of x (one line before (1)). I thought that obj was linked to x by a pointer or something similar, so that it would change its behavior, as well. Apparently, this is not the case.
Do you know any resources that could help me understand this behavior? Or is it obvious to anyone that is familiar with Python? Then, where could I learn about it?
I'm working on a blending problem similar to the pulp example
I have this constrain to make sure the quantity produced is the desired one
prob += lpSum([KG[i] * deposit_vars[i] for i in deposit]) == 64, "KGRequirement"
But I also need to add another constraint for the minimun value different than zero, this is because is not convenient that I take for example, 0.002KG of one ingredient, I have to take either 0 or at least 2 kg, hence valid cases are e.g. 0, 2, 2.3, 6, 3.23.
I tried to make it this way:
for i in deposit:
prob += (KG[i] * deposit_vars[i] == 0) or (TM[i] * deposit_vars[i] >= 30)
But that is not working and it just make the problem Infeasible
EDIT
This my current code:
import pulp
from pulp import *
import pandas as pd
food = ["f1","f2","f3","f4"]
KG = [10,20,50,80]
Protein = [18,12,16,18]
Grass = [13,14,13,16]
price_per_kg = [15,11,10,22]
## protein,carbohydrates,kg
df = pd.DataFrame({"tkid":food,"KG":KG,"Protein":Protein,"Grass":Grass,"value":price_per_kg})
deposit = df["tkid"].values.tolist()
factor_volumen = 1
costs = dict((k,v) for k,v in zip(df["tkid"],df["value"]))
Protein = dict((k,v) for k,v in zip(df["tkid"],df["Protein"]))
Grass = dict((k,v) for k,v in zip(df["tkid"],df["Grass"]))
KG = dict((k,v) for k,v in zip(df["tkid"],df["KG"]))
prob = LpProblem("The Whiskas Problem", LpMinimize)
deposit_vars = LpVariable.dicts("Ingr",deposit,0)
prob += lpSum([costs[i]*deposit_vars[i] for i in deposit]), "Total Cost of Ingredients per can"
#prob += lpSum([deposit_vars[i] for i in deposit]) == 1.0, "PercentagesSum"
prob += lpSum([Protein[i] *KG[i] * deposit_vars[i] for i in deposit]) >= 17.2*14, "ProteinRequirement"
prob += lpSum([Grass[i] *KG[i] * deposit_vars[i] for i in deposit]) >= 12.8*14, "FatRequirement"
prob += lpSum([KG[i] * deposit_vars[i] for i in deposit]) == 14, "KGRequirement"
prob += lpSum([KG[i] * deposit_vars[i] for i in deposit]) <= 80, "KGRequirement1"
prob.writeLP("WhiskasModel.lp")
prob.solve()
# The status of the solution is printed to the screen
print ("Status:", LpStatus[prob.status])
# Each of the variables is printed with it's resolved optimum value
for v in prob.variables():
print (v.name, "=", v.varValue)
# The optimised objective function value is printed to the screen
print ("Total Cost of Ingredients per can = ", value(prob.objective))
The new contrain I want to add is in this part:
prob += lpSum([KG[i] * deposit_vars[i] for i in deposit]) <= 80, "KGRequirement1"
Where I want the product KG[i] * deposit_vars[i] be either 0 or to be between a and b
In the traditional linear programming formulation, all variables, objective function(s), and constraints need to be continuous. What you are asking is how to make this variable a discrete variable, i.e. it can only accept values a,b,... and not anything in between. When you have a combination of continuous and discrete variables, that is called a mixed integer problem (MIP). See PuLP documentation that reflects this explanation. I suggest you carefully read the blending problems mentions on "integers;" they are scattered about the page. According to PuLP's documentation, it can solve MIP problems by calling external MIP solver, some of which are already included.
Without a minimum working example, it is a little tricky to explain how to implement this. One way to do this would be to specify the variable(s) as an integer with the values it can take as a dict. Leaving the default solver, COIN-OR's CBC solver solver, will then solve the MIP. Meanwhile, here's a couple of resources for you to move forward:
https://www.toptal.com/algorithms/mixed-integer-programming#example-problem-scheduling
Note how it uses CBC solver, which is the default solver, to solve this problem
http://yetanothermathprogrammingconsultant.blogspot.com/2018/08/scheduling-easy-mip.html
A more explicit example on how they set-up their integer variables and call the CBC solver
'or' is not something you can use in an LP / MIP model directly. Remember, an LP/MIP consists of a linear objective and linear constraints.
To model x=0 or x≥L you can use socalled semi-continuous variables. Most advanced solvers support them. I don't believe Pulp supports this however. As a workaround you can also use a binary variable δ:
δ*L ≤ x ≤ δ*U
where U is an upperbound on x. It is easy to see this works:
δ = 0 ⇒ x = 0
δ = 1 ⇒ L ≤ x ≤ U
Semi-continuous variables don't require these constraints. Just tell the solver variable x is semi-continuous with bounds [L,U] (or just L if there is no upperbound).
The constraint
a*x=0 or L ≤ a*x ≤ U
can be rewritten as
δ*L ≤ x*a ≤ δ*U
δ binary variable
This is a fairly standard formulation. Semi-continuous variables are often used in finance (portfolio models) to prevent small allocations.
All of this keeps the model perfectly linear (not quadratic), so one can use a standard MIP solver and a standard LP/MIP modeling tool such as Pulp.