Skip to content

Commit

Permalink
Upgrade to PyQuil v4 (#237)
Browse files Browse the repository at this point in the history
* chore: update dependencies

* chore: replace deprecated usages and pi inlining

* chore: fix library complaints about np types

* chore: rerun notebooks and fix others

* chore: do not test python 3.7

* fix: 3.10 is not 3.1

* test: maybe 3.10 is problematic

* chore: ci is testing with the wrong version

* chore: pip freeze into requirements-ci.txt

* chore: python version compat

* chore: pip freeze on 3.8 install

* chore: use [0] when no qubits are measured

* chore: update version and changelog

* chore: remove deprecated usage

* chore: use delay instructions instead of pragma

* chore: remove need for flag by delaying instruction pruning

* fix: typo

* chore: add 3.10 and 3.11 to test matrix

* chore: update pyquil dependency to at least 4.5.0

* chore: use program.remove_quil_t_instructions

* chore: fix naming of remove_quil_t_instructions
  • Loading branch information
jselig-rigetti authored Feb 5, 2024
1 parent 4c2c3bf commit aa288ff
Show file tree
Hide file tree
Showing 31 changed files with 405 additions and 407 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.7, 3.8, 3.9]
python-version: [3.8, 3.9, "3.10", "3.11"]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
Expand Down
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
Changelog
=========

