Skip to content

Commit

Permalink
Refactor variational/vqe
Browse files Browse the repository at this point in the history
  • Loading branch information
javinoram committed Jul 27, 2024
1 parent 93bd6a1 commit c6e0b0a
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 289 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
venv
venv/
__pycache__
.DS_Store
notconsider
pruebas/
115 changes: 13 additions & 102 deletions quantumsim/variational/vqe/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import pennylane as qml
import itertools
from pennylane import numpy as np
import numpy as np

"""
Clase base del VQE.
Expand All @@ -10,110 +8,23 @@ class vqe_base():
Variables de la clase
"""
hamiltonian= None
coeff = None
qubits = 0
node = None
interface = None


"""
Funcion para setear el nodo de ejecucion del circuito
input:
nodo: nodo de ejecucion
interface: interface en la cual se construyo el nodo
"""
def set_node(self, node, interface) -> None:
self.node = node
self.interface = interface


"""
Funcion de coste.
input:
theta: vector de parametros del circuito
output:
result: evaluacion de la funcion de coste dado theta.
"""
def cost_function(self, theta) -> float:
result = self.node( theta=theta, obs=self.hamiltonian )
return result


"""
Funcion para obtener las proyeccion en la base computacional del vector de
estado del circuito.
input:
theta: vector de parametros del circuito
output:
result: lista con el valor de las proyecciones
s: lista con los estados de la base computacional
"""
def get_projections(self, theta):
combos = itertools.product([0, 1], repeat=self.qubits)
s = [list(c) for c in combos]
pro = [ qml.Projector(state, wires=range(self.qubits)) for state in s]
result = [self.node( theta=theta, obs=state) for state in pro]
return result, s


"""
Funcion para calcular el espin total del estado del circuito
input:
theta: vector de parametros del circuito
electrons: cantidad de electrones del sistema
output:
result: valor del espin total del estado del circuito
"""
def get_totalspinS(self, theta, electrons):
s_square = qml.qchem.spin2(electrons, self.qubits)
result = self.node( theta=theta, obs=s_square )
return result
#def cost_function(self, params, ansatz, estimator) -> float:
# if self.hamiltonian is None:
# return 0.0
# result = estimator.run(pubs=[ (ansatz, [self.hamiltonian], [params]) ]).result()
# energy = result[0].data.evs[0]
# return energy


"""
Funcion para calcular la proyeccion Sz del estado del circuito
input:
theta: vector de parametros del circuito
electrons: cantidad de electrones del sistema
output:
result: valor de la proyeccion Sz del estado del circuito
"""
def get_totalspinSz(self, theta):
s_z = qml.qchem.spinz(self.qubits)
result = self.node( theta=theta, obs=s_z )
return result


"""
Funcion para calcular el numero de particulas del estado del circuito
input:
theta: vector de parametros del circuito
output:
result: valor del numero de particulas del estado del circuito
"""
def get_particlenumber(self, theta):
n = qml.qchem.particle_number(self.qubits)
result = self.node( theta=theta, obs=n )
return result


"""
Funcion para calcular los valores y vectores propios del hamiltoniano
output:
ee: vectores propios del hamiltoniano ordenados de menor a mayor
vv: vectores propios del hamiltoniano, para acceder a ellos usar vv[:,i]
"""
def energies_and_states(self):
H = np.array( qml.matrix(self.hamiltonian, wire_order=[i for i in range(self.qubits)]) )
ee, vv = np.linalg.eigh(H)
if self.hamiltonian is None:
return [], []
ee, vv = np.linalg.eigh( self.get_matrix() )
return ee,vv


"""
Funcion para calcular la matriz asociada al hamiltoniano
output:
Arreglo de numpy con la representacion matricial del hamiltoniano
"""
def get_matrix(self):
return np.array( qml.matrix(self.hamiltonian, wire_order=[i for i in range(self.qubits)]) )
if self.hamiltonian is None:
return None
return self.hamiltonian.to_matrix()

88 changes: 22 additions & 66 deletions quantumsim/variational/vqe/fermihubbard.py
Original file line number Diff line number Diff line change
@@ -1,83 +1,39 @@
from .base import vqe_base
from quantumsim.lattice import lattice, custom_lattice
from pennylane import FermiC, FermiA
import pennylane as qml
from pennylane import numpy as np
from qiskit_nature.second_q.mappers import JordanWignerMapper
import numpy as np
from qiskit_nature.second_q.operators import FermionicOp


"""
Clase para construir el hamiltoniano usado en el proceso de VQE, hereda metodos de la clase
vqe_base
"""
class vqe_fermihubbard(vqe_base):
class fermihubbard_model(vqe_base):

"""
Constructor de la clase
params: diccionario con los parametros del hamiltoniano
lat: diccionario con los parametros de la lattice
"""
def __init__(self, params, lat):
self.qubits = params["sites"]*2
fermi_sentence = 0.0
def __init__(self, params):
self.qubits = int( 2*params['sites'] )
pair_indexes = params['indexs']
hopping = params['hopping']
onsite = params['onsite']

x,y = lat['size']
if lat['lattice'] != 'custom':
lattice_edge, lattice_node = lattice(lat)
else:
lattice_edge, lattice_node = custom_lattice(lat)

#Construir terminos asociados al termino -t
hopping = -params["hopping"]
fermi_hopping = 0.0
for pair in lattice_edge:
p1, p2 = pair
fermi_hopping += FermiC(2*(y*p1[0] + p1[1]))*FermiA(2*(y*p2[0] + p2[1])) + FermiC(2*(y*p2[0] + p2[1]))*FermiA(2*(y*p1[0] + p1[1]))
fermi_hopping += FermiC(2*(y*p1[0] + p1[1])+1)*FermiA(2*(y*p2[0] + p2[1])+1) + FermiC(2*(y*p2[0] + p2[1])+1)*FermiA(2*(y*p1[0] + p1[1])+1)

