Skip to content

Commit

Permalink
Merge branch 'main' into ffsim
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinsung committed Jan 29, 2024
2 parents aed0437 + f6c2390 commit fde76cc
Show file tree
Hide file tree
Showing 28 changed files with 542 additions and 463 deletions.
34 changes: 13 additions & 21 deletions docs/tutorials/01_introduction_and_fermionic_circuits.ipynb

Large diffs are not rendered by default.

38 changes: 15 additions & 23 deletions docs/tutorials/02_spin_circuits.ipynb

Large diffs are not rendered by default.

26 changes: 13 additions & 13 deletions docs/tutorials/03_fermionic_tweezer_hardware.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"# give initial occupations separated by spin species\n",
"qc = backend.initialize_circuit([[1, 0, 0, 1], [0, 0, 1, 1]])\n",
"\n",
"qc.draw(output='mpl')"
"qc.draw(output='mpl', style='clifford')"
]
},
{
Expand Down Expand Up @@ -163,7 +163,7 @@
"# alternatively append the FH gate directly:\n",
"# qc.fhubbard(j=[0.5, 1., -1.], u=5., mu=[0., -1., 1., 0.], modes=all_modes)\n",
"\n",
"qc.draw(output='mpl')"
"qc.draw(output='mpl', style='clifford')"
]
},
{
Expand Down Expand Up @@ -220,7 +220,7 @@
"# qc.fint(2., all_modes)\n",
"# qc.fphase([1.], [2, 6])\n",
"\n",
"qc.draw(output= \"mpl\")"
"qc.draw(output= \"mpl\", style='clifford')"
]
},
{
Expand All @@ -245,23 +245,23 @@
"text": [
"hopping generator: \n",
" Fermionic Operator\n",
"register length=4, number terms=4\n",
"number spin orbitals=4, number terms=4\n",
" (-0.5+0j) * ( +_0 -_1 )\n",
"+ (0.5+0j) * ( -_0 +_1 )\n",
"+ (-0.5+0j) * ( +_2 -_3 )\n",
"+ (0.5+0j) * ( -_2 +_3 )\n",
"\n",
" interaction generator: \n",
" Fermionic Operator\n",
"register length=8, number terms=4\n",
"number spin orbitals=8, number terms=4\n",
" (2+0j) * ( +_0 -_0 +_4 -_4 )\n",
"+ (2+0j) * ( +_1 -_1 +_5 -_5 )\n",
"+ (2+0j) * ( +_2 -_2 +_6 -_6 )\n",
"+ (2+0j) * ( +_3 -_3 +_7 -_7 )\n",
"\n",
" phase generator: \n",
" Fermionic Operator\n",
"register length=2, number terms=2\n",
"number spin orbitals=2, number terms=2\n",
" (1+0j) * ( +_0 -_0 )\n",
"+ (1+0j) * ( +_1 -_1 )\n"
]
Expand Down Expand Up @@ -293,8 +293,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"counts: {'10100101': 19, '01101001': 21, '01100101': 55, '10101001': 5}\n",
"time taken: 0.05585169792175293\n"
"counts: {'10101001': 7, '01100101': 62, '01101001': 22, '10100101': 9}\n",
"time taken: 0.05115008354187012\n"
]
}
],
Expand Down Expand Up @@ -371,8 +371,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"counts: {'10100101': 18, '01101001': 18, '01100101': 57, '10101001': 7}\n",
"time taken: 0.03786921501159668\n",
"counts: {'10101001': 6, '01100101': 61, '01101001': 16, '10100101': 17}\n",
"time taken: 0.03921198844909668\n",
"basis: \n",
" 0. |0, 0, 1, 1>|0, 0, 1, 1>\n",
" 1. |0, 0, 1, 1>|0, 1, 0, 1>\n",
Expand Down Expand Up @@ -445,7 +445,7 @@
"\n",
"print(\"counts: \", backend.run(qc, num_species=2).result().get_counts())\n",
"\n",
"print(\"basis dimension :\", backend.get_basis(qc).dimension)\n"
"print(\"basis dimension :\", backend.get_basis(qc).dimension)"
]
},
{
Expand Down Expand Up @@ -624,7 +624,7 @@
{
"data": {
"text/html": [
"<h3>Version Information</h3><table><tr><th>Qiskit Software</th><th>Version</th></tr><tr><td><code>qiskit-terra</code></td><td>0.23.1</td></tr><tr><td><code>qiskit-aer</code></td><td>0.11.2</td></tr><tr><td><code>qiskit-nature</code></td><td>0.5.2</td></tr><tr><th>System information</th></tr><tr><td>Python version</td><td>3.9.16</td></tr><tr><td>Python compiler</td><td>MSC v.1916 64 bit (AMD64)</td></tr><tr><td>Python build</td><td>main, Jan 11 2023 16:16:36</td></tr><tr><td>OS</td><td>Windows</td></tr><tr><td>CPUs</td><td>8</td></tr><tr><td>Memory (Gb)</td><td>63.724937438964844</td></tr><tr><td colspan='2'>Wed Feb 22 16:58:34 2023 W. Europe Standard Time</td></tr></table>"
"<h3>Version Information</h3><table><tr><th>Software</th><th>Version</th></tr><tr><td><code>qiskit</code></td><td>0.45.1</td></tr><tr><td><code>qiskit_cold_atom</code></td><td>0.1.0</td></tr><tr><td><code>qiskit_algorithms</code></td><td>0.2.1</td></tr><tr><td><code>qiskit_aer</code></td><td>0.12.0</td></tr><tr><td><code>qiskit_nature</code></td><td>0.7.1</td></tr><tr><th colspan='2'>System information</th></tr><tr><td>Python version</td><td>3.9.16</td></tr><tr><td>Python compiler</td><td>MSC v.1916 64 bit (AMD64)</td></tr><tr><td>Python build</td><td>main, Jan 11 2023 16:16:36</td></tr><tr><td>OS</td><td>Windows</td></tr><tr><td>CPUs</td><td>8</td></tr><tr><td>Memory (Gb)</td><td>63.724937438964844</td></tr><tr><td colspan='2'>Tue Jan 09 11:26:13 2024 W. Europe Standard Time</td></tr></table>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
Expand All @@ -636,7 +636,7 @@
{
"data": {
"text/html": [
"<div style='width: 100%; background-color:#d5d9e0;padding-left: 10px; padding-bottom: 10px; padding-right: 10px; padding-top: 5px'><h3>This code is a part of Qiskit</h3><p>&copy; Copyright IBM 2017, 2023.</p><p>This code is licensed under the Apache License, Version 2.0. You may<br>obtain a copy of this license in the LICENSE.txt file in the root directory<br> of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.<p>Any modifications or derivative works of this code must retain this<br>copyright notice, and modified files need to carry a notice indicating<br>that they have been altered from the originals.</p></div>"
"<div style='width: 100%; background-color:#d5d9e0;padding-left: 10px; padding-bottom: 10px; padding-right: 10px; padding-top: 5px'><h3>This code is a part of Qiskit</h3><p>&copy; Copyright IBM 2017, 2024.</p><p>This code is licensed under the Apache License, Version 2.0. You may<br>obtain a copy of this license in the LICENSE.txt file in the root directory<br> of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.<p>Any modifications or derivative works of this code must retain this<br>copyright notice, and modified files need to carry a notice indicating<br>that they have been altered from the originals.</p></div>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
Expand Down
24 changes: 15 additions & 9 deletions docs/tutorials/04_collective_spin_hardware.ipynb

Large diffs are not rendered by default.

95 changes: 53 additions & 42 deletions docs/tutorials/06_time_evolution.ipynb

Large diffs are not rendered by default.

92 changes: 62 additions & 30 deletions docs/tutorials/07_squeezing_in_qudits.ipynb

Large diffs are not rendered by default.

76 changes: 52 additions & 24 deletions docs/tutorials/08_fermions_in_double_well.ipynb

Large diffs are not rendered by default.

163 changes: 98 additions & 65 deletions docs/tutorials/09_ryberg_dynamics.ipynb

Large diffs are not rendered by default.

35 changes: 17 additions & 18 deletions qiskit_cold_atom/applications/fermi_hubbard.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from typing import List

from qiskit import QuantumCircuit
from qiskit_nature.operators.second_quantization import FermionicOp
from qiskit_nature.second_q.operators import FermionicOp
from qiskit_cold_atom.fermions.fermion_circuit_solver import FermionicBasis
from qiskit_cold_atom.fermions.fermion_gate_library import FermiHubbard
from qiskit_cold_atom.exceptions import QiskitColdAtomError
Expand Down Expand Up @@ -110,33 +110,32 @@ def to_fermionic_op(self) -> FermionicOp:
A FermionicOp defining the systems Hamiltonian
"""

operator_labels = []
operator_labels = {}

# add hopping terms
for idx in range(self.size - 1):

right_to_left_up = "I" * idx + "+-" + "I" * (self.size * 2 - idx - 2)
operator_labels.append((right_to_left_up, -self.J))
left_to_right_up = "I" * idx + "-+" + "I" * (self.size * 2 - idx - 2)
operator_labels.append((left_to_right_up, self.J))
right_to_left_down = "I" * (self.size + idx) + "+-" + "I" * (self.size - idx - 2)
operator_labels.append((right_to_left_down, -self.J))
left_to_right_down = "I" * (self.size + idx) + "-+" + "I" * (self.size - idx - 2)
operator_labels.append((left_to_right_down, self.J))
right_to_left_up = f"+_{idx} -_{idx+1}"
operator_labels[right_to_left_up] = -self.J
left_to_right_up = f"-_{idx} +_{idx+1}"
operator_labels[left_to_right_up] = self.J
right_to_left_down = f"+_{self.size + idx} -_{self.size + idx+1}"
operator_labels[right_to_left_down] = -self.J
left_to_right_down = f"-_{self.size + idx} +_{self.size + idx+1}"
operator_labels[left_to_right_down] = self.J

# add interaction terms
for idx in range(self.size):
opstring = "I" * idx + "N" + "I" * (self.size - 1) + "N" + "I" * (self.size - 1 - idx)
operator_labels.append((opstring, self.U))
opstring = f"+_{idx} -_{idx} +_{self.size + idx} -_{self.size + idx}"
operator_labels[opstring] = self.U

# add potential terms
for idx in range(self.size):
op_up = "I" * idx + "N" + "I" * (2 * self.size - idx - 1)
operator_labels.append((op_up, self.mu[idx]))
op_down = "I" * (self.size + idx) + "N" + "I" * (self.size - idx - 1)
operator_labels.append((op_down, self.mu[idx]))
op_up = f"+_{idx} -_{idx}"
operator_labels[op_up] = self.mu[idx]
op_down = f"+_{self.size + idx} -_{self.size + idx}"
operator_labels[op_down] = self.mu[idx]

return FermionicOp(operator_labels)
return FermionicOp(operator_labels, num_spin_orbitals=2 * self.size)

def to_circuit(self, time: float = 1.0) -> QuantumCircuit:
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from typing import List, Union

from qiskit import QuantumCircuit
from qiskit_nature.operators.second_quantization import FermionicOp
from qiskit_nature.second_q.operators import FermionicOp

from qiskit_cold_atom.fermions.fermionic_state import FermionicState
from qiskit_cold_atom.fermions.fermionic_basis import FermionicBasis
Expand Down
61 changes: 20 additions & 41 deletions qiskit_cold_atom/applications/time_evolution_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,18 @@

from typing import List

from qiskit_nature.operators.second_quantization import FermionicOp
from qiskit_nature.mappers.second_quantization import (
from qiskit_nature.second_q.operators import FermionicOp
from qiskit_nature.second_q.mappers import (
JordanWignerMapper,
BravyiKitaevMapper,
ParityMapper,
)
from qiskit_nature.converters.second_quantization import QubitConverter

from qiskit import QuantumCircuit, QuantumRegister
from qiskit.opflow.evolutions import PauliTrotterEvolution
from qiskit import execute
from qiskit import QuantumRegister
from qiskit import QuantumCircuit
from qiskit.algorithms import TimeEvolutionProblem
from qiskit.algorithms.time_evolvers import TrotterQRTE
from qiskit.quantum_info import Statevector

from qiskit_cold_atom.applications.fermionic_evolution_problem import (
FermionicEvolutionProblem,
Expand Down Expand Up @@ -91,24 +92,11 @@ def solve(self, problem: FermionicEvolutionProblem) -> List[float]:
# use qubit pipeline
circuits = self.construct_qubit_circuits(problem)

# construct observable
mapper = self.MAPPER_DICT[self.map_type]
qubit_observable = mapper.map(problem.observable)
observable_mat = qubit_observable.to_spmatrix()

observable_evs = [0.0] * len(problem.evolution_times)

for idx, circuit in enumerate(circuits):

circuit.measure_all()

job = execute(circuit, self.backend, shots=self.shots)
counts = job.result().get_counts().int_outcomes()

for outcome_ind in counts:
prob = counts[outcome_ind] / self.shots

observable_evs[idx] += prob * observable_mat.diagonal()[outcome_ind].real
observable_evs = [
Statevector(qc).expectation_value(qubit_observable) for qc in circuits
]

return observable_evs

Expand All @@ -133,12 +121,12 @@ def construct_qubit_circuits(self, problem: FermionicEvolutionProblem) -> List[Q
circuits = []

# construct circuit of initial state:
label = ["+" if bit else "I" for bit in psi_0.occupations_flat]
bitstr_op = FermionicOp("".join(label))
qubit_op = QubitConverter(mapper).convert(bitstr_op)[0]
label = {f"+_{i}": 1.0 for i, bit in enumerate(psi_0.occupations_flat) if bit}
bitstr_op = FermionicOp(label, num_spin_orbitals=len(psi_0.occupations_flat))
qubit_op = mapper.map(bitstr_op)[0]
init_circ = QuantumCircuit(QuantumRegister(qubit_op.num_qubits, "q"))

for i, pauli_label in enumerate(qubit_op.primitive.paulis[0].to_label()[::-1]):
for i, pauli_label in enumerate(qubit_op.paulis.to_labels()[0][::-1]):
if pauli_label == "X":
init_circ.x(i)
elif pauli_label == "Y":
Expand All @@ -147,21 +135,12 @@ def construct_qubit_circuits(self, problem: FermionicEvolutionProblem) -> List[Q
init_circ.z(i)

for time in problem.evolution_times:

# time-step of zero will cause PauliTrotterEvolution to fail
if time == 0.0:
time += 1e-10

# map fermionic hamiltonian to qubits
qubit_hamiltonian = mapper.map(hamiltonian * time)
# get time evolution operator by exponentiating
exp_op = qubit_hamiltonian.exp_i()
# perform trotterization

evolved_op = PauliTrotterEvolution(reps=self.trotter_steps).convert(exp_op)

trotter_circ = evolved_op.to_circuit_op().to_circuit()

circuits.append(init_circ.compose(trotter_circ))
qubit_hamiltonian = mapper.map(hamiltonian)
# construct trotterization circuits
evolution_problem = TimeEvolutionProblem(qubit_hamiltonian, time, init_circ)
trotter_qrte = TrotterQRTE(num_timesteps=self.trotter_steps)
evolved_state = trotter_qrte.evolve(evolution_problem).evolved_state
circuits.append(evolved_state)

return circuits
22 changes: 11 additions & 11 deletions qiskit_cold_atom/base_circuit_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

from qiskit import QuantumCircuit
from qiskit.circuit import Gate
from qiskit_nature.operators.second_quantization import SecondQuantizedOp
from qiskit_nature.second_q.operators import SparseLabelOp
from qiskit_cold_atom.exceptions import QiskitColdAtomError


Expand Down Expand Up @@ -175,10 +175,10 @@ def __call__(self, circuit: QuantumCircuit) -> Dict[str, Any]:

return output

def to_operators(self, circuit: QuantumCircuit) -> List[SecondQuantizedOp]:
def to_operators(self, circuit: QuantumCircuit) -> List[SparseLabelOp]:
"""
Convert a circuit to a list of second quantized operators that describe the generators of the
gates applied to the circuit. The SecondQuantizedOps generating the gates are embedded in the
gates applied to the circuit. The SparseLabelOps generating the gates are embedded in the
larger space corresponding to the entire circuit.
Args:
Expand Down Expand Up @@ -221,9 +221,9 @@ def to_operators(self, circuit: QuantumCircuit) -> List[SecondQuantizedOp]:
f"Gate {inst[0].name} has no defined generator"
) from attribute_error

if not isinstance(second_quantized_op, SecondQuantizedOp):
if not isinstance(second_quantized_op, SparseLabelOp):
raise QiskitColdAtomError(
"Gate generator needs to be initialized as qiskit_nature SecondQuantizedOp"
"Gate generator needs to be initialized as qiskit_nature SparseLabelOp"
)
for idx in qargs:
if measured[idx]:
Expand Down Expand Up @@ -251,24 +251,24 @@ def get_initial_state(self, circuit: QuantumCircuit) -> csc_matrix:

@abstractmethod
def _embed_operator(
self, operator: SecondQuantizedOp, num_wires: int, qargs: List[int]
) -> SecondQuantizedOp:
self, operator: SparseLabelOp, num_wires: int, qargs: List[int]
) -> SparseLabelOp:
"""
Turning an operator that acts on the wires given in qargs into an operator
that acts on the entire state space of a circuit. The implementation of the subclasses
depends on whether the operators use sparse labels (SpinOp) or dense labels (FermionicOp).
Args:
operator: SecondQuantizedOp describing the generating Hamiltonian of a gate
operator: SparseLabelOp describing the generating Hamiltonian of a gate
num_wires: number of wires of the space in which to embed the operator
qargs: The wire indices the gate acts on
Returns: A SecondQuantizedOp acting on the entire quantum register of the Circuit
Returns: A SparseLabelOp acting on the entire quantum register of the Circuit
"""

@abstractmethod
def operator_to_mat(self, operator: SecondQuantizedOp) -> csc_matrix:
"""Turn a SecondQuantizedOp into a sparse matrix."""
def operator_to_mat(self, operator: SparseLabelOp) -> csc_matrix:
"""Turn a SparseLabelOp into a sparse matrix."""

@abstractmethod
def preprocess_circuit(self, circuit: QuantumCircuit):
Expand Down
1 change: 0 additions & 1 deletion qiskit_cold_atom/circuit_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ def validate_circuits(
config_dict = backend.configuration().to_dict()

for circuit in circuits:

try:
native_gates = {
gate.name: gate.coupling_map for gate in backend.configuration().gates
Expand Down
3 changes: 1 addition & 2 deletions qiskit_cold_atom/fermions/base_fermion_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

from qiskit.providers import BackendV1 as Backend
from qiskit import QuantumCircuit
from qiskit_nature.operators.second_quantization import FermionicOp
from qiskit_nature.second_q.operators import FermionicOp

from qiskit_cold_atom.fermions.fermion_gate_library import LoadFermions
from qiskit_cold_atom.fermions.fermion_circuit_solver import FermionCircuitSolver
Expand Down Expand Up @@ -129,7 +129,6 @@ def measure_observable_expectation(
observable_vars = [0] * len(circuits)

for idx, circuit in enumerate(circuits):

# check whether the observable is diagonal in the computational basis.
solver = FermionCircuitSolver(num_species=2)
solver.preprocess_circuit(circuit)
Expand Down
Loading

0 comments on commit fde76cc

Please sign in to comment.