diff --git a/include/mqt-core/Permutation.hpp b/include/mqt-core/Permutation.hpp index e90c1f344..1f644f257 100644 --- a/include/mqt-core/Permutation.hpp +++ b/include/mqt-core/Permutation.hpp @@ -3,6 +3,8 @@ #include "Definitions.hpp" #include "operations/Control.hpp" +#include +#include #include namespace qc { @@ -30,3 +32,17 @@ class Permutation : public std::map { } }; } // namespace qc + +// define hash function for Permutation +namespace std { +template <> struct hash { + std::size_t operator()(const qc::Permutation& p) const { + std::size_t seed = 0; + for (const auto& [k, v] : p) { + qc::hashCombine(seed, k); + qc::hashCombine(seed, v); + } + return seed; + } +}; +} // namespace std diff --git a/include/mqt-core/operations/Control.hpp b/include/mqt-core/operations/Control.hpp index 72cb3472a..85e6b8ece 100644 --- a/include/mqt-core/operations/Control.hpp +++ b/include/mqt-core/operations/Control.hpp @@ -2,8 +2,11 @@ #include "Definitions.hpp" +#include +#include #include #include +#include namespace qc { struct Control { @@ -73,3 +76,12 @@ inline Control operator""_nc(unsigned long long int q) { } } // namespace literals } // namespace qc + +namespace std { +template <> struct hash { + std::size_t operator()(const qc::Control& c) const { + return std::hash{}(c.qubit) ^ + std::hash{}(c.type); + } +}; +} // namespace std diff --git a/src/mqt/core/_core/__init__.pyi b/src/mqt/core/_core/__init__.pyi index d54981b2b..6ef5aba88 100644 --- a/src/mqt/core/_core/__init__.pyi +++ b/src/mqt/core/_core/__init__.pyi @@ -7,7 +7,16 @@ from .operations import Control, Operation, OpType from .symbolic import Expression, Variable class Permutation(MutableMapping[int, int]): - """A class to represent a permutation of the qubits in a quantum circuit.""" + """A class to represent a permutation of the qubits in a quantum circuit. + + Args: + permutation: The permutation to initialize the object with. + + """ + + def __init__(self: Self, permutation: dict[int, int] | None = None) -> None: + """Initialize the permutation.""" + def __getitem__(self: Self, idx: int) -> int: """Get the value of the permutation at the given index. @@ -17,6 +26,7 @@ class Permutation(MutableMapping[int, int]): Returns: The value of the permutation at the given index. """ + def __setitem__(self: Self, idx: int, val: int) -> None: """Set the value of the permutation at the given index. @@ -24,16 +34,29 @@ class Permutation(MutableMapping[int, int]): idx: The index to set the value of the permutation at. val: The value to set the permutation at the given index to. """ + def __delitem__(self: Self, key: int) -> None: """Delete the value of the permutation at the given index. Args: key: The index to delete the value of the permutation at. """ + def __iter__(self: Self) -> Iterator[int]: """Return an iterator over the indices of the permutation.""" + def __len__(self: Self) -> int: """Return the number of indices in the permutation.""" + + def __eq__(self: Self, other: object) -> bool: + """Check if the permutation is equal to another permutation.""" + + def __ne__(self: Self, other: object) -> bool: + """Check if the permutation is not equal to another permutation.""" + + def __hash__(self: Self) -> int: + """Return the hash of the permutation.""" + @overload def apply(self: Self, controls: set[Control]) -> set[Control]: """Apply the permutation to a set of controls. @@ -44,6 +67,7 @@ class Permutation(MutableMapping[int, int]): Returns: The set of controls with the permutation applied. """ + @overload def apply(self: Self, targets: list[int]) -> list[int]: """Apply the permutation to a list of targets. @@ -102,6 +126,7 @@ class QuantumComputation(MutableSequence[Operation]): @property def num_qubits(self: Self) -> int: """The total number of qubits in the quantum computation.""" + @property def num_ancilla_qubits(self: Self) -> int: r"""The number of ancilla qubits in the quantum computation. @@ -109,6 +134,7 @@ class QuantumComputation(MutableSequence[Operation]): Note: Ancilla qubits are qubits that always start in a fixed state (usually :math:`|0\\rangle`). """ + @property def num_garbage_qubits(self: Self) -> int: """The number of garbage qubits in the quantum computation. @@ -116,35 +142,44 @@ class QuantumComputation(MutableSequence[Operation]): Note: Garbage qubits are qubits whose final state is not relevant for the computation. """ + @property def num_measured_qubits(self: Self) -> int: """The number of qubits that are measured in the quantum computation. Computed as :math:`|qubits| - |garbage|`. """ + @property def num_data_qubits(self: Self) -> int: """The number of data qubits in the quantum computation. Computed as :math:`|qubits| - |ancilla|`. """ + @property def num_classical_bits(self: Self) -> int: """The number of classical bits in the quantum computation.""" + @property def num_ops(self: Self) -> int: """The number of operations in the quantum computation.""" + def num_single_qubit_ops(self: Self) -> int: """Return the number of single-qubit operations in the quantum computation.""" + def num_total_ops(self: Self) -> int: """Return the total number of operations in the quantum computation. Recursively counts sub-operations (e.g., from :class:`~mqt.core.operations.CompoundOperation` objects). """ + def depth(self: Self) -> int: """Return the depth of the quantum computation.""" + def invert(self: Self) -> None: """Invert the quantum computation (in-place) by inverting each operation and reversing the order of the operations.""" + def to_operation(self: Self) -> Operation: """Convert the quantum computation to a single operation. @@ -162,6 +197,7 @@ class QuantumComputation(MutableSequence[Operation]): def __len__(self: Self) -> int: """Return the number of operations in the quantum computation.""" + @overload def __getitem__(self: Self, idx: int) -> Operation: """Get the operation at the given index. @@ -175,6 +211,7 @@ class QuantumComputation(MutableSequence[Operation]): Returns: The operation at the given index. """ + @overload def __getitem__(self: Self, idx: slice) -> list[Operation]: """Get a slice of operations from the quantum computation. @@ -188,6 +225,7 @@ class QuantumComputation(MutableSequence[Operation]): Returns: The operations in the given slice. """ + @overload def __setitem__(self: Self, idx: int, op: Operation) -> None: """Set the operation at the given index. @@ -196,6 +234,7 @@ class QuantumComputation(MutableSequence[Operation]): idx: The index of the operation to set. op: The operation to set at the given index. """ + @overload def __setitem__(self: Self, idx: slice, ops: Iterable[Operation]) -> None: """Set the operations in the given slice. @@ -204,6 +243,7 @@ class QuantumComputation(MutableSequence[Operation]): idx: The slice of operations to set. ops: The operations to set in the given slice. """ + @overload def __delitem__(self: Self, idx: int) -> None: """Delete the operation at the given index. @@ -211,6 +251,7 @@ class QuantumComputation(MutableSequence[Operation]): Args: idx: The index of the operation to delete. """ + @overload def __delitem__(self: Self, idx: slice) -> None: """Delete the operations in the given slice. @@ -218,6 +259,7 @@ class QuantumComputation(MutableSequence[Operation]): Args: idx: The slice of operations to delete. """ + def insert(self: Self, idx: int, op: Operation) -> None: """Insert an operation at the given index. @@ -225,14 +267,17 @@ class QuantumComputation(MutableSequence[Operation]): idx: The index to insert the operation at. op: The operation to insert. """ + def append(self: Self, op: Operation) -> None: """Append an operation to the end of the quantum computation. Args: op: The operation to append. """ + def reverse(self: Self) -> None: """Reverse the order of the operations in the quantum computation (in-place).""" + def clear(self: Self) -> None: """Clear the quantum computation of all operations.""" @@ -247,6 +292,7 @@ class QuantumComputation(MutableSequence[Operation]): n: The number of qubits in the ancillary register. name: The name of the ancillary register. """ + def add_classical_register(self: Self, n: int, name: str = "c") -> None: """Add a classical register to the quantum computation. @@ -254,6 +300,7 @@ class QuantumComputation(MutableSequence[Operation]): n: The number of bits in the classical register. name: The name of the classical register. """ + def add_qubit_register(self: Self, n: int, name: str = "anc") -> None: """Add a qubit register to the quantum computation. @@ -261,6 +308,7 @@ class QuantumComputation(MutableSequence[Operation]): n: The number of qubits in the qubit register. name: The name of the qubit register. """ + def unify_quantum_registers(self: Self, name: str = "q") -> None: """Unify all quantum registers in the quantum computation. @@ -335,12 +383,14 @@ class QuantumComputation(MutableSequence[Operation]): @property def ancillary(self: Self) -> list[bool]: """A list of booleans indicating whether each qubit is ancillary.""" + def set_circuit_qubit_ancillary(self: Self, q: int) -> None: """Set a circuit (i.e., logical) qubit to be ancillary. Args: q: The index of the circuit qubit to set as ancillary. """ + def set_circuit_qubits_ancillary(self: Self, q_min: int, q_max: int) -> None: """Set a range of circuit (i.e., logical) qubits to be ancillary. @@ -348,6 +398,7 @@ class QuantumComputation(MutableSequence[Operation]): q_min: The minimum index of the circuit qubits to set as ancillary. q_max: The maximum index of the circuit qubits to set as ancillary. """ + def is_circuit_qubit_ancillary(self: Self, q: int) -> bool: """Check if a circuit (i.e., logical) qubit is ancillary. @@ -357,15 +408,18 @@ class QuantumComputation(MutableSequence[Operation]): Returns: True if the circuit qubit is ancillary, False otherwise. """ + @property def garbage(self: Self) -> list[bool]: """A list of booleans indicating whether each qubit is garbage.""" + def set_circuit_qubit_garbage(self: Self, q: int) -> None: """Set a circuit (i.e., logical) qubit to be garbage. Args: q: The index of the circuit qubit to set as garbage. """ + def set_circuit_qubits_garbage(self: Self, q_min: int, q_max: int) -> None: """Set a range of circuit (i.e., logical) qubits to be garbage. @@ -373,6 +427,7 @@ class QuantumComputation(MutableSequence[Operation]): q_min: The minimum index of the circuit qubits to set as garbage. q_max: The maximum index of the circuit qubits to set as garbage. """ + def is_circuit_qubit_garbage(self: Self, q: int) -> bool: """Check if a circuit (i.e., logical) qubit is garbage. @@ -390,24 +445,28 @@ class QuantumComputation(MutableSequence[Operation]): @property def variables(self: Self) -> set[Variable]: """The set of variables in the quantum computation.""" + def add_variable(self: Self, var: Expression | float) -> None: """Add a variable to the quantum computation. Args: var: The variable to add. """ + def add_variables(self: Self, vars_: Sequence[Expression | float]) -> None: """Add multiple variables to the quantum computation. Args: vars_: The variables to add. """ + def is_variable_free(self: Self) -> bool: """Check if the quantum computation is free of variables. Returns: True if the quantum computation is free of variables, False otherwise. """ + def instantiate(self: Self, assignment: Mapping[Variable, float]) -> QuantumComputation: """Instantiate the quantum computation with the given variable assignment. @@ -417,6 +476,7 @@ class QuantumComputation(MutableSequence[Operation]): Returns: The instantiated quantum computation. """ + def instantiate_inplace(self: Self, assignment: Mapping[Variable, float]) -> None: """Instantiate the quantum computation with the given variable assignment in-place. @@ -442,6 +502,7 @@ class QuantumComputation(MutableSequence[Operation]): Returns: The OpenQASM2 representation of the quantum computation as a string. """ + def qasm2(self: Self, filename: PathLike[str] | str) -> None: """Write the OpenQASM2 representation of the quantum computation to a file. @@ -451,12 +512,14 @@ class QuantumComputation(MutableSequence[Operation]): Args: filename: The filename of the file to write the OpenQASM2 representation to. """ + def qasm3_str(self: Self) -> str: """Return the OpenQASM3 representation of the quantum computation as a string. Returns: The OpenQASM3 representation of the quantum computation as a string. """ + def qasm3(self: Self, filename: PathLike[str] | str) -> None: """Write the OpenQASM3 representation of the quantum computation to a file. @@ -480,6 +543,7 @@ class QuantumComputation(MutableSequence[Operation]): Args: q: The target qubit """ + def ci(self: Self, control: Control | int, target: int) -> None: """Apply a controlled identity operation. @@ -490,6 +554,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`i` """ + def mci(self: Self, controls: set[Control | int], target: int) -> None: """Apply a multi-controlled identity operation. @@ -500,6 +565,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`i` """ + def x(self: Self, q: int) -> None: r"""Apply a Pauli-X gate. @@ -509,6 +575,7 @@ class QuantumComputation(MutableSequence[Operation]): Args: q: The target qubit """ + def cx(self: Self, control: Control | int, target: int) -> None: """Apply a controlled Pauli-X (CNOT / CX) gate. @@ -519,6 +586,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`x` """ + def mcx(self: Self, controls: set[Control | int], target: int) -> None: """Apply a multi-controlled Pauli-X (Toffoli / MCX) gate. @@ -529,6 +597,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`x` """ + def y(self: Self, q: int) -> None: r"""Apply a Pauli-Y gate. @@ -538,6 +607,7 @@ class QuantumComputation(MutableSequence[Operation]): Args: q: The target qubit """ + def cy(self: Self, control: Control | int, target: int) -> None: """Apply a controlled Pauli-Y gate. @@ -548,6 +618,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`y` """ + def mcy(self: Self, controls: set[Control | int], target: int) -> None: """Apply a multi-controlled Pauli-Y gate. @@ -558,6 +629,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`y` """ + def z(self: Self, q: int) -> None: r"""Apply a Pauli-Z gate. @@ -567,6 +639,7 @@ class QuantumComputation(MutableSequence[Operation]): Args: q: The target qubit """ + def cz(self: Self, control: Control | int, target: int) -> None: """Apply a controlled Pauli-Z (CZ) gate. @@ -577,6 +650,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`z` """ + def mcz(self: Self, controls: set[Control | int], target: int) -> None: """Apply a multi-controlled Pauli-Z (MCZ) gate. @@ -587,6 +661,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`z` """ + def h(self: Self, q: int) -> None: r"""Apply a Hadamard gate. @@ -596,6 +671,7 @@ class QuantumComputation(MutableSequence[Operation]): Args: q: The target qubit """ + def ch(self: Self, control: Control | int, target: int) -> None: """Apply a controlled Hadamard gate. @@ -606,6 +682,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`h` """ + def mch(self: Self, controls: set[Control | int], target: int) -> None: """Apply a multi-controlled Hadamard gate. @@ -616,6 +693,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`h` """ + def s(self: Self, q: int) -> None: r"""Apply an S gate (phase gate). @@ -625,6 +703,7 @@ class QuantumComputation(MutableSequence[Operation]): Args: q: The target qubit """ + def cs(self: Self, control: Control | int, target: int) -> None: """Apply a controlled S gate (CS gate). @@ -635,6 +714,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`s` """ + def mcs(self: Self, controls: set[Control | int], target: int) -> None: """Apply a multi-controlled S gate. @@ -645,6 +725,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`s` """ + def sdg(self: Self, q: int) -> None: r"""Apply an :math:`S^{\dagger}` gate. @@ -654,6 +735,7 @@ class QuantumComputation(MutableSequence[Operation]): Args: q: The target qubit """ + def csdg(self: Self, control: Control | int, target: int) -> None: r"""Apply a controlled :math:`S^{\dagger}` gate. @@ -664,6 +746,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`sdg` """ + def mcsdg(self: Self, controls: set[Control | int], target: int) -> None: r"""Apply a multi-controlled :math:`S^{\dagger}` gate. @@ -674,6 +757,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`sdg` """ + def t(self: Self, q: int) -> None: r"""Apply a T gate. @@ -683,6 +767,7 @@ class QuantumComputation(MutableSequence[Operation]): Args: q: The target qubit """ + def ct(self: Self, control: Control | int, target: int) -> None: """Apply a controlled T gate. @@ -693,6 +778,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`t` """ + def mct(self: Self, controls: set[Control | int], target: int) -> None: """Apply a multi-controlled T gate. @@ -703,6 +789,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`t` """ + def tdg(self: Self, q: int) -> None: r"""Apply a :math:`T^{\dagger}` gate. @@ -712,6 +799,7 @@ class QuantumComputation(MutableSequence[Operation]): Args: q: The target qubit """ + def ctdg(self: Self, control: Control | int, target: int) -> None: r"""Apply a controlled :math:`T^{\dagger}` gate. @@ -722,6 +810,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`tdg` """ + def mctdg(self: Self, controls: set[Control | int], target: int) -> None: r"""Apply a multi-controlled :math:`T^{\dagger}` gate. @@ -732,6 +821,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`tdg` """ + def v(self: Self, q: int) -> None: r"""Apply a V gate. @@ -741,6 +831,7 @@ class QuantumComputation(MutableSequence[Operation]): Args: q: The target qubit """ + def cv(self: Self, control: Control | int, target: int) -> None: """Apply a controlled V gate. @@ -751,6 +842,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`v` """ + def mcv(self: Self, controls: set[Control | int], target: int) -> None: """Apply a multi-controlled V gate. @@ -761,6 +853,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`v` """ + def vdg(self: Self, q: int) -> None: r"""Apply a :math:`V^{\dagger}` gate. @@ -770,6 +863,7 @@ class QuantumComputation(MutableSequence[Operation]): Args: q: The target qubit """ + def cvdg(self: Self, control: Control | int, target: int) -> None: r"""Apply a controlled :math:`V^{\dagger}` gate. @@ -780,6 +874,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`vdg` """ + def mcvdg(self: Self, controls: set[Control | int], target: int) -> None: r"""Apply a multi-controlled :math:`V^{\dagger}` gate. @@ -790,6 +885,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`vdg` """ + def sx(self: Self, q: int) -> None: r"""Apply a :math:`\sqrt{X}` gate. @@ -799,6 +895,7 @@ class QuantumComputation(MutableSequence[Operation]): Args: q: The target qubit """ + def csx(self: Self, control: Control | int, target: int) -> None: r"""Apply a controlled :math:`\sqrt{X}` gate. @@ -809,6 +906,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`sx` """ + def mcsx(self: Self, controls: set[Control | int], target: int) -> None: r"""Apply a multi-controlled :math:`\sqrt{X}` gate. @@ -819,6 +917,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`sx` """ + def sxdg(self: Self, q: int) -> None: r"""Apply a :math:`\sqrt{X}^{\dagger}` gate. @@ -828,6 +927,7 @@ class QuantumComputation(MutableSequence[Operation]): Args: q: The target qubit """ + def csxdg(self: Self, control: Control | int, target: int) -> None: r"""Apply a controlled :math:`\sqrt{X}^{\dagger}` gate. @@ -838,6 +938,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`sxdg` """ + def mcsxdg(self: Self, controls: set[Control | int], target: int) -> None: r"""Apply a multi-controlled :math:`\sqrt{X}^{\dagger}` gate. @@ -848,6 +949,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`sxdg` """ + def rx(self: Self, theta: float | Expression, q: int) -> None: r"""Apply an :math:`R_x(\theta)` gate. @@ -858,6 +960,7 @@ class QuantumComputation(MutableSequence[Operation]): theta: The rotation angle q: The target qubit """ + def crx(self: Self, theta: float | Expression, control: Control | int, target: int) -> None: r"""Apply a controlled :math:`R_x(\theta)` gate. @@ -869,6 +972,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`rx` """ + def mcrx(self: Self, theta: float | Expression, controls: set[Control | int], target: int) -> None: r"""Apply a multi-controlled :math:`R_x(\theta)` gate. @@ -880,6 +984,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`rx` """ + def ry(self: Self, theta: float | Expression, q: int) -> None: r"""Apply an :math:`R_y(\theta)` gate. @@ -890,6 +995,7 @@ class QuantumComputation(MutableSequence[Operation]): theta: The rotation angle q: The target qubit """ + def cry(self: Self, theta: float | Expression, control: Control | int, target: int) -> None: r"""Apply a controlled :math:`R_y(\theta)` gate. @@ -901,6 +1007,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`ry` """ + def mcry(self: Self, theta: float | Expression, controls: set[Control | int], target: int) -> None: r"""Apply a multi-controlled :math:`R_y(\theta)` gate. @@ -912,6 +1019,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`ry` """ + def rz(self: Self, theta: float | Expression, q: int) -> None: r"""Apply an :math:`R_z(\theta)` gate. @@ -922,6 +1030,7 @@ class QuantumComputation(MutableSequence[Operation]): theta: The rotation angle q: The target qubit """ + def crz(self: Self, theta: float | Expression, control: Control | int, target: int) -> None: r"""Apply a controlled :math:`R_z(\theta)` gate. @@ -933,6 +1042,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`rz` """ + def mcrz(self: Self, theta: float | Expression, controls: set[Control | int], target: int) -> None: r"""Apply a multi-controlled :math:`R_z(\theta)` gate. @@ -944,6 +1054,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`rz` """ + def p(self: Self, theta: float | Expression, q: int) -> None: r"""Apply a phase gate. @@ -954,6 +1065,7 @@ class QuantumComputation(MutableSequence[Operation]): theta: The rotation angle q: The target qubit """ + def cp(self: Self, theta: float | Expression, control: Control | int, target: int) -> None: """Apply a controlled phase gate. @@ -965,6 +1077,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`p` """ + def mcp(self: Self, theta: float | Expression, controls: set[Control | int], target: int) -> None: """Apply a multi-controlled phase gate. @@ -976,6 +1089,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`p` """ + def u2(self: Self, phi: float | Expression, lambda_: float | Expression, q: int) -> None: r"""Apply a :math:`U_2(\phi, \lambda)` gate. @@ -988,6 +1102,7 @@ class QuantumComputation(MutableSequence[Operation]): lambda_: The rotation angle q: The target qubit """ + def cu2( self: Self, phi: float | Expression, lambda_: float | Expression, control: Control | int, target: int ) -> None: @@ -1002,6 +1117,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`u2` """ + def mcu2( self: Self, phi: float | Expression, lambda_: float | Expression, controls: set[Control | int], target: int ) -> None: @@ -1016,6 +1132,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`u2` """ + def u(self: Self, theta: float | Expression, phi: float | Expression, lambda_: float | Expression, q: int) -> None: r"""Apply a :math:`U(\theta, \phi, \lambda)` gate. @@ -1029,6 +1146,7 @@ class QuantumComputation(MutableSequence[Operation]): lambda_: The rotation angle q: The target qubit """ + def cu( self: Self, theta: float | Expression, @@ -1049,6 +1167,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`u` """ + def mcu( self: Self, theta: float | Expression, @@ -1069,6 +1188,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`u` """ + def swap(self: Self, target1: int, target2: int) -> None: r"""Apply a SWAP gate. @@ -1079,6 +1199,7 @@ class QuantumComputation(MutableSequence[Operation]): target1: The first target qubit target2: The second target qubit """ + def cswap(self: Self, control: Control | int, target1: int, target2: int) -> None: """Apply a controlled SWAP gate. @@ -1090,6 +1211,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`swap` """ + def mcswap(self: Self, controls: set[Control | int], target1: int, target2: int) -> None: """Apply a multi-controlled SWAP gate. @@ -1101,6 +1223,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`swap` """ + def dcx(self: Self, target1: int, target2: int) -> None: r"""Apply a DCX (double CNOT) gate. @@ -1111,6 +1234,7 @@ class QuantumComputation(MutableSequence[Operation]): target1: The first target qubit target2: The second target qubit """ + def cdcx(self: Self, control: Control | int, target1: int, target2: int) -> None: """Apply a controlled DCX (double CNOT) gate. @@ -1122,6 +1246,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`dcx` """ + def mcdcx(self: Self, controls: set[Control | int], target1: int, target2: int) -> None: """Apply a multi-controlled DCX (double CNOT) gate. @@ -1133,6 +1258,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`dcx` """ + def ecr(self: Self, target1: int, target2: int) -> None: r"""Apply an ECR (echoed cross-resonance) gate. @@ -1143,6 +1269,7 @@ class QuantumComputation(MutableSequence[Operation]): target1: The first target qubit target2: The second target qubit """ + def cecr(self: Self, control: Control | int, target1: int, target2: int) -> None: """Apply a controlled ECR (echoed cross-resonance) gate. @@ -1154,6 +1281,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`ecr` """ + def mcecr(self: Self, controls: set[Control | int], target1: int, target2: int) -> None: """Apply a multi-controlled ECR (echoed cross-resonance) gate. @@ -1165,6 +1293,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`ecr` """ + def iswap(self: Self, target1: int, target2: int) -> None: r"""Apply an iSWAP gate. @@ -1175,6 +1304,7 @@ class QuantumComputation(MutableSequence[Operation]): target1: The first target qubit target2: The second target qubit """ + def ciswap(self: Self, control: Control | int, target1: int, target2: int) -> None: """Apply a controlled iSWAP gate. @@ -1186,6 +1316,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`iswap` """ + def mciswap(self: Self, controls: set[Control | int], target1: int, target2: int) -> None: """Apply a multi-controlled iSWAP gate. @@ -1197,6 +1328,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`iswap` """ + def iswapdg(self: Self, target1: int, target2: int) -> None: r"""Apply an :math:`iSWAP^{\dagger}` gate. @@ -1207,6 +1339,7 @@ class QuantumComputation(MutableSequence[Operation]): target1: The first target qubit target2: The second target qubit """ + def ciswapdg(self: Self, control: Control | int, target1: int, target2: int) -> None: r"""Apply a controlled :math:`iSWAP^{\dagger}` gate. @@ -1218,6 +1351,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`iswapdg` """ + def mciswapdg(self: Self, controls: set[Control | int], target1: int, target2: int) -> None: r"""Apply a multi-controlled :math:`iSWAP^{\dagger}` gate. @@ -1229,6 +1363,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`iswapdg` """ + def peres(self: Self, target1: int, target2: int) -> None: r"""Apply a Peres gate. @@ -1239,6 +1374,7 @@ class QuantumComputation(MutableSequence[Operation]): target1: The first target qubit target2: The second target qubit """ + def cperes(self: Self, control: Control | int, target1: int, target2: int) -> None: """Apply a controlled Peres gate. @@ -1250,6 +1386,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`peres` """ + def mcperes(self: Self, controls: set[Control | int], target1: int, target2: int) -> None: """Apply a multi-controlled Peres gate. @@ -1261,6 +1398,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`peres` """ + def peresdg(self: Self, target1: int, target2: int) -> None: r"""Apply a :math:`Peres^{\dagger}` gate. @@ -1271,6 +1409,7 @@ class QuantumComputation(MutableSequence[Operation]): target1: The first target qubit target2: The second target qubit """ + def cperesdg(self: Self, control: Control | int, target1: int, target2: int) -> None: r"""Apply a controlled :math:`Peres^{\dagger}` gate. @@ -1282,6 +1421,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`peresdg` """ + def mcperesdg(self: Self, controls: set[Control | int], target1: int, target2: int) -> None: r"""Apply a multi-controlled :math:`Peres^{\dagger}` gate. @@ -1293,6 +1433,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`peresdg` """ + def rxx(self: Self, theta: float | Expression, target1: int, target2: int) -> None: r"""Apply an :math:`R_{xx}(\theta)` gate. @@ -1304,6 +1445,7 @@ class QuantumComputation(MutableSequence[Operation]): target1: The first target qubit target2: The second target qubit """ + def crxx(self: Self, theta: float | Expression, control: Control | int, target1: int, target2: int) -> None: r"""Apply a controlled :math:`R_{xx}(\theta)` gate. @@ -1316,6 +1458,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`rxx` """ + def mcrxx(self: Self, theta: float | Expression, controls: set[Control | int], target1: int, target2: int) -> None: r"""Apply a multi-controlled :math:`R_{xx}(\theta)` gate. @@ -1328,6 +1471,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`rxx` """ + def ryy(self: Self, theta: float | Expression, target1: int, target2: int) -> None: r"""Apply an :math:`R_{yy}(\theta)` gate. @@ -1339,6 +1483,7 @@ class QuantumComputation(MutableSequence[Operation]): target1: The first target qubit target2: The second target qubit """ + def cryy(self: Self, theta: float | Expression, control: Control | int, target1: int, target2: int) -> None: r"""Apply a controlled :math:`R_{yy}(\theta)` gate. @@ -1351,6 +1496,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`ryy` """ + def mcryy(self: Self, theta: float | Expression, controls: set[Control | int], target1: int, target2: int) -> None: r"""Apply a multi-controlled :math:`R_{yy}(\theta)` gate. @@ -1363,6 +1509,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`ryy` """ + def rzz(self: Self, theta: float | Expression, target1: int, target2: int) -> None: r"""Apply an :math:`R_{zz}(\theta)` gate. @@ -1374,6 +1521,7 @@ class QuantumComputation(MutableSequence[Operation]): target1: The first target qubit target2: The second target qubit """ + def crzz(self: Self, theta: float | Expression, control: Control | int, target1: int, target2: int) -> None: r"""Apply a controlled :math:`R_{zz}(\theta)` gate. @@ -1386,6 +1534,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`rzz` """ + def mcrzz(self: Self, theta: float | Expression, controls: set[Control | int], target1: int, target2: int) -> None: r"""Apply a multi-controlled :math:`R_{zz}(\theta)` gate. @@ -1398,6 +1547,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`rzz` """ + def rzx(self: Self, theta: float | Expression, target1: int, target2: int) -> None: r"""Apply an :math:`R_{zx}(\theta)` gate. @@ -1409,6 +1559,7 @@ class QuantumComputation(MutableSequence[Operation]): target1: The first target qubit target2: The second target qubit """ + def crzx(self: Self, theta: float | Expression, control: Control | int, target1: int, target2: int) -> None: r"""Apply a controlled :math:`R_{zx}(\theta)` gate. @@ -1421,6 +1572,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`rzx` """ + def mcrzx(self: Self, theta: float | Expression, controls: set[Control | int], target1: int, target2: int) -> None: r"""Apply a multi-controlled :math:`R_{zx}(\theta)` gate. @@ -1433,6 +1585,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`rzx` """ + def xx_minus_yy( self: Self, theta: float | Expression, beta: float | Expression, target1: int, target2: int ) -> None: @@ -1448,6 +1601,7 @@ class QuantumComputation(MutableSequence[Operation]): target1: The first target qubit target2: The second target qubit """ + def cxx_minus_yy( self: Self, theta: float | Expression, @@ -1468,6 +1622,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`xx_minus_yy` """ + def mcxx_minus_yy( self: Self, theta: float | Expression, @@ -1488,6 +1643,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`xx_minus_yy` """ + def xx_plus_yy(self: Self, theta: float | Expression, beta: float | Expression, target1: int, target2: int) -> None: r"""Apply an :math:`R_{XX + YY}(\theta, \beta)` gate. @@ -1501,6 +1657,7 @@ class QuantumComputation(MutableSequence[Operation]): target1: The first target qubit target2: The second target qubit """ + def cxx_plus_yy( self: Self, theta: float | Expression, @@ -1521,6 +1678,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`xx_plus_yy` """ + def mcxx_plus_yy( self: Self, theta: float | Expression, @@ -1541,6 +1699,7 @@ class QuantumComputation(MutableSequence[Operation]): See Also: :meth:`xx_plus_yy` """ + def gphase(self: Self, theta: float) -> None: r"""Apply a global phase gate. @@ -1550,6 +1709,7 @@ class QuantumComputation(MutableSequence[Operation]): Args: theta: The rotation angle """ + @overload def measure(self: Self, qubit: int, cbit: int) -> None: """Measure a qubit and store the result in a classical bit. @@ -1558,6 +1718,7 @@ class QuantumComputation(MutableSequence[Operation]): qubit: The qubit to measure cbit: The classical bit to store the result """ + @overload def measure(self: Self, qubit: int, creg_bit: tuple[str, int]) -> None: """Measure a qubit and store the result in a bit of a classical register. @@ -1566,6 +1727,7 @@ class QuantumComputation(MutableSequence[Operation]): qubit: The qubit to measure creg_bit: The classical register and index to store the result """ + @overload def measure(self: Self, qubits: Sequence[int], cbits: Sequence[int]) -> None: """Measure multiple qubits and store the results in classical bits. @@ -1576,6 +1738,7 @@ class QuantumComputation(MutableSequence[Operation]): qubits: The qubits to measure cbits: The classical bits to store the results """ + def measure_all(self: Self, add_bits: bool = True) -> None: """Measure all qubits and store the results in classical bits. @@ -1589,6 +1752,7 @@ class QuantumComputation(MutableSequence[Operation]): Args: add_bits: Whether to explicitly add a classical register """ + @overload def reset(self: Self, q: int) -> None: """Add a reset operation to the circuit. @@ -1596,6 +1760,7 @@ class QuantumComputation(MutableSequence[Operation]): Args: q: The qubit to reset """ + @overload def reset(self: Self, qubits: Sequence[int]) -> None: """Add a reset operation to the circuit. @@ -1603,9 +1768,11 @@ class QuantumComputation(MutableSequence[Operation]): Args: qubits: The qubits to reset """ + @overload def barrier(self: Self) -> None: """Add a barrier to the circuit.""" + @overload def barrier(self: Self, q: int) -> None: """Add a barrier to the circuit. @@ -1613,6 +1780,7 @@ class QuantumComputation(MutableSequence[Operation]): Args: q: The qubit to add the barrier to """ + @overload def barrier(self: Self, qubits: Sequence[int]) -> None: """Add a barrier to the circuit. @@ -1620,6 +1788,7 @@ class QuantumComputation(MutableSequence[Operation]): Args: qubits: The qubits to add the barrier to """ + @overload def classic_controlled( self: Self, @@ -1638,6 +1807,7 @@ class QuantumComputation(MutableSequence[Operation]): expected_value: The expected value of the classical register params: The parameters of the operation """ + @overload def classic_controlled( self: Self, @@ -1658,6 +1828,7 @@ class QuantumComputation(MutableSequence[Operation]): expected_value: The expected value of the classical register params: The parameters of the operation """ + @overload def classic_controlled( self: Self, diff --git a/src/mqt/core/_core/operations.pyi b/src/mqt/core/_core/operations.pyi index 35690f8d2..bb9f164ba 100644 --- a/src/mqt/core/_core/operations.pyi +++ b/src/mqt/core/_core/operations.pyi @@ -10,8 +10,9 @@ class Control: Args: qubit: The qubit that is the control. - type_: The type of the control. + type_: The type of the control (default is positive). """ + class Type: """The type of a control. It can be either positive or negative.""" @@ -43,10 +44,22 @@ class Control: qubit: int type_: Type - @overload - def __init__(self: Self, qubit: int) -> None: ... - @overload - def __init__(self: Self, qubit: int, type_: Type) -> None: ... + def __init__(self: Self, qubit: int, type_: Type = ...) -> None: + """Initialize a control. + + Args: + qubit: The qubit that is the control. + type_: The type of the control (default is positive). + """ + + def __eq__(self: Self, other: object) -> bool: + """Check if two controls are equal.""" + + def __ne__(self: Self, other: object) -> bool: + """Check if two controls are not equal.""" + + def __hash__(self: Self) -> int: + """Get the hash of the control.""" class OpType: """An Enum-like class that represents the type of an operation.""" @@ -326,6 +339,7 @@ class OpType: See Also: :meth:`mqt.core.QuantumComputation.z` """ + @property def name(self: Self) -> str: ... @property @@ -370,15 +384,19 @@ class Operation(ABC): Note: The notion of a parameter might not make sense for all types of operations. """ + @property def name(self: Self) -> str: """The name of the operation.""" + @property def num_targets(self: Self) -> int: """The number of targets of the operation.""" + @property def num_controls(self: Self) -> int: """The number of controls of the operation.""" + @abstractmethod def add_control(self: Self, control: Control) -> None: """Add a control to the operation. @@ -386,15 +404,18 @@ class Operation(ABC): Args: control: The control to add. """ + def add_controls(self: Self, controls: set[Control]) -> None: """Add multiple controls to the operation. Args: controls: The controls to add. """ + @abstractmethod def clear_controls(self: Self) -> None: """Clear all controls of the operation.""" + @abstractmethod def remove_control(self: Self, control: Control) -> None: """Remove a control from the operation. @@ -402,12 +423,14 @@ class Operation(ABC): Args: control: The control to remove. """ + def remove_controls(self: Self, controls: set[Control]) -> None: """Remove multiple controls from the operation. Args: controls: The controls to remove. """ + def acts_on(self: Self, qubit: int) -> bool: """Check if the operation acts on a specific qubit. @@ -417,63 +440,74 @@ class Operation(ABC): Returns: True if the operation acts on the qubit, False otherwise. """ + def get_used_qubits(self: Self) -> set[int]: """Get the qubits that are used by the operation. Returns: The set of qubits that are used by the operation. """ + def is_classic_controlled_operation(self: Self) -> bool: """Check if the operation is a :class:`ClassicControlledOperation`. Returns: True if the operation is a :class:`ClassicControlledOperation`, False otherwise. """ + def is_compound_operation(self: Self) -> bool: """Check if the operation is a :class:`CompoundOperation`. Returns: True if the operation is a :class:`CompoundOperation`, False otherwise. """ + def is_controlled(self: Self) -> bool: """Check if the operation is controlled. Returns: True if the operation is controlled, False otherwise. """ + def is_non_unitary_operation(self: Self) -> bool: """Check if the operation is a :class:`NonUnitaryOperation`. Returns: True if the operation is a :class:`NonUnitaryOperation`, False otherwise. """ + def is_standard_operation(self: Self) -> bool: """Check if the operation is a :class:`StandardOperation`. Returns: True if the operation is a :class:`StandardOperation`, False otherwise. """ + def is_symbolic_operation(self: Self) -> bool: """Check if the operation is a :class:`SymbolicOperation`. Returns: True if the operation is a :class:`SymbolicOperation`, False otherwise. """ + def is_unitary(self: Self) -> bool: """Check if the operation is unitary. Returns: True if the operation is unitary, False otherwise. """ + def get_inverted(self: Self) -> Operation: """Get the inverse of the operation. Returns: The inverse of the operation. """ + @abstractmethod def invert(self: Self) -> None: """Invert the operation (in-place).""" + def __eq__(self: Self, other: object) -> bool: ... def __hash__(self: Self) -> int: ... def __ne__(self: Self, other: object) -> bool: ... @@ -492,6 +526,7 @@ class StandardOperation(Operation): op_type: The type of the operation. params: The parameters of the operation (if any). """ + @overload def __init__(self: Self) -> None: ... @overload @@ -557,14 +592,17 @@ class StandardOperation(Operation): Args: control: The control to add. """ + def clear_controls(self: Self) -> None: """Clear all controls of the operation.""" + def remove_control(self: Self, control: Control) -> None: """Remove a control from the operation. Args: control: The control to remove. """ + def invert(self: Self) -> None: """Invert the operation (in-place). @@ -583,9 +621,11 @@ class NonUnitaryOperation(Operation): classics: The classical bit(s) that are associated with the operation (only relevant for measurements). op_type: The type of the operation. """ + @property def classics(self: Self) -> list[int]: """The classical registers that are associated with the operation.""" + @overload def __init__(self: Self, targets: Sequence[int], classics: Sequence[int]) -> None: ... @overload @@ -594,10 +634,13 @@ class NonUnitaryOperation(Operation): def __init__(self: Self, targets: Sequence[int], op_type: OpType = ...) -> None: ... def add_control(self: Self, control: Control) -> None: """Adding controls to a non-unitary operation is not supported.""" + def clear_controls(self: Self) -> None: """Cannot clear controls of a non-unitary operation.""" + def remove_control(self: Self, control: Control) -> None: """Removing controls from a non-unitary operation is not supported.""" + def invert(self: Self) -> None: """Non-unitary operations are, per definition, not invertible.""" @@ -612,12 +655,14 @@ class CompoundOperation(Operation): Args: ops: The operations that are part of the compound operation. """ + @overload def __init__(self: Self) -> None: ... @overload def __init__(self: Self, ops: Sequence[Operation]) -> None: ... def __len__(self: Self) -> int: """The number of operations in the compound operation.""" + @overload def __getitem__(self: Self, idx: int) -> Operation: """Get the operation at the given index. @@ -631,6 +676,7 @@ class CompoundOperation(Operation): Notes: This gives direct access to the operations in the compound operation. """ + @overload def __getitem__(self: Self, idx: slice) -> list[Operation]: """Get the operations in the given slice. @@ -644,10 +690,13 @@ class CompoundOperation(Operation): Notes: This gives direct access to the operations in the compound operation. """ + def append(self: Self, op: Operation) -> None: """Append an operation to the compound operation.""" + def empty(self: Self) -> bool: """Check if the compound operation is empty.""" + def add_control(self: Self, control: Control) -> None: """Add a control to the operation. @@ -658,6 +707,7 @@ class CompoundOperation(Operation): Args: control: The control to add. """ + def clear_controls(self: Self) -> None: """Clear all controls of the operation. @@ -665,6 +715,7 @@ class CompoundOperation(Operation): operation itself and will clear these controls of all operations that are part of the compound operation. """ + def remove_control(self: Self, control: Control) -> None: """Remove a control from the operation. @@ -675,6 +726,7 @@ class CompoundOperation(Operation): Args: control: The control to remove. """ + def invert(self: Self) -> None: """Invert the operation (in-place). @@ -699,6 +751,7 @@ class SymbolicOperation(StandardOperation): op_type: The type of the operation. params: The parameters of the operation (if any). """ + @overload def __init__(self: Self) -> None: ... @overload @@ -765,12 +818,14 @@ class SymbolicOperation(StandardOperation): Returns: The parameter at the given index. """ + def get_parameters(self: Self) -> list[Expression | float]: """Get all parameters of the operation. Returns: The parameters of the operation. """ + def get_instantiated_operation(self: Self, assignment: Mapping[Variable, float]) -> StandardOperation: """Get the instantiated operation. @@ -780,6 +835,7 @@ class SymbolicOperation(StandardOperation): Returns: The instantiated operation. """ + def instantiate(self: Self, assignment: Mapping[Variable, float]) -> None: """Instantiate the operation (in-place). @@ -799,12 +855,14 @@ class ClassicControlledOperation(Operation): control_register: The classical register that controls the operation. expected_value: The expected value of the classical register. """ + def __init__( self: Self, operation: Operation, control_register: tuple[int, int], expected_value: int = 1 ) -> None: ... @property def operation(self: Self) -> Operation: """The operation that is classically controlled.""" + @property def control_register(self: Self) -> tuple[int, int]: """The classical register that controls the operation. @@ -814,6 +872,7 @@ class ClassicControlledOperation(Operation): Examples: A register that starts at index 0 and has a length of 2 is specified as ``(0, 2)``. """ + @property def expected_value(self: Self) -> int: """The expected value of the classical register. @@ -823,6 +882,7 @@ class ClassicControlledOperation(Operation): Otherwise, the expected value is an integer that is interpreted as a binary number, where the least significant bit is at the start index of the classical register. """ + def add_control(self: Self, control: Control) -> None: """Adds a control to the underlying operation. @@ -832,12 +892,14 @@ class ClassicControlledOperation(Operation): See Also: :meth:`Operation.add_control` """ + def clear_controls(self: Self) -> None: """Clears the controls of the underlying operation. See Also: :meth:`Operation.clear_controls` """ + def remove_control(self: Self, control: Control) -> None: """Removes a control from the underlying operation. @@ -847,6 +909,7 @@ class ClassicControlledOperation(Operation): See Also: :meth:`Operation.remove_control` """ + def invert(self: Self) -> None: """Inverts the underlying operation. diff --git a/src/mqt/core/_core/symbolic.pyi b/src/mqt/core/_core/symbolic.pyi index d1ea1e484..fd33d665f 100644 --- a/src/mqt/core/_core/symbolic.pyi +++ b/src/mqt/core/_core/symbolic.pyi @@ -12,6 +12,7 @@ class Variable: Note: Variables are uniquely identified by their name, so if a variable with the same name already exists, the existing variable will be returned. """ + def __eq__(self: Self, arg0: object) -> bool: ... def __gt__(self: Self, arg0: Variable) -> bool: ... def __hash__(self: Self) -> int: ... @@ -29,6 +30,7 @@ class Term: variable: The variable of the term. coefficient: The coefficient of the term. """ + def __eq__(self: Self, arg0: object) -> bool: ... def __hash__(self: Self) -> int: ... def __init__(self: Self, variable: Variable, coefficient: float = 1.0) -> None: ... @@ -43,6 +45,7 @@ class Term: Args: coeff: The coefficient to add. """ + def evaluate(self: Self, assignment: Mapping[Variable, float]) -> float: """Evaluate the term with a given variable assignment. @@ -52,11 +55,14 @@ class Term: Returns: The evaluated value of the term. """ + def has_zero_coefficient(self: Self) -> bool: """Check if the coefficient of the term is zero.""" + @property def coefficient(self: Self) -> float: """The coefficient of the term.""" + @property def variable(self: Self) -> Variable: """The variable of the term.""" @@ -95,6 +101,7 @@ class Expression: terms: The list of terms. constant: The constant. """ + @overload def __init__(self: Self, term: Term, constant: float = 0.0) -> None: """Create an expression with a given term and a constant. @@ -103,6 +110,7 @@ class Expression: term: The term. constant: The constant. """ + @overload def __init__(self: Self, constant: float = 0.0) -> None: """Create an expression with a given constant. @@ -110,6 +118,7 @@ class Expression: Args: constant: The constant. """ + def __iter__(self: Self) -> Iterator[Term]: ... def __len__(self: Self) -> int: ... def __mul__(self: Self, arg0: float) -> Expression: ... @@ -140,15 +149,20 @@ class Expression: Returns: The evaluated value of the expression. """ + def is_constant(self: Self) -> bool: """Check if the expression is a constant.""" + def is_zero(self: Self) -> bool: """Check if the expression is zero.""" + def num_terms(self: Self) -> int: """The number of terms in the expression.""" + @property def terms(self: Self) -> list[Term]: """The terms of the expression.""" + @property def variables(self: Self) -> set[Variable]: """The variables in the expression.""" diff --git a/src/python/operations/register_control.cpp b/src/python/operations/register_control.cpp index cf2dae036..2286ee762 100644 --- a/src/python/operations/register_control.cpp +++ b/src/python/operations/register_control.cpp @@ -1,4 +1,5 @@ #include "operations/Control.hpp" +#include "pybind11/operators.h" #include "python/pybind11.hpp" namespace mqt { @@ -33,6 +34,9 @@ void registerControl(py::module& m) { control.def_readwrite("qubit", &qc::Control::qubit); control.def("__str__", [](const qc::Control& c) { return c.toString(); }); control.def("__repr__", [](const qc::Control& c) { return c.toString(); }); + control.def(py::self == py::self); + control.def(py::self != py::self); + control.def(hash(py::self)); py::implicitly_convertible(); } diff --git a/src/python/register_permutation.cpp b/src/python/register_permutation.cpp index e1324ef29..20e262878 100644 --- a/src/python/register_permutation.cpp +++ b/src/python/register_permutation.cpp @@ -1,4 +1,5 @@ #include "Permutation.hpp" +#include "pybind11/operators.h" #include "python/pybind11.hpp" namespace mqt { @@ -34,6 +35,9 @@ void registerPermutation(py::module& m) { return py::make_iterator(p.begin(), p.end()); }, py::keep_alive<0, 1>()) + .def(py::self == py::self) + .def(py::self != py::self) + .def(hash(py::self)) .def("__str__", [](const qc::Permutation& p) { std::stringstream ss; @@ -53,6 +57,7 @@ void registerPermutation(py::module& m) { ss << "})"; return ss.str(); }); + py::implicitly_convertible(); } } // namespace mqt