Skip to content

Commit

Permalink
Merge branch 'main' into gate-correct-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mtreinish committed Jun 26, 2024
2 parents 8f04bc0 + e36027c commit 871b685
Show file tree
Hide file tree
Showing 9 changed files with 84 additions and 8 deletions.
4 changes: 4 additions & 0 deletions constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
# https://github.com/Qiskit/qiskit-terra/issues/10345 for current details.
scipy<1.11; python_version<'3.12'

# Temporary pin to avoid CI issues caused by scipy 1.14.0
# See https://github.com/Qiskit/qiskit/issues/12655 for current details.
scipy==1.13.1; python_version=='3.12'

# z3-solver from 4.12.3 onwards upped the minimum macOS API version for its
# wheels to 11.7. The Azure VM images contain pre-built CPythons, of which at
# least CPython 3.8 was compiled for an older macOS, so does not match a
Expand Down
5 changes: 5 additions & 0 deletions qiskit/providers/fake_provider/generic_backend_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,11 @@ def _build_generic_target(self):
f"in the standard qiskit circuit library."
)
gate = self._supported_gates[name]
if self.num_qubits < gate.num_qubits:
raise QiskitError(
f"Provided basis gate {name} needs more qubits than {self.num_qubits}, "
f"which is the size of the backend."
)
noise_params = self._get_noise_defaults(name, gate.num_qubits)
self._add_noisy_instruction_to_target(gate, noise_params, calibration_inst_map)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def generate_basic_approximations(
data = {}
for sequence in sequences:
gatestring = sequence.name
data[gatestring] = sequence.product
data[gatestring] = (sequence.product, sequence.global_phase)

np.save(filename, data)

Expand Down
24 changes: 18 additions & 6 deletions qiskit/synthesis/discrete_basis/solovay_kitaev.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,19 @@ def __init__(

self.basic_approximations = self.load_basic_approximations(basic_approximations)

def load_basic_approximations(self, data: list | str | dict) -> list[GateSequence]:
@staticmethod
def load_basic_approximations(data: list | str | dict) -> list[GateSequence]:
"""Load basic approximations.
Args:
data: If a string, specifies the path to the file from where to load the data.
If a dictionary, directly specifies the decompositions as ``{gates: matrix}``.
There ``gates`` are the names of the gates producing the SO(3) matrix ``matrix``,
e.g. ``{"h t": np.array([[0, 0.7071, -0.7071], [0, -0.7071, -0.7071], [-1, 0, 0]]}``.
If a dictionary, directly specifies the decompositions as ``{gates: matrix}``
or ``{gates: (matrix, global_phase)}``. There, ``gates`` are the names of the gates
producing the SO(3) matrix ``matrix``, e.g.
``{"h t": np.array([[0, 0.7071, -0.7071], [0, -0.7071, -0.7071], [-1, 0, 0]]}``
and the ``global_phase`` can be given to account for a global phase difference
between the U(2) matrix of the quantum gates and the stored SO(3) matrix.
If not given, the ``global_phase`` will be assumed to be 0.
Returns:
A list of basic approximations as type ``GateSequence``.
Expand All @@ -72,13 +77,20 @@ def load_basic_approximations(self, data: list | str | dict) -> list[GateSequenc

# if a file, load the dictionary
if isinstance(data, str):
data = np.load(data, allow_pickle=True)
data = np.load(data, allow_pickle=True).item()

sequences = []
for gatestring, matrix in data.items():
for gatestring, matrix_and_phase in data.items():
if isinstance(matrix_and_phase, tuple):
matrix, global_phase = matrix_and_phase
else:
matrix, global_phase = matrix_and_phase, 0

sequence = GateSequence()
sequence.gates = [_1q_gates[element] for element in gatestring.split()]
sequence.labels = [gate.name for gate in sequence.gates]
sequence.product = np.asarray(matrix)
sequence.global_phase = global_phase
sequences.append(sequence)

return sequences
Expand Down
10 changes: 10 additions & 0 deletions releasenotes/notes/fix-sk-load-from-file-02c6eabbbd7fcda3.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
fixes:
- |
Fix the :class:`.SolovayKitaev` transpiler pass when loading basic
approximations from an exising ``.npy`` file. Previously, loading
a stored approximation which allowed for further reductions (e.g. due
to gate cancellations) could cause a runtime failure.
Additionally, the global phase difference of the U(2) gate product
and SO(3) representation was lost during a save-reload procedure.
Fixes `Qiskit/qiskit#12576 <https://github.com/Qiskit/qiskit/issues/12576>`_.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
fixes:
- |
The constructor :class:`.GenericBackendV2` was allowing to create malformed backends because it accepted basis gates that couldn't be allocated in the backend size . That is, a backend with a single qubit should not accept a basis with two-qubit gates.
10 changes: 10 additions & 0 deletions test/python/providers/fake_provider/test_generic_backend_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ def test_supported_basis_gates(self):
with self.assertRaises(QiskitError):
GenericBackendV2(num_qubits=8, basis_gates=["cx", "id", "rz", "sx", "zz"])

def test_cx_1Q(self):
"""Test failing with a backend with single qubit but with a two-qubit basis gate"""
with self.assertRaises(QiskitError):
GenericBackendV2(num_qubits=1, basis_gates=["cx", "id"])

def test_ccx_2Q(self):
"""Test failing with a backend with two qubits but with a three-qubit basis gate"""
with self.assertRaises(QiskitError):
GenericBackendV2(num_qubits=2, basis_gates=["ccx", "id"])

def test_operation_names(self):
"""Test that target basis gates include "delay", "measure" and "reset" even
if not provided by user."""
Expand Down
31 changes: 31 additions & 0 deletions test/python/transpiler/test_solovay_kitaev.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@

"""Test the Solovay Kitaev transpilation pass."""

import os
import unittest
import math
import tempfile
import numpy as np
import scipy

Expand Down Expand Up @@ -230,6 +232,35 @@ def test_u_gates_work(self):
included_gates = set(discretized.count_ops().keys())
self.assertEqual(set(basis_gates), included_gates)

def test_load_from_file(self):
"""Test loading basic approximations from a file works.
Regression test of Qiskit/qiskit#12576.
"""
filename = "approximations.npy"

with tempfile.TemporaryDirectory() as tmp_dir:
fullpath = os.path.join(tmp_dir, filename)

# dump approximations to file
generate_basic_approximations(basis_gates=["h", "s", "sdg"], depth=3, filename=fullpath)

# circuit to decompose and reference decomp
circuit = QuantumCircuit(1)
circuit.rx(0.8, 0)

reference = QuantumCircuit(1, global_phase=3 * np.pi / 4)
reference.h(0)
reference.s(0)
reference.h(0)

# load the decomp and compare to reference
skd = SolovayKitaev(basic_approximations=fullpath)
# skd = SolovayKitaev(basic_approximations=filename)
discretized = skd(circuit)

self.assertEqual(discretized, reference)


@ddt
class TestGateSequence(QiskitTestCase):
Expand Down
2 changes: 1 addition & 1 deletion test/visual/mpl/graph/test_graph_matplotlib_drawer.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ def test_plot_1_qubit_gate_map(self):
"""Test plot_gate_map using 1 qubit backend"""
# getting the mock backend from FakeProvider

backend = GenericBackendV2(num_qubits=1)
backend = GenericBackendV2(num_qubits=1, basis_gates=["id", "rz", "sx", "x"])

fname = "1_qubit_gate_map.png"
self.graph_plot_gate_map(backend=backend, filename=fname)
Expand Down

0 comments on commit 871b685

Please sign in to comment.