Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
9d81eed
New algorithm for generating Clifford circuits for single qubit. We g…
merav-aharoni May 25, 2022
c836831
Changed basis for transpilation to match the one in single_qubit_test…
merav-aharoni May 29, 2022
86cad33
Modified the methods in create_clifford_map so that compose does not …
merav-aharoni May 31, 2022
9a873dc
Changed generation of generation of random numbers to be identical to…
merav-aharoni May 31, 2022
76ff366
Test to run on device
merav-aharoni Jun 1, 2022
ab7d4f3
Merge pull request #2 from merav-aharoni/rb_performance
merav-aharoni Jun 7, 2022
274432a
added methods for new algorithm to generate rb circuits: build_rb_cir…
merav-aharoni Jun 9, 2022
890d890
Added the method _layout_for_rb_single_qubit and added test_full_samp…
merav-aharoni Jun 12, 2022
8dc9aa1
In test_full_sampling_single_qubit fixed num_samples to be 1, because…
merav-aharoni Jun 12, 2022
f0c3cc8
Tidied up build_rb_circuits
merav-aharoni Jun 13, 2022
10a7e9f
Added documentation and moved methods
merav-aharoni Jun 13, 2022
aea856a
Added test_single_qubit_parallel
merav-aharoni Jun 13, 2022
eec3721
Merge branch 'main' into transpiled-rb
merav-aharoni Jun 14, 2022
dd0b62b
Changed name _format_data to format_data because the method wasn't be…
merav-aharoni Jun 14, 2022
b9c9f41
Fixed handling of num_samples>1. Cleaned out prints. Reverted previou…
merav-aharoni Jun 15, 2022
92f5580
Added assertExperimentDone to test_single-qubit_parallel. Fixed param…
merav-aharoni Jun 15, 2022
3e9c386
Modified assertAllIdentity to support circuits with rz gates
merav-aharoni Jun 15, 2022
4557572
Changed name _new_rb to _transpiled_rb. Also changed default to be Fa…
merav-aharoni Jun 15, 2022
0673e23
removed fast_rb.py
merav-aharoni Jun 15, 2022
c543fa4
removed rb_on_device.py
merav-aharoni Jun 15, 2022
3c2dca8
Removed temporary 'import time'
merav-aharoni Jun 15, 2022
b9e0884
Added support for interleaved rb single qubit
merav-aharoni Jun 21, 2022
f899247
Fixed handling of interleaved element
merav-aharoni Jun 21, 2022
2bc7469
Fixed bug caused by change of interface of _buil_rb_circuits after ad…
merav-aharoni Jun 22, 2022
bbaf218
added test_number_to_clifford_mapping and fixed the method num_from_1…
merav-aharoni Jun 23, 2022
402bf69
Added support for computation of the Clifford to number mapping of a …
merav-aharoni Jun 23, 2022
67f07ca
Moved setting of interleaved metadata to be under 'if is_interleaved'
merav-aharoni Jun 26, 2022
d72beb1
Added transpilation of interleaved element before creating the rb cir…
merav-aharoni Jun 26, 2022
7f9778f
Fixed incorrect parameter 'qubits' in test_non_clifford_interleaved_e…
merav-aharoni Jun 28, 2022
af42386
Added support for 'delay' as interleaved element
merav-aharoni Jun 28, 2022
95125ed
Moved setting of basis gates to circuits(), because in __init__ the t…
merav-aharoni Jun 28, 2022
d5949f4
Cleaned up setting of tranpile_options in the test. Added call to gen…
merav-aharoni Jun 29, 2022
a9e3146
Changed the clifford compose mapping so that the rhs includes only si…
merav-aharoni Jun 30, 2022
de1d6ff
Changed all methods in CliffordUtils to be classmethod
merav-aharoni Jun 30, 2022
b79b5bf
Fixes following changes in CliffordUtils
merav-aharoni Jun 30, 2022
96046a5
Changed structure of CLIFF_COMPOSE_DATA to be an array instead of a d…
merav-aharoni Jun 30, 2022
352539e
Improved _layout_for_rb_single_qubit to be more robust
merav-aharoni Jul 3, 2022
b10b1c6
Documentation, black, pylint
merav-aharoni Jul 3, 2022
0cef2b7
Merge branch 'main' into transpiled-rb
merav-aharoni Jul 3, 2022
eab49ec
black
merav-aharoni Jul 3, 2022
16fe2e1
Removed the parameter transpiled_rb used for choosing whether to use …
merav-aharoni Jul 5, 2022
e2fe641
Cleaning up
merav-aharoni Jul 5, 2022
6c4662e
Generated transpiled clifford circuits for 2 qubits, and stored in a …
merav-aharoni Jul 6, 2022
2654c1a
Merged with branch transpiled_rb
merav-aharoni Jul 6, 2022
a27ded2
re-generated transpiled circuits
merav-aharoni Jul 6, 2022
6c38ad1
Added support for compose_num_with_clifford_2q. Added suitable test …
merav-aharoni Jul 10, 2022
8c47f94
Created method load_transpiled_cliff_circuits
merav-aharoni Jul 10, 2022
5882832
Added support for two sets of basis gates and their corresponding tra…
merav-aharoni Jul 12, 2022
5d0b893
United the two class variables _transpiled_cliff_circuits_1q and _tra…
merav-aharoni Jul 12, 2022
bbb91ea
Added setting of transpile_options to all tests
merav-aharoni Jul 12, 2022
3788e0f
Fixed error messages
merav-aharoni Jul 12, 2022
ce72dd9
United methods compose_num_with_clifford and num_from_clifford_single…
merav-aharoni Jul 12, 2022
57a6ab0
Unified the format for CLIFF_SINGLE_GATE_MAP_1Q and CLIFF_SINGLE_GATE…
merav-aharoni Jul 12, 2022
eed14b7
Added method clifford_inverse_by_num to unite handling of 1 and 2 qub…
merav-aharoni Jul 12, 2022
93dfec7
Fixes to support 1 and 2 qubits in _build_rb_circuits
merav-aharoni Jul 12, 2022
4408e31
Fixed more places where the code assumed 1 qubit rb
merav-aharoni Jul 14, 2022
04622e3
Fix for handling of delay. Changed test_interleaving_circuit_with_del…
merav-aharoni Jul 17, 2022
89b7602
changed basis gates in test, because rz is not supported in conversio…
merav-aharoni Jul 19, 2022
7415163
Extended lenths for test, because test was failing for statistical re…
merav-aharoni Jul 19, 2022
f7d6609
changed name of method after addition of 2q
merav-aharoni Jul 19, 2022
b0a5e24
Merged with main. Changed test_poor_experiment_result to use a set of…
merav-aharoni Jul 19, 2022
968a7fc
black and lint
merav-aharoni Jul 20, 2022
b05f7df
black, lint and documentation
merav-aharoni Jul 20, 2022
b4f4eb7
black, lint, cleaning
merav-aharoni Jul 21, 2022
d75adc3
Removed legacy code. Additional cleaning
merav-aharoni Jul 21, 2022
60a41f0
Removed more legacy code. More cleaning
merav-aharoni Jul 21, 2022
506eaa7
Merge branch 'main' into 2_qubits_rb
merav-aharoni Jul 21, 2022
6d42ef1
Removed pylint messages for the data file
merav-aharoni Jul 21, 2022
535ffb0
pylint, and use method CliffordUtils.file_name
merav-aharoni Jul 21, 2022
db4b782
Removed additional legacy code
merav-aharoni Jul 21, 2022
ed2824a
Release notes
merav-aharoni Jul 21, 2022
b8c4fb6
Added missing ()
merav-aharoni Jul 21, 2022
d1c304b
Fixed error message
merav-aharoni Jul 21, 2022
a2dd0a7
Added QiskitError to documentation
merav-aharoni Jul 21, 2022
7a0acfc
black
merav-aharoni Jul 21, 2022
059904f
Fixed error message
merav-aharoni Jul 22, 2022
1cafa16
Changed varible name
merav-aharoni Jul 22, 2022
66ceb08
Merge branch 'main' into 2_qubits_rb
merav-aharoni Jul 22, 2022
a4ff280
attempt to fix documentation failure in CI
merav-aharoni Jul 24, 2022
0eda2e7
pylint
merav-aharoni Jul 24, 2022
e6b13c5
Added transpile options to every experiment
merav-aharoni Jul 24, 2022
1efbd0c
Made setting transpile_options mandatory, because if basis_gates are …
merav-aharoni Jul 24, 2022
1fdecee
Split test_rb_utils into itself and test_clifford_utils
merav-aharoni Jul 25, 2022
08e3a27
Added tests for composing a clifford with a number
merav-aharoni Jul 25, 2022
60505a8
Added test for inverse clifford by num
merav-aharoni Jul 25, 2022
14411d4
Changed random to rng because of failure on Windows
merav-aharoni Jul 25, 2022
2990180
Removed dependency on Aer for transpile. Removed the method transpile…
merav-aharoni Jul 27, 2022
0d63efa
Changed parameter backend to be optional, as it was before
merav-aharoni Jul 27, 2022
e90a5e5
Improved format for CLIFF_SINGLE_GATE_MAP, CLIFF_COMPOSE_DATA, CLIFF_…
merav-aharoni Jul 27, 2022
b91b4a0
Changed usage from cliff.__repr__ to repr(cliff)
merav-aharoni Jul 27, 2022
30654f4
Merge branch 'main' into 2_qubits_rb
merav-aharoni Aug 4, 2022
632fa52
Fixed a bug where transpiled_circuits were loaded multiple times
merav-aharoni Aug 7, 2022
0923667
Merge branch '2_qubits_rb' of github.com:merav-aharoni/qiskit-experim…
merav-aharoni Aug 7, 2022
5cf5c5f
Merge branch 'main' into 2_qubits_rb
merav-aharoni Aug 10, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions docs/tutorials/randomized_benchmarking.rst
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,13 @@ interleaved RB experiment will always give you accurate error value :math:`e_i`.

