Example #1
0
def M_formula(power, tau = True):
    r"""
    Generate the formula for the conditional moments with second-order
    corrections based on the relation with the ordinary Bell polynomials

    .. math::

        M_n(x^{\prime},\tau) \sim (n!)\tau D_n(x^{\prime}) + \frac{(n!)\tau^2}{2}
        \sum_{m=1}^{n-1}   D_m(x^{\prime})  D_{n-m}(x^{\prime})

    Parameters
    ----------
    power: int
        Desired order of the formula.

    Returns
    -------
    term: sympy.symbols
        Expression up to given ``power``.
    """
    init_sym = symbols('D:'+str(int(power+1)))[1:]
    sym = ()
    for i in range(1,power + 1):
        sym += (factorial(i)*init_sym[i-1],)

    if tau == True:
        t = symbols('tau')

        term = t*bell(power, 1, sym) + t**2*bell(power, 2, sym)

    else:
        term = bell(power, 1, sym) + bell(power, 2, sym)

    return term
Example #2
0
def test_bell():
    assert [bell(n) for n in range(8)] == [1, 1, 2, 5, 15, 52, 203, 877]

    assert bell(0, x) == 1
    assert bell(1, x) == x
    assert bell(2, x) == x**2 + x
    assert bell(5, x) == x**5 + 10 * x**4 + 25 * x**3 + 15 * x**2 + x

    X = symbols('x:6')
    # X = (x0, x1, .. x5)
    # at the same time: X[1] = x1, X[2] = x2 for standard readablity.
    # but we must supply zero-based indexed object X[1:] = (x1, .. x5)

    assert bell(6, 2,
                X[1:]) == 6 * X[5] * X[1] + 15 * X[4] * X[2] + 10 * X[3]**2
    assert bell(
        6, 3,
        X[1:]) == 15 * X[4] * X[1]**2 + 60 * X[3] * X[2] * X[1] + 15 * X[2]**3

    X = (1, 10, 100, 1000, 10000)
    assert bell(6, 2, X) == (6 + 15 + 10) * 10000

    X = (1, 2, 3, 3, 5)
    assert bell(6, 2, X) == 6 * 5 + 15 * 3 * 2 + 10 * 3**2

    X = (1, 2, 3, 5)
    assert bell(6, 3, X) == 15 * 5 + 60 * 3 * 2 + 15 * 2**3
Example #3
0
def test_bell():
    assert [bell(n) for n in range(8)] == [1, 1, 2, 5, 15, 52, 203, 877]

    assert bell(0, x) == 1
    assert bell(1, x) == x
    assert bell(2, x) == x**2 + x
    assert bell(5, x) == x**5 + 10*x**4 + 25*x**3 + 15*x**2 + x

    X = symbols('x:6')
    # X = (x0, x1, .. x5)
    # at the same time: X[1] = x1, X[2] = x2 for standard readablity.
    # but we must supply zero-based indexed object X[1:] = (x1, .. x5)

    assert bell(6, 2, X[1:]) == 6*X[5]*X[1] + 15*X[4]*X[2] + 10*X[3]**2
    assert bell(
        6, 3, X[1:]) == 15*X[4]*X[1]**2 + 60*X[3]*X[2]*X[1] + 15*X[2]**3

    X = (1, 10, 100, 1000, 10000)
    assert bell(6, 2, X) == (6 + 15 + 10)*10000

    X = (1, 2, 3, 3, 5)
    assert bell(6, 2, X) == 6*5 + 15*3*2 + 10*3**2

    X = (1, 2, 3, 5)
    assert bell(6, 3, X) == 15*5 + 60*3*2 + 15*2**3
Example #4
0
def test_bell():
    assert [bell(n) for n in range(8)] == [1, 1, 2, 5, 15, 52, 203, 877]

    assert bell(0, x) == 1
    assert bell(1, x) == x
    assert bell(2, x) == x**2 + x
    assert bell(5, x) == x**5 + 10 * x**4 + 25 * x**3 + 15 * x**2 + x
Example #5
0
def test_bell():
    assert [bell(n) for n in range(8)] == [1, 1, 2, 5, 15, 52, 203, 877]

    assert bell(0, x) == 1
    assert bell(1, x) == x
    assert bell(2, x) == x**2 + x
    assert bell(5, x) == x**5 + 10*x**4 + 25*x**3 + 15*x**2 + x
