Note
Run interactively in jupyter notebook.
Spin circuits¶
In addition to the fermionic setting introduced in the first tutorial, the Qiskit cold atom module supports a framework to describe cold atomic architectures based on high-dimensional spins.
Here, the unit of information, i.e. the individual wires in the quantum circuit, are given as quantum angular momenta or spins of length
Each individual spin has
The spin module comes with a SpinSimulator()
backend which serves as a general-purpose simulation backend analogous to the FermionSimulator
for fermionic circuits.
[1]:
from qiskit_cold_atom.spins import SpinSimulator
from qiskit import QuantumCircuit, QuantumRegister
backend = SpinSimulator()
qc = QuantumCircuit(4)
qc.measure_all()
# confirm that an empty circuit will return '0 0 0 0'
job = backend.run(qc, spin=3)
print(job.result().get_counts())
{'0 0 0 0': 1000}
Spin Gates¶
Quantum gates define the unitary operations that are carried out on the system. The gate unitary
As a formal language to describe Hamiltonians for spin systems, we utilize the SpinOp
from Qiskit Nature. Gates are then defined as instances or subclasses of the SpinGate
class which inherits from Qiskit’s original Gate
class.
In order for the SpinSimulator
to run a circuit, each gate of the circuit needs to have its generating Hamiltonian given as a SpinOp
.
As a first example, let’s look at the generalization of a
[2]:
from qiskit_nature.operators.second_quantization import SpinOp
from qiskit_cold_atom.spins import SpinGate
import numpy as np
Hx = np.pi*SpinOp("X") # generator of a rotation of angle pi around the x-axis
rx_spin = SpinGate(name="rx_spin", num_modes=1, generator=Hx)
For details on the syntax of how to define a SpinOp please see the SpinOp documentation.
[3]:
qc = QuantumCircuit(QuantumRegister(1, "spin"))
qc.append(rx_spin, [0])
qc.measure_all()
qc.draw(output='mpl')
[3]:

[4]:
job = backend.run(qc, spin=10)
print(job.result().get_counts())
{'20': 1000}
After applying the x-rotation, the spin has been flipped from pointing all the way “up” (0) to pointing all the way “down” to its largest value
Note that for the definition of the gate, the length of the spin
[5]:
job = backend.run(qc, spin=6)
print(job.result().get_counts())
{'12': 1000}
Example: Superposition¶
As is the case for qubit systems, superpositions in the initial states can be created by rotating around the x-axis with an angle SpinGateLibrary
, it is called the LXGate
(as
[6]:
from qiskit_cold_atom.spins import RLXGate
qc = QuantumCircuit(QuantumRegister(1, "spin"))
qc.append(RLXGate(np.pi/2), [0])
qc.measure_all()
qc.draw(output='mpl')
[6]:

[7]:
# The same circuit can also be built by the following shorthand notation
# which is added to QuantumCircuit upon importing from qiskit_cold_atom.spins
qc = QuantumCircuit(QuantumRegister(1, "spin"))
qc.rlx(np.pi/2, 0)
qc.measure_all()
qc.draw(output='mpl')
[7]:

[8]:
from qiskit.visualization import plot_histogram
job = backend.run(qc, spin=10, shots=1000, seed=123)
counts = job.result().get_counts()
# convert counts to integers for better formatting
plot_histogram({int(k):v for k,v in counts.items()})
[8]:

We see from the above result that a single rotation creates a superposition of all possible values, where the value “in the middle”
Example: Multi-spin gates¶
We can also use the language of generating Hamiltonians to create multi-spin gates. As an example, we can use a ZZ-type interaction to create an equal superposition between the different combinations of extremal spin orientations
[9]:
Hzz = np.pi*SpinOp("Z_0 Z_1", register_length=2) # generating Hamiltonian acting on two spins
zz_gate = SpinGate(name="zz_spin", num_modes=2, generator=Hzz)
qc = QuantumCircuit(QuantumRegister(2, "spin"))
qc.rlx(np.pi/2, [0, 1])
qc.append(zz_gate, [0, 1])
qc.rlx(np.pi/2, [0, 1])
qc.measure_all()
qc.draw(output='mpl')
[9]:

Note that we could have achieved the same result by using the
qc.rlzlz(np.pi, [0, 1])
instruction.
[10]:
job = backend.run(qc, spin=1, shots=1000, seed=1234)
print("counts: ", job.result().get_counts())
plot_histogram(job.result().get_counts())
counts: {'2 2': 267, '2 0': 231, '0 2': 253, '0 0': 249}
[10]:

The measured counts are returned in a space delimited format, where the first entry corresponds to the last spin in the register, etc. When executing circuits with spin-1/2, this recovers Qiskit’s ordering of bitstrings for qubits.
The spin simulator backend¶
The SpinSimulator
backend can also be used to access the statevector and the unitary of the circuit. Internally, the backend simulates the evolution of the circuit by exact diagonalization. The statevector and unitary of the system can be retrieved in the familiar way of result.get_statevector()
and result.get_unitary()
. If there is a final measurement in the circuit, the returned state and unitary of the circuit are those just prior to measurement.
Let’s demonstrate this on the above circuit:
[11]:
# access the statevector
print("\nstatevector :", job.result().get_statevector())
# accedd the unitary
print("\ncircuit unitary : \n", job.result().get_unitary())
statevector : [-5.00000000e-01-1.36845553e-48j -1.30736460e-32+1.11022302e-16j
5.00000000e-01-1.36845553e-48j -1.23259516e-32+1.34015774e-16j
-6.16297582e-33+6.12323400e-17j 0.00000000e+00+2.29934717e-17j
5.00000000e-01+3.42113883e-49j 4.35788200e-33+1.77904863e-17j
5.00000000e-01+3.42113883e-49j]
circuit unitary :
[[-5.00000000e-01-1.36845553e-48j -1.54074396e-32+1.66533454e-16j
5.00000000e-01+1.36845553e-48j -1.23259516e-32+1.25886354e-16j
1.38777878e-17+6.12323400e-17j 6.16297582e-33+7.03752031e-17j
5.00000000e-01+3.42113883e-49j 6.16297582e-33+3.74166420e-17j
5.00000000e-01-3.42113883e-49j]
[-1.30736460e-32+1.11022302e-16j 1.00000000e+00+0.00000000e+00j
1.30736460e-32+0.00000000e+00j -6.16297582e-33+6.12323400e-17j
1.23259516e-32-1.37383090e-16j -6.16297582e-33-6.12323400e-17j
4.35788200e-33+3.74166420e-17j -3.92523115e-17+0.00000000e+00j
-4.35788200e-33+3.74166420e-17j]
[ 5.00000000e-01-1.36845553e-48j 1.54074396e-32-5.55111512e-17j
-5.00000000e-01+1.36845553e-48j 0.00000000e+00+7.03752031e-17j
-1.38777878e-17-6.12323400e-17j 6.16297582e-33+1.25886354e-16j
5.00000000e-01+3.42113883e-49j -6.16297582e-33+3.74166420e-17j
5.00000000e-01-3.42113883e-49j]
[-1.23259516e-32+1.34015774e-16j 1.38777878e-17+6.12323400e-17j
6.16297582e-33+5.07490473e-17j 1.00000000e+00+6.84227766e-49j
9.24446373e-33+0.00000000e+00j 0.00000000e+00-6.84227766e-49j
1.54074396e-32-8.12941988e-18j -4.62223187e-33-6.12323400e-17j
-6.16297582e-33+4.73817313e-17j]
[-6.16297582e-33+6.12323400e-17j 6.16297582e-33-9.81307787e-17j
-6.16297582e-33-6.12323400e-17j 8.71576399e-33+0.00000000e+00j
-1.00000000e+00-4.35788200e-33j -8.71576399e-33+5.55111512e-17j
0.00000000e+00-6.12323400e-17j -6.16297582e-33+3.92523115e-17j
0.00000000e+00+6.12323400e-17j]
[ 0.00000000e+00+2.29934717e-17j -1.38777878e-17-6.12323400e-17j
6.16297582e-33+1.06260199e-16j 0.00000000e+00+6.84227766e-49j
-9.24446373e-33+0.00000000e+00j 1.00000000e+00-6.84227766e-49j
-3.08148791e-33+4.73817313e-17j -6.16297582e-33+6.12323400e-17j
-6.16297582e-33-8.12941988e-18j]
[ 5.00000000e-01+3.42113883e-49j 3.08148791e-33+3.74166420e-17j
5.00000000e-01-3.42113883e-49j 1.23259516e-32-8.12941988e-18j
-4.62223187e-33-6.12323400e-17j -6.16297582e-33+4.73817313e-17j
-5.00000000e-01+3.42113883e-49j 6.16297582e-33+1.11022302e-16j
5.00000000e-01-3.42113883e-49j]
[ 4.35788200e-33+1.77904863e-17j -3.92523115e-17+0.00000000e+00j
-4.35788200e-33+3.74166420e-17j 0.00000000e+00-6.12323400e-17j
-6.16297582e-33+3.92523115e-17j 0.00000000e+00+6.12323400e-17j
4.35788200e-33+1.11022302e-16j 1.00000000e+00+0.00000000e+00j
-4.35788200e-33+0.00000000e+00j]
[ 5.00000000e-01+3.42113883e-49j -3.08148791e-33+3.74166420e-17j
5.00000000e-01-3.42113883e-49j 0.00000000e+00+4.73817313e-17j
-6.16297582e-33+6.12323400e-17j -6.16297582e-33-8.12941988e-18j
5.00000000e-01+3.42113883e-49j -6.16297582e-33+0.00000000e+00j
-5.00000000e-01-3.42113883e-49j]]
For the above circuit with two spins of length
Further remarks¶
The SpinSimulator
is a general simulator backend in the sense that it accepts any SpinGate
with a well-defined generator. Similar to the qasm_simulator
for qubits, there are no coupling maps or further restrictions posed on the applicable gates.
In order to see how the spin setting introduced here can be used to describe a concrete experimental system of ultracold bosonic atoms with a gateset and coupling maps of a real device, check out the collective spins hardware tutorial.
[12]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
Version Information
Qiskit Software | Version |
---|---|
qiskit-terra | 0.23.1 |
qiskit-aer | 0.11.2 |
qiskit-nature | 0.5.2 |
System information | |
Python version | 3.9.16 |
Python compiler | MSC v.1916 64 bit (AMD64) |
Python build | main, Jan 11 2023 16:16:36 |
OS | Windows |
CPUs | 8 |
Memory (Gb) | 63.724937438964844 |
Wed Feb 22 16:57:25 2023 W. Europe Standard Time |
This code is a part of Qiskit
© Copyright IBM 2017, 2023.
This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.
[ ]: