diff --git a/.github/workflows/push_event_workflow.yml b/.github/workflows/push_event_workflow.yml index 5550066..667d96b 100644 --- a/.github/workflows/push_event_workflow.yml +++ b/.github/workflows/push_event_workflow.yml @@ -13,6 +13,6 @@ jobs: - name : Install Packages run : pip install -r requirements.txt - - name : Run tests hamiltonians - run : pytest tests/test.py + - name : Run tests hamiltonians models + run : pytest tests/model_test.py diff --git a/quantumsim/__init__.py b/quantumsim/__init__.py index c86a497..9b71379 100644 --- a/quantumsim/__init__.py +++ b/quantumsim/__init__.py @@ -1,8 +1,8 @@ from quantumsim.ansatz import * -from quantumsim.variational import * +from quantumsim.models import * from quantumsim.optimizers import * from quantumsim.lattice import * from quantumsim.beginstate import * -package_version = '1.0.0' \ No newline at end of file +package_version = '1.0.0' diff --git a/quantumsim/variational/vqe/__init__.py b/quantumsim/models/__init__.py similarity index 100% rename from quantumsim/variational/vqe/__init__.py rename to quantumsim/models/__init__.py diff --git a/quantumsim/variational/vqe/base.py b/quantumsim/models/base.py similarity index 56% rename from quantumsim/variational/vqe/base.py rename to quantumsim/models/base.py index baa31ac..bbd6f49 100644 --- a/quantumsim/variational/vqe/base.py +++ b/quantumsim/models/base.py @@ -1,22 +1,15 @@ import numpy as np """ -Clase base del VQE. +Clase base de los modelos """ -class vqe_base(): +class model_base(): """ Variables de la clase """ hamiltonian= None qubits = 0 - #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 - def energies_and_states(self): if self.hamiltonian is None: return [], [] @@ -27,4 +20,6 @@ def get_matrix(self): if self.hamiltonian is None: return None return self.hamiltonian.to_matrix() - + + def get_hamiltonian(self): + return self.hamiltonian diff --git a/quantumsim/variational/vqe/fermihubbard.py b/quantumsim/models/fermihubbard.py similarity index 94% rename from quantumsim/variational/vqe/fermihubbard.py rename to quantumsim/models/fermihubbard.py index 888f7cf..9c80f0c 100644 --- a/quantumsim/variational/vqe/fermihubbard.py +++ b/quantumsim/models/fermihubbard.py @@ -1,4 +1,4 @@ -from .base import vqe_base +from .base import model_base from qiskit_nature.second_q.mappers import JordanWignerMapper import numpy as np from qiskit_nature.second_q.operators import FermionicOp @@ -8,7 +8,7 @@ Clase para construir el hamiltoniano usado en el proceso de VQE, hereda metodos de la clase vqe_base """ -class fermihubbard_model(vqe_base): +class fermihubbard_model(model_base): """ Constructor de la clase diff --git a/quantumsim/models/molecular.py b/quantumsim/models/molecular.py new file mode 100644 index 0000000..78c10d2 --- /dev/null +++ b/quantumsim/models/molecular.py @@ -0,0 +1,60 @@ +from .base import model_base +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 + + +class molecule_model(model_base): + mapper = JordanWignerMapper() + params = {} + + """ + Constructor de la clase + params: diccionario con los parametros del hamiltoniano + """ + def __init__(self, params): + + self.params = params + + if "coordinates" not in params: + return + + coor = params['coordinates'] + atom_string = "" + for i,s in enumerate(params['symbols']): + atom_string += f"{s} {coor[3*i]} {coor[3*i + 1]} {coor[3*i +2]};" + + driver = PySCFDriver( + atom=atom_string, + unit=DistanceUnit.ANGSTROM, + basis=params['basis'], + charge=params['charge'], + spin=params['spin'] + ) + + problem = driver.run() + second_q_op, _ = problem.second_q_ops() + self.hamiltonian = self.mapper.map(second_q_op) + return + + + def set_hamiltonian(self, dist): + atom_string = "" + for i,s in enumerate( self.params['symbols'] ): + atom_string += f"{s} {dist[3*i]} {dist[3*i + 1]} {dist[3*i +2]};" + + driver = PySCFDriver( + atom=atom_string, + unit=DistanceUnit.ANGSTROM, + basis=self.params['basis'], + charge=self.params['charge'], + spin=self.params['spin'] + ) + + problem = driver.run() + second_q_op, _ = problem.second_q_ops() + self.hamiltonian = self.mapper.map( second_q_op ) + return self.hamiltonian + + diff --git a/quantumsim/variational/vqe/spin.py b/quantumsim/models/spin.py similarity index 96% rename from quantumsim/variational/vqe/spin.py rename to quantumsim/models/spin.py index 3e52c22..6d6f1b4 100644 --- a/quantumsim/variational/vqe/spin.py +++ b/quantumsim/models/spin.py @@ -1,11 +1,11 @@ -from .base import vqe_base +from .base import model_base from qiskit.quantum_info import SparsePauliOp """ Clase para construir el hamiltoniano usado en el proceso de VQE, hereda metodos de la clase vqe_base """ -class spin_model(vqe_base): +class spin_model(model_base): """ Constructor de la clase diff --git a/quantumsim/variational/__init__.py b/quantumsim/variational/__init__.py deleted file mode 100644 index 7868560..0000000 --- a/quantumsim/variational/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .struct import * -from .vqe import * -from .adapt import * \ No newline at end of file diff --git a/quantumsim/variational/adapt/__init__.py b/quantumsim/variational/adapt/__init__.py deleted file mode 100644 index 78f395d..0000000 --- a/quantumsim/variational/adapt/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .molecular import * -from .fermihubbard import * diff --git a/quantumsim/variational/adapt/base.py b/quantumsim/variational/adapt/base.py deleted file mode 100644 index 1850d1e..0000000 --- a/quantumsim/variational/adapt/base.py +++ /dev/null @@ -1,127 +0,0 @@ -import pennylane as qml -import jax as jax - - -""" -Clase base del proceso ADAPT-VQE, en esta se almacenan las variables y funciones generales, que son -independientes del sistema -""" -class adap_base(): - """ - Variables de la clase - """ - hamiltonian = None - qubits = 0 - base = "" - backend = "" - token = "" - device= None - node = None - begin_state = None - - """ - Funcion para setear caracteristicas del entorno de ejecucion del circuito - input: - params: diccionario de parametros para inicializar el entorno de ejecucion - del circuito - """ - def set_device(self, params) -> None: - self.base = params['base'] - - ##Maquinas reales - if self.base == 'qiskit.ibmq': - try: - self.backend = params['backend'] - self.token = params['token'] - self.device= qml.device(self.base, backend=self.backend, - wires=self.qubits, ibmqx_token= self.token) - except KeyError: - print( "Parametro no encontrado, recuerde agregar backend y token" ) - - ##Simuladores de qiskit - elif self.base == "qiskit.aer": - try: - self.backend = params['backend'] - self.device= qml.device(self.base, backend=self.backend, wires=self.qubits) - self.device= qml.device(self.base, backend=self.backend, wires=self.qubits) - except KeyError: - print( "Parametro no encontrado, recuerde agregar backend" ) - - ##Simuladores de pennylane - else: - self.device= qml.device(self.base, wires=self.qubits) - - - """ - Funcion para setear caracteristicas del ejecutor del circuito - input: - params: diccionario de parametros para inicializar el ejecutor del circuito - """ - def set_node(self, params) -> None: - if "pattern" in params: - self.pattern = params["pattern"] - - if params['repetitions']: - self.repetition = params['repetitions'] - - self.node = qml.QNode(self.circuit_state, self.device, interface=self.interface, diff_method=self.diff_method) - if self.interface == "jax" or self.interface == "jax-jit": - self.node = jax.jit(self.node) - - - """ - Funcion para setear el estado FH inicial del circuito - input: - electrons: numero de electrones del sistema - """ - def set_state(self, electrons): - self.begin_state = qml.qchem.hf_state(electrons=electrons, orbitals=self.qubits) - - - """ - Circuito pararametrizado para el proceso de ADAPT-VQE - input: - params: vector de parametros del circuito - excitations: lista de compuertas - params_select: lista de los parametros de las compuertas dobles seleccionadas - gates_select: lista de las compuertas dobles selecciondas - output: - valor: valor esperado del hamiltoniano - """ - def circuit(self, params, excitations, params_select=None, gates_select=None): - qml.BasisState(self.begin_state, wires=range(self.qubits)) - - if gates_select != None: - for i, gate in enumerate(gates_select): - if len(gate) == 4: - qml.DoubleExcitation(params_select[i], wires=gate) - elif len(gate) == 2: - qml.SingleExcitation(params_select[i], wires=gate) - - for i, gate in enumerate(excitations): - if len(gate) == 4: - qml.DoubleExcitation(params[i], wires=gate) - elif len(gate) == 2: - qml.SingleExcitation(params[i], wires=gate) - return qml.expval(self.hamiltonian) - - - """ - 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) - 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)]) ) \ No newline at end of file diff --git a/quantumsim/variational/adapt/fermihubbard.py b/quantumsim/variational/adapt/fermihubbard.py deleted file mode 100644 index 3c7644b..0000000 --- a/quantumsim/variational/adapt/fermihubbard.py +++ /dev/null @@ -1,84 +0,0 @@ -from .base import adap_base -import pennylane as qml -from quantumsim.lattice import lattice, custom_lattice -from pennylane import numpy as np -from pennylane import FermiC, FermiA - -""" -Clase del modelo de Fermi Hubbard, esta se encarga de construir el hamiltoniano, -hereda metodos de la clase adap_base -""" -class adap_fermihubbard(adap_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 - - 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 - - #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 - - #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 - - #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) diff --git a/quantumsim/variational/adapt/molecular.py b/quantumsim/variational/adapt/molecular.py deleted file mode 100644 index 28ed729..0000000 --- a/quantumsim/variational/adapt/molecular.py +++ /dev/null @@ -1,58 +0,0 @@ -from .base import adap_base -from pennylane import qchem - -""" -Clase del modelo de moleculas, esta se encarga de construir el hamiltoniano, -hereda metodos de la clase adap_base -""" -class adap_molecular(adap_base): - """ - Variables extras de la clase - """ - mapping= 'jordan_wigner' - charge= 0 - mult= 1 - basis='sto-3g' - method='dhf' - active_electrons = None - active_orbitals = None - - - """ - 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'] - - if 'mult' in params: - self.mult = params['mult'] - - if 'basis' in params: - self.basis = params['basis'] - - if 'method' in params: - self.method = params['method'] - - if 'active_electrons' in params: - self.active_electrons = params['active_electrons'] - - 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) diff --git a/quantumsim/variational/struct/__init__.py b/quantumsim/variational/struct/__init__.py deleted file mode 100644 index 359698b..0000000 --- a/quantumsim/variational/struct/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .struct import * diff --git a/quantumsim/variational/struct/struct.py b/quantumsim/variational/struct/struct.py deleted file mode 100644 index 10089ec..0000000 --- a/quantumsim/variational/struct/struct.py +++ /dev/null @@ -1,128 +0,0 @@ -from pennylane import qchem -from pennylane import numpy as np - -""" -Clase del proceso relajacion de estructuras moleculares. -""" -class structure_molecular(): - """ - Variables de la clase - """ - symbols = None - coordinates = None - begin_state = None - mapping= 'jordan_wigner' - charge= 0 - mult= 1 - basis='sto-3g' - method='dhf' - active_electrons = None - active_orbitals = None - node = None - interface = None - - - """ - 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): - self.symbols = symbols - self.coordinates = coordinates - - if 'mapping' in params: - self.mapping = params['mapping'] - - if 'charge' in params: - self.charge = params['charge'] - - if 'mult' in params: - self.mult = params['mult'] - - if 'basis' in params: - self.basis = params['basis'] - - if 'method' in params: - self.method = params['method'] - - if 'active_electrons' in params: - self.active_electrons = params['active_electrons'] - - if 'active_orbitals' in params: - self.active_orbitals = params['active_orbitals'] - - _, 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) - - self.begin_state = qml.qchem.hf_state(self.active_electrons, self.qubits) - - - """ - 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 que calcula el gradiente de la funcion de coste respecto a las posiciones de los elementos - input: - nodo: nodo de ejecucion - interface: interface en la cual se construyo el nodo - output: - grad: retorna el gradiente - """ - def grad_x(self, theta, x): - delta = 0.01 - n = len(x) - shift = np.eye( n ) * 0.5 * delta - grad = [ self.node( theta=theta, obs=((self.hamiltonian_x(x + shift[i]) - self.hamiltonian_x(x - shift[i])) / delta) ) for i in range(n)] - return np.array(grad) - - - """ - Funcion que calcula el hamiltoniano molecular considerando las caracteristicas antes ingresadas. - input: - x: vector con las posiciones de los elementos - output: - h: hamiltoniano molecular - """ - def hamiltonian_x(self, x): - h, _ = qchem.molecular_hamiltonian(symbols= self.symbols, - coordinates= x, - 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) - return h - - - """ - Funcion de coste. - input: - theta: vector de parametros del circuito - x: vector con las posiciones de los elementos - output: - result: evaluacion de la funcion de coste dado theta y x. - """ - def cost_function(self, theta, x): - hamiltonian = self.hamiltonian_x(x) - result = self.node( theta=theta, obs=hamiltonian ) - return result diff --git a/quantumsim/variational/vqe/molecular.py b/quantumsim/variational/vqe/molecular.py deleted file mode 100644 index b97c76d..0000000 --- a/quantumsim/variational/vqe/molecular.py +++ /dev/null @@ -1,37 +0,0 @@ -from .base import vqe_base -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 - - -class molecule_model(vqe_base): - """ - Constructor de la clase - params: diccionario con los parametros del hamiltoniano - """ - 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]};" - - driver = PySCFDriver( - atom=atom_string, - unit=DistanceUnit.ANGSTROM, - basis=params['basis'], - charge=params['charge'], - spin=params['spin'] - ) - - problem = driver.run() - second_q_op, _ = problem.second_q_ops() - mapper = JordanWignerMapper() - - self.hamiltonian = mapper.map(second_q_op) - - - diff --git a/tests/model_test.py b/tests/model_test.py index b754a94..cbb9f71 100644 --- a/tests/model_test.py +++ b/tests/model_test.py @@ -4,7 +4,7 @@ from qiskit_nature.second_q.hamiltonians import FermiHubbardModel from qiskit_nature.second_q.hamiltonians.lattices import LineLattice, BoundaryCondition import numpy as np -from quantumsim.variational.vqe import molecule_model, spin_model, fermihubbard_model +from quantumsim.models import molecule_model, spin_model, fermihubbard_model #Test the hamiltonian of the h2 molecule @@ -21,7 +21,7 @@ def test_molecule(): mapper = JordanWignerMapper() h = (mapper.map(a)).to_matrix() - hvqe = molecule_model({'symbols': ["H", "H"], 'coor':coord, + hvqe = molecule_model({'symbols': ["H", "H"], 'coordinates':coord, 'basis':'sto3g', 'spin':0, 'charge': 0}).hamiltonian.to_matrix() assert np.array_equal(h,hvqe)