Example #6
0
def F_formula(power):
    r"""
    Generate the formula for the conditional moments with second-order
    corrections based on the relation with the ordinary Bell polynomials

    .. math::

        D_n(x) &=  \frac{1}{\tau (n!)} \bigg[ \hat{B}_{n,1}
        \left(M_1(x,\tau),M_2(x,\tau),\ldots,M_{n}(x,\tau)\right) \\
        &\qquad  \left.-\frac{\tau}{2} \hat{B}_{n,2}
        \left(M_1(x,\tau),M_2(x,\tau),\ldots,M_{n-1}(x,\tau)\right)\right].

    Parameters
    ----------
    power: int
        Desired order of the formula.

    Returns
    -------
    term: sympy.symbols
        Expression up to given ``power``.
    """

    init_sym = symbols('D:'+str(int(power+1)))[1:]
    sym = ()
    for i in range(1,power + 1):
        sym += (factorial(i)*init_sym[i-1],)

    term = (symbols('M'+str(int(power))) - bell(power, 2, sym))/factorial(power)

    return term
def get_n_der_vecs(dk_f, gx, N):
    """
    This function applies Faa di Bruno's formula to compute the derivatives of order from 1 to N
    given the calling elementary operator has dk_f as its kth order derivative function.
    INPUTS
    =======
    dk_f(val, k): A lambda function of the kth order derivative of f at the point val
    gx: Potentially an AutoDiff object
    N: highest derivative order of gx
    RETURNS
    =======
    a list of high-order derivatives up until gx.N
    """
    # Create symbols and symbol-value mapping for eval() in the loop
    dxs = symbols('q:%d' % N)
    dx_mapping = {str(dxs[i]): gx.der[i] for i in range(N)}
    # Use Faa di Bruno's formula
    der_new = []
    for n in range(1, N + 1):
        nth_der = 0
        for k in range(1, n + 1):
            # The first n-k+1 derivatives
            t = n - k + 1
            vars = dxs[:t]
            # bell polynomial as python function str
            bell_nk_str = str(bell(n, k, vars))
            # evaluate the bell polynomial using the symbol-value mapping
            val_bell_nk = eval(bell_nk_str, dx_mapping)
            nth_der += dk_f(gx.val, k) * val_bell_nk
        der_new.append(nth_der)
    return der_new
Example #8
0
 def bellyn(self, nmax):
     Lbell = [[0] * nmax for i in range(nmax)]
     Lbell[0][0] = 0
     for n in range(1, nmax):
         for k in range(1, n + 1):
             Lbell[n][k] = bell(n, k, self.kminus)
     return Lbell
Example #9
0
 def kappac(self, n):
     fn = self.fn
     r = self.r
     z = self.z
     y = self.y
     W = self.W
     Q = self.Q
     S = self.S
     NBc = self.NBc
     NBbc = self.NBbc
     j = self.j
     L1 = [fn[0] * bell(n, 1, (r))]
     for k in range(2, n + 1):
         L1.extend([fn[k - 1] * bell(n, k, r)])
     L2 = Sum(Indexed('y', j), (j, 0, n - 1))
     L22 = lambdify(y, L2)
     L3 = L22(L1)
     L4 = L3.subs({W(z): Q(z) * S(z) - NBc * NBbc})
     L5 = L4.subs({S(z): NBc + NBbc, Q(z): z * z - NBc * NBbc})
     return L5
Example #10
0
def _derivative_transformation_matrix(deriv_func_list: list, point: float,
                                      order: int):
    r"""Compute transformation from one variable to another.

    Suppose there is a function :math:`f(x)` and a :math:`N`-th differentiable
    transformation :math:`g(x) = r` from variable :math:`x` to variable :math:`r`.
    This function returns a matrix that converts the derivatives
    :math:`[\frac{df}{dx}, \cdots, \frac{d^N f}{dx^N}]^T` by matrix multiplication
    to the derivatives of the new transformation
    :math:`[\frac{df}{dr}, \cdots, \frac{d^Nf}{dr^N}]^T`.

    It is a matrix with (i,j)-th entries:

    .. math::
        m_{i, j} = \begin{cases}
           B_{i,j}\bigg(\frac{dg}{dx}, \cdots, \frac{d^{k-j+1} g}{dx^{k-j+1}} \bigg) &
           i \leq j \\
           0 & \text{else}
        \end{cases}

    Parameters
    ----------
    deriv_func_list : list[K]
        List of size :math:`K` callable functions representing the derivatives of
        :math:`g(x)`, i.e. :math:`[\frac{dg}{dx}, \cdots, \frac{d^K g}{dx^K}]`.
    point : float
        The point in :math:`r`, in which the derivatives are being calculated at.
    order : int
        The number of derivatives from 1 to `order` that are going to be transformed.

    Returns
    -------
    ndarray((order, order))
        Returns the matrix that converts derivatives of one domain to another.

    """
    numb_derivs = len(deriv_func_list)
    if order > numb_derivs:
        raise ValueError("TODO")
    # Calculate derivatives of transformation evaluated at the point
    derivs_at_pt = np.array([dev(point) for dev in deriv_func_list],
                            dtype=np.float64)
    deriv_transf = np.zeros((order, order))
    for i in range(0, order):
        for j in range(0, i + 1):
            deriv_transf[i, j] = float(bell(i + 1, j + 1, derivs_at_pt))
    return deriv_transf
