# Source code for mitiq.ddd.rules.rules

```
# Copyright (C) 2022 Unitary Fund
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""Built-in rules determining what DDD sequence should be applied in a given
slack window.
"""
from cirq import (
Circuit,
X,
Y,
I,
LineQubit,
Gate,
unitary,
allclose_up_to_global_phase,
)
from typing import List
from itertools import cycle
import numpy as np
[docs]def general_rule(
slack_length: int, gates: List[Gate], spacing: int = -1
) -> Circuit:
"""Returns a digital dynamical decoupling sequence, based on inputs.
Args:
slack_length: Length of idle window to fill.
spacing: How many identity spacing gates to apply between dynamical
decoupling gates, as a non-negative int. Negative int corresponds
to default. Defaults to maximal spacing that fits a single sequence
in the given slack window.
E.g. given slack_length = 8, gates = [X, X] the spacing defaults
to 2 and the rule returns the sequence:
──I──I──X──I──I──X──I──I──
given slack_length = 9, gates [X, Y, X, Y] the spacing defaults
to 1 and the rule returns the sequence:
──I──X──I──Y──I──X──I──Y──I──.
gates: A list of single qubit Cirq gates to build the rule. E.g. [X, X]
is the xx sequence, [X, Y, X, Y] is the xyxy sequence.
- Note: To repeat the sequence, specify a repeated gateset.
Returns:
A digital dynamical decoupling sequence, as a Cirq circuit.
"""
if len(gates) < 2:
raise ValueError("Gateset too short to make a ddd sequence.")
if slack_length < 2 or slack_length < len(gates):
return Circuit()
num_decoupling_gates = len(gates)
slack_difference = slack_length - num_decoupling_gates
if spacing < 0:
spacing = slack_difference // (num_decoupling_gates + 1)
slack_remainder = slack_length - (
spacing * (num_decoupling_gates + 1) + num_decoupling_gates
)
if slack_remainder < 0:
return Circuit()
q = LineQubit(0)
slack_gates = [I(q) for _ in range(spacing)]
sequence = Circuit(slack_gates)
for gate in gates:
sequence.append(gate.on(q))
sequence.append(slack_gates)
if not allclose_up_to_global_phase(np.eye(2), unitary(sequence)):
raise ValueError("Sequence is not equivalent to the identity!")
for i in range(slack_remainder):
sequence.append(I(q)) if i % 2 else sequence.insert(0, I(q))
return sequence
[docs]def xx(slack_length: int, spacing: int = -1) -> Circuit:
"""Returns an XX digital dynamical decoupling sequence, based on inputs.
Args:
slack_length: Length of idle window to fill.
spacing: How many identity spacing gates to apply between dynamical
decoupling gates, as a non-negative int. Negative int corresponds
to default. Defaults to maximal spacing that fits a single sequence
in the given slack window.
E.g. given slack_length = 8 the spacing defaults to 2 and this
rule returns the sequence:
──I──I──X──I──I──X──I──I──.
Returns:
An XX digital dynamical decoupling sequence, as a Cirq circuit.
"""
xx_sequence = general_rule(
slack_length=slack_length,
spacing=spacing,
gates=[X, X],
)
return xx_sequence
[docs]def xyxy(slack_length: int, spacing: int = -1) -> Circuit:
"""Returns an XYXY digital dynamical decoupling sequence, based on inputs.
Args:
slack_length: Length of idle window to fill.
spacing: How many identity spacing gates to apply between dynamical
decoupling gates, as a non-negative int. Negative int corresponds
to default. Defaults to maximal spacing that fits a single sequence
in the given slack window.
E.g. given slack_length = 9 the spacing defaults to 1 and this
rule returns the sequence:
──I──X──I──Y──I──X──I──Y──I──.
Returns:
An XYXY digital dynamical decoupling sequence, as a Cirq circuit.
"""
xyxy_sequence = general_rule(
slack_length=slack_length,
spacing=spacing,
gates=[X, Y, X, Y],
)
return xyxy_sequence
[docs]def yy(slack_length: int, spacing: int = -1) -> Circuit:
"""Returns a YY digital dynamical decoupling sequence, based on inputs.
Args:
slack_length: Length of idle window to fill.
spacing: How many identity spacing gates to apply between dynamical
decoupling gates, as a non-negative int. Negative int corresponds
to default. Defaults to maximal spacing that fits a single sequence
in the given slack window.
E.g. given slack_length = 8 the spacing defaults to 2 and
this rule returns the sequence:
──I──I──Y──I──I──Y──I──I──.
Returns:
An YY digital dynamical decoupling sequence, as a Cirq circuit.
"""
yy_sequence = general_rule(
slack_length=slack_length,
spacing=spacing,
gates=[Y, Y],
)
return yy_sequence
[docs]def repeated_rule(slack_length: int, gates: List[Gate]) -> Circuit:
"""Returns a general digital dynamical decoupling sequence that repeats
until the slack is filled without spacing, up to a complete repetition.
Args:
slack_length: Length of idle window to fill.
gates: A list of single qubit Cirq gates to build the rule. E.g. [X, X]
is the xx sequence, [X, Y, X, Y] is the xyxy sequence.
Returns:
A repeated digital dynamical decoupling sequence, as a Cirq circuit.
Note:
Where :func:`.general_rule()` fills a slack window with a single
sequence, this rule attempts to fill every moment with sequence
repetitions (up to a complete repetition of the gate set).
E.g. given slack_length = 8 and gates = [X, Y, X, Y], this rule returns
the sequence: ──X──Y──X──Y──X──Y──X──Y──.
"""
num_decoupling_gates = len(gates)
if num_decoupling_gates > slack_length:
return Circuit()
sequence = general_rule(
slack_length=slack_length,
spacing=0,
gates=[
gate
for (_, gate) in zip(
range(slack_length - (slack_length % num_decoupling_gates)),
cycle(gates),
)
],
)
return sequence
```