Skip to content

Commit

Permalink
Merge branch 'main' into dec2023rap
Browse files Browse the repository at this point in the history
  • Loading branch information
chmwzc committed Nov 12, 2024
2 parents fce3705 + a711c66 commit 0c290f9
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ h2.run_pyscf()
hamiltonian = h2.hamiltonian()
# Build a UCC circuit ansatz for running VQE
circuit = hf_circuit(h2.nso, sum(h2.nelec))
circuit = hf_circuit(h2.nso, h2.nelec)
circuit += ucc_circuit(h2.nso, [0, 1, 2, 3])
# Create and run the VQE, starting with random initial parameters
Expand Down
3 changes: 3 additions & 0 deletions doc/source/api-reference/ansatz.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ Unitary Coupled Cluster

.. autofunction:: qibochem.ansatz.ucc.ucc_ansatz

.. autofunction:: qibochem.ansatz.qeb.qeb_circuit


Basis rotation
--------------

Expand Down
39 changes: 39 additions & 0 deletions doc/source/tutorials/ansatz.rst
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,41 @@ An example of how to build a UCC doubles circuit ansatz for the :math:`H_2` mole
q3: ... ─────o─RX─RX─o────────────o─RX─
UCC with Qubit-Excitation-Based n-tuple Excitation
--------------------------------------------------