Example #11
0
def chain_rule(ad, new_val, der, der2, higher_der=None):
    """
    Applies chain rule to returns a new AD object with correct value and derivatives.

            Parameters:
                    ad (AD): An AD object
                    new_val (float): Value of the new AD object
                    der (float): Derivative of the outer function in chain rule

            Returns:
                    new_ad (AD): a new AD object with correct value and derivatives

            Example:
            >>> import AD as AD
            >>> x = AD.AD(2,order=5)
            >>> newad = 5*x**3+2
            >>> higherde = np.array([60,60,30,0,0])
            >>> chain_rule(x, 42, 60, 60, higher_der=higherde)
            AD(value: [42], derivatives: [60.])
    """
    new_der = der * ad.der
    new_der2 = der * ad.der2 + der2 * np.matmul(
        np.array([ad.der]).T, np.array([ad.der]))
    if ad.higher is None:
        new_ad = AD.AD(new_val, tag=ad.tag, der=new_der, der2=new_der2)
    else:
        new_higher_der = np.array([0.0] * len(ad.higher))
        new_higher_der[0] = new_der
        new_higher_der[1] = new_der2
        for i in range(2, len(ad.higher)):
            n = i + 1
            sum = 0
            for k in range(1, n + 1):
                sum += higher_der[k - 1] * sp.bell(n, k,
                                                   ad.higher[0:n - k + 1])
            new_higher_der[i] = sum
        new_ad = AD.AD(new_val,
                       tag=ad.tag,
                       der=new_der,
                       der2=new_der2,
                       order=len(ad.higher))
        new_ad.higher = new_higher_der

    return new_ad
