Skip to content

Commit

Permalink
Fixes to pass all the tests
Browse files Browse the repository at this point in the history
  • Loading branch information
itoko committed Feb 14, 2024
1 parent 757909e commit 81c95dd
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 39 deletions.
112 changes: 75 additions & 37 deletions qiskit_experiments/library/randomized_benchmarking/layer_fidelity.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import functools
import logging
from collections import defaultdict
from typing import Union, Iterable, Optional, List, Sequence, Tuple
from typing import Union, Iterable, Optional, List, Sequence, Tuple, Dict

import numpy as np
from numpy.random import Generator, default_rng
Expand All @@ -30,6 +30,7 @@
from qiskit.pulse.instruction_schedule_map import CalibrationPublisher

from qiskit_experiments.framework import BaseExperiment, Options
from qiskit_experiments.framework.configs import ExperimentConfig
from qiskit_experiments.framework.restless_mixin import RestlessMixin

from .clifford_utils import (
Expand All @@ -50,6 +51,7 @@
LOG = logging.getLogger(__name__)


GATE_NAME_MAP = get_standard_gate_name_mapping()
NUM_1Q_CLIFFORD = CliffordUtils.NUM_CLIFFORD_1_QUBIT


Expand Down Expand Up @@ -103,6 +105,10 @@ def __init__(
full_layers = []
for two_q_layer in two_qubit_layers:
qubits_in_layer = {q for qpair in two_q_layer for q in qpair}
if len(qubits_in_layer) != 2 * len(two_q_layer):
raise QiskitError(
f"two_qubit_layers have a layer with gates on non-disjoint qubits"
)
for q in qubits_in_layer:
if q not in physical_qubits:
raise QiskitError(f"Qubit {q} in two_qubit_layers is not in physical_qubits")
Expand All @@ -121,45 +127,39 @@ def __init__(
if num_samples <= 0:
raise QiskitError(f"The number of samples {num_samples} should be positive.")

if two_qubit_gate:
if self.backend:
if two_qubit_gate not in self.backend.target.operation_names:
raise QiskitError(f"two_qubit_gate {two_qubit_gate} is not in backend.target")
for two_q_layer in two_qubit_layers:
for qpair in two_q_layer:
if not self.backend.target.instruction_supported(two_qubit_gate, qpair):
raise QiskitError(f"{two_qubit_gate}{qpair} is not in backend.target")
else:
if self.backend:
# Try to set default two_qubit_gate from backend
for op in self.backend.target.operations:
if isinstance(op, Gate) and op.num_qubits == 2:
two_qubit_gate = op.name
LOG.info("%s is set for two_qubit_gate", op.name)
break
if two_qubit_gate is None:
if self.backend is None:
raise QiskitError(f"two_qubit_gate or backend must be supplied.")
# Try to set default two_qubit_gate from backend
for op in self.backend.target.operations:
if isinstance(op, Gate) and op.num_qubits == 2:
two_qubit_gate = op.name
LOG.info("%s is set for two_qubit_gate", op.name)
break
if not two_qubit_gate:
raise QiskitError(f"two_qubit_gate is not provided and failed to set from backend.")

if one_qubit_basis_gates:
for gate in one_qubit_basis_gates:
if gate not in self.backend.target.operation_names:
raise QiskitError(f"{gate} in one_qubit_basis_gates is not in backend.target")
for gate in one_qubit_basis_gates:
for q in self.physical_qubits:
if not self.backend.target.instruction_supported(gate, (q,)):
raise QiskitError(f"{gate}({q}) is not in backend.target")
else:
if self.backend:
# Try to set default one_qubit_basis_gates from backend
one_qubit_basis_gates = []
for op in self.backend.target.operations:
if isinstance(op, Gate) and op.num_qubits == 1:
one_qubit_basis_gates.append(op.name)
LOG.info("%s is set for one_qubit_basis_gates", str(one_qubit_basis_gates))
if self.backend is None and two_qubit_gate not in GATE_NAME_MAP:
raise QiskitError(f"Unknown two_qubit_gate: {two_qubit_gate}.")

if one_qubit_basis_gates is None:
if self.backend is None:
raise QiskitError(f"one_qubit_basis_gates or backend must be supplied.")
# Try to set default one_qubit_basis_gates from backend
one_qubit_basis_gates = []
for op in self.backend.target.operations:
if isinstance(op, Gate) and op.num_qubits == 1:
one_qubit_basis_gates.append(op.name)
LOG.info("%s is set for one_qubit_basis_gates", str(one_qubit_basis_gates))
if not one_qubit_basis_gates:
raise QiskitError(
f"one_qubit_basis_gates is not provided and failed to set from backend."
)
else:
if self.backend is None:
for gate in one_qubit_basis_gates:
if gate not in GATE_NAME_MAP:
raise QiskitError(f"Unknown gate in one_qubit_basis_gates: {gate}.")

# Set configurable options
self.set_experiment_options(
Expand All @@ -172,6 +172,9 @@ def __init__(
replicate_in_parallel=replicate_in_parallel,
)

# Verify two_qubit_gate and one_qubit_basis_gates
self.__validate_basis_gates()

@classmethod
def _default_experiment_options(cls) -> Options:
"""Default experiment options.
Expand Down Expand Up @@ -199,7 +202,7 @@ def _default_experiment_options(cls) -> Options:
seed=None,
two_qubit_layers=None,
two_qubit_gate=None,
one_qubit_basis_gates=(),
one_qubit_basis_gates=None,
clifford_synthesis_method=DEFAULT_SYNTHESIS_METHOD,
replicate_in_parallel=True,
)
Expand Down Expand Up @@ -246,6 +249,28 @@ def _set_backend(self, backend: Backend):
super()._set_backend(BackendV2Converter(backend, add_delay=True))
else:
super()._set_backend(backend)
self.__validate_basis_gates()

def __validate_basis_gates(self) -> None:
if not self.backend:
return
opts = self.experiment_options
# validate two_qubit_gate if it is set
if opts.two_qubit_gate:
if opts.two_qubit_gate not in self.backend.target.operation_names:
raise QiskitError(f"two_qubit_gate {opts.two_qubit_gate} is not in backend.target")
for two_q_layer in opts.two_qubit_layers:
for qpair in two_q_layer:
if not self.backend.target.instruction_supported(opts.two_qubit_gate, qpair):
raise QiskitError(f"{opts.two_qubit_gate}{qpair} is not in backend.target")
# validate one_qubit_basis_gates if it is set
for gate in opts.one_qubit_basis_gates or []:
if gate not in self.backend.target.operation_names:
raise QiskitError(f"{gate} in one_qubit_basis_gates is not in backend.target")
for gate in opts.one_qubit_basis_gates or []:
for q in self.physical_qubits:
if not self.backend.target.instruction_supported(gate, (q,)):
raise QiskitError(f"{gate}({q}) is not in backend.target")

def __residual_qubits(self, two_qubit_layer):
qubits_in_layer = {q for qpair in two_qubit_layer for q in qpair}
Expand Down Expand Up @@ -280,7 +305,10 @@ def circuits_generator(self) -> Iterable[QuantumCircuit]:
coupling_tuple=((0, 1),),
synthesis_method=opts.clifford_synthesis_method,
)
GATE2Q = self.backend.target.operation_from_name(opts.two_qubit_gate)
if self.backend:
GATE2Q = self.backend.target.operation_from_name(opts.two_qubit_gate)
else:
GATE2Q = GATE_NAME_MAP[opts.two_qubit_gate]
GATE2Q_CLIFF = num_from_2q_circuit(Clifford(GATE2Q).to_circuit())
# Circuit generation
circuits = []
Expand Down Expand Up @@ -462,11 +490,11 @@ def _transpiled_circuits(self) -> List[QuantumCircuit]:
transpiled = [_decompose_clifford_ops(circ) for circ in self.circuits()]
# Set custom calibrations provided in backend
if isinstance(self.backend, BackendV2):
instructions = [] # (op_name, qargs) for each element where qargs means qubit tuple
instructions = [] # (op_name, qargs) for each element where qargs mean qubit tuple
for two_qubit_layer in self.experiment_options.two_qubit_layers:
for qpair in two_qubit_layer:
instructions.append((self.experiment_options.two_qubit_gate, tuple(qpair)))
for q in self.__residual_qubits(two_qubit_layer):
for q in self.physical_qubits:
for gate_1q in self.experiment_options.one_qubit_basis_gates:
instructions.append((gate_1q, (q,)))

Expand Down Expand Up @@ -494,3 +522,13 @@ def _metadata(self):
metadata = super()._metadata()
metadata["two_qubit_layers"] = self.experiment_options.two_qubit_layers
return metadata

@classmethod
def from_config(cls, config: Union[ExperimentConfig, Dict]) -> "LayerFidelity":
"""Initialize an experiment from experiment config"""
if isinstance(config, dict):
config = ExperimentConfig(**dict)
ret = cls(*config.args, **config.kwargs)
if config.run_options:
ret.set_run_options(**config.run_options)
return ret
5 changes: 3 additions & 2 deletions test/library/randomized_benchmarking/test_layer_fidelity.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,14 @@ def test_backend_with_directed_basis_gates(self):

exp = LayerFidelity(
physical_qubits=(0, 1, 2, 3),
two_qubit_layers=[[(1, 0), (2, 3)], [(1, 2)]],
two_qubit_layers=[[(1, 0), (2, 3)], [(2, 1)]],
lengths=[10, 20, 30],
seed=42,
num_samples=1,
backend=my_backend,
)
transpiled = exp._transpiled_circuits()
for qc in transpiled:
for qc in transpiled[3:]: # check only the second layer
self.assertTrue(qc.count_ops().get("cx", 0) > 0)
expected_qubits = (qc.qubits[2], qc.qubits[1])
for inst in qc:
Expand Down

0 comments on commit 81c95dd

Please sign in to comment.