def project_orbitals_mgs(obasis0, obasis1, exp0, exp1, eps=1e-10): '''Project the orbitals in ``exp0`` (wrt ``obasis0``) on ``obasis1`` and store in ``exp1`` with the modified Gram-Schmidt algorithm. **Arguments:** obasis0 The orbital basis for the original wavefunction expansion. obasis1 The new orbital basis for the projected wavefunction expansion. exp0 The expansion of the original orbitals. exp1 (output) An output argument in which the projected orbitals will be stored. **Optional arguments:** eps A threshold for the renormalization in the Gram-Schmidt procedure The projection is based on the Modified Gram-Schmidt (MGS) process. In each iteration of the MGS, a renormalization is carried out. If the norm in this step is smaller than ``eps``, an error is raised. Note that ``exp1`` will be incomplete in several ways. The orbital energies are not copied. Only the occupied orbitals in ``exp0`` are projected. Coefficients of higher orbitals are set to zero. The orbital occupations are simply copied. This should be sufficient to construct an initial guess in a new orbital basis set based on a previous solution. If the number of orbitals in ``exp1`` is too small to store all projected orbitals, an error is raised. ''' # Compute the overlap matrix of the combined orbital basis obasis_both = GOBasis.concatenate(obasis0, obasis1) lf = DenseLinalgFactory(obasis_both.nbasis) olp_both = obasis_both.compute_overlap(lf) # Select the blocks of interest from the big overlap matrix olp_21 = olp_both._array[obasis0.nbasis:, :obasis0.nbasis] olp_22 = olp_both._array[obasis0.nbasis:, obasis0.nbasis:] # construct the projector projector = np.dot(np.linalg.pinv(olp_22), olp_21) # project occupied orbitals i1 = 0 for i0 in xrange(exp0.nfn): if exp0.occupations[i0] == 0.0: continue if i1 > exp1.nfn: raise ProjectionError('Not enough functions available in exp1 to store the projected orbitals.') exp1.coeffs[:,i1] = np.dot(projector, exp0.coeffs[:,i0]) exp1.occupations[i1] = exp0.occupations[i0] i1 += 1 # clear all parts of exp1 that were not touched by the projection loop ntrans = i1 del i1 exp1.coeffs[:,ntrans:] = 0.0 exp1.occupations[ntrans:] = 0.0 exp1.energies[:] = 0.0 # auxiliary function for the MGS algo def dot22(a, b): return np.dot(np.dot(a, olp_22), b) # Apply the MGS algorithm to orthogonalize the orbitals for i1 in xrange(ntrans): orb = exp1.coeffs[:,i1] # Subtract overlap with previous orbitals for j1 in xrange(i1): other = exp1.coeffs[:,j1] orb -= other*dot22(other, orb)/np.sqrt(dot22(orb, orb)) # Renormalize norm = np.sqrt(dot22(orb, orb)) if norm < eps: raise ProjectionError('The norm of a vector in the MGS algorithm becomes too small. Orbitals are redundant in new basis.') orb /= norm
def project_orbitals_mgs(obasis0, obasis1, orb0, orb1, eps=1e-10): '''Project the orbitals onto a new basis set with the modified Gram-Schmidt algorithm. The orbitals in ``orb0`` (w.r.t. ``obasis0``) are projected onto ``obasis1`` and stored in ``orb1``. Parameters ---------- obasis0 : GOBasis The orbital basis for the original wavefunction expansion. obasis1 : GOBasis The new orbital basis for the projected wavefunction expansion. orb0 : Orbitals The expansion of the original orbitals. orb1 : Orbitals An output argument in which the projected orbitals will be stored. eps : float A threshold for the renormalization in the Gram-Schmidt procedure Notes ----- The projection is based on the Modified Gram-Schmidt (MGS) process. In each iteration of the MGS, a renormalization is carried out. If the norm in this step is smaller than ``eps``, an error is raised. Note that ``orb1`` will be incomplete in several ways. The orbital energies are not copied. Only the occupied orbitals in ``orb0`` are projected. Coefficients of higher orbitals are set to zero. The orbital occupations are simply copied. This should be sufficient to construct an initial guess in a new orbital basis set based on a previous solution. If the number of orbitals in ``orb1`` is too small to store all projected orbitals, an error is raised. ''' # Compute the overlap matrix of the combined orbital basis obasis_both = GOBasis.concatenate(obasis0, obasis1) olp_both = obasis_both.compute_overlap() # Select the blocks of interest from the big overlap matrix olp_21 = olp_both[obasis0.nbasis:, :obasis0.nbasis] olp_22 = olp_both[obasis0.nbasis:, obasis0.nbasis:] # Construct the projector. This minimizes the L2 norm between the new and old # orbitals, which does not account for orthonormality. projector = np.dot(np.linalg.pinv(olp_22), olp_21) # Project occupied orbitals. i1 = 0 for i0 in xrange(orb0.nfn): if orb0.occupations[i0] == 0.0: continue if i1 > orb1.nfn: raise ProjectionError('Not enough functions available in orb1 to store the ' 'projected orbitals.') orb1.coeffs[:,i1] = np.dot(projector, orb0.coeffs[:,i0]) orb1.occupations[i1] = orb0.occupations[i0] i1 += 1 # clear all parts of orb1 that were not touched by the projection loop ntrans = i1 del i1 orb1.coeffs[:,ntrans:] = 0.0 orb1.occupations[ntrans:] = 0.0 orb1.energies[:] = 0.0 # auxiliary function for the MGS algo def dot22(a, b): return np.dot(np.dot(a, olp_22), b) # Apply the MGS algorithm to orthogonalize the orbitals for i1 in xrange(ntrans): orb = orb1.coeffs[:, i1] # Subtract overlap with previous orbitals for j1 in xrange(i1): other = orb1.coeffs[:, j1] orb -= other*dot22(other, orb)/np.sqrt(dot22(other, other)) # Renormalize norm = np.sqrt(dot22(orb, orb)) if norm < eps: raise ProjectionError('The norm of a vector in the MGS algorithm becomes too ' 'small. Orbitals are redundant in new basis.') orb /= norm
def project_orbitals_mgs(obasis0, obasis1, exp0, exp1, eps=1e-10): '''Project the orbitals onto a new basis set with the modified Gram-Schmidt algorithm. The orbitals in ``exp0`` (w.r.t. ``obasis0``) are projected onto ``obasis1`` and stored in ``exp1``. Parameters ---------- obasis0 : GOBasis The orbital basis for the original wavefunction expansion. obasis1 : GOBasis The new orbital basis for the projected wavefunction expansion. exp0 : DenseExpansion The expansion of the original orbitals. exp1 : DenseExpansion An output argument in which the projected orbitals will be stored. eps : float A threshold for the renormalization in the Gram-Schmidt procedure Notes ----- The projection is based on the Modified Gram-Schmidt (MGS) process. In each iteration of the MGS, a renormalization is carried out. If the norm in this step is smaller than ``eps``, an error is raised. Note that ``exp1`` will be incomplete in several ways. The orbital energies are not copied. Only the occupied orbitals in ``exp0`` are projected. Coefficients of higher orbitals are set to zero. The orbital occupations are simply copied. This should be sufficient to construct an initial guess in a new orbital basis set based on a previous solution. If the number of orbitals in ``exp1`` is too small to store all projected orbitals, an error is raised. ''' # Compute the overlap matrix of the combined orbital basis obasis_both = GOBasis.concatenate(obasis0, obasis1) lf = DenseLinalgFactory(obasis_both.nbasis) olp_both = obasis_both.compute_overlap(lf) # Select the blocks of interest from the big overlap matrix olp_21 = olp_both._array[obasis0.nbasis:, :obasis0.nbasis] olp_22 = olp_both._array[obasis0.nbasis:, obasis0.nbasis:] # Construct the projector. This minimizes the L2 norm between the new and old # orbitals, which does not account for orthonormality. projector = np.dot(np.linalg.pinv(olp_22), olp_21) # Project occupied orbitals. i1 = 0 for i0 in xrange(exp0.nfn): if exp0.occupations[i0] == 0.0: continue if i1 > exp1.nfn: raise ProjectionError( 'Not enough functions available in exp1 to store the ' 'projected orbitals.') exp1.coeffs[:, i1] = np.dot(projector, exp0.coeffs[:, i0]) exp1.occupations[i1] = exp0.occupations[i0] i1 += 1 # clear all parts of exp1 that were not touched by the projection loop ntrans = i1 del i1 exp1.coeffs[:, ntrans:] = 0.0 exp1.occupations[ntrans:] = 0.0 exp1.energies[:] = 0.0 # auxiliary function for the MGS algo def dot22(a, b): return np.dot(np.dot(a, olp_22), b) # Apply the MGS algorithm to orthogonalize the orbitals for i1 in xrange(ntrans): orb = exp1.coeffs[:, i1] # Subtract overlap with previous orbitals for j1 in xrange(i1): other = exp1.coeffs[:, j1] orb -= other * dot22(other, orb) / np.sqrt(dot22(other, other)) # Renormalize norm = np.sqrt(dot22(orb, orb)) if norm < eps: raise ProjectionError( 'The norm of a vector in the MGS algorithm becomes too ' 'small. Orbitals are redundant in new basis.') orb /= norm