Gaussian Iterative Stockholder Analysis (GISA) method

[1]:
import numpy as np
from scipy.optimize import least_squares, minimize
from setup import prepare_argument_dict, prepare_grid_and_dens, print_results

from horton_part import GaussianISAWPart

Quadprog Solver

The problem of this solver is that the initials values are set to zeros for all pro-atom parameters and no threshold setting is availiable.

Using API

[2]:
mol, grid, rho = prepare_grid_and_dens("data/h2o.fchk")


def quadprog_solver():
    kwargs = prepare_argument_dict(mol, grid, rho)
    kwargs["solver"] = "quadprog"
    part = GaussianISAWPart(**kwargs)
    part.do_all()
    print_results(part)


quadprog_solver()
The number of electrons: 10.000003764139395
Coordinates of the atoms
 [[ 0.     0.     0.224]
 [-0.     1.457 -0.896]
 [-0.    -1.457 -0.896]]
Atomic numbers of the atom
 [8 1 1]

================================================================================
Information of integral grids.
--------------------------------------------------------------------------------
Grid size of molecular grid: 18460
************************************ Atom 0 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18
          18 18 18 18 18 18 26 26 26 26 38 38 38 38 38 50 50 50 50 50
          86 86 110 110 110 110 170 194 194 434 590 590 434 434 434 302 302 302 194 194
          170 110 110 110 110 110 110 110 110 110 110 110 86 50 50 18 18 18 18 18

************************************ Atom 1 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 18 18 18 18 18
          18 18 18 18 18 18 18 18 18 26 26 26 38 38 38 38 38 38 38 50
          50 50 50 50 50 86 86 86 110 110 110 170 170 170 194 194 194 194 170 170
          170 110 110 110 110 110 110 110 110 110 110 86 86 50 50 50 18 18 18 18

************************************ Atom 2 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 18 18 18 18 18
          18 18 18 18 18 18 18 18 18 26 26 26 38 38 38 38 38 38 38 50
          50 50 50 50 50 86 86 86 110 110 110 170 170 170 194 194 194 194 170 170
          170 110 110 110 110 110 110 110 110 110 110 86 86 50 50 50 18 18 18 18

--------------------------------------------------------------------------------
================================================================================

Performing a density-based AIM analysis with a wavefunction as input.
  Molecular grid    : MolGrid
  Using local grids : True
  Scheme                           : Gaussian Iterative Stockholder Analysis (GISA)
  Outer loop convergence threshold : 1.0e-06
  Inner loop convergence threshold : 1.0e-08
  Maximum iterations               : 1000
  lmax                             : 3
  Solver                           : quadprog
  Grid type                        : 1
Iteration       Change      Entropy
        1   2.11650e-01   2.11396e-01
        2   2.49517e-02   1.12635e-01
        3   1.32558e-02   8.50056e-02
        4   8.55599e-03   6.90944e-02
        5   5.91191e-03   5.95412e-02
        6   4.24249e-03   5.35617e-02
        7   3.12465e-03   4.96967e-02
        8   2.34690e-03   4.71329e-02
        9   1.79038e-03   4.53930e-02
       10   1.38337e-03   4.41873e-02
       11   1.08043e-03   4.33355e-02
       12   8.51611e-04   4.27227e-02
       13   6.76617e-04   4.22746e-02
       14   5.41337e-04   4.19419e-02
       15   4.35763e-04   4.16914e-02
       16   3.52679e-04   4.15003e-02
       17   2.86803e-04   4.13530e-02
       18   2.34217e-04   4.12381e-02
       19   1.91987e-04   4.11477e-02
       20   1.57888e-04   4.10759e-02
       21   1.30221e-04   4.10186e-02
       22   1.07673e-04   4.09724e-02
       23   8.92255e-05   4.09350e-02
       24   7.40810e-05   4.09045e-02
       25   6.16098e-05   4.08796e-02
       26   5.13121e-05   4.08592e-02
       27   4.27890e-05   4.08424e-02
       28   3.57201e-05   4.08285e-02
       29   2.98466e-05   4.08169e-02
       30   2.49587e-05   4.08074e-02
       31   2.08855e-05   4.07994e-02
       32   1.74872e-05   4.07928e-02
       33   1.46491e-05   4.07873e-02
       34   1.22769e-05   4.07827e-02
       35   1.02925e-05   4.07788e-02
       36   8.63147e-06   4.07756e-02
       37   7.24041e-06   4.07729e-02
       38   6.07489e-06   4.07706e-02
       39   5.09794e-06   4.07687e-02
       40   4.27879e-06   4.07671e-02
       41   3.59174e-06   4.07658e-02
       42   3.01536e-06   4.07647e-02
       43   2.53171e-06   4.07637e-02
       44   2.12581e-06   4.07630e-02
       45   1.78511e-06   4.07623e-02
       46   1.49911e-06   4.07617e-02
       47   1.25898e-06   4.07613e-02
       48   1.05737e-06   4.07609e-02
       49   8.88068e-07   4.07606e-02

Computing atomic populations.
Computing atomic charges.
Computing density decomposition for atom 0
Computing density decomposition for atom 1
Computing density decomposition for atom 2
Computing cartesian and pure AIM multipoles and radial AIM moments.
Storing proatom density spline for atom 0.
Storing proatom density spline for atom 1.
Storing proatom density spline for atom 2.
charges:
[-0.863  0.432  0.432]
cartesian multipoles:
[[-0.863  0.    -0.     0.113 -5.313 -0.    -0.    -4.718 -0.    -5.013 -0.    -0.    -0.039  0.    -0.     0.    -0.     0.322 -0.     0.155]
 [ 0.432  0.     0.023 -0.001 -0.309 -0.    -0.    -0.282  0.002 -0.288  0.     0.013  0.003  0.     0.     0.     0.039  0.009  0.015  0.02 ]
 [ 0.432  0.    -0.023 -0.001 -0.309  0.     0.    -0.282 -0.002 -0.288  0.    -0.013  0.003  0.    -0.     0.    -0.039  0.009 -0.015  0.02 ]]

Customized Implementation

[3]:
import quadprog

from horton_part import check_pro_atom_parameters


def _prepare_quantities(bs_funcs, rho, propars, weights, alphas):
    nprim = len(propars)
    G = (
        2
        / np.pi**1.5
        * (alphas[:, None] * alphas[None, :]) ** 1.5
        / (alphas[:, None] + alphas[None, :]) ** 1.5
    )
    G = (G + G.T) / 2

    # a = np.zeros(nprim, float)
    # for k in range(nprim):
    #     a[k] = 2 * np.einsum("i,i,i", weights, bs_funcs[k], rho)
    a = 2 * np.einsum("i,ni,i->n", weights, bs_funcs, rho)

    # Construct linear equality or inequality constraints
    C = np.zeros([nprim, nprim + 1])
    # First column : corresponds to the EQUALITY constraint sum_{k=1..Ka} c_(a,k)= N_a
    C[:, 0] = np.ones(nprim)
    # Other K_a columns : correspond to the INEQUALITY constraints c_(a,k) >=0
    C[0:nprim, 1 : (nprim + 1)] = np.identity(nprim)
    b = np.zeros(nprim + 1)
    # First coefficient : corresponds to the EQUALITY constraint sum_{k=1..Ka} c_(a,k) = N_a
    pop = np.einsum("i,i", weights, rho)
    b[0] = pop
    return G, a, C, b