Example #12
0
    def grand_potential_derivative(self, n_elec, order=1):
        r"""
        Evaluate the :math:`n^{\text{th}}`-order derivative of grand potential at the given n_elec.

        This returns the :math:`n^{\text{th}}`-order derivative of grand potential model w.r.t.
        to the chemical potential, at fixed external potential, evaluated for the specified
        number of electrons :math:`N_{\text{elec}}`.

        That is,

        .. math::
             \left. \left(\frac{\partial^n \Omega}{\partial \mu^n}
                    \right)_{v(\mathbf{r})} \right|_{N = N_{\text{elec}}} =
             \left. \left(\frac{\partial^{n-1}}{\partial \mu^{n-1}}
                          \frac{\partial \Omega}{\partial \mu}
                    \right)_{v(\mathbf{r})} \right|_{N = N_{\text{elec}}} =
           - \left. \left(\frac{\partial^{n-1} N}{\partial \mu^{n-1}}
                    \right)_{v(\mathbf{r})} \right|_{N = N_{\text{elec}}}
             \quad  n = 1, 2, \dots

        These derivatives can be computed using the derivative of energy model w.r.t. number of
        electrons, at fixed external potential, evaluated at :math:`N_{\text{elec}}`.
        More specifically,

        .. math::
             \left. \left(\frac{\partial \Omega}{\partial \mu}
                    \right)_{v(\mathbf{r})} \right|_{N = N_{\text{elec}}} &= - N_{\text{elec}} \\
             \left. \left(\frac{\partial^2 \Omega}{\partial \mu^2}
                    \right)_{v(\mathbf{r})} \right|_{N = N_{\text{elec}}} &= -\frac{1}{\eta^{(1)}}

        where :math:`\eta^{(n)}` denotes the :math:`(n+1)^{\text{th}}`-order derivative of energy
        w.r.t. number of electrons evaluated at :math:`N_{\text{elec}}`, i.e.

        .. math::
           \eta^{(n)} = \left. \left(\frac{\partial^{n+1} E}{\partial N^{n+1}}
                               \right)_{v(\mathbf{r})} \right|_{N = N_{\text{elec}}}
                        \qquad n = 1, 2, \dots

        To compute higher-order derivatives, Faa di Bruno formula which generalizes the chain rule
        to higher derivatives can be used. i.e. for :math:`n \geq 2`,

        .. math::
           \left. \left(\frac{\partial^n \Omega}{\partial \mu^n}
                  \right)_{v(\mathbf{r})} \right|_{N = N_{\text{elec}}} =
           \frac{-\displaystyle\sum_{k=1}^{n-2} \left.\left(\frac{\partial^k \Omega}{\partial \mu^k}
                                    \right)_{v(\mathbf{r})} \right|_{N = N_{\text{elec}}}
                 \cdot B_{n-1,k} \left(\eta^{(1)}, \eta^{(2)}, \dots, \eta^{(n-k)} \right)}
                {B_{n-1,n-1} \left(\eta^{(1)}\right)}

        where :math:`B_{n-1,k} \left(x_1, x_2, \dots, x_{n-k}\right)` denotes the Bell polynomials.

        Parameters
        ----------
        n_elec : float
            Number of electrons, :math:`N_{\text{elec}}`.
        order : int, default=1
            The order of derivative denoted by :math:`n` in the formula.
        """
        if n_elec is not None and n_elec < 0.0:
            raise ValueError(
                'Number of electrons cannot be negativ! #elec={0}'.format(
                    n_elec))
        if not (isinstance(order, int) and order > 0):
            raise ValueError(
                'Argument order should be an integer greater than or equal to 1.'
            )

        if n_elec is None:
            deriv = None
        elif order == 1:
            # 1st order derivative is minus number of electrons
            deriv = -n_elec
        elif order == 2:
            # 2nd order derivative is inverse hardness
            hardness = self.energy_derivative(n_elec, order=2)
            if hardness is not None and hardness != 0.0:
                deriv = -1.0 / hardness
            else:
                deriv = None
        else:
            # higher-order derivatives are compute with Faa Di Bruno formula
            # list of hyper-hardneses (derivatives of energy w.r.t. N)
            e_deriv = [
                self.energy_derivative(n_elec, i + 1)
                for i in xrange(1, order)
            ]
            g_deriv = [
                self.grand_potential_derivative(n_elec, k + 1)
                for k in xrange(1, order - 1)
            ]
            if any([item is None for item in e_deriv]) or any(
                [item is None for item in g_deriv]):
                deriv = None
            else:
                deriv = 0
                for k in xrange(1, order - 1):
                    deriv -= g_deriv[k - 1] * sp.bell(order - 1, k,
                                                      e_deriv[:order - k])
                deriv /= sp.bell(order - 1, order - 1, [e_deriv[0]])
        return deriv
Example #13
0
def count(smilesdata,
          output='lib_data.txt',
          timeout=5,
          symmetry=True,
          sample_size=8000):
    '''
    Count...?
    '''
    x = []
    compression = []
    #counter = 0
    line_count = 0
    #sample_size=int(sample_size)
    for file_lines in open(smilesdata).readlines():
        line_count += 1

    if line_count <= sample_size:
        sample_idx = np.arange(line_count)
    else:
        sample_idx = np.random.choice(line_count, sample_size, replace=False)
    f = open(output, "w")
    f.write(
        '#SMILES heavy_atoms bond_number bell_number naive_count starsbars symmetry_count MOG_nodes \n'
    )
    with open(smilesdata) as infile:
        for counter, line in enumerate(infile):
            if counter not in sample_idx:
                continue
            smiles = line.split()[1]
            #print(smiles)
            try:
                G = smiles2graph(smiles)

            except:
                continue
            mol = rdkit.Chem.MolFromSmiles(smiles)
            heavy_atoms = mol.GetNumHeavyAtoms()
            atoms = len(G)
            edges = G.number_of_edges()
            LG = chem_line_graph(G)
            bond_classes = equiv_classes(LG)
            atom_classes = equiv_classes(G,
                                         node_key='atom_type',
                                         edge_key='bond')
            bellnum = bell(atoms) - 1
            naive_count = (2**edges) - 1
            product = 1
            for i in range(len(bond_classes)):
                product *= (len(bond_classes[i]) + 1)
            stars_bars = product - 1
            symmetric_counts = (2**(len(bond_classes))) - 1
            #print(G.number_of_edges(),len(atom_classes))
            if (edges == 0) or (len(atom_classes) == 1):
                continue
            try:
                mog = MOG(smiles, symmetry, timeout)
                f.write(
                    '{smiles} {h_atoms} {bond_number} {bell} {naive} {starsbars} {symmetric} {mog_nodes} \n'
                    .format(smiles=smiles,
                            h_atoms=heavy_atoms,
                            bond_number=edges,
                            bell=bellnum,
                            naive=naive_count,
                            starsbars=stars_bars,
                            symmetric=symmetric_counts,
                            mog_nodes=len(mog.graph)))
            except MOG.TimeoutError:
                f.write(
                    '{smiles} {h_atoms} {bond_number} {bell} {naive} {starsbars} {symmetric} {mog_nodes} \n'
                    .format(smiles=smiles,
                            h_atoms=heavy_atoms,
                            bond_number=edges,
                            bell=bellnum,
                            naive=naive_count,
                            starsbars=stars_bars,
                            symmetric=symmetric_counts,
                            mog_nodes='NA'))
                continue

    f.close()
