Following internal instabilities in GHF#

Import gqcpy and numpy#

# Force the local gqcpy to be imported
import sys
sys.path.insert(0, '../../build/gqcpy/')

import gqcpy
import numpy as np
from scipy import linalg as la

Define a function to perform a GHF calculation#

def GHF_calculation(molecule, basis_set='STO-3G', guess=None):
    N = H3.numberOfElectrons()
    basis = gqcpy.GSpinorBasis_d(H3, basis_set)
    S = basis.quantize(gqcpy.OverlapOperator())

    fq_hamiltonian = gqcpy.FQMolecularHamiltonian(molecule)
    gsq_hamiltonian = basis.quantize(fq_hamiltonian)

    if guess == None:
        environment = gqcpy.GHFSCFEnvironment_d.WithCoreGuess(N, gsq_hamiltonian, S) 
    else:
        environment = gqcpy.GHFSCFEnvironment_d(N, gsq_hamiltonian, S, guess)

    solver = gqcpy.GHFSCFSolver_d.Plain(1.0e-08, 4000)
    qc_structure = gqcpy.GHF_d.optimize(solver, environment)
    
    nuc_rep = gqcpy.NuclearRepulsionOperator(molecule.nuclearFramework()).value()
    print(qc_structure.groundStateEnergy() + nuc_rep)
    
    return qc_structure

Create a molecule and run the initial GHF calculation#

H3 = gqcpy.Molecule.HRingFromDistance(3, 1.889, 0)
GHF_model = GHF_calculation(H3).parameters()
-1.3358471594598837

Run a stability check#

generalized_basis = gqcpy.GSpinorBasis_d(H3, 'STO-3G')
fq_hamiltonian = gqcpy.FQMolecularHamiltonian(H3)
gsq_hamiltonian_mo = generalized_basis.quantize(fq_hamiltonian).transformed(GHF_model.expansion())
generalized_stability_matrices = GHF_model.calculateStabilityMatrices(gsq_hamiltonian_mo)
generalized_stability_matrices.printStabilityDescription()
The real valued GHF wavefunction contains an internal instability.
The real valued GHF wavefunction contains a real->complex external instability.

Follow the internal instability#

def calculateNewGuess(GHF_stability_matrices, GHF_QC_Model):

    # The real GHF stability matrix consists of the sum of the A and B submatrices.
    Stability_matrix = GHF_stability_matrices.subMatrixA() + GHF_stability_matrices.subMatrixB()

    # diagonalize the stability matrix
    eigenvalues, eigenvectors = np.linalg.eigh(Stability_matrix)

    # Extract the lowest eigenvector
    lowest_eigenvector = eigenvectors[:, 0]

    # Create the rotation matrix that rotates the coefficients to the lowest eigenvector
    occupied_orbitals =  GHF_QC_Model.numberOfElectrons()
    virtual_orbitals = int(len(GHF_QC_Model.expansion().matrix()[0]) - occupied_orbitals)
    
    K = np.zeros(((occupied_orbitals + virtual_orbitals), (occupied_orbitals + virtual_orbitals)))
 
    lowest_eigenvector = lowest_eigenvector.reshape((occupied_orbitals, virtual_orbitals))

    K[occupied_orbitals:, :occupied_orbitals] = -1 * lowest_eigenvector.T.conjugate()
    K[:occupied_orbitals, occupied_orbitals:] = lowest_eigenvector

    rotated_coefficients = GHF_QC_Model.expansion().matrix() @ la.expm(-K)

    return gqcpy.GTransformation_d(rotated_coefficients)

Set up a new guess by rotating the old one to the lowest eigenvector of the Hessian#

new_guess = calculateNewGuess(generalized_stability_matrices, GHF_model)

Run a calculation with the new guess and check stability#

GHF_model_rotated = GHF_calculation(H3, guess=new_guess).parameters()
-1.3403026284286064
generalized_basis_rotated = gqcpy.GSpinorBasis_d(H3, 'STO-3G')
fq_hamiltonian_rotated = gqcpy.FQMolecularHamiltonian(H3)
gsq_hamiltonian_mo_rotated = generalized_basis.quantize(fq_hamiltonian_rotated).transformed(GHF_model_rotated.expansion())
generalized_stability_matrices_rotated = GHF_model_rotated.calculateStabilityMatrices(gsq_hamiltonian_mo_rotated)
generalized_stability_matrices_rotated.printStabilityDescription()
The real valued GHF wavefunction is internally stable.
The real valued GHF wavefunction is externally stable.