Pattern Simulation

graphix.simulator module

class graphix.simulator.PatternSimulator(pattern: Pattern, backend: Backend[_StateT_co] | _BackendLiteral = 'statevector', prepare_method: PrepareMethod | None = None, measure_method: MeasureMethod | None = None, noise_model: NoiseModel | None = None, branch_selector: BranchSelector | None = None, graph_prep: str | None = None, symbolic: bool = False)[source]

MBQC simulator.

Executes the measurement pattern.

__init__(pattern: Pattern, backend: Backend[_StateT_co] | _BackendLiteral = 'statevector', prepare_method: PrepareMethod | None = None, measure_method: MeasureMethod | None = None, noise_model: NoiseModel | None = None, branch_selector: BranchSelector | None = None, graph_prep: str | None = None, symbolic: bool = False) None[source]

Construct a pattern simulator.

Parameters:
  • pattern (Pattern object) – MBQC pattern to be simulated.

  • backend (Backend object,) – or ‘statevector’, or ‘densitymatrix’, or ‘tensornetwork’ simulation backend (optional), default is ‘statevector’.

  • prepare_method (PrepareMethod, optional) – Prepare method used by the simulator. Default is DefaultPrepareMethod.

  • measure_method (MeasureMethod, optional) – Measure method used by the simulator. Default is DefaultMeasureMethod.

  • noise_model (NoiseModel, optional) – [Density matrix backend only] Noise model used by the simulator.

  • branch_selector (BranchSelector, optional) – Branch selector used for measurements. Can only be specified if backend is not an already instantiated Backend object. Default is RandomBranchSelector.

  • graph_prep (str, optional) – [Tensor network backend only] Strategy for preparing the graph state. See TensorNetworkBackend.

  • symbolic (bool, optional) – [State vector and density matrix backends only] If True, support arbitrary objects (typically, symbolic expressions) in measurement angles.

  • seealso: (..) – graphix.sim.statevec.StatevectorBackend graphix.sim.tensornet.TensorNetworkBackend graphix.sim.density_matrix.DensityMatrixBackend:

run(input_state: Data = graphix.states.PlanarState(Plane.XY, 0), rng: Generator | None = None) None[source]

Perform the simulation.

Returns:

  • input_state (Data, optional) – the output quantum state, in the representation depending on the backend used. Default: |+>.

  • rng (Generator, optional) – Random-number generator for measurements. This generator is used only in case of random branch selection (see RandomBranchSelector).

Simulator backends

Tensor Network

class graphix.sim.tensornet.TensorNetworkBackend(pattern: Pattern, graph_prep: str = 'auto', input_state: Data | None = None, branch_selector: BranchSelector | None = None)[source]

Tensor Network Simulator for MBQC.

Executes the measurement pattern using TN expression of graph states.

Parameters:
  • pattern (graphix.Pattern)

  • graph_prep (str) –

    ‘parallel’ :

    Faster method for preparing a graph state. The expression of a graph state can be obtained from the graph geometry. See https://journals.aps.org/pra/abstract/10.1103/PhysRevA.76.052315 for detail calculation. Note that ‘N’ and ‘E’ commands in the measurement pattern are ignored.

    ’sequential’ :

    Sequentially execute N and E commands, strictly following the measurement pattern. In this strategy, All N and E commands executed sequentially.

    ’auto’(default) :

    Automatically select a preparation strategy based on the max degree of a graph

  • input_state (preparation for input states (only BasicStates.PLUS is supported for tensor networks yet),)

  • branch_selector (graphix.branch_selector.BranchSelector, optional) – Branch selector to be used for measurements.

__init__(pattern: Pattern, graph_prep: str = 'auto', input_state: Data | None = None, branch_selector: BranchSelector | None = None) None[source]

Construct a tensor network backend.

add_nodes(nodes: Sequence[int], data: Data = graphix.states.PlanarState(Plane.XY, 0)) None[source]

Add new nodes (qubits) to the network and initialize them in a specified state.

Parameters:
  • nodes (Sequence[int]) – A list of node indices to add to the backend. These indices can be any integer values but must be fresh: each index must be distinct from all previously added nodes.

  • data (Data, optional) –

    The state in which to initialize the newly added nodes.

    • If a single basic state is provided, all new nodes are initialized in that state.

    • If a list of basic states is provided, it must match the length of nodes, and each node is initialized with its corresponding state.