def opt_propars_qp_quadprog(bs_funcs, rho, propars, points, weights, alphas, threshold):
    r"""
    Optimize parameters for proatom density functions using quadratic programming.

    .. math::

        N_{Ai} = \int \rho_A(r) \frac{\rho_{Ai}^0(r)}{\rho_A^0(r)} dr


        G = \frac{1}{2} c^T S c - c^T b

        S = 2 \int \zeta(\vec{r}) \zeta(\vec{r}) d\vec{r}
        = \frac{2}{\pi \sqrt{\pi}} \frac{(\alpha_k \alpha_l)^{3/2}}{(\alpha_k + \alpha_l)^{3/2}}

        b = 2 * \int \zeta(\vec{r}) \rho_a(\vec{r}) d\vec{r}

    Parameters
    ----------
    bs_funcs : 2D np.ndarray
        Basis functions array with shape (M, N), where 'M' is the number of basis functions
        and 'N' is the number of grid points.
    rho : 1D np.ndarray
        Spherically-averaged atomic density as a function of radial distance, with shape (N,).
    propars : 1D np.ndarray
        Pro-atom parameters with shape (M). 'M' is the number of basis functions.
    points : 1D np.ndarray
        Radial coordinates of grid points, with shape (N,).
    weights : 1D np.ndarray
        Weights for integration, including the angular part (4πr²), with shape (N,).
    threshold : float
        Convergence threshold for the iterative process.
    alphas : 1D np.ndarray
        The Gaussian exponential coefficients.
    threshold : float
        The convergence threshold of the optimization method.

    Returns
    -------
    1D np.ndarray
        Optimized proatom parameters.

    Raises
    ------
    RuntimeError
        If the inner iteration does not converge.


    """
    G, a, C, b = _prepare_quantities(bs_funcs, rho, propars, weights, alphas)
    result = quadprog.solve_qp(G=G, a=a, C=C, b=b, meq=1)[0]
    check_pro_atom_parameters(result, total_population=float(b[0]))
    return result

Now we can call the customized solver through follows:

[4]:
def customized_quadprog_solver():
    kwargs = prepare_argument_dict(mol, grid, rho)
    kwargs["solver"] = opt_propars_qp_quadprog
    kwargs["inner_threshold"] = 1e-8
    part = GaussianISAWPart(**kwargs)
    part.do_all()
    print_results(part)


customized_quadprog_solver()

================================================================================
Information of integral grids.
--------------------------------------------------------------------------------
Grid size of molecular grid: 18460
************************************ Atom 0 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18
          18 18 18 18 18 18 26 26 26 26 38 38 38 38 38 50 50 50 50 50
          86 86 110 110 110 110 170 194 194 434 590 590 434 434 434 302 302 302 194 194
          170 110 110 110 110 110 110 110 110 110 110 110 86 50 50 18 18 18 18 18

************************************ Atom 1 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 18 18 18 18 18
          18 18 18 18 18 18 18 18 18 26 26 26 38 38 38 38 38 38 38 50
          50 50 50 50 50 86 86 86 110 110 110 170 170 170 194 194 194 194 170 170
          170 110 110 110 110 110 110 110 110 110 110 86 86 50 50 50 18 18 18 18

************************************ Atom 2 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 18 18 18 18 18
          18 18 18 18 18 18 18 18 18 26 26 26 38 38 38 38 38 38 38 50
          50 50 50 50 50 86 86 86 110 110 110 170 170 170 194 194 194 194 170 170
          170 110 110 110 110 110 110 110 110 110 110 86 86 50 50 50 18 18 18 18

--------------------------------------------------------------------------------
================================================================================

Performing a density-based AIM analysis with a wavefunction as input.
  Molecular grid    : MolGrid
  Using local grids : True
  Scheme                           : Gaussian Iterative Stockholder Analysis (GISA)
  Outer loop convergence threshold : 1.0e-06
  Inner loop convergence threshold : 1.0e-08
  Maximum iterations               : 1000
  lmax                             : 3
  Solver                           : <function opt_propars_qp_quadprog at 0x10880fe20>
  Grid type                        : 1
Iteration       Change      Entropy
        1   2.11650e-01   2.11396e-01
        2   2.49517e-02   1.12635e-01
        3   1.32558e-02   8.50056e-02
        4   8.55599e-03   6.90944e-02
        5   5.91191e-03   5.95412e-02
        6   4.24249e-03   5.35617e-02
        7   3.12465e-03   4.96967e-02
        8   2.34690e-03   4.71329e-02
        9   1.79038e-03   4.53930e-02
       10   1.38337e-03   4.41873e-02
       11   1.08043e-03   4.33355e-02
       12   8.51611e-04   4.27227e-02
       13   6.76617e-04   4.22746e-02
       14   5.41337e-04   4.19419e-02
       15   4.35763e-04   4.16914e-02
       16   3.52679e-04   4.15003e-02
       17   2.86803e-04   4.13530e-02
       18   2.34217e-04   4.12381e-02
       19   1.91987e-04   4.11477e-02
       20   1.57888e-04   4.10759e-02
       21   1.30221e-04   4.10186e-02
       22   1.07673e-04   4.09724e-02
       23   8.92255e-05   4.09350e-02
       24   7.40810e-05   4.09045e-02
       25   6.16098e-05   4.08796e-02
/Users/runner/work/horton-part/horton-part/src/horton_part/gisa.py:192: UserWarning: Customized solver is used, the argument `inner_threshold` is not used.
  warnings.warn("Customized solver is used, the argument `inner_threshold` is not used.")
       26   5.13121e-05   4.08592e-02
       27   4.27890e-05   4.08424e-02
       28   3.57201e-05   4.08285e-02
       29   2.98466e-05   4.08169e-02
       30   2.49587e-05   4.08074e-02
       31   2.08855e-05   4.07994e-02
       32   1.74872e-05   4.07928e-02
       33   1.46491e-05   4.07873e-02
       34   1.22769e-05   4.07827e-02
       35   1.02925e-05   4.07788e-02
       36   8.63147e-06   4.07756e-02
       37   7.24041e-06   4.07729e-02
       38   6.07489e-06   4.07706e-02
       39   5.09794e-06   4.07687e-02
       40   4.27879e-06   4.07671e-02
       41   3.59174e-06   4.07658e-02
       42   3.01536e-06   4.07647e-02
       43   2.53171e-06   4.07637e-02
       44   2.12581e-06   4.07630e-02
       45   1.78511e-06   4.07623e-02
       46   1.49911e-06   4.07617e-02
       47   1.25898e-06   4.07613e-02
       48   1.05737e-06   4.07609e-02
       49   8.88068e-07   4.07606e-02

