<jupyter_start><jupyter_text>Bell State Tomography via Parametric CompilationThis notebook walks through how to run **Bell state tomography** on a noisy QVM, using _parametric compilation_ and pyQuil's `Experiment` framework. This notebook is copied partially from the [rigetti/qcs-paper](https://github.com/rigetti/qcs-paper) repository, where it was used to produce **Figure A2** from [_A quantum-classical cloud platform optimized for variational hybrid algorithms_](https://scirate.com/arxiv/2001.04449).<jupyter_code>import itertools
from typing import Generator, List

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib.cm import ScalarMappable
from matplotlib.colors import LinearSegmentedColormap, Normalize
from scipy.linalg import pinv

import pyquil.simulation.matrices as psm
from forest.benchmarking.distance_measures import fidelity
from forest.benchmarking.operator_tools.superoperator_transformations import unvec, vec
from forest.benchmarking.utils import all_traceless_pauli_terms
from pyquil import get_qc, Program
from pyquil.experiment import (
    Experiment, ExperimentResult, ExperimentSetting, correct_experiment_result, zeros_state
)
from pyquil.gates import CNOT, H, RESET
from pyquil.paulis import PauliTerm
from pyquil.simulation.tools import lifted_pauli<jupyter_output>/Users/peter/.pyenv/versions/3.6.3/envs/forest-tutorials/lib/python3.6/site-packages/pyquil/gate_matrices.py:24: FutureWarning: The code in pyquil.gate_matrices has been moved to pyquil.simulation.matrices, please update your import statements.
  FutureWarning,<jupyter_text>Simulate the Data on a Noisy QVM<jupyter_code>qubits = (0, 1)
shots = 500
qc = get_qc("2q-noisy-qvm")<jupyter_output><empty_output><jupyter_text>Define Bell state tomography `Experiment`<jupyter_code>p = Program()
p += RESET()
p += H(qubits[0])
p += CNOT(qubits[0], qubits[1])
p.wrap_in_numshots_loop(shots)
print(p)
def state_tomo_settings(qubits: List[int]) -> Generator[ExperimentSetting, None, None]:
    """
    Adapted from forest.benchmarking.tomography._state_tomo_settings,
    to use pyquil.experiment.ExperimentSetting objects instead.
    """
    list_of_terms = all_traceless_pauli_terms(qubits)
    for obs in all_traceless_pauli_terms(qubits):
        yield ExperimentSetting(
            in_state=zeros_state(qubits),
            out_operator=obs,
        )
state_tomography_settings = list(state_tomo_settings(qubits))
state_tomography_settings
bell_state_tomography = Experiment(settings=state_tomography_settings, program=p)
bell_state_tomography<jupyter_output><empty_output><jupyter_text>Collect data using readout symmetrization<jupyter_code>%%time
results = qc.experiment(bell_state_tomography)
results<jupyter_output>CPU times: user 1.8 s, sys: 11.1 ms, total: 1.81 s
Wall time: 3.61 s<jupyter_text>Perform readout calibration on all observables required for 2Q state tomography<jupyter_code>%%time
calibrations = qc.calibrate(bell_state_tomography)
calibrations<jupyter_output>CPU times: user 1.76 s, sys: 10.4 ms, total: 1.77 s
Wall time: 3.64 s<jupyter_text>Correct for noisy readout using calibration results<jupyter_code>results_corrected = []
for r, c in zip(results, calibrations):
    results_corrected.append(correct_experiment_result(r, c))
results_corrected<jupyter_output><empty_output><jupyter_text>Build ideal density matrix for Bell state $|00\rangle + |11\rangle$<jupyter_code>def build_rho_true() -> np.ndarray:
    """
    Generate the density matrix for state |00> + |11>.
    """
    psi00 = np.array([[1], [0], [0], [0]])
    bell00 = psm.CNOT @ np.kron(psm.H, psm.I) @ psi00
    return np.outer(bell00, bell00.T.conj())

rho_true = build_rho_true()
rho_true<jupyter_output><empty_output><jupyter_text>Estimate density matrix from noisy QVM data using the linear inversion method<jupyter_code>def linear_inv_state_estimate(results: List[ExperimentResult], qubits: List[int]) -> np.ndarray:
    """
    Adapted from forest.benchmarking.tomography.linear_inv_state_estimate,
    to use pyquil.experiment.ExperimentResult objects instead.
    """
    measurement_matrix = np.vstack([
        vec(lifted_pauli(result.setting.out_operator, qubits=qubits)).T.conj() for result in results])
    expectations = np.array([result.expectation for result in results])
    rho = pinv(measurement_matrix) @ expectations
    dim = 2**len(qubits)
    return unvec(rho) + np.eye(dim) / dim

rho_simulated = linear_inv_state_estimate(results_corrected, [0,1])
np.round(rho_simulated, 3)<jupyter_output><empty_output><jupyter_text>Plot the simulated data<jupyter_code>DARK_TEAL = "#47717d"
GOLD = "#f8ba2b"
LIGHT_TEAL = "#66acb4"
NAVY = "#00507b"

# build a custom colormap for the hinton plot
lsc = LinearSegmentedColormap.from_list(name="rigetti", colors=[NAVY, GOLD, LIGHT_TEAL, DARK_TEAL])
ANGLE_MAPPER = ScalarMappable(norm=Normalize(vmin=-np.pi, vmax=np.pi))
ANGLE_MAPPER.set_cmap(lsc)

def hinton(matrix: np.ndarray, ax: plt.Axes) -> None:
    """
    Adapted from forest.benchmarking.tomography.hinton to use custom colors.
    """
    max_weight=1.0
    ax.patch.set_facecolor("white")
    ax.set_aspect("equal", "box")
    ax.xaxis.set_major_locator(plt.NullLocator())
    ax.yaxis.set_major_locator(plt.NullLocator())

    for (x, y), w in np.ndenumerate(matrix):
        color = np.arctan2(w.real, w.imag)
        color = ANGLE_MAPPER.to_rgba(color)
        size = np.sqrt(np.abs(w) / max_weight)
        rect = plt.Rectangle([x - size / 2, y - size / 2], size, size,
                             facecolor=color, edgecolor=color)
        ax.add_patch(rect)

    ax.set_xlim((-max_weight / 2, matrix.shape[0] - max_weight / 2))
    ax.set_ylim((-max_weight / 2, matrix.shape[1] - max_weight / 2))
    ax.autoscale_view()
    ax.invert_yaxis()
fig_qvm, (ax0_qvm, ax1_qvm) = plt.subplots(2, 1, figsize=(6, 12))
fig_qvm.subplots_adjust(hspace=0.1)
hinton(rho_true, ax=ax0_qvm)
hinton(rho_simulated, ax=ax1_qvm)
print(f"Simulated Bell state fidelity = {np.round(fidelity(rho_true, rho_simulated), 4)*100:.2f}%")<jupyter_output>Simulated Bell state fidelity = 97.75%
{'fraction_non_alphanumeric': 0.1088379705400982, 'fraction_numerical': 0.019967266775777415, 'mean_word_length': 5.339211618257261, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 39, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '398477', 'license': 'Apache-2.0', 'n_tokens_mistral': 2269, 'n_tokens_neox': 2132, 'n_words': 458, 'path': 'notebooks/BellStateTomography.ipynb', 'repo_name': 'stjordanis/forest-tutorials', 'size': '6110'}