Notes

Previously existing nodes remain unchanged.

apply_clifford(node: int, clifford: Clifford) None[source]

Apply single-qubit Clifford gate.

Parameters:

cmd (list) – clifford command. See https://arxiv.org/pdf/2212.11975.pdf for the detail.

correct_byproduct(cmd: command.X | command.Z, measure_method: MeasureMethod) None[source]

Perform byproduct correction.

Parameters:
  • cmd (list) – Byproduct command i.e. [‘X’ or ‘Z’, node, signal_domain]

  • measure_method (MeasureMethod) – The measure method to use

entangle_nodes(edge: tuple[int, int]) None[source]

Make entanglement between nodes specified by edge.

Parameters:

edge (tuple of int) – edge specifies two target nodes of the CZ gate.

finalize(output_nodes: Iterable[int]) None[source]

Do nothing.

measure(node: int, measurement: Measurement, rng: Generator | None = None) Outcome[source]

Perform measurement of the node.

In the context of tensornetwork, performing measurement equals to applying measurement operator to the tensor. Here, directly contracted with the projected state.

Parameters:
  • node (int) – index of the node to measure

  • measurement (Measurement) – measure plane and angle

graphix.sim.tensornet.gen_str() str[source]

Generate dummy string for einsum.

graphix.sim.tensornet.outer_product(vectors: Sequence[npt.NDArray[np.complex128]]) npt.NDArray[np.complex128][source]

Return the outer product of the given vectors.

Parameters:

vectors (list of vector) – vectors

Returns:

tensor object.

Return type:

numpy.ndarray

Statevector

class graphix.sim.statevec.StatevectorBackend(node_index: NodeIndex = <factory>, branch_selector: BranchSelector = <factory>, symbolic: bool = False)[source]

MBQC simulator with statevector method.

__init__(node_index: NodeIndex = <factory>, branch_selector: BranchSelector = <factory>, symbolic: bool = False) None
class graphix.sim.statevec.Statevec(data: Data = graphix.states.PlanarState(Plane.XY, 0), nqubit: int | None = None)[source]

Statevector object.

__init__(data: Data = graphix.states.PlanarState(Plane.XY, 0), nqubit: int | None = None) None[source]

Initialize statevector objects.

Pattern data structure can be: - a single graphix.states.State (classical description of a quantum state) - an iterable of graphix.states.State objects - an iterable of scalars (A 2**n numerical statevector) - a graphix.statevec.Statevec object

If nqubit is not provided, the number of qubit is inferred from data and checked for consistency. If only one graphix.states.State is provided and nqubit is a valid integer, initialize the statevector in the tensor product state. If both nqubit and data are provided, consistency of the dimensions is checked. If a graphix.statevec.Statevec is passed, returns a copy.

Parameters:
  • data (Data, optional) – input data to prepare the state. Can be a classical description or a numerical input, defaults to graphix.states.BasicStates.PLUS

  • nqubit (int, optional) – number of qubits to prepare, defaults to None

add_nodes(nqubit: int, data: Data) None[source]

Add nodes (qubits) to the state vector and initialize them in a specified state.

Parameters:
  • nqubit (int) – The number of qubits to add to the state vector.

  • data (Data, optional) –

    The state in which to initialize the newly added nodes.

    • If a single basic state is provided, all new nodes are initialized in that state.

    • If a list of basic states is provided, it must match the length of nodes, and each node is initialized with its corresponding state.

    • A single-qubit state vector will be broadcast to all nodes.

    • A multi-qubit state vector of dimension \(2^n\), where \(n = \mathrm{len}(nodes)\), initializes the new nodes jointly.

Notes

Previously existing nodes remain unchanged.

cnot(qubits: tuple[int, int]) None[source]

Apply CNOT.

Parameters:

qubits (tuple of int) – (control, target) qubit indices

dims() tuple[int, ...][source]

Return the dimensions.

entangle(edge: tuple[int, int]) None[source]

Connect graph nodes.

Parameters:

edge (tuple of int) – (control, target) qubit indices

evolve(op: Matrix, qargs: Sequence[int]) None[source]