Computing atomic populations.
Computing atomic charges.
Computing density decomposition for atom 0
Computing density decomposition for atom 1
Computing density decomposition for atom 2
Computing cartesian and pure AIM multipoles and radial AIM moments.
Storing proatom density spline for atom 0.
Storing proatom density spline for atom 1.
Storing proatom density spline for atom 2.
charges:
[-0.863  0.432  0.432]
cartesian multipoles:
[[-0.863  0.    -0.     0.113 -5.313 -0.    -0.    -4.718 -0.    -5.013 -0.    -0.    -0.039  0.    -0.     0.    -0.     0.322 -0.     0.155]
 [ 0.432  0.     0.023 -0.001 -0.309 -0.    -0.    -0.282  0.002 -0.288  0.     0.013  0.003  0.     0.     0.     0.039  0.009  0.015  0.02 ]
 [ 0.432  0.    -0.023 -0.001 -0.309  0.     0.    -0.282 -0.002 -0.288  0.    -0.013  0.003  0.    -0.     0.    -0.039  0.009 -0.015  0.02 ]]

CVXOPT Solver

Another sovler implemented in CVXOPT can also be applied.

Using API

[5]:
def cvxopt_solver():
    kwargs = prepare_argument_dict(mol, grid, rho)
    kwargs["solver"] = "cvxopt"
    kwargs["solver_options"] = {"eps_rel": 1e-8}
    part = GaussianISAWPart(**kwargs)
    part.do_all()
    print_results(part)
    opt_propars = part._cache["propars"]
    print("Difference between total electrons:")
    print(grid.integrate(rho) - np.sum(opt_propars))


cvxopt_solver()

================================================================================
Information of integral grids.
--------------------------------------------------------------------------------
Grid size of molecular grid: 18460
************************************ Atom 0 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18
          18 18 18 18 18 18 26 26 26 26 38 38 38 38 38 50 50 50 50 50
          86 86 110 110 110 110 170 194 194 434 590 590 434 434 434 302 302 302 194 194
          170 110 110 110 110 110 110 110 110 110 110 110 86 50 50 18 18 18 18 18

************************************ Atom 1 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 18 18 18 18 18
          18 18 18 18 18 18 18 18 18 26 26 26 38 38 38 38 38 38 38 50
          50 50 50 50 50 86 86 86 110 110 110 170 170 170 194 194 194 194 170 170
          170 110 110 110 110 110 110 110 110 110 110 86 86 50 50 50 18 18 18 18

************************************ Atom 2 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 18 18 18 18 18
          18 18 18 18 18 18 18 18 18 26 26 26 38 38 38 38 38 38 38 50
          50 50 50 50 50 86 86 86 110 110 110 170 170 170 194 194 194 194 170 170
          170 110 110 110 110 110 110 110 110 110 110 86 86 50 50 50 18 18 18 18

--------------------------------------------------------------------------------
================================================================================

Performing a density-based AIM analysis with a wavefunction as input.
  Molecular grid    : MolGrid
  Using local grids : True
  Scheme                           : Gaussian Iterative Stockholder Analysis (GISA)
  Outer loop convergence threshold : 1.0e-06
  Inner loop convergence threshold : 1.0e-08
  Maximum iterations               : 1000
  lmax                             : 3
  Solver                           : cvxopt
  Grid type                        : 1
Iteration       Change      Entropy
        1   2.11603e-01   2.11396e-01
        2   2.49842e-02   1.12501e-01
        3   1.32921e-02   8.48478e-02
        4   8.57237e-03   6.89801e-02
        5   5.91785e-03   5.94610e-02
        6   4.24425e-03   5.35056e-02
        7   3.12533e-03   4.96581e-02
        8   2.34836e-03   4.71079e-02
        9   1.79441e-03   4.53802e-02
       10   1.37842e-03   4.41882e-02
       11   1.07512e-03   4.33343e-02
       12   8.48063e-04   4.27203e-02
       13   6.74378e-04   4.22717e-02
       14   5.39965e-04   4.19391e-02
       15   4.34960e-04   4.16889e-02
       16   3.52247e-04   4.14983e-02
       17   2.86606e-04   4.13515e-02
       18   2.34163e-04   4.12373e-02
       19   1.92009e-04   4.11475e-02
       20   1.57947e-04   4.10765e-02
       21   1.30281e-04   4.10198e-02
       22   1.07710e-04   4.09742e-02
       23   8.92219e-05   4.09375e-02
       24   7.40284e-05   4.09077e-02
       25   6.15061e-05   4.08834e-02
       26   5.11599e-05   4.08635e-02
       27   4.25939e-05   4.08472e-02
       28   3.54894e-05   4.08338e-02
       29   2.95885e-05   4.08227e-02
       30   2.46814e-05   4.08136e-02
       31   2.05967e-05   4.08060e-02
       32   1.71939e-05   4.07997e-02
       33   1.43571e-05   4.07945e-02
       34   1.19911e-05   4.07902e-02
       35   1.00168e-05   4.07866e-02
       36   8.36874e-06   4.07836e-02
       37   6.99267e-06   4.07811e-02
       38   5.84343e-06   4.07790e-02
       39   4.88343e-06   4.07772e-02
       40   4.08141e-06   4.07758e-02
       41   3.41127e-06   4.07746e-02
       42   2.85128e-06   4.07736e-02
       43   2.38330e-06   4.07727e-02
       44   1.99218e-06   4.07720e-02
       45   1.66528e-06   4.07714e-02
       46   1.39205e-06   4.07709e-02
       47   1.16366e-06   4.07705e-02
       48   9.72756e-07   4.07702e-02

Computing atomic populations.
Computing atomic charges.
Computing density decomposition for atom 0
Computing density decomposition for atom 1
Computing density decomposition for atom 2
Computing cartesian and pure AIM multipoles and radial AIM moments.
Storing proatom density spline for atom 0.
Storing proatom density spline for atom 1.
Storing proatom density spline for atom 2.
charges:
[-0.863  0.432  0.432]
cartesian multipoles:
[[-0.863  0.    -0.     0.112 -5.312 -0.    -0.    -4.718 -0.    -5.013  0.    -0.    -0.04   0.     0.     0.    -0.     0.322 -0.     0.153]
 [ 0.432  0.     0.023 -0.001 -0.309 -0.    -0.    -0.283  0.002 -0.288  0.     0.013  0.003  0.     0.     0.     0.04   0.009  0.015  0.02 ]
 [ 0.432  0.    -0.023 -0.001 -0.309  0.     0.    -0.283 -0.002 -0.288  0.    -0.013  0.003  0.    -0.     0.    -0.04   0.009 -0.015  0.02 ]]
Difference between total electrons:
1.99248908909766e-05

Customized Implementation

[6]:
import cvxopt


