Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
7f0ed9b
implementa gate modules
masa10-f Jun 26, 2025
5ce6c8a
Merge branch 'master' into gates
masa10-f Jul 23, 2025
d8e32e8
add test for gates
masa10-f Jul 23, 2025
db2bf44
WIP: implement circuit
masa10-f Jul 23, 2025
7125aae
update circuit2graph func
masa10-f Jul 23, 2025
a24070a
avoid get_
masato-fuk Jul 24, 2025
bf6fe65
rename
masato-fuk Jul 24, 2025
929c3b2
add doc for circuit and gate
masato-fuk Jul 24, 2025
2322b59
update reference
masato-fuk Jul 24, 2025
259ee5e
add missing backtick
masato-fuk Jul 24, 2025
5038f97
ruff
masato-fuk Jul 24, 2025
f8375c2
add docstring for type alias
masato-fuk Jul 24, 2025
ebf5d55
resolve conflict in link name
masato-fuk Jul 24, 2025
4404ce8
use TypeAlias instead of NewType
masato-fuk Jul 24, 2025
6c16741
fix sphinx setting
masato-fuk Jul 24, 2025
94cfa5e
add missing backslashes
masato-fuk Jul 24, 2025
e621c23
resolve mypy error
masato-fuk Jul 24, 2025
0f7fe3b
ruff
masato-fuk Jul 24, 2025
5ea212e
remove duplicated docstring
masa10-f Jul 25, 2025
20732a6
resolve sphinx error
masa10-f Jul 25, 2025
f306208
remove copy module
masa10-f Jul 25, 2025
a3ca0bf
update type
masa10-f Jul 25, 2025
9f5c103
add macro_gate property
masa10-f Jul 25, 2025
6f3bff6
fix math rendering
masa10-f Jul 25, 2025
5d8f73b
fix missing conversion
masa10-f Jul 25, 2025
4266a93
use np.abs
masa10-f Jul 25, 2025
0172358
resolve pyright information level warnings
masa10-f Jul 28, 2025
50c1e4e
use deepcopy
masa10-f Jul 30, 2025
4261a4c
fix redundant error message
masa10-f Jul 30, 2025
a7095ba
use asarray instead of type assertion
masa10-f Jul 30, 2025
f997a3a
iterator unpacking
masa10-f Jul 30, 2025
0a651de
fix type error
masa10-f Jul 30, 2025
8de9446
use itertools.chain
masa10-f Jul 30, 2025
c4f3e19
use from_iterable method
masa10-f Jul 31, 2025
29024b3
add circuit test
masa10-f Jul 31, 2025
81af2f8
patch for py39
masa10-f Jul 31, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions docs/source/circuit.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Quantum Circuit
===============


Circuit
-------

.. automodule:: graphix_zx.circuit
:members:
:undoc-members:
:show-inheritance:

Gate
----

.. automodule:: graphix_zx.gates
:members:
4 changes: 2 additions & 2 deletions docs/source/matrix.rst → docs/source/matrix_utility.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Matrix
======
Matrix Utilities
================

:mod:`graphix_zx.matrix` module
+++++++++++++++++++++++++++++++
Expand Down
3 changes: 2 additions & 1 deletion docs/source/references.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ Module reference
:maxdepth: 2

common
circuit
simulator_backend
statevec
euler
matrix
matrix_utility
graphstate
random_objects
feedforward
Expand Down
244 changes: 244 additions & 0 deletions graphix_zx/circuit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
"""Circuit classes for encoding quantum operations.

This module provides:

- `BaseCircuit`: An abstract base class for quantum circuits.
- `MBQCCircuit`: A circuit class composed solely of a unit gate set.
- `Circuit`: A class for circuits that include macro instructions.
- `circuit2graph`: A function that converts a circuit to a graph state and gflow.
"""

from __future__ import annotations

import copy
import itertools
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING

from graphix_zx.common import Plane, PlannerMeasBasis
from graphix_zx.gates import CZ, Gate, J, PhaseGadget, UnitGate
from graphix_zx.graphstate import GraphState

if TYPE_CHECKING:
from collections.abc import Sequence


class BaseCircuit(ABC):
"""
Abstract base class for quantum circuits.

This class defines the interface for quantum circuit objects.
It enforces implementation of core methods that must be present
in any subclass representing a specific type of quantum circuit.
"""

@property
@abstractmethod
def num_qubits(self) -> int:
"""Get the number of qubits in the circuit.

Returns
-------
`int`
The number of qubits in the circuit
"""
raise NotImplementedError

@abstractmethod
def instructions(self) -> list[UnitGate]:
r"""Get the list of instructions in the circuit.

Returns
-------
`list`\[`UnitGate`\]
List of unit instructions in the circuit.
"""
raise NotImplementedError


class MBQCCircuit(BaseCircuit):
"""A circuit class composed solely of a unit gate set."""

__num_qubits: int
__gate_instructions: list[UnitGate]

