OpenVQE Overall
A Openvqe extension to create cards. Cards can be shown as links or as plain text.
Usage
Options
Parameter | Description |
---|---|
molecule_symbol | molecule examples : H2 , H4 , H6 , LiH , H2O , CO , CO2 , NH3 etc … |
type_of_generator | user can apply type of generators , such as: uccsd , quccsd , uccgsd , k- upccgsd , etc … |
transform | user type Jordan wigner (JW), user can also type Bravyi - Kitaev or Parity basis. |
active | user can use AS (Active Space) method - True or non active space - False |
The user specifies these parameters in a class called MoleculeFactory. This class takes those parameters as input
1from openvqe . common_files . molecule_factory import MoleculeFactory
2# returns the properties of a molecule :
3r , geometry , charge , spin , basis = MoleculeFactory . get_parameters ( molecule_symbol = ’H2O ’)
define a function named as generate_hamiltonian that generates the electronic structure Hamiltonian (hamiltonian) and other properties such as the spin hamiltonian (for example hamiltonian_jw), number of electrons (n_els), the list contains the number of natural orbital occupation numbers (noons_full), the list of orbital energies (orb_energies_full) where the orbital energies are doubled due to spin degeneracy and info which is a dictionary that stores energies of some classical methods( such as Hartree-Fock, CCSD and FCI):
1Hamiltonian , hamiltonian_jw , n_els , noons_full , orb_energies_full , info = MoleculeFactory . generate_hamiltonian (
2molecule_symbol =’H2O ’, active = False , transform =’JW ’)
*Briefing about the geometry and energy level of H20 you can visualize the geometry of the molecule on the ORCA application
In addition to that, we define another function named as generate_cluster_ops() that takes as input the name of excitation generator user need (e.g., UCCSD, QUCCSD, UCCGSD, etc.) and internally it calls the file name generator_excitations.py which allows generate_cluster_ops() to return as output the size of pool excitations, fermionic operators, and JW transformed operators denoted in our code respectively as pool_size, cluster_ops and cluster_ops_jw:
1from . generator_excitations import ( uccsd , quccsd , singlet_gsd , singlet_sd , singlet_upccgsd , spin_complement_gsd ,
2spin_complement_gsd_twin )
3pool_size , cluster_ops , cluster_ops_jw = MoleculeFactory . generate_cluster_ops ( molecule_symbol =’H2O ’,
4type_of_generator =’ spin_complement_gsd ’, transform =’JW ’, active = False )
5# in our example ’ spin_complement_gsd ’:
6def generate_cluster_ops () :
7 pool_size , cluster_ops , cluster_ops_jw = None , None , None
8 if type_of_generator == ’ spin_complement_gsd ’:
9 pool_size , cluster_ops , cluster_ops_jw = spin_complement_gsd ( n_el , n_orb ,’JW ’)
10# elif for other excitations (uccsd , quccsd , singlet_upccgsd ...)
11# ::::
12 return pool_size , cluster_ops , cluster_ops_jw
Once these are generated, we import them as input to the UCC-family and ADAPT modules. In the example of fermionic-ADAPT sub-module, we call the function fermionic_adapt_vqe() that takes as parameters the fermionic cluster operators, spin Hamiltonian, maximum number of gradients to be taken per iteration, the type of optimizer, tolerance, threshold of norm () and the maximum number of adaptive iterations:
1from openvqe . adapt . fermionic_adapt_vqe import fermionic_adapt_vqe
2# choose maximum number of gradients needed (1 ,2 ,3....)
3n_max_grads = 1
4# choose optimizer needed ( COBYLA , BFGS , SLSQP , Nelder - Mead etc ...)
5optimizer = ’COBYLA ’
6tolerance = 10**( -6)
7# according to a given norm value we stop the ADAPT - VQE loop
8type_conver = ’norm ’
9threshold_needed = 1e -2
10# the maximum external number of iterations to complete the ADAPT - VQE under a given threshold_needed
11max_iterations = 35
12fci = info [’FCI ’]
13# sparse the Hamiltonian and cluster operators using myQLM - fermion tools obtained from MoleculeFactory , which are
14explicitly :
15hamiltonian_sparse = hamiltonian_jw . get_matrix ( sparse = True )
16cluster_ops_sparse = cluster_ops . get_matrix ( sparse = True )
17# reference_ket and hf_init_sp can be obtained from class MoleculeFactory ():
18reference_ket , hf_init_sp = MoleculeFactory . get_reference_ket ( hf_init , nbqbits , ’JW ’)
19# when all these parameters are satisfied , then fermionic -ADAPT - VQE function is:
20fermionic_adapt_vqe ( cluster_ops , hamiltonian_sparse , cluster_ops_sparse , reference_ket , h_sp , cluster_ops_jw ,
21hf_init_sp , n_max_grads , fci , optimizer , tolerance , type_conver = type_conver , threshold_needed =
22threshold_needed , max_external_iterations = max_iterations )
The function fermionic_adapt_vqe()
shows several steps (1) It prepares the trial state using prepare_state()
, (2) computes the commutator between the Hamiltonian and the fermionic operator with compute_gradient()
(or numerically via hamiltonian_jw | cluster_ops_sp
), (3) sorts the gradients in descending order while excluding zeros using sorted_gradient()
, and (4) checks if the norm meets a threshold to decide whether to exit or continue. If continuing, it optimizes the maximum gradient operator(s) using ucc_action()
before appending them to the trial state. The function returns the number of classical parameters, CNOT gates, other gates, optimized energies, and energy difference from FCI. The qubit-ADAPT sub-module is similar but differs in using qubit pool generators, a distinct trial state preparation, and a different gradient calculation method, while returning the same properties as fermionic_adapt_vqe()
.
Demo of the fermionic adapt VQE ( Method + Algorithms )
OpenVQE algorithms have targetted the following object
Step 1
Import the librabries from the main folder
1from openvqe.common_files.molecule_factory_with_sparse import MoleculeFactory
2from openvqe.adapt.fermionic_adapt_vqe import fermionic_adapt_vqe
3molecule_factory = MoleculeFactory()
Step 2
In this step we will run the non-active case with 8 qubits
1## non active case
2molecule_symbol = 'H2'
3type_of_generator = 'spin_complement_gsd'
4transform = 'JW'
5active = False
6r, geometry, charge, spin, basis = molecule_factory.get_parameters(molecule_symbol)
7print(" --------------------------------------------------------------------------")
8print("Running in the non active case: ")
9print(" molecule symbol: %s " %(molecule_symbol))
10print(" molecule basis: %s " %(basis))
11print(" type of generator: %s " %(type_of_generator))
12print(" transform: %s " %(transform))
13print(" --------------------------------------------------------------------------")
14
15print(" --------------------------------------------------------------------------")
16print(" ")
17print(" Generate Hamiltonians and Properties from :")
18print(" ")
19print(" --------------------------------------------------------------------------")
20print(" ")
21hamiltonian, hamiltonian_sparse, hamiltonian_sp, hamiltonian_sp_sparse, n_elec, noons_full, orb_energies_full, info = molecule_factory.generate_hamiltonian(molecule_symbol,active=active, transform=transform)
22nbqbits = len(orb_energies_full)
23print(n_elec)
24hf_init = molecule_factory.find_hf_init(hamiltonian, n_elec, noons_full, orb_energies_full)
25reference_ket, hf_init_sp = molecule_factory.get_reference_ket(hf_init, nbqbits, transform)
26print(" --------------------------------------------------------------------------")
27print(" ")
28print(" Generate Cluster OPS from :")
29print(" ")
30print(" --------------------------------------------------------------------------")
31print(" ")
32pool_size,cluster_ops, cluster_ops_sp, cluster_ops_sparse = molecule_factory.generate_cluster_ops(molecule_symbol, type_of_generator=type_of_generator, transform=transform, active=active)
33# for case of UCCSD from library
34# pool_size,cluster_ops, cluster_ops_sp, cluster_ops_sparse,theta_MP2, hf_init = molecule_factory.generate_cluster_ops(molecule_symbol, type_of_generator=type_of_generator,transform=transform, active=active)
35
36print('Pool size: ', pool_size)
37print('length of the cluster OP: ', len(cluster_ops))
38print('length of the cluster OPS: ', len(cluster_ops_sp))
39print(hf_init_sp)
40print(reference_ket)
41print(" --------------------------------------------------------------------------")
42print(" ")
43print(" Start adapt-VQE algorithm:")
44print(" ")
45print(" --------------------------------------------------------------------------")
46print(" ")
47
48
49n_max_grads = 1
50optimizer = 'COBYLA'
51tolerance = 10**(-6)
52type_conver = 'norm'
53threshold_needed = 1e-2
54max_external_iterations = 35
55fci = info['FCI']
56fermionic_adapt_vqe(hamiltonian_sparse, cluster_ops_sparse, reference_ket, hamiltonian_sp,
57 cluster_ops_sp, hf_init_sp, n_max_grads, fci,
58 optimizer,
59 tolerance,
60 type_conver = type_conver,
61 threshold_needed = threshold_needed,
62 max_external_iterations = max_external_iterations)
After the fifth iteration we obtain
1 --------------------------------------------------------------------------
2 Fermionic_ADAPT-VQE iteration: 5
3 --------------------------------------------------------------------------
4 Check gradient list chronological order
5 Norm of the gradients in current iteration = 0.00009243
6 Max gradient in current iteration= -0.00006448
7 Index of the Max gradient in current iteration= 29
8Convergence is done
9 -----------Final ansatz-----------
10 *final converged energy iteration is -1.151688545279
&
1({'energies': [-1.1327826008725905,
2 -1.1381935787336812,
3 -1.1446756115964445,
4 -1.1516131561999758,
5 -1.1516885452787875],
6 'energies_substracted_from_FCI': [0.018905946644018012,
7 0.01349496878292733,
8 0.007012935920164054,
9 7.539131663270027e-05,
10 2.2378210395856968e-09],
11 'norms': [1.1787990251924685,
12 0.8635450036709927,
13 0.6286051658725658,
14 0.5004792404827069,
15 0.044516747244794035],
16 'Max_gradients': [0.5465649202698061,
17 0.4109751817065878,
18 0.32276851883318675,
19 0.3507577344126926,
20 0.02644094985356347],
21 'fidelity': [0.9852300170572142,
22 0.9870545052389407,
23 0.9895621336258196,
24 0.9937025822193148,
25 0.9999286595965128],
26 'CNOTs': [48, 96, 288, 336, 368],
27 'Hadamard': [32, 64, 128, 160, 168],
28 'RY': [0, 4, 4, 4, 4],
29 'RX': [16, 32, 64, 80, 84]},
30 {'indices': [38, 32, 29, 23, 2],
31 'Number_operators': 5,
32 'final_norm': 9.243327155226241e-05,
33 'parameters': [-0.02140083663347611,
34 -0.025081299644836987,
35 -0.046035927664188785,
36 -0.03941898799451784,
37 -0.005705200709434039],
38 'Number_CNOT_gates': 368,
39 'Number_Hadamard_gates': 168,
40 'Number_RX_gates': 84,
41 'final_energy_last_iteration': -1.1516885452787875})
Step 3
In this step we consider the case of active space selection with the H4 molecule
1## active case
2from openvqe.common_files.molecule_factory_with_sparse import MoleculeFactory
3from openvqe.adapt.fermionic_adapt_vqe import fermionic_adapt_vqe
4
5# initializing the variables in the case of active
6molecule_symbol = 'H4'
7type_of_generator = 'spin_complement_gsd' #'spin_complement_gsd_twin'
8transform = 'JW'
9active = True
10
11
12r, geometry, charge, spin, basis = molecule_factory.get_parameters(molecule_symbol)
13print(" --------------------------------------------------------------------------")
14print("Running in the active case: ")
15print(" molecule symbol: %s " %(molecule_symbol))
16print(" molecule basis: %s " %(basis))
17print(" type of generator: %s " %(type_of_generator))
18print(" transform: %s " %(transform))
19print(" --------------------------------------------------------------------------")
20
21
22print(" --------------------------------------------------------------------------")
23print(" ")
24print(" Generate Hamiltonians and Properties from :")
25print(" ")
26print(" --------------------------------------------------------------------------")
27print(" ")
28hamiltonian_active, hamiltonian_active_sparse, hamiltonian_sp,hamiltonian_sp_sparse,nb_active_els,active_noons,active_orb_energies,info=molecule_factory.generate_hamiltonian(molecule_symbol,active=active,transform=transform)
29print(" --------------------------------------------------------------------------")
30print(" ")
31print(" Generate Cluster OPS from :")
32print(" ")
33print(" --------------------------------------------------------------------------")
34print(" ")
35nbqbits = hamiltonian_sp.nbqbits
36hf_init = molecule_factory.find_hf_init(hamiltonian_active, nb_active_els,active_noons, active_orb_energies)
37reference_ket, hf_init_sp = molecule_factory.get_reference_ket(hf_init, nbqbits, transform)
38pool_size,cluster_ops, cluster_ops_sp, cluster_ops_sparse = molecule_factory.generate_cluster_ops(molecule_symbol, type_of_generator=type_of_generator, transform=transform, active=active)
39print("Clusters were generated...")
40print('Pool size: ', pool_size)
41print('length of the cluster OP: ', len(cluster_ops))
42print('length of the cluster OPS: ', len(cluster_ops_sp))
43print('length of the cluster OPS_sparse: ', len(cluster_ops_sp))
44
45print(" --------------------------------------------------------------------------")
46print(" ")
47print(" Start adapt-VQE algorithm:")
48print(" ")
49print(" --------------------------------------------------------------------------")
50print(" ")
51
52n_max_grads = 1
53optimizer = 'COBYLA'
54tolerance = 10**(-7)
55type_conver = 'norm'
56threshold_needed = 1e-3
57max_external_iterations = 30
58fci = info['FCI']
59fermionic_adapt_vqe(hamiltonian_active_sparse, cluster_ops_sparse, reference_ket, hamiltonian_sp,
60 cluster_ops_sp, hf_init_sp, n_max_grads, fci,
61 optimizer,
62 tolerance,
63 type_conver = type_conver,
64 threshold_needed = threshold_needed,
65 max_external_iterations = max_external_iterations)
After the third iteration we obtain:
1--------------------------------------------------------------------------
2 Fermionic_ADAPT-VQE iteration: 3
3 --------------------------------------------------------------------------
4 Check gradient list chronological order
5 Norm of the gradients in current iteration = 0.00000157
6 Max gradient in current iteration= 0.00000098
7 Index of the Max gradient in current iteration= 22
8Convergence is done
9 -----------Final ansatz-----------
10 *final converged energy iteration is -2.150071872977
&
1({'energies': [-2.1475588531337, -2.149998032049138, -2.150071872976795],
2 'energies_substracted_from_FCI': [0.0307547797466996,
3 0.028315600831261722,
4 0.02824175990360489],
5 'norms': [0.9780904621524203, 0.44476756978359294, 0.04777693846151295],
6 'Max_gradients': [0.5600407258694875,
7 0.3073244780480784,
8 0.027972834656648148],
9 'fidelity': [0.9796754217306467, 0.9989670200933669, 0.9999401845822582],
10 'CNOTs': [48, 96, 128],
11 'Hadamard': [32, 64, 72],
12 'RY': [0, 4, 4],
13 'RX': [16, 32, 36]},
14 {'indices': [16, 22, 2],
15 'Number_operators': 3,
16 'final_norm': 1.56924035843115e-06,
17 'parameters': [-0.06970457006634999,
18 -0.015636706722331747,
19 -0.005282460997328426],
20 'Number_CNOT_gates': 128,
21 'Number_Hadamard_gates': 72,
22 'Number_RX_gates': 36,
23 'final_energy_last_iteration': -2.150071872976795})