Error with passing arguments to python optimised - Nlopt

445 views Asked by At

I am trying to get to grips with using Nlopt for optimisation problems in Python. I set myself a basic example as a ways of getting to grips with how to navigate the lib. I have set up 3 simple simultaneous equations shown below, and have cast them into matrix form. I am now struggling to figure out why Nlopt isn't happy with what I'm giving it.

#testing nlopt
import nlopt
import numpy as np
from numpy import array as array

# create 3 simple simultaenous equations

#4x - 3y + z = -10 (1)
#2x + y + 3z = 0 (2)
#-x + 2y -5z = 17 (3)
# sol: x = 1, y = 4, z = -2

constraint_matrix = []
constraint_rhs = []

# eq 1
constraint_matrix.append([4,-3,1])
constraint_rhs.append(-10)

# eq 2
constraint_matrix.append([2,1,3])
constraint_rhs.append(0)

# eq 3
constraint_matrix.append([-1,2,-5])
constraint_rhs.append(17)

A = array(constraint_matrix)
b = np.reshape(array(constraint_rhs), (-1,))

#Ax = b represents the above equations
print(A)
print(b)

ncontrols = A.shape[1]

opt = nlopt.opt(nlopt.LD_SLSQP,ncontrols)

#defining a general function for the above
def my_fn(x):
    print('test')
    print(A)
    print(b)
    val = (A*x) - b
    return val

opt.set_min_objective(my_fn)
x0 = array([6,2,-5])
xopt = opt.optimize(x0)
opt_val = opt.last_optimum_value()
result = opt.last_optimize_result()
print('end')

With the above I get the following error:

  File "C:\Users\sm\Anaconda3\lib\site-packages\nlopt.py", line 335, in optimize    
    return _nlopt.opt_optimize(self, *args)
TypeError: my_fn() takes 1 positional argument but 2 were given

I've also tried to figure out how I actually pass A and b to my_fn to no avail. I tried simply adding A and b as arguments to the function, and doing opt.optimize([x0,A,b]) which has not worked. Can Nlopt not handle matrices, do I need to break this down into 3 individual equality constraints somehow?

Any help would be appreciated

Cheers

1

There are 1 answers

0
Rolf Fankhauser On

try

def my_fn(x, grad):
    val = A.dot(x) - b
    return val[0]*val[0] + val[1]*val[1] + val[2]*val[2]

Some NLopt algorithms need an analytical gradient. LD_SLSQP needs a gradient. Your function returns an array. It should return a scalar. I never tried to solve equation systems with NLopt. It works for me with the objective function above and:

opt = nlopt.opt(nlopt.LN_NELDERMEAD, ncontrols)
opt.set_min_objective(my_fn)
opt.set_xtol_rel(1e-3)
x0 = array([6,2,-5])
xopt = opt.optimize(x0)
opt_val = opt.last_optimum_value()
print('Number of iterations:',opt.get_numevals())
print(xopt) # to see accuracy
print(xopt.round()) # rounded to ints to better check result
print('minf:', opt_val)

Algorithm LN_LN_NEWUOA needs fewer iterations. But to solve this equation system you can use:

A = np.array([[4, -3, 1], 
              [2, 1, 3], 
              [-1, 2, -5]])
y = np.array([-10, 0, 17])

x = np.linalg.solve(A, y)
print(x)

Here is another example of minimizing an objective function with 2 parameters x[0] and x[1] and a constraint x[0] + x[1] <= 10:

import nlopt
import numpy as np

# definition of objective function to be minimized (Minima at (5,4), (5,6), (7,4), (7,6), 
# but only first point fulfills constraint)
# grad (partial derivatives of the function) must be defined for algorithms using it
# 
def myfunc(x, grad):
    y = 3*abs(x[0] - 7)*abs(x[0] - 5) + 2*abs(x[1] - 6)*abs(x[1] - 4)
    return y

def fc(x, grad):
    y = x[0] + x[1] - 10
    return y

# setting algorithm and number of parameters 
# LN_BOBYQA is the name of the algorithm (L for local, N for no derivatives)
opt = nlopt.opt(nlopt.LN_COBYLA, 2)
opt.set_min_objective(myfunc)
opt.add_inequality_constraint(fc, 1e-8)
opt.set_xtol_rel(1e-8)
#x = opt.optimize([4, 5]) # f(5,5)=2 !!
#x = opt.optimize([0, 0]) # f(5,4)=0 correct
#x = opt.optimize([10, 0]) # f(5,4)=0 correct
#x = opt.optimize([4, 5]) # f(5,5)=2 !!
x = opt.optimize([0, 0])
minf = opt.last_optimum_value()

print('Number of iterations:',opt.get_numevals())
print('x[0]:', x[0])
print('x[1]:', x[1])
print('minf:', minf)

Output should be:

Number of iterations: 107
x[0]: 4.999999989270988
x[1]: 3.9997967140036654
minf: 0.0008132910098052998