[next](https://github.com/rigetti/forest-benchmarking/compare/v0.7.1...master) (in development)
v0.9.0 (September 20, 2023)
------------------------------------------------------------------------------------

### Announcements

- Upgraded pyQuil to v4 (gh-237). As a result, the minimum supported Python version is now 3.8.

### Bugfixes

- Omits `PRAGMA DELAY` from experiments to be run on the QVM.


v0.8.0 (February 4, 2022)
------------------------------------------------------------------------------------

Expand Down
69 changes: 24 additions & 45 deletions docs/examples/entangled_states.ipynb

Large diffs are not rendered by default.

239 changes: 110 additions & 129 deletions docs/examples/quantum_volume.ipynb

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions docs/examples/qubit_spectroscopy_cz_ramsey.ipynb

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions docs/examples/qubit_spectroscopy_t1.ipynb

Large diffs are not rendered by default.

18 changes: 9 additions & 9 deletions docs/examples/qubit_spectroscopy_t2.ipynb

Large diffs are not rendered by default.

81 changes: 46 additions & 35 deletions docs/examples/random_operators.ipynb

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion forest/benchmarking/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.8.0"
__version__ = "0.9.0"
7 changes: 2 additions & 5 deletions forest/benchmarking/analysis/fitting.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,9 @@ def fit_result_to_json(fit_result):
GRAY = '#494949'

FIT_PLOT_KWS = {
'data_kws': {'color': 'black', 'marker': 'o', 'markersize': 4.0},
'data_kws': {'color': 'black', 'markersize': 4.0},
'init_kws': {'color': TEAL, 'alpha': 0.4, 'linestyle': '--'},
'fit_kws': {'color': TEAL, 'alpha': 1.0, 'linewidth': 2.0},
'fit_kws': {'alpha': 1.0, 'linewidth': 2.0},
'numpoints': 1000
}

Expand Down Expand Up @@ -237,9 +237,6 @@ def plot_figure_for_fit(fit_result: ModelResult, xlabel: str = 'x', ylabel: str
axs[0].set_ylabel(ylabel, fontsize=axis_fontsize)
axs[0].set_title(title, fontsize=axis_fontsize)

# residuals don't need a legend
axs[1].legend().set_visible(False)

# adjust tick labels for scales
xticks = ticker.FuncFormatter(lambda x, pos: '{0:g}'.format(x / xscale))
axs[1].xaxis.set_major_formatter(xticks)
Expand Down
5 changes: 3 additions & 2 deletions forest/benchmarking/classical_logic/ripple_carry_adder.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,11 +303,12 @@ def get_n_bit_adder_results(qc: QuantumComputer, n_bits: int,
nat_quil = qc.compiler.quil_to_native_quil(prog)
exe = qc.compiler.native_quil_to_executable(nat_quil)

memory_map = {}
if use_param_program:
exe.write_memory(region_name=REG_NAME, value=bits)
memory_map[REG_NAME] = [bits]

# Run it on the QPU or QVM
results = qc.run(exe).readout_data.get('ro')
results = qc.run(exe, memory_map).get_register_map().get('ro')
all_results.append(results)

return all_results
Expand Down
8 changes: 5 additions & 3 deletions forest/benchmarking/entangled_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
from forest.benchmarking.compilation import basic_compile


def create_ghz_program(tree: nx.DiGraph):
def create_ghz_program(tree: nx.DiGraph, skip_measurements = False):
"""
Create a Bell/GHZ state with CNOTs described by tree.
:param tree: A tree that describes the CNOTs to perform to create a bell/GHZ state.
:param skip_measurements: Whether to skip adding MEASURE instructions to the program.
:return: the program
"""
assert nx.is_tree(tree), 'Needs to be a tree'
Expand All @@ -25,8 +26,9 @@ def create_ghz_program(tree: nx.DiGraph):
program += CNOT(node, child)

ro = program.declare('ro', 'BIT', n_qubits)
for i, q in enumerate(nodes):
program += MEASURE(q, ro[i])
if not skip_measurements:
for i, q in enumerate(nodes):
program += MEASURE(q, ro[i])

return program

Expand Down
10 changes: 7 additions & 3 deletions forest/benchmarking/observable_estimation.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
import networkx as nx
from networkx.algorithms.approximation.clique import clique_removal
from pyquil import Program
from pyquil.api import QuantumComputer
from pyquil.api import QuantumComputer, QVM
from pyquil.gates import RX, RZ, MEASURE, RESET
from pyquil.quilbase import Delay
from pyquil.paulis import PauliTerm, sI, is_identity

from forest.benchmarking.compilation import basic_compile, _RY
Expand Down Expand Up @@ -900,7 +901,10 @@ def estimate_observables(qc: QuantumComputer, obs_expt: ObservablesExperiment,
programs, meas_qubits = generate_experiment_programs(obs_expt, active_reset)
for prog, meas_qs, settings in zip(tqdm(programs, disable=not show_progress_bar), meas_qubits,
obs_expt):
results = qc.run_symmetrized_readout(prog, num_shots, symm_type, meas_qs)
if isinstance(qc.qam, QVM):
prog = prog.remove_quil_t_instructions()

results = qc.run_symmetrized_readout(prog, num_shots, symm_type, meas_qs or [0])

for setting in settings:
observable = setting.observable
Expand Down Expand Up @@ -1009,7 +1013,7 @@ def calibrate_observable_estimates(qc: QuantumComputer, expt_results: List[Exper
calibrations = {}
for prog, meas_qs, obs in zip(tqdm(programs, disable=not show_progress_bar), meas_qubits,
observables):
results = qc.run_symmetrized_readout(prog, num_shots, symm_type, meas_qs)
results = qc.run_symmetrized_readout(prog, num_shots, symm_type, meas_qs or [0])

# Obtain statistics from result of experiment
obs_mean, obs_var = shots_to_obs_moments(results, meas_qs, obs)
Expand Down
2 changes: 1 addition & 1 deletion forest/benchmarking/operator_tools/random_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ def rand_map_with_BCSZ_dist(dim: int, kraus_rank: int) -> np.ndarray:
# But as we use column stacking we need:
Q = np.kron(sqrtm(la.inv(rho_red)), np.eye(dim))
Z = Q @ rho @ Q
return Z
return Z.astype(np.complex128)


def permute_tensor_factors(dims: Union[int, List[int]], perm: List[int]) -> np.ndarray:
Expand Down
2 changes: 1 addition & 1 deletion forest/benchmarking/plotting/state_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def plot_pauli_transfer_matrix(ptransfermatrix: np.ndarray, ax, labels=None, tit
im = ax.imshow(ptransfermatrix, interpolation="nearest", cmap="RdBu", vmin=-1, vmax=1)
if labels is None:
dim_squared = ptransfermatrix.shape[0]
num_qubits = np.int(np.log2(np.sqrt(dim_squared)))
num_qubits = np.int32(np.log2(np.sqrt(dim_squared)))
labels = [''.join(x) for x in itertools.product('IXYZ', repeat=num_qubits)]
else:
dim_squared = len(labels)
Expand Down
2 changes: 1 addition & 1 deletion forest/benchmarking/quantum_volume.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ def sample_rand_circuits_for_heavy_out(qc: QuantumComputer,
# run the program num_shots many times
program.wrap_in_numshots_loop(num_shots)
executable = qc.compiler.native_quil_to_executable(program)
results = qc.run(executable).readout_data.get('ro')
results = qc.run(executable).get_register_map().get('ro')

# classically simulate model circuit represented by the perms and gates for heavy outputs
heavy_outputs = collect_heavy_outputs(wfn_sim, permutations, gates)
Expand Down
10 changes: 5 additions & 5 deletions forest/benchmarking/qubit_spectroscopy.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
from tqdm import tqdm

from pyquil.api import QuantumComputer
from pyquil.gates import RX, RY, RZ, CZ, MEASURE
from pyquil.gates import RX, RY, RZ, CZ
from pyquil.quil import Program
from pyquil.quilbase import Pragma
from pyquil.quilbase import Delay
from pyquil.paulis import PauliTerm

from forest.benchmarking.utils import transform_pauli_moments_to_bit
Expand Down Expand Up @@ -104,7 +104,7 @@ def generate_t1_experiments(qubits: Sequence[int], times: Sequence[float]) \
program = Program()
settings = []
for q in qubits:
program += Pragma('DELAY', [q], str(t))
program += Delay(frames=[], qubits=[q], duration=t)
settings.append(ExperimentSetting(minusZ(q), PauliTerm('Z', q)))

expts.append(ObservablesExperiment([settings], program))
Expand Down Expand Up @@ -223,7 +223,7 @@ def generate_t2_star_experiments(qubits: Sequence[int], times: Sequence[float],
program = Program()
settings = []
for q in qubits:
program += Pragma('DELAY', [q], str(t))
program += Delay(frames=[], qubits=[q], duration=t)
program += RZ(2 * pi * t * detuning, q)
settings.append(ExperimentSetting(minusY(q), PauliTerm('Y', q)))

Expand Down Expand Up @@ -264,7 +264,7 @@ def generate_t2_echo_experiments(qubits: Sequence[int], times: Sequence[float],
program = Program()
settings = []
for q in qubits:
half_delay = Pragma('DELAY', [q], str(half_time))
half_delay = Delay(frames=[], qubits=[q], duration=half_time)
# echo
program += [half_delay, RY(pi, q), half_delay]
# apply detuning
Expand Down
15 changes: 8 additions & 7 deletions forest/benchmarking/readout.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,15 @@ def estimate_confusion_matrix(qc: QuantumComputer, qubit: int, num_shots: int =
ro_zero = zero_meas.declare("ro", "BIT", 1)
zero_meas += MEASURE(qubit, ro_zero[0])
zero_meas.wrap_in_numshots_loop(num_shots)
should_be_0 = qc.run(qc.compile(zero_meas)).readout_data.get('ro')
should_be_0 = qc.run(qc.compile(zero_meas)).get_register_map().get('ro')

# prepare one and measure; repeat shots number of times
one_meas = Program()
one_meas += RX(pi, qubit)
ro_one = one_meas.declare("ro", "BIT", 1)
one_meas += MEASURE(qubit, ro_one[0])
one_meas.wrap_in_numshots_loop(num_shots)
should_be_1 = qc.run(qc.compile(one_meas)).readout_data.get('ro')
should_be_1 = qc.run(qc.compile(one_meas)).get_register_map().get('ro')

p00 = 1 - np.mean(should_be_0)
p11 = np.mean(should_be_1)
Expand Down Expand Up @@ -152,9 +152,10 @@ def estimate_joint_confusion_in_set(qc: QuantumComputer, qubits: Sequence[int] =
matrix = np.zeros((2 ** joint_group_size, 2 ** joint_group_size))
for row, bitstring in enumerate(itertools.product([0, 1], repeat=joint_group_size)):

memory_map = {}
if use_param_program:
# specify bitstring in parameterization at run-time
executable.write_memory(region_name=reg_name, value=[float(b) for b in bitstring])
memory_map[reg_name] = [float(b) for b in bitstring]

else:
# generate program that measures given bitstring on group, and append to start
Expand All @@ -164,7 +165,7 @@ def estimate_joint_confusion_in_set(qc: QuantumComputer, qubits: Sequence[int] =
executable = qc.compiler.native_quil_to_executable(bitstring_program)

# update confusion matrix
results = qc.run(executable).readout_data.get('ro')
results = qc.run(executable, memory_map=memory_map).get_register_map().get('ro')
for result in results:
base = np.array([2 ** i for i in reversed(range(joint_group_size))])
observed = np.sum(base * result)
Expand Down Expand Up @@ -298,8 +299,8 @@ def estimate_joint_reset_confusion(qc: QuantumComputer, qubits: Sequence[int] =
# try preparation at most 10 times.
for _ in range(10):
# prepare the given bitstring and measure
prep_executable.write_memory(region_name=reg_name, value=[float(b) for b in bitstring])
result = qc.run(prep_executable).readout_data.get('ro')
memory_map = {reg_name: [float(b) for b in bitstring]}
result = qc.run(prep_executable, memory_map).get_register_map().get('ro')

# if the preparation is successful, move on to reset.
if np.array_equal(result[0], bitstring):
Expand All @@ -317,7 +318,7 @@ def estimate_joint_reset_confusion(qc: QuantumComputer, qubits: Sequence[int] =
for idx, qubit in enumerate(group):
reset_measure_program += MEASURE(qubit, ro[idx])
executable = qc.compiler.native_quil_to_executable(reset_measure_program)
results = qc.run(executable).readout_data.get('ro')
results = qc.run(executable).get_register_map().get('ro')

# update confusion matrix
for result in results:
Expand Down
Empty file.
17 changes: 9 additions & 8 deletions forest/benchmarking/tests/test_classical_logic_primites.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
"""
Test over all inputs
"""
from pyquil.api import QAM
from pyquil.quil import Program
from pyquil.gates import X, I, CNOT, CCNOT, H, MEASURE
from forest.benchmarking.classical_logic.primitives import *


def test_majority_gate(qvm):
def test_majority_gate(qvm: QAM):
"""
Testing the majority gate with a truth table
"""
Expand All @@ -31,7 +32,7 @@ def test_majority_gate(qvm):
for q in range(3):
prog += MEASURE(q, ro[q])
exe = qvm.compiler.native_quil_to_executable(prog)
result = qvm.run(exe).readout_data.get('ro')
result = qvm.run(exe).get_register_map().get('ro')
assert tuple(result[0]) == true_truth_table[key]


Expand Down Expand Up @@ -59,7 +60,7 @@ def test_unmajority_add_gate(qvm):
for q in range(3):
prog += MEASURE(q, ro[q])
exe = qvm.compiler.native_quil_to_executable(prog)
result = qvm.run(exe).readout_data.get('ro')
result = qvm.run(exe).get_register_map().get('ro')
assert tuple(result[0]) == true_truth_table[key]


Expand Down Expand Up @@ -87,11 +88,11 @@ def test_composition_of_majority_and_unmajority_gates(qvm):
for q in range(3):
prog += MEASURE(q, ro[q])
exe = qvm.compiler.native_quil_to_executable(prog)
result = qvm.run(exe).readout_data.get('ro')
result = qvm.run(exe).get_register_map().get('ro')
assert tuple(result[0]) == true_truth_table[key]


def test_CNOT_in_X_basis(qvm):
def test_CNOT_in_X_basis(qvm: QAM):
"""
Testing the definition of CNOT in the X basis.
"""
Expand All @@ -114,11 +115,11 @@ def test_CNOT_in_X_basis(qvm):
meas_prog += H(qbit_idx)

prog = state_prep_prog + CNOTX + meas_prog
ro = prog.declare('ro', 'BIT', 3)
ro = prog.declare('ro', 'BIT', 2)
for q in range(2):
prog += MEASURE(q, ro[q])
exe = qvm.compiler.native_quil_to_executable(prog)
result = qvm.run(exe).readout_data.get('ro')
result = qvm.run(exe).get_register_map().get('ro')
assert tuple(result[0]) == true_truth_table[key]


Expand Down Expand Up @@ -153,5 +154,5 @@ def test_CCNOT_in_X_basis(qvm):
for q in range(3):
prog += MEASURE(q, ro[q])
exe = qvm.compiler.native_quil_to_executable(prog)
result = qvm.run(exe).readout_data.get('ro')
result = qvm.run(exe).get_register_map().get('ro')
assert tuple(result[0]) == true_truth_table[key]
2 changes: 1 addition & 1 deletion forest/benchmarking/tests/test_compilation.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ def assert_all_close_up_to_global_phase(actual, desired, rtol: float = 1e-7, ato

def test_basic_compile_defgate():
p = Program()
p.inst(RX(pi, 0))
p.defgate("test", [[0, 1], [1, 0]])
p.inst(RX(pi, 0))
p.inst(("test", 2))
p.inst(RZ(pi / 2, 0))

Expand Down
7 changes: 2 additions & 5 deletions forest/benchmarking/tests/test_entangled_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@

def test_create_ghz_program(wfn):
tree = nx.from_edgelist([(0, 1), (0, 2)], create_using=nx.DiGraph())
prog = create_ghz_program(tree)
for _ in tree.nodes:
prog.pop() # remove measurements
prog = create_ghz_program(tree, skip_measurements=True)
prog = address_qubits(prog)
wf = wfn.wavefunction(prog)
should_be = [0.5] + [0] * (2 ** tree.number_of_nodes() - 2) + [0.5]
Expand Down Expand Up @@ -52,9 +50,8 @@ def test_create_graph_state():
def test_measure_graph_state():
graph = nx.complete_graph(4)
prog, addr = measure_graph_state(graph, focal_node=0)
print(prog.out())

assert 'RY(theta)' in str(prog)
assert 'RY(theta[0])' in str(prog)
assert addr == list(range(4))
for a in addr:
assert f'ro[{a}]' in prog.out()
2 changes: 1 addition & 1 deletion forest/benchmarking/tests/test_example_notebooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def out(command):


def run_notebook(filename: str):
return out('pytest --nbval-lax ' + EXAMPLES_PATH + f'/{filename}')
return out('python -m pytest --nbval-lax ' + EXAMPLES_PATH + f'/{filename}')


def _is_not_empty_str(string: str) -> bool:
Expand Down
2 changes: 1 addition & 1 deletion forest/benchmarking/tests/test_quantum_volume.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def test_qv_get_results_by_depth(qvm):

program.wrap_in_numshots_loop(n_shots)
executable = qvm.compiler.native_quil_to_executable(program)
results = qvm.run(executable).readout_data.get('ro')
results = qvm.run(executable).get_register_map().get('ro')
ckt_results.append(results)

heavy_outputs = collect_heavy_outputs(wfn_sim, permutations, gates)
Expand Down
2 changes: 1 addition & 1 deletion forest/benchmarking/tests/test_readout.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def test_get_flipped_program():
if ma is not None:
matched += 1
assert int(ma.group(1)) == int(ma.group(2))
assert l1 == 'RX(pi) {}'.format(int(ma.group(1)))
assert l1 == f'RX({np.pi}) {ma.group(1)}'

assert matched == 2

Expand Down
4 changes: 2 additions & 2 deletions forest/benchmarking/tests/test_ripple_carry_adder.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def test_one_bit_addition(qvm):
for key, value in true_truth_table.items():
adder_prog = adder([key[0]], [key[1]], [2], [1], 0, 3)
exe = qvm.compile(adder_prog)
result = qvm.run(exe).readout_data.get('ro')
result = qvm.run(exe).get_register_map().get('ro')

assert tuple(result[0]) == value

Expand All @@ -38,6 +38,6 @@ def test_one_bit_addition_X_basis(qvm):
for key, value in true_truth_table.items():
adder_prog = adder([key[0]], [key[1]], [2], [1], 0, 3, in_x_basis=True)
exe = qvm.compile(adder_prog)
result = qvm.run(exe).readout_data.get('ro')
result = qvm.run(exe).get_register_map().get('ro')

assert tuple(result[0]) == value
Loading

0 comments on commit aa288ff

Please sign in to comment.