Skip to content

Commit 4c7f2b4

Browse files
dcmckayibmwshanks
andauthored
Layer Fidelity Extensions (#1517)
Some extensions to the layer fidelity code to allow more custom layer fidelity experiments. - Relax the barrier constraint so that the code can perform direct simultaneous RB - Add a minimum 2Q gate length so that one can force the 2Q layer to have a minimum length (for studying the length dependence of gate error) --------- Co-authored-by: Will Shanks <[email protected]>
1 parent ea34d61 commit 4c7f2b4

File tree

3 files changed

+98
-7
lines changed

3 files changed

+98
-7
lines changed

qiskit_experiments/library/randomized_benchmarking/layer_fidelity.py

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ def __init__(
132132
seed: Optional[Union[int, SeedSequence, BitGenerator, Generator]] = None,
133133
two_qubit_gate: Optional[str] = None,
134134
one_qubit_basis_gates: Optional[Sequence[str]] = None,
135+
layer_barrier: Optional[bool] = True,
136+
min_delay: Optional[Sequence[int]] = None,
135137
):
136138
"""Initialize a layer fidelity experiment.
137139
@@ -153,6 +155,18 @@ def __init__(
153155
one_qubit_basis_gates: Optional, 1q-gates to use for implementing 1q-Clifford operations.
154156
If not specified (but ``backend`` is supplied),
155157
all 1q-gates supported in the backend are automatically set.
158+
layer_barrier (bool): Optional, enforce a barrier across the whole layer.
159+
Default is True, which is the defined protocol for layer fidelity.
160+
If this is set to false the code runs
161+
simultaneous direct 1+2Q RB without a barrier across all qubits.
162+
min_delay: Optional. Define a minimum delay in each 2Q layer in units of dt. This
163+
delay operation will be applied in any 1Q edge of the layer during the 2Q gate layer
164+
in order to enforce a minimum duration of the 2Q layer. This enables some crosstalk
165+
testing by removing a gate from the layer without changing the layer duration. If not
166+
None then is a list equal in length to the number of two_qubit_layers. Note that
167+
this options requires at least one 1Q edge (a qubit in physical_qubits but
168+
not in two_qubit_layers) to be applied. Also will not have an impact on the 2Q gates
169+
if layer_barrier=False.
156170
157171
Raises:
158172
QiskitError: If any invalid argument is supplied.
@@ -231,6 +245,8 @@ def __init__(
231245
two_qubit_layers=two_qubit_layers,
232246
two_qubit_gate=two_qubit_gate,
233247
one_qubit_basis_gates=tuple(one_qubit_basis_gates),
248+
layer_barrier=layer_barrier,
249+
min_delay=min_delay,
234250
)
235251

236252
# Verify two_qubit_gate and one_qubit_basis_gates
@@ -254,6 +270,18 @@ def _default_experiment_options(cls) -> Options:
254270
one_qubit_basis_gates (Tuple[str]): One-qubit gates to use for implementing 1q Cliffords.
255271
clifford_synthesis_method (str): The name of the Clifford synthesis plugin to use
256272
for building circuits of RB sequences.
273+
layer_barrier (bool): Optional, enforce a barrier across the whole layer.
274+
Default is True, which is the defined protocol for layer fidelity.
275+
If this is set to false the code runs
276+
simultaneous direct 1+2Q RB without a barrier across all qubits.
277+
min_delay (List[int]): Optional. Define a minimum delay in each 2Q layer in units of dt. This
278+
delay operation will be applied in any 1Q edge of the layer during the 2Q gate layer
279+
in order to enforce a minimum duration of the 2Q layer. This enables some crosstalk
280+
testing by removing a gate from the layer without changing the layer duration. If not
281+
None then is a list equal in length to the number of two_qubit_layers. Note that
282+
this options requires at least one 1Q edge (a qubit in physical_qubits but
283+
not in two_qubit_layers) to be applied. Also will not have an impact on the 2Q gates
284+
if layer_barrier=False.
257285
"""
258286
options = super()._default_experiment_options()
259287
options.update_options(
@@ -264,6 +292,8 @@ def _default_experiment_options(cls) -> Options:
264292
two_qubit_gate=None,
265293
one_qubit_basis_gates=None,
266294
clifford_synthesis_method=DEFAULT_SYNTHESIS_METHOD,
295+
layer_barrier=True,
296+
min_delay=None,
267297
)
268298
return options
269299

@@ -367,6 +397,11 @@ def circuits_generator(self) -> Iterable[QuantumCircuit]:
367397
else:
368398
gate2q = GATE_NAME_MAP[opts.two_qubit_gate]
369399
gate2q_cliff = num_from_2q_circuit(Clifford(gate2q).to_circuit())
400+
401+
# warn if min delay is not None and barrier is false
402+
if opts.min_delay is not None and not opts.layer_barrier:
403+
warnings.warn("Min delay applied when layer_barrier is False.")
404+
370405
# Circuit generation
371406
num_qubits = max(self.physical_qubits) + 1
372407
for i_sample in range(opts.num_samples):
@@ -380,9 +415,32 @@ def circuits_generator(self) -> Iterable[QuantumCircuit]:
380415
composite_clbits.extend(
381416
[(c,) for c in range(2 * num_2q_gates, 2 * num_2q_gates + num_1q_gates)]
382417
)
418+
419+
if opts.min_delay is None:
420+
min_delay = None
421+
else:
422+
min_delay = opts.min_delay[i_set]
423+
383424
for length in opts.lengths:
384425
circ = QuantumCircuit(num_qubits, num_qubits)
385-
barrier_inst = CircuitInstruction(Barrier(num_qubits), circ.qubits)
426+
# define the barrier instruction
427+
full_barrier_inst = CircuitInstruction(Barrier(num_qubits), circ.qubits)
428+
if not opts.layer_barrier:
429+
# we want separate barriers for each qubit so define them individually
430+
barrier_inst_gate = []
431+
for two_q_gate in two_qubit_layer:
432+
barrier_inst_gate.append(
433+
CircuitInstruction(
434+
Barrier(2),
435+
[circ.qubits[two_q_gate[0]], circ.qubits[two_q_gate[1]]],
436+
)
437+
)
438+
for one_q in one_qubits:
439+
barrier_inst_gate.append(
440+
CircuitInstruction(Barrier(1), [circ.qubits[one_q]])
441+
)
442+
else:
443+
barrier_inst_gate = [full_barrier_inst]
386444
self.__circuit_body(
387445
circ,
388446
length,
@@ -393,10 +451,11 @@ def circuits_generator(self) -> Iterable[QuantumCircuit]:
393451
_to_gate_2q,
394452
gate2q,
395453
gate2q_cliff,
396-
barrier_inst,
454+
barrier_inst_gate,
455+
min_delay,
397456
)
398457
# add the measurements
399-
circ._append(barrier_inst)
458+
circ._append(full_barrier_inst)
400459
for qubits, clbits in zip(composite_qubits, composite_clbits):
401460
circ.measure(qubits, clbits)
402461
# store composite structure in metadata
@@ -443,8 +502,14 @@ def __circuit_body(
443502
_to_gate_2q,
444503
gate2q,
445504
gate2q_cliff,
446-
barrier_inst,
505+
barrier_inst_lst,
506+
min_delay=None,
447507
):
508+
509+
# warn if min_delay is not none and one_qubits is empty
510+
if min_delay is not None and len(one_qubits) == 0:
511+
warnings.warn("Min delay will not be applied because there are no 1Q edges.")
512+
448513
# initialize cliffords and a ciruit (0: identity clifford)
449514
cliffs_2q = [0] * len(two_qubit_layer)
450515
cliffs_1q = [0] * len(one_qubits)
@@ -463,16 +528,21 @@ def __circuit_body(
463528
sample = rng.integers(NUM_1Q_CLIFFORD)
464529
cliffs_1q[k] = compose_1q(cliffs_1q[k], sample)
465530
circ._append(_to_gate_1q(sample), (circ.qubits[q],), ())
466-
circ._append(barrier_inst)
531+
for barrier_inst in barrier_inst_lst:
532+
circ._append(barrier_inst)
467533
# add two qubit gates
468534
for j, qpair in enumerate(two_qubit_layer):
469535
circ._append(gate2q, tuple(circ.qubits[q] for q in qpair), ())
470536
cliffs_2q[j] = compose_2q(cliffs_2q[j], gate2q_cliff)
471537
# TODO: add dd if necessary
472538
for k, q in enumerate(one_qubits):
473539
# TODO: add dd if necessary
474-
pass
475-
circ._append(barrier_inst)
540+
# if there is a min_delay, just need
541+
# to add to one of the qubits
542+
if min_delay is not None and k == 0:
543+
circ.delay(min_delay, q)
544+
for barrier_inst in barrier_inst_lst:
545+
circ._append(barrier_inst)
476546
# add the last inverse
477547
for j, qpair in enumerate(two_qubit_layer):
478548
inv = inverse_2q(cliffs_2q[j])

qiskit_experiments/library/randomized_benchmarking/layer_fidelity_analysis.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ def _create_analysis_results(
147147
alpha = fit_data.ufloat_params["alpha"]
148148
pf = (1 + (d * d - 1) * alpha) / (d * d)
149149

150+
# calculate error per layer
151+
epl = (1 - alpha) * (d - 1) / d
152+
150153
quality, reason = self._evaluate_quality_with_reason(fit_data)
151154

152155
metadata["qubits"] = self._physical_qubits
@@ -161,6 +164,15 @@ def _create_analysis_results(
161164
extra=metadata,
162165
)
163166
)
167+
outcomes.append(
168+
AnalysisResultData(
169+
name="EPL",
170+
value=epl,
171+
chisq=fit_data.reduced_chisq,
172+
quality=quality,
173+
extra=metadata,
174+
)
175+
)
164176
return outcomes
165177

166178
def _run_analysis(
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
features:
3+
- |
4+
:class:`.LayerFidelity` has two new options, ``layer_barrier`` and ``min_delay``.
5+
``layer_barrier`` turns off the barrier between all qubits in the layer,
6+
so that layer run becomes simultaneous direct RB. ``min_delay`` allows the
7+
user to enforce a minimum delay during the two-qubit layer using a qubit
8+
that is not part of the two-qubit gate list. Also added the EPL
9+
(error per layer) calculation to the layer analysis output and to the figure.

0 commit comments

Comments
 (0)