问题
I have a function which takes a list of variables as an argument and I would like to minimize this function using scipy.optimize.minimize.
The problem is that it is decided on runtime for which variable in the argument list the minimization should be done. All other variables will get a fixed value.
Let's make an example to clarify:
a = 1
c = 1.1
d = -1.2
def func( b ):
return function_to_minimize( array=[a,b,c,d] )
sol = scipy.optimize.minimize( func, [b0], args=(a,c,d) )
This works, however, it could be that b
, c
and d
are known and I want to optimize a
to find the minimum solution.
To make it even more complicated, the length of the list is not known either. That means there could be a variabel e
, f
, g
, ... and so on.
The actual notation is as follows. The element which is None
is the one which should be optimized for.
array = [1, 1.1, None, -0.5, 4]
def func(arr):
return function_to_minimize(arr)
startvalue = 1.0
sol = scipy.optimize.minimize( func, [startvalue], args='Array without None' )
Is there a way to tell scipy.optimize.minimize for which element to optimize for? Is there perhaps a smart lambda trick which I could do?
I would really appreciate your help!
回答1:
As you know, the function to be minimized changes depending on what parameters
are given. So we need to write some code which defines the function dynamically.
One way to do this is to define a template string, do some string formatting to
modify the template based on which parameters are given, and then use exec
to
define the function. There is some precedence for this -- the standard library uses this technique to define namedtuples.
So, for example, if the expression we wish to minimize is
4*(b-a)**2 + 5*(c-d)**2
then you could use
import textwrap
import scipy.optimize as optimize
def make_model(*fixed):
template = textwrap.dedent("""
def func(variable, {fixed}):
{variable} = variable
return 4*(b-a)**2 + 5*(c-d)**2
""")
variable = set(('a', 'b', 'c', 'd')).difference(fixed)
ns = dict()
funcstr = template.format(variable=', '.join(variable), fixed=', '.join(fixed))
print(funcstr) # comment out if you don't want to see the function
exec funcstr in ns
return ns['func']
def solve(initial_guess, **givens):
fixed = tuple(givens.keys())
vals = tuple(givens.values())
sol = optimize.minimize(make_model(*fixed), initial_guess, args=vals)
return sol
print(solve(initial_guess=1, a=1, c=1.1, d=-1.2))
which yields
def func(variable, a, c, d):
b = variable
return 4*(b-a)**2 + 5*(c-d)**2
status: 0
success: True
njev: 1
nfev: 3
hess_inv: array([[1]])
fun: array([ 26.45])
x: array([ 1.])
message: 'Optimization terminated successfully.'
jac: array([ 0.])
nit: 0
print(solve(initial_guess=(1, 1), a=1, c=1.1))
yields
def func(variable, a, c):
b, d = variable
return 4*(b-a)**2 + 5*(c-d)**2
status: 0
success: True
njev: 3
nfev: 12
hess_inv: array([[1, 0],
[0, 1]])
fun: 2.4611848645596973e-16
x: array([ 0.99999999, 1.1 ])
message: 'Optimization terminated successfully.'
jac: array([ 1.19209279e-08, 2.88966118e-08])
nit: 1
回答2:
I just wanted to provide the adaptation of unutbu's answer regarding an unknown number of variables using a list arr
as single input, where the parameter to fit is set to None
.
The function to minimize fm
is a dummy function, simply trying to minimize the standard deviation of the array using numpy.std
.
It looks a bit clunky and not very pythonesque, but it works.
import textwrap
import scipy.optimize as optimize
def make_model(n,*fixed):
template = textwrap.dedent("""
import numpy as np
def fm(arr):
return np.std(arr)
def func(variable, {fixed}):
{variable} = variable
return fm(["""+",".join(["a"+str(i) for i in range(n)])+"""])
""")
settuple = tuple(['a'+str(i) for i in range(n)])
variable = set(settuple).difference(fixed)
ns = dict()
funcstr = template.format(variable=', '.join(variable), fixed=', '.join(fixed))
print(funcstr) # comment out if you don't want to see the function
exec funcstr in ns
return ns['func']
def solve(initial_guess, n, **givens):
fixed = tuple(givens.keys())
vals = tuple(givens.values())
sol = optimize.minimize(make_model(n,*fixed), initial_guess, args=vals)
return sol
arr = [1, 1.1, None, -0.5, 4, 3]
s = ""
for i,a in enumerate(arr):
if a is not None:
s+=", a"+str(i)+"="+str(a)
print "print(solve(initial_guess=1, n="+str(len(arr))+s+"))" # comment out if you don't want to see the function
exec "print(solve(initial_guess=1, n="+str(len(arr))+s+"))"
来源:https://stackoverflow.com/questions/31951903/dynamically-choose-argument-for-which-to-minimize-a-function-in-python-using-sci