diff --git a/src/tequila/circuit/gates.py b/src/tequila/circuit/gates.py index 2c63f66a0..d4a182324 100644 --- a/src/tequila/circuit/gates.py +++ b/src/tequila/circuit/gates.py @@ -504,6 +504,71 @@ def SWAP(first: int, second: int, control: typing.Union[int, list] = None, power else: return GeneralizedRotation(angle=power * np.pi, control=control, generator=generator, eigenvalues_magnitude=0.25) + + +def iSWAP(first: int, second: int, control: typing.Union[int, list] = None, power: float = 1.0, *args, + **kwargs) -> QCircuit: + """ + Notes + ---------- + iSWAP gate + .. math:: + iSWAP = e^{i\\frac{\\pi}{4} (X \\otimes X + Y \\otimes Y )} + + Parameters + ---------- + first: int + target qubit + second: int + target qubit + control + int or list of ints + power + numeric type (fixed exponent) or hashable type (parametrized exponent) + + Returns + ------- + QCircuit + + """ + + generator = paulis.from_string(f"X({first})X({second}) + Y({first})Y({second})") + + p0 = paulis.Projector("|00>") + paulis.Projector("|11>") + p0 = p0.map_qubits({0:first, 1:second}) + + gate = QubitExcitationImpl(angle=power*(-np.pi/2), target=generator.qubits, generator=generator, p0=p0, control=control, compile_options="vanilla", *args, **kwargs) + + return QCircuit.wrap_gate(gate) + + +def Givens(first: int, second: int, control: typing.Union[int, list] = None, angle: float = None, *args, + **kwargs) -> QCircuit: + """ + Notes + ---------- + Givens gate G + .. math:: + G = e^{-i\\theta \\frac{(Y \\otimes X - X \\otimes Y )}{2}} + + Parameters + ---------- + first: int + target qubit + second: int + target qubit + control + int or list of ints + angle + numeric type (fixed exponent) or hashable type (parametrized exponent), theta in the above formula + + Returns + ------- + QCircuit + + """ + + return QubitExcitation(target=[second,first], angle=2*angle, control=control, *args, **kwargs) # twice the angle since theta is not divided by two in the matrix exponential """ diff --git a/src/tequila/quantumchemistry/orbital_optimizer.py b/src/tequila/quantumchemistry/orbital_optimizer.py index 34b593ae2..c8c9877e3 100644 --- a/src/tequila/quantumchemistry/orbital_optimizer.py +++ b/src/tequila/quantumchemistry/orbital_optimizer.py @@ -37,7 +37,7 @@ def __call__(self, local_data, *args, **kwargs): self.iterations += 1 def optimize_orbitals(molecule, circuit=None, vqe_solver=None, pyscf_arguments=None, silent=False, - vqe_solver_arguments=None, initial_guess=None, return_mcscf=False, use_hcb=False, *args, **kwargs): + vqe_solver_arguments=None, initial_guess=None, return_mcscf=False, use_hcb=False, molecule_factory=None, *args, **kwargs): """ Parameters @@ -97,9 +97,9 @@ def optimize_orbitals(molecule, circuit=None, vqe_solver=None, pyscf_arguments=N if n_qubits > n_orbitals: warnings.warn("Potential inconsistency in orbital optimization: use_hcb is switched on but we have\n n_qubits={} in the circuit\n n_orbital={} in the molecule\n".format(n_qubits,n_orbitals), TequilaWarning) - wrapper = PySCFVQEWrapper(molecule_arguments=pyscf_molecule.parameters, n_electrons=pyscf_molecule.n_electrons, + wrapper = PySCFVQEWrapper(molecule_arguments={"parameters":pyscf_molecule.parameters, "transformation":molecule.transformation}, n_electrons=pyscf_molecule.n_electrons, const_part=c, circuit=circuit, vqe_solver_arguments=vqe_solver_arguments, silent=silent, - vqe_solver=vqe_solver, *args, **kwargs) + vqe_solver=vqe_solver, molecule_factory=molecule_factory, *args, **kwargs) mc.fcisolver = wrapper mc.internal_rotation = True if pyscf_arguments is not None: @@ -153,7 +153,7 @@ class PySCFVQEWrapper: # needs initialization n_electrons: int = None - molecule_arguments: ParametersQC = None + molecule_arguments: dict = None # internal data rdm1: numpy.ndarray = None @@ -168,6 +168,7 @@ class PySCFVQEWrapper: vqe_solver: typing.Callable = None circuit: QCircuit = None vqe_solver_arguments: dict = field(default_factory=dict) + molecule_factory: typing.Callable = None def reorder(self, M, ordering, to): # convenience since we need to reorder @@ -183,9 +184,14 @@ def kernel(self, h1, h2, *args, **kwargs): restrict_to_hcb = self.vqe_solver_arguments is not None and "restrict_to_hcb" in self.vqe_solver_arguments and \ self.vqe_solver_arguments["restrict_to_hcb"] - molecule = QuantumChemistryBase(one_body_integrals=h1, two_body_integrals=h2of, + if self.molecule_factory is None: + molecule = QuantumChemistryBase(one_body_integrals=h1, two_body_integrals=h2of, nuclear_repulsion=self.const_part, n_electrons=self.n_electrons, - parameters=self.molecule_arguments) + **self.molecule_arguments) + else: + molecule = self.molecule_factory(one_body_integrals=h1, two_body_integrals=h2of, + nuclear_repulsion=self.const_part, n_electrons=self.n_electrons, + **self.molecule_arguments) if restrict_to_hcb: H = molecule.make_hardcore_boson_hamiltonian() else: @@ -214,7 +220,7 @@ def kernel(self, h1, h2, *args, **kwargs): else: # static ansatz U = self.circuit - + rdm1, rdm2 = molecule.compute_rdms(U=U, variables=result.variables, spin_free=True, get_rdm1=True, get_rdm2=True, use_hcb=restrict_to_hcb) rdm2 = self.reorder(rdm2, 'dirac', 'mulliken') if not self.silent: diff --git a/src/tequila/quantumchemistry/pyscf_interface.py b/src/tequila/quantumchemistry/pyscf_interface.py index d72175400..1b60fd615 100644 --- a/src/tequila/quantumchemistry/pyscf_interface.py +++ b/src/tequila/quantumchemistry/pyscf_interface.py @@ -1,11 +1,9 @@ -from tequila import TequilaException, TequilaWarning, ExpectationValue, QCircuit, minimize -from openfermion import MolecularData +from tequila import TequilaException from tequila.quantumchemistry.qc_base import QuantumChemistryBase from tequila.quantumchemistry import ParametersQC, NBodyTensor -from dataclasses import dataclass, field import pyscf -import numpy, typing, warnings +import numpy, typing class OpenVQEEPySCFException(TequilaException): @@ -71,18 +69,6 @@ def __init__(self, parameters: ParametersQC, super().__init__(parameters=parameters, transformation=transformation, *args, **kwargs) - @classmethod - def from_tequila(cls, molecule, transformation=None, *args, **kwargs): - c, h1, h2 = molecule.get_integrals() - if transformation is None: - transformation = molecule.transformation - return cls(nuclear_repulsion=c, - one_body_integrals=h1, - two_body_integrals=h2, - n_electrons=molecule.n_electrons, - transformation=transformation, - parameters=molecule.parameters, *args, **kwargs) - def compute_fci(self, *args, **kwargs): from pyscf import fci c, h1, h2 = self.get_integrals(ordering="chem") diff --git a/src/tequila/quantumchemistry/qc_base.py b/src/tequila/quantumchemistry/qc_base.py index 385ced81a..0f0c1823c 100644 --- a/src/tequila/quantumchemistry/qc_base.py +++ b/src/tequila/quantumchemistry/qc_base.py @@ -106,6 +106,19 @@ def __init__(self, parameters: ParametersQC, self._rdm1 = None self._rdm2 = None + + @classmethod + def from_tequila(cls, molecule, transformation=None, *args, **kwargs): + c, h1, h2 = molecule.get_integrals() + if transformation is None: + transformation = molecule.transformation + return cls(nuclear_repulsion=c, + one_body_integrals=h1, + two_body_integrals=h2, + n_electrons=molecule.n_electrons, + transformation=transformation, + parameters=molecule.parameters, *args, **kwargs) + def supports_ucc(self): """ check if the current molecule supports UCC operations diff --git a/tests/test_circuits.py b/tests/test_circuits.py index aab25d220..05a9fb868 100644 --- a/tests/test_circuits.py +++ b/tests/test_circuits.py @@ -6,7 +6,7 @@ from tequila import assign_variable, paulis, TequilaWarning from tequila.circuit._gates_impl import RotationGateImpl from tequila.circuit.gates import CNOT, ExpPauli, H, Phase, QCircuit, RotationGate, Rx, Ry, Rz, S, \ - SWAP, T, Trotterized, u1, u2, u3, X, Y, Z + SWAP, iSWAP, Givens, T, Trotterized, u1, u2, u3, X, Y, Z from tequila.objective.objective import Variable from tequila.simulators.simulator_api import simulate from tequila.wavefunction.qubit_wavefunction import QubitWaveFunction @@ -332,6 +332,31 @@ def test_swap(): wfn = simulate(U) wfnx = simulate(X(2)+X(3)) assert numpy.isclose(numpy.abs(wfn.inner(wfnx))**2,1.0) + +def test_iswap(): + U = X(0) + U += iSWAP(0, 2, power=0.5) + wfn = simulate(U) + wfnx = simulate(X(2)) + assert numpy.isclose(numpy.abs(wfn.inner(wfnx))**2, 0.5) + + +def test_givens(): + U = X(0) + U += Givens(0, 1, angle=-numpy.pi/4) + wfn = simulate(U) + wfnx = simulate(X(0)) + assert numpy.isclose(numpy.abs(wfn.inner(wfnx))**2, 0.5) + wfnx = simulate(X(1)) + assert numpy.isclose(numpy.abs(wfn.inner(wfnx))**2, 0.5) + + U = X(0) + U += Givens(0, 1, angle=numpy.pi/4) + wfn = simulate(U) + wfnx0 = simulate(Phase([0, 1], angle=numpy.pi) + X(0)) + wfnx1 = simulate(X(1)) + assert numpy.isclose(wfn.inner(wfnx0), -wfn.inner(wfnx1)) + def test_variable_map(): U = Ry(angle="a", target=0) + Rx(angle="b", target=1) + Rz(angle="c", target=2) + H(angle="d", target=3) + ExpPauli(paulistring="X(0)Y(1)Z(2)", angle="e")