fermi_sentence = hopping*fermi_hopping
second_op_f = {}
for t,p in zip(hopping, pair_indexes):
# 0,1
second_op_f[f"+_{2*p[0]} -_{2*p[1]}"] = t
second_op_f[f"-_{2*p[0]} +_{2*p[1]}"] = -t

#Construir terminos asociados al potencial U
if 'U' in params:
u_potential = params["U"]
fermi_u = 0.0
for node in lattice_node:
p1, p2 = node
fermi_u += FermiC(y*p1 + 2*p2)*FermiA(y*p1 + 2*p2)*FermiC(y*p1 + 2*p2+1)*FermiA(y*p1 + 2*p2+1)
fermi_sentence += u_potential*fermi_u
second_op_f[f"+_{2*p[0] +1} -_{2*p[1] +1}"] = t
second_op_f[f"-_{2*p[0] +1} +_{2*p[1] +1}"] = -t

#Construir terminos asociados al campo electroico E
if 'E' in params:
e_field = params["E"]
fermi_e = 0.0
for node in lattice_node:
p1, p2 = node
fermi_e += FermiC(y*p1 + 2*p2)*FermiA(y*p1 + 2*p2)
fermi_e += FermiC(y*p1 + 2*p2+1)*FermiA(y*p1 + 2*p2+1)
fermi_sentence += e_field*fermi_e
for u,p in zip( onsite, [i for i in range(params['sites'])] ):
second_op_f[f"+_{2*p} -_{2*p} +_{2*p +1} -_{2*p +1}"] = u
op = FermionicOp( second_op_f, num_spin_orbitals=self.qubits )
mapper = JordanWignerMapper()
self.hamiltonian = mapper.map(op)

#Construir terminos asociados al potencial V
if 'V' in params:
v_potencial = params["V"]
fermi_v = 0.0
for pair in lattice_edge:
p1, p2 = pair
n_i = FermiC(2*(y*p1[0] + p1[1]))*FermiA(2*(y*p1[0] + p1[1])) + FermiC(2*(y*p1[0] + p1[1]) +1)*FermiA(2*(y*p1[0] + p1[1]) +1)
n_j = FermiC(2*(y*p2[0] + p2[1]))*FermiA(2*(y*p2[0] + p2[1])) + FermiC(2*(y*p2[0] + p2[1]) +1)*FermiA(2*(y*p2[0] + p2[1]) +1)
fermi_v += n_i*n_j
fermi_sentence += v_potencial*fermi_v

#Transformar los terminos de segunda cuantizacion a espines
coeff, terms = qml.jordan_wigner( fermi_sentence, ps=True ).hamiltonian().terms()

#Eliminar terminos cuyos coefficientes son 0
to_delete = [i for i,c in enumerate(coeff) if np.abs(c)<1e-10 ]
for i,_ in enumerate(coeff):
if isinstance(terms[i], qml.Identity):
terms[i] = qml.Identity(wires=[0])

to_delete = -np.sort(-np.array(to_delete))
for index in to_delete:
coeff.pop(index)
terms.pop(index)

#Almacenar hamiltoniano
self.hamiltonian = qml.Hamiltonian(np.real(np.array(coeff)), terms)
70 changes: 24 additions & 46 deletions quantumsim/variational/vqe/molecular.py
Original file line number Diff line number Diff line change
@@ -1,59 +1,37 @@
from .base import vqe_base
from pennylane import qchem
from qiskit_nature.units import DistanceUnit
from qiskit_nature.second_q.drivers import PySCFDriver
from qiskit_nature.second_q.mappers import JordanWignerMapper
import numpy as np

"""
Clase para construir el hamiltoniano usado en el proceso de VQE, hereda metodos de la clase
vqe_base
"""
class vqe_molecular(vqe_base):
"""
Variables de la clase
"""
mapping= 'jordan_wigner'
charge= 0
mult= 1
basis='sto-3g'
method='dhf'
active_electrons = None
active_orbitals = None

class molecule_model(vqe_base):
"""
Constructor de la clase
symbols: lista con los elementos de la molecula
coordinates: vector con las posiciones de los elementos en el espacio
params: diccionario con los parametros del hamiltoniano
"""
def __init__(self, symbols, coordinates, params= None):
if 'mapping' in params:
self.mapping = params['mapping']

if 'charge' in params:
self.charge = params['charge']
def __init__(self, params):

symbols = params['symbols']
coor = params['coor']

atom_string = ""
for i,s in enumerate(symbols):
atom_string += f"{s} {coor[3*i]} {coor[3*i + 1]} {coor[3*i +2]};"

if 'mult' in params:
self.mult = params['mult']
driver = PySCFDriver(
atom=atom_string,
unit=DistanceUnit.ANGSTROM,
basis=params['basis'],
charge=params['charge'],
spin=params['spin']
)

if 'basis' in params:
self.basis = params['basis']
problem = driver.run()
second_q_op, _ = problem.second_q_ops()
mapper = JordanWignerMapper()

if 'method' in params:
self.method = params['method']

if 'active_electrons' in params:
self.active_electrons = params['active_electrons']
self.hamiltonian = mapper.map(second_q_op)

if 'active_orbitals' in params:
self.active_orbitals = params['active_orbitals']


self.hamiltonian, self.qubits = qchem.molecular_hamiltonian(
symbols= symbols,
coordinates= coordinates,
mapping= self.mapping,
charge= self.charge,
mult= self.mult,
basis= self.basis,
method= self.method,
active_electrons=self.active_electrons,
active_orbitals=self.active_orbitals)

Loading

0 comments on commit c6e0b0a

Please sign in to comment.