# Run an RB experiment on qubit 0
exp1 = StandardRB(qubits, lengths, num_samples=num_samples, seed=seed)

# basis_gates must be set for randomized benchmarking
transpiler_options = {
"basis_gates": ["rz", "sx", "cx"],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use this set as a default if the user does not pass this option?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is possible. The question is whether we think it is better to have a default, or to make sure the user specifies their needs. @nkanazawa1989 , @ShellyGarion ? what do you think?

Copy link
Copy Markdown
Collaborator

@nkanazawa1989 nkanazawa1989 Jul 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RB experiment itself should be general. Experiment must define a protocol, not executable (target) code.

"optimization_level": 1,
}
exp1.set_transpile_options(**transpiler_options)
expdata1 = exp1.run(backend).block_for_results()
results1 = expdata1.analysis_results()

Expand Down Expand Up @@ -179,14 +186,15 @@ The EPGs of two-qubit RB are analyzed with the corrected EPC if available.
],
flatten_results=True,
)
single_exps.set_transpile_options(**transpiler_options)
expdata_1q = single_exps.run(backend).block_for_results()


.. jupyter-execute::

# Run an RB experiment on qubits 1, 4
exp_2q = StandardRB(qubits, lengths_2_qubit, num_samples=num_samples, seed=seed)

