Nota
Esta página foi gerada a partir do tutorials/noise/8_tomography.ipynb.
Execute interativamente no IBM Quantum lab.
Tomografia Quântica¶
Introdução¶
A tomografia quântica é um procedimento experimental para reconstruir uma descrição de parte de um sistema quântico a partir dos resultados de medição de um conjunto específico de experimentos. No Qiskit implementamos os seguintes tipos de tomografia:
Tomografia de estado quântico: Dado um circuito de preparação de estado que prepara um sistema em um estado, reconstrói uma descrição da matriz de densidade \(\rho\) do estado real obtido no sistema.
Quantum process tomography: Given a circuit, reconstruct a description of the quantum channel \(\mathcal{E}\) that describes the circuit’s operator when running on the system.
Quantum gate set tomography: Performs process tomography on a set of gates in a self-consistent manner, meaning quantum noises on gates used by the tomography process itself is also taken into account.
Este notebook dá exemplos de como usar os módulos ignis.verification.tomography
.
[1]:
# Needed for functions
import numpy as np
import time
from copy import deepcopy
# Import Qiskit classes
import qiskit
import qiskit.quantum_info as qi
from qiskit import QuantumRegister, QuantumCircuit, ClassicalRegister, Aer
from qiskit.providers.aer import noise
from qiskit.compiler import assemble
# Tomography functions
from qiskit.ignis.verification.tomography import state_tomography_circuits, StateTomographyFitter
from qiskit.ignis.verification.tomography import process_tomography_circuits, ProcessTomographyFitter
from qiskit.ignis.verification.tomography import gateset_tomography_circuits, GatesetTomographyFitter
import qiskit.ignis.mitigation.measurement as mc
# Auxiliary methods
from qiskit.quantum_info import Choi, Kraus
from qiskit.extensions import HGate, XGate
Exemplos iniciais¶
Exemplo de tomografia de estado de 2 Qubits¶
No exemplo abaixo queremos realizar a tomografia de estado em um estado de Bell de 2 Qubits entre os qubits 3 e 5. Para fazer o circuito de referência geramos o vetor de estado esperado utilizando statevector_simulator
entre os qubits 0 e 1.
[2]:
# Create the expected statevector
q2 = QuantumRegister(2)
bell = QuantumCircuit(q2)
bell.h(q2[0])
bell.cx(q2[0], q2[1])
print(bell)
target_state_bell = qi.Statevector.from_instruction(bell)
print(target_state_bell)
┌───┐
q0_0: ┤ H ├──■──
└───┘┌─┴─┐
q0_1: ─────┤ X ├
└───┘
Statevector([0.70710678+0.j, 0. +0.j, 0. +0.j,
0.70710678+0.j],
dims=(2, 2))
[3]:
# Create the actual circuit
q2 = QuantumRegister(6)
bell = QuantumCircuit(q2)
bell.h(q2[3])
bell.cx(q2[3], q2[5])
print(bell)
q1_0: ──────────
q1_1: ──────────
q1_2: ──────────
┌───┐
q1_3: ┤ H ├──■──
└───┘ │
q1_4: ───────┼──
┌─┴─┐
q1_5: ─────┤ X ├
└───┘
Aqui vamos gerar e executar os circuitos de tomografia de estado. Ao passar apenas os 2 registradores nos quais queremos medir, a tomografia de estado executará apenas naquele espaço de Hilbert \(2^2\) reduzido. No entanto, se passarmos o registrador inteiro o módulo de tomografia de estado tentará ajustar o espaço completo de \(2^6\).
[4]:
# Generate circuits and run on simulator
t = time.time()
# Generate the state tomography circuits.
qst_bell = state_tomography_circuits(bell, [q2[3], q2[5]])
# Execute
job = qiskit.execute(qst_bell, Aer.get_backend('qasm_simulator'), shots=5000)
print('Time taken:', time.time() - t)
# Fit result
tomo_fitter_bell = StateTomographyFitter(job.result(), qst_bell)
Time taken: 0.3742101192474365
O fitter retornará uma matriz de densidade ordenada de acordo com a forma como passamos os registradores a state_tomography_circuits
.
[5]:
# Perform the tomography fit
# which outputs a density matrix
rho_fit_bell = tomo_fitter_bell.fit(method='lstsq')
F_bell = qi.state_fidelity(rho_fit_bell, target_state_bell)
print('State Fidelity: F = {:.5f}'.format(F_bell))
State Fidelity: F = 0.99796
Repita o Exemplo com Ruído de Medição¶
[6]:
#Add measurement noise
noise_model = noise.NoiseModel()
for qubit in range(6):
read_err = noise.errors.readout_error.ReadoutError([[0.75, 0.25],[0.1,0.9]])
noise_model.add_readout_error(read_err,[qubit])
#generate the calibration circuits
meas_calibs, state_labels = mc.complete_meas_cal(qubit_list=[3,5])
backend = Aer.get_backend('qasm_simulator')
job_cal = qiskit.execute(meas_calibs, backend=backend, shots=15000, noise_model=noise_model)
job_tomo = qiskit.execute(qst_bell, backend=backend, shots=15000, noise_model=noise_model)
meas_fitter = mc.CompleteMeasFitter(job_cal.result(),state_labels)
tomo_bell = StateTomographyFitter(job_tomo.result(), qst_bell)
#no correction
rho_bell = tomo_bell.fit(method='lstsq')
F_bell = qi.state_fidelity(rho_bell, target_state_bell)
print('State fidelity (no correction): F = {:.5f}'.format(F_bell))
#correct data
correct_tomo_results = meas_fitter.filter.apply(job_tomo.result(), method='least_squares')
tomo_bell_mit = StateTomographyFitter(correct_tomo_results, qst_bell)
rho_fit_bell_mit = tomo_bell_mit.fit(method='lstsq')
F_bell_mit = qi.state_fidelity(rho_fit_bell_mit, target_state_bell)
print('State fidelity (w/ correction): F = {:.5f}'.format(F_bell_mit))
State fidelity (no correction): F = 0.56463
State fidelity (w/ correction): F = 0.98752
Exemplo de tomografia de processo de 1 qubit¶
[7]:
# Process tomography of a Hadamard gate
q = QuantumRegister(1)
circ = QuantumCircuit(q)
circ.h(q[0])
# Get the ideal unitary operator
target_unitary = qi.Operator(circ)
# Generate process tomography circuits and run on qasm simulator
qpt_circs = process_tomography_circuits(circ, q)
job = qiskit.execute(qpt_circs, Aer.get_backend('qasm_simulator'), shots=4000)
# Extract tomography data so that counts are indexed by measurement configuration
qpt_tomo = ProcessTomographyFitter(job.result(), qpt_circs)
qpt_tomo.data
[7]:
{(('Zp',), ('X',)): {'0': 4000},
(('Zp',), ('Y',)): {'0': 2013, '1': 1987},
(('Zp',), ('Z',)): {'0': 1973, '1': 2027},
(('Zm',), ('X',)): {'1': 4000},
(('Zm',), ('Y',)): {'0': 1938, '1': 2062},
(('Zm',), ('Z',)): {'0': 1979, '1': 2021},
(('Xp',), ('X',)): {'0': 1991, '1': 2009},
(('Xp',), ('Y',)): {'0': 1998, '1': 2002},
(('Xp',), ('Z',)): {'0': 4000},
(('Yp',), ('X',)): {'0': 1990, '1': 2010},
(('Yp',), ('Y',)): {'1': 4000},
(('Yp',), ('Z',)): {'0': 1997, '1': 2003}}
[8]:
# Tomographic reconstruction
t = time.time()
choi_fit_lstsq = qpt_tomo.fit(method='lstsq')
print('Fit time:', time.time() - t)
print('Average gate fidelity: F = {:.5f}'.format(qi.average_gate_fidelity(choi_fit_lstsq, target=target_unitary)))
Fit time: 0.008244037628173828
Average gate fidelity: F = 0.99722
Tomografia de processo de 1 qubit de uma porta swap de 2 qubits¶
Vamos preparar o qubit-0 e medir o qubit-1 de modo que o canal reconstruído deve ser uma identidade.
[9]:
# Process tomography of a Hadamard gate
q = QuantumRegister(2)
circ = QuantumCircuit(q)
circ.swap(q[0], q[1])
# Generate process tomography circuits and run on qasm simulator
# We use the optional prepared_qubits kwarg to specify that the prepared qubit was different to measured qubit
qpt_circs = process_tomography_circuits(circ, q[1], prepared_qubits=q[0])
job = qiskit.execute(qpt_circs, Aer.get_backend('qasm_simulator'), shots=2000)
# Extract tomography data so that counts are indexed by measurement configuration
qpt_tomo = ProcessTomographyFitter(job.result(), qpt_circs)
qpt_tomo.data
[9]:
{(('Zp',), ('X',)): {'0': 975, '1': 1025},
(('Zp',), ('Y',)): {'0': 1009, '1': 991},
(('Zp',), ('Z',)): {'0': 2000},
(('Zm',), ('X',)): {'0': 1046, '1': 954},
(('Zm',), ('Y',)): {'0': 989, '1': 1011},
(('Zm',), ('Z',)): {'1': 2000},
(('Xp',), ('X',)): {'0': 2000},
(('Xp',), ('Y',)): {'0': 1016, '1': 984},
(('Xp',), ('Z',)): {'0': 1003, '1': 997},
(('Yp',), ('X',)): {'0': 969, '1': 1031},
(('Yp',), ('Y',)): {'0': 2000},
(('Yp',), ('Z',)): {'0': 992, '1': 1008}}
[10]:
# Tomographic reconstruction
t = time.time()
choi_fit = qpt_tomo.fit(method='lstsq')
print('Fit time:', time.time() - t)
print('Average gate fidelity: F = {:.5f}'.format(qi.average_gate_fidelity(choi_fit)))
Fit time: 0.008944988250732422
Average gate fidelity: F = 0.99541
Exemplos Avançados¶
Gerando e ajustando estados aleatórios¶
Nós agora testamos as funções no estado gerado por um circuito composto por uma camada de unitárias aleatórias u3 de um único qubit.
[11]:
def random_u_tomo(nq, shots):
def rand_angles():
return tuple(2 * np.pi * np.random.random(3) - np.pi)
q = QuantumRegister(nq)
circ = QuantumCircuit(q)
for j in range(nq):
circ.u(*rand_angles(), q[j])
target_state = qi.Statevector.from_instruction(circ)
qst_circs = state_tomography_circuits(circ, q)
job = qiskit.execute(qst_circs, Aer.get_backend('qasm_simulator'),
shots=shots)
tomo_data = StateTomographyFitter(job.result(), qst_circs)
rho_fit = tomo_data.fit(method='lstsq')
print('F = {:.5f}'.format(qi.state_fidelity(rho_fit, target_state)))
[12]:
for j in range(5):
print('Random single-qubit unitaries: set {}'.format(j))
random_u_tomo(3, 5000)
Random single-qubit unitaries: set 0
F = 0.99913
Random single-qubit unitaries: set 1
F = 0.99580
Random single-qubit unitaries: set 2
F = 0.99601
Random single-qubit unitaries: set 3
F = 0.98930
Random single-qubit unitaries: set 4
F = 0.99547
Estado de Bell de 5 Qubits¶
[13]:
# Create a state preparation circuit
q5 = QuantumRegister(5)
bell5 = QuantumCircuit(q5)
bell5.h(q5[0])
for j in range(4):
bell5.cx(q5[j], q5[j + 1])
# Get ideal output state
target_state_bell5 = qi.Statevector.from_instruction(bell5)
# Generate circuits and run on simulator
t = time.time()
qst_bell5 = state_tomography_circuits(bell5, q5)
job = qiskit.execute(qst_bell5, Aer.get_backend('qasm_simulator'), shots=5000)
# Extract tomography data so that counts are indexed by measurement configuration
tomo_bell5 = StateTomographyFitter(job.result(), qst_bell5)
print('Time taken:', time.time() - t)
Time taken: 7.226781845092773
[14]:
t = time.time()
rho_fit_bell5 = tomo_bell5.fit(method='lstsq')
print('Time taken:', time.time() - t)
print('State fidelity: F = {:.5f}'.format(qi.state_fidelity(rho_fit_bell5, target_state_bell5)))
Time taken: 3.6470580101013184
State fidelity: F = 0.99430
Tomografia de Estado Condicional de 2 Qubits¶
Neste exemplo, temos um sistema de três qubit em que um dos qubits será um auxiliar para a realização da tomografia de estado, ou seja, apenas realiza a tomografia quando o terceiro qubit está no estado “1”. O circuito é configurado de tal forma que após a tomografia condicional obteremos um estado de Bell nos dois primeiros qubits.
Primeiro faça um estado GHZ de 3 qubits sem medições clássicas.
[15]:
# Create the actual circuit
q2 = QuantumRegister(3)
ghz = QuantumCircuit(q2)
ghz.h(q2[0])
ghz.cx(q2[0], q2[1])
ghz.cx(q2[1], q2[2])
ghz.h(q2[2])
print(ghz)
┌───┐
q11_0: ┤ H ├──■────────────
└───┘┌─┴─┐
q11_1: ─────┤ X ├──■───────
└───┘┌─┴─┐┌───┐
q11_2: ──────────┤ X ├┤ H ├
└───┘└───┘
Aqui vamos gerar e executar os circuitos de tomografia de estado. Só passe os registradores nos quais queremos realizar a tomografia de estado. O código gerará um novo registrador clássico apenas para essas medições.
[16]:
qst_ghz = state_tomography_circuits(ghz, [q2[0],q2[1]])
print(qst_ghz[0])
┌───┐ ░ ┌───┐┌─┐
q11_0: ┤ H ├──■─────────────░─┤ H ├┤M├───
└───┘┌─┴─┐ ░ ├───┤└╥┘┌─┐
q11_1: ─────┤ X ├──■────────░─┤ H ├─╫─┤M├
└───┘┌─┴─┐┌───┐ ░ └───┘ ║ └╥┘
q11_2: ──────────┤ X ├┤ H ├─░───────╫──╫─
└───┘└───┘ ░ ║ ║
c10: 2/═════════════════════════════╩══╩═
0 1
Agora faça uma cópia deste circuito (precisaremos dele para o fitter) e faça um novo circuito com uma medição auxiliar acoplada (é isso que será executado):
[17]:
#Make a copy without the ancilla register
qst_ghz_no_anc = deepcopy(qst_ghz)
ca = ClassicalRegister(1)
for qst_ghz_circ in qst_ghz:
qst_ghz_circ.add_register(ca)
qst_ghz_circ.measure(q2[2],ca[0])
[18]:
#Run in Aer
job = qiskit.execute(qst_ghz, Aer.get_backend('qasm_simulator'), shots=10000)
raw_results = job.result()
Antes de enviar os resultados para o fitter da tomografia de estado devemos retirar do registrador a medição do Q2 e apenas manter os resultados quando aquele registrador for 1.
[19]:
new_result = deepcopy(raw_results)
for resultidx, _ in enumerate(raw_results.results):
old_counts = raw_results.get_counts(resultidx)
new_counts = {}
#change the size of the classical register
new_result.results[resultidx].header.creg_sizes = [new_result.results[resultidx].header.creg_sizes[0]]
new_result.results[resultidx].header.clbit_labels = new_result.results[resultidx].header.clbit_labels[0:-1]
new_result.results[resultidx].header.memory_slots = 2
for reg_key in old_counts:
reg_bits = reg_key.split(' ')
if reg_bits[0]=='1':
new_counts[reg_bits[1]]=old_counts[reg_key]
new_result.results[resultidx].data.counts = new_counts
[20]:
tomo_bell = StateTomographyFitter(new_result, qst_ghz_no_anc)
# Perform the tomography fit
# which outputs a density matrix
rho_fit_bell = tomo_bell.fit(method='lstsq')
[21]:
np.around(rho_fit_bell, 3)
[21]:
array([[ 0.502+0.j , 0. +0.001j, 0.004+0.001j, -0.498-0.006j],
[ 0. -0.001j, 0.001+0.j , -0.001-0.j , -0.002+0.j ],
[ 0.004-0.001j, -0.001+0.j , 0.001+0.j , -0.003+0.001j],
[-0.498+0.006j, -0.002-0.j , -0.003-0.001j, 0.497+0.j ]])
Tomografia de conjunto de portas¶
Exemplo de tomografia de conjunto de portas de 1 qubit¶
A principal diferença entre a tomografia de conjunto de portas e a tomografia de processo é que na tomografia de conjunto de portas, a entrada consiste em uma base de conjunto de portas: um conjunto de portas que são usados tanto na fase de inicialização/medição da tomografia, quanto estão sendo reconstruídos.
O Qiskit fornece uma base de conjunto de portas padrão; a fim de utilizar esta base de conjuto de portas para reconstruir outra porta, esta porta deve ser adicionada à base. Usamos o seguinte método para simplificar o processo:
[22]:
from qiskit.ignis.verification.tomography.basis import default_gateset_basis
def collect_tomography_data(shots=10000,
noise_model=None,
gateset_basis='Standard GST'):
backend_qasm = Aer.get_backend('qasm_simulator')
circuits = gateset_tomography_circuits(gateset_basis=gateset_basis)
qobj = assemble(circuits, shots=shots)
result = backend_qasm.run(qobj, noise_model=noise_model).result()
fitter = GatesetTomographyFitter(result, circuits, gateset_basis)
return fitter
def gate_set_tomography(gate, noise_model=None):
basis = default_gateset_basis()
basis.add_gate(gate)
fitter = collect_tomography_data(shots=10000, noise_model=noise_model, gateset_basis=basis)
result_gates = fitter.fit()
result_gate = result_gates[gate.name]
return Choi(result_gate)
Tomografia de conjunto de portas de 1 qubit sem ruido¶
[23]:
target_unitary = qi.Operator(HGate())
t = time.time()
channel_fit = gate_set_tomography(HGate())
print('fit time:', time.time() - t)
print('Average gate fidelity: F = {:.5f}'.format(qi.average_gate_fidelity(channel_fit, target_unitary)))
fit time: 2.530395746231079
Average gate fidelity: F = 1.00084
[26]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
/Users/cjwood/anaconda3/envs/qiskit-legacy/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.
and should_run_async(code)
/Users/cjwood/anaconda3/envs/qiskit-legacy/lib/python3.7/site-packages/qiskit/tools/jupyter/__init__.py:134: RuntimeWarning: matplotlib can't be found, ensure you have matplotlib and other visualization dependencies installed. You can run '!pip install qiskit-terra[visualization]' to install it from jupyter
"jupyter", RuntimeWarning)
Version Information
Qiskit Software | Version |
---|---|
Qiskit | 0.22.0 |
Terra | 0.15.2 |
Aer | 0.6.1 |
Ignis | 0.4.0 |
Aqua | 0.7.5 |
IBM Q Provider | 0.10.0 |
System information | |
Python | 3.7.9 (default, Aug 31 2020, 07:22:35) [Clang 10.0.0 ] |
OS | Darwin |
CPUs | 6 |
Memory (Gb) | 32.0 |
Tue Nov 03 13:53:54 2020 EST |
This code is a part of Qiskit
© Copyright IBM 2017, 2020.
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.