"""
Energy minimization solvers for the sheet vertex model
"""
import numpy as np
from scipy import optimize
from .. import config
[docs]class Solver:
[docs] @classmethod
def find_energy_min(cls, sheet, geom, model, pos_idx=None, **settings_kw):
# Loads 'tyssue/config/solvers/minimize.json
settings = config.solvers.minimize_spec()
settings.update(**settings_kw)
coords = sheet.coords
if pos_idx is None:
pos_idx = sheet.vert_df[sheet.vert_df["is_active"] == 1].index
pos0 = sheet.vert_df.loc[pos_idx, coords].values.ravel()
max_length = 2 * sheet.edge_df["length"].max()
bounds = np.vstack([pos0 - max_length, pos0 + max_length]).T
res = optimize.minimize(
cls.opt_energy,
pos0,
args=(pos_idx, sheet, geom, model),
bounds=bounds,
jac=cls.opt_grad,
**settings["minimize"]
)
return res
[docs] @staticmethod
def set_pos(pos, pos_idx, sheet):
ndims = len(sheet.coords)
pos_ = pos.reshape((pos.size // ndims, ndims))
sheet.vert_df.loc[pos_idx, sheet.coords] = pos_
[docs] @classmethod
def opt_energy(cls, pos, pos_idx, sheet, geom, model):
cls.set_pos(pos, pos_idx, sheet)
geom.update_all(sheet)
return model.compute_energy(sheet, full_output=False)
# The unused arguments bellow are legit, need same call sig as above
[docs] @staticmethod
def opt_grad(pos, pos_idx, sheet, geom, model):
grad_i = model.compute_gradient(sheet, components=False)
grad_i = grad_i.loc[pos_idx]
return grad_i.values.flatten()
[docs] @classmethod
def approx_grad(cls, sheet, geom, model):
pos0 = sheet.vert_df[sheet.coords].values.ravel()
pos_idx = sheet.vert_df[sheet.vert_df["is_active"] == 1].index
grad = optimize.approx_fprime(
pos0, cls.opt_energy, 1e-9, pos_idx, sheet, geom, model
)
return grad
[docs] @classmethod
def check_grad(cls, sheet, geom, model):
pos_idx = sheet.vert_df[sheet.vert_df["is_active"] == 1].index
pos0 = sheet.vert_df.loc[pos_idx, sheet.coords].values.ravel()
grad_err = optimize.check_grad(
cls.opt_energy, cls.opt_grad, pos0.flatten(), pos_idx, sheet, geom, model
)
return grad_err