A CNOT depth-efficient quantum circuit for employing the UCC ansatz, dubbed the Qubit-Excitation-Based (QEB) n-tuple excitations for UCC, was constructed by Yordanov et al. [#f7]_ and Magoulas et al. [#f8]_, avoiding the exponential number of CNOT cascades in those developed before. [#f5]_ The quantum circuits generated for :math:`N` qubits have a reduction of CNOTs from :math:`(2N-1)2^{2N}` to :math:`2^{2N-1}+4N-2`.

An example for the :math:`H_2` molecule is given here:


.. code-block:: python
from qibochem.driver.molecule import Molecule
from qibochem.ansatz import hf_circuit, qeb_circuit
mol = Molecule([("H", (0.0, 0.0, 0.0)), ("H", (0.0, 0.0, 0.74804))])
mol.run_pyscf()
hamiltonian = mol.hamiltonian()
# Set parameters for the rest of the experiment
n_qubits = mol.nso
n_electrons = mol.nelec
# Build UCCD circuit
circuit = hf_circuit(n_qubits, n_electrons) # Start with HF circuit
circuit += qeb_circuit(n_qubits, [0, 1, 2, 3]) # Then add the double excitation circuit ansatz
print(circuit.draw())
.. code-block:: output
q0: ─X─X─────X─o──X─────X─
q1: ─X─o───X───o────X───o─
q2: ─────X─|─X─o──X─|─X───
q3: ─────o─o───RY───o─o───
..
_Basis rotation ansatz
Expand Down Expand Up @@ -233,3 +268,7 @@ The orthonormal molecular orbitals :math:`\phi` are optimized by a direct minimi
.. [#f6] Piela, L. (2007). 'Ideas of Quantum Chemistry'. Elsevier B. V., the Netherlands.
.. [#f7] Clements, W. R. et al., 'Optimal Design for Universal Multiport Interferometers', Optica 3 (2016) 1460.
.. [#f8] Yordanov Y. S. et al., 'Efficient Quantum Circuits for Quantum Computational Chemistry', Phys Rev A 102 (2020) 062612.
.. [#f9] Magoulas, I. and Evangelista, F. A., 'CNOT-Efficient Circuits for Arbitrary Rank Many-Body Fermionic and Qubit Excitations', J. Chem. Theory Comput. 19 (2023) 822.
1 change: 1 addition & 0 deletions src/qibochem/ansatz/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
)
from qibochem.ansatz.hardware_efficient import he_circuit
from qibochem.ansatz.hf_reference import hf_circuit
from qibochem.ansatz.qeb import qeb_circuit
from qibochem.ansatz.symmetry import symm_preserving_circuit
from qibochem.ansatz.ucc import ucc_ansatz, ucc_circuit
from qibochem.ansatz.util import generate_excitations, mp2_amplitude, sort_excitations
43 changes: 43 additions & 0 deletions src/qibochem/ansatz/qeb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from qibo import Circuit, gates


def qeb_circuit(n_qubits, excitation, theta=0.0) -> Circuit:
r"""
Qubit-excitation-based (QEB) circuit corresponding to the unitary coupled-cluster ansatz for a single excitation
Supports only Jordan-Wigner encoded circuits
Ref: arXiv:2210.05771
Args:
n_qubits: Number of qubits in the quantum circuit
excitation: Iterable of orbitals involved in the excitation; must have an even number of elements
E.g. ``[0, 1, 2, 3]`` represents the excitation of electrons in orbitals ``(0, 1)`` to ``(2, 3)``
theta: UCC parameter. Defaults to 0.0
trotter_steps: Number of Trotter steps; i.e. number of times this ansatz is applied with ``theta`` = ``theta / trotter_steps``. Default: 1
Returns:
Qibo ``Circuit``: Circuit corresponding to a single UCC excitation
"""

n_orbitals = len(excitation)
assert n_orbitals % 2 == 0, f"{excitation} must have an even number of items"

n_tuples = len(excitation) // 2
i_array = excitation[:n_tuples]
a_array = excitation[n_tuples:]
fwd_gates = [gates.CNOT(i_array[-1], _i) for _i in i_array[-2::-1]]
fwd_gates += [gates.CNOT(a_array[-1], _a) for _a in a_array[-2::-1]]
fwd_gates.append(gates.CNOT(a_array[-1], i_array[-1]))
fwd_gates += [gates.X(_ia) for _ia in excitation if _ia not in (i_array[-1], a_array[-1])]
circuit = Circuit(n_qubits)
circuit.add(gate for gate in fwd_gates)
# MCRY
# multi-controlled RY gate,
# negative controls i, a
# positive control on i_n
mcry_controls = excitation[:-1]
ry_angle = 2.0 * theta
circuit.add(gates.RY(a_array[-1], ry_angle).controlled_by(*mcry_controls))
circuit.add(gate for gate in fwd_gates[::-1])
return circuit
46 changes: 46 additions & 0 deletions tests/test_ucc.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from qibo.hamiltonians import SymbolicHamiltonian

from qibochem.ansatz import hf_circuit
from qibochem.ansatz.qeb import qeb_circuit
from qibochem.ansatz.ucc import expi_pauli, ucc_ansatz, ucc_circuit
from qibochem.ansatz.util import generate_excitations, mp2_amplitude, sort_excitations
from qibochem.driver import Molecule
Expand Down Expand Up @@ -95,6 +96,51 @@ def test_ucc_circuit(excitation, mapping, basis_rotations, coeffs):
assert len(test_circuit.get_parameters()) == len(basis_rotations)


@pytest.mark.parametrize(
"excitation,mapping,basis_rotations,coeffs",
[
([0, 2], None, ("Y0 X2", "X0 Y2"), (0.5, -0.5)), # JW singles
(
[0, 1, 2, 3],
None,
(
"X0 X1 Y2 X3",
"Y0 Y1 Y2 X3",
"Y0 X1 X2 X3",
"X0 Y1 X2 X3",
"Y0 X1 Y2 Y3",
"X0 Y1 Y2 Y3",
"X0 X1 X2 Y3",
"Y0 Y1 X2 Y3",
),
(-0.25, 0.25, 0.25, 0.25, -0.25, -0.25, -0.25, 0.25),
), # JW doubles
],
)
def test_qeb_circuit(excitation, mapping, basis_rotations, coeffs):
"""Build QEB circuit"""
theta = 0.1
n_qubits = 4

# Build the control array using SymbolicHamiltonian.circuit
# But need to multiply theta by some coefficient introduced by the fermion->qubit mapping
control_circuit = Circuit(n_qubits)
for coeff, basis_rotation in zip(coeffs, basis_rotations):
n_terms = len(basis_rotation)
pauli_term = SymbolicHamiltonian(
symbols.I(n_qubits - 1)
* reduce(lambda x, y: x * y, (getattr(symbols, _op)(int(qubit)) for _op, qubit in basis_rotation.split()))
)
control_circuit += pauli_term.circuit(-coeff * theta)
control_result = control_circuit(nshots=1)
control_state = control_result.state(True)

test_circuit = qeb_circuit(n_qubits, excitation, theta=theta)
test_result = test_circuit(nshots=1)
test_state = test_result.state(True)
assert np.allclose(control_state, test_state)


def test_ucc_ferm_qubit_map_error():
"""If unknown fermion to qubit map used"""
with pytest.raises(KeyError):
Expand Down

0 comments on commit 0c290f9

Please sign in to comment.