Apply a multi-qubit operation.

Parameters:
  • op (numpy.ndarray) – 2^n*2^n matrix

  • qargs (list of int) – target qubits’ indices

evolve_single(op: ndarray[tuple[Any, ...], dtype[object_ | complex128]], i: int) None[source]

Apply a single-qubit operation.

Parameters:
  • op (numpy.ndarray) – 2*2 matrix

  • i (int) – qubit index

expectation_single(op: ndarray[tuple[Any, ...], dtype[object_ | complex128]], loc: int) complex[source]

Return the expectation value of single-qubit operator.

Parameters:
  • op (numpy.ndarray) – 2*2 operator

  • loc (int) – target qubit index

Returns:

complex

Return type:

expectation value.

expectation_value(op: Matrix, qargs: Sequence[int]) complex[source]

Return the expectation value of multi-qubit operator.

Parameters:
  • op (numpy.ndarray) – 2^n*2^n operator

  • qargs (list of int) – target qubit indices

Returns:

complex

Return type:

expectation value

flatten() ndarray[tuple[Any, ...], dtype[object_ | complex128]][source]

Return flattened statevector.

normalize() None[source]

Normalize the state in-place.

property nqubit: int

Return the number of qubits.

remove_qubit(qarg: int) None[source]

Remove a separable qubit from the system and assemble a statevector for remaining qubits.

This results in the same result as partial trace, if the qubit qarg is separable from the rest.

For a statevector \(\ket{\psi} = \sum c_i \ket{i}\) with sum taken over \(i \in [ 0 \dots 00,\ 0\dots 01,\ \dots,\ 1 \dots 11 ]\), this method returns