def opt_propars_qp_cvxopt(bs_funcs, rho, propars, points, weights, alphas, threshold):
    """
    Optimize pro-atom parameters using quadratic-programming implemented in the `CVXOPT` package.

    Parameters
    ----------
    bs_funcs : 2D np.ndarray
        Basis functions array with shape (M, N), where 'M' is the number of basis functions
        and 'N' is the number of grid points.
    rho : 1D np.ndarray
        Spherically-averaged atomic density as a function of radial distance, with shape (N,).
    propars : 1D np.ndarray
        Pro-atom parameters with shape (M). 'M' is the number of basis functions.
    points : 1D np.ndarray
        Radial coordinates of grid points, with shape (N,).
    weights : 1D np.ndarray
        Weights for integration, including the angular part (4πr²), with shape (N,).
    threshold : float
        Convergence threshold for the iterative process.
    alphas : 1D np.ndarray
        The Gaussian exponential coefficients.
    threshold : float
        The convergence threshold of the optimization method.

    Returns
    -------
    1D np.ndarray
        Optimized proatom parameters.

    Raises
    ------
    RuntimeError
        If the inner iteration does not converge.

    """
    nprim, npt = bs_funcs.shape

    S = (
        2
        / np.pi**1.5
        * (alphas[:, None] * alphas[None, :]) ** 1.5
        / (alphas[:, None] + alphas[None, :]) ** 1.5
    )
    P = cvxopt.matrix(S)

    vec_b = np.zeros((nprim, 1), float)
    for k in range(nprim):
        vec_b[k] = 2 * np.einsum("i,i", weights * bs_funcs[k], rho)
    q = -cvxopt.matrix(vec_b)

    # Linear inequality constraints
    G = cvxopt.matrix(0.0, (nprim, nprim))
    G[:: nprim + 1] = -1.0
    h = cvxopt.matrix(0.0, (nprim, 1))

    # Linear equality constraints
    A = cvxopt.matrix(1.0, (1, nprim))
    pop = np.einsum("i,i", weights, rho)
    b = cvxopt.matrix(pop, (1, 1))

    # initial_values = cvxopt.matrix(np.array([1.0] * nprim).reshape((nprim, 1)))
    opt_CVX = cvxopt.solvers.qp(
        P,
        q,
        G,
        h,
        A,
        b,
        # initvals=propars,
        initvals=np.zeros_like(propars),
        options={"feastol": threshold, "show_progress": 0},
        # options={"show_progress": log.do_medium, "feastol": threshold},
    )
    new_propars = np.asarray(opt_CVX["x"]).flatten()
    check_pro_atom_parameters(new_propars, total_population=float(pop))
    return new_propars
[7]:
def customized_cvxopt_solver():
    kwargs = prepare_argument_dict(mol, grid, rho)
    kwargs["solver"] = opt_propars_qp_cvxopt
    kwargs["inner_threshold"] = 1e-8
    part = GaussianISAWPart(**kwargs)
    part.do_all()
    print_results(part)
    opt_propars = part._cache["propars"]
    print("Difference between total electrons:")
    print(grid.integrate(rho) - np.sum(opt_propars))


customized_cvxopt_solver()

================================================================================
Information of integral grids.
--------------------------------------------------------------------------------
Grid size of molecular grid: 18460
************************************ Atom 0 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18
          18 18 18 18 18 18 26 26 26 26 38 38 38 38 38 50 50 50 50 50
          86 86 110 110 110 110 170 194 194 434 590 590 434 434 434 302 302 302 194 194
          170 110 110 110 110 110 110 110 110 110 110 110 86 50 50 18 18 18 18 18

************************************ Atom 1 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 18 18 18 18 18
          18 18 18 18 18 18 18 18 18 26 26 26 38 38 38 38 38 38 38 50
          50 50 50 50 50 86 86 86 110 110 110 170 170 170 194 194 194 194 170 170
          170 110 110 110 110 110 110 110 110 110 110 86 86 50 50 50 18 18 18 18

************************************ Atom 2 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 18 18 18 18 18
          18 18 18 18 18 18 18 18 18 26 26 26 38 38 38 38 38 38 38 50
          50 50 50 50 50 86 86 86 110 110 110 170 170 170 194 194 194 194 170 170
          170 110 110 110 110 110 110 110 110 110 110 86 86 50 50 50 18 18 18 18

--------------------------------------------------------------------------------
================================================================================

Performing a density-based AIM analysis with a wavefunction as input.
  Molecular grid    : MolGrid
  Using local grids : True
  Scheme                           : Gaussian Iterative Stockholder Analysis (GISA)
  Outer loop convergence threshold : 1.0e-06
  Inner loop convergence threshold : 1.0e-08
  Maximum iterations               : 1000
  lmax                             : 3
  Solver                           : <function opt_propars_qp_cvxopt at 0x1088948b0>
  Grid type                        : 1
Iteration       Change      Entropy
        1   2.11603e-01   2.11396e-01
        2   2.49842e-02   1.12501e-01
        3   1.32921e-02   8.48478e-02
        4   8.57237e-03   6.89801e-02
        5   5.91785e-03   5.94610e-02
        6   4.24425e-03   5.35056e-02
        7   3.12533e-03   4.96581e-02
        8   2.34836e-03   4.71079e-02
        9   1.79441e-03   4.53802e-02
       10   1.37842e-03   4.41882e-02
       11   1.07512e-03   4.33343e-02
       12   8.48063e-04   4.27203e-02
       13   6.74378e-04   4.22717e-02
       14   5.39965e-04   4.19391e-02
       15   4.34960e-04   4.16889e-02
       16   3.52247e-04   4.14983e-02
       17   2.86606e-04   4.13515e-02
       18   2.34163e-04   4.12373e-02
       19   1.92009e-04   4.11475e-02
       20   1.57947e-04   4.10765e-02
       21   1.30281e-04   4.10198e-02
       22   1.07710e-04   4.09742e-02
       23   8.92219e-05   4.09375e-02
       24   7.40284e-05   4.09077e-02
       25   6.15061e-05   4.08834e-02
       26   5.11599e-05   4.08635e-02
       27   4.25939e-05   4.08472e-02
       28   3.54894e-05   4.08338e-02
       29   2.95885e-05   4.08227e-02
       30   2.46814e-05   4.08136e-02
       31   2.05967e-05   4.08060e-02
       32   1.71939e-05   4.07997e-02
       33   1.43571e-05   4.07945e-02
       34   1.19911e-05   4.07902e-02
       35   1.00168e-05   4.07866e-02
       36   8.36874e-06   4.07836e-02
       37   6.99267e-06   4.07811e-02
       38   5.84343e-06   4.07790e-02
       39   4.88343e-06   4.07772e-02
       40   4.08141e-06   4.07758e-02
       41   3.41127e-06   4.07746e-02
       42   2.85128e-06   4.07736e-02
       43   2.38330e-06   4.07727e-02
       44   1.99218e-06   4.07720e-02
       45   1.66528e-06   4.07714e-02
       46   1.39205e-06   4.07709e-02
       47   1.16366e-06   4.07705e-02
       48   9.72756e-07   4.07702e-02

Computing atomic populations.
Computing atomic charges.
Computing density decomposition for atom 0
Computing density decomposition for atom 1
Computing density decomposition for atom 2
Computing cartesian and pure AIM multipoles and radial AIM moments.
Storing proatom density spline for atom 0.
Storing proatom density spline for atom 1.
Storing proatom density spline for atom 2.
charges:
[-0.863  0.432  0.432]
cartesian multipoles:
[[-0.863  0.     0.     0.112 -5.312 -0.    -0.    -4.718 -0.    -5.013 -0.    -0.    -0.04   0.    -0.     0.    -0.     0.322 -0.     0.153]
 [ 0.432  0.     0.023 -0.001 -0.309 -0.    -0.    -0.283  0.002 -0.288  0.     0.013  0.003  0.     0.     0.     0.04   0.009  0.015  0.02 ]
 [ 0.432  0.    -0.023 -0.001 -0.309  0.     0.    -0.283 -0.002 -0.288  0.    -0.013  0.003  0.    -0.     0.    -0.04   0.009 -0.015  0.02 ]]
Difference between total electrons:
1.9924890892752956e-05

ECOS Sovler

