Source code for graphix.transpiler

"""Gate-to-MBQC transpiler.

accepts desired gate operations and transpile into MBQC measurement patterns.

"""

from __future__ import annotations

import dataclasses
from copy import deepcopy
from typing import TYPE_CHECKING, Callable

import numpy as np

from graphix import command, instruction, parameter
from graphix.command import CommandKind, E, M, N, X, Z
from graphix.fundamentals import Plane
from graphix.ops import Ops
from graphix.parameter import ExpressionOrSupportsFloat, Parameter
from graphix.pattern import Pattern
from graphix.sim import base_backend
from graphix.sim.statevec import Data, Statevec

if TYPE_CHECKING:
    from collections.abc import Mapping, Sequence


[docs] @dataclasses.dataclass class TranspileResult: """ The result of a transpilation. pattern : :class:`graphix.pattern.Pattern` object classical_outputs : tuple[int,...], index of nodes measured with `M` gates """ pattern: Pattern classical_outputs: tuple[int, ...]
[docs] @dataclasses.dataclass class SimulateResult: """ The result of a simulation. statevec : :class:`graphix.sim.statevec.Statevec` object classical_measures : tuple[int,...], classical measures """ statevec: Statevec classical_measures: tuple[int, ...]
Angle = ExpressionOrSupportsFloat
[docs] class Circuit: """Gate-to-MBQC transpiler. Holds gate operations and translates into MBQC measurement patterns. Attributes ---------- width : int Number of logical qubits (for gate network) instruction : list List containing the gate sequence applied. """
[docs] def __init__(self, width: int): """ Construct a circuit. Parameters ---------- width : int number of logical qubits for the gate network """ self.width = width self.instruction: list[instruction.Instruction] = [] self.active_qubits = set(range(width))
[docs] def cnot(self, control: int, target: int): """Apply a CNOT gate. Parameters ---------- control : int control qubit target : int target qubit """ assert control in self.active_qubits assert target in self.active_qubits assert control != target self.instruction.append(instruction.CNOT(control=control, target=target))
def swap(self, qubit1: int, qubit2: int): """Apply a SWAP gate. Parameters ---------- qubit1 : int first qubit to be swapped qubit2 : int second qubit to be swapped """ assert qubit1 in self.active_qubits assert qubit2 in self.active_qubits assert qubit1 != qubit2 self.instruction.append(instruction.SWAP(targets=(qubit1, qubit2)))
[docs] def h(self, qubit: int): """Apply a Hadamard gate. Parameters ---------- qubit : int target qubit """ assert qubit in self.active_qubits self.instruction.append(instruction.H(target=qubit))
[docs] def s(self, qubit: int): """Apply an S gate. Parameters ---------- qubit : int target qubit """ assert qubit in self.active_qubits self.instruction.append(instruction.S(target=qubit))
[docs] def x(self, qubit): """Apply a Pauli X gate. Parameters ---------- qubit : int target qubit """ assert qubit in self.active_qubits self.instruction.append(instruction.X(target=qubit))
[docs] def y(self, qubit: int): """Apply a Pauli Y gate. Parameters ---------- qubit : int target qubit """ assert qubit in self.active_qubits self.instruction.append(instruction.Y(target=qubit))
[docs] def z(self, qubit: int): """Apply a Pauli Z gate. Parameters ---------- qubit : int target qubit """ assert qubit in self.active_qubits self.instruction.append(instruction.Z(target=qubit))
[docs] def rx(self, qubit: int, angle: Angle): """Apply an X rotation gate. Parameters ---------- qubit : int target qubit angle : Angle rotation angle in radian """ assert qubit in self.active_qubits self.instruction.append(instruction.RX(target=qubit, angle=angle))
[docs] def ry(self, qubit: int, angle: Angle): """Apply a Y rotation gate. Parameters ---------- qubit : int target qubit angle : Angle angle in radian """ assert qubit in self.active_qubits self.instruction.append(instruction.RY(target=qubit, angle=angle))
[docs] def rz(self, qubit: int, angle: Angle): """Apply a Z rotation gate. Parameters ---------- qubit : int target qubit angle : Angle rotation angle in radian """ assert qubit in self.active_qubits self.instruction.append(instruction.RZ(target=qubit, angle=angle))
def rzz(self, control: int, target: int, angle: Angle): r"""Apply a ZZ-rotation gate. Equivalent to the sequence CNOT(control, target), Rz(target, angle), CNOT(control, target) and realizes rotation expressed by :math:`e^{-i \frac{\theta}{2} Z_c Z_t}`. Parameters ---------- control : int control qubit target : int target qubit angle : Angle rotation angle in radian """ assert control in self.active_qubits assert target in self.active_qubits self.instruction.append(instruction.RZZ(control=control, target=target, angle=angle))
[docs] def ccx(self, control1: int, control2: int, target: int): r"""Apply a CCX (Toffoli) gate. Prameters --------- control1 : int first control qubit control2 : int second control qubit target : int target qubit """ assert control1 in self.active_qubits assert control2 in self.active_qubits assert target in self.active_qubits assert control1 != control2 assert control1 != target assert control2 != target self.instruction.append(instruction.CCX(controls=(control1, control2), target=target))
def i(self, qubit: int): """Apply an identity (teleportation) gate. Parameters ---------- qubit : int target qubit """ assert qubit in self.active_qubits self.instruction.append(instruction.I(target=qubit))
[docs] def m(self, qubit: int, plane: Plane, angle: Angle): """Measure a quantum qubit. The measured qubit cannot be used afterwards. Parameters ---------- qubit : int target qubit plane : Plane angle : Angle """ assert qubit in self.active_qubits self.instruction.append(instruction.M(target=qubit, plane=plane, angle=angle)) self.active_qubits.remove(qubit)
[docs] def transpile(self) -> TranspileResult: """Transpile the circuit to a pattern. Returns ------- result : :class:`TranspileResult` object """ n_node = self.width out = list(range(self.width)) pattern = Pattern(input_nodes=list(range(self.width))) classical_outputs = [] for instr in self.instruction: kind = instr.kind if kind == instruction.InstructionKind.CNOT: ancilla = [n_node, n_node + 1] assert out[instr.control] is not None assert out[instr.target] is not None out[instr.control], out[instr.target], seq = self._cnot_command( out[instr.control], out[instr.target], ancilla ) pattern.extend(seq) n_node += 2 elif kind == instruction.InstructionKind.SWAP: out[instr.targets[0]], out[instr.targets[1]] = ( out[instr.targets[1]], out[instr.targets[0]], ) elif kind == instruction.InstructionKind.I: pass elif kind == instruction.InstructionKind.H: ancilla = n_node out[instr.target], seq = self._h_command(out[instr.target], ancilla) pattern.extend(seq) n_node += 1 elif kind == instruction.InstructionKind.S: ancilla = [n_node, n_node + 1] out[instr.target], seq = self._s_command(out[instr.target], ancilla) pattern.extend(seq) n_node += 2 elif kind == instruction.InstructionKind.X: ancilla = [n_node, n_node + 1] out[instr.target], seq = self._x_command(out[instr.target], ancilla) pattern.extend(seq) n_node += 2 elif kind == instruction.InstructionKind.Y: ancilla = [n_node, n_node + 1, n_node + 2, n_node + 3] out[instr.target], seq = self._y_command(out[instr.target], ancilla) pattern.extend(seq) n_node += 4 elif kind == instruction.InstructionKind.Z: ancilla = [n_node, n_node + 1] out[instr.target], seq = self._z_command(out[instr.target], ancilla) pattern.extend(seq) n_node += 2 elif kind == instruction.InstructionKind.RX: ancilla = [n_node, n_node + 1] out[instr.target], seq = self._rx_command(out[instr.target], ancilla, instr.angle) pattern.extend(seq) n_node += 2 elif kind == instruction.InstructionKind.RY: ancilla = [n_node, n_node + 1, n_node + 2, n_node + 3] out[instr.target], seq = self._ry_command(out[instr.target], ancilla, instr.angle) pattern.extend(seq) n_node += 4 elif kind == instruction.InstructionKind.RZ: ancilla = [n_node, n_node + 1] out[instr.target], seq = self._rz_command(out[instr.target], ancilla, instr.angle) pattern.extend(seq) n_node += 2 elif kind == instruction.InstructionKind.CCX: ancilla = [n_node + i for i in range(18)] ( out[instr.controls[0]], out[instr.controls[1]], out[instr.target], seq, ) = self._ccx_command( out[instr.controls[0]], out[instr.controls[1]], out[instr.target], ancilla, ) pattern.extend(seq) n_node += 18 elif kind == instruction.InstructionKind.M: node_index = out[instr.target] seq = self._m_command(instr.target, instr.plane, instr.angle) pattern.extend(seq) classical_outputs.append(node_index) out[instr.target] = None else: raise ValueError("Unknown instruction, commands not added") out = filter(lambda node: node is not None, out) pattern.reorder_output_nodes(out) return TranspileResult(pattern, tuple(classical_outputs))
@classmethod def _cnot_command( cls, control_node: int, target_node: int, ancilla: Sequence[int] ) -> tuple[int, int, list[command.Command]]: """MBQC commands for CNOT gate. Parameters ---------- control_node : int control node on graph target : int target node on graph ancilla : list of two ints ancilla node indices to be added to graph Returns ------- control_out : int control node on graph after the gate target_out : int target node on graph after the gate commands : list list of MBQC commands """ assert len(ancilla) == 2 seq = [N(node=ancilla[0]), N(node=ancilla[1])] seq.append(E(nodes=(target_node, ancilla[0]))) seq.append(E(nodes=(control_node, ancilla[0]))) seq.append(E(nodes=(ancilla[0], ancilla[1]))) seq.append(M(node=target_node)) seq.append(M(node=ancilla[0])) seq.append(X(node=ancilla[1], domain={ancilla[0]})) seq.append(Z(node=ancilla[1], domain={target_node})) seq.append(Z(node=control_node, domain={target_node})) return control_node, ancilla[1], seq @classmethod def _m_command(cls, input_node: int, plane: Plane, angle: Angle): """MBQC commands for measuring qubit. Parameters ---------- input_node : int target node on graph plane : Plane plane of the measure angle : Angle angle of the measure (unit: pi radian) Returns ------- commands : list list of MBQC commands """ return [M(node=input_node, plane=plane, angle=angle)] @classmethod def _h_command(cls, input_node: int, ancilla: int): """MBQC commands for Hadamard gate. Parameters ---------- input_node : int target node on graph ancilla : int ancilla node index to be added Returns ------- out_node : int control node on graph after the gate commands : list list of MBQC commands """ seq = [N(node=ancilla)] seq.append(E(nodes=(input_node, ancilla))) seq.append(M(node=input_node)) seq.append(X(node=ancilla, domain={input_node})) return ancilla, seq @classmethod def _s_command(cls, input_node: int, ancilla: Sequence[int]) -> tuple[int, list[command.Command]]: """MBQC commands for S gate. Parameters ---------- input_node : int input node index ancilla : list of two ints ancilla node indices to be added to graph Returns ------- out_node : int control node on graph after the gate commands : list list of MBQC commands """ assert len(ancilla) == 2 seq = [N(node=ancilla[0]), command.N(node=ancilla[1])] seq.append(E(nodes=(input_node, ancilla[0]))) seq.append(E(nodes=(ancilla[0], ancilla[1]))) seq.append(M(node=input_node, angle=-0.5)) seq.append(M(node=ancilla[0])) seq.append(X(node=ancilla[1], domain={ancilla[0]})) seq.append(Z(node=ancilla[1], domain={input_node})) return ancilla[1], seq @classmethod def _x_command(cls, input_node: int, ancilla: Sequence[int]) -> tuple[int, list[command.Command]]: """MBQC commands for Pauli X gate. Parameters ---------- input_node : int input node index ancilla : list of two ints ancilla node indices to be added to graph Returns ------- out_node : int control node on graph after the gate commands : list list of MBQC commands """ assert len(ancilla) == 2 seq = [N(node=ancilla[0]), N(node=ancilla[1])] seq.append(E(nodes=(input_node, ancilla[0]))) seq.append(E(nodes=(ancilla[0], ancilla[1]))) seq.append(M(node=input_node)) seq.append(M(node=ancilla[0], angle=-1)) seq.append(X(node=ancilla[1], domain={ancilla[0]})) seq.append(Z(node=ancilla[1], domain={input_node})) return ancilla[1], seq @classmethod def _y_command(cls, input_node: int, ancilla: Sequence[int]) -> tuple[int, list[command.Command]]: """MBQC commands for Pauli Y gate. Parameters ---------- input_node : int input node index ancilla : list of four ints ancilla node indices to be added to graph Returns ------- out_node : int control node on graph after the gate commands : list list of MBQC commands """ assert len(ancilla) == 4 seq = [N(node=ancilla[0]), N(node=ancilla[1])] seq.extend([N(node=ancilla[2]), N(node=ancilla[3])]) seq.append(E(nodes=(input_node, ancilla[0]))) seq.append(E(nodes=(ancilla[0], ancilla[1]))) seq.append(E(nodes=(ancilla[1], ancilla[2]))) seq.append(E(nodes=(ancilla[2], ancilla[3]))) seq.append(M(node=input_node, angle=0.5)) seq.append(M(node=ancilla[0], angle=1.0, s_domain={input_node})) seq.append(M(node=ancilla[1], angle=-0.5, s_domain={input_node})) seq.append(M(node=ancilla[2])) seq.append(X(node=ancilla[3], domain={ancilla[0], ancilla[2]})) seq.append(Z(node=ancilla[3], domain={ancilla[0], ancilla[1]})) return ancilla[3], seq @classmethod def _z_command(cls, input_node: int, ancilla: Sequence[int]) -> tuple[int, list[command.Command]]: """MBQC commands for Pauli Z gate. Parameters ---------- input_node : int input node index ancilla : list of two ints ancilla node indices to be added to graph Returns ------- out_node : int control node on graph after the gate commands : list list of MBQC commands """ assert len(ancilla) == 2 seq = [N(node=ancilla[0]), N(node=ancilla[1])] seq.append(E(nodes=(input_node, ancilla[0]))) seq.append(E(nodes=(ancilla[0], ancilla[1]))) seq.append(M(node=input_node, angle=-1)) seq.append(M(node=ancilla[0])) seq.append(X(node=ancilla[1], domain={ancilla[0]})) seq.append(Z(node=ancilla[1], domain={input_node})) return ancilla[1], seq @classmethod def _rx_command(cls, input_node: int, ancilla: Sequence[int], angle: Angle) -> tuple[int, list[command.Command]]: """MBQC commands for X rotation gate. Parameters ---------- input_node : int input node index ancilla : list of two ints ancilla node indices to be added to graph angle : Angle measurement angle in radian Returns ------- out_node : int control node on graph after the gate commands : list list of MBQC commands """ assert len(ancilla) == 2 seq = [N(node=ancilla[0]), N(node=ancilla[1])] seq.append(E(nodes=(input_node, ancilla[0]))) seq.append(E(nodes=(ancilla[0], ancilla[1]))) seq.append(M(node=input_node)) seq.append(M(node=ancilla[0], angle=-angle / np.pi, s_domain={input_node})) seq.append(X(node=ancilla[1], domain={ancilla[0]})) seq.append(Z(node=ancilla[1], domain={input_node})) return ancilla[1], seq @classmethod def _ry_command(cls, input_node: int, ancilla: Sequence[int], angle: Angle) -> tuple[int, list[command.Command]]: """MBQC commands for Y rotation gate. Parameters ---------- input_node : int input node index ancilla : list of four ints ancilla node indices to be added to graph angle : Angle rotation angle in radian Returns ------- out_node : int control node on graph after the gate commands : list list of MBQC commands """ assert len(ancilla) == 4 seq = [N(node=ancilla[0]), N(node=ancilla[1])] seq.extend([N(node=ancilla[2]), N(node=ancilla[3])]) seq.append(E(nodes=(input_node, ancilla[0]))) seq.append(E(nodes=(ancilla[0], ancilla[1]))) seq.append(E(nodes=(ancilla[1], ancilla[2]))) seq.append(E(nodes=(ancilla[2], ancilla[3]))) seq.append(M(node=input_node, angle=0.5)) seq.append(M(node=ancilla[0], angle=-angle / np.pi, s_domain={input_node})) seq.append(M(node=ancilla[1], angle=-0.5, s_domain={input_node})) seq.append(M(node=ancilla[2])) seq.append(X(node=ancilla[3], domain={ancilla[0], ancilla[2]})) seq.append(Z(node=ancilla[3], domain={ancilla[0], ancilla[1]})) return ancilla[3], seq @classmethod def _rz_command(cls, input_node: int, ancilla: Sequence[int], angle: Angle) -> tuple[int, list[command.Command]]: """MBQC commands for Z rotation gate. Parameters ---------- input_node : int input node index ancilla : list of two ints ancilla node indices to be added to graph angle : Angle measurement angle in radian Returns ------- out_node : int node on graph after the gate commands : list list of MBQC commands """ assert len(ancilla) == 2 seq = [N(node=ancilla[0]), N(node=ancilla[1])] # assign new qubit labels seq.append(E(nodes=(input_node, ancilla[0]))) seq.append(E(nodes=(ancilla[0], ancilla[1]))) seq.append(M(node=input_node, angle=-angle / np.pi)) seq.append(M(node=ancilla[0])) seq.append(X(node=ancilla[1], domain={ancilla[0]})) seq.append(Z(node=ancilla[1], domain={input_node})) return ancilla[1], seq @classmethod def _ccx_command( cls, control_node1: int, control_node2: int, target_node: int, ancilla: Sequence[int], ) -> tuple[int, int, int, list[command.Command]]: """MBQC commands for CCX gate. Parameters ---------- control_node1 : int first control node on graph control_node2 : int second control node on graph target_node : int target node on graph ancilla : list of int ancilla node indices to be added to graph Returns ------- control_out1 : int first control node on graph after the gate control_out2 : int second control node on graph after the gate target_out : int target node on graph after the gate commands : list list of MBQC commands """ assert len(ancilla) == 18 seq = [N(node=ancilla[i]) for i in range(18)] # assign new qubit labels seq.append(E(nodes=(target_node, ancilla[0]))) seq.append(E(nodes=(ancilla[0], ancilla[1]))) seq.append(E(nodes=(ancilla[1], ancilla[2]))) seq.append(E(nodes=(ancilla[1], control_node2))) seq.append(E(nodes=(control_node1, ancilla[14]))) seq.append(E(nodes=(ancilla[2], ancilla[3]))) seq.append(E(nodes=(ancilla[14], ancilla[4]))) seq.append(E(nodes=(ancilla[3], ancilla[5]))) seq.append(E(nodes=(ancilla[3], ancilla[4]))) seq.append(E(nodes=(ancilla[5], ancilla[6]))) seq.append(E(nodes=(control_node2, ancilla[6]))) seq.append(E(nodes=(control_node2, ancilla[9]))) seq.append(E(nodes=(ancilla[6], ancilla[7]))) seq.append(E(nodes=(ancilla[9], ancilla[4]))) seq.append(E(nodes=(ancilla[9], ancilla[10]))) seq.append(E(nodes=(ancilla[7], ancilla[8]))) seq.append(E(nodes=(ancilla[10], ancilla[11]))) seq.append(E(nodes=(ancilla[4], ancilla[8]))) seq.append(E(nodes=(ancilla[4], ancilla[11]))) seq.append(E(nodes=(ancilla[4], ancilla[16]))) seq.append(E(nodes=(ancilla[8], ancilla[12]))) seq.append(E(nodes=(ancilla[11], ancilla[15]))) seq.append(E(nodes=(ancilla[12], ancilla[13]))) seq.append(E(nodes=(ancilla[16], ancilla[17]))) seq.append(M(node=target_node)) seq.append(M(node=ancilla[0], s_domain={target_node})) seq.append(M(node=ancilla[1], s_domain={ancilla[0]})) seq.append(M(node=control_node1)) seq.append(M(node=ancilla[2], angle=-1.75, s_domain={ancilla[1], target_node})) seq.append(M(node=ancilla[14], s_domain={control_node1})) seq.append(M(node=ancilla[3], s_domain={ancilla[2], ancilla[0]})) seq.append( M( node=ancilla[5], angle=-0.25, s_domain={ancilla[3], ancilla[1], ancilla[14], target_node}, ) ) seq.append(M(node=control_node2, angle=-0.25)) seq.append(M(node=ancilla[6], s_domain={ancilla[5], ancilla[2], ancilla[0]})) seq.append( M( node=ancilla[9], s_domain={control_node2, ancilla[5], ancilla[2]}, ) ) seq.append( M( node=ancilla[7], angle=-1.75, s_domain={ancilla[6], ancilla[3], ancilla[1], ancilla[14], target_node}, ) ) seq.append(M(node=ancilla[10], angle=-1.75, s_domain={ancilla[9], ancilla[14]})) seq.append(M(node=ancilla[4], angle=-0.25, s_domain={ancilla[14]})) seq.append( M( node=ancilla[8], s_domain={ancilla[7], ancilla[5], ancilla[2], ancilla[0]}, ) ) seq.append( M( node=ancilla[11], s_domain={ancilla[10], control_node2, ancilla[5], ancilla[2]}, ) ) seq.append( M( node=ancilla[12], angle=-0.25, s_domain={ ancilla[8], ancilla[6], ancilla[3], ancilla[1], target_node, }, ) ) seq.append( M( node=ancilla[16], s_domain={ ancilla[4], control_node1, ancilla[2], control_node2, ancilla[7], ancilla[10], ancilla[2], control_node2, ancilla[5], }, ) ) seq.append(X(node=ancilla[17], domain={ancilla[14], ancilla[16]})) seq.append(X(node=ancilla[15], domain={ancilla[9], ancilla[11]})) seq.append( X( node=ancilla[13], domain={ancilla[0], ancilla[2], ancilla[5], ancilla[7], ancilla[12]}, ) ) seq.append( Z( node=ancilla[17], domain={ancilla[4], ancilla[5], ancilla[7], ancilla[10], control_node1}, ) ) seq.append( Z( node=ancilla[15], domain={control_node2, ancilla[2], ancilla[5], ancilla[10]}, ) ) seq.append( Z( node=ancilla[13], domain={ancilla[1], ancilla[3], ancilla[6], ancilla[8], target_node}, ) ) return ancilla[17], ancilla[15], ancilla[13], seq @classmethod def _sort_outputs(cls, pattern: Pattern, output_nodes: Sequence[int]): """Sort the node indices of ouput qubits. Parameters ---------- pattern : :meth:`~graphix.pattern.Pattern` pattern object output_nodes : list of int output node indices Returns ------- out_node : int control node on graph after the gate commands : list list of MBQC commands """ old_out = deepcopy(output_nodes) output_nodes.sort() # check all commands and swap node indices for cmd in pattern: if cmd.kind == CommandKind.E: j, k = cmd.nodes if j in old_out: j = output_nodes[old_out.index(j)] if k in old_out: k = output_nodes[old_out.index(k)] cmd.nodes = (j, k) elif cmd.nodes in old_out: cmd.nodes = output_nodes[old_out.index(cmd.nodes)]
[docs] def simulate_statevector(self, input_state: Data | None = None) -> SimulateResult: """Run statevector simulation of the gate sequence. Parameters ---------- input_state : :class:`graphix.sim.statevec.Statevec` Returns ------- result : :class:`SimulateResult` output state of the statevector simulation and results of classical measures. """ state = Statevec(nqubit=self.width) if input_state is None else Statevec(nqubit=self.width, data=input_state) classical_measures = [] for i in range(len(self.instruction)): instr = self.instruction[i] kind = instr.kind if kind == instruction.InstructionKind.CNOT: state.cnot((instr.control, instr.target)) elif kind == instruction.InstructionKind.SWAP: state.swap(instr.targets) elif kind == instruction.InstructionKind.I: pass elif kind == instruction.InstructionKind.S: state.evolve_single(Ops.S, instr.target) elif kind == instruction.InstructionKind.H: state.evolve_single(Ops.H, instr.target) elif kind == instruction.InstructionKind.X: state.evolve_single(Ops.X, instr.target) elif kind == instruction.InstructionKind.Y: state.evolve_single(Ops.Y, instr.target) elif kind == instruction.InstructionKind.Z: state.evolve_single(Ops.Z, instr.target) elif kind == instruction.InstructionKind.RX: state.evolve_single(Ops.rx(instr.angle), instr.target) elif kind == instruction.InstructionKind.RY: state.evolve_single(Ops.ry(instr.angle), instr.target) elif kind == instruction.InstructionKind.RZ: state.evolve_single(Ops.rz(instr.angle), instr.target) elif kind == instruction.InstructionKind.RZZ: state.evolve(Ops.rzz(instr.angle), [instr.control, instr.target]) elif kind == instruction.InstructionKind.CCX: state.evolve(Ops.CCX, [instr.controls[0], instr.controls[1], instr.target]) elif kind == instruction.InstructionKind.M: result = base_backend.perform_measure(instr.target, instr.plane, instr.angle * np.pi, state, np.random) classical_measures.append(result) else: raise ValueError(f"Unknown instruction: {instr}") return SimulateResult(state, classical_measures)
def map_angle(self, f: Callable[[Angle], Angle]) -> Circuit: """Apply `f` to all angles that occur in the circuit.""" result = Circuit(self.width) for instr in self.instruction: angle = getattr(instr, "angle", None) if angle is None: result.instruction.append(instr) else: new_instr = dataclasses.replace(instr, angle=f(angle)) result.instruction.append(new_instr) return result def subs(self, variable: Parameter, substitute: ExpressionOrSupportsFloat) -> Circuit: """Return a copy of the circuit where all occurrences of the given variable in measurement angles are substituted by the given value.""" return self.map_angle(lambda angle: parameter.subs(angle, variable, substitute)) def xreplace(self, assignment: Mapping[Parameter, ExpressionOrSupportsFloat]) -> Circuit: """Return a copy of the circuit where all occurrences of the given keys in measurement angles are substituted by the given values in parallel.""" return self.map_angle(lambda angle: parameter.xreplace(angle, assignment))
def _extend_domain(measure: M, domain: set[int]) -> None: if measure.plane == Plane.XY: measure.s_domain ^= domain else: measure.t_domain ^= domain