# 4.8.4. Adaptive Rate MC¶

class ARMCOptimizer(phi: float = 1.0, gamma: float = 2.0, a_target: float = 0.25, super_iter_len: int = 1000, sub_iter_len: int = 100, move_range: Union[float, Sequence[float]] = array([0.9 , 0.905, 0.91 , 0.915, 0.92 , 0.925, 0.93 , 0.935, 0.94 , 0.945, 0.95 , 0.955, 0.96 , 0.965, 0.97 , 0.975, 0.98 , 0.985, 0.99 , 0.995, 1.005, 1.01 , 1.015, 1.02 , 1.025, 1.03 , 1.035, 1.04 , 1.045, 1.05 , 1.055, 1.06 , 1.065, 1.07 , 1.075, 1.08 , 1.085, 1.09 , 1.095, 1.1 ]), move_func: Callable[[float, float], float] = <ufunc 'multiply'>)

An optimzer using the Addaptive Rate Monte Carlo (ARMC) algorithm.

1. A trial state, $$S_{\omega}$$, is generated by moving a random parameter retrieved from a user-specified parameter set (e.g. atomic charge). By default, parameters moves are applied in a multiplicative manner.
2. The move is accepted if the new set of parameters, $$S_{\omega}$$, lowers the auxiliary error ($$\Delta \varepsilon_{QM-MM}$$) with respect to the previous set of accepted parameters $$S_{\omega-i}$$ ($$i > 0$$; see (4.1)). The auxiliary error is calculated with a user-specified cost function.
(4.1)$p(\omega \leftarrow \omega-i) = \Biggl \lbrace { 1, \quad \Delta \varepsilon_{QM-MM} ( S_{\omega} ) \; \lt \; \Delta \varepsilon_{QM-MM} ( S_{\omega-i} ) \atop 0, \quad \Delta \varepsilon_{QM-MM} ( S_{\omega} ) \; \gt \; \Delta \varepsilon_{QM-MM} ( S_{\omega-i} ) }$
1. The parameter history is updated. Either $$S_{\omega}$$ or $$S_{\omega-i}$$ is increased by the variable $$\phi$$ (see (4.2)) if, respectivelly, the new parameters are accepted or rejected. In this manner the underlying PES is continuously modified, preventing the optimizer from getting permanently stuck in a (local) parameter space minima.
(4.2)$\Delta \varepsilon_{QM-MM} ( S_{\omega} ) + \phi \quad \text{if} \quad \Delta \varepsilon_{QM-MM} ( S_{\omega} ) \; \lt \; \Delta \varepsilon_{QM-MM} ( S_{\omega-i} ) \atop \Delta \varepsilon_{QM-MM} ( S_{\omega-i} ) + \phi \quad \text{if} \quad \Delta \varepsilon_{QM-MM} ( S_{\omega} ) \; \gt \; \Delta \varepsilon_{QM-MM} ( S_{\omega-i} )$
1. The parameter $$\phi$$ is updated at regular intervals in order to maintain a constant acceptance rate $$\alpha_{t}$$. This is illustrated in (4.3), where $$\phi$$ is updated the begining of every super-iteration $$\kappa$$. In this example the total number of iterations, $$\kappa \omega$$, is divided into $$\kappa$$ super- and $$\omega$$ sub-iterations.
(4.3)$\phi_{\kappa \omega} = \phi_{ ( \kappa - 1 ) \omega} * \gamma^{ \text{sgn} ( \alpha_{t} - \overline{\alpha}_{ ( \kappa - 1 ) }) } \quad \kappa = 1, 2, 3, ..., N$

Attributes:

phi : float
The variable $$\phi$$.
gamma : float
The constant $$\gamma$$.
a_target : float
The target acceptance rate $$\alpha_{t}$$.
super_iter : range
A range object with the total number of each super-iterations $$\kappa$$. Total number of iterations: $$\kappa \omega$$.
sub_iter : range
A range object with the the length of each ARMC sub-iteration $$\omega$$. Total number of iterations: $$\kappa \omega$$.
move_range : numpy.ndarray[float]
An array-like object containing all allowed move sizes.
move_func : Callable[[float, float], float]
A callable for performing the moves. The callable should take 2 floats (i.e. a single value from the parameter set S_{omega-i} and the move size) and return 1 float (i.e. an updated value for the parameter set S_{omega}).
run : Callable
The (bound) ParameterOptimization.run() method. See the function parameter in ARMCOptimizer.minimize().
bounds : numpy.ndarray or (None, None)
An 2D array (or 2-tuple filled with None) denoting minimum and maximum values for each to-be moved parameter. See the bounds parameter in ARMCOptimizer.minimize().
x_best : numpy.ndarray[float]
The parameter set which minimizes the user-specified error function.
fx_best : float
The error associated with ARMCOptimizer.x_best.
fx_old : float
The error of the last set of accepted parameters.

See Also:

Paper
The paper describing the original ARMC implementation: Salvatore Cosseddu et al, J. Chem. Theory Comput., 2017, 13, 297–308 10.1021/acs.jctc.6b01089.
Code
The Python-based implementation of ARMC this class is based on: Automated Forcefield Optimization Extension github.com/nlesc-nano/auto-FOX.
__init__(phi: float = 1.0, gamma: float = 2.0, a_target: float = 0.25, super_iter_len: int = 1000, sub_iter_len: int = 100, move_range: Union[float, Sequence[float]] = array([0.9 , 0.905, 0.91 , 0.915, 0.92 , 0.925, 0.93 , 0.935, 0.94 , 0.945, 0.95 , 0.955, 0.96 , 0.965, 0.97 , 0.975, 0.98 , 0.985, 0.99 , 0.995, 1.005, 1.01 , 1.015, 1.02 , 1.025, 1.03 , 1.035, 1.04 , 1.045, 1.05 , 1.055, 1.06 , 1.065, 1.07 , 1.075, 1.08 , 1.085, 1.09 , 1.095, 1.1 ]), move_func: Callable[[float, float], float] = <ufunc 'multiply'>) → None

Initialize a ARMCOptimizer instance.

Parameters:

phi : float
The variable $$\phi$$.
gamma : float
The constant $$\gamma$$.
a_target : float
The target acceptance rate $$\alpha_{t}$$.
super_iter_len : int
The total number of each super-iterations $$\kappa$$. Total number of iterations: $$\kappa \omega$$.
sub_iter_len : int
The length of each ARMC sub-iteration $$\omega$$. Total number of iterations: $$\kappa \omega$$.
move_range : array-like[float]
An array-like object containing all allowed move sizes.
move_func : Callable[[float, float], float]
A callable for performing the moves. The callable should take 2 floats (i.e. a single value from the parameter set S_{omega-i} and the move size) and return 1 float (i.e. an updated value for the parameter set S_{omega}).
bounds

Get or set the bounds parameter of ARMCOptimizer.minimize().

minimize(function: Callable, x0: Union[float, Sequence[float]], bounds: Optional[Sequence[Tuple[float, float]]] = None, workers: int = 1) → scm.params.optimizers.base.MinimizeResult

Start the minimization process.

Parameters:

function : Callable
The (bound) ParameterOptimization.run() method.
x0 : array-like[float]
A 1D array-like object representing the set of to-be optimized parameters $$S$$.
bounds : array-like[float], optional
An (optional) 2D array-like object denoting minimum and maximum values for each to-be moved parameter. The sequence should be of the same length as x0.
callbacks : Callable, optional
A callable which allows for Optimization specific callbacks such as early stopping.
_inner(x0_old: numpy.ndarray, omega: int, acceptance: numpy.ndarray) → numpy.ndarray

Run the inner loop of ARMCOptimizer.minimize().

Parameters:

x0_old : numpy.ndarray[float]
The last set of accepted parameters $$S_{\omega-i}$$.
omega : int
The ARMC sub-iteration $$\omega$$.
acceptance : numpy.ndarray[bool]
A boolean array for keeping track of accepted moves during the current sub-iteration.

Returns:

numpy.ndarray[float]:
The accepted parameters $$S_{\omega}$$ or $$S_{\omega-i}$$. Equivalent to x0_old if the new parameter set is not accepted.
static callstop(reason: Any = None)

Signal to terminate the minimize() loop while still returning MinimizeResult.

move(x0: numpy.ndarray, x0_min: Optional[numpy.ndarray] = None, x0_max: Optional[numpy.ndarray] = None) → numpy.ndarray

Create a copy of x0 and apply a random move to it.

The move will be applied with ARMCOptimizer.move_func (multiplication by default) using a random value from ARMCOptimizer.move_range.

Parameters:

x0 : numpy.ndarray[float]
A 1D array of parameters $$S_{\omega-i}$$.
value_min : numpy.ndarray[float], optional
A 1D array minimum values for each to-be moved parameter. The array should be of the same length as x0.
value_max : numpy.ndarray[float], optional
A 1D array maximum values for each to-be moved parameter. The array should be of the same length as x0.

Returns:

numpy.ndarray[float]
A copy of x0 $$S_{\omega}$$. A single value is moved within this parameter set.
phi_apply(aux_err: float) → float

Apply ARMCOptimizer.phi to the supplied auxiliary error: $$\Delta \varepsilon_{QM-MM} + \phi$$.

phi_update(acceptance: numpy.ndarray) → None

Update the variable $$\phi$$ (ARMCOptimizer.phi).

$$\phi$$ is updated based on the target accepatance rate, $$\alpha_{t}$$ (ARMCOptimizer.a_target), and the acceptance rate, acceptance, of the current super-iteration:

$\phi_{\kappa \omega} = \phi_{ ( \kappa - 1 ) \omega} * \gamma^{ \text{sgn} ( \alpha_{t} - \overline{\alpha}_{ ( \kappa - 1 ) }) }$

Parameters:

acceptance : numpy.ndarray[bool]
A boolean array for keeping track of accepted moves over the course of the current sub-iteration of $$\kappa$$.
reset()

This method is called when the Optimization() class is initialized and should reset a previously used optimizer instance.

The Adaptive Rate MC optimizer was contributed by Bas van Beek.