I'm a beginner at python and I try to use cvxpy library for my optimization project. I try to change the first value of my n dimensional variable But I get an AttributeError
import cvxpy as cp
S = cp.Variable(100)
S[0].value=320000
output:AttributeError: can't set attribute
It works for 1-dim variable
import cvxpy as cp
S = cp.Variable()
S.value=320000
Thanks in advance
The 'Variable' object does not support item assignment. You may enforce your requirement as a constraint:
import cvxpy as cp
S = cp.Variable(100) # Define your variables
objective = ... # Define your objective function
constraints = [] # Create an array of constraints
constraints.append(S[0]==320000) # Make your requirement a constraint
# Add more constraints
prob = Problem(objective, constraints) # Define your optimization problem
prob.solve(verbose=True) # Solve the problem
print(S.value)
Related
In Pyomo with "Ipopt" solver, I am trying to define a nonlinear objective function to be piecewise such that when the decision variables q_i < 1, it is a quadratic function; otherwise it is a log function. As suggested by the answer here, using the "Expr_If" expression should do the job. However, when I run the code (below), the solver indicates that it reaches an optimal solution and I can print the value of optimal decision variables by running:
for x in model.q: print(model.q[x].value)
but I can not print the optimal value of the objective function when I run:
model.total_cost() such that I get this error "math domain error". This error might suggest that a log function is being evaluated at a negative value, but based on the objective function that I defined this should not happen. Also, I can calculate the value of the objective function by rewriting it (after obtaining the optimal solution) directly using the values of optimal solution by running the code:
total_cost_ = 0
for i in model.P:
if model.q[i].value>=1:
total_cost_ += model.Beta[i] * log(model.q[i])
else:
total_cost_ += (-0.5)*model.Alpha[i] * (model.q[i]-1)**2
print(total_cost_())
Do you know why I am getting the "math domain error" when I run model.total_cost() ?
My code:
model = ConcreteModel()
#Define the set
model.P = Set(initialize=['P1','P2','P3','P4'])
#Parameters
model.Beta = Param(model.P, initialize = {'P1':1,'P2':1.2,'P3':1.4,'P4':1.6})
model.Alpha = Param(model.P, initialize = {'P1':0.1,'P2':0.2,'P3':0.3,'P4':0.4})
#Variables
model.q = Var(model.P)
#Objective
def Total_Cost(model):
return sum(Expr_if(IF=model.q[i]>=1, THEN=model.Beta[i] * log(model.q[i]),
ELSE=(-0.5)*model.Alpha[i] * (model.q[i]-1)**2) for i in model.P)
model.total_cost = Objective(expr = Total_Cost, sense = maximize)
#Constraints
def limit(model, i):
return -1.1<= model.q[i]
model.limit = Constraint(model.P, rule = limit)
def balance(model):
return summation(model.q) == 0
model.balance = Constraint(rule = balance)
solver = SolverFactory('ipopt')
solver.solve(model)
#model.pprint()
model.total_cost()
You'd get more luck posting this in the Operations Research SE.
I want to implement MLE (Maximum likelihood estimation) with gekko package in python. Suppose that we have a DataFrame that contains two columns: ['Loss', 'Target'] and it length is equal to 500.
First we have to import packages that we need:
from gekko import GEKKO
import numpy as np
import pandas as pd
Then we simply create the DataFrame like this:
My_DataFrame = pd.DataFrame({"Loss":np.linspace(-555.795 , 477.841 , 500) , "Target":0.0})
My_DataFrame = My_DataFrame.sort_values(by=["Loss"] , ascending=False).reset_index(drop=True)
My_DataFrame
It going to be look like this:
Some components of [‘Target’] column should be calculated with a formula that I wrote it right down below in the picture(and the rest of them remains zero. I explained more in continue, please keep reading) so you can see it perfectly. Two main elements of formula are ‘Kasi’ and ‘Betaa’. I want to find best value for them that maximize sum of My_DataFrame[‘Target’]. So you got the idea and what is going to happen!
Now let me show you how I wrote the code for this purpose. First I define my objective function:
def obj_function(Array):
"""
[Purpose]:
+ it will calculate each component of My_DataFrame["Target"] column! then i can maximize sum(My_DataFrame["Target"]) and find best 'Kasi' and 'Betaa' for it!
[Parameters]:
+ This function gets Array that contains 'Kasi' and 'Betaa'.
Array[0] represents 'Kasi' and Array[1] represents 'Betaa'
[returns]:
+ returns a pandas.series.
actually it returns new components of My_DataFrame["Target"]
"""
# in following code if you don't know what is `qw`, just look at the next code cell right after this cell (I mean next section).
# in following code np.where(My_DataFrame["Loss"] == item)[0][0] is telling me the row's index of item.
for item in My_DataFrame[My_DataFrame["Loss"]>160]['Loss']:
My_DataFrame.iloc[np.where(My_DataFrame["Loss"] == item)[0][0] , 1] = qw.log10((1/Array[1])*( 1 + (Array[0]*(item-160)/Array[1])**( (-1/Array[0]) - 1 )))
return My_DataFrame["Target"]
if you got confused what's happening in for loop in obj_function function, check picture below, it contains a brief example! and if not, skip this part :
Then just we need to go through optimization. I use gekko package for this purpose. Note that I want to find best values of ‘Kasi’ and ‘Betaa’ so I have two main variables and I don’t have any kind of constraints!
So let’s get started:
# i have 2 variables : 'Kasi' and 'Betaa', so I put nd=2
nd = 2
qw = GEKKO()
# now i want to specify my variables ('Kasi' and 'Betaa') with initial values --> Kasi = 0.7 and Betaa = 20.0
x = qw.Array(qw.Var , nd , value = [0.7 , 20])
# So i guess now x[0] represents 'Kasi' and x[1] represents 'Betaa'
qw.Maximize(np.sum(obj_function(x)))
And then when I want to solve the optimization with qw.solve():
qw.solve()
But i got this error:
Exception: This steady-state IMODE only allows scalar values.
How can I fix this problem? (Complete script gathered in next section for the purpose of convenience)
from gekko import GEKKO
import numpy as np
import pandas as pd
My_DataFrame = pd.DataFrame({"Loss":np.linspace(-555.795 , 477.841 , 500) , "Target":0.0})
My_DataFrame = My_DataFrame.sort_values(by=["Loss"] , ascending=False).reset_index(drop=True)
def obj_function(Array):
"""
[Purpose]:
+ it will calculate each component of My_DataFrame["Target"] column! then i can maximize sum(My_DataFrame["Target"]) and find best 'Kasi' and 'Betaa' for it!
[Parameters]:
+ This function gets Array that contains 'Kasi' and 'Betaa'.
Array[0] represents 'Kasi' and Array[1] represents 'Betaa'
[returns]:
+ returns a pandas.series.
actually it returns new components of My_DataFrame["Target"]
"""
# in following code if you don't know what is `qw`, just look at the next code cell right after this cell (I mean next section).
# in following code np.where(My_DataFrame["Loss"] == item)[0][0] is telling me the row's index of item.
for item in My_DataFrame[My_DataFrame["Loss"]>160]['Loss']:
My_DataFrame.iloc[np.where(My_DataFrame["Loss"] == item)[0][0] , 1] = qw.log10((1/Array[1])*( 1 + (Array[0]*(item-160)/Array[1])**( (-1/Array[0]) - 1 )))
return My_DataFrame["Target"]
# i have 2 variables : 'Kasi' and 'Betaa', so I put nd=2
nd = 2
qw = GEKKO()
# now i want to specify my variables ('Kasi' and 'Betaa') with initial values --> Kasi = 0.7 and Betaa = 20.0
x = qw.Array(qw.Var , nd)
for i,xi in enumerate([0.7, 20]):
x[i].value = xi
# So i guess now x[0] represents 'Kasi' and x[1] represents 'Betaa'
qw.Maximize(qw.sum(obj_function(x)))
proposed potential script is here:
from gekko import GEKKO
import numpy as np
import pandas as pd
My_DataFrame = pd.read_excel("[<FILE_PATH_IN_YOUR_MACHINE>]\\Losses.xlsx")
# i'll put link of "Losses.xlsx" file in the end of my explaination
# so you can download it from my google drive.
loss = My_DataFrame["Loss"]
def obj_function(x):
k,b = x
target = []
for iloss in loss:
if iloss>160:
t = qw.log((1/b)*(1+(k*(iloss-160)/b)**((-1/k)-1)))
target.append(t)
return target
qw = GEKKO(remote=False)
nd = 2
x = qw.Array(qw.Var,nd)
# initial values --> Kasi = 0.7 and Betaa = 20.0
for i,xi in enumerate([0.7, 20]):
x[i].value = xi
# bounds
k,b = x
k.lower=0.1; k.upper=0.8
b.lower=10; b.upper=500
qw.Maximize(qw.sum(obj_function(x)))
qw.options.SOLVER = 1
qw.solve()
print('k = ',k.value[0])
print('b = ',b.value[0])
python output:
objective function = -1155.4861315885942
b = 500.0
k = 0.1
note that in python output b is representing "Betaa" and k is representing "Kasi".
output seems abit strange, so i decide to test it! for this purpose I used Microsoft Excel Solver!
(i put the link of excel file at the end of my explaination so you can check it out yourself if
you want.) as you can see in picture bellow, optimization by excel has been done and optimal solution
has been found successfully (see picture bellow for optimization result).
excel output:
objective function = -108.21
Betaa = 32.53161
Kasi = 0.436246
as you can see there is huge difference between python output and excel output and seems that excel is performing pretty well! so i guess problem still stands and proposed python script is not performing well...
Implementation_in_Excel.xls file of Optimization by Microsoft excel application is available here.(also you can see the optimization options in Data tab --> Analysis --> Slover.)
data that used for optimization in excel and python are same and it's available here (it's pretty simple and contains 501 rows and 1 column).
*if you can't download the files, let me know then I'll update them.
The initialization is applying the values of [0.7, 20] to each parameter. A scalar should be used to initialize value instead such as:
x = qw.Array(qw.Var , nd)
for i,xi in enumerate([0.7, 20]):
x[i].value = xi
Another issue is that gekko needs to use special functions to perform automatic differentiation for the solvers. For the objective function, switch to the gekko version of summation as:
qw.Maximize(qw.sum(obj_function(x)))
If loss is computed by changing the values of x then the objective function has logical expressions that need special treatment for solution with gradient-based solvers. Try using the if3() function for a conditional statement or else slack variables (preferred). The objective function is evaluated once to build a symbolic expressions that are then compiled to byte-code and solved with one of the solvers. The symbolic expressions are found in m.path in the gk0_model.apm file.
Response to Edit
Thanks for posting an edit with the complete code. Here is a potential solution:
from gekko import GEKKO
import numpy as np
import pandas as pd
loss = np.linspace(-555.795 , 477.841 , 500)
def obj_function(x):
k,b = x
target = []
for iloss in loss:
if iloss>160:
t = qw.log((1/b)*(1+(k*(iloss-160)/b)**((-1/k)-1)))
target.append(t)
return target
qw = GEKKO(remote=False)
nd = 2
x = qw.Array(qw.Var,nd)
# initial values --> Kasi = 0.7 and Betaa = 20.0
for i,xi in enumerate([0.7, 20]):
x[i].value = xi
# bounds
k,b = x
k.lower=0.6; k.upper=0.8
b.lower=10; b.upper=30
qw.Maximize(qw.sum(obj_function(x)))
qw.options.SOLVER = 1
qw.solve()
print('k = ',k.value[0])
print('b = ',b.value[0])
The solver reaches bounds at the solution. The bounds may need to be widened so that arbitrary limits are not the solution.
Update
Here is a final solution. That objective function in code had a problem so it should be fixed Here is the correct script:
from gekko import GEKKO
import numpy as np
import pandas as pd
My_DataFrame = pd.read_excel("<FILE_PATH_IN_YOUR_MACHINE>\\Losses.xlsx")
loss = My_DataFrame["Loss"]
def obj_function(x):
k,b = x
q = ((-1/k)-1)
target = []
for iloss in loss:
if iloss>160:
t = qw.log(1/b) + q* ( qw.log(b+k*(iloss-160)) - qw.log(b))
target.append(t)
return target
qw = GEKKO(remote=False)
nd = 2
x = qw.Array(qw.Var,nd)
# initial values --> Kasi = 0.7 and Betaa = 20.0
for i,xi in enumerate([0.7, 20]):
x[i].value = xi
qw.Maximize(qw.sum(obj_function(x)))
qw.solve()
print('Kasi = ',x[0].value)
print('Betaa = ',x[1].value)
Output:
The final value of the objective function is 108.20609317143486
---------------------------------------------------
Solver : IPOPT (v3.12)
Solution time : 0.031200000000000006 sec
Objective : 108.20609317143486
Successful solution
---------------------------------------------------
Kasi = [0.436245842]
Betaa = [32.531632983]
Results are close to the optimization result from Microsoft Excel.
qw.Maximize() only sets the objective of the optimization, you still need to call solve() on your model.
If I can see correctly, My_DataFrame has been defined in the global scope.
The problem is that the obj_funtion tries to access it (successful) and then, modify it's value (fails)
This is because you can't modify global variables from a local scope by default.
Fix:
At the beginning of the obj_function, add a line:
def obj_function(Array):
# comments
global My_DataFrame
for item .... # remains same
This should fix your problem.
Additional Note:
If you just wanted to access My_DataFrame, it would work without any errors and you don't need to add the global keyword
Also, just wanted to appreciate the effort you put into this. There's a proper explanation of what you want to do, relevant background information, an excellent diagram (Whiteboard is pretty great too), and even a minimal working example.
This should be how all SO questions are, it would make everyone's lives easier
I came across following code:
from pulp import *
import numpy as np
prob = LpProblem("lp_prob", LpMinimize)
decision_variables = LpVariable.dicts('x', range(5))
prob += np.sum(decision_variables.values())
When I tried same code on my machine, it gave following error on last line:
TypeError: Can only add LpConstraintVar, LpConstraint, LpAffineExpression or True objects
Cant I add numpy array to LpProblem. Now I am guessing if the given code is incorrect? Also is there any other way / version (of python and/or numpy and/or pulp) in which adding numpy object to LpProblem works?
decision_variables is not a numpy array. It is a dictionary and that's why you can do decision_variables.values().
decision_variables.values() is not a numpy array either, it's a dictvalues object.
the result of np.sum is not a numpy array. When applied to a numpy array it should return a scalar value. But for a dictvalues object it does nothing (no sum, at least).
I'm not sure why you would need to use np.sum at all here.
In pulp, the sum of a set of pulp variables (list, dictvalues, dictkeys, etc.) is done with the lpSum function (https://www.coin-or.org/PuLP/pulp.html#pulp.lpSum).
prob += lpSum(decision_variables.values())
import pulp as p
import numpy as np
a1=np.array([1000,2000,3000,7000,8000,13000,223000,32000,35000,369000,38000,3885000])
x=p.LpVariable('x', lowBound=5000, cat='Continuous')
y=p.LpVariable('y', lowBound=8000,cat='Continuous')
Lp_prob=(((y-x)*1.3+x)*0.014428)+((a1-y)*1.5*0.014428)
Lp_prob.solve()
I try to do linear programming in pulp. But I have 'LpAffineExpression' object has no attribute 'solve' error.
How can I fix it? Thanks.
I would suggest to first study this example: https://www.coin-or.org/PuLP/CaseStudies/a_blending_problem.html. It has all the ingredients to cover your example.
So a working model can look like:
import pulp as p
import numpy as np
a1=np.array([1000,2000,3000,7000,8000,13000,223000,32000,35000,369000,38000,3885000])
x=p.LpVariable('x', lowBound=5000, cat='Continuous')
y=p.LpVariable('y', lowBound=8000,cat='Continuous')
Lp_prob = p.LpProblem("This_Example_Works",p.LpMaximize)
Lp_prob += (((y-x)*1.3+x)*0.014428)+((a1-y)*1.5*0.014428)
Lp_prob.solve()
print("Status:", p.LpStatus[Lp_prob.status])
Note that PuLP interprets this as:
MAXIMIZE
-0.0043284000000000005*x + -0.24094759999999996*y + 99899.472
VARIABLES
5000 <= x Continuous
8000 <= y Continuous
The very strange construct a1-y is interpreted here as sum(a1-y)=sum(a1)-n*y where n=a1.size. I would suggest not to use NumPy arrays this way in a PuLP model.
I am trying to solve a sample nonlinear problem and I want to setup a preconditioner for newton_krylov solver of scipy.optimize.
I set the preconditioner by the LinearOperator using spilu. The Matrix is changing with the independent variable, i.e., matrix M(x), and M(x) needs to be updated as like F(x), the residual, in each nonlinear iteration.
It seems the "inner_M" option in newton_krylov is only called once the LinearOperator at the beginning of the process. How I can set the "inner_M" preconditioner option to perform M(x) update in each newton iteration?
Specifically, I can not understand the description in
https://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.optimize.newton_krylov.html
for the "inner_M" setup:
"...If the preconditioner has a method named ‘update’, it will be called as update(x, f) after each nonlinear step, with x giving the current point, and f the current function value."
Thanks a lot.
Following the example under
doc/scipy/ ... still-too-slow-precondition,
set M.update to a function which copies x to a global, here xglo.
Or, make a class with self.xupdate.
xglo = None
def get_preconditioner():
...
J1_ilu = spilu(J1) # better spsolve ?
M = LinearOperator(shape=(nx*ny, nx*ny), matvec=J1_ilu.solve)
#.......................................................................
def xglobal( x, F ):
""" newton_krylov calls this at each iteration: xglo = x """
global xglo
xglo = x.copy()
print( "update: x %s F %s " % (nu.asum(x), nu.asum(F)) ) # test
# nu.asum: array summary, size min av max
M.update = xglobal
return M
(I don't know if this is really a good idea -- depends on your preconditioner.
scicomp.stack might be a better place to ask, see e.g.
when-is-newton-krylov-not-an-appropriate-solver
.)