exp_2q.set_transpile_options(**transpiler_options)
# Use the EPG data of the 1-qubit runs to ensure correct 2-qubit EPG computation
exp_2q.analysis.set_options(epg_1_qubit=expdata_1q.analysis_results())

Expand All @@ -213,6 +221,7 @@ Generating an example RB circuit:

# Run an RB experiment on qubit 0
exp = StandardRB(qubits=[0], lengths=[10], num_samples=1, seed=seed)
exp.set_transpile_options(**transpiler_options)
c = exp.circuits()[0]

We transpile the circuit into the backend’s basis gate set:
Expand Down Expand Up @@ -264,7 +273,8 @@ Running a 1-qubit interleaved RB experiment
# The interleaved gate is the x gate
int_exp1 = InterleavedRB(
circuits.XGate(), qubits, lengths, num_samples=num_samples, seed=seed)

int_exp1.set_transpile_options(**transpiler_options)

# Run
int_expdata1 = int_exp1.run(backend).block_for_results()
int_results1 = int_expdata1.analysis_results()
Expand All @@ -291,7 +301,8 @@ Running a 2-qubit interleaved RB experiment
# The interleaved gate is the cx gate
int_exp2 = InterleavedRB(
circuits.CXGate(), qubits, lengths, num_samples=num_samples, seed=seed)

int_exp2.set_transpile_options(**transpiler_options)

# Run
int_expdata2 = int_exp2.run(backend).block_for_results()
int_results2 = int_expdata2.analysis_results()
Expand Down Expand Up @@ -322,6 +333,7 @@ different qubits (see Ref. [5])
exps = [StandardRB([i], lengths, num_samples=num_samples, seed=seed + i)
for i in qubits]
par_exp = ParallelExperiment(exps)
par_exp.set_transpile_options(**transpiler_options)
par_expdata = par_exp.run(backend).block_for_results()
par_results = par_expdata.analysis_results()

Expand Down

Large diffs are not rendered by default.

190 changes: 136 additions & 54 deletions qiskit_experiments/library/randomized_benchmarking/clifford_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,26 @@
Utilities for using the Clifford group in randomized benchmarking
"""

from typing import Optional, Union
import os
from typing import List
from functools import lru_cache
from numpy.random import Generator, default_rng
from math import isclose
import numpy as np

from qiskit import QuantumCircuit, QuantumRegister
from qiskit.circuit import Gate
from qiskit.circuit.library import SdgGate, HGate, SGate, SXdgGate
from qiskit.quantum_info import Clifford, random_clifford
from qiskit.exceptions import QiskitError
from qiskit.quantum_info import Clifford

from .clifford_data import (
CLIFF_SINGLE_GATE_MAP_1Q,
CLIFF_SINGLE_GATE_MAP_2Q,
CLIFF_COMPOSE_DATA_1Q,
CLIFF_COMPOSE_DATA_2Q,
CLIFF_INVERSE_DATA_1Q,
CLIFF_INVERSE_DATA_2Q,
)


class VGate(Gate):
Expand Down Expand Up @@ -64,67 +77,35 @@ class CliffordUtils:
(2, 2, 3, 3, 3, 3, 4, 4),
(2, 2, 3, 3, 4, 4),
]
GENERAL_CLIFF_LIST = ["id", "h", "sdg", "s", "x", "sx", "sxdg", "y", "z", "cx"]
TRANSPILED_CLIFF_LIST = ["sx", "rz", "cx"]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assumes a particular IBM-ish backend. Other providers may use different basis set depending on their hardware architecture, or, even us may want to use different set, e.g. "ecr" rather than "cx" which are locally equivalent.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is difficult to make the code efficient without optimizing it for a specific set of basis gates.
For non-IBM backend, one can change the basis gates, and pre-generate the data files (there is a code for this in generate_transpiled_circuits.py).
As for an ecr gate, it is currently not one of the gates that the Clifford class can handle:
https://qiskit.org/documentation/stubs/qiskit.quantum_info.Clifford.html

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but Qiskit is backend agnostic. This violates very important policy of Qiskit.

CLIFF_SINGLE_GATE_MAP = {1: CLIFF_SINGLE_GATE_MAP_1Q, 2: CLIFF_SINGLE_GATE_MAP_2Q}
CLIFF_COMPOSE_DATA = {1: CLIFF_COMPOSE_DATA_1Q, 2: CLIFF_COMPOSE_DATA_2Q}
CLIFF_INVERSE_DATA = {1: CLIFF_INVERSE_DATA_1Q, 2: CLIFF_INVERSE_DATA_2Q}

def clifford_1_qubit(self, num):
@classmethod
def clifford_1_qubit(cls, num):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was planning to deprecate CliffordUtils because it uses a class just as a namespace and doesn't make any difference from defining a set of functions in a module unless you need to define a subclass for a particular RB experiment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could remove CliffordUtils as a class, and just keep the methods. I think it is nicer and more expressive to have all these methods grouped in a class, to indicate the common functionality.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the method does not access any class-relevant data (i.e. doesn't use the cls parameter) you can make is a @staticmethod instead.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but still these are just a collection of functions. I wonder why these must be class methods, i.e.

from qiskit_experiments.library.randomized_benchmarking import clifford_utils

clifford_utils.clifford_1_qubits(...)

v.s.

from qiskit_experiments.library.randomized_benchmarking.clifford_utils import CliffordUtils

CliffordUtils.clifford_1_qubits(...)

"""Return the 1-qubit clifford element corresponding to `num`
where `num` is between 0 and 23.
"""
return Clifford(self.clifford_1_qubit_circuit(num), validate=False)
return Clifford(cls.clifford_1_qubit_circuit(num), validate=False)

def clifford_2_qubit(self, num):
@classmethod
def clifford_2_qubit(cls, num):
"""Return the 2-qubit clifford element corresponding to `num`
where `num` is between 0 and 11519.
"""
return Clifford(self.clifford_2_qubit_circuit(num), validate=False)

def random_cliffords(
self, num_qubits: int, size: int = 1, rng: Optional[Union[int, Generator]] = None
):
"""Generate a list of random clifford elements"""
if num_qubits > 2:
return random_clifford(num_qubits, seed=rng)

if rng is None:
rng = default_rng()

if isinstance(rng, int):
rng = default_rng(rng)

if num_qubits == 1:
samples = rng.integers(24, size=size)
return [Clifford(self.clifford_1_qubit_circuit(i), validate=False) for i in samples]
else:
samples = rng.integers(11520, size=size)
return [Clifford(self.clifford_2_qubit_circuit(i), validate=False) for i in samples]
Comment thread
merav-aharoni marked this conversation as resolved.

def random_clifford_circuits(
self, num_qubits: int, size: int = 1, rng: Optional[Union[int, Generator]] = None
):
"""Generate a list of random clifford circuits"""
if num_qubits > 2:
return [random_clifford(num_qubits, seed=rng).to_circuit() for _ in range(size)]

if rng is None:
rng = default_rng()

if isinstance(rng, int):
rng = default_rng(rng)

if num_qubits == 1:
samples = rng.integers(24, size=size)
return [self.clifford_1_qubit_circuit(i) for i in samples]
else:
samples = rng.integers(11520, size=size)
return [self.clifford_2_qubit_circuit(i) for i in samples]
Comment thread
merav-aharoni marked this conversation as resolved.
return Clifford(cls.clifford_2_qubit_circuit(num), validate=False)

@classmethod
@lru_cache(maxsize=24)
def clifford_1_qubit_circuit(self, num):
def clifford_1_qubit_circuit(cls, num):
"""Return the 1-qubit clifford circuit corresponding to `num`
where `num` is between 0 and 23.
"""
# pylint: disable=unbalanced-tuple-unpacking
# This is safe since `_unpack_num` returns list the size of the sig
(i, j, p) = self._unpack_num(num, self.CLIFFORD_1_QUBIT_SIG)
(i, j, p) = cls._unpack_num(num, cls.CLIFFORD_1_QUBIT_SIG)
qr = QuantumRegister(1)
qc = QuantumCircuit(qr)
if i == 1:
Expand All @@ -141,12 +122,13 @@ def clifford_1_qubit_circuit(self, num):
qc.z(0)
return qc

@classmethod
@lru_cache(maxsize=11520)
def clifford_2_qubit_circuit(self, num):
def clifford_2_qubit_circuit(cls, num):
"""Return the 2-qubit clifford circuit corresponding to `num`
where `num` is between 0 and 11519.
"""
vals = self._unpack_num_multi_sigs(num, self.CLIFFORD_2_QUBIT_SIGS)
vals = cls._unpack_num_multi_sigs(num, cls.CLIFFORD_2_QUBIT_SIGS)
qr = QuantumRegister(2)
qc = QuantumCircuit(qr)
if vals[0] == 0 or vals[0] == 3:
Expand Down Expand Up @@ -195,7 +177,8 @@ def clifford_2_qubit_circuit(self, num):
qc.z(1)
return qc

def _unpack_num(self, num, sig):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In lines 206-207 above - can replace:

                qc._append(VGate(), [qr[1]], [])
                qc._append(VGate(), [qr[1]], [])

by:
qc._append(WGate(), [qr[1]], [])

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this equivalent? I can't see it. I also tried running and comparing the two, and didn't get the same.

Copy link
Copy Markdown
Contributor

@ShellyGarion ShellyGarion Jul 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One can replace two VGate by one WGate. VGate is of order 3, and WGate is the inverse of VGate.
Lines 163-164 should be the same as line 159 (case k0==2 is the same as case k1==2)

image

@staticmethod
def _unpack_num(num, sig):
r"""Returns a tuple :math:`(a_1, \ldots, a_n)` where
:math:`0 \le a_i \le \sigma_i` where
sig=:math:`(\sigma_1, \ldots, \sigma_n)` and num is the sequential
Expand All @@ -207,7 +190,8 @@ def _unpack_num(self, num, sig):
num //= k
return res

