import numpy as np
from astropy import table
import matplotlib.pyplot as plt
[docs]
class RandomWalkMinimizer:
""" Random Walk Minimizer """
def __init__(self, fun, x0, dx, maxiter=20, args=[], kwargs={}, optind=None,
verbose=False, random="normal"):
""" multiple loops over different dx
Parameters
----------
fun:
objective function
x0: array like
initial guess of x
dx:
a list of different scales. e.g. []
maxiter:
number of max iterations
args, kwargs:
fun(x, *args, **kwargs)
optind:
a subset ind of parameters
verbose: bool
if True, print verbose
random: [uniform, normal]
type of random number generator of
"""
self.fun = fun
self.x0 = x0
self.dx = dx
self.maxiter = maxiter
self.args = args
self.kwargs = kwargs
self.optind = optind
self.verbose = verbose
assert random in ["uniform", "normal"]
self.random = random
self.xhist = []
def __call__(self, x):
return np.float(self.fun(np.array(x), *self.args, **self.kwargs))
[docs]
def run(self, fun=None, x0=None, dx=None, maxiter=None, args=None,
kwargs=None, optind=None, verbose=None, random=None):
x0 = x0 if x0 is not None else self.x0
xhist = []
opthist = []
cloop = 0
for _dx in dx if dx is not None else self.dx:
for _optind in optind if optind is not None else self.optind:
cloop += 1
info = cloop
_result = self.minimize(
fun=fun if fun is not None else self.fun,
x0=x0,
dx=_dx,
maxiter=maxiter if maxiter is not None else self.maxiter,
args=args if args is not None else self.args,
kwargs=kwargs if kwargs is not None else self.kwargs,
optind=_optind,
verbose=verbose if verbose is not None else self.verbose,
info=info if info is not None else self.info,
random=random if random is not None else self.random,
)
opthist.append(_result)
xhist.append(x0)
return dict(x=_result["x"],
nfev=np.sum([_["nfev"]for _ in opthist]),
niter=np.sum([_["niter"] for _ in opthist]),
msg=table.vstack([table.Table(_["msg"]) for _ in opthist]))
[docs]
@staticmethod
def minimize(fun, x0, dx, maxiter=10, args=None, kwargs={},
optind=None, verbose=False, info="", random="normal"):
""" a single
Parameters
----------
fun:
objective function
x0: array like
initial guess of x
dx:
a list of different scales. e.g. []
maxiter:
number of max iterations
args:
arguments
kwargs:
keyword arguments
optind:
a subset ind of parameters, e.g., [5, 7]
verbose: bool
if True, print verbose
random: [uniform, normal]
type of random number generator
info:
additional info appended in msg
"""
if args is None:
args = []
# evaluate cost0
x0 = np.asarray(x0, float)
dx = np.asarray(dx, float)
ndim = len(x0)
cost0 = fun(x0, *args, **kwargs)
# opt ind
if optind is None:
optmask = np.ones(ndim, dtype=bool)
elif len(optind) == ndim:
optmask = np.asarray(optind, dtype=bool)
else:
optmask = np.zeros(ndim, dtype=bool)
optmask[optind] = True
# max number of iterations --> maxiter
niter = 0
nfev = 0
iiter = 0
if verbose:
print("X{} = {}, cost={}".format(niter, x0, cost0))
# messages
this_msg = dict(x=x0,
cost=cost0,
accept=True,
info=info,
nfev=0,
niter=0)
msg = [this_msg]
while iiter < maxiter:
# random step
if random == "normal":
x1 = x0 + np.random.normal(loc=0, scale=dx,
size=x0.shape) * optmask
elif random == "uniform":
x1 = x0 + np.random.uniform(low=-dx, high=dx,
size=x0.shape) * optmask
else:
raise ValueError("Invalid random type! [{}]".format(random))
cost1 = fun(x1, *args, **kwargs)
nfev += 1
if cost1 <= cost0:
x0 = np.copy(x1)
cost0 = cost1
iiter = 0
niter += 1
if verbose:
print("X{} = {}, cost={}".format(niter, x0, cost0), iiter)
# messages
this_msg = dict(x=x1,
cost=cost1,
accept=True,
info=info,
nfev=nfev,
niter=niter)
msg.append(this_msg)
else:
iiter += 1
# messages
this_msg = dict(x=x1,
cost=cost1,
accept=False,
info=info,
nfev=nfev,
niter=niter)
msg.append(this_msg)
return dict(x=x0, nfev=nfev, niter=niter, msg=table.Table(msg))
[docs]
def test1():
def fun(x, asin=1):
return asin * np.sin(x) + x ** 2
xx = np.linspace(-10, 10, 1000)
plt.figure()
plt.plot(xx, fun(xx, 10))
res = RandomWalkMinimizer.minimize(fun, x0=[10], dx=10, maxiter=10,
args=[], optind=None, verbose=True, )
print("")
print(res)
[docs]
def test2():
def fun(x, asin=1):
return np.sum(asin * np.sin(x) + x ** 2)
res = RandomWalkMinimizer.minimize(fun, x0=[-2, -1], dx=1, maxiter=20,
args=[], optind=[0], verbose=True,
random="uniform")
print("")
print(res)
[docs]
def test3():
def fun(x, asin=1):
return np.sum(asin * np.sin(x) + x ** 2)
rwm = RandomWalkMinimizer(fun, x0=[10, 10], dx=[[10, 10], [2, 2]],
maxiter=20, args=[], optind=[[0], [1]],
verbose=True, random="uniform")
res = rwm.run()
print("")
print(res)
if __name__ == "__main__":
test3()