Related
I am trying to solve the Isoperimetric problem (7.14) from Additional Exercises for Convex Optimization by Stephen Boyd using CVXPY. The problem formulation is:
The code of constraints is given below:
constraints = [ y[1] == 0,
y[N] == 0,
y[F] == yfixed[F],
cp.abs(( y[i+2] - 2 * y[i+1] + y[i]) / h**2) <= C for i in np.arange(1,199) ] #not using the first constraint here
The constraints have for loops, and when I tried to formulate the problem according to the CVXPY documentation, I got the following error
Invalid syntax
cp.abs(( y[i+2] - 2 * y[i+1] + y[i]) / h**2) <= C for i in np.arange(1,199) ]
^
How to use the loops in CVXPY constraints?
You need to express the constraints in terms of matrix-vector equalities and inequalities which follow the DCP protocol for cvxpy.
To elaborate, I can see three kinds of constraints in this problem: (I am going to assume 0-based indexing for programming convenience for the rest of the answer.)
Take y as the N+1 dimensional optimization variable.
Fixed point equality constraints: These basically set some indices of the y vector to a set of given values. Note that the zero boundary conditions y[0] == 0 and y[N] == 0 also fall under this.
Perimeter constraint: This is to be computed using successive differences. And finally we set something like the sum of the square roots of 1 plus the squares of the differences to be less than L. This part is probably the most involved one to write following the cvxpy protocols.
Curvature constraints: This also involves a calculation similar to successive differences like above but this is much easier to write as you'll see this is simply a matrix-vector multiplication type constraint like the first one.
Now let's write the constraints in code.
Necessary imports:
import numpy as np
import cvxpy as cp
import matplotlib.pyplot as plt
from scipy.linalg import circulant
1. Equality constraints:
These basically pick some indices from y and set those to given values. This can be implemented as follows:
def equality_constraints(N, F, vals):
'''
Sets some indices (F) in the y vector to given values. Also sets
y[0] == 0 and y[N] == 0.
Returns E (a matrix) and e (a vector) such that E # y == e.
'''
E = np.zeros((F.shape[0]+2, N+1)) # '+2' for selecting y[0] and y[N] also
e = np.zeros(F.shape[0]+2)
E[0, 0] = 1
E[-1, -1] = 1
if F.shape[0]:
E[1:-1:1][np.arange(F.shape[0]), F] = 1
e[1:-1:1] = vals
return E, e
E is a binary matrix of shape (F.shape[0] + 2) x (N+1) and it has exactly one column set to 1 in each row, giving an index for the (N+1) dimensional vector y and e contains the value for that index of y.
2. Perimeter constraint:
For this, we need successive differences of the form y[i+1] - y[i] for i = 0, 1, 2, . . . , N-1. Note that we can similarly construct a vector having this N successive differences as its elements. We can perform the square root and other operations on this vector easily using vectorized computations. Here we are constructing an N x (N+1) matrix M, which when multiplied by y will give the N differences.
def length_matrix(N):
'''
Returns L with [-1, 1, 0, . . . , 0] as first row and sliding it
to the right to get the following rows.
'''
val = np.array([-1, 1])
offsets = np.array([0, 1])
col0 = np.zeros(N+1)
col0[offsets] = val
M = circulant(col0).T[:-(len(val) - 1)]
return M
The matrix M will be a circulant matrix. I simply transposed it and removed the last row to get the desired matrix. You can see this post to know how to create one such matrix. M looks like this:
array([[-1., 1., 0., ..., 0., 0., 0.],
[ 0., -1., 1., ..., 0., 0., 0.],
[ 0., 0., -1., ..., 0., 0., 0.],
...,
[ 0., 0., 0., ..., 1., 0., 0.],
[ 0., 0., 0., ..., -1., 1., 0.],
[ 0., 0., 0., ..., 0., -1., 1.]])
3. Curvature constraints:
Exactly same matrix calculation like the last one. Just repeat and slide [1, -2, 1] along the rows!
def curvature_constraints(N, C, h):
'''
Returns D and C_vec to be used as D # y <= C and D # y >= -C
as curvature constraints.
'''
val = np.array([1, -2, 1])
offsets = np.array([0, 1, 2])
col0 = np.zeros(N+1)
col0[offsets] = val
D = circulant(col0).T[:-(len(val) - 1)]
D /= h**2
C_vec = np.ones(D.shape[0]) * C
return D, C_vec
I am dividing by h**2 in the matrix itself.
Example:
I have taken this example from the site of the book itself. The data is also available here.
L = 1.5
a = 1
C = 15
N = 200
h = a/N
F = np.array([20,40,140,180]) # fixed points
vals = np.array([0.1, 0.15, 0.15, 0.2])
# Declare an array for plotting purposes
yfixed = np.zeros(N+1)
yfixed[F] = vals
x = np.linspace(0, a, N+1)
Problem Formulation and Solution:
I am leaving it for you to understand how I have assembled the matrices in formulating the constraints, especially the perimeter one. This is not difficult, but might require you some practice depending on how comfortable you are with vectorization. The DCP page is a very good place to start.
y = cp.Variable(N+1)
E, e = equality_constraints(N, F, vals)
M = length_matrix(N)
D, d = curvature_constraints(N, C, h)
constraints = [
E # y == e,
h * cp.sum(cp.norm(cp.vstack([(M # y)/h, np.ones(N)]), p = 2, axis = 0)) <= L,
D # y <= d,
D # y >= -d
]
objective_function = h * cp.sum(y)
objective = cp.Maximize(objective_function)
problem = cp.Problem(objective, constraints)
problem.solve()
plt.plot(0, 0, 'ko')
plt.plot(a, 0, 'ko')
for i in F:
plt.plot(x[i], yfixed[i], 'bo')
plt.plot(x, y.value) # y.value gives the value of the cp Variable
plt.savefig('curve.png')
I got the answer as 0.1594237500556726 for the above example and the curve looks like this:
I have checked this solution with few other contrived test cases to verify correctness. However, there might be other more efficient solutions formulating this problem differently or there might even be some unexpected or embarrassing errors here! Feel free to let me know in case there is some error or you find anything difficult to understand in the answer.
Try to split in two:
constraints = [ y[1] == 0,
y[N] == 0,
y[F] == yfixed[F] ] +
[ cp.abs(( y[i+2] - 2 * y[i+1] + y[i]) / h**2) <= C for i in np.arange(1,199) ]
I try to find optimized weight values of my model by using minimize function in scipy. As seen below the code, I define my error function returning one minus f1 score of the model.
def err_func(weights,x,y):
undetected=0
correct=0
incorrect=0
results=fun(weights,x)
for i in range(0,len(results)):
if(results[i]==y[i]):
correct+=1
elif(not (results[i]==y[i])):
incorrect+=1
undetected=len(y)-(correct+incorrect)
precision=float(correct) / float(correct + incorrect)
recall=float(correct) / float(correct + incorrect + undetected)
f1=2 * precision * recall / (precision + recall)
return 1.0-f1
I use constraints that each value in weights between zero and one, and sum of weights are equals to one. These definitions are as below:
cons = ({'type': 'eq', 'fun': lambda x: 1 - sum(x)})
bnds = tuple((0.0, 1.0) for x in weights)
eps=1e-2
But while running minimze method, my function does not satisfy the constraint.
from scipy.optimize import minimize
res = minimize(err_func, weights,method='L-BFGS-B', args=(x,y),constraints=cons,bounds=bnds,options = {'eps':eps,'maxiter':100})
print res
test_weights=res.x
print sum(test_weights)
I got such an output, sum of weights are larger than one. What am I missing?
> fun: 0.4955555555555555 hess_inv: <11x11 LbfgsInvHessProduct with
> dtype=float64>
> jac: array([ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) message: 'CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL'
> nfev: 24
> nit: 1 status: 0 success: True
> x: array([ 0. , 0.22222222, 0. , 1. , 1. ,
> 0.11111111, 1. , 1. , 1. , 0. , 1. ])
> 6.33333333333
L-BFGS-B only supports bound constraints (that is what the second 'B' means). General constraints are not supported by this method.
Excerpt from scipy docs:
Parameters:
...
constraints : dict or sequence of dict, optional
...
Constraints definition (only for COBYLA and SLSQP)
I am solving the following optimization problem:
with this Python code:
from scipy.optimize import minimize
import math
def f(x):
return math.log(x[0]**2 + 1) + x[1]**4 + x[0]*x[2]
x0 = [0, 0, 0]
cons=({'type': 'ineq',
'fun': lambda x: x[0]**3 - x[1]**2 - 1},
{'type': 'ineq',
'fun': lambda x: x[0]},
{'type': 'ineq',
'fun': lambda x: x[2]})
res = minimize(f, x0, constraints=cons)
print res
I am getting an error
message: 'Inequality constraints incompatible'
What can cause this error?
The issue seems to be with your initial guess. If I change your starting values to
x0 = [1.0, 1.0, 1.0]
Then your code will execute fine (at least on my machine)
Python 3.5.1 (v3.5.1:37a07cee5969, Dec 6 2015, 01:54:25) [MSC v.1900 64 bit (AMD64)] on win32
message: 'Optimization terminated successfully.'
njev: 10
jac: array([ 1., 0., 1., 0.])
fun: 0.6931471805582502
nit: 10
status: 0
x: array([ 1.00000000e+00, -1.39724765e-06, 1.07686548e-14])
success: True
nfev: 51
Scipy's optimize module has lots of options. See the documentation or this tutorial. Since you didn't specify the method here, it will use Sequential Least SQuares Programming (SLSQP). Alternatively, you could use the Trust-Region Constrained Algorithm (trust-const).
For this problem, I found that trust-const seemed much more robust to starting values than SLSQP, handling starting values from [-2,-2,-2] to [10,10,10], although negative initial values resulted in increased iterations, as you'd expect. Negative values below -2 exceeded the max iterations, although I suspect might still converge if you increased max iterations, although specifying negative values at all for x1 and x3 is kind of silly, of course, I just did it to get a sense of how robust it was to a range of starting values.
The specifications for SLSQP and trust-const are conceptually the same, but the syntax is a little different (in particular, note the use of NonlinearConstraint).
from scipy.optimize import minimize, NonlinearConstraint, SR1
def f(x):
return math.log(x[0]**2 + 1) + x[1]**4 + x[0]*x[2]
constr_func = lambda x: np.array( [ x[0]**3 - x[1]**2 - 1,
x[0],
x[2] ] )
x0=[0.,0.,0.]
nonlin_con = NonlinearConstraint( constr_func, 0., np.inf )
res = minimize( f, x0, method='trust-constr',
jac='2-point', hess=SR1(),
constraints = nonlin_con )
Here are the results, edited for conciseness:
fun: 0.6931502233468916
message: '`gtol` termination condition is satisfied.'
x: array([1.00000063e+00, 8.21427026e-09, 2.40956900e-06])
Note that the function value and x values are the same as in #CoryKramer's answer. The x array may look superficially different at first glance, but both answers round to [1, 0, 0].
Ok, so basically my problem is shifting frame of mind from solving math problems „on the paper“ to solving them by programing. Let me explain: I want to know is it possible to perform operations on variable before assigning it a value. Like if I have something like (1-x)**n can I firstly assign n a value, then turn it into a from specific for certain degree and then give x a value or values. If I wasn’t clear enough: if n=2 can I firstly turn equation in form 1-2x+x**2 and then in the next step take care of x value?
I want to write a code for calculating and drawing n-th degree Bezier curve .I am using Bernstein polynomials for this, so I realized that equations consists of 3 parts: first part are polynomial coefficients which are all part of Pascal triangle; I am calculating those and putting them in one list. Second part are coordinates of control points which are also some kind of coefficients, and put them in separate list. Now comes the hard part: part of equation that has a variable.Bernsteins are working with barocentric coordinates (meaning u and 1-u).N-th degree formula for this part of equation is:
u**i *(1-u)**(n-i)
where n is curve degree, I goes from 0->n and U is variable.U is acctualy normalised variable,meaning that it value can be from 0 to 1 and i want to itterate it later in certain number of steps (like 1000).But problem is if i try to use mentioned equation i keep getting error, because Python doesnt know what to do with u.I taught about nested loops in which first one would itterate a value of u from 0 to 1 and second would take care of the mentioned equation from 0 to n, but not sure if it is right solution,and no idea how to chech results.What do you think?
PS: I have not uploaded the code because the part with which im having problem i can not even start,and ,I think but could be wrong, that it is separated from the rest of the code; but if you think it can help solving problem i can upload it.
You can do with higher-order functions, that is functions that return functions, like in
def Bernstein(n,i):
def f(t):
return t**i*(1.0-t)**(n-i)
return f
that you could use like this
b52 = Bernstein(5,2)
val = b52(0.74)
but instead you'll rather use lists
Bernstein_ni = [Bernstein(n,i) for i in range(n+1)]
to be used in a higher order function to build the Bezier curve function
def mk_bezier(Px,Py):
"Input, lists of control points, output a function of t that returns (x,y)"
n = len(Px)
binomials = {0:[1], 1:[1,1], 2:[1,2,1],
3:[1,3,3,1], 4:[1,4,6,4,1], 5:[1,5,10,10,5,1]}
binomial = binomials[n-1]
bPx = [b*x for b,x in zip(binomial,Px)]
bPy = [b*y for b,y in zip(binomial,Py)]
bns = [Bernstein(n-1,i) for i in range(n)]
def f(t):
x = 0 ; y = 0
for i in range(n):
berns = bns[i](t)
x = x + bPx[i]*berns
y = y + bPy[i]*berns
return x, y
return f
eventually, in your program, you can use the function factory like this
linear = mk_bezier([0.0,1.0],[1.0,0.0])
quadra = mk_bezier([0.0,1.0,2.0],[1.0,3.0,1.0])
for t in (0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0):
l = linear(t) ; q = quadra(t)
print "%3.1f (%6.4f,%6.4f) (%6.4f,%6.4f)" % (t, l[0],l[1], q[0],q[1])
and this is the testing output
0.0 (0.0000,1.0000) (0.0000,1.0000)
0.1 (0.1000,0.9000) (0.2000,1.3600)
0.2 (0.2000,0.8000) (0.4000,1.6400)
0.3 (0.3000,0.7000) (0.6000,1.8400)
0.4 (0.4000,0.6000) (0.8000,1.9600)
0.5 (0.5000,0.5000) (1.0000,2.0000)
0.6 (0.6000,0.4000) (1.2000,1.9600)
0.7 (0.7000,0.3000) (1.4000,1.8400)
0.8 (0.8000,0.2000) (1.6000,1.6400)
0.9 (0.9000,0.1000) (1.8000,1.3600)
1.0 (1.0000,0.0000) (2.0000,1.0000)
Edit
I think that the right way to do it is at the module level, with a top level sort-of-defaultdictionary that memoizes all the different lists required to perform the actual computations, but defaultdict doesn't pass a variable to its default_factory and I don't feel like subclassing dict (not now) for the sake of this answer, the main reason being that I've never subclassed before...
In response to OP comment
You say that the function degree is the main parameter? But it is implicitely defined by length of the list of control points...
N = user_input()
P0x = user_input()
P0y = user_input()
PNx = user_input()
PNy = user_input()
# code that computes P1, ..., PNminus1
orderN = mk_bezier([P0x,P1x,...,PNminus1x,PNx],
[P0y,P1y,...,PNminus1y,PNy])
x077, y077 = orderN(0.77)
But the customer is always right, so I'll never try again to convince you that my solution works for you if you state that it does things differently from your expectations.
There are Python packages for doing symbolic math, but it might be easier to use some of the polynomial functions available in Numpy. These functions use the convention that a polynomial is represented as an array of coefficients, starting with the lowest order coefficient. So a polynomial a*x^2 + b*x + c would be represented as array([c, b, a]).
Some examples:
In [49]: import numpy.polynomial.polynomial as poly
In [50]: p = [-1, 1] # -x + 1
In [51]: p = poly.polypow(p, 2)
In [52]: p # should be 1 - 2x + x^2
Out[52]: array([ 1., -2., 1.])
In [53]: x = np.arange(10)
In [54]: poly.polyval(x, p) # evaluate polynomial at points x
Out[54]: array([ 1., 0., 1., 4., 9., 16., 25., 36., 49., 64.])
And you could calculate your Bernstein polynomial in a way similar to this (there is still a binomial coefficient missing):
In [55]: def Bernstein(n, i):
...: part1 = poly.polypow([0, 1], i) # (0 + u)**i
...: part2 = poly.polypow([1, -1], n - i) # (1 - u)**(n - i)
...: return poly.polymul(part1, part2)
In [56]: p = Bernstein(3, 2)
In [57]: p
Out[57]: array([ 0., 0., 1., -1.])
In [58]: poly.polyval(x, p) # evaluate polynomial at points x
Out[58]: array([ 0., 0., -4., -18., ..., -448., -648.])
I would like students to solve a quadratic program in an assignment without them having to install extra software like cvxopt etc. Is there a python implementation available that only depends on NumPy/SciPy?
I'm not very familiar with quadratic programming, but I think you can solve this sort of problem just using scipy.optimize's constrained minimization algorithms. Here's an example:
import numpy as np
from scipy import optimize
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D
# minimize
# F = x[1]^2 + 4x[2]^2 -32x[2] + 64
# subject to:
# x[1] + x[2] <= 7
# -x[1] + 2x[2] <= 4
# x[1] >= 0
# x[2] >= 0
# x[2] <= 4
# in matrix notation:
# F = (1/2)*x.T*H*x + c*x + c0
# subject to:
# Ax <= b
# where:
# H = [[2, 0],
# [0, 8]]
# c = [0, -32]
# c0 = 64
# A = [[ 1, 1],
# [-1, 2],
# [-1, 0],
# [0, -1],
# [0, 1]]
# b = [7,4,0,0,4]
H = np.array([[2., 0.],
[0., 8.]])
c = np.array([0, -32])
c0 = 64
A = np.array([[ 1., 1.],
[-1., 2.],
[-1., 0.],
[0., -1.],
[0., 1.]])
b = np.array([7., 4., 0., 0., 4.])
x0 = np.random.randn(2)
def loss(x, sign=1.):
return sign * (0.5 * np.dot(x.T, np.dot(H, x))+ np.dot(c, x) + c0)
def jac(x, sign=1.):
return sign * (np.dot(x.T, H) + c)
cons = {'type':'ineq',
'fun':lambda x: b - np.dot(A,x),
'jac':lambda x: -A}
opt = {'disp':False}
def solve():
res_cons = optimize.minimize(loss, x0, jac=jac,constraints=cons,
method='SLSQP', options=opt)
res_uncons = optimize.minimize(loss, x0, jac=jac, method='SLSQP',
options=opt)
print '\nConstrained:'
print res_cons
print '\nUnconstrained:'
print res_uncons
x1, x2 = res_cons['x']
f = res_cons['fun']
x1_unc, x2_unc = res_uncons['x']
f_unc = res_uncons['fun']
# plotting
xgrid = np.mgrid[-2:4:0.1, 1.5:5.5:0.1]
xvec = xgrid.reshape(2, -1).T
F = np.vstack([loss(xi) for xi in xvec]).reshape(xgrid.shape[1:])
ax = plt.axes(projection='3d')
ax.hold(True)
ax.plot_surface(xgrid[0], xgrid[1], F, rstride=1, cstride=1,
cmap=plt.cm.jet, shade=True, alpha=0.9, linewidth=0)
ax.plot3D([x1], [x2], [f], 'og', mec='w', label='Constrained minimum')
ax.plot3D([x1_unc], [x2_unc], [f_unc], 'oy', mec='w',
label='Unconstrained minimum')
ax.legend(fancybox=True, numpoints=1)
ax.set_xlabel('x1')
ax.set_ylabel('x2')
ax.set_zlabel('F')
Output:
Constrained:
status: 0
success: True
njev: 4
nfev: 4
fun: 7.9999999999997584
x: array([ 2., 3.])
message: 'Optimization terminated successfully.'
jac: array([ 4., -8., 0.])
nit: 4
Unconstrained:
status: 0
success: True
njev: 3
nfev: 5
fun: 0.0
x: array([ -2.66453526e-15, 4.00000000e+00])
message: 'Optimization terminated successfully.'
jac: array([ -5.32907052e-15, -3.55271368e-15, 0.00000000e+00])
nit: 3
This might be a late answer, but I found CVXOPT - http://cvxopt.org/ - as the commonly used free python library for Quadratic Programming. However, it is not easy to install, as it requires the installation of other dependencies.
I ran across a good solution and wanted to get it out there. There is a python implementation of LOQO in the ELEFANT machine learning toolkit out of NICTA (http://elefant.forge.nicta.com.au as of this posting). Have a look at optimization.intpointsolver. This was coded by Alex Smola, and I've used a C-version of the same code with great success.
mystic provides a pure python implementation of nonlinear/non-convex optimization algorithms with advanced constraints functionality that typically is only found in QP solvers. mystic actually provides more robust constraints than most QP solvers. However, if you are looking for optimization algorithmic speed, then the following is not for you. mystic is not slow, but it's pure python as opposed to python bindings to C. If you are looking for flexibility and QP constraints functionality in a nonlinear solver, then you might be interested.
"""
Maximize: f = 2*x[0]*x[1] + 2*x[0] - x[0]**2 - 2*x[1]**2
Subject to: -2*x[0] + 2*x[1] <= -2
2*x[0] - 4*x[1] <= 0
x[0]**3 -x[1] == 0
where: 0 <= x[0] <= inf
1 <= x[1] <= inf
"""
import numpy as np
import mystic.symbolic as ms
import mystic.solvers as my
import mystic.math as mm
# generate constraints and penalty for a nonlinear system of equations
ieqn = '''
-2*x0 + 2*x1 <= -2
2*x0 - 4*x1 <= 0'''
eqn = '''
x0**3 - x1 == 0'''
cons = ms.generate_constraint(ms.generate_solvers(ms.simplify(eqn,target='x1')))
pens = ms.generate_penalty(ms.generate_conditions(ieqn), k=1e3)
bounds = [(0., None), (1., None)]
# get the objective
def objective(x, sign=1):
x = np.asarray(x)
return sign * (2*x[0]*x[1] + 2*x[0] - x[0]**2 - 2*x[1]**2)
# solve
x0 = np.random.rand(2)
sol = my.fmin_powell(objective, x0, constraint=cons, penalty=pens, disp=True,
bounds=bounds, gtol=3, ftol=1e-6, full_output=True,
args=(-1,))
print 'x* = %s; f(x*) = %s' % (sol[0], -sol[1])
Things to note is that mystic can generically apply LP, QP, and higher order equality and inequality constraints to any given optimizer, not just a special QP solver. Secondly, mystic can digest symbolic math, so the ease of defining/entering the constraints is a bit nicer than working with the matrices and derivatives of functions. mystic depends on numpy, and will use scipy if it is installed (however, scipy is not required). mystic utilizes sympy to handle symbolic constraints, but it's also not required for optimization in general.
Output:
Optimization terminated successfully.
Current function value: -2.000000
Iterations: 3
Function evaluations: 103
x* = [ 2. 1.]; f(x*) = 2.0
Get mystic here: https://github.com/uqfoundation
The qpsolvers package also seems to fit the bill. It only depends on NumPy and can be installed by pip install qpsolvers. Then, you can do:
from numpy import array, dot
from qpsolvers import solve_qp
M = array([[1., 2., 0.], [-8., 3., 2.], [0., 1., 1.]])
P = dot(M.T, M) # quick way to build a symmetric matrix
q = dot(array([3., 2., 3.]), M).reshape((3,))
G = array([[1., 2., 1.], [2., 0., 1.], [-1., 2., -1.]])
h = array([3., 2., -2.]).reshape((3,))
# min. 1/2 x^T P x + q^T x with G x <= h
print "QP solution:", solve_qp(P, q, G, h)
You can also try different QP solvers (such as CVXOPT mentioned by Curious) by changing the solver keyword argument, for example solver='cvxopt' or solver='osqp'.