def _unpack_num_multi_sigs(self, num, sigs):
@classmethod
def _unpack_num_multi_sigs(cls, num, sigs):
"""Returns the result of `_unpack_num` on one of the
signatures in `sigs`
"""
Expand All @@ -216,6 +200,104 @@ def _unpack_num_multi_sigs(self, num, sigs):
for k in sig:
sig_size *= k
if num < sig_size:
return [i] + self._unpack_num(num, sig)
return [i] + cls._unpack_num(num, sig)
num -= sig_size
return None

@classmethod
def num_from_clifford_single_gate(cls, inst, qubits, rb_num_qubits, basis_gates):
"""
This method does the reverse of clifford_1_qubit_circuit and clifford_2_qubit_circuit -
given a clifford, it returns the corresponding integer, with the mapping
defined in the above method.
The mapping is in the context of the basis_gates. Therefore, we define here
the possible supersets of basis gates, and verify that the given inst belong to
one of these sets.
"""
name = inst.name
gates_with_delay = basis_gates.copy()
gates_with_delay.append("delay")

if not name in gates_with_delay:
raise QiskitError("Instruction {} is not in the basis gates".format(inst.name))
if set(basis_gates).issubset(set(cls.GENERAL_CLIFF_LIST)):
if name == "delay":
return 0
map_index = name

