SIRIUS 7.5.0
Electronic structure library and applications
|
Broyden mixer. More...
#include <broyden2_mixer.hpp>
Inherits sirius::mixer::Mixer< FUNCS... >.
Public Member Functions | |
Broyden2 (std::size_t max_history, double beta, double beta0, double beta_scaling_factor, double linear_mix_rmse_tol) | |
void | mix_impl () override |
Public Member Functions inherited from sirius::mixer::Mixer< FUNCS... > | |
Mixer (std::size_t max_history) | |
Construct a mixer. Functions have to initialized individually. More... | |
void | initialize_function (const FunctionProperties< typename std::tuple_element< FUNC_INDEX, std::tuple< FUNCS... > >::type > &function_prop, const typename std::tuple_element< FUNC_INDEX, std::tuple< FUNCS... > >::type &init_value, ARGS &&... args) |
void | set_input (const typename std::tuple_element< FUNC_INDEX, std::tuple< FUNCS... > >::type &input) |
Set input for next mixing step. More... | |
void | get_output (typename std::tuple_element< FUNC_INDEX, std::tuple< FUNCS... > >::type &output) |
Access last generated output. Mixing must have been performed at least once. More... | |
double | mix (double rms_min__) |
Mix input and stored history. Returns the root mean square error computed by inner products of residuals. More... | |
Private Attributes | |
double | beta_ |
double | beta0_ |
double | beta_scaling_factor_ |
double | linear_mix_rmse_tol_ |
sddk::mdarray< double, 2 > | S_ |
sddk::mdarray< double, 1 > | gamma_ |
Additional Inherited Members | |
Static Public Attributes inherited from sirius::mixer::Mixer< FUNCS... > | |
static constexpr std::size_t | number_of_functions |
Protected Member Functions inherited from sirius::mixer::Mixer< FUNCS... > | |
virtual void | mix_impl ()=0 |
void | update_residual () |
void | update_rms () |
std::size_t | idx_hist (std::size_t step) const |
double | inner_product (const std::tuple< std::unique_ptr< FUNCS >... > &x, const std::tuple< std::unique_ptr< FUNCS >... > &y) |
void | scale (double alpha, std::tuple< std::unique_ptr< FUNCS >... > &x) |
void | copy (const std::tuple< std::unique_ptr< FUNCS >... > &x, std::tuple< std::unique_ptr< FUNCS >... > &y) |
void | axpy (double alpha, const std::tuple< std::unique_ptr< FUNCS >... > &x, std::tuple< std::unique_ptr< FUNCS >... > &y) |
void | rotate (double c, double s, std::tuple< std::unique_ptr< FUNCS >... > &x, std::tuple< std::unique_ptr< FUNCS >... > &y) |
Protected Attributes inherited from sirius::mixer::Mixer< FUNCS... > | |
std::size_t | step_ |
std::size_t | max_history_ |
std::vector< double > | rmse_history_ |
std::tuple< FunctionProperties< FUNCS >... > | functions_ |
std::tuple< std::unique_ptr< FUNCS >... > | input_ |
std::vector< std::tuple< std::unique_ptr< FUNCS >... > > | output_history_ |
std::vector< std::tuple< std::unique_ptr< FUNCS >... > > | residual_history_ |
Broyden mixer.
Quasi-Newton, limited-memory method which updates \( x_{n+1} = x_n - G_nf_n \) where \( G_n \) is an approximate inverse Jacobian. Broyden 2 is derived by recursively taking rank-1 updates to the inverse Jacobian. It requires the secant equation \( G_{n+1}\Delta f_n = \Delta x_n\) to hold for the next approximate Jacobian \( G_{n+1}\), leading to the update:
\[ G_{n+1} := G_n + (\Delta x_n - G_n \Delta f_n)(\Delta f_n^*\Delta f_n)^{-1}\Delta f_n^*. \]
By induction we can show that \( G_n \) satisfies
\[ G_n = G_1\prod_{i=1}^{n-1}\left(I - \frac{\Delta f_i \Delta f_i^*}{\Delta f_i^*\Delta f_i}\right) + \sum_{i=1}^{n-1}\frac{\Delta x_i \Delta f_i^*}{\Delta f_i^*\Delta f_i}\prod_{j=i+1}^{n-1} \left(I - \frac{\Delta f_j \Delta f_j^*}{\Delta f_j^*\Delta f_j}\right) \]
which shows orthogonalization with respect to the \( \Delta f_i \) vectors. A practical implementation can be derived from the identity
\[ G_n = G_1 + \sum_{i=1}^{n-1}\left(\Delta x_i - G_1\Delta f_i\right)\gamma_{i,n}^* \]
where
\[ \gamma_{i,n}^* = \frac{\Delta f_i^*}{\Delta f_i^*\Delta f_i}\prod_{j=i+1}^{n-1} \left(I - \frac{\Delta f_j\Delta f_j^*}{\Delta f_j^*\Delta f_j}\right). \]
The \( \gamma_{i,n}\) vector satisfies the following recursion relation:
\[ \gamma_{i,n}^* = \frac{\Delta f_i^*}{\Delta f_i^*\Delta f_i}\left(I - \sum_{j=i+1}^{n-1} \Delta f_j \gamma_{j,n}^*\right) \]
When updating \( x_{n+1} = x_n - G_nf_n\) we only have to compute:
\[ \alpha_i := \gamma_i^*f_n = \frac{1}{\Delta f_i^*\Delta f_i}\left[\Delta f_i^*f_n - \sum_{j=i+1}^{n-1}\alpha_j\Delta f_i^* \Delta f_j \right] \]
for \( i \) from \( n-1\) down to \( 1 \) so that
\[\begin{aligned} x_{n+1} &= x_n - G_nf_n \\ &= x_n - G_1f_n + \sum_{i=1}^{n-1}\alpha_iG_1\Delta f_i - \sum_{i=1}^{n-1}\alpha_i\Delta x_i. \end{aligned}\]
In particular when \( G_1 = -\beta I\) we get the following update:
\[ x_{n+1} = x_n + \beta f_n - \sum_{i=1}^{n-1}\alpha_i \beta \Delta f_i - \sum_{i=1}^{n-1}\alpha_i\Delta x_i. \]
Finally, we store the vectors \( f_1, \cdots, f_n \) and \( x_1, \dots, x_n \) and update the Gram-matrix \( S_{ij} = f_i^*f_j \) in every iteration. The \( \alpha_i \) coefficients can be easily computed from \( S \).
Definition at line 99 of file broyden2_mixer.hpp.
|
inline |
Definition at line 102 of file broyden2_mixer.hpp.
|
inlineoverridevirtual |
Implements sirius::mixer::Mixer< FUNCS... >.
Definition at line 113 of file broyden2_mixer.hpp.
|
private |
Definition at line 188 of file broyden2_mixer.hpp.
|
private |
Definition at line 189 of file broyden2_mixer.hpp.
|
private |
Definition at line 190 of file broyden2_mixer.hpp.
|
private |
Definition at line 191 of file broyden2_mixer.hpp.
|
private |
Definition at line 192 of file broyden2_mixer.hpp.
|
private |
Definition at line 193 of file broyden2_mixer.hpp.