[8]:
def ecos_solver():
    kwargs = prepare_argument_dict(mol, grid, rho)
    kwargs["solver"] = "ecos"
    kwargs["solver_options"] = {"reltol": 1e-10}
    part = GaussianISAWPart(**kwargs)
    part.do_all()
    print_results(part)
    opt_propars = part._cache["propars"]
    print("Difference between total electrons:")
    print(grid.integrate(rho) - np.sum(opt_propars))


ecos_solver()

================================================================================
Information of integral grids.
--------------------------------------------------------------------------------
Grid size of molecular grid: 18460
************************************ Atom 0 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18
          18 18 18 18 18 18 26 26 26 26 38 38 38 38 38 50 50 50 50 50
          86 86 110 110 110 110 170 194 194 434 590 590 434 434 434 302 302 302 194 194
          170 110 110 110 110 110 110 110 110 110 110 110 86 50 50 18 18 18 18 18

************************************ Atom 1 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 18 18 18 18 18
          18 18 18 18 18 18 18 18 18 26 26 26 38 38 38 38 38 38 38 50
          50 50 50 50 50 86 86 86 110 110 110 170 170 170 194 194 194 194 170 170
          170 110 110 110 110 110 110 110 110 110 110 86 86 50 50 50 18 18 18 18

************************************ Atom 2 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 18 18 18 18 18
          18 18 18 18 18 18 18 18 18 26 26 26 38 38 38 38 38 38 38 50
          50 50 50 50 50 86 86 86 110 110 110 170 170 170 194 194 194 194 170 170
          170 110 110 110 110 110 110 110 110 110 110 86 86 50 50 50 18 18 18 18

--------------------------------------------------------------------------------
================================================================================

Performing a density-based AIM analysis with a wavefunction as input.
  Molecular grid    : MolGrid
  Using local grids : True
  Scheme                           : Gaussian Iterative Stockholder Analysis (GISA)
  Outer loop convergence threshold : 1.0e-06
  Inner loop convergence threshold : 1.0e-08
  Maximum iterations               : 1000
  lmax                             : 3
  Solver                           : ecos
  Grid type                        : 1
Iteration       Change      Entropy
        1   2.11662e-01   2.11396e-01
        2   2.49254e-02   1.12672e-01
        3   1.32492e-02   8.50267e-02
        4   8.53556e-03   6.91032e-02
        5   5.93385e-03   5.95247e-02
        6   4.24356e-03   5.35553e-02
        7   3.11140e-03   4.96930e-02
        8   2.33603e-03   4.71239e-02
        9   1.82012e-03   4.53760e-02
       10   1.39577e-03   4.41781e-02
       11   1.08352e-03   4.33397e-02
       12   8.45891e-04   4.27194e-02
       13   6.72398e-04   4.22667e-02
       14   5.38656e-04   4.19314e-02
       15   4.34270e-04   4.16797e-02
       16   3.52063e-04   4.14884e-02
       17   2.86808e-04   4.13414e-02
       18   2.34613e-04   4.12271e-02
       19   1.92580e-04   4.11376e-02
       20   1.57877e-04   4.10667e-02
       21   1.36816e-04   4.10081e-02
       22   1.13458e-04   4.09653e-02
       23   9.30038e-05   4.09299e-02
       24   7.62189e-05   4.09004e-02
       25   6.26717e-05   4.08757e-02
       26   5.17460e-05   4.08552e-02
       27   4.28451e-05   4.08381e-02
       28   3.55703e-05   4.08239e-02
       29   2.96656e-05   4.08120e-02
       30   2.47262e-05   4.08022e-02
       31   2.06659e-05   4.07939e-02
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/qpsolvers/solvers/ecos_.py:126: UserWarning: warm-start values are ignored by this wrapper
  warnings.warn("warm-start values are ignored by this wrapper")
       32   1.73065e-05   4.07871e-02
       33   1.45088e-05   4.07813e-02
       34   1.21624e-05   4.07765e-02
       35   1.02060e-05   4.07725e-02
       36   8.54192e-06   4.07692e-02
       37   7.18210e-06   4.07664e-02
       38   6.06573e-06   4.07640e-02
       39   5.04520e-06   4.07621e-02
       40   4.27853e-06   4.07604e-02
       41   3.55020e-06   4.07590e-02
       42   3.03717e-06   4.07578e-02
       43   2.51777e-06   4.07569e-02
       44   2.12495e-06   4.07560e-02
       45   1.79963e-06   4.07554e-02
       46   1.49222e-06   4.07548e-02
       47   1.26242e-06   4.07543e-02
       48   1.07009e-06   4.07539e-02
       49   8.93342e-07   4.07535e-02

Computing atomic populations.
Computing atomic charges.
Computing density decomposition for atom 0
Computing density decomposition for atom 1
Computing density decomposition for atom 2
Computing cartesian and pure AIM multipoles and radial AIM moments.
Storing proatom density spline for atom 0.
Storing proatom density spline for atom 1.
Storing proatom density spline for atom 2.
charges:
[-0.864  0.432  0.432]
cartesian multipoles:
[[-0.864 -0.     0.     0.113 -5.313 -0.    -0.    -4.719 -0.    -5.014  0.    -0.    -0.039 -0.    -0.     0.    -0.     0.323 -0.     0.156]
 [ 0.432  0.     0.023 -0.001 -0.309 -0.    -0.    -0.282  0.002 -0.288  0.     0.013  0.003  0.     0.     0.     0.039  0.009  0.015  0.02 ]
 [ 0.432  0.    -0.023 -0.001 -0.309  0.     0.    -0.282 -0.002 -0.288  0.    -0.013  0.003  0.    -0.     0.    -0.039  0.009 -0.015  0.02 ]]
Difference between total electrons:
1.9957000491288568e-05

OSQP Solver

[9]:
def osqp_solver():
    kwargs = prepare_argument_dict(mol, grid, rho)
    kwargs["solver"] = "osqp"
    kwargs["solver_options"] = {"eps_rel": 1e-14}
    part = GaussianISAWPart(**kwargs)
    part.do_all()
    print_results(part)
    opt_propars = part._cache["propars"]
    print("Difference between total electrons:")
    print(grid.integrate(rho) - np.sum(opt_propars))


osqp_solver()

================================================================================
Information of integral grids.
--------------------------------------------------------------------------------
Grid size of molecular grid: 18460
************************************ Atom 0 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18
          18 18 18 18 18 18 26 26 26 26 38 38 38 38 38 50 50 50 50 50
          86 86 110 110 110 110 170 194 194 434 590 590 434 434 434 302 302 302 194 194
          170 110 110 110 110 110 110 110 110 110 110 110 86 50 50 18 18 18 18 18

************************************ Atom 1 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 18 18 18 18 18
          18 18 18 18 18 18 18 18 18 26 26 26 38 38 38 38 38 38 38 50
          50 50 50 50 50 86 86 86 110 110 110 170 170 170 194 194 194 194 170 170
          170 110 110 110 110 110 110 110 110 110 110 86 86 50 50 50 18 18 18 18

************************************ Atom 2 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 18 18 18 18 18
          18 18 18 18 18 18 18 18 18 26 26 26 38 38 38 38 38 38 38 50
          50 50 50 50 50 86 86 86 110 110 110 170 170 170 194 194 194 194 170 170
          170 110 110 110 110 110 110 110 110 110 110 86 86 50 50 50 18 18 18 18