if set(basis_gates).issubset(set(cls.TRANSPILED_CLIFF_LIST)):
if name in {"sx", "cx"}:
map_index = name
elif name == "delay":
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can do this check once in the beginning of the method

return 0
elif name == "rz":
# The next two are identical up to a phase, which makes no difference
# for the associated Cliffords
if isclose(inst.params[0], np.pi) or isclose(inst.params[0], -np.pi):
map_index = "z"
elif isclose(inst.params[0], np.pi / 2):
map_index = "s"
elif isclose(inst.params[0], -np.pi / 2):
map_index = "sdg"
else:
raise QiskitError("wrong param {} for rz in clifford".format(inst.params[0]))
else:
raise QiskitError(
"Instruction {} could not be converted to Clifford gate".format(name)
)

return cls.CLIFF_SINGLE_GATE_MAP[rb_num_qubits][(map_index, str(qubits))]

@classmethod
def compose_num_with_clifford(
cls, num_qubits: int, composed_num: int, qc: QuantumCircuit, basis_gates: List[str]
) -> int:
"""Compose a number that represents a Clifford, with a single-gate Clifford, and return the
number that represents the resulting Clifford."""

# The numbers corresponding to single gate Cliffords are not in sequence -
# see num_from_1q_clifford_single_gate. To compute the index in
# the array CLIFF_COMPOSE_DATA_1Q, we map the numbers to [0, 8].
map_clifford_num_to_array_index = {}
num_single_gate_cliffs = len(cls.CLIFF_SINGLE_GATE_MAP[num_qubits])
for k in list(cls.CLIFF_SINGLE_GATE_MAP[num_qubits]):
map_clifford_num_to_array_index[cls.CLIFF_SINGLE_GATE_MAP[num_qubits][k]] = list(
cls.CLIFF_SINGLE_GATE_MAP[num_qubits].keys()
).index(k)
if num_qubits == 1:
for inst, qargs, _ in qc:
num = cls.num_from_clifford_single_gate(
inst=inst, qubits=[0], rb_num_qubits=1, basis_gates=basis_gates
)
index = num_single_gate_cliffs * composed_num + map_clifford_num_to_array_index[num]
composed_num = cls.CLIFF_COMPOSE_DATA[num_qubits][index]
else:
for inst, qargs, _ in qc:
if inst.num_qubits == 2:
qubits = [qc.find_bit(qargs[0]).index, qc.find_bit(qargs[1]).index]
else:
qubits = [qc.find_bit(qargs[0]).index]
num = cls.num_from_clifford_single_gate(
inst=inst, qubits=qubits, rb_num_qubits=2, basis_gates=basis_gates
)
index = num_single_gate_cliffs * composed_num + map_clifford_num_to_array_index[num]
composed_num = cls.CLIFF_COMPOSE_DATA[num_qubits][index]
return composed_num

@classmethod
def clifford_inverse_by_num(cls, num: int, num_qubits: int):
"""Return the number of the inverse Clifford to the input num"""
return cls.CLIFF_INVERSE_DATA[num_qubits][num]

@staticmethod
def file_name(num_qubits, basis_gates):
"""Return the name of the file containing the transpiled Cliffords"""
suffix = ""
for n in basis_gates[0:-1]:
suffix += "_" + n
if num_qubits == 2:
suffix += "_" + basis_gates[-1]
circs_file_name = "/transpiled_circs_" + str(num_qubits) + "q" + suffix + ".qpy"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another possibility:

circs_file_name = f"/transpiled_circs_{num_qubits}q{suffix}.qpy"

root_dir = os.path.dirname(os.path.abspath(__file__))
transpiled_circs_file = root_dir + circs_file_name
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using os.path.join() is considered more robust than using +

return transpiled_circs_file
Loading