\[\begin{split}\begin{align} \ket{\psi}' =& c_{0 \dots 0_{\mathrm{k-1}}0_{\mathrm{k}}0_{\mathrm{k+1}} \dots 00} \ket{0 \dots 0_{\mathrm{k-1}}0_{\mathrm{k+1}} \dots 00} \\ & + c_{0 \dots 0_{\mathrm{k-1}}0_{\mathrm{k}}0_{\mathrm{k+1}} \dots 01} \ket{0 \dots 0_{\mathrm{k-1}}0_{\mathrm{k+1}} \dots 01} \\ & + c_{0 \dots 0_{\mathrm{k-1}}0_{\mathrm{k}}0_{\mathrm{k+1}} \dots 10} \ket{0 \dots 0_{\mathrm{k-1}}0_{\mathrm{k+1}} \dots 10} \\ & + \dots \\ & + c_{1 \dots 1_{\mathrm{k-1}}0_{\mathrm{k}}1_{\mathrm{k+1}} \dots 11} \ket{1 \dots 1_{\mathrm{k-1}}1_{\mathrm{k+1}} \dots 11}, \end{align}\end{split}\]

(after normalization) for \(k =\) qarg. If the \(k\) th qubit is in \(\ket{1}\) state, above will return zero amplitudes; in such a case the returned state will be the one above with \(0_{\mathrm{k}}\) replaced with \(1_{\mathrm{k}}\) .

Warning

This method assumes the qubit with index qarg to be separable from the rest, and is implemented as a significantly faster alternative for partial trace to be used after single-qubit measurements. Care needs to be taken when using this method. Checks for separability will be implemented soon as an option.

Parameters:

qarg (int) – qubit index

subs(variable: Parameter, substitute: ExpressionOrSupportsFloat) Statevec[source]

Return a copy of the state vector where all occurrences of the given variable in measurement angles are substituted by the given value.

swap(qubits: tuple[int, int]) None[source]

Swap qubits.

Parameters:

qubits (tuple of int) – (control, target) qubit indices

tensor(other: Statevec) None[source]

Tensor product state with other qubits.

Results in self \(\otimes\) other.

Parameters:

other (graphix.sim.statevec.Statevec) – statevector to be tensored with self

xreplace(assignment: Mapping[Parameter, ExpressionOrSupportsFloat]) Statevec[source]

Return a copy of the state vector where all occurrences of the given keys in measurement angles are substituted by the given values in parallel.

Density Matrix

class graphix.sim.density_matrix.DensityMatrixBackend(node_index: NodeIndex = <factory>, branch_selector: BranchSelector = <factory>, symbolic: bool = False)[source]

MBQC simulator with density matrix method.

__init__(node_index: NodeIndex = <factory>, branch_selector: BranchSelector = <factory>, symbolic: bool = False) None
class graphix.sim.density_matrix.DensityMatrix(data: Data = graphix.states.PlanarState(Plane.XY, 0), nqubit: int | None = None)[source]

DensityMatrix object.

__init__(data: Data = graphix.states.PlanarState(Plane.XY, 0), nqubit: int | None = None) None[source]

Initialize density matrix objects.

The behaviour builds on the one of graphix.statevec.Statevec. Pattern data structure can be: - a single graphix.states.State (classical description of a quantum state) - an iterable of graphix.states.State objects - an iterable of iterable of scalars (A 2**n x 2**n numerical density matrix) - a graphix.statevec.DensityMatrix object - a graphix.statevec.Statevector object

If nqubit is not provided, the number of qubit is inferred from Pattern data structure and checked for consistency. If only one graphix.states.State is provided and nqubit is a valid integer, initialize the statevector in the tensor product state. If both nqubit and Pattern data structure are provided, consistency of the dimensions is checked. If a graphix.statevec.Statevec or graphix.statevec.DensityMatrix is passed, returns a copy.

Parameters:
  • data (Data) – input data to prepare the state. Can be a classical description or a numerical input, defaults to graphix.states.BasicStates.PLUS

  • nqubit (int, optional) – number of qubits to prepare, defaults to None

add_nodes(nqubit: int, data: Data) None[source]

Add nodes (qubits) to the density matrix and initialize them in a specified state.

Parameters:
  • nqubit (int) – The number of qubits to add to the density matrix.

  • data (Data, optional) –

    The state in which to initialize the newly added nodes.

    • If a single basic state is provided, all new nodes are initialized in that state.

    • If a list of basic states is provided, it must match the length of nodes, and each node is initialized with its corresponding state.

    • A single-qubit state vector will be broadcast to all nodes.

    • A multi-qubit state vector of dimension \(2^n\) initializes the new nodes jointly.

    • A density matrix must have shape \(2^n \times 2^n\), and is used to jointly initialize the new nodes.

Notes

Previously existing nodes remain unchanged.

apply_channel(channel: KrausChannel, qargs: Sequence[int]) None[source]

Apply a channel to a density matrix.

:param : :type : rho: density matrix. :param channel: KrausChannel to be applied to the density matrix :type channel: graphix.channel.KrausChannel object :param qargs: :type qargs: target qubit indices

Return type:

nothing

Raises:
  • ValueError – If the final density matrix is not normalized after application of the channel. This shouldn’t happen since graphix.channel.KrausChannel objects are normalized by construction.

  • ....

apply_noise(qubits: Sequence[int], noise: Noise) None[source]

Apply noise.

Parameters:
  • qubits (sequence of ints.) – Target qubits

  • noise (Noise) – Noise to apply

cnot(edge: tuple[int, int]) None[source]

Apply CNOT gate to density matrix.

Parameters:

edge ((int, int) or [int, int]) – Edge to apply CNOT gate.

dims() tuple[int, ...][source]

Return the dimensions of the density matrix.

entangle(edge: tuple[int, int]) None[source]

Connect graph nodes.

Parameters:

edge ((int, int) or [int, int]) – (control, target) qubit indices.

evolve(op: Matrix, qargs: Sequence[int]) None[source]

Multi-qubit operation.

Parameters:
  • op (np.array) – 2^n*2^n matrix

  • qargs (list of ints) – target qubits’ indexes

evolve_single(op: ndarray[tuple[Any, ...], dtype[object_ | complex128]], i: int) None[source]

Single-qubit operation.

Parameters:
  • op (np.ndarray) – 2*2 matrix.

  • i (int) – Index of qubit to apply operator.

expectation_single(op: ndarray[tuple[Any, ...], dtype[object_ | complex128]], loc: int) complex[source]

Return the expectation value of single-qubit operator.

Parameters:
  • op (np.array) – 2*2 Hermite operator

  • loc (int) – Index of qubit on which to apply operator.

Returns:

complex

Return type:

expectation value (real for hermitian ops!).

fidelity(statevec: Statevec) Expression | float[source]

Calculate the fidelity against reference statevector.

Parameters:

statevec (numpy array) – statevector (flattened numpy array) to compare with

flatten() ndarray[tuple[Any, ...], dtype[object_ | complex128]][source]

Return flattened density matrix.

normalize() None[source]

Normalize density matrix.

property nqubit: int

Return the number of qubits.

ptrace(qargs: Collection[int] | int) None[source]

Partial trace.

Parameters:

qargs (list of ints or int) – Indices of qubit to trace out.

remove_qubit(qarg: int) None[source]

Remove a qubit.

subs(variable: Parameter, substitute: ExpressionOrSupportsFloat) DensityMatrix[source]

Return a copy of the density matrix where all occurrences of the given variable in measurement angles are substituted by the given value.

swap(qubits: tuple[int, int]) None[source]

Swap qubits.

Parameters:

qubits ((int, int)) – (control, target) qubits indices.

tensor(other: DensityMatrix) None[source]

Tensor product state with other density matrix.

Results in self \(\otimes\) other.

Parameters:

other – DensityMatrix object to be tensored with self.

xreplace(assignment: Mapping[Parameter, ExpressionOrSupportsFloat]) DensityMatrix[source]

Return a copy of the density matrix where all occurrences of the given keys in measurement angles are substituted by the given values in parallel.

Branch Selection: graphix.branch_selector module

Abstract Branch Selector

class graphix.branch_selector.BranchSelector[source]

Abstract class for branch selectors.

A branch selector provides the method measure, which returns the measurement outcome (0 or 1) for a given qubit.

abstractmethod measure(qubit: int, f_expectation0: Callable[[], float], rng: Generator | None = None) Outcome[source]

Return the measurement outcome of qubit.

Parameters:
  • qubit (int) – Index of qubit to measure

  • f_expectation0 (Callable[[], float]) – A function that the method can use to retrieve the expected probability of outcome 0. The probability is computed only if this function is called (lazy computation), ensuring no unnecessary computational cost.

  • rng (Generator, optional) – Random-number generator for measurements. This generator is used only in case of random branch selection (see RandomBranchSelector). If None, a default random-number generator is used. Default is None.

Random Branch Selector

class graphix.branch_selector.RandomBranchSelector(pr_calc: bool = True)[source]

Random branch selector.

Parameters:

pr_calc (bool, optional) – Whether to compute the probability distribution before selecting the measurement result. If False, measurements yield 0/1 with equal probability (50% each). Default is True.

__init__(pr_calc: bool = True) None
measure(qubit: int, f_expectation0: Callable[[], float], rng: Generator | None = None) Outcome[source]

Return the measurement outcome of qubit.

If pr_calc is True, the measurement outcome is determined based on the computed probability of outcome 0. Otherwise, the result is randomly chosen with a 50% chance for either outcome.

Fixed Branch Selector

class graphix.branch_selector.FixedBranchSelector(results: _T, default: BranchSelector | None = None)[source]

Branch selector with predefined measurement outcomes.

The mapping is fixed in results. By default, an error is raised if a qubit is measured without a predefined outcome. However, another branch selector can be specified in default to handle such cases.

Parameters:
  • results (Mapping[int, bool]) – A dictionary mapping qubits to their measurement outcomes. If a qubit is not present in this mapping, the default branch selector is used.

  • default (BranchSelector | None, optional) – Branch selector to use for qubits not present in results. If None, an error is raised when an unmapped qubit is measured. Default is None.

__init__(results: _T, default: BranchSelector | None = None) None
measure(qubit: int, f_expectation0: Callable[[], float], rng: Generator | None = None) Outcome[source]

Return the predefined measurement outcome of qubit, if available.

If the qubit is not present in results, the default branch selector is used. If no default is provided, an error is raised.

Constant Branch Selector

class graphix.branch_selector.ConstBranchSelector(result: Literal[0, 1])[source]

Branch selector with a constant measurement outcome.

The value result is returned for every qubit.

Parameters:

result (Outcome) – The fixed measurement outcome for all qubits.

__init__(result: Literal[0, 1]) None
measure(qubit: int, f_expectation0: Callable[[], float], rng: Generator | None = None) Outcome[source]

Return the constant measurement outcome result for any qubit.