def __init__(self, num_qubits: int) -> None:
self.__num_qubits = num_qubits
self.__gate_instructions = []

@property
def num_qubits(self) -> int:
"""Get the number of qubits in the circuit.

Returns
-------
`int`
The number of qubits in the circuit.
"""
return self.__num_qubits

def instructions(self) -> list[UnitGate]:
r"""Get the list of instructions in the circuit.

Returns
-------
`list`\[`UnitGate`\]
List of unit instructions in the circuit.
"""
return list(self.__gate_instructions)

def j(self, qubit: int, angle: float) -> None:
"""Add a J gate to the circuit.

Parameters
----------
qubit : `int`
The qubit index.
angle : `float`
The angle of the J gate.
"""
self.__gate_instructions.append(J(qubit=qubit, angle=angle))

def cz(self, qubit1: int, qubit2: int) -> None:
"""Add a CZ gate to the circuit.

Parameters
----------
qubit1 : `int`
The first qubit index.
qubit2 : `int`
The second qubit index.
"""
self.__gate_instructions.append(CZ(qubits=(qubit1, qubit2)))

def phase_gadget(self, qubits: Sequence[int], angle: float) -> None:
r"""Add a phase gadget to the circuit.

Parameters
----------
qubits : `collections.abc.Sequence`\[`int`\]
The qubit indices.
angle : `float`
The angle of the phase gadget
"""
self.__gate_instructions.append(PhaseGadget(qubits=list(qubits), angle=angle))


class Circuit(BaseCircuit):
"""A class for circuits that include macro instructions."""

__num_qubits: int
__macro_gate_instructions: list[Gate]

def __init__(self, num_qubits: int) -> None:
self.__num_qubits = num_qubits
self.__macro_gate_instructions = []

@property
def num_qubits(self) -> int:
"""Get the number of qubits in the circuit.

Returns
-------
`int`
The number of qubits in the circuit.
"""
return self.__num_qubits

@property
def macro_gate_instructions(self) -> list[Gate]:
r"""Get the list of macro gate instructions in the circuit.

Returns
-------
`list`\[`Gate`\]
The list of macro gate instructions in the circuit.
"""
return copy.deepcopy(self.__macro_gate_instructions)

def instructions(self) -> list[UnitGate]:
r"""Get the list of instructions in the circuit.

Returns
-------
`list`\[`UnitGate`\]
The list of unit instructions in the circuit.
"""
return list(
itertools.chain.from_iterable(macro_gate.unit_gates() for macro_gate in self.__macro_gate_instructions)
)

def apply_macro_gate(self, gate: Gate) -> None:
"""Apply a macro gate to the circuit.

Parameters
----------
gate : `Gate`
The macro gate to apply.
"""
self.__macro_gate_instructions.append(gate)


def circuit2graph(circuit: BaseCircuit) -> tuple[GraphState, dict[int, set[int]]]:
r"""Convert a circuit to a graph state and gflow.

Parameters
----------
circuit : `BaseCircuit`
The quantum circuit to convert.

Returns
-------
`tuple`\[`GraphState`, `dict`\[`int`, `set`\[`int`\]\]\]
The graph state and gflow converted from the circuit.

Raises
------
TypeError
If the circuit contains an invalid instruction.
"""
graph = GraphState()
gflow: dict[int, set[int]] = {}

qindex2front_nodes: dict[int, int] = {}
qid_ex2in: dict[int, int] = {}

# input nodes
for i in range(circuit.num_qubits):
node = graph.add_physical_node()
qindex = graph.register_input(node)
qindex2front_nodes[qindex] = node
qid_ex2in[i] = qindex

for instruction in circuit.instructions():
if isinstance(instruction, J):
new_node = graph.add_physical_node()
graph.add_physical_edge(qindex2front_nodes[qid_ex2in[instruction.qubit]], new_node)
graph.assign_meas_basis(
qindex2front_nodes[qid_ex2in[instruction.qubit]],
PlannerMeasBasis(Plane.XY, -instruction.angle),
)

gflow[qindex2front_nodes[qid_ex2in[instruction.qubit]]] = {new_node}
qindex2front_nodes[qid_ex2in[instruction.qubit]] = new_node

elif isinstance(instruction, CZ):
graph.add_physical_edge(
qindex2front_nodes[qid_ex2in[instruction.qubits[0]]],
qindex2front_nodes[qid_ex2in[instruction.qubits[1]]],
)
elif isinstance(instruction, PhaseGadget):
new_node = graph.add_physical_node()
graph.assign_meas_basis(new_node, PlannerMeasBasis(Plane.YZ, instruction.angle))
for qubit in instruction.qubits:
graph.add_physical_edge(qindex2front_nodes[qid_ex2in[qubit]], new_node)

gflow[new_node] = {new_node}
else:
msg = f"Invalid instruction: {instruction}"
raise TypeError(msg)

for qindex, node in qindex2front_nodes.items():
graph.register_output(node, qindex)

return graph, gflow
Loading
Loading