--------------------------------------------------------------------------------
================================================================================

Performing a density-based AIM analysis with a wavefunction as input.
  Molecular grid    : MolGrid
  Using local grids : True
  Scheme                           : Gaussian Iterative Stockholder Analysis (GISA)
  Outer loop convergence threshold : 1.0e-06
  Inner loop convergence threshold : 1.0e-08
  Maximum iterations               : 1000
  lmax                             : 3
  Solver                           : osqp
  Grid type                        : 1
Iteration       Change      Entropy
        1   2.11649e-01   2.11396e-01
        2   2.49710e-02   1.12965e-01
        3   1.32661e-02   8.52882e-02
        4   8.56305e-03   6.93508e-02
        5   5.91729e-03   5.97838e-02
        6   4.24685e-03   5.37974e-02
        7   3.12836e-03   4.99294e-02
        8   2.35018e-03   4.73647e-02
        9   1.79338e-03   4.56249e-02
       10   1.38617e-03   4.44201e-02
       11   1.08306e-03   4.35694e-02
       12   8.54102e-04   4.29579e-02
       13   6.78981e-04   4.25110e-02
       14   5.43600e-04   4.21797e-02
       15   4.37940e-04   4.19309e-02
       16   3.54765e-04   4.17414e-02
       17   2.88797e-04   4.15952e-02
       18   2.36114e-04   4.14815e-02
       19   1.93779e-04   4.13921e-02
       20   1.59569e-04   4.13211e-02
       21   1.31788e-04   4.12644e-02
       22   1.09127e-04   4.12188e-02
       23   9.05683e-05   4.11818e-02
       24   7.53150e-05   4.11518e-02
       25   6.27388e-05   4.11271e-02
       26   5.23410e-05   4.11069e-02
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/qpsolvers/conversions/ensure_sparse_matrices.py:28: SparseConversionWarning: Converted matrix 'P' of your problem to scipy.sparse.csc_matrix to pass it to solver 'osqp'; for best performance, build your matrix as a csc_matrix directly.
  warnings.warn(
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/qpsolvers/conversions/ensure_sparse_matrices.py:28: SparseConversionWarning: Converted matrix 'G' of your problem to scipy.sparse.csc_matrix to pass it to solver 'osqp'; for best performance, build your matrix as a csc_matrix directly.
  warnings.warn(
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/qpsolvers/conversions/ensure_sparse_matrices.py:28: SparseConversionWarning: Converted matrix 'A' of your problem to scipy.sparse.csc_matrix to pass it to solver 'osqp'; for best performance, build your matrix as a csc_matrix directly.
  warnings.warn(
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.94063E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.78186E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.67535E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.60093E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.54745E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.50816E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.47877E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.45646E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.44044E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.42800E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.41825E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.41053E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40618E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40489E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40397E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40331E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40326E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40336E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40348E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40360E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40372E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40384E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40395E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40405E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40413E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40421E-04
  warnings.warn(info)
       27   4.37231e-05   4.10903e-02
       28   3.65650e-05   4.10765e-02
       29   3.06083e-05   4.10651e-02
       30   2.56433e-05   4.10556e-02
       31   2.14990e-05   4.10477e-02
       32   1.80354e-05   4.10411e-02
       33   1.51377e-05   4.10356e-02
       34   1.27113e-05   4.10310e-02
       35   1.06778e-05   4.10271e-02
       36   8.97254e-06   4.10239e-02
       37   7.54170e-06   4.10212e-02
       38   6.34051e-06   4.10189e-02
       39   5.33170e-06   4.10170e-02
       40   4.48416e-06   4.10154e-02
       41   3.77188e-06   4.10141e-02
       42   3.17314e-06   4.10129e-02
       43   2.66971e-06   4.10120e-02
       44   2.24634e-06   4.10112e-02
       45   1.89026e-06   4.10105e-02
       46   1.59072e-06   4.10099e-02
       47   1.33872e-06   4.10095e-02
       48   1.12669e-06   4.10091e-02
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40428E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40434E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40439E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40443E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40447E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40450E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40453E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40455E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40457E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40459E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40461E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40462E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40463E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40464E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40465E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40466E-04
  warnings.warn(info)
/Users/runner/work/horton-part/horton-part/src/horton_part/utils.py:454: UserWarning: WARNING: The sum of pro-atom parameters is not equal to reference population.
WARNING: The difference is -1.40467E-04
  warnings.warn(info)
       49   9.48273e-07   4.10087e-02

Computing atomic populations.
Computing atomic charges.
Computing density decomposition for atom 0
Computing density decomposition for atom 1
Computing density decomposition for atom 2
Computing cartesian and pure AIM multipoles and radial AIM moments.
Storing proatom density spline for atom 0.
Storing proatom density spline for atom 1.
Storing proatom density spline for atom 2.
charges:
[-0.865  0.432  0.432]
cartesian multipoles:
[[-0.865  0.    -0.     0.114 -5.316 -0.    -0.    -4.722  0.    -5.017 -0.    -0.    -0.037  0.    -0.     0.    -0.     0.326 -0.     0.162]
 [ 0.432  0.     0.023 -0.001 -0.308 -0.    -0.    -0.281  0.002 -0.287  0.     0.013  0.003  0.     0.     0.     0.038  0.009  0.015  0.02 ]
 [ 0.432  0.    -0.023 -0.001 -0.308  0.     0.    -0.281 -0.002 -0.287  0.    -0.013  0.003  0.    -0.     0.    -0.038  0.009 -0.015  0.02 ]]
Difference between total electrons:
0.00030096872035656475

More supported sovlers can be found in `qpsovlers <https://qpsolvers.github.io/qpsolvers/supported-solvers.html>`__ package.

Customized SLSQP Solver

A equivalent method is using minimization with the ‘SLSQP’ sovler implemented in SciPy package.

[10]:
def opt_propars_qp_slsqp(bs_funcs, rho, propars, points, weights, alphas, threshold):
    r"""
    Optimize parameters for proatom density functions using quadratic programming.

    .. math::

        N_{Ai} = \int \rho_A(r) \frac{\rho_{Ai}^0(r)}{\rho_A^0(r)} dr


        G = \frac{1}{2} c^T S c - c^T b

        S = 2 \int \zeta(\vec{r}) \zeta(\vec{r}) d\vec{r}
        = \frac{2}{\pi \sqrt{\pi}} \frac{(\alpha_k \alpha_l)^{3/2}}{(\alpha_k + \alpha_l)^{3/2}}

        b = 2 * \int \zeta(\vec{r}) \rho_a(\vec{r}) d\vec{r}

    Parameters
    ----------
    bs_funcs : 2D np.ndarray
        Basis functions array with shape (M, N), where 'M' is the number of basis functions
        and 'N' is the number of grid points.
    rho : 1D np.ndarray
        Spherically-averaged atomic density as a function of radial distance, with shape (N,).
    propars : 1D np.ndarray
        Pro-atom parameters with shape (M). 'M' is the number of basis functions.
    points : 1D np.ndarray
        Radial coordinates of grid points, with shape (N,).
    weights : 1D np.ndarray
        Weights for integration, including the angular part (4πr²), with shape (N,).
    threshold : float
        Convergence threshold for the iterative process.
    alphas : 1D np.ndarray
        The Gaussian exponential coefficients.
    threshold : float
        The convergence threshold of the optimization method.

    Returns
    -------
    1D np.ndarray
        Optimized proatom parameters.

    Raises
    ------
    RuntimeError
        If the inner iteration does not converge.


    """
    G, a, C, b = _prepare_quantities(bs_funcs, rho, propars, weights, alphas)
    meq = 1

    def _obj_func(x):
        return 0.5 * np.dot(x, G).dot(x) - np.dot(a, x)

    constraints = []
    if C is not None:
        constraints = [
            {
                "type": "eq" if i < meq else "ineq",
                "fun": lambda x, C=C, b=b, i=i: (np.dot(C.T, x) - b)[i],
            }
            for i in range(C.shape[1])
        ]

    result = minimize(
        _obj_func,
        x0=np.zeros(len(G)),
        method="SLSQP",
        constraints=constraints,
        tol=threshold,
        options={"maxiter": 2000},
    )
    return result.x
[11]:
def customized_slsqp_solver():
    kwargs = prepare_argument_dict(mol, grid, rho)
    kwargs["solver"] = opt_propars_qp_cvxopt
    # the value is from the test file of quadprog package.
    kwargs["inner_threshold"] = 1e-10
    part = GaussianISAWPart(**kwargs)
    part.do_all()
    print_results(part)
    opt_propars = part._cache["propars"]
    print("Difference between total electrons:")
    print(grid.integrate(rho) - np.sum(opt_propars))


customized_slsqp_solver()

================================================================================
Information of integral grids.
--------------------------------------------------------------------------------
Grid size of molecular grid: 18460
************************************ Atom 0 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18
          18 18 18 18 18 18 26 26 26 26 38 38 38 38 38 50 50 50 50 50
          86 86 110 110 110 110 170 194 194 434 590 590 434 434 434 302 302 302 194 194
          170 110 110 110 110 110 110 110 110 110 110 110 86 50 50 18 18 18 18 18

************************************ Atom 1 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 18 18 18 18 18
          18 18 18 18 18 18 18 18 18 26 26 26 38 38 38 38 38 38 38 50
          50 50 50 50 50 86 86 86 110 110 110 170 170 170 194 194 194 194 170 170
          170 110 110 110 110 110 110 110 110 110 110 86 86 50 50 50 18 18 18 18

************************************ Atom 2 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 18 18 18 18 18
          18 18 18 18 18 18 18 18 18 26 26 26 38 38 38 38 38 38 38 50
          50 50 50 50 50 86 86 86 110 110 110 170 170 170 194 194 194 194 170 170
          170 110 110 110 110 110 110 110 110 110 110 86 86 50 50 50 18 18 18 18

--------------------------------------------------------------------------------
================================================================================

Performing a density-based AIM analysis with a wavefunction as input.
  Molecular grid    : MolGrid
  Using local grids : True
  Scheme                           : Gaussian Iterative Stockholder Analysis (GISA)
  Outer loop convergence threshold : 1.0e-06
  Inner loop convergence threshold : 1.0e-10
  Maximum iterations               : 1000
  lmax                             : 3
  Solver                           : <function opt_propars_qp_cvxopt at 0x1088948b0>
  Grid type                        : 1
Iteration       Change      Entropy
        1   2.11603e-01   2.11396e-01
        2   2.49842e-02   1.12501e-01
        3   1.32921e-02   8.48478e-02
        4   8.57237e-03   6.89801e-02
        5   5.91785e-03   5.94610e-02
        6   4.24425e-03   5.35056e-02
        7   3.12533e-03   4.96581e-02
        8   2.34836e-03   4.71079e-02
        9   1.79441e-03   4.53802e-02
       10   1.37842e-03   4.41882e-02
       11   1.07512e-03   4.33343e-02
       12   8.48063e-04   4.27203e-02
       13   6.74378e-04   4.22717e-02
       14   5.39965e-04   4.19391e-02
       15   4.34960e-04   4.16889e-02
       16   3.52247e-04   4.14983e-02
       17   2.86606e-04   4.13515e-02
       18   2.34163e-04   4.12373e-02
       19   1.92009e-04   4.11475e-02
       20   1.57947e-04   4.10765e-02
       21   1.30281e-04   4.10198e-02
       22   1.07710e-04   4.09742e-02
       23   8.92219e-05   4.09375e-02
       24   7.40284e-05   4.09077e-02
       25   6.15061e-05   4.08834e-02
       26   5.11599e-05   4.08635e-02
       27   4.25939e-05   4.08472e-02
       28   3.54894e-05   4.08338e-02
       29   2.95885e-05   4.08227e-02
       30   2.46814e-05   4.08136e-02
       31   2.05967e-05   4.08060e-02
       32   1.71939e-05   4.07997e-02
       33   1.43571e-05   4.07945e-02
       34   1.19911e-05   4.07902e-02
       35   1.00168e-05   4.07866e-02
       36   8.36874e-06   4.07836e-02
       37   6.99267e-06   4.07811e-02
       38   5.84343e-06   4.07790e-02
       39   4.88343e-06   4.07772e-02
       40   4.08141e-06   4.07758e-02
       41   3.41127e-06   4.07746e-02
       42   2.85128e-06   4.07736e-02
       43   2.38330e-06   4.07727e-02
       44   1.99218e-06   4.07720e-02
       45   1.66528e-06   4.07714e-02
       46   1.39205e-06   4.07709e-02
       47   1.16366e-06   4.07705e-02
       48   9.72756e-07   4.07702e-02

Computing atomic populations.
Computing atomic charges.
Computing density decomposition for atom 0
Computing density decomposition for atom 1
Computing density decomposition for atom 2
Computing cartesian and pure AIM multipoles and radial AIM moments.
Storing proatom density spline for atom 0.
Storing proatom density spline for atom 1.
Storing proatom density spline for atom 2.
charges:
[-0.863  0.432  0.432]
cartesian multipoles:
[[-0.863  0.     0.     0.112 -5.312 -0.    -0.    -4.718 -0.    -5.013 -0.    -0.    -0.04   0.    -0.     0.    -0.     0.322 -0.     0.153]
 [ 0.432  0.     0.023 -0.001 -0.309 -0.    -0.    -0.283  0.002 -0.288  0.     0.013  0.003  0.     0.     0.     0.04   0.009  0.015  0.02 ]
 [ 0.432  0.    -0.023 -0.001 -0.309  0.     0.    -0.283 -0.002 -0.288  0.    -0.013  0.003  0.    -0.     0.    -0.04   0.009 -0.015  0.02 ]]
Difference between total electrons:
1.9924890892752956e-05

Customized Least-Square Sovler

Users can easily apply customized solvers. The first step involves defining a customized solver, for example, customized_solver. The corresponding arguments and their meanings are as follows:

[12]:
def opt_propars_lstsq(bs_funcs, rho, propars, points, weights, alphas, threshold):
    r"""
    Optimize pro-atom parameters using quadratic-programming implemented in the `CVXOPT` package.

    .. math::

        N_{Ai} = \int \rho_A(r) \frac{\rho_{Ai}^0(r)}{\rho_A^0(r)} dr

        G = \frac{1}{2} c^T S c - c^T b

        S = 2 \int \zeta(\vec{r}) \zeta(\vec{r}) d\vec{r}
        = \frac{2}{\pi \sqrt{\pi}} \frac{(\alpha_k \alpha_l)^{3/2}}{(\alpha_k + \alpha_l)^{3/2}}

        b = \int \zeta(\vec{r}) \rho_a(\vec{r}) d\vec{r}

    Parameters
    ----------
    bs_funcs : 2D np.ndarray
        Basis functions array with shape (M, N), where 'M' is the number of basis functions
        and 'N' is the number of grid points.
    rho : 1D np.ndarray
        Spherically-averaged atomic density as a function of radial distance, with shape (N,).
    propars : 1D np.ndarray
        Pro-atom parameters with shape (M). 'M' is the number of basis functions.
    points : 1D np.ndarray
        Radial coordinates of grid points, with shape (N,).
    weights : 1D np.ndarray
        Weights for integration, including the angular part (4πr²), with shape (N,).
    threshold : float
        Convergence threshold for the iterative process.
    alphas : 1D np.ndarray
        The Gaussian exponential coefficients.
    threshold : float
        The convergence threshold of the optimization method.

    Returns
    -------
    1D np.ndarray
        Optimized proatom parameters.

    Raises
    ------
    RuntimeError
        If the inner iteration does not converge.

    """

    def _obj_func(x):
        pro = np.sum(x[:, None] * bs_funcs, axis=0)
        return np.sqrt(weights) * np.abs(pro - rho)

    result = least_squares(
        _obj_func, x0=np.zeros_like(propars), bounds=(0, np.inf), ftol=threshold
    )
    return result.x

Next, we need to pass this customized solver to our partitioning method.

[13]:
def customized_lstsq_solver():
    kwargs = prepare_argument_dict(mol, grid, rho)
    kwargs["solver"] = opt_propars_lstsq
    kwargs["inner_threshold"] = 1e-14
    part = GaussianISAWPart(**kwargs)
    part.do_all()

    print_results(part)
    opt_propars = part._cache["propars"]
    print("Difference between total electrons:")
    print(grid.integrate(rho) - np.sum(opt_propars))


customized_lstsq_solver()

================================================================================
Information of integral grids.
--------------------------------------------------------------------------------
Grid size of molecular grid: 18460
************************************ Atom 0 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18
          18 18 18 18 18 18 26 26 26 26 38 38 38 38 38 50 50 50 50 50
          86 86 110 110 110 110 170 194 194 434 590 590 434 434 434 302 302 302 194 194
          170 110 110 110 110 110 110 110 110 110 110 110 86 50 50 18 18 18 18 18

************************************ Atom 1 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 18 18 18 18 18
          18 18 18 18 18 18 18 18 18 26 26 26 38 38 38 38 38 38 38 50
          50 50 50 50 50 86 86 86 110 110 110 170 170 170 194 194 194 194 170 170
          170 110 110 110 110 110 110 110 110 110 110 86 86 50 50 50 18 18 18 18

************************************ Atom 2 ************************************
   |-- Radial grid size: 120
   |-- Angular grid sizes:
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
          6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 18 18 18 18 18
          18 18 18 18 18 18 18 18 18 26 26 26 38 38 38 38 38 38 38 50
          50 50 50 50 50 86 86 86 110 110 110 170 170 170 194 194 194 194 170 170
          170 110 110 110 110 110 110 110 110 110 110 86 86 50 50 50 18 18 18 18

--------------------------------------------------------------------------------
================================================================================

Performing a density-based AIM analysis with a wavefunction as input.
  Molecular grid    : MolGrid
  Using local grids : True
  Scheme                           : Gaussian Iterative Stockholder Analysis (GISA)
  Outer loop convergence threshold : 1.0e-06
  Inner loop convergence threshold : 1.0e-14
  Maximum iterations               : 1000
  lmax                             : 3
  Solver                           : <function opt_propars_lstsq at 0x108897be0>
  Grid type                        : 1
Iteration       Change      Entropy
        1   2.11298e-01   2.11396e-01
        2   2.46519e-02   1.80014e-01
        3   1.28132e-02   1.26698e-01
        4   8.42982e-03   9.80376e-02
        5   5.91102e-03   8.25970e-02
        6   4.27284e-03   7.36446e-02
        7   3.15178e-03   6.83471e-02
        8   2.36034e-03   6.52028e-02
        9   1.78897e-03   6.33447e-02
       10   1.36930e-03   6.22637e-02
       11   1.05671e-03   6.16543e-02
       12   8.21161e-04   6.13315e-02
       13   6.41884e-04   6.11797e-02
       14   5.04238e-04   6.11293e-02
       15   3.97855e-04   6.11379e-02
       16   3.15079e-04   6.11756e-02
       17   2.50290e-04   6.12273e-02
       18   1.99396e-04   6.12843e-02
       19   1.59204e-04   6.13391e-02
       20   1.27371e-04   6.13904e-02
       21   1.02074e-04   6.14360e-02
       22   8.18963e-05   6.14757e-02
       23   6.58049e-05   6.15106e-02
       24   5.29191e-05   6.15398e-02
       25   4.26226e-05   6.15643e-02
       26   3.42929e-05   6.15839e-02
       27   2.76582e-05   6.16019e-02
       28   2.23147e-05   6.16157e-02
       29   1.80136e-05   6.16268e-02
       30   1.44952e-05   6.16356e-02
       31   1.17148e-05   6.16445e-02
       32   9.45362e-06   6.16508e-02
       33   7.63823e-06   6.16559e-02
       34   6.19796e-06   6.16599e-02
       35   4.97943e-06   6.16625e-02
       36   4.02984e-06   6.16658e-02
       37   3.24430e-06   6.16681e-02
       38   2.63320e-06   6.16703e-02
       39   2.13223e-06   6.16714e-02
       40   1.70096e-06   6.16723e-02
       41   1.41018e-06   6.16739e-02
       42   1.11664e-06   6.16739e-02
       43   9.19287e-07   6.16750e-02

Computing atomic populations.
Computing atomic charges.
Computing density decomposition for atom 0
Computing density decomposition for atom 1
Computing density decomposition for atom 2
Computing cartesian and pure AIM multipoles and radial AIM moments.
Storing proatom density spline for atom 0.
Storing proatom density spline for atom 1.
Storing proatom density spline for atom 2.
charges:
[-0.829  0.415  0.415]
cartesian multipoles:
[[-0.829 -0.     0.     0.08  -5.229 -0.    -0.    -4.611 -0.    -4.916 -0.     0.    -0.116  0.    -0.    -0.     0.     0.229  0.    -0.061]
 [ 0.415  0.     0.028 -0.003 -0.351 -0.    -0.    -0.313  0.001 -0.322  0.     0.024 -0.005  0.     0.     0.     0.066  0.004  0.025  0.001]
 [ 0.415  0.    -0.028 -0.003 -0.351  0.     0.    -0.313 -0.001 -0.322  0.    -0.024 -0.005  0.    -0.     0.    -0.066  0.004 -0.025  0.001]]
Difference between total electrons:
0.018706938820722385

The result of the least squares method differs slightly from the quadratic programming method due to the constraints. We can observe that the deviation in the total electrons obtained by the least squares method is larger than that obtained by the quadratic programming method.