Example #14
0
def _transform_ode_from_derivs(coeffs: Union[list, np.ndarray],
                               deriv_transformation: list, x: np.ndarray):
    r"""
    Transform the coefficients of ODE from one variable to another evaluated on mesh points.

    Given a :math:`K`-th differentiable transformation function :math:`g(x)`
    and a linear ODE of :math:`K`-th order on independent variable :math:`x`

    .. math::
        \sum_{k=1}^{K} a_k(x) \frac{d^k y(x)}{d x^k}.

    This transforms it into a new coordinate system :math:`g(x) =: r`

    .. math::
        \sum_{j=1}^K b_j(r) \frac{d^j y(r)}{d r^j},

    where :math:`b_j(r) = \sum_{k=1}^K a_k(g^{-1}(r)) B_{k, j}({g^{-1}}^\prime(g^{-1}(r)),
    \cdots, {g^{-1}}^{k - j + 1}(g^{-1}(r)))`.

    Parameters
    ----------
    coeffs : list[callable or number] or ndarray
        Coefficients :math:`a_k` of each term :math:`\frac{d^k y(x}{d x^k}`
        ordered from 0 to K. Either a list of callable functions :math:`a_k(x)` that depends
        on :math:`x` or array of constants :math:`\{a_k\}_{k=0}^{K}`.
    deriv_transformation : list[callable]
        List of functions for compute transformation derivatives from 1 to :math:`K`.
    x : ndarray
        Points from the original domain that is to be transformed.

    Returns
    -------
    ndarray((K+1, N))
        Coefficients :math:`b_j(r)` of the new ODE with respect to transformed variable :math:`r`.

    """
    # `derivs` has shape (K, N), calculate d^j g/ dr^j
    derivs = np.array([dev(x) for dev in deriv_transformation],
                      dtype=np.float64)
    total = len(coeffs)  # Should be K+1, K is the order of the ODE
    # coeff_a_mtr has shape (K+1, N)
    coeff_a_mtr = _evaluate_coeffs_on_points(x, coeffs)  # a_k(x)
    coeff_b = np.zeros((total, x.size), dtype=float)
    # The first term doesn't contain a derivative so no transformation is required:
    coeff_b[0] += coeff_a_mtr[0]
    # construct 1 - 3 directly with vectorization (faster)
    if total > 1:
        coeff_b[1] += coeff_a_mtr[1] * derivs[0]
    if total > 2:
        coeff_b[1] += coeff_a_mtr[2] * derivs[1]
        coeff_b[2] += coeff_a_mtr[2] * derivs[0]**2
    if total > 3:
        coeff_b[1] += coeff_a_mtr[3] * derivs[2]
        coeff_b[2] += coeff_a_mtr[3] * 3 * derivs[0] * derivs[1]
        coeff_b[3] += coeff_a_mtr[3] * derivs[0]**3

    # construct 4th order and onwards without vectorization (slower)
    # formula is: d^k f / dr^k = \sum_{j=1}^{k} Bell_{k, j}(...)  (d^jf / dx^j)
    if total > 4:
        # Go Through each Pt
        for i_pt in range(len(x)):
            # Go through each order from 4 to K + 1
            for j in range(4, total):
                # Go through the sum to calculate Bell's polynomial
                for k in range(j, total):
                    all_derivs_at_pt = derivs[:, i_pt]
                    coeff_b[j, i_pt] += (float(bell(k, j, all_derivs_at_pt)) *
                                         coeff_a_mtr[k, i_pt])
    return coeff_b