From 823226bd37749475677ff95c4d206f12855ad757 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 9 Dec 2025 00:54:36 +0100 Subject: [PATCH 001/144] Switch IR bindings to nanobind --- bindings/ir/CMakeLists.txt | 2 +- .../register_compound_operation.cpp | 90 +++------ bindings/ir/operations/register_control.cpp | 43 ++-- .../operations/register_if_else_operation.cpp | 110 +++++----- .../register_non_unitary_operation.cpp | 31 ++- bindings/ir/operations/register_operation.cpp | 31 ++- bindings/ir/operations/register_optype.cpp | 20 +- .../register_standard_operation.cpp | 36 ++-- .../register_symbolic_operation.cpp | 34 ++-- bindings/ir/register_ir.cpp | 30 ++- bindings/ir/register_operations.cpp | 27 +-- bindings/ir/register_permutation.cpp | 66 +++--- bindings/ir/register_quantum_computation.cpp | 189 ++++++++---------- bindings/ir/register_registers.cpp | 56 +++--- bindings/ir/register_symbolic.cpp | 17 +- bindings/ir/symbolic/register_expression.cpp | 88 ++++---- bindings/ir/symbolic/register_term.cpp | 40 ++-- bindings/ir/symbolic/register_variable.cpp | 34 ++-- cmake/AddMQTPythonBinding.cmake | 44 ++++ cmake/ExternalDependencies.cmake | 7 + pyproject.toml | 8 +- uv.lock | 13 ++ 22 files changed, 470 insertions(+), 546 deletions(-) diff --git a/bindings/ir/CMakeLists.txt b/bindings/ir/CMakeLists.txt index 719c8b6734..b560e19675 100644 --- a/bindings/ir/CMakeLists.txt +++ b/bindings/ir/CMakeLists.txt @@ -11,7 +11,7 @@ if(NOT TARGET ${MQT_CORE_TARGET_NAME}-ir-bindings) file(GLOB_RECURSE IR_SOURCES **.cpp) # declare the Python module - add_mqt_python_binding( + add_mqt_python_binding_nanobind( CORE ${MQT_CORE_TARGET_NAME}-ir-bindings ${IR_SOURCES} diff --git a/bindings/ir/operations/register_compound_operation.cpp b/bindings/ir/operations/register_compound_operation.cpp index 811b6ac12f..bc55acc375 100644 --- a/bindings/ir/operations/register_compound_operation.cpp +++ b/bindings/ir/operations/register_compound_operation.cpp @@ -11,18 +11,10 @@ #include "ir/operations/CompoundOperation.hpp" #include "ir/operations/Operation.hpp" -// These includes must be the first includes for any bindings code -// clang-format off -#include -#include // NOLINT(misc-include-cleaner) - -#include -#include -// clang-format on - #include #include #include +#include #include #include #include @@ -30,35 +22,38 @@ namespace mqt { -namespace py = pybind11; -using namespace pybind11::literals; +namespace nb = nanobind; +using namespace nb::literals; using DiffType = std::vector>::difference_type; using SizeType = std::vector>::size_type; // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerCompoundOperation(const py::module& m) { +void registerCompoundOperation(const nb::module_& m) { auto wrap = [](DiffType i, const SizeType size) { if (i < 0) { i += static_cast(size); } if (i < 0 || std::cmp_greater_equal(i, size)) { - throw py::index_error(); + throw nb::index_error(); } return i; }; - py::class_(m, "CompoundOperation") - .def(py::init<>()) - .def(py::init([](const std::vector& ops) { - std::vector> uniqueOps; - uniqueOps.reserve(ops.size()); - for (const auto& op : ops) { - uniqueOps.emplace_back(op->clone()); - } - return qc::CompoundOperation(std::move(uniqueOps)); - }), - "ops"_a) + nb::class_(m, "CompoundOperation") + .def(nb::init<>()) + .def( + "__init__", + [](qc::CompoundOperation* self, + const std::vector& ops) { + std::vector> uniqueOps; + uniqueOps.reserve(ops.size()); + for (const auto& op : ops) { + uniqueOps.emplace_back(op->clone()); + } + new (self) qc::CompoundOperation(std::move(uniqueOps)); + }, + "ops"_a) .def("__len__", &qc::CompoundOperation::size) .def( "__getitem__", @@ -66,17 +61,11 @@ void registerCompoundOperation(const py::module& m) { i = wrap(i, op.size()); return op.at(static_cast(i)).get(); }, - py::return_value_policy::reference_internal, "index"_a) + nb::rv_policy::reference_internal, "index"_a) .def( "__getitem__", - [](const qc::CompoundOperation& op, const py::slice& slice) { - std::size_t start{}; - std::size_t stop{}; - std::size_t step{}; - std::size_t sliceLength{}; - if (!slice.compute(op.size(), &start, &stop, &step, &sliceLength)) { - throw py::error_already_set(); - } + [](const qc::CompoundOperation& op, const nb::slice& slice) { + auto [start, stop, step, sliceLength] = slice.compute(op.size()); auto ops = std::vector(); ops.reserve(sliceLength); for (std::size_t i = start; i < stop; i += step) { @@ -84,7 +73,7 @@ void registerCompoundOperation(const py::module& m) { } return ops; }, - py::return_value_policy::reference_internal, "index"_a) + nb::rv_policy::reference_internal, "index"_a) .def( "__setitem__", [&wrap](qc::CompoundOperation& compOp, DiffType i, @@ -95,16 +84,10 @@ void registerCompoundOperation(const py::module& m) { "index"_a, "value"_a) .def( "__setitem__", - [](qc::CompoundOperation& compOp, const py::slice& slice, + [](qc::CompoundOperation& compOp, const nb::slice& slice, const std::vector& ops) { - std::size_t start{}; - std::size_t stop{}; - std::size_t step{}; - std::size_t sliceLength{}; - if (!slice.compute(compOp.size(), &start, &stop, &step, - &sliceLength)) { - throw py::error_already_set(); - } + auto [start, stop, step, sliceLength] = + slice.compute(compOp.size()); if (sliceLength != ops.size()) { throw std::runtime_error( "Length of slice and number of operations do not match."); @@ -117,26 +100,19 @@ void registerCompoundOperation(const py::module& m) { "index"_a, "value"_a) .def( "__delitem__", - [&wrap](qc::CompoundOperation& compOp, DiffType i) { - i = wrap(i, compOp.size()); - compOp.erase(compOp.begin() + i); + [&wrap](qc::CompoundOperation& op, DiffType i) { + i = wrap(i, op.size()); + op.erase(op.begin() + i); }, "index"_a) .def( "__delitem__", - [](qc::CompoundOperation& compOp, const py::slice& slice) { - std::size_t start{}; - std::size_t stop{}; - std::size_t step{}; - std::size_t sliceLength{}; - if (!slice.compute(compOp.size(), &start, &stop, &step, - &sliceLength)) { - throw py::error_already_set(); - } + [](qc::CompoundOperation& op, const nb::slice& slice) { + auto [start, stop, step, sliceLength] = slice.compute(op.size()); // delete in reverse order to not invalidate indices for (std::size_t i = sliceLength; i > 0; --i) { - compOp.erase(compOp.begin() + - static_cast(start + ((i - 1) * step))); + op.erase(op.begin() + + static_cast(start + ((i - 1) * step))); } }, "index"_a) diff --git a/bindings/ir/operations/register_control.cpp b/bindings/ir/operations/register_control.cpp index e8796729cc..e2148b0886 100644 --- a/bindings/ir/operations/register_control.cpp +++ b/bindings/ir/operations/register_control.cpp @@ -11,43 +11,34 @@ #include "ir/Definitions.hpp" #include "ir/operations/Control.hpp" -// These includes must be the first includes for any bindings code -// clang-format off -#include -#include // NOLINT(misc-include-cleaner) - -#include -#include -#include -#include -// clang-format on +#include +#include +#include namespace mqt { -namespace py = pybind11; -using namespace pybind11::literals; +namespace nb = nanobind; +using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerControl(const py::module& m) { - - auto control = py::class_(m, "Control"); +void registerControl(const nb::module_& m) { + auto control = nb::class_(m, "Control"); - py::native_enum(control, "Type", "enum.Enum", - "Enumeration of control types.") + nb::enum_(control, "Type", "enum.Enum", + "Enumeration of control types.") .value("Pos", qc::Control::Type::Pos) - .value("Neg", qc::Control::Type::Neg) - .finalize(); + .value("Neg", qc::Control::Type::Neg); - control.def(py::init(), "qubit"_a, + control.def(nb::init(), "qubit"_a, "type_"_a = qc::Control::Type::Pos); - control.def_readwrite("type_", &qc::Control::type); - control.def_readwrite("qubit", &qc::Control::qubit); + control.def_rw("type_", &qc::Control::type); + control.def_rw("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); // NOLINT(misc-redundant-expression) - control.def(py::self != py::self); // NOLINT(misc-redundant-expression) - control.def(hash(py::self)); - py::implicitly_convertible(); + control.def(nb::self == nb::self); + control.def(nb::self != nb::self); + control.def(nb::hash(nb::self)); + nb::implicitly_convertible(); } } // namespace mqt diff --git a/bindings/ir/operations/register_if_else_operation.cpp b/bindings/ir/operations/register_if_else_operation.cpp index 947a087e02..32e4995644 100644 --- a/bindings/ir/operations/register_if_else_operation.cpp +++ b/bindings/ir/operations/register_if_else_operation.cpp @@ -13,28 +13,21 @@ #include "ir/operations/IfElseOperation.hpp" #include "ir/operations/Operation.hpp" -// These includes must be the first includes for any bindings code -// clang-format off -#include -#include // NOLINT(misc-include-cleaner) - -#include -#include -// clang-format on - #include #include +#include +#include #include #include namespace mqt { -namespace py = pybind11; -using namespace pybind11::literals; +namespace nb = nanobind; +using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerIfElseOperation(const py::module& m) { - py::native_enum( +void registerIfElseOperation(const nb::module_& m) { + nb::enum_( m, "ComparisonKind", "enum.Enum", "Enumeration of comparison types for classic-controlled operations.") .value("eq", qc::ComparisonKind::Eq) @@ -43,57 +36,52 @@ void registerIfElseOperation(const py::module& m) { .value("leq", qc::ComparisonKind::Leq) .value("gt", qc::ComparisonKind::Gt) .value("geq", qc::ComparisonKind::Geq) - .export_values() - .finalize(); + .export_values(); auto ifElse = - py::class_(m, "IfElseOperation"); + nb::class_(m, "IfElseOperation"); - ifElse.def(py::init([](qc::Operation* thenOp, qc::Operation* elseOp, - qc::ClassicalRegister& controlReg, - const std::uint64_t expectedVal, - const qc::ComparisonKind kind) { - std::unique_ptr thenPtr = - thenOp ? thenOp->clone() : nullptr; - std::unique_ptr elsePtr = - elseOp ? elseOp->clone() : nullptr; - return std::make_unique( - std::move(thenPtr), std::move(elsePtr), controlReg, - expectedVal, kind); - }), - "then_operation"_a, "else_operation"_a, "control_register"_a, - "expected_value"_a = 1U, - "comparison_kind"_a = qc::ComparisonKind::Eq); - ifElse.def(py::init([](qc::Operation* thenOp, qc::Operation* elseOp, - qc::Bit controlBit, std::uint64_t expectedVal, - qc::ComparisonKind kind) { - std::unique_ptr thenPtr = - thenOp ? thenOp->clone() : nullptr; - std::unique_ptr elsePtr = - elseOp ? elseOp->clone() : nullptr; - return std::make_unique( - std::move(thenPtr), std::move(elsePtr), controlBit, - expectedVal, kind); - }), - "then_operation"_a, "else_operation"_a, "control_bit"_a, - "expected_value"_a = 1U, - "comparison_kind"_a = qc::ComparisonKind::Eq); - ifElse.def_property_readonly("then_operation", - &qc::IfElseOperation::getThenOp, - py::return_value_policy::reference_internal); - ifElse.def_property_readonly("else_operation", - &qc::IfElseOperation::getElseOp, - py::return_value_policy::reference_internal); - ifElse.def_property_readonly("control_register", - &qc::IfElseOperation::getControlRegister); - ifElse.def_property_readonly("control_bit", - &qc::IfElseOperation::getControlBit); - ifElse.def_property_readonly("expected_value_register", - &qc::IfElseOperation::getExpectedValueRegister); - ifElse.def_property_readonly("expected_value_bit", - &qc::IfElseOperation::getExpectedValueBit); - ifElse.def_property_readonly("comparison_kind", - &qc::IfElseOperation::getComparisonKind); + ifElse.def( + "__init__", + [](qc::IfElseOperation* self, qc::Operation* thenOp, + qc::Operation* elseOp, qc::ClassicalRegister& controlReg, + const std::uint64_t expectedVal, const qc::ComparisonKind kind) { + std::unique_ptr thenPtr = + thenOp ? thenOp->clone() : nullptr; + std::unique_ptr elsePtr = + elseOp ? elseOp->clone() : nullptr; + new (self) qc::IfElseOperation(std::move(thenPtr), std::move(elsePtr), + controlReg, expectedVal, kind); + }, + "then_operation"_a, "else_operation"_a, "control_register"_a, + "expected_value"_a = 1U, "comparison_kind"_a = qc::ComparisonKind::Eq); + ifElse.def( + "__init__", + [](qc::IfElseOperation* self, qc::Operation* thenOp, + qc::Operation* elseOp, qc::Bit controlBit, std::uint64_t expectedVal, + qc::ComparisonKind kind) { + std::unique_ptr thenPtr = + thenOp ? thenOp->clone() : nullptr; + std::unique_ptr elsePtr = + elseOp ? elseOp->clone() : nullptr; + new (self) qc::IfElseOperation(std::move(thenPtr), std::move(elsePtr), + controlBit, expectedVal, kind); + }, + "then_operation"_a, "else_operation"_a, "control_bit"_a, + "expected_value"_a = 1U, "comparison_kind"_a = qc::ComparisonKind::Eq); + ifElse.def_prop_ro("then_operation", &qc::IfElseOperation::getThenOp, + nb::rv_policy::reference_internal); + ifElse.def_prop_ro("else_operation", &qc::IfElseOperation::getElseOp, + nb::rv_policy::reference_internal); + ifElse.def_prop_ro("control_register", + &qc::IfElseOperation::getControlRegister); + ifElse.def_prop_ro("control_bit", &qc::IfElseOperation::getControlBit); + ifElse.def_prop_ro("expected_value_register", + &qc::IfElseOperation::getExpectedValueRegister); + ifElse.def_prop_ro("expected_value_bit", + &qc::IfElseOperation::getExpectedValueBit); + ifElse.def_prop_ro("comparison_kind", + &qc::IfElseOperation::getComparisonKind); ifElse.def("__repr__", [](const qc::IfElseOperation& op) { std::stringstream ss; ss << "IfElseOperation(<...then-op...>, <...else-op...>, "; diff --git a/bindings/ir/operations/register_non_unitary_operation.cpp b/bindings/ir/operations/register_non_unitary_operation.cpp index e14e23db25..d48c915cac 100644 --- a/bindings/ir/operations/register_non_unitary_operation.cpp +++ b/bindings/ir/operations/register_non_unitary_operation.cpp @@ -13,33 +13,28 @@ #include "ir/operations/OpType.hpp" #include "ir/operations/Operation.hpp" -// These includes must be the first includes for any bindings code -// clang-format off -#include -#include // NOLINT(misc-include-cleaner) - -#include -// clang-format on - +#include +#include +#include #include #include namespace mqt { -namespace py = pybind11; -using namespace pybind11::literals; +namespace nb = nanobind; +using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerNonUnitaryOperation(const py::module& m) { - py::class_(m, "NonUnitaryOperation") - .def(py::init, std::vector>(), +void registerNonUnitaryOperation(const nb::module_& m) { + nb::class_(m, "NonUnitaryOperation") + .def(nb::init, std::vector>(), "targets"_a, "classics"_a) - .def(py::init(), "target"_a, "classic"_a) - .def(py::init, qc::OpType>(), "targets"_a, + .def(nb::init(), "target"_a, "classic"_a) + .def(nb::init, qc::OpType>(), "targets"_a, "op_type"_a = qc::OpType::Reset) - .def_property_readonly( - "classics", py::overload_cast<>(&qc::NonUnitaryOperation::getClassics, - py::const_)) + .def_prop_ro("classics", + nb::overload_cast<>(&qc::NonUnitaryOperation::getClassics, + nb::const_)) .def("__repr__", [](const qc::NonUnitaryOperation& op) { std::stringstream ss; ss << "NonUnitaryOperation("; diff --git a/bindings/ir/operations/register_operation.cpp b/bindings/ir/operations/register_operation.cpp index e316eeb6ef..881f5a2254 100644 --- a/bindings/ir/operations/register_operation.cpp +++ b/bindings/ir/operations/register_operation.cpp @@ -11,34 +11,27 @@ #include "ir/operations/Control.hpp" #include "ir/operations/Operation.hpp" -// These includes must be the first includes for any bindings code -// clang-format off -#include -#include // NOLINT(misc-include-cleaner) - -#include -// clang-format on - +#include #include namespace mqt { -namespace py = pybind11; -using namespace pybind11::literals; +namespace nb = nanobind; +using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerOperation(const py::module& m) { - py::class_(m, "Operation") - .def_property_readonly("name", &qc::Operation::getName) - .def_property("type_", &qc::Operation::getType, &qc::Operation::setGate) - .def_property( +void registerOperation(const nb::module_& m) { + nb::class_(m, "Operation") + .def_prop_ro("name", &qc::Operation::getName) + .def_prop_rw("type_", &qc::Operation::getType, &qc::Operation::setGate) + .def_prop_rw( "targets", [](const qc::Operation& op) { return op.getTargets(); }, &qc::Operation::setTargets) - .def_property_readonly("num_targets", &qc::Operation::getNtargets) - .def_property( + .def_prop_ro("num_targets", &qc::Operation::getNtargets) + .def_prop_rw( "controls", [](const qc::Operation& op) { return op.getControls(); }, &qc::Operation::setControls) - .def_property_readonly("num_controls", &qc::Operation::getNcontrols) + .def_prop_ro("num_controls", &qc::Operation::getNcontrols) .def("add_control", &qc::Operation::addControl, "control"_a) .def("add_controls", &qc::Operation::addControls, "controls"_a) .def("clear_controls", &qc::Operation::clearControls) @@ -49,7 +42,7 @@ void registerOperation(const py::module& m) { .def("remove_controls", &qc::Operation::removeControls, "controls"_a) .def("get_used_qubits", &qc::Operation::getUsedQubits) .def("acts_on", &qc::Operation::actsOn, "qubit"_a) - .def_property( + .def_prop_rw( "parameter", [](const qc::Operation& op) { return op.getParameter(); }, &qc::Operation::setParameter) diff --git a/bindings/ir/operations/register_optype.cpp b/bindings/ir/operations/register_optype.cpp index 3cc10f3e48..69ed7dea9c 100644 --- a/bindings/ir/operations/register_optype.cpp +++ b/bindings/ir/operations/register_optype.cpp @@ -10,23 +10,16 @@ #include "ir/operations/OpType.hpp" -// These includes must be the first includes for any bindings code -// clang-format off -#include -#include // NOLINT(misc-include-cleaner) - -#include -// clang-format on +#include namespace mqt { -namespace py = pybind11; -using namespace pybind11::literals; +namespace nb = nanobind; // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerOptype(const py::module& m) { - py::native_enum(m, "OpType", "enum.Enum", - "Enumeration of operation types.") +void registerOptype(const nb::module_& m) { + nb::enum_(m, "OpType", "enum.Enum", + "Enumeration of operation types.") .value("none", qc::OpType::None) .value("gphase", qc::OpType::GPhase) .value("i", qc::OpType::I) @@ -67,8 +60,7 @@ void registerOptype(const py::module& m) { .value("reset", qc::OpType::Reset) .value("barrier", qc::OpType::Barrier) .value("if_else", qc::OpType::IfElse) - .export_values() - .finalize(); + .export_values(); } } // namespace mqt diff --git a/bindings/ir/operations/register_standard_operation.cpp b/bindings/ir/operations/register_standard_operation.cpp index 461105798b..9a99ca3024 100644 --- a/bindings/ir/operations/register_standard_operation.cpp +++ b/bindings/ir/operations/register_standard_operation.cpp @@ -14,47 +14,43 @@ #include "ir/operations/Operation.hpp" #include "ir/operations/StandardOperation.hpp" -// These includes must be the first includes for any bindings code -// clang-format off -#include -#include // NOLINT(misc-include-cleaner) - -#include -// clang-format on - +#include +#include +#include +#include #include #include namespace mqt { -namespace py = pybind11; -using namespace pybind11::literals; +namespace nb = nanobind; +using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerStandardOperation(const py::module& m) { - py::class_(m, "StandardOperation") - .def(py::init<>()) - .def(py::init>(), "target"_a, +void registerStandardOperation(const nb::module_& m) { + nb::class_(m, "StandardOperation") + .def(nb::init<>()) + .def(nb::init>(), "target"_a, "op_type"_a, "params"_a = std::vector{}) - .def(py::init>(), + .def(nb::init>(), "targets"_a, "op_type"_a, "params"_a = std::vector{}) - .def(py::init&>(), "control"_a, "target"_a, "op_type"_a, "params"_a = std::vector{}) - .def(py::init&>(), "control"_a, "targets"_a, "op_type"_a, "params"_a = std::vector{}) - .def(py::init&>(), "controls"_a, "target"_a, "op_type"_a, "params"_a = std::vector{}) - .def(py::init>(), "controls"_a, "targets"_a, "op_type"_a, "params"_a = std::vector{}) - .def(py::init>(), "controls"_a, "target0"_a, "target1"_a, "op_type"_a, "params"_a = std::vector{}) diff --git a/bindings/ir/operations/register_symbolic_operation.cpp b/bindings/ir/operations/register_symbolic_operation.cpp index 997ab6193d..2576f86299 100644 --- a/bindings/ir/operations/register_symbolic_operation.cpp +++ b/bindings/ir/operations/register_symbolic_operation.cpp @@ -15,54 +15,48 @@ #include "ir/operations/StandardOperation.hpp" #include "ir/operations/SymbolicOperation.hpp" -// These includes must be the first includes for any bindings code -// clang-format off -#include -#include // NOLINT(misc-include-cleaner) - -#include -// clang-format on - +#include +#include #include namespace mqt { -namespace py = pybind11; -using namespace pybind11::literals; +namespace nb = nanobind; +using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerSymbolicOperation(const py::module& m) { - py::class_( +void registerSymbolicOperation(const nb::module_& m) { + nb::class_( m, "SymbolicOperation", "Class representing a symbolic operation." "This encompasses all symbolic versions of `StandardOperation` that " "involve (float) angle parameters.") - .def(py::init<>(), "Create an empty symbolic operation.") - .def(py::init(), "Create an empty symbolic operation.") + .def(nb::init&>(), "target"_a, "op_type"_a, "params"_a = std::vector{}) - .def(py::init&>(), "targets"_a, "op_type"_a, "params"_a = std::vector{}) - .def(py::init&>(), "control"_a, "target"_a, "op_type"_a, "params"_a = std::vector{}) - .def(py::init&>(), "control"_a, "targets"_a, "op_type"_a, "params"_a = std::vector{}) - .def(py::init&>(), "controls"_a, "target"_a, "op_type"_a, "params"_a = std::vector{}) - .def(py::init&>(), "controls"_a, "targets"_a, "op_type"_a, "params"_a = std::vector{}) - .def(py::init&>(), "controls"_a, "target0"_a, "target1"_a, "op_type"_a, "params"_a = std::vector{}) diff --git a/bindings/ir/register_ir.cpp b/bindings/ir/register_ir.cpp index 00cd53c340..0aec88fe32 100644 --- a/bindings/ir/register_ir.cpp +++ b/bindings/ir/register_ir.cpp @@ -8,33 +8,29 @@ * Licensed under the MIT License */ -// These includes must be the first includes for any bindings code -// clang-format off -#include -#include // NOLINT(misc-include-cleaner) -// clang-format on +#include namespace mqt { -namespace py = pybind11; -using namespace py::literals; +namespace nb = nanobind; // forward declarations -void registerRegisters(py::module& m); -void registerPermutation(py::module& m); -void registerOperations(py::module& m); -void registerSymbolic(py::module& m); -void registerQuantumComputation(py::module& m); +void registerRegisters(nb::module_& m); +void registerPermutation(nb::module_& m); +void registerOperations(nb::module_& m); +void registerSymbolic(nb::module_& m); +void registerQuantumComputation(nb::module_& m); -PYBIND11_MODULE(MQT_CORE_MODULE_NAME, m, py::mod_gil_not_used()) { +NB_MODULE(MQT_CORE_MODULE_NAME, m) { registerPermutation(m); - py::module registers = m.def_submodule("registers"); - registerRegisters(registers); - py::module symbolic = m.def_submodule("symbolic"); + nb::module_ symbolic = m.def_submodule("symbolic"); registerSymbolic(symbolic); - py::module operations = m.def_submodule("operations"); + nb::module_ registers = m.def_submodule("registers"); + registerRegisters(registers); + + nb::module_ operations = m.def_submodule("operations"); registerOperations(operations); registerQuantumComputation(m); diff --git a/bindings/ir/register_operations.cpp b/bindings/ir/register_operations.cpp index d8250ed915..4fc8e3fe6f 100644 --- a/bindings/ir/register_operations.cpp +++ b/bindings/ir/register_operations.cpp @@ -8,29 +8,24 @@ * Licensed under the MIT License */ -// These includes must be the first includes for any bindings code -// clang-format off -#include -#include // NOLINT(misc-include-cleaner) -// clang-format on +#include namespace mqt { -namespace py = pybind11; -using namespace pybind11::literals; +namespace nb = nanobind; // forward declarations -void registerOptype(const py::module& m); -void registerControl(const py::module& m); -void registerOperation(const py::module& m); -void registerStandardOperation(const py::module& m); -void registerCompoundOperation(const py::module& m); -void registerNonUnitaryOperation(const py::module& m); -void registerSymbolicOperation(const py::module& m); -void registerIfElseOperation(const py::module& m); +void registerOptype(const nb::module_& m); +void registerControl(const nb::module_& m); +void registerOperation(const nb::module_& m); +void registerStandardOperation(const nb::module_& m); +void registerCompoundOperation(const nb::module_& m); +void registerNonUnitaryOperation(const nb::module_& m); +void registerSymbolicOperation(const nb::module_& m); +void registerIfElseOperation(const nb::module_& m); // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerOperations(py::module& m) { +void registerOperations(nb::module_& m) { registerOptype(m); registerControl(m); registerOperation(m); diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index 237112bc71..7e64410ff1 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -12,44 +12,39 @@ #include "ir/Permutation.hpp" #include "ir/operations/Control.hpp" -// These includes must be the first includes for any bindings code -// clang-format off -#include -#include // NOLINT(misc-include-cleaner) - -#include -#include -#include -#include -// clang-format on - #include +#include +#include +#include +#include #include namespace mqt { -namespace py = pybind11; -using namespace pybind11::literals; +namespace nb = nanobind; +using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerPermutation(py::module& m) { - py::class_(m, "Permutation") - .def(py::init<>()) - .def(py::init([](const py::dict& p) { - qc::Permutation perm; - for (const auto& [key, value] : p) { - perm[key.cast()] = value.cast(); - } - return perm; - }), - "perm"_a, "Create a permutation from a dictionary.") +void registerPermutation(nb::module_& m) { + nb::class_(m, "Permutation") + .def(nb::init<>()) + .def( + "__init__", + [](qc::Permutation* self, const nb::dict& p) { + qc::Permutation perm; + for (const auto& [key, value] : p) { + perm[nb::cast(key)] = nb::cast(value); + } + new (self) qc::Permutation(std::move(perm)); + }, + "perm"_a, "Create a permutation from a dictionary.") .def("apply", - py::overload_cast(&qc::Permutation::apply, - py::const_), + nb::overload_cast(&qc::Permutation::apply, + nb::const_), "controls"_a) .def("apply", - py::overload_cast(&qc::Permutation::apply, - py::const_), + nb::overload_cast(&qc::Permutation::apply, + nb::const_), "targets"_a) .def("clear", [](qc::Permutation& p) { p.clear(); }) .def("__getitem__", @@ -61,17 +56,19 @@ void registerPermutation(py::module& m) { .def("__len__", &qc::Permutation::size) .def("__iter__", [](const qc::Permutation& p) { - return py::make_key_iterator(p.begin(), p.end()); + return nb::make_key_iterator( + nb::handle(), "PermutationKeyIterator", p.begin(), p.end()); }) .def( "items", [](const qc::Permutation& p) { - return py::make_iterator(p.begin(), p.end()); + return nb::make_iterator(nb::handle(), "PermutationItemIterator", + p.begin(), p.end()); }, - py::keep_alive<0, 1>()) - .def(py::self == py::self) // NOLINT(misc-redundant-expression) - .def(py::self != py::self) // NOLINT(misc-redundant-expression) - .def(hash(py::self)) + nb::keep_alive<0, 1>()) + .def(nb::self == nb::self) + .def(nb::self != nb::self) + .def(nb::hash(nb::self)) .def("__str__", [](const qc::Permutation& p) { std::stringstream ss; @@ -97,7 +94,6 @@ void registerPermutation(py::module& m) { ss << "})"; return ss.str(); }); - py::implicitly_convertible(); } } // namespace mqt diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index 902f5da612..151700dd7f 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -17,18 +17,12 @@ #include "ir/operations/Operation.hpp" #include "qasm3/Importer.hpp" -// These includes must be the first includes for any bindings code -// clang-format off -#include -#include // NOLINT(misc-include-cleaner) - -#include -#include -// clang-format on - #include #include #include +#include +#include +#include #include #include #include @@ -36,30 +30,30 @@ namespace mqt { -namespace py = pybind11; -using namespace pybind11::literals; +namespace nb = nanobind; +using namespace nb::literals; using DiffType = std::vector>::difference_type; using SizeType = std::vector>::size_type; // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerQuantumComputation(py::module& m) { +void registerQuantumComputation(nb::module_& m) { auto wrap = [](DiffType i, const SizeType size) { if (i < 0) { i += static_cast(size); } if (i < 0 || std::cmp_greater_equal(i, size)) { - throw py::index_error(); + throw nb::index_error(); } return i; }; - auto qc = py::class_(m, "QuantumComputation"); + auto qc = nb::class_(m, "QuantumComputation"); ///--------------------------------------------------------------------------- /// \n Constructors \n ///--------------------------------------------------------------------------- - qc.def(py::init(), "nq"_a = 0U, + qc.def(nb::init(), "nq"_a = 0U, "nc"_a = 0U, "seed"_a = 0U); // expose the static constructor from qasm strings or files @@ -70,25 +64,23 @@ void registerQuantumComputation(py::module& m) { /// \n General Properties \n ///--------------------------------------------------------------------------- - qc.def_property("name", &qc::QuantumComputation::getName, - &qc::QuantumComputation::setName); - qc.def_property_readonly("num_qubits", &qc::QuantumComputation::getNqubits); - qc.def_property_readonly("num_ancilla_qubits", - &qc::QuantumComputation::getNancillae); - qc.def_property_readonly("num_garbage_qubits", - &qc::QuantumComputation::getNgarbageQubits); - qc.def_property_readonly("num_measured_qubits", - &qc::QuantumComputation::getNmeasuredQubits); - qc.def_property_readonly("num_data_qubits", - &qc::QuantumComputation::getNqubitsWithoutAncillae); - qc.def_property_readonly("num_classical_bits", - &qc::QuantumComputation::getNcbits); - qc.def_property_readonly("num_ops", &qc::QuantumComputation::getNops); + qc.def_prop_rw("name", &qc::QuantumComputation::getName, + &qc::QuantumComputation::setName); + qc.def_prop_ro("num_qubits", &qc::QuantumComputation::getNqubits); + qc.def_prop_ro("num_ancilla_qubits", &qc::QuantumComputation::getNancillae); + qc.def_prop_ro("num_garbage_qubits", + &qc::QuantumComputation::getNgarbageQubits); + qc.def_prop_ro("num_measured_qubits", + &qc::QuantumComputation::getNmeasuredQubits); + qc.def_prop_ro("num_data_qubits", + &qc::QuantumComputation::getNqubitsWithoutAncillae); + qc.def_prop_ro("num_classical_bits", &qc::QuantumComputation::getNcbits); + qc.def_prop_ro("num_ops", &qc::QuantumComputation::getNops); qc.def("num_single_qubit_ops", &qc::QuantumComputation::getNsingleQubitOps); qc.def("num_total_ops", &qc::QuantumComputation::getNindividualOps); qc.def("depth", &qc::QuantumComputation::getDepth); - qc.def_property("global_phase", &qc::QuantumComputation::getGlobalPhase, - &qc::QuantumComputation::gphase); + qc.def_prop_rw("global_phase", &qc::QuantumComputation::getGlobalPhase, + &qc::QuantumComputation::gphase); qc.def("invert", &qc::QuantumComputation::invert); qc.def("to_operation", &qc::QuantumComputation::asOperation); @@ -102,18 +94,11 @@ void registerQuantumComputation(py::module& m) { i = wrap(i, circ.getNops()); return circ.at(static_cast(i)).get(); }, - py::return_value_policy::reference_internal, "index"_a); + nb::rv_policy::reference_internal, "index"_a); qc.def( "__getitem__", - [](qc::QuantumComputation& circ, const py::slice& slice) { - std::size_t start{}; - std::size_t stop{}; - std::size_t step{}; - std::size_t sliceLength{}; - if (!slice.compute(circ.getNops(), &start, &stop, &step, - &sliceLength)) { - throw py::error_already_set(); - } + [](qc::QuantumComputation& circ, const nb::slice& slice) { + auto [start, stop, step, sliceLength] = slice.compute(circ.getNops()); auto ops = std::vector(); ops.reserve(sliceLength); for (std::size_t i = start; i < stop; i += step) { @@ -121,7 +106,7 @@ void registerQuantumComputation(py::module& m) { } return ops; }, - py::return_value_policy::reference_internal, "index"_a); + nb::rv_policy::reference_internal, "index"_a); qc.def( "__setitem__", [&wrap](qc::QuantumComputation& circ, DiffType i, @@ -132,16 +117,9 @@ void registerQuantumComputation(py::module& m) { "index"_a, "value"_a); qc.def( "__setitem__", - [](qc::QuantumComputation& circ, const py::slice& slice, + [](qc::QuantumComputation& circ, const nb::slice& slice, const std::vector& ops) { - std::size_t start{}; - std::size_t stop{}; - std::size_t step{}; - std::size_t sliceLength{}; - if (!slice.compute(circ.getNops(), &start, &stop, &step, - &sliceLength)) { - throw py::error_already_set(); - } + auto [start, stop, step, sliceLength] = slice.compute(circ.getNops()); if (sliceLength != ops.size()) { throw std::runtime_error( "Length of slice and number of operations do not match."); @@ -161,15 +139,8 @@ void registerQuantumComputation(py::module& m) { "index"_a); qc.def( "__delitem__", - [](qc::QuantumComputation& circ, const py::slice& slice) { - std::size_t start{}; - std::size_t stop{}; - std::size_t step{}; - std::size_t sliceLength{}; - if (!slice.compute(circ.getNops(), &start, &stop, &step, - &sliceLength)) { - throw py::error_already_set(); - } + [](qc::QuantumComputation& circ, const nb::slice& slice) { + auto [start, stop, step, sliceLength] = slice.compute(circ.getNops()); // delete in reverse order to not invalidate indices for (std::size_t i = sliceLength; i > 0; --i) { circ.erase(circ.begin() + @@ -192,7 +163,7 @@ void registerQuantumComputation(py::module& m) { }, "value"_a); qc.def("reverse", &qc::QuantumComputation::reverse); - qc.def("clear", py::overload_cast<>(&qc::QuantumComputation::reset)); + qc.def("clear", nb::overload_cast<>(&qc::QuantumComputation::reset)); ///--------------------------------------------------------------------------- /// \n (Qu)Bit Registers \n @@ -208,28 +179,24 @@ void registerQuantumComputation(py::module& m) { qc.def("unify_quantum_registers", &qc::QuantumComputation::unifyQuantumRegisters, "name"_a = "q"); - qc.def_property_readonly("qregs", - &qc::QuantumComputation::getQuantumRegisters); - qc.def_property_readonly("cregs", - &qc::QuantumComputation::getClassicalRegisters); - qc.def_property_readonly("ancregs", - &qc::QuantumComputation::getAncillaRegisters); + qc.def_prop_ro("qregs", &qc::QuantumComputation::getQuantumRegisters); + qc.def_prop_ro("cregs", &qc::QuantumComputation::getClassicalRegisters); + qc.def_prop_ro("ancregs", &qc::QuantumComputation::getAncillaRegisters); ///--------------------------------------------------------------------------- /// \n Input Layout and Output Permutation \n ///--------------------------------------------------------------------------- - qc.def_readwrite("initial_layout", &qc::QuantumComputation::initialLayout); - qc.def_readwrite("output_permutation", - &qc::QuantumComputation::outputPermutation); + qc.def_rw("initial_layout", &qc::QuantumComputation::initialLayout); + qc.def_rw("output_permutation", &qc::QuantumComputation::outputPermutation); qc.def("initialize_io_mapping", &qc::QuantumComputation::initializeIOMapping); ///--------------------------------------------------------------------------- /// \n Ancillary and Garbage Handling \n ///--------------------------------------------------------------------------- - qc.def_property_readonly( - "ancillary", py::overload_cast<>(&qc::QuantumComputation::getAncillary)); + qc.def_prop_ro("ancillary", + nb::overload_cast<>(&qc::QuantumComputation::getAncillary)); qc.def("set_circuit_qubit_ancillary", &qc::QuantumComputation::setLogicalQubitAncillary, "q"_a); qc.def("se_circuit_qubits_ancillary", @@ -237,8 +204,8 @@ void registerQuantumComputation(py::module& m) { "q_max"_a); qc.def("is_circuit_qubit_ancillary", &qc::QuantumComputation::logicalQubitIsAncillary, "q"_a); - qc.def_property_readonly( - "garbage", py::overload_cast<>(&qc::QuantumComputation::getGarbage)); + qc.def_prop_ro("garbage", + nb::overload_cast<>(&qc::QuantumComputation::getGarbage)); qc.def("set_circuit_qubit_garbage", &qc::QuantumComputation::setLogicalQubitGarbage, "q"_a); qc.def("set_circuit_qubits_garbage", @@ -251,7 +218,7 @@ void registerQuantumComputation(py::module& m) { /// \n Symbolic Circuit Handling \n ///--------------------------------------------------------------------------- - qc.def_property_readonly("variables", &qc::QuantumComputation::getVariables); + qc.def_prop_ro("variables", &qc::QuantumComputation::getVariables); qc.def("add_variable", &qc::QuantumComputation::addVariable, "var"_a); qc.def( "add_variables", @@ -325,10 +292,10 @@ void registerQuantumComputation(py::module& m) { DEFINE_SINGLE_TARGET_OPERATION(sxdg) #define DEFINE_SINGLE_TARGET_SINGLE_PARAMETER_OPERATION(op, param) \ - qc.def(#op, &qc::QuantumComputation::op, py::arg(#param), "q"_a); \ - qc.def("c" #op, &qc::QuantumComputation::c##op, py::arg(#param), \ + qc.def(#op, &qc::QuantumComputation::op, nb::arg(#param), "q"_a); \ + qc.def("c" #op, &qc::QuantumComputation::c##op, nb::arg(#param), \ "control"_a, "target"_a); \ - qc.def("mc" #op, &qc::QuantumComputation::mc##op, py::arg(#param), \ + qc.def("mc" #op, &qc::QuantumComputation::mc##op, nb::arg(#param), \ "controls"_a, "target"_a); DEFINE_SINGLE_TARGET_SINGLE_PARAMETER_OPERATION(rx, theta) @@ -337,24 +304,24 @@ void registerQuantumComputation(py::module& m) { DEFINE_SINGLE_TARGET_SINGLE_PARAMETER_OPERATION(p, theta) #define DEFINE_SINGLE_TARGET_TWO_PARAMETER_OPERATION(op, param0, param1) \ - qc.def(#op, &qc::QuantumComputation::op, py::arg(#param0), py::arg(#param1), \ + qc.def(#op, &qc::QuantumComputation::op, nb::arg(#param0), nb::arg(#param1), \ "q"_a); \ - qc.def("c" #op, &qc::QuantumComputation::c##op, py::arg(#param0), \ - py::arg(#param1), "control"_a, "target"_a); \ - qc.def("mc" #op, &qc::QuantumComputation::mc##op, py::arg(#param0), \ - py::arg(#param1), "controls"_a, "target"_a); + qc.def("c" #op, &qc::QuantumComputation::c##op, nb::arg(#param0), \ + nb::arg(#param1), "control"_a, "target"_a); \ + qc.def("mc" #op, &qc::QuantumComputation::mc##op, nb::arg(#param0), \ + nb::arg(#param1), "controls"_a, "target"_a); DEFINE_SINGLE_TARGET_TWO_PARAMETER_OPERATION(u2, phi, lambda_) DEFINE_SINGLE_TARGET_TWO_PARAMETER_OPERATION(r, theta, phi) #define DEFINE_SINGLE_TARGET_THREE_PARAMETER_OPERATION(op, param0, param1, \ param2) \ - qc.def(#op, &qc::QuantumComputation::op, py::arg(#param0), py::arg(#param1), \ - py::arg(#param2), "q"_a); \ - qc.def("c" #op, &qc::QuantumComputation::c##op, py::arg(#param0), \ - py::arg(#param1), py::arg(#param2), "control"_a, "target"_a); \ - qc.def("mc" #op, &qc::QuantumComputation::mc##op, py::arg(#param0), \ - py::arg(#param1), py::arg(#param2), "controls"_a, "target"_a); + qc.def(#op, &qc::QuantumComputation::op, nb::arg(#param0), nb::arg(#param1), \ + nb::arg(#param2), "q"_a); \ + qc.def("c" #op, &qc::QuantumComputation::c##op, nb::arg(#param0), \ + nb::arg(#param1), nb::arg(#param2), "control"_a, "target"_a); \ + qc.def("mc" #op, &qc::QuantumComputation::mc##op, nb::arg(#param0), \ + nb::arg(#param1), nb::arg(#param2), "controls"_a, "target"_a); DEFINE_SINGLE_TARGET_THREE_PARAMETER_OPERATION(u, theta, phi, lambda_) @@ -373,11 +340,11 @@ void registerQuantumComputation(py::module& m) { DEFINE_TWO_TARGET_OPERATION(peresdg) #define DEFINE_TWO_TARGET_SINGLE_PARAMETER_OPERATION(op, param) \ - qc.def(#op, &qc::QuantumComputation::op, py::arg(#param), "target1"_a, \ + qc.def(#op, &qc::QuantumComputation::op, nb::arg(#param), "target1"_a, \ "target2"_a); \ - qc.def("c" #op, &qc::QuantumComputation::c##op, py::arg(#param), \ + qc.def("c" #op, &qc::QuantumComputation::c##op, nb::arg(#param), \ "control"_a, "target1"_a, "target2"_a); \ - qc.def("mc" #op, &qc::QuantumComputation::mc##op, py::arg(#param), \ + qc.def("mc" #op, &qc::QuantumComputation::mc##op, nb::arg(#param), \ "controls"_a, "target1"_a, "target2"_a); DEFINE_TWO_TARGET_SINGLE_PARAMETER_OPERATION(rxx, theta) @@ -386,12 +353,12 @@ void registerQuantumComputation(py::module& m) { DEFINE_TWO_TARGET_SINGLE_PARAMETER_OPERATION(rzx, theta) #define DEFINE_TWO_TARGET_TWO_PARAMETER_OPERATION(op, param0, param1) \ - qc.def(#op, &qc::QuantumComputation::op, py::arg(#param0), py::arg(#param1), \ + qc.def(#op, &qc::QuantumComputation::op, nb::arg(#param0), nb::arg(#param1), \ "target1"_a, "target2"_a); \ - qc.def("c" #op, &qc::QuantumComputation::c##op, py::arg(#param0), \ - py::arg(#param1), "control"_a, "target1"_a, "target2"_a); \ - qc.def("mc" #op, &qc::QuantumComputation::mc##op, py::arg(#param0), \ - py::arg(#param1), "controls"_a, "target1"_a, "target2"_a); + qc.def("c" #op, &qc::QuantumComputation::c##op, nb::arg(#param0), \ + nb::arg(#param1), "control"_a, "target1"_a, "target2"_a); \ + qc.def("mc" #op, &qc::QuantumComputation::mc##op, nb::arg(#param0), \ + nb::arg(#param1), "controls"_a, "target1"_a, "target2"_a); DEFINE_TWO_TARGET_TWO_PARAMETER_OPERATION(xx_minus_yy, theta, beta) DEFINE_TWO_TARGET_TWO_PARAMETER_OPERATION(xx_plus_yy, theta, beta) @@ -407,28 +374,28 @@ void registerQuantumComputation(py::module& m) { qc.def("gphase", &qc::QuantumComputation::gphase, "phase"_a); qc.def("measure", - py::overload_cast( + nb::overload_cast( &qc::QuantumComputation::measure), "qubit"_a, "cbit"_a); qc.def("measure", - py::overload_cast&, + nb::overload_cast&, const std::vector&>( &qc::QuantumComputation::measure), "qubits"_a, "cbits"_a); - qc.def("measure_all", &qc::QuantumComputation::measureAll, py::kw_only(), + qc.def("measure_all", &qc::QuantumComputation::measureAll, nb::kw_only(), "add_bits"_a = true); - qc.def("reset", py::overload_cast(&qc::QuantumComputation::reset), + qc.def("reset", nb::overload_cast(&qc::QuantumComputation::reset), "q"_a); qc.def("reset", - py::overload_cast&>( + nb::overload_cast&>( &qc::QuantumComputation::reset), "qubits"_a); - qc.def("barrier", py::overload_cast<>(&qc::QuantumComputation::barrier)); + qc.def("barrier", nb::overload_cast<>(&qc::QuantumComputation::barrier)); qc.def("barrier", - py::overload_cast(&qc::QuantumComputation::barrier), "q"_a); - qc.def("barrier", py::overload_cast&>( + nb::overload_cast(&qc::QuantumComputation::barrier), "q"_a); + qc.def("barrier", nb::overload_cast&>( &qc::QuantumComputation::barrier)); qc.def( @@ -464,7 +431,7 @@ void registerQuantumComputation(py::module& m) { qc.def( "if_", - py::overload_cast&>( &qc::QuantumComputation::if_), @@ -473,7 +440,7 @@ void registerQuantumComputation(py::module& m) { "params"_a = std::vector{}); qc.def( "if_", - py::overload_cast&>( &qc::QuantumComputation::if_), @@ -482,7 +449,7 @@ void registerQuantumComputation(py::module& m) { "params"_a = std::vector{}); qc.def( "if_", - py::overload_cast&>( &qc::QuantumComputation::if_), @@ -490,7 +457,7 @@ void registerQuantumComputation(py::module& m) { "expected_value"_a = 1U, "comparison_kind"_a = qc::ComparisonKind::Eq, "params"_a = std::vector{}); qc.def("if_", - py::overload_cast&>( &qc::QuantumComputation::if_), @@ -498,7 +465,7 @@ void registerQuantumComputation(py::module& m) { "comparison_kind"_a = qc::ComparisonKind::Eq, "params"_a = std::vector{}); qc.def("if_", - py::overload_cast&>( &qc::QuantumComputation::if_), @@ -508,7 +475,7 @@ void registerQuantumComputation(py::module& m) { "params"_a = std::vector{}); qc.def( "if_", - py::overload_cast&>( &qc::QuantumComputation::if_), diff --git a/bindings/ir/register_registers.cpp b/bindings/ir/register_registers.cpp index 991327d7f7..b34086086d 100644 --- a/bindings/ir/register_registers.cpp +++ b/bindings/ir/register_registers.cpp @@ -11,47 +11,41 @@ #include "ir/Definitions.hpp" #include "ir/Register.hpp" -// These includes must be the first includes for any bindings code -// clang-format off -#include -#include // NOLINT(misc-include-cleaner) - -#include -#include -// clang-format on - #include +#include +#include +#include #include namespace mqt { -namespace py = pybind11; -using namespace pybind11::literals; +namespace nb = nanobind; +using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerRegisters(py::module& m) { - py::class_(m, "QuantumRegister") - .def(py::init(), +void registerRegisters(nb::module_& m) { + nb::class_(m, "QuantumRegister") + .def(nb::init(), "start"_a, "size"_a, "name"_a = "") - .def_property_readonly( - "name", [](const qc::QuantumRegister& reg) { return reg.getName(); }) - .def_property( + .def_prop_ro("name", + [](const qc::QuantumRegister& reg) { return reg.getName(); }) + .def_prop_rw( "start", [](const qc::QuantumRegister& reg) { return reg.getStartIndex(); }, [](qc::QuantumRegister& reg, const qc::Qubit start) { reg.getStartIndex() = start; }) - .def_property( + .def_prop_rw( "size", [](const qc::QuantumRegister& reg) { return reg.getSize(); }, [](qc::QuantumRegister& reg, const std::size_t size) { reg.getSize() = size; }) - .def_property_readonly( + .def_prop_ro( "end", [](const qc::QuantumRegister& reg) { return reg.getEndIndex(); }) - .def(py::self == py::self) // NOLINT(misc-redundant-expression) - .def(py::self != py::self) // NOLINT(misc-redundant-expression) - .def(hash(py::self)) + .def(nb::self == nb::self) // NOLINT(misc-redundant-expression) + .def(nb::self != nb::self) // NOLINT(misc-redundant-expression) + .def(hash(nb::self)) .def("__getitem__", &qc::QuantumRegister::getGlobalIndex, "key"_a) .def("__contains__", &qc::QuantumRegister::contains) .def("__repr__", [](const qc::QuantumRegister& reg) { @@ -60,30 +54,30 @@ void registerRegisters(py::module& m) { ", size=" + std::to_string(reg.getSize()) + ")"; }); - py::class_(m, "ClassicalRegister") - .def(py::init(), + nb::class_(m, "ClassicalRegister") + .def(nb::init(), "start"_a, "size"_a, "name"_a = "") - .def_property_readonly( + .def_prop_ro( "name", [](const qc::ClassicalRegister& reg) { return reg.getName(); }) - .def_property( + .def_prop_rw( "start", [](const qc::ClassicalRegister& reg) { return reg.getStartIndex(); }, [](qc::ClassicalRegister& reg, const qc::Bit start) { reg.getStartIndex() = start; }) - .def_property( + .def_prop_rw( "size", [](const qc::ClassicalRegister& reg) { return reg.getSize(); }, [](qc::ClassicalRegister& reg, const std::size_t size) { reg.getSize() = size; }) - .def_property_readonly( + .def_prop_ro( "end", [](const qc::ClassicalRegister& reg) { return reg.getEndIndex(); }) - .def(py::self == py::self) // NOLINT(misc-redundant-expression) - .def(py::self != py::self) // NOLINT(misc-redundant-expression) - .def(hash(py::self)) + .def(nb::self == nb::self) + .def(nb::self != nb::self) + .def(nb::hash(nb::self)) .def("__getitem__", &qc::ClassicalRegister::getGlobalIndex, "key"_a) .def("__contains__", &qc::ClassicalRegister::contains) .def("__repr__", [](const qc::ClassicalRegister& reg) { diff --git a/bindings/ir/register_symbolic.cpp b/bindings/ir/register_symbolic.cpp index c9cd9f4d8e..7935b7bf3a 100644 --- a/bindings/ir/register_symbolic.cpp +++ b/bindings/ir/register_symbolic.cpp @@ -8,24 +8,19 @@ * Licensed under the MIT License */ -// These includes must be the first includes for any bindings code -// clang-format off -#include -#include // NOLINT(misc-include-cleaner) -// clang-format on +#include namespace mqt { -namespace py = pybind11; -using namespace pybind11::literals; +namespace nb = nanobind; // forward declarations -void registerVariable(py::module& m); -void registerTerm(py::module& m); -void registerExpression(py::module& m); +void registerVariable(nb::module_& m); +void registerTerm(nb::module_& m); +void registerExpression(nb::module_& m); // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerSymbolic(pybind11::module& m) { +void registerSymbolic(nb::module_& m) { registerVariable(m); registerTerm(m); registerExpression(m); diff --git a/bindings/ir/symbolic/register_expression.cpp b/bindings/ir/symbolic/register_expression.cpp index b9e546f310..5598f21873 100644 --- a/bindings/ir/symbolic/register_expression.cpp +++ b/bindings/ir/symbolic/register_expression.cpp @@ -10,52 +10,52 @@ #include "ir/operations/Expression.hpp" -// These includes must be the first includes for any bindings code -// clang-format off -#include -#include // NOLINT(misc-include-cleaner) - -#include -#include -#include -// clang-format on - #include +#include +#include +#include +#include #include #include namespace mqt { -namespace py = pybind11; -using namespace pybind11::literals; +namespace nb = nanobind; +using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerExpression(py::module& m) { - py::class_>(m, "Expression") - .def(py::init([](const std::vector>& terms, - double constant) { - return sym::Expression(terms, constant); - }), - "terms"_a, "constant"_a = 0.0) - .def(py::init([](const sym::Term& term, double constant) { - return sym::Expression( - std::vector>{term}, constant); - }), - "term"_a, "constant"_a = 0.0) - .def(py::init(), "constant"_a = 0.0) - .def_property("constant", &sym::Expression::getConst, - &sym::Expression::setConst) +void registerExpression(nb::module_& m) { + nb::class_>(m, "Expression") + .def( + "__init__", + [](sym::Expression* self, + const std::vector>& terms, double constant) { + new (self) sym::Expression(terms, constant); + }, + "terms"_a, "constant"_a = 0.0) + .def( + "__init__", + [](sym::Expression* self, + const sym::Term& term, double constant) { + new (self) sym::Expression( + std::vector>{term}, constant); + }, + "term"_a, "constant"_a = 0.0) + .def(nb::init(), "constant"_a = 0.0) + .def_prop_rw("constant", &sym::Expression::getConst, + &sym::Expression::setConst) .def( "__iter__", [](const sym::Expression& expr) { - return py::make_iterator(expr.begin(), expr.end()); + return nb::make_iterator(nb::handle(), "ExpressionIterator", + expr.begin(), expr.end()); }, - py::keep_alive<0, 1>()) + nb::keep_alive<0, 1>()) .def("__getitem__", [](const sym::Expression& expr, const std::size_t idx) { if (idx >= expr.numTerms()) { - throw py::index_error(); + throw nb::index_error(); } return expr.getTerms()[idx]; }) @@ -63,15 +63,13 @@ void registerExpression(py::module& m) { .def("is_constant", &sym::Expression::isConstant) .def("num_terms", &sym::Expression::numTerms) .def("__len__", &sym::Expression::numTerms) - .def_property_readonly("terms", - &sym::Expression::getTerms) - .def_property_readonly("variables", - &sym::Expression::getVariables) + .def_prop_ro("terms", &sym::Expression::getTerms) + .def_prop_ro("variables", &sym::Expression::getVariables) .def("evaluate", &sym::Expression::evaluate, "assignment"_a) // addition operators - .def(py::self + py::self) - .def(py::self + double()) + .def(nb::self + nb::self) + .def(nb::self + double()) .def("__add__", [](const sym::Expression& lhs, const sym::Term& rhs) { return lhs + rhs; }) .def("__radd__", [](const sym::Expression& rhs, @@ -79,24 +77,24 @@ void registerExpression(py::module& m) { .def("__radd__", [](const sym::Expression& rhs, const double lhs) { return rhs + lhs; }) // subtraction operators - .def(py::self - py::self) // NOLINT(misc-redundant-expression) - .def(py::self - double()) - .def(double() - py::self) + .def(nb::self - nb::self) // NOLINT(misc-redundant-expression) + .def(nb::self - double()) + .def(double() - nb::self) .def("__sub__", [](const sym::Expression& lhs, const sym::Term& rhs) { return lhs - rhs; }) .def("__rsub__", [](const sym::Expression& rhs, const sym::Term& lhs) { return lhs - rhs; }) // multiplication operators - .def(py::self * double()) - .def(double() * py::self) + .def(nb::self * double()) + .def(double() * nb::self) // division operators - .def(py::self / double()) + .def(nb::self / double()) .def("__rtruediv__", [](const sym::Expression& rhs, double lhs) { return rhs / lhs; }) // comparison operators - .def(py::self == py::self) // NOLINT(misc-redundant-expression) - .def(py::self != py::self) // NOLINT(misc-redundant-expression) - .def(hash(py::self)) + .def(nb::self == nb::self) + .def(nb::self != nb::self) + .def(nb::hash(nb::self)) .def("__str__", [](const sym::Expression& expr) { std::stringstream ss; diff --git a/bindings/ir/symbolic/register_term.cpp b/bindings/ir/symbolic/register_term.cpp index 5e9637b769..54fb6f5d9d 100644 --- a/bindings/ir/symbolic/register_term.cpp +++ b/bindings/ir/symbolic/register_term.cpp @@ -10,39 +10,33 @@ #include "ir/operations/Expression.hpp" -// These includes must be the first includes for any bindings code -// clang-format off -#include -#include // NOLINT(misc-include-cleaner) - -#include -#include -// clang-format on - +#include +#include +#include #include namespace mqt { -namespace py = pybind11; -using namespace pybind11::literals; +namespace nb = nanobind; +using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerTerm(py::module& m) { - py::class_>(m, "Term") - .def(py::init(), "variable"_a, +void registerTerm(nb::module_& m) { + nb::class_>(m, "Term") + .def(nb::init(), "variable"_a, "coefficient"_a = 1.0) - .def_property_readonly("variable", &sym::Term::getVar) - .def_property_readonly("coefficient", &sym::Term::getCoeff) + .def_prop_ro("variable", &sym::Term::getVar) + .def_prop_ro("coefficient", &sym::Term::getCoeff) .def("has_zero_coefficient", &sym::Term::hasZeroCoeff) .def("add_coefficient", &sym::Term::addCoeff, "coeff"_a) .def("evaluate", &sym::Term::evaluate, "assignment"_a) - .def(py::self * double()) - .def(double() * py::self) - .def(py::self / double()) - .def(double() / py::self) - .def(py::self == py::self) // NOLINT(misc-redundant-expression) - .def(py::self != py::self) // NOLINT(misc-redundant-expression) - .def(hash(py::self)) + .def(nb::self * double()) + .def(double() * nb::self) + .def(nb::self / double()) + .def(double() / nb::self) + .def(nb::self == nb::self) + .def(nb::self != nb::self) + .def(nb::hash(nb::self)) .def("__str__", [](const sym::Term& term) { std::stringstream ss; diff --git a/bindings/ir/symbolic/register_variable.cpp b/bindings/ir/symbolic/register_variable.cpp index ca7d91405c..a7e8e2ec3f 100644 --- a/bindings/ir/symbolic/register_variable.cpp +++ b/bindings/ir/symbolic/register_variable.cpp @@ -10,33 +10,27 @@ #include "ir/operations/Expression.hpp" -// These includes must be the first includes for any bindings code -// clang-format off -#include -#include // NOLINT(misc-include-cleaner) - -#include -#include -// clang-format on - +#include +#include +#include #include namespace mqt { -namespace py = pybind11; -using namespace pybind11::literals; +namespace nb = nanobind; +using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerVariable(py::module& m) { - py::class_(m, "Variable") - .def(py::init(), "name"_a = "") - .def_property_readonly("name", &sym::Variable::getName) +void registerVariable(nb::module_& m) { + nb::class_(m, "Variable") + .def(nb::init(), "name"_a = "") + .def_prop_ro("name", &sym::Variable::getName) .def("__str__", &sym::Variable::getName) .def("__repr__", &sym::Variable::getName) - .def(py::self == py::self) // NOLINT(misc-redundant-expression) - .def(py::self != py::self) // NOLINT(misc-redundant-expression) - .def(hash(py::self)) - .def(py::self < py::self) // NOLINT(misc-redundant-expression) - .def(py::self > py::self); // NOLINT(misc-redundant-expression) + .def(nb::self == nb::self) + .def(nb::self != nb::self) + .def(nb::hash(nb::self)) + .def(nb::self < nb::self) + .def(nb::self > nb::self); } } // namespace mqt diff --git a/cmake/AddMQTPythonBinding.cmake b/cmake/AddMQTPythonBinding.cmake index f75be0808c..be9e890684 100644 --- a/cmake/AddMQTPythonBinding.cmake +++ b/cmake/AddMQTPythonBinding.cmake @@ -53,3 +53,47 @@ function(add_mqt_python_binding package_name target_name) DESTINATION ${ARG_INSTALL_DIR} COMPONENT ${MQT_${package_name}_TARGET_NAME}_Python) endfunction() + +function(add_mqt_python_binding_nanobind package_name target_name) + # parse the arguments + cmake_parse_arguments(ARG "" "MODULE_NAME;INSTALL_DIR" "LINK_LIBS" ${ARGN}) + set(SOURCES ${ARG_UNPARSED_ARGUMENTS}) + + # declare the Python module + nanobind_add_module( + # name of the extension + ${target_name} + # source code goes here + ${SOURCES}) + + # set default "." for INSTALL_DIR + if(NOT ARG_INSTALL_DIR) + set(ARG_INSTALL_DIR ".") + endif() + + if(ARG_MODULE_NAME) + # the library name must be the same as the module name + set_target_properties(${target_name} PROPERTIES OUTPUT_NAME ${ARG_MODULE_NAME}) + target_compile_definitions(${target_name} + PRIVATE MQT_${package_name}_MODULE_NAME=${ARG_MODULE_NAME}) + else() + # use the target name as the module name + target_compile_definitions(${target_name} + PRIVATE MQT_${package_name}_MODULE_NAME=${target_name}) + endif() + + # add project libraries to the link libraries + list(APPEND ARG_LINK_LIBS MQT::ProjectOptions MQT::ProjectWarnings) + + # Set c++ standard + target_compile_features(${target_name} PRIVATE cxx_std_20) + + # link the required libraries + target_link_libraries(${target_name} PRIVATE ${ARG_LINK_LIBS}) + + # install directive for scikit-build-core + install( + TARGETS ${target_name} + DESTINATION ${ARG_INSTALL_DIR} + COMPONENT ${MQT_${package_name}_TARGET_NAME}_Python) +endfunction() diff --git a/cmake/ExternalDependencies.cmake b/cmake/ExternalDependencies.cmake index d96db70872..e67aac6c85 100644 --- a/cmake/ExternalDependencies.cmake +++ b/cmake/ExternalDependencies.cmake @@ -26,6 +26,13 @@ if(BUILD_MQT_CORE_BINDINGS) # add pybind11 library find_package(pybind11 3.0.1 CONFIG REQUIRED) + + # Detect the installed nanobind package and import it into CMake + execute_process( + COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir + OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE nanobind_ROOT) + find_package(nanobind CONFIG REQUIRED) endif() set(JSON_VERSION diff --git a/pyproject.toml b/pyproject.toml index 3546c9bebf..a7457df8e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,12 @@ # Licensed under the MIT License [build-system] -requires = ["scikit-build-core>=0.11.6", "setuptools-scm>=9.2.2", "pybind11>=3.0.1"] +requires = [ + "nanobind>=2.10.1", + "pybind11>=3.0.1", + "scikit-build-core>=0.11.6", + "setuptools-scm>=9.2.2", +] build-backend = "scikit_build_core.build" [project] @@ -311,6 +316,7 @@ exclude = [ [dependency-groups] build = [ + "nanobind>=2.10.1", "pybind11>=3.0.1", "scikit-build-core>=0.11.6", "setuptools-scm>=9.2.2", diff --git a/uv.lock b/uv.lock index 67b9d90347..9ee9d1a41d 100644 --- a/uv.lock +++ b/uv.lock @@ -1497,12 +1497,14 @@ qiskit = [ [package.dev-dependencies] build = [ + { name = "nanobind" }, { name = "pybind11" }, { name = "scikit-build-core" }, { name = "setuptools-scm" }, ] dev = [ { name = "lit" }, + { name = "nanobind" }, { name = "nox" }, { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, { name = "pybind11" }, @@ -1550,12 +1552,14 @@ provides-extras = ["qiskit"] [package.metadata.requires-dev] build = [ + { name = "nanobind", specifier = ">=2.10.1" }, { name = "pybind11", specifier = ">=3.0.1" }, { name = "scikit-build-core", specifier = ">=0.11.6" }, { name = "setuptools-scm", specifier = ">=9.2.2" }, ] dev = [ { name = "lit", specifier = ">=18.1.8" }, + { name = "nanobind", specifier = ">=2.10.1" }, { name = "nox", specifier = ">=2025.11.12" }, { name = "numpy", marker = "python_full_version >= '3.13'", specifier = ">=2.1" }, { name = "numpy", marker = "python_full_version >= '3.14'", specifier = ">=2.3.2" }, @@ -1640,6 +1644,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5f/df/76d0321c3797b54b60fef9ec3bd6f4cfd124b9e422182156a1dd418722cf/myst_parser-4.0.1-py3-none-any.whl", hash = "sha256:9134e88959ec3b5780aedf8a99680ea242869d012e8821db3126d427edc9c95d", size = 84579, upload-time = "2025-02-12T10:53:02.078Z" }, ] +[[package]] +name = "nanobind" +version = "2.10.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/f6/91c4a25793a7861011b5f2eb9f6075703a514851adf106756c88877cd2f3/nanobind-2.10.1.tar.gz", hash = "sha256:66d2c6fea9541401551b0ca6df674758bb769cf4939b11c1bcd73774c1dcc760", size = 993567, upload-time = "2025-12-08T19:14:19.322Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/ff/819dd252a0262b69d6f659fb09b41ed281ac58467b159a9d3e5ef7c9b539/nanobind-2.10.1-py3-none-any.whl", hash = "sha256:ffd52007bcdf806773d7b44efd1c839657922e0b396e595a8c9514ac88cc42c3", size = 246506, upload-time = "2025-12-08T19:14:17.197Z" }, +] + [[package]] name = "nbclient" version = "0.10.2" From 38f0fe374cc22ab86cfcd5eebd2741d0e897106a Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 9 Dec 2025 14:46:46 +0100 Subject: [PATCH 002/144] Switch FoMaC bindings to nanobind --- bindings/fomac/CMakeLists.txt | 2 +- bindings/fomac/fomac.cpp | 174 ++++++++++++++++------------------ 2 files changed, 82 insertions(+), 94 deletions(-) diff --git a/bindings/fomac/CMakeLists.txt b/bindings/fomac/CMakeLists.txt index 76e491345a..179c3c02fa 100644 --- a/bindings/fomac/CMakeLists.txt +++ b/bindings/fomac/CMakeLists.txt @@ -13,7 +13,7 @@ if(NOT TARGET ${TARGET_NAME}) file(GLOB_RECURSE SOURCES **.cpp) # declare the Python module - add_mqt_python_binding( + add_mqt_python_binding_nanobind( CORE ${TARGET_NAME} ${SOURCES} diff --git a/bindings/fomac/fomac.cpp b/bindings/fomac/fomac.cpp index 03de2ca837..3044ba38b3 100644 --- a/bindings/fomac/fomac.cpp +++ b/bindings/fomac/fomac.cpp @@ -12,62 +12,53 @@ #include "qdmi/Driver.hpp" +#include +#include +#include +#include +#include +#include +#include +#include #include -#include -#include // NOLINT(misc-include-cleaner) -#include -#include -#include -#include // NOLINT(misc-include-cleaner) #include #include #include namespace mqt { -namespace py = pybind11; -using namespace py::literals; - -PYBIND11_MODULE(MQT_CORE_MODULE_NAME, m, py::mod_gil_not_used()) { - auto session = py::class_(m, "Session"); +namespace nb = nanobind; +using namespace nb::literals; +NB_MODULE(MQT_CORE_MODULE_NAME, m) { + // Session class + auto session = nb::class_(m, "Session"); session.def( - py::init([](const std::optional& token = std::nullopt, - const std::optional& authFile = std::nullopt, - const std::optional& authUrl = std::nullopt, - const std::optional& username = std::nullopt, - const std::optional& password = std::nullopt, - const std::optional& projectId = std::nullopt, - const std::optional& custom1 = std::nullopt, - const std::optional& custom2 = std::nullopt, - const std::optional& custom3 = std::nullopt, - const std::optional& custom4 = std::nullopt, - const std::optional& custom5 = - std::nullopt) -> fomac::Session { - const fomac::SessionConfig config{.token = token, - .authFile = authFile, - .authUrl = authUrl, - .username = username, - .password = password, - .projectId = projectId, - .custom1 = custom1, - .custom2 = custom2, - .custom3 = custom3, - .custom4 = custom4, - .custom5 = custom5}; - return fomac::Session{config}; - }), + "__init__", + [](fomac::Session* self, std::optional token, + std::optional authFile, + std::optional authUrl, + std::optional username, + std::optional password, + std::optional projectId, + std::optional custom1, std::optional custom2, + std::optional custom3, std::optional custom4, + std::optional custom5) { + fomac::SessionConfig config{token, authFile, authUrl, username, + password, projectId, custom1, custom2, + custom3, custom4, custom5}; + new (self) fomac::Session(config); + }, "token"_a = std::nullopt, "auth_file"_a = std::nullopt, "auth_url"_a = std::nullopt, "username"_a = std::nullopt, "password"_a = std::nullopt, "project_id"_a = std::nullopt, "custom1"_a = std::nullopt, "custom2"_a = std::nullopt, "custom3"_a = std::nullopt, "custom4"_a = std::nullopt, "custom5"_a = std::nullopt); - session.def("get_devices", &fomac::Session::getDevices); // Job class - auto job = py::class_(m, "Job"); + auto job = nb::class_(m, "Job"); job.def("check", &fomac::Session::Job::check); job.def("wait", &fomac::Session::Job::wait, "timeout"_a = 0); job.def("cancel", &fomac::Session::Job::cancel); @@ -79,17 +70,16 @@ PYBIND11_MODULE(MQT_CORE_MODULE_NAME, m, py::mod_gil_not_used()) { job.def("get_sparse_statevector", &fomac::Session::Job::getSparseStateVector); job.def("get_sparse_probabilities", &fomac::Session::Job::getSparseProbabilities); - job.def_property_readonly("id", &fomac::Session::Job::getId); - job.def_property_readonly("program_format", - &fomac::Session::Job::getProgramFormat); - job.def_property_readonly("program", &fomac::Session::Job::getProgram); - job.def_property_readonly("num_shots", &fomac::Session::Job::getNumShots); - job.def(py::self == py::self); // NOLINT(misc-redundant-expression) - job.def(py::self != py::self); // NOLINT(misc-redundant-expression) + job.def_prop_ro("id", &fomac::Session::Job::getId); + job.def_prop_ro("program_format", &fomac::Session::Job::getProgramFormat); + job.def_prop_ro("program", &fomac::Session::Job::getProgram); + job.def_prop_ro("num_shots", &fomac::Session::Job::getNumShots); + job.def(nb::self == nb::self); + job.def(nb::self != nb::self); // JobStatus enum - py::native_enum(job, "Status", "enum.Enum", - "Enumeration of job status.") + nb::enum_(job, "Status", "enum.Enum", + "Enumeration of job status.") .value("CREATED", QDMI_JOB_STATUS_CREATED) .value("SUBMITTED", QDMI_JOB_STATUS_SUBMITTED) .value("QUEUED", QDMI_JOB_STATUS_QUEUED) @@ -97,12 +87,11 @@ PYBIND11_MODULE(MQT_CORE_MODULE_NAME, m, py::mod_gil_not_used()) { .value("DONE", QDMI_JOB_STATUS_DONE) .value("CANCELED", QDMI_JOB_STATUS_CANCELED) .value("FAILED", QDMI_JOB_STATUS_FAILED) - .export_values() - .finalize(); + .export_values(); // ProgramFormat enum - py::native_enum(m, "ProgramFormat", "enum.Enum", - "Enumeration of program formats.") + nb::enum_(m, "ProgramFormat", "enum.Enum", + "Enumeration of program formats.") .value("QASM2", QDMI_PROGRAM_FORMAT_QASM2) .value("QASM3", QDMI_PROGRAM_FORMAT_QASM3) .value("QIR_BASE_STRING", QDMI_PROGRAM_FORMAT_QIRBASESTRING) @@ -117,23 +106,49 @@ PYBIND11_MODULE(MQT_CORE_MODULE_NAME, m, py::mod_gil_not_used()) { .value("CUSTOM3", QDMI_PROGRAM_FORMAT_CUSTOM3) .value("CUSTOM4", QDMI_PROGRAM_FORMAT_CUSTOM4) .value("CUSTOM5", QDMI_PROGRAM_FORMAT_CUSTOM5) - .export_values() - .finalize(); - - auto device = py::class_(m, "Device"); + .export_values(); - py::native_enum(device, "Status", "enum.Enum", - "Enumeration of device status.") + // Device class + auto device = nb::class_(m, "Device"); + nb::enum_(device, "Status", "enum.Enum", + "Enumeration of device status.") .value("offline", QDMI_DEVICE_STATUS_OFFLINE) .value("idle", QDMI_DEVICE_STATUS_IDLE) .value("busy", QDMI_DEVICE_STATUS_BUSY) .value("error", QDMI_DEVICE_STATUS_ERROR) .value("maintenance", QDMI_DEVICE_STATUS_MAINTENANCE) .value("calibration", QDMI_DEVICE_STATUS_CALIBRATION) - .export_values() - .finalize(); + .export_values(); + device.def("name", &fomac::Session::Device::getName); + device.def("version", &fomac::Session::Device::getVersion); + device.def("status", &fomac::Session::Device::getStatus); + device.def("library_version", &fomac::Session::Device::getLibraryVersion); + device.def("qubits_num", &fomac::Session::Device::getQubitsNum); + device.def("sites", &fomac::Session::Device::getSites); + device.def("regular_sites", &fomac::Session::Device::getRegularSites); + device.def("zones", &fomac::Session::Device::getZones); + device.def("operations", &fomac::Session::Device::getOperations); + device.def("coupling_map", &fomac::Session::Device::getCouplingMap); + device.def("needs_calibration", &fomac::Session::Device::getNeedsCalibration); + device.def("length_unit", &fomac::Session::Device::getLengthUnit); + device.def("length_scale_factor", + &fomac::Session::Device::getLengthScaleFactor); + device.def("duration_unit", &fomac::Session::Device::getDurationUnit); + device.def("duration_scale_factor", + &fomac::Session::Device::getDurationScaleFactor); + device.def("min_atom_distance", &fomac::Session::Device::getMinAtomDistance); + device.def("supported_program_formats", + &fomac::Session::Device::getSupportedProgramFormats); + device.def("submit_job", &fomac::Session::Device::submitJob, "program"_a, + "program_format"_a, "num_shots"_a); + device.def("__repr__", [](const fomac::Session::Device& dev) { + return ""; + }); + device.def(nb::self == nb::self); + device.def(nb::self != nb::self); - auto site = py::class_(device, "Site"); + // Site class + auto site = nb::class_(device, "Site"); site.def("index", &fomac::Session::Device::Site::getIndex); site.def("t1", &fomac::Session::Device::Site::getT1); site.def("t2", &fomac::Session::Device::Site::getT2); @@ -150,11 +165,12 @@ PYBIND11_MODULE(MQT_CORE_MODULE_NAME, m, py::mod_gil_not_used()) { site.def("__repr__", [](const fomac::Session::Device::Site& s) { return ""; }); - site.def(py::self == py::self); // NOLINT(misc-redundant-expression) - site.def(py::self != py::self); // NOLINT(misc-redundant-expression) + site.def(nb::self == nb::self); + site.def(nb::self != nb::self); + // Operation class auto operation = - py::class_(device, "Operation"); + nb::class_(device, "Operation"); operation.def("name", &fomac::Session::Device::Operation::getName, "sites"_a = std::vector{}, "params"_a = std::vector{}); @@ -193,36 +209,8 @@ PYBIND11_MODULE(MQT_CORE_MODULE_NAME, m, py::mod_gil_not_used()) { operation.def("__repr__", [](const fomac::Session::Device::Operation& op) { return ""; }); - operation.def(py::self == py::self); // NOLINT(misc-redundant-expression) - operation.def(py::self != py::self); // NOLINT(misc-redundant-expression) - - device.def("name", &fomac::Session::Device::getName); - device.def("version", &fomac::Session::Device::getVersion); - device.def("status", &fomac::Session::Device::getStatus); - device.def("library_version", &fomac::Session::Device::getLibraryVersion); - device.def("qubits_num", &fomac::Session::Device::getQubitsNum); - device.def("sites", &fomac::Session::Device::getSites); - device.def("regular_sites", &fomac::Session::Device::getRegularSites); - device.def("zones", &fomac::Session::Device::getZones); - device.def("operations", &fomac::Session::Device::getOperations); - device.def("coupling_map", &fomac::Session::Device::getCouplingMap); - device.def("needs_calibration", &fomac::Session::Device::getNeedsCalibration); - device.def("length_unit", &fomac::Session::Device::getLengthUnit); - device.def("length_scale_factor", - &fomac::Session::Device::getLengthScaleFactor); - device.def("duration_unit", &fomac::Session::Device::getDurationUnit); - device.def("duration_scale_factor", - &fomac::Session::Device::getDurationScaleFactor); - device.def("min_atom_distance", &fomac::Session::Device::getMinAtomDistance); - device.def("supported_program_formats", - &fomac::Session::Device::getSupportedProgramFormats); - device.def("submit_job", &fomac::Session::Device::submitJob, "program"_a, - "program_format"_a, "num_shots"_a); - device.def("__repr__", [](const fomac::Session::Device& dev) { - return ""; - }); - device.def(py::self == py::self); // NOLINT(misc-redundant-expression) - device.def(py::self != py::self); // NOLINT(misc-redundant-expression) + operation.def(nb::self == nb::self); + operation.def(nb::self != nb::self); #ifndef _WIN32 // Module-level function to add dynamic device libraries on non-Windows From 1aa9833782b540c9472ffd5bc6f670f70d7aa710 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 9 Dec 2025 15:13:01 +0100 Subject: [PATCH 003/144] Switch NA bindings to nanobind --- bindings/na/fomac/CMakeLists.txt | 2 +- bindings/na/fomac/fomac.cpp | 98 +++++++++++++++----------------- 2 files changed, 46 insertions(+), 54 deletions(-) diff --git a/bindings/na/fomac/CMakeLists.txt b/bindings/na/fomac/CMakeLists.txt index ff31c55551..8ee95e3500 100644 --- a/bindings/na/fomac/CMakeLists.txt +++ b/bindings/na/fomac/CMakeLists.txt @@ -13,7 +13,7 @@ if(NOT TARGET ${TARGET_NAME}) file(GLOB_RECURSE SOURCES **.cpp) # declare the Python module - add_mqt_python_binding( + add_mqt_python_binding_nanobind( CORE ${TARGET_NAME} ${SOURCES} diff --git a/bindings/na/fomac/fomac.cpp b/bindings/na/fomac/fomac.cpp index c292f1cf14..dd16e12cbe 100644 --- a/bindings/na/fomac/fomac.cpp +++ b/bindings/na/fomac/fomac.cpp @@ -8,105 +8,97 @@ * Licensed under the MIT License */ -// These includes must be the first includes for any bindings code -// clang-format off #include "fomac/FoMaC.hpp" + #include "na/fomac/Device.hpp" #include "qdmi/na/Generator.hpp" -#include -#include -#include -#include -#include // NOLINT(misc-include-cleaner) +#include +#include +#include +#include #include -// clang-format on namespace mqt { -namespace py = pybind11; -using namespace py::literals; +namespace nb = nanobind; +using namespace nb::literals; namespace { + template -concept pyClass = requires(T t) { py::cast(t); }; +concept pyClass = requires(T t) { nb::cast(t); }; template [[nodiscard]] auto repr(T c) -> std::string { - return py::repr(py::cast(c)).template cast(); + return nb::repr(nb::cast(c)).c_str(); } + } // namespace -// The definition of the (in-)equality operators produces warnings in clang-tidy -// which are ignored by the following comment -// NOLINTBEGIN(misc-redundant-expression) -PYBIND11_MODULE(MQT_CORE_MODULE_NAME, m, py::mod_gil_not_used()) { - pybind11::module_::import("mqt.core.fomac"); +NB_MODULE(MQT_CORE_MODULE_NAME, m) { + nb::module_::import_("mqt.core.fomac"); auto device = - py::class_(m, "Device"); + nb::class_(m, "Device"); - auto lattice = py::class_(device, "Lattice"); + auto lattice = nb::class_(device, "Lattice"); - auto vector = py::class_(lattice, "Vector"); - vector.def_readonly("x", &na::Device::Vector::x); - vector.def_readonly("y", &na::Device::Vector::y); + auto vector = nb::class_(lattice, "Vector"); + vector.def_ro("x", &na::Device::Vector::x); + vector.def_ro("y", &na::Device::Vector::y); vector.def("__repr__", [](const na::Device::Vector& v) { return ""; }); - vector.def(py::self == py::self); - vector.def(py::self != py::self); + vector.def(nb::self == nb::self); + vector.def(nb::self != nb::self); - auto region = py::class_(lattice, "Region"); + auto region = nb::class_(lattice, "Region"); - auto size = py::class_(region, "Size"); - size.def_readonly("width", &na::Device::Region::Size::width); - size.def_readonly("height", &na::Device::Region::Size::height); + auto size = nb::class_(region, "Size"); + size.def_ro("width", &na::Device::Region::Size::width); + size.def_ro("height", &na::Device::Region::Size::height); size.def("__repr__", [](const na::Device::Region::Size& s) { return ""; }); - size.def(py::self == py::self); - size.def(py::self != py::self); + size.def(nb::self == nb::self); + size.def(nb::self != nb::self); - region.def_readonly("origin", &na::Device::Region::origin); - region.def_readonly("size", &na::Device::Region::size); + region.def_ro("origin", &na::Device::Region::origin); + region.def_ro("size", &na::Device::Region::size); region.def("__repr__", [](const na::Device::Region& r) { return ""; }); - region.def(py::self == py::self); - region.def(py::self != py::self); - - lattice.def_readonly("lattice_origin", &na::Device::Lattice::latticeOrigin); - lattice.def_readonly("lattice_vector_1", - &na::Device::Lattice::latticeVector1); - lattice.def_readonly("lattice_vector_2", - &na::Device::Lattice::latticeVector2); - lattice.def_readonly("sublattice_offsets", - &na::Device::Lattice::sublatticeOffsets); - lattice.def_readonly("extent", &na::Device::Lattice::extent); + region.def(nb::self == nb::self); + region.def(nb::self != nb::self); + + lattice.def_ro("lattice_origin", &na::Device::Lattice::latticeOrigin); + lattice.def_ro("lattice_vector_1", &na::Device::Lattice::latticeVector1); + lattice.def_ro("lattice_vector_2", &na::Device::Lattice::latticeVector2); + lattice.def_ro("sublattice_offsets", &na::Device::Lattice::sublatticeOffsets); + lattice.def_ro("extent", &na::Device::Lattice::extent); lattice.def("__repr__", [](const na::Device::Lattice& l) { - return ""; + return ""; }); - lattice.def(py::self == py::self); - lattice.def(py::self != py::self); + lattice.def(nb::self == nb::self); + lattice.def(nb::self != nb::self); - device.def_property_readonly("traps", &na::Session::Device::getTraps); - device.def_property_readonly("t1", [](const na::Session::Device& dev) { + device.def_prop_ro("traps", &na::Session::Device::getTraps); + device.def_prop_ro("t1", [](const na::Session::Device& dev) { return dev.getDecoherenceTimes().t1; }); - device.def_property_readonly("t2", [](const na::Session::Device& dev) { + device.def_prop_ro("t2", [](const na::Session::Device& dev) { return dev.getDecoherenceTimes().t2; }); device.def("__repr__", [](const fomac::Session::Device& dev) { return ""; }); - device.def(py::self == py::self); - device.def(py::self != py::self); + device.def(nb::self == nb::self); + device.def(nb::self != nb::self); m.def("devices", &na::Session::getDevices); device.def_static("try_create_from_device", &na::Session::Device::tryCreateFromDevice, "device"_a); } -// NOLINTEND(misc-redundant-expression) + } // namespace mqt From b882d10412c80f77226008223dec9e14f551636a Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 9 Dec 2025 17:18:23 +0100 Subject: [PATCH 004/144] Switch DD bindings to nanobind --- bindings/dd/CMakeLists.txt | 2 +- bindings/dd/register_dd.cpp | 49 +++----- bindings/dd/register_dd_package.cpp | 186 +++++++++++++--------------- bindings/dd/register_matrix_dds.cpp | 56 ++++----- bindings/dd/register_vector_dds.cpp | 45 +++---- test/python/dd/test_vector_dds.py | 1 + 6 files changed, 144 insertions(+), 195 deletions(-) diff --git a/bindings/dd/CMakeLists.txt b/bindings/dd/CMakeLists.txt index 9b9950402a..aa149adc3c 100644 --- a/bindings/dd/CMakeLists.txt +++ b/bindings/dd/CMakeLists.txt @@ -11,7 +11,7 @@ if(NOT TARGET ${MQT_CORE_TARGET_NAME}-dd-bindings) file(GLOB_RECURSE DD_SOURCES **.cpp) # declare the Python module - add_mqt_python_binding( + add_mqt_python_binding_nanobind( CORE ${MQT_CORE_TARGET_NAME}-dd-bindings ${DD_SOURCES} diff --git a/bindings/dd/register_dd.cpp b/bindings/dd/register_dd.cpp index 5c7c3e4f30..2a80a3e15e 100644 --- a/bindings/dd/register_dd.cpp +++ b/bindings/dd/register_dd.cpp @@ -16,57 +16,48 @@ #include "dd/StateGeneration.hpp" #include "ir/QuantumComputation.hpp" -// These includes must be the first includes for any bindings code -// clang-format off -#include -#include // NOLINT(misc-include-cleaner) - -#include -// clang-format on - #include #include #include +#include +#include +#include +#include #include namespace mqt { -namespace py = pybind11; -using namespace pybind11::literals; +namespace nb = nanobind; +using namespace nb::literals; // forward declarations -void registerVectorDDs(const py::module& mod); -void registerMatrixDDs(const py::module& mod); -void registerDDPackage(const py::module& mod); +void registerVectorDDs(const nb::module_& m); +void registerMatrixDDs(const nb::module_& m); +void registerDDPackage(const nb::module_& m); -struct Vector { - dd::CVec v; -}; +using Vector = nb::ndarray, nb::ndim<1>>; Vector getVector(const dd::vEdge& v, dd::fp threshold = 0.); -struct Matrix { - std::vector> data; - size_t n; -}; +using Matrix = nb::ndarray, nb::ndim<2>>; Matrix getMatrix(const dd::mEdge& m, size_t numQubits, dd::fp threshold = 0.); -PYBIND11_MODULE(MQT_CORE_MODULE_NAME, mod, py::mod_gil_not_used()) { +NB_MODULE(MQT_CORE_MODULE_NAME, m) { // Vector Decision Diagrams - registerVectorDDs(mod); + registerVectorDDs(m); // Matrix Decision Diagrams - registerMatrixDDs(mod); + registerMatrixDDs(m); // DD Package - registerDDPackage(mod); + registerDDPackage(m); - mod.def( + m.def( "sample", [](const qc::QuantumComputation& qc, const size_t shots = 1024U, const size_t seed = 0U) { return dd::sample(qc, shots, seed); }, "qc"_a, "shots"_a = 1024U, "seed"_a = 0U); - mod.def( + m.def( "simulate_statevector", [](const qc::QuantumComputation& qc) { auto dd = std::make_unique(qc.getNqubits()); @@ -76,7 +67,7 @@ PYBIND11_MODULE(MQT_CORE_MODULE_NAME, mod, py::mod_gil_not_used()) { }, "qc"_a); - mod.def( + m.def( "build_unitary", [](const qc::QuantumComputation& qc, const bool recursive = false) { auto dd = std::make_unique(qc.getNqubits()); @@ -86,9 +77,9 @@ PYBIND11_MODULE(MQT_CORE_MODULE_NAME, mod, py::mod_gil_not_used()) { }, "qc"_a, "recursive"_a = false); - mod.def("simulate", &dd::simulate, "qc"_a, "initial_state"_a, "dd_package"_a); + m.def("simulate", &dd::simulate, "qc"_a, "initial_state"_a, "dd_package"_a); - mod.def( + m.def( "build_functionality", [](const qc::QuantumComputation& qc, dd::Package& p, const bool recursive = false) { diff --git a/bindings/dd/register_dd_package.cpp b/bindings/dd/register_dd_package.cpp index 4900e256dc..b188b58ab7 100644 --- a/bindings/dd/register_dd_package.cpp +++ b/bindings/dd/register_dd_package.cpp @@ -20,22 +20,15 @@ #include "ir/operations/NonUnitaryOperation.hpp" #include "ir/operations/Operation.hpp" -// These includes must be the first includes for any bindings code -// clang-format off -#include -#include // NOLINT(misc-include-cleaner) - -#include -#include -#include -#include -// clang-format on - #include #include #include #include #include +#include +#include +#include +#include #include #include #include @@ -43,15 +36,17 @@ namespace mqt { -namespace py = pybind11; -using namespace pybind11::literals; +namespace nb = nanobind; +using namespace nb::literals; + +using Vector = nb::ndarray, nb::ndim<1>>; +using Matrix = nb::ndarray, nb::ndim<2>>; namespace { /// Recursive helper function to create a vector DD from a numpy array -dd::vCachedEdge makeDDFromVector( - dd::Package& p, - const py::detail::unchecked_reference, 1>& v, - const size_t startIdx, const size_t endIdx, const dd::Qubit level) { +dd::vCachedEdge makeDDFromVector(dd::Package& p, const Vector& v, + const size_t startIdx, const size_t endIdx, + const dd::Qubit level) { if (level == 0U) { const auto zeroSuccessor = dd::vCachedEdge::terminal(v(startIdx)); const auto oneSuccessor = dd::vCachedEdge::terminal(v(startIdx + 1)); @@ -67,11 +62,10 @@ dd::vCachedEdge makeDDFromVector( } /// Recursive helper function to create a matrix DD from a numpy array -dd::mCachedEdge makeDDFromMatrix( - dd::Package& p, - const py::detail::unchecked_reference, 2>& m, - const size_t rowStart, const size_t rowEnd, const size_t colStart, - const size_t colEnd, const dd::Qubit level) { +dd::mCachedEdge makeDDFromMatrix(dd::Package& p, const Matrix& m, + const size_t rowStart, const size_t rowEnd, + const size_t colStart, const size_t colEnd, + const dd::Qubit level) { if (level == 0U) { const auto zeroSuccessor = dd::mCachedEdge::terminal(m(rowStart, colStart)); const auto oneSuccessor = @@ -96,18 +90,17 @@ dd::mCachedEdge makeDDFromMatrix( } // namespace // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerDDPackage(const py::module& mod) { - auto dd = - py::class_>(mod, "DDPackage"); +void registerDDPackage(const nb::module_& m) { + auto dd = nb::class_(m, "DDPackage"); // Constructor - dd.def(py::init(), "num_qubits"_a = dd::Package::DEFAULT_QUBITS); + dd.def(nb::init(), "num_qubits"_a = dd::Package::DEFAULT_QUBITS); // Resizing the package dd.def("resize", &dd::Package::resize, "num_qubits"_a); // Getting the number of qubits the package is configured for - dd.def_property_readonly("max_qubits", &dd::Package::qubits); + dd.def_prop_ro("max_qubits", &dd::Package::qubits); ///------------------------------------------------------------------------/// /// Vector DD Generation @@ -120,7 +113,7 @@ void registerDDPackage(const py::module& mod) { }, "num_qubits"_a, // keep the DD package alive while the returned vector DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def( "computational_basis_state", @@ -130,17 +123,16 @@ void registerDDPackage(const py::module& mod) { }, "num_qubits"_a, "state"_a, // keep the DD package alive while the returned vector DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); - py::native_enum(mod, "BasisStates", "enum.Enum", - "Enumeration of basis states.") + nb::enum_(m, "BasisStates", "enum.Enum", + "Enumeration of basis states.") .value("zero", dd::BasisStates::zero) .value("one", dd::BasisStates::one) .value("plus", dd::BasisStates::plus) .value("minus", dd::BasisStates::minus) .value("right", dd::BasisStates::right) - .value("left", dd::BasisStates::left) - .finalize(); + .value("left", dd::BasisStates::left); dd.def( "basis_state", @@ -150,7 +142,7 @@ void registerDDPackage(const py::module& mod) { }, "num_qubits"_a, "state"_a, // keep the DD package alive while the returned vector DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def( "ghz_state", @@ -159,7 +151,7 @@ void registerDDPackage(const py::module& mod) { }, "num_qubits"_a, // keep the DD package alive while the returned vector DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def( "w_state", @@ -168,13 +160,12 @@ void registerDDPackage(const py::module& mod) { }, "num_qubits"_a, // keep the DD package alive while the returned vector DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def( "from_vector", - [](dd::Package& p, const py::array_t>& v) { - const auto data = v.unchecked<1>(); - const auto length = static_cast(data.shape(0)); + [](dd::Package& p, const Vector& v) { + const auto length = static_cast(v.shape(0)); if (length == 0) { return dd::vEdge::one(); } @@ -183,20 +174,19 @@ void registerDDPackage(const py::module& mod) { "State vector must have a length of a power of two."); } if (length == 1) { - const auto state = dd::vEdge::terminal(p.cn.lookup(data(0))); + const auto state = dd::vEdge::terminal(p.cn.lookup(v(0))); p.incRef(state); return state; } - const auto level = static_cast(std::log2(length) - 1); - const auto state = makeDDFromVector(p, data, 0, length, level); + const auto state = makeDDFromVector(p, v, 0, length, level); const dd::vEdge e{state.p, p.cn.lookup(state.w)}; p.incRef(e); return e; }, "state"_a, // keep the DD package alive while the returned vector DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def( "apply_unitary_operation", @@ -206,7 +196,7 @@ void registerDDPackage(const py::module& mod) { }, "vec"_a, "operation"_a, "permutation"_a = qc::Permutation{}, // keep the DD package alive while the returned vector DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def( "apply_measurement", @@ -222,7 +212,7 @@ void registerDDPackage(const py::module& mod) { "vec"_a, "operation"_a, "measurements"_a, "permutation"_a = qc::Permutation{}, // keep the DD package alive while the returned vector DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def( "apply_reset", @@ -233,7 +223,7 @@ void registerDDPackage(const py::module& mod) { }, "vec"_a, "operation"_a, "permutation"_a = qc::Permutation{}, // keep the DD package alive while the returned vector DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def( "apply_if_else_operation", @@ -245,7 +235,7 @@ void registerDDPackage(const py::module& mod) { "vec"_a, "operation"_a, "measurements"_a, "permutation"_a = qc::Permutation{}, // keep the DD package alive while the returned vector DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def( "measure_collapsing", @@ -265,115 +255,105 @@ void registerDDPackage(const py::module& mod) { dd.def_static("identity", &dd::Package::makeIdent); - using NumPyMatrix = py::array_t, - py::array::c_style | py::array::forcecast>; dd.def( "single_qubit_gate", - [](dd::Package& p, const NumPyMatrix& m, const dd::Qubit target) { + [](dd::Package& p, const Matrix& m, const dd::Qubit target) { if (m.ndim() != 2 || m.shape(0) != 2 || m.shape(1) != 2) { throw std::invalid_argument("Matrix must be 2x2."); } - const auto data = m.unchecked<2>(); - return p.makeGateDD({data(0, 0), data(0, 1), data(1, 0), data(1, 1)}, - target); + return p.makeGateDD({m(0, 0), m(0, 1), m(1, 0), m(1, 1)}, target); }, "matrix"_a, "target"_a, // keep the DD package alive while the returned matrix DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def( "controlled_single_qubit_gate", - [](dd::Package& p, const NumPyMatrix& m, const qc::Control& control, + [](dd::Package& p, const Matrix& m, const qc::Control& control, const dd::Qubit target) { if (m.ndim() != 2 || m.shape(0) != 2 || m.shape(1) != 2) { throw std::invalid_argument("Matrix must be 2x2."); } - const auto data = m.unchecked<2>(); - return p.makeGateDD({data(0, 0), data(0, 1), data(1, 0), data(1, 1)}, - control, target); + return p.makeGateDD({m(0, 0), m(0, 1), m(1, 0), m(1, 1)}, control, + target); }, "matrix"_a, "control"_a, "target"_a, // keep the DD package alive while the returned matrix DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def( "multi_controlled_single_qubit_gate", - [](dd::Package& p, const NumPyMatrix& m, const qc::Controls& controls, + [](dd::Package& p, const Matrix& m, const qc::Controls& controls, const dd::Qubit target) { if (m.ndim() != 2 || m.shape(0) != 2 || m.shape(1) != 2) { throw std::invalid_argument("Matrix must be 2x2."); } - const auto data = m.unchecked<2>(); - return p.makeGateDD({data(0, 0), data(0, 1), data(1, 0), data(1, 1)}, - controls, target); + return p.makeGateDD({m(0, 0), m(0, 1), m(1, 0), m(1, 1)}, controls, + target); }, "matrix"_a, "controls"_a, "target"_a, // keep the DD package alive while the returned matrix DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def( "two_qubit_gate", - [](dd::Package& p, const NumPyMatrix& m, const dd::Qubit target0, + [](dd::Package& p, const Matrix& m, const dd::Qubit target0, const dd::Qubit target1) { if (m.ndim() != 2 || m.shape(0) != 4 || m.shape(1) != 4) { throw std::invalid_argument("Matrix must be 4x4."); } - const auto data = m.unchecked<2>(); return p.makeTwoQubitGateDD( - {std::array{data(0, 0), data(0, 1), data(0, 2), data(0, 3)}, - {data(1, 0), data(1, 1), data(1, 2), data(1, 3)}, - {data(2, 0), data(2, 1), data(2, 2), data(2, 3)}, - {data(3, 0), data(3, 1), data(3, 2), data(3, 3)}}, + {std::array{m(0, 0), m(0, 1), m(0, 2), m(0, 3)}, + {m(1, 0), m(1, 1), m(1, 2), m(1, 3)}, + {m(2, 0), m(2, 1), m(2, 2), m(2, 3)}, + {m(3, 0), m(3, 1), m(3, 2), m(3, 3)}}, target0, target1); }, "matrix"_a, "target0"_a, "target1"_a, // keep the DD package alive while the returned matrix DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def( "controlled_two_qubit_gate", - [](dd::Package& p, const NumPyMatrix& m, const qc::Control& control, + [](dd::Package& p, const Matrix& m, const qc::Control& control, const dd::Qubit target0, const dd::Qubit target1) { if (m.ndim() != 2 || m.shape(0) != 4 || m.shape(1) != 4) { throw std::invalid_argument("Matrix must be 4x4."); } - const auto data = m.unchecked<2>(); return p.makeTwoQubitGateDD( - {std::array{data(0, 0), data(0, 1), data(0, 2), data(0, 3)}, - {data(1, 0), data(1, 1), data(1, 2), data(1, 3)}, - {data(2, 0), data(2, 1), data(2, 2), data(2, 3)}, - {data(3, 0), data(3, 1), data(3, 2), data(3, 3)}}, + {std::array{m(0, 0), m(0, 1), m(0, 2), m(0, 3)}, + {m(1, 0), m(1, 1), m(1, 2), m(1, 3)}, + {m(2, 0), m(2, 1), m(2, 2), m(2, 3)}, + {m(3, 0), m(3, 1), m(3, 2), m(3, 3)}}, control, target0, target1); }, "matrix"_a, "control"_a, "target0"_a, "target1"_a, // keep the DD package alive while the returned matrix DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def( "multi_controlled_two_qubit_gate", - [](dd::Package& p, const NumPyMatrix& m, const qc::Controls& controls, + [](dd::Package& p, const Matrix& m, const qc::Controls& controls, const dd::Qubit target0, const dd::Qubit target1) { if (m.ndim() != 2 || m.shape(0) != 4 || m.shape(1) != 4) { throw std::invalid_argument("Matrix must be 4x4."); } - const auto data = m.unchecked<2>(); return p.makeTwoQubitGateDD( - {std::array{data(0, 0), data(0, 1), data(0, 2), data(0, 3)}, - {data(1, 0), data(1, 1), data(1, 2), data(1, 3)}, - {data(2, 0), data(2, 1), data(2, 2), data(2, 3)}, - {data(3, 0), data(3, 1), data(3, 2), data(3, 3)}}, + {std::array{m(0, 0), m(0, 1), m(0, 2), m(0, 3)}, + {m(1, 0), m(1, 1), m(1, 2), m(1, 3)}, + {m(2, 0), m(2, 1), m(2, 2), m(2, 3)}, + {m(3, 0), m(3, 1), m(3, 2), m(3, 3)}}, controls, target0, target1); }, "matrix"_a, "controls"_a, "target0"_a, "target1"_a, // keep the DD package alive while the returned matrix DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def( "from_matrix", - [](dd::Package& p, const NumPyMatrix& m) { - const auto data = m.unchecked<2>(); - const auto rows = static_cast(data.shape(0)); - const auto cols = static_cast(data.shape(1)); + [](dd::Package& p, const Matrix& m) { + const auto rows = static_cast(m.shape(0)); + const auto cols = static_cast(m.shape(1)); if (rows != cols) { throw std::invalid_argument("Matrix must be square."); } @@ -385,16 +365,15 @@ void registerDDPackage(const py::module& mod) { "Matrix must have a size of a power of two."); } if (rows == 1) { - return dd::mEdge::terminal(p.cn.lookup(data(0, 0))); + return dd::mEdge::terminal(p.cn.lookup(m(0, 0))); } const auto level = static_cast(std::log2(rows) - 1); - const auto matrixDD = - makeDDFromMatrix(p, data, 0, rows, 0, cols, level); + const auto matrixDD = makeDDFromMatrix(p, m, 0, rows, 0, cols, level); return dd::mEdge{matrixDD.p, p.cn.lookup(matrixDD.w)}; }, "matrix"_a, // keep the DD package alive while the returned matrix DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def( "from_operation", @@ -406,7 +385,7 @@ void registerDDPackage(const py::module& mod) { }, "operation"_a, "invert"_a = false, // keep the DD package alive while the returned matrix DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); // Reference counting and garbage collection dd.def("inc_ref_vec", &dd::Package::incRef, "vec"_a); @@ -421,22 +400,22 @@ void registerDDPackage(const py::module& mod) { const dd::vEdge&, const dd::vEdge&)>(&dd::Package::add), "lhs"_a, "rhs"_a, // keep the DD package alive while the returned vector DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def("matrix_add", static_cast(&dd::Package::add), "lhs"_a, "rhs"_a, // keep the DD package alive while the returned matrix DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def("conjugate", &dd::Package::conjugate, "vec"_a, // keep the DD package alive while the returned vector DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def("conjugate_transpose", &dd::Package::conjugateTranspose, "mat"_a, // keep the DD package alive while the returned matrix DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def( "matrix_vector_multiply", @@ -445,7 +424,7 @@ void registerDDPackage(const py::module& mod) { }, "mat"_a, "vec"_a, // keep the DD package alive while the returned vector DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def( "matrix_multiply", @@ -454,7 +433,7 @@ void registerDDPackage(const py::module& mod) { }, "lhs"_a, "rhs"_a, // keep the DD package alive while the returned matrix DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def( "inner_product", @@ -474,7 +453,7 @@ void registerDDPackage(const py::module& mod) { &dd::Package::kronecker), "top"_a, "bottom"_a, "bottom_num_qubits"_a, "increment_index"_a = true, // keep the DD package alive while the returned vector DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def("matrix_kronecker", static_cast()); + nb::keep_alive<0, 1>()); dd.def("partial_trace", &dd::Package::partialTrace, "mat"_a, "eliminate"_a, // keep the DD package alive while the returned matrix DD is alive. - py::keep_alive<0, 1>()); + nb::keep_alive<0, 1>()); dd.def( "trace", @@ -495,4 +474,5 @@ void registerDDPackage(const py::module& mod) { }, "mat"_a, "num_qubits"_a); } + } // namespace mqt diff --git a/bindings/dd/register_matrix_dds.cpp b/bindings/dd/register_matrix_dds.cpp index fe6298596e..c7910c3d26 100644 --- a/bindings/dd/register_matrix_dds.cpp +++ b/bindings/dd/register_matrix_dds.cpp @@ -13,76 +13,64 @@ #include "dd/Export.hpp" #include "dd/Node.hpp" -// These includes must be the first includes for any bindings code -// clang-format off -#include -#include // NOLINT(misc-include-cleaner) - -#include -#include -#include // NOLINT(misc-include-cleaner) -// clang-format on - #include #include #include +#include +#include +#include +#include +#include #include #include #include namespace mqt { -namespace py = pybind11; -using namespace pybind11::literals; +namespace nb = nanobind; +using namespace nb::literals; -struct Matrix { - std::vector> data; - size_t n; -}; +using Matrix = nb::ndarray, nb::ndim<2>>; // NOLINTNEXTLINE(misc-use-internal-linkage) Matrix getMatrix(const dd::mEdge& m, const size_t numQubits, const dd::fp threshold = 0.) { if (numQubits == 0U) { - return Matrix{.data = {static_cast>(m.w)}, .n = 1}; + auto* data = new std::complex[1]; + data[0] = static_cast>(m.w); + nb::capsule owner( + data, [](void* p) noexcept { delete[] (std::complex*)p; }); + return Matrix(data, {1, 1}, owner); } - const size_t dim = 1ULL << numQubits; - auto data = std::vector>(dim * dim); + + const auto dim = 1ULL << numQubits; + auto* data = new std::complex[dim * dim]; m.traverseMatrix( std::complex{1., 0.}, 0ULL, 0ULL, [&data, dim](const std::size_t i, const std::size_t j, const std::complex& c) { data[(i * dim) + j] = c; }, numQubits, threshold); - return Matrix{.data = data, .n = dim}; + nb::capsule owner( + data, [](void* p) noexcept { delete[] (std::complex*)p; }); + return Matrix(data, {dim, dim}, owner); } // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerMatrixDDs(const py::module& mod) { - auto mat = py::class_(mod, "MatrixDD"); +void registerMatrixDDs(const nb::module_& m) { + auto mat = nb::class_(m, "MatrixDD"); mat.def("is_terminal", &dd::mEdge::isTerminal); mat.def("is_zero_terminal", &dd::mEdge::isZeroTerminal); mat.def("is_identity", &dd::mEdge::isIdentity<>, "up_to_global_phase"_a = true); - mat.def("size", py::overload_cast<>(&dd::mEdge::size, py::const_)); + mat.def("size", nb::overload_cast<>(&dd::mEdge::size, nb::const_)); mat.def("get_entry", &dd::mEdge::getValueByIndex<>, "num_qubits"_a, "row"_a, "col"_a); mat.def("get_entry_by_path", &dd::mEdge::getValueByPath, "num_qubits"_a, "decisions"_a); - py::class_(mod, "Matrix", py::buffer_protocol()) - .def_buffer([](Matrix& matrix) -> py::buffer_info { - return py::buffer_info( - matrix.data.data(), sizeof(std::complex), - // NOLINTNEXTLINE(misc-include-cleaner) - py::format_descriptor>::format(), 2, - {matrix.n, matrix.n}, - {sizeof(std::complex) * matrix.n, - sizeof(std::complex)}); - }); - mat.def("get_matrix", &getMatrix, "num_qubits"_a, "threshold"_a = 0.); mat.def( diff --git a/bindings/dd/register_vector_dds.cpp b/bindings/dd/register_vector_dds.cpp index d32465970a..d7715c38c8 100644 --- a/bindings/dd/register_vector_dds.cpp +++ b/bindings/dd/register_vector_dds.cpp @@ -13,44 +13,42 @@ #include "dd/Export.hpp" #include "dd/Node.hpp" -// These includes must be the first includes for any bindings code -// clang-format off -#include -#include // NOLINT(misc-include-cleaner) - -#include -#include -#include // NOLINT(misc-include-cleaner) -// clang-format on - #include #include #include +#include +#include +#include +#include +#include #include #include namespace mqt { -namespace py = pybind11; -using namespace pybind11::literals; +namespace nb = nanobind; +using namespace nb::literals; -struct Vector { - dd::CVec v; -}; +using Vector = nb::ndarray, nb::ndim<1>>; // NOLINTNEXTLINE(misc-use-internal-linkage) Vector getVector(const dd::vEdge& v, const dd::fp threshold = 0.) { - return {v.getVector(threshold)}; + auto vec = v.getVector(threshold); + auto* data = new std::complex[vec.size()]; + std::copy(vec.begin(), vec.end(), data); + nb::capsule owner( + data, [](void* p) noexcept { delete[] (std::complex*)p; }); + return Vector(data, {vec.size()}, owner); } // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerVectorDDs(const py::module& mod) { - auto vec = py::class_(mod, "VectorDD"); +void registerVectorDDs(const nb::module_& m) { + auto vec = nb::class_(m, "VectorDD"); vec.def("is_terminal", &dd::vEdge::isTerminal); vec.def("is_zero_terminal", &dd::vEdge::isZeroTerminal); - vec.def("size", py::overload_cast<>(&dd::vEdge::size, py::const_)); + vec.def("size", nb::overload_cast<>(&dd::vEdge::size, nb::const_)); vec.def( "__getitem__", @@ -67,15 +65,6 @@ void registerVectorDDs(const py::module& mod) { }, "num_qubits"_a, "decisions"_a); - py::class_(mod, "Vector", py::buffer_protocol()) - .def_buffer([](Vector& vector) -> py::buffer_info { - return py::buffer_info( - vector.v.data(), sizeof(std::complex), - // NOLINTNEXTLINE(misc-include-cleaner) - py::format_descriptor>::format(), 1, - {vector.v.size()}, {sizeof(std::complex)}); - }); - vec.def("get_vector", &getVector, "threshold"_a = 0.); vec.def( diff --git a/test/python/dd/test_vector_dds.py b/test/python/dd/test_vector_dds.py index c9e2d5cc4d..2871ff1fca 100644 --- a/test/python/dd/test_vector_dds.py +++ b/test/python/dd/test_vector_dds.py @@ -96,5 +96,6 @@ def test_from_vector() -> None: vec /= np.linalg.norm(vec) dd = p.from_vector(vec) vec2 = dd.get_vector() + print(vec2) assert np.allclose(vec, vec2) p.dec_ref_vec(dd) From 1babd3edca9490a6999548884eec3140e657148e Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 9 Dec 2025 19:24:04 +0100 Subject: [PATCH 005/144] Add necessary nanobind/stl headers --- bindings/ir/operations/register_compound_operation.cpp | 2 ++ bindings/ir/operations/register_control.cpp | 1 + bindings/ir/operations/register_if_else_operation.cpp | 1 + bindings/ir/operations/register_operation.cpp | 3 +++ bindings/ir/operations/register_standard_operation.cpp | 2 +- bindings/ir/operations/register_symbolic_operation.cpp | 3 +++ bindings/ir/register_quantum_computation.cpp | 3 +++ bindings/ir/symbolic/register_expression.cpp | 1 + test/python/plugins/test_qiskit.py | 8 ++++++++ 9 files changed, 23 insertions(+), 1 deletion(-) diff --git a/bindings/ir/operations/register_compound_operation.cpp b/bindings/ir/operations/register_compound_operation.cpp index bc55acc375..622b25cbe4 100644 --- a/bindings/ir/operations/register_compound_operation.cpp +++ b/bindings/ir/operations/register_compound_operation.cpp @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/bindings/ir/operations/register_control.cpp b/bindings/ir/operations/register_control.cpp index e2148b0886..dfd2bc1dec 100644 --- a/bindings/ir/operations/register_control.cpp +++ b/bindings/ir/operations/register_control.cpp @@ -13,6 +13,7 @@ #include #include +#include #include namespace mqt { diff --git a/bindings/ir/operations/register_if_else_operation.cpp b/bindings/ir/operations/register_if_else_operation.cpp index 32e4995644..020eae5df8 100644 --- a/bindings/ir/operations/register_if_else_operation.cpp +++ b/bindings/ir/operations/register_if_else_operation.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/bindings/ir/operations/register_operation.cpp b/bindings/ir/operations/register_operation.cpp index 881f5a2254..0f371b1ff4 100644 --- a/bindings/ir/operations/register_operation.cpp +++ b/bindings/ir/operations/register_operation.cpp @@ -12,6 +12,9 @@ #include "ir/operations/Operation.hpp" #include +#include +#include +#include #include namespace mqt { diff --git a/bindings/ir/operations/register_standard_operation.cpp b/bindings/ir/operations/register_standard_operation.cpp index 9a99ca3024..537be99e80 100644 --- a/bindings/ir/operations/register_standard_operation.cpp +++ b/bindings/ir/operations/register_standard_operation.cpp @@ -15,9 +15,9 @@ #include "ir/operations/StandardOperation.hpp" #include +#include #include #include -#include #include #include diff --git a/bindings/ir/operations/register_symbolic_operation.cpp b/bindings/ir/operations/register_symbolic_operation.cpp index 2576f86299..84d56103c3 100644 --- a/bindings/ir/operations/register_symbolic_operation.cpp +++ b/bindings/ir/operations/register_symbolic_operation.cpp @@ -16,6 +16,9 @@ #include "ir/operations/SymbolicOperation.hpp" #include +#include +#include +#include #include #include diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index 151700dd7f..0d77d9b543 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -21,7 +21,10 @@ #include #include #include +#include #include +#include +#include #include #include #include diff --git a/bindings/ir/symbolic/register_expression.cpp b/bindings/ir/symbolic/register_expression.cpp index 5598f21873..ae9bd5cfa6 100644 --- a/bindings/ir/symbolic/register_expression.cpp +++ b/bindings/ir/symbolic/register_expression.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include diff --git a/test/python/plugins/test_qiskit.py b/test/python/plugins/test_qiskit.py index b396d742ba..2125a4964e 100644 --- a/test/python/plugins/test_qiskit.py +++ b/test/python/plugins/test_qiskit.py @@ -579,6 +579,7 @@ def test_if_else_operation_bit() -> None: assert qasm_input == qasm_output +@pytest.mark.skip(reason="Causes crash") def test_trivial_initial_layout_multiple_registers() -> None: """Test that trivial initial layout import works with multiple registers. @@ -609,6 +610,7 @@ def test_trivial_initial_layout_multiple_registers() -> None: assert mqt_qc.output_permutation == mqt_qc_2.output_permutation +@pytest.mark.skip(reason="Causes crash") def test_non_trivial_initial_layout_multiple_registers() -> None: """Test that non-trivial initial layout import works with multiple registers.""" a = QuantumRegister(2, "a") @@ -634,6 +636,7 @@ def test_non_trivial_initial_layout_multiple_registers() -> None: assert mqt_qc.output_permutation == mqt_qc_2.output_permutation +@pytest.mark.skip(reason="Causes crash") def test_non_symmetric_initial_layout_multiple_registers() -> None: """Test that non-symmetric initial layout import works with multiple registers.""" a = QuantumRegister(2, "a") @@ -659,6 +662,7 @@ def test_non_symmetric_initial_layout_multiple_registers() -> None: assert mqt_qc.output_permutation == mqt_qc_2.output_permutation +@pytest.mark.skip(reason="Causes crash") def test_initial_layout_with_ancilla_in_front() -> None: """Test that initial layout import works with ancilla in front.""" a = QuantumRegister(2, "a") @@ -686,6 +690,7 @@ def test_initial_layout_with_ancilla_in_front() -> None: assert mqt_qc.output_permutation == mqt_qc_2.output_permutation +@pytest.mark.skip(reason="Causes crash") def test_initial_layout_with_ancilla_in_back() -> None: """Test that initial layout import works with ancilla in back.""" a = QuantumRegister(2, "a") @@ -725,6 +730,7 @@ def test_symbolic_global_phase() -> None: assert mqt_qc.global_phase == 0 +@pytest.mark.skip(reason="Causes crash") def test_final_layout_without_permutation() -> None: """Test that the output permutation remains the same as the initial layout when routing is not performed.""" qc = QuantumCircuit(3) @@ -766,6 +772,7 @@ def backend() -> GenericBackendV2: ) +@pytest.mark.skip(reason="Causes crash") def test_final_layout_with_permutation(backend: GenericBackendV2) -> None: """Test that the output permutation gets updated correctly when routing is performed.""" qc = QuantumCircuit(3) @@ -797,6 +804,7 @@ def test_final_layout_with_permutation(backend: GenericBackendV2) -> None: assert mqt_qc.output_permutation == mqt_qc_2.output_permutation +@pytest.mark.skip(reason="Causes crash") def test_final_layout_with_permutation_ancilla_in_front_and_back(backend: GenericBackendV2) -> None: """Test that permutation update is correct with multiple registers and ancilla qubits.""" e = QuantumRegister(2, "e") From 5b22fad299fcea90a31e791413b1693dee1b9af6 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 9 Dec 2025 20:34:23 +0100 Subject: [PATCH 006/144] Fix iterator implementations --- bindings/ir/register_permutation.cpp | 9 +++++---- bindings/ir/symbolic/register_expression.cpp | 5 +++-- test/python/plugins/test_qiskit.py | 8 -------- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index 7e64410ff1..cc7bce5130 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -56,14 +56,14 @@ void registerPermutation(nb::module_& m) { .def("__len__", &qc::Permutation::size) .def("__iter__", [](const qc::Permutation& p) { - return nb::make_key_iterator( - nb::handle(), "PermutationKeyIterator", p.begin(), p.end()); + return nb::make_key_iterator(nb::type(), + "key_iterator", p.begin(), p.end()); }) .def( "items", [](const qc::Permutation& p) { - return nb::make_iterator(nb::handle(), "PermutationItemIterator", - p.begin(), p.end()); + return nb::make_iterator(nb::type(), + "item_iterator", p.begin(), p.end()); }, nb::keep_alive<0, 1>()) .def(nb::self == nb::self) @@ -94,6 +94,7 @@ void registerPermutation(nb::module_& m) { ss << "})"; return ss.str(); }); + nb::implicitly_convertible(); } } // namespace mqt diff --git a/bindings/ir/symbolic/register_expression.cpp b/bindings/ir/symbolic/register_expression.cpp index ae9bd5cfa6..53e611a4be 100644 --- a/bindings/ir/symbolic/register_expression.cpp +++ b/bindings/ir/symbolic/register_expression.cpp @@ -48,8 +48,9 @@ void registerExpression(nb::module_& m) { .def( "__iter__", [](const sym::Expression& expr) { - return nb::make_iterator(nb::handle(), "ExpressionIterator", - expr.begin(), expr.end()); + return nb::make_iterator( + nb::type>(), "iterator", + expr.begin(), expr.end()); }, nb::keep_alive<0, 1>()) .def("__getitem__", diff --git a/test/python/plugins/test_qiskit.py b/test/python/plugins/test_qiskit.py index 2125a4964e..b396d742ba 100644 --- a/test/python/plugins/test_qiskit.py +++ b/test/python/plugins/test_qiskit.py @@ -579,7 +579,6 @@ def test_if_else_operation_bit() -> None: assert qasm_input == qasm_output -@pytest.mark.skip(reason="Causes crash") def test_trivial_initial_layout_multiple_registers() -> None: """Test that trivial initial layout import works with multiple registers. @@ -610,7 +609,6 @@ def test_trivial_initial_layout_multiple_registers() -> None: assert mqt_qc.output_permutation == mqt_qc_2.output_permutation -@pytest.mark.skip(reason="Causes crash") def test_non_trivial_initial_layout_multiple_registers() -> None: """Test that non-trivial initial layout import works with multiple registers.""" a = QuantumRegister(2, "a") @@ -636,7 +634,6 @@ def test_non_trivial_initial_layout_multiple_registers() -> None: assert mqt_qc.output_permutation == mqt_qc_2.output_permutation -@pytest.mark.skip(reason="Causes crash") def test_non_symmetric_initial_layout_multiple_registers() -> None: """Test that non-symmetric initial layout import works with multiple registers.""" a = QuantumRegister(2, "a") @@ -662,7 +659,6 @@ def test_non_symmetric_initial_layout_multiple_registers() -> None: assert mqt_qc.output_permutation == mqt_qc_2.output_permutation -@pytest.mark.skip(reason="Causes crash") def test_initial_layout_with_ancilla_in_front() -> None: """Test that initial layout import works with ancilla in front.""" a = QuantumRegister(2, "a") @@ -690,7 +686,6 @@ def test_initial_layout_with_ancilla_in_front() -> None: assert mqt_qc.output_permutation == mqt_qc_2.output_permutation -@pytest.mark.skip(reason="Causes crash") def test_initial_layout_with_ancilla_in_back() -> None: """Test that initial layout import works with ancilla in back.""" a = QuantumRegister(2, "a") @@ -730,7 +725,6 @@ def test_symbolic_global_phase() -> None: assert mqt_qc.global_phase == 0 -@pytest.mark.skip(reason="Causes crash") def test_final_layout_without_permutation() -> None: """Test that the output permutation remains the same as the initial layout when routing is not performed.""" qc = QuantumCircuit(3) @@ -772,7 +766,6 @@ def backend() -> GenericBackendV2: ) -@pytest.mark.skip(reason="Causes crash") def test_final_layout_with_permutation(backend: GenericBackendV2) -> None: """Test that the output permutation gets updated correctly when routing is performed.""" qc = QuantumCircuit(3) @@ -804,7 +797,6 @@ def test_final_layout_with_permutation(backend: GenericBackendV2) -> None: assert mqt_qc.output_permutation == mqt_qc_2.output_permutation -@pytest.mark.skip(reason="Causes crash") def test_final_layout_with_permutation_ancilla_in_front_and_back(backend: GenericBackendV2) -> None: """Test that permutation update is correct with multiple registers and ancilla qubits.""" e = QuantumRegister(2, "e") From 78c840c421b4af89276c644a28f50ce25a388045 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 9 Dec 2025 20:45:55 +0100 Subject: [PATCH 007/144] Remove pybind11 dependency --- .clang-tidy | 2 - .github/workflows/ci.yml | 2 +- README.md | 1 - cmake/ExternalDependencies.cmake | 14 -- docs/qdmi/driver.md | 2 +- pyproject.toml | 2 - uv.lock | 215 +++++++++++++++---------------- 7 files changed, 103 insertions(+), 135 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 515cf5881a..5e602655a6 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -73,5 +73,3 @@ CheckOptions: value: CamelCase - key: readability-identifier-naming.VariableCase value: camelBack - - key: misc-include-cleaner.IgnoreHeaders - value: pybind11/detail/.* diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a7663d27c3..e662ed902b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -149,7 +149,7 @@ jobs: build-project: true files-changed-only: true setup-python: true - install-pkgs: "pybind11==3.0.1" + install-pkgs: "nanobind==2.10.1" cpp-linter-extra-args: "-std=c++20" python-tests: diff --git a/README.md b/README.md index b17ad7bd4a..130b320002 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,6 @@ The project relies on some external dependencies: - [boost/multiprecision](https://github.com/boostorg/multiprecision): A library for multiprecision arithmetic (used in the ZX package). - [nlohmann/json](https://github.com/nlohmann/json): A JSON library for modern C++. -- [pybind/pybind11_json](https://github.com/pybind/pybind11_json): A library for using `nlohmann::json` with `pybind11` (only used for creating the Python bindings). - [google/googletest](https://github.com/google/googletest): A testing framework for C++ (only used in tests). CMake will automatically look for installed versions of these libraries. If it does not find them, they will be fetched automatically at configure time via the [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html) module (check out the documentation for more information on how to customize this behavior). diff --git a/cmake/ExternalDependencies.cmake b/cmake/ExternalDependencies.cmake index e67aac6c85..07da29d47a 100644 --- a/cmake/ExternalDependencies.cmake +++ b/cmake/ExternalDependencies.cmake @@ -13,20 +13,6 @@ include(CMakeDependentOption) set(FETCH_PACKAGES "") if(BUILD_MQT_CORE_BINDINGS) - if(NOT SKBUILD) - # Manually detect the installed pybind11 package and import it into CMake. - execute_process( - COMMAND "${Python_EXECUTABLE}" -m pybind11 --cmakedir - OUTPUT_STRIP_TRAILING_WHITESPACE - OUTPUT_VARIABLE pybind11_DIR) - list(APPEND CMAKE_PREFIX_PATH "${pybind11_DIR}") - endif() - - message(STATUS "Python executable: ${Python_EXECUTABLE}") - - # add pybind11 library - find_package(pybind11 3.0.1 CONFIG REQUIRED) - # Detect the installed nanobind package and import it into CMake execute_process( COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir diff --git a/docs/qdmi/driver.md b/docs/qdmi/driver.md index ed836dae88..906c6cd390 100644 --- a/docs/qdmi/driver.md +++ b/docs/qdmi/driver.md @@ -17,7 +17,7 @@ Other devices can be loaded dynamically at runtime via {cpp:func}`qdmi::Driver:: ## Python Bindings -The QDMI Driver is implemented in C++ and exposed to Python via [pybind11](https://pybind11.readthedocs.io). +The QDMI Driver is implemented in C++ and exposed to Python via [{code}`nanobind`](https://nanobind.readthedocs.io/). Direct binding of the QDMI Client interface functions is not feasible due to technical limitations. Instead, a FoMaC (Figure of Merits and Constraints) library defines wrapper classes ({cpp:class}`~fomac::Session`, {cpp:class}`~fomac::Session::Device`, {cpp:class}`~fomac::Session::Device::Site`, {cpp:class}`~fomac::Session::Device::Operation`, {cpp:class}`~fomac::Session::Job`) for the QDMI entities. These classes together with their methods are then exposed to Python, see {py:class}`~mqt.core.fomac.Session`, {py:class}`~mqt.core.fomac.Device`, {py:class}`~mqt.core.fomac.Device.Site`, {py:class}`~mqt.core.fomac.Device.Operation`, {py:class}`~mqt.core.fomac.Job`. diff --git a/pyproject.toml b/pyproject.toml index a7457df8e5..f5bd654001 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,6 @@ [build-system] requires = [ "nanobind>=2.10.1", - "pybind11>=3.0.1", "scikit-build-core>=0.11.6", "setuptools-scm>=9.2.2", ] @@ -317,7 +316,6 @@ exclude = [ [dependency-groups] build = [ "nanobind>=2.10.1", - "pybind11>=3.0.1", "scikit-build-core>=0.11.6", "setuptools-scm>=9.2.2", ] diff --git a/uv.lock b/uv.lock index 9ee9d1a41d..d39bd57eed 100644 --- a/uv.lock +++ b/uv.lock @@ -522,101 +522,101 @@ wheels = [ [[package]] name = "coverage" -version = "7.12.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/89/26/4a96807b193b011588099c3b5c89fbb05294e5b90e71018e065465f34eb6/coverage-7.12.0.tar.gz", hash = "sha256:fc11e0a4e372cb5f282f16ef90d4a585034050ccda536451901abfb19a57f40c", size = 819341, upload-time = "2025-11-18T13:34:20.766Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/4a/0dc3de1c172d35abe512332cfdcc43211b6ebce629e4cc42e6cd25ed8f4d/coverage-7.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:32b75c2ba3f324ee37af3ccee5b30458038c50b349ad9b88cee85096132a575b", size = 217409, upload-time = "2025-11-18T13:31:53.122Z" }, - { url = "https://files.pythonhosted.org/packages/01/c3/086198b98db0109ad4f84241e8e9ea7e5fb2db8c8ffb787162d40c26cc76/coverage-7.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cb2a1b6ab9fe833714a483a915de350abc624a37149649297624c8d57add089c", size = 217927, upload-time = "2025-11-18T13:31:54.458Z" }, - { url = "https://files.pythonhosted.org/packages/5d/5f/34614dbf5ce0420828fc6c6f915126a0fcb01e25d16cf141bf5361e6aea6/coverage-7.12.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5734b5d913c3755e72f70bf6cc37a0518d4f4745cde760c5d8e12005e62f9832", size = 244678, upload-time = "2025-11-18T13:31:55.805Z" }, - { url = "https://files.pythonhosted.org/packages/55/7b/6b26fb32e8e4a6989ac1d40c4e132b14556131493b1d06bc0f2be169c357/coverage-7.12.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b527a08cdf15753279b7afb2339a12073620b761d79b81cbe2cdebdb43d90daa", size = 246507, upload-time = "2025-11-18T13:31:57.05Z" }, - { url = "https://files.pythonhosted.org/packages/06/42/7d70e6603d3260199b90fb48b537ca29ac183d524a65cc31366b2e905fad/coverage-7.12.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9bb44c889fb68004e94cab71f6a021ec83eac9aeabdbb5a5a88821ec46e1da73", size = 248366, upload-time = "2025-11-18T13:31:58.362Z" }, - { url = "https://files.pythonhosted.org/packages/2d/4a/d86b837923878424c72458c5b25e899a3c5ca73e663082a915f5b3c4d749/coverage-7.12.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4b59b501455535e2e5dde5881739897967b272ba25988c89145c12d772810ccb", size = 245366, upload-time = "2025-11-18T13:31:59.572Z" }, - { url = "https://files.pythonhosted.org/packages/e6/c2/2adec557e0aa9721875f06ced19730fdb7fc58e31b02b5aa56f2ebe4944d/coverage-7.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d8842f17095b9868a05837b7b1b73495293091bed870e099521ada176aa3e00e", size = 246408, upload-time = "2025-11-18T13:32:00.784Z" }, - { url = "https://files.pythonhosted.org/packages/5a/4b/8bd1f1148260df11c618e535fdccd1e5aaf646e55b50759006a4f41d8a26/coverage-7.12.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c5a6f20bf48b8866095c6820641e7ffbe23f2ac84a2efc218d91235e404c7777", size = 244416, upload-time = "2025-11-18T13:32:01.963Z" }, - { url = "https://files.pythonhosted.org/packages/0e/13/3a248dd6a83df90414c54a4e121fd081fb20602ca43955fbe1d60e2312a9/coverage-7.12.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:5f3738279524e988d9da2893f307c2093815c623f8d05a8f79e3eff3a7a9e553", size = 244681, upload-time = "2025-11-18T13:32:03.408Z" }, - { url = "https://files.pythonhosted.org/packages/76/30/aa833827465a5e8c938935f5d91ba055f70516941078a703740aaf1aa41f/coverage-7.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0d68c1f7eabbc8abe582d11fa393ea483caf4f44b0af86881174769f185c94d", size = 245300, upload-time = "2025-11-18T13:32:04.686Z" }, - { url = "https://files.pythonhosted.org/packages/38/24/f85b3843af1370fb3739fa7571819b71243daa311289b31214fe3e8c9d68/coverage-7.12.0-cp310-cp310-win32.whl", hash = "sha256:7670d860e18b1e3ee5930b17a7d55ae6287ec6e55d9799982aa103a2cc1fa2ef", size = 220008, upload-time = "2025-11-18T13:32:05.806Z" }, - { url = "https://files.pythonhosted.org/packages/3a/a2/c7da5b9566f7164db9eefa133d17761ecb2c2fde9385d754e5b5c80f710d/coverage-7.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:f999813dddeb2a56aab5841e687b68169da0d3f6fc78ccf50952fa2463746022", size = 220943, upload-time = "2025-11-18T13:32:07.166Z" }, - { url = "https://files.pythonhosted.org/packages/5a/0c/0dfe7f0487477d96432e4815537263363fb6dd7289743a796e8e51eabdf2/coverage-7.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa124a3683d2af98bd9d9c2bfa7a5076ca7e5ab09fdb96b81fa7d89376ae928f", size = 217535, upload-time = "2025-11-18T13:32:08.812Z" }, - { url = "https://files.pythonhosted.org/packages/9b/f5/f9a4a053a5bbff023d3bec259faac8f11a1e5a6479c2ccf586f910d8dac7/coverage-7.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d93fbf446c31c0140208dcd07c5d882029832e8ed7891a39d6d44bd65f2316c3", size = 218044, upload-time = "2025-11-18T13:32:10.329Z" }, - { url = "https://files.pythonhosted.org/packages/95/c5/84fc3697c1fa10cd8571919bf9693f693b7373278daaf3b73e328d502bc8/coverage-7.12.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:52ca620260bd8cd6027317bdd8b8ba929be1d741764ee765b42c4d79a408601e", size = 248440, upload-time = "2025-11-18T13:32:12.536Z" }, - { url = "https://files.pythonhosted.org/packages/f4/36/2d93fbf6a04670f3874aed397d5a5371948a076e3249244a9e84fb0e02d6/coverage-7.12.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f3433ffd541380f3a0e423cff0f4926d55b0cc8c1d160fdc3be24a4c03aa65f7", size = 250361, upload-time = "2025-11-18T13:32:13.852Z" }, - { url = "https://files.pythonhosted.org/packages/5d/49/66dc65cc456a6bfc41ea3d0758c4afeaa4068a2b2931bf83be6894cf1058/coverage-7.12.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7bbb321d4adc9f65e402c677cd1c8e4c2d0105d3ce285b51b4d87f1d5db5245", size = 252472, upload-time = "2025-11-18T13:32:15.068Z" }, - { url = "https://files.pythonhosted.org/packages/35/1f/ebb8a18dffd406db9fcd4b3ae42254aedcaf612470e8712f12041325930f/coverage-7.12.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22a7aade354a72dff3b59c577bfd18d6945c61f97393bc5fb7bd293a4237024b", size = 248592, upload-time = "2025-11-18T13:32:16.328Z" }, - { url = "https://files.pythonhosted.org/packages/da/a8/67f213c06e5ea3b3d4980df7dc344d7fea88240b5fe878a5dcbdfe0e2315/coverage-7.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3ff651dcd36d2fea66877cd4a82de478004c59b849945446acb5baf9379a1b64", size = 250167, upload-time = "2025-11-18T13:32:17.687Z" }, - { url = "https://files.pythonhosted.org/packages/f0/00/e52aef68154164ea40cc8389c120c314c747fe63a04b013a5782e989b77f/coverage-7.12.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:31b8b2e38391a56e3cea39d22a23faaa7c3fc911751756ef6d2621d2a9daf742", size = 248238, upload-time = "2025-11-18T13:32:19.2Z" }, - { url = "https://files.pythonhosted.org/packages/1f/a4/4d88750bcf9d6d66f77865e5a05a20e14db44074c25fd22519777cb69025/coverage-7.12.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:297bc2da28440f5ae51c845a47c8175a4db0553a53827886e4fb25c66633000c", size = 247964, upload-time = "2025-11-18T13:32:21.027Z" }, - { url = "https://files.pythonhosted.org/packages/a7/6b/b74693158899d5b47b0bf6238d2c6722e20ba749f86b74454fac0696bb00/coverage-7.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6ff7651cc01a246908eac162a6a86fc0dbab6de1ad165dfb9a1e2ec660b44984", size = 248862, upload-time = "2025-11-18T13:32:22.304Z" }, - { url = "https://files.pythonhosted.org/packages/18/de/6af6730227ce0e8ade307b1cc4a08e7f51b419a78d02083a86c04ccceb29/coverage-7.12.0-cp311-cp311-win32.whl", hash = "sha256:313672140638b6ddb2c6455ddeda41c6a0b208298034544cfca138978c6baed6", size = 220033, upload-time = "2025-11-18T13:32:23.714Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a1/e7f63021a7c4fe20994359fcdeae43cbef4a4d0ca36a5a1639feeea5d9e1/coverage-7.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a1783ed5bd0d5938d4435014626568dc7f93e3cb99bc59188cc18857c47aa3c4", size = 220966, upload-time = "2025-11-18T13:32:25.599Z" }, - { url = "https://files.pythonhosted.org/packages/77/e8/deae26453f37c20c3aa0c4433a1e32cdc169bf415cce223a693117aa3ddd/coverage-7.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:4648158fd8dd9381b5847622df1c90ff314efbfc1df4550092ab6013c238a5fc", size = 219637, upload-time = "2025-11-18T13:32:27.265Z" }, - { url = "https://files.pythonhosted.org/packages/02/bf/638c0427c0f0d47638242e2438127f3c8ee3cfc06c7fdeb16778ed47f836/coverage-7.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:29644c928772c78512b48e14156b81255000dcfd4817574ff69def189bcb3647", size = 217704, upload-time = "2025-11-18T13:32:28.906Z" }, - { url = "https://files.pythonhosted.org/packages/08/e1/706fae6692a66c2d6b871a608bbde0da6281903fa0e9f53a39ed441da36a/coverage-7.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8638cbb002eaa5d7c8d04da667813ce1067080b9a91099801a0053086e52b736", size = 218064, upload-time = "2025-11-18T13:32:30.161Z" }, - { url = "https://files.pythonhosted.org/packages/a9/8b/eb0231d0540f8af3ffda39720ff43cb91926489d01524e68f60e961366e4/coverage-7.12.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:083631eeff5eb9992c923e14b810a179798bb598e6a0dd60586819fc23be6e60", size = 249560, upload-time = "2025-11-18T13:32:31.835Z" }, - { url = "https://files.pythonhosted.org/packages/e9/a1/67fb52af642e974d159b5b379e4d4c59d0ebe1288677fbd04bbffe665a82/coverage-7.12.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:99d5415c73ca12d558e07776bd957c4222c687b9f1d26fa0e1b57e3598bdcde8", size = 252318, upload-time = "2025-11-18T13:32:33.178Z" }, - { url = "https://files.pythonhosted.org/packages/41/e5/38228f31b2c7665ebf9bdfdddd7a184d56450755c7e43ac721c11a4b8dab/coverage-7.12.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e949ebf60c717c3df63adb4a1a366c096c8d7fd8472608cd09359e1bd48ef59f", size = 253403, upload-time = "2025-11-18T13:32:34.45Z" }, - { url = "https://files.pythonhosted.org/packages/ec/4b/df78e4c8188f9960684267c5a4897836f3f0f20a20c51606ee778a1d9749/coverage-7.12.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d907ddccbca819afa2cd014bc69983b146cca2735a0b1e6259b2a6c10be1e70", size = 249984, upload-time = "2025-11-18T13:32:35.747Z" }, - { url = "https://files.pythonhosted.org/packages/ba/51/bb163933d195a345c6f63eab9e55743413d064c291b6220df754075c2769/coverage-7.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b1518ecbad4e6173f4c6e6c4a46e49555ea5679bf3feda5edb1b935c7c44e8a0", size = 251339, upload-time = "2025-11-18T13:32:37.352Z" }, - { url = "https://files.pythonhosted.org/packages/15/40/c9b29cdb8412c837cdcbc2cfa054547dd83affe6cbbd4ce4fdb92b6ba7d1/coverage-7.12.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:51777647a749abdf6f6fd8c7cffab12de68ab93aab15efc72fbbb83036c2a068", size = 249489, upload-time = "2025-11-18T13:32:39.212Z" }, - { url = "https://files.pythonhosted.org/packages/c8/da/b3131e20ba07a0de4437a50ef3b47840dfabf9293675b0cd5c2c7f66dd61/coverage-7.12.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:42435d46d6461a3b305cdfcad7cdd3248787771f53fe18305548cba474e6523b", size = 249070, upload-time = "2025-11-18T13:32:40.598Z" }, - { url = "https://files.pythonhosted.org/packages/70/81/b653329b5f6302c08d683ceff6785bc60a34be9ae92a5c7b63ee7ee7acec/coverage-7.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5bcead88c8423e1855e64b8057d0544e33e4080b95b240c2a355334bb7ced937", size = 250929, upload-time = "2025-11-18T13:32:42.915Z" }, - { url = "https://files.pythonhosted.org/packages/a3/00/250ac3bca9f252a5fb1338b5ad01331ebb7b40223f72bef5b1b2cb03aa64/coverage-7.12.0-cp312-cp312-win32.whl", hash = "sha256:dcbb630ab034e86d2a0f79aefd2be07e583202f41e037602d438c80044957baa", size = 220241, upload-time = "2025-11-18T13:32:44.665Z" }, - { url = "https://files.pythonhosted.org/packages/64/1c/77e79e76d37ce83302f6c21980b45e09f8aa4551965213a10e62d71ce0ab/coverage-7.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:2fd8354ed5d69775ac42986a691fbf68b4084278710cee9d7c3eaa0c28fa982a", size = 221051, upload-time = "2025-11-18T13:32:46.008Z" }, - { url = "https://files.pythonhosted.org/packages/31/f5/641b8a25baae564f9e52cac0e2667b123de961985709a004e287ee7663cc/coverage-7.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:737c3814903be30695b2de20d22bcc5428fdae305c61ba44cdc8b3252984c49c", size = 219692, upload-time = "2025-11-18T13:32:47.372Z" }, - { url = "https://files.pythonhosted.org/packages/b8/14/771700b4048774e48d2c54ed0c674273702713c9ee7acdfede40c2666747/coverage-7.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:47324fffca8d8eae7e185b5bb20c14645f23350f870c1649003618ea91a78941", size = 217725, upload-time = "2025-11-18T13:32:49.22Z" }, - { url = "https://files.pythonhosted.org/packages/17/a7/3aa4144d3bcb719bf67b22d2d51c2d577bf801498c13cb08f64173e80497/coverage-7.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ccf3b2ede91decd2fb53ec73c1f949c3e034129d1e0b07798ff1d02ea0c8fa4a", size = 218098, upload-time = "2025-11-18T13:32:50.78Z" }, - { url = "https://files.pythonhosted.org/packages/fc/9c/b846bbc774ff81091a12a10203e70562c91ae71badda00c5ae5b613527b1/coverage-7.12.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b365adc70a6936c6b0582dc38746b33b2454148c02349345412c6e743efb646d", size = 249093, upload-time = "2025-11-18T13:32:52.554Z" }, - { url = "https://files.pythonhosted.org/packages/76/b6/67d7c0e1f400b32c883e9342de4a8c2ae7c1a0b57c5de87622b7262e2309/coverage-7.12.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bc13baf85cd8a4cfcf4a35c7bc9d795837ad809775f782f697bf630b7e200211", size = 251686, upload-time = "2025-11-18T13:32:54.862Z" }, - { url = "https://files.pythonhosted.org/packages/cc/75/b095bd4b39d49c3be4bffbb3135fea18a99a431c52dd7513637c0762fecb/coverage-7.12.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:099d11698385d572ceafb3288a5b80fe1fc58bf665b3f9d362389de488361d3d", size = 252930, upload-time = "2025-11-18T13:32:56.417Z" }, - { url = "https://files.pythonhosted.org/packages/6e/f3/466f63015c7c80550bead3093aacabf5380c1220a2a93c35d374cae8f762/coverage-7.12.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:473dc45d69694069adb7680c405fb1e81f60b2aff42c81e2f2c3feaf544d878c", size = 249296, upload-time = "2025-11-18T13:32:58.074Z" }, - { url = "https://files.pythonhosted.org/packages/27/86/eba2209bf2b7e28c68698fc13437519a295b2d228ba9e0ec91673e09fa92/coverage-7.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:583f9adbefd278e9de33c33d6846aa8f5d164fa49b47144180a0e037f0688bb9", size = 251068, upload-time = "2025-11-18T13:32:59.646Z" }, - { url = "https://files.pythonhosted.org/packages/ec/55/ca8ae7dbba962a3351f18940b359b94c6bafdd7757945fdc79ec9e452dc7/coverage-7.12.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2089cc445f2dc0af6f801f0d1355c025b76c24481935303cf1af28f636688f0", size = 249034, upload-time = "2025-11-18T13:33:01.481Z" }, - { url = "https://files.pythonhosted.org/packages/7a/d7/39136149325cad92d420b023b5fd900dabdd1c3a0d1d5f148ef4a8cedef5/coverage-7.12.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:950411f1eb5d579999c5f66c62a40961f126fc71e5e14419f004471957b51508", size = 248853, upload-time = "2025-11-18T13:33:02.935Z" }, - { url = "https://files.pythonhosted.org/packages/fe/b6/76e1add8b87ef60e00643b0b7f8f7bb73d4bf5249a3be19ebefc5793dd25/coverage-7.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b1aab7302a87bafebfe76b12af681b56ff446dc6f32ed178ff9c092ca776e6bc", size = 250619, upload-time = "2025-11-18T13:33:04.336Z" }, - { url = "https://files.pythonhosted.org/packages/95/87/924c6dc64f9203f7a3c1832a6a0eee5a8335dbe5f1bdadcc278d6f1b4d74/coverage-7.12.0-cp313-cp313-win32.whl", hash = "sha256:d7e0d0303c13b54db495eb636bc2465b2fb8475d4c8bcec8fe4b5ca454dfbae8", size = 220261, upload-time = "2025-11-18T13:33:06.493Z" }, - { url = "https://files.pythonhosted.org/packages/91/77/dd4aff9af16ff776bf355a24d87eeb48fc6acde54c907cc1ea89b14a8804/coverage-7.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:ce61969812d6a98a981d147d9ac583a36ac7db7766f2e64a9d4d059c2fe29d07", size = 221072, upload-time = "2025-11-18T13:33:07.926Z" }, - { url = "https://files.pythonhosted.org/packages/70/49/5c9dc46205fef31b1b226a6e16513193715290584317fd4df91cdaf28b22/coverage-7.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:bcec6f47e4cb8a4c2dc91ce507f6eefc6a1b10f58df32cdc61dff65455031dfc", size = 219702, upload-time = "2025-11-18T13:33:09.631Z" }, - { url = "https://files.pythonhosted.org/packages/9b/62/f87922641c7198667994dd472a91e1d9b829c95d6c29529ceb52132436ad/coverage-7.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:459443346509476170d553035e4a3eed7b860f4fe5242f02de1010501956ce87", size = 218420, upload-time = "2025-11-18T13:33:11.153Z" }, - { url = "https://files.pythonhosted.org/packages/85/dd/1cc13b2395ef15dbb27d7370a2509b4aee77890a464fb35d72d428f84871/coverage-7.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:04a79245ab2b7a61688958f7a855275997134bc84f4a03bc240cf64ff132abf6", size = 218773, upload-time = "2025-11-18T13:33:12.569Z" }, - { url = "https://files.pythonhosted.org/packages/74/40/35773cc4bb1e9d4658d4fb669eb4195b3151bef3bbd6f866aba5cd5dac82/coverage-7.12.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:09a86acaaa8455f13d6a99221d9654df249b33937b4e212b4e5a822065f12aa7", size = 260078, upload-time = "2025-11-18T13:33:14.037Z" }, - { url = "https://files.pythonhosted.org/packages/ec/ee/231bb1a6ffc2905e396557585ebc6bdc559e7c66708376d245a1f1d330fc/coverage-7.12.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:907e0df1b71ba77463687a74149c6122c3f6aac56c2510a5d906b2f368208560", size = 262144, upload-time = "2025-11-18T13:33:15.601Z" }, - { url = "https://files.pythonhosted.org/packages/28/be/32f4aa9f3bf0b56f3971001b56508352c7753915345d45fab4296a986f01/coverage-7.12.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b57e2d0ddd5f0582bae5437c04ee71c46cd908e7bc5d4d0391f9a41e812dd12", size = 264574, upload-time = "2025-11-18T13:33:17.354Z" }, - { url = "https://files.pythonhosted.org/packages/68/7c/00489fcbc2245d13ab12189b977e0cf06ff3351cb98bc6beba8bd68c5902/coverage-7.12.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:58c1c6aa677f3a1411fe6fb28ec3a942e4f665df036a3608816e0847fad23296", size = 259298, upload-time = "2025-11-18T13:33:18.958Z" }, - { url = "https://files.pythonhosted.org/packages/96/b4/f0760d65d56c3bea95b449e02570d4abd2549dc784bf39a2d4721a2d8ceb/coverage-7.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4c589361263ab2953e3c4cd2a94db94c4ad4a8e572776ecfbad2389c626e4507", size = 262150, upload-time = "2025-11-18T13:33:20.644Z" }, - { url = "https://files.pythonhosted.org/packages/c5/71/9a9314df00f9326d78c1e5a910f520d599205907432d90d1c1b7a97aa4b1/coverage-7.12.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:91b810a163ccad2e43b1faa11d70d3cf4b6f3d83f9fd5f2df82a32d47b648e0d", size = 259763, upload-time = "2025-11-18T13:33:22.189Z" }, - { url = "https://files.pythonhosted.org/packages/10/34/01a0aceed13fbdf925876b9a15d50862eb8845454301fe3cdd1df08b2182/coverage-7.12.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:40c867af715f22592e0d0fb533a33a71ec9e0f73a6945f722a0c85c8c1cbe3a2", size = 258653, upload-time = "2025-11-18T13:33:24.239Z" }, - { url = "https://files.pythonhosted.org/packages/8d/04/81d8fd64928acf1574bbb0181f66901c6c1c6279c8ccf5f84259d2c68ae9/coverage-7.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:68b0d0a2d84f333de875666259dadf28cc67858bc8fd8b3f1eae84d3c2bec455", size = 260856, upload-time = "2025-11-18T13:33:26.365Z" }, - { url = "https://files.pythonhosted.org/packages/f2/76/fa2a37bfaeaf1f766a2d2360a25a5297d4fb567098112f6517475eee120b/coverage-7.12.0-cp313-cp313t-win32.whl", hash = "sha256:73f9e7fbd51a221818fd11b7090eaa835a353ddd59c236c57b2199486b116c6d", size = 220936, upload-time = "2025-11-18T13:33:28.165Z" }, - { url = "https://files.pythonhosted.org/packages/f9/52/60f64d932d555102611c366afb0eb434b34266b1d9266fc2fe18ab641c47/coverage-7.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:24cff9d1f5743f67db7ba46ff284018a6e9aeb649b67aa1e70c396aa1b7cb23c", size = 222001, upload-time = "2025-11-18T13:33:29.656Z" }, - { url = "https://files.pythonhosted.org/packages/77/df/c303164154a5a3aea7472bf323b7c857fed93b26618ed9fc5c2955566bb0/coverage-7.12.0-cp313-cp313t-win_arm64.whl", hash = "sha256:c87395744f5c77c866d0f5a43d97cc39e17c7f1cb0115e54a2fe67ca75c5d14d", size = 220273, upload-time = "2025-11-18T13:33:31.415Z" }, - { url = "https://files.pythonhosted.org/packages/bf/2e/fc12db0883478d6e12bbd62d481210f0c8daf036102aa11434a0c5755825/coverage-7.12.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a1c59b7dc169809a88b21a936eccf71c3895a78f5592051b1af8f4d59c2b4f92", size = 217777, upload-time = "2025-11-18T13:33:32.86Z" }, - { url = "https://files.pythonhosted.org/packages/1f/c1/ce3e525d223350c6ec16b9be8a057623f54226ef7f4c2fee361ebb6a02b8/coverage-7.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8787b0f982e020adb732b9f051f3e49dd5054cebbc3f3432061278512a2b1360", size = 218100, upload-time = "2025-11-18T13:33:34.532Z" }, - { url = "https://files.pythonhosted.org/packages/15/87/113757441504aee3808cb422990ed7c8bcc2d53a6779c66c5adef0942939/coverage-7.12.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5ea5a9f7dc8877455b13dd1effd3202e0bca72f6f3ab09f9036b1bcf728f69ac", size = 249151, upload-time = "2025-11-18T13:33:36.135Z" }, - { url = "https://files.pythonhosted.org/packages/d9/1d/9529d9bd44049b6b05bb319c03a3a7e4b0a8a802d28fa348ad407e10706d/coverage-7.12.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fdba9f15849534594f60b47c9a30bc70409b54947319a7c4fd0e8e3d8d2f355d", size = 251667, upload-time = "2025-11-18T13:33:37.996Z" }, - { url = "https://files.pythonhosted.org/packages/11/bb/567e751c41e9c03dc29d3ce74b8c89a1e3396313e34f255a2a2e8b9ebb56/coverage-7.12.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a00594770eb715854fb1c57e0dea08cce6720cfbc531accdb9850d7c7770396c", size = 253003, upload-time = "2025-11-18T13:33:39.553Z" }, - { url = "https://files.pythonhosted.org/packages/e4/b3/c2cce2d8526a02fb9e9ca14a263ca6fc074449b33a6afa4892838c903528/coverage-7.12.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5560c7e0d82b42eb1951e4f68f071f8017c824ebfd5a6ebe42c60ac16c6c2434", size = 249185, upload-time = "2025-11-18T13:33:42.086Z" }, - { url = "https://files.pythonhosted.org/packages/0e/a7/967f93bb66e82c9113c66a8d0b65ecf72fc865adfba5a145f50c7af7e58d/coverage-7.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d6c2e26b481c9159c2773a37947a9718cfdc58893029cdfb177531793e375cfc", size = 251025, upload-time = "2025-11-18T13:33:43.634Z" }, - { url = "https://files.pythonhosted.org/packages/b9/b2/f2f6f56337bc1af465d5b2dc1ee7ee2141b8b9272f3bf6213fcbc309a836/coverage-7.12.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6e1a8c066dabcde56d5d9fed6a66bc19a2883a3fe051f0c397a41fc42aedd4cc", size = 248979, upload-time = "2025-11-18T13:33:46.04Z" }, - { url = "https://files.pythonhosted.org/packages/f4/7a/bf4209f45a4aec09d10a01a57313a46c0e0e8f4c55ff2965467d41a92036/coverage-7.12.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f7ba9da4726e446d8dd8aae5a6cd872511184a5d861de80a86ef970b5dacce3e", size = 248800, upload-time = "2025-11-18T13:33:47.546Z" }, - { url = "https://files.pythonhosted.org/packages/b8/b7/1e01b8696fb0521810f60c5bbebf699100d6754183e6cc0679bf2ed76531/coverage-7.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e0f483ab4f749039894abaf80c2f9e7ed77bbf3c737517fb88c8e8e305896a17", size = 250460, upload-time = "2025-11-18T13:33:49.537Z" }, - { url = "https://files.pythonhosted.org/packages/71/ae/84324fb9cb46c024760e706353d9b771a81b398d117d8c1fe010391c186f/coverage-7.12.0-cp314-cp314-win32.whl", hash = "sha256:76336c19a9ef4a94b2f8dc79f8ac2da3f193f625bb5d6f51a328cd19bfc19933", size = 220533, upload-time = "2025-11-18T13:33:51.16Z" }, - { url = "https://files.pythonhosted.org/packages/e2/71/1033629deb8460a8f97f83e6ac4ca3b93952e2b6f826056684df8275e015/coverage-7.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:7c1059b600aec6ef090721f8f633f60ed70afaffe8ecab85b59df748f24b31fe", size = 221348, upload-time = "2025-11-18T13:33:52.776Z" }, - { url = "https://files.pythonhosted.org/packages/0a/5f/ac8107a902f623b0c251abdb749be282dc2ab61854a8a4fcf49e276fce2f/coverage-7.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:172cf3a34bfef42611963e2b661302a8931f44df31629e5b1050567d6b90287d", size = 219922, upload-time = "2025-11-18T13:33:54.316Z" }, - { url = "https://files.pythonhosted.org/packages/79/6e/f27af2d4da367f16077d21ef6fe796c874408219fa6dd3f3efe7751bd910/coverage-7.12.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:aa7d48520a32cb21c7a9b31f81799e8eaec7239db36c3b670be0fa2403828d1d", size = 218511, upload-time = "2025-11-18T13:33:56.343Z" }, - { url = "https://files.pythonhosted.org/packages/67/dd/65fd874aa460c30da78f9d259400d8e6a4ef457d61ab052fd248f0050558/coverage-7.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:90d58ac63bc85e0fb919f14d09d6caa63f35a5512a2205284b7816cafd21bb03", size = 218771, upload-time = "2025-11-18T13:33:57.966Z" }, - { url = "https://files.pythonhosted.org/packages/55/e0/7c6b71d327d8068cb79c05f8f45bf1b6145f7a0de23bbebe63578fe5240a/coverage-7.12.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ca8ecfa283764fdda3eae1bdb6afe58bf78c2c3ec2b2edcb05a671f0bba7b3f9", size = 260151, upload-time = "2025-11-18T13:33:59.597Z" }, - { url = "https://files.pythonhosted.org/packages/49/ce/4697457d58285b7200de6b46d606ea71066c6e674571a946a6ea908fb588/coverage-7.12.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:874fe69a0785d96bd066059cd4368022cebbec1a8958f224f0016979183916e6", size = 262257, upload-time = "2025-11-18T13:34:01.166Z" }, - { url = "https://files.pythonhosted.org/packages/2f/33/acbc6e447aee4ceba88c15528dbe04a35fb4d67b59d393d2e0d6f1e242c1/coverage-7.12.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b3c889c0b8b283a24d721a9eabc8ccafcfc3aebf167e4cd0d0e23bf8ec4e339", size = 264671, upload-time = "2025-11-18T13:34:02.795Z" }, - { url = "https://files.pythonhosted.org/packages/87/ec/e2822a795c1ed44d569980097be839c5e734d4c0c1119ef8e0a073496a30/coverage-7.12.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bb5b894b3ec09dcd6d3743229dc7f2c42ef7787dc40596ae04c0edda487371e", size = 259231, upload-time = "2025-11-18T13:34:04.397Z" }, - { url = "https://files.pythonhosted.org/packages/72/c5/a7ec5395bb4a49c9b7ad97e63f0c92f6bf4a9e006b1393555a02dae75f16/coverage-7.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:79a44421cd5fba96aa57b5e3b5a4d3274c449d4c622e8f76882d76635501fd13", size = 262137, upload-time = "2025-11-18T13:34:06.068Z" }, - { url = "https://files.pythonhosted.org/packages/67/0c/02c08858b764129f4ecb8e316684272972e60777ae986f3865b10940bdd6/coverage-7.12.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:33baadc0efd5c7294f436a632566ccc1f72c867f82833eb59820ee37dc811c6f", size = 259745, upload-time = "2025-11-18T13:34:08.04Z" }, - { url = "https://files.pythonhosted.org/packages/5a/04/4fd32b7084505f3829a8fe45c1a74a7a728cb251aaadbe3bec04abcef06d/coverage-7.12.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c406a71f544800ef7e9e0000af706b88465f3573ae8b8de37e5f96c59f689ad1", size = 258570, upload-time = "2025-11-18T13:34:09.676Z" }, - { url = "https://files.pythonhosted.org/packages/48/35/2365e37c90df4f5342c4fa202223744119fe31264ee2924f09f074ea9b6d/coverage-7.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e71bba6a40883b00c6d571599b4627f50c360b3d0d02bfc658168936be74027b", size = 260899, upload-time = "2025-11-18T13:34:11.259Z" }, - { url = "https://files.pythonhosted.org/packages/05/56/26ab0464ca733fa325e8e71455c58c1c374ce30f7c04cebb88eabb037b18/coverage-7.12.0-cp314-cp314t-win32.whl", hash = "sha256:9157a5e233c40ce6613dead4c131a006adfda70e557b6856b97aceed01b0e27a", size = 221313, upload-time = "2025-11-18T13:34:12.863Z" }, - { url = "https://files.pythonhosted.org/packages/da/1c/017a3e1113ed34d998b27d2c6dba08a9e7cb97d362f0ec988fcd873dcf81/coverage-7.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e84da3a0fd233aeec797b981c51af1cabac74f9bd67be42458365b30d11b5291", size = 222423, upload-time = "2025-11-18T13:34:15.14Z" }, - { url = "https://files.pythonhosted.org/packages/4c/36/bcc504fdd5169301b52568802bb1b9cdde2e27a01d39fbb3b4b508ab7c2c/coverage-7.12.0-cp314-cp314t-win_arm64.whl", hash = "sha256:01d24af36fedda51c2b1aca56e4330a3710f83b02a5ff3743a6b015ffa7c9384", size = 220459, upload-time = "2025-11-18T13:34:17.222Z" }, - { url = "https://files.pythonhosted.org/packages/ce/a3/43b749004e3c09452e39bb56347a008f0a0668aad37324a99b5c8ca91d9e/coverage-7.12.0-py3-none-any.whl", hash = "sha256:159d50c0b12e060b15ed3d39f87ed43d4f7f7ad40b8a534f4dd331adbb51104a", size = 209503, upload-time = "2025-11-18T13:34:18.892Z" }, +version = "7.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/45/2c665ca77ec32ad67e25c77daf1cee28ee4558f3bc571cdbaf88a00b9f23/coverage-7.13.0.tar.gz", hash = "sha256:a394aa27f2d7ff9bc04cf703817773a59ad6dfbd577032e690f961d2460ee936", size = 820905, upload-time = "2025-12-08T13:14:38.055Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/08/bdd7ccca14096f7eb01412b87ac11e5d16e4cb54b6e328afc9dee8bdaec1/coverage-7.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:02d9fb9eccd48f6843c98a37bd6817462f130b86da8660461e8f5e54d4c06070", size = 217979, upload-time = "2025-12-08T13:12:14.505Z" }, + { url = "https://files.pythonhosted.org/packages/fa/f0/d1302e3416298a28b5663ae1117546a745d9d19fde7e28402b2c5c3e2109/coverage-7.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:367449cf07d33dc216c083f2036bb7d976c6e4903ab31be400ad74ad9f85ce98", size = 218496, upload-time = "2025-12-08T13:12:16.237Z" }, + { url = "https://files.pythonhosted.org/packages/07/26/d36c354c8b2a320819afcea6bffe72839efd004b98d1d166b90801d49d57/coverage-7.13.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cdb3c9f8fef0a954c632f64328a3935988d33a6604ce4bf67ec3e39670f12ae5", size = 245237, upload-time = "2025-12-08T13:12:17.858Z" }, + { url = "https://files.pythonhosted.org/packages/91/52/be5e85631e0eec547873d8b08dd67a5f6b111ecfe89a86e40b89b0c1c61c/coverage-7.13.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d10fd186aac2316f9bbb46ef91977f9d394ded67050ad6d84d94ed6ea2e8e54e", size = 247061, upload-time = "2025-12-08T13:12:19.132Z" }, + { url = "https://files.pythonhosted.org/packages/0f/45/a5e8fa0caf05fbd8fa0402470377bff09cc1f026d21c05c71e01295e55ab/coverage-7.13.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f88ae3e69df2ab62fb0bc5219a597cb890ba5c438190ffa87490b315190bb33", size = 248928, upload-time = "2025-12-08T13:12:20.702Z" }, + { url = "https://files.pythonhosted.org/packages/f5/42/ffb5069b6fd1b95fae482e02f3fecf380d437dd5a39bae09f16d2e2e7e01/coverage-7.13.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c4be718e51e86f553bcf515305a158a1cd180d23b72f07ae76d6017c3cc5d791", size = 245931, upload-time = "2025-12-08T13:12:22.243Z" }, + { url = "https://files.pythonhosted.org/packages/95/6e/73e809b882c2858f13e55c0c36e94e09ce07e6165d5644588f9517efe333/coverage-7.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a00d3a393207ae12f7c49bb1c113190883b500f48979abb118d8b72b8c95c032", size = 246968, upload-time = "2025-12-08T13:12:23.52Z" }, + { url = "https://files.pythonhosted.org/packages/87/08/64ebd9e64b6adb8b4a4662133d706fbaccecab972e0b3ccc23f64e2678ad/coverage-7.13.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a7b1cd820e1b6116f92c6128f1188e7afe421c7e1b35fa9836b11444e53ebd9", size = 244972, upload-time = "2025-12-08T13:12:24.781Z" }, + { url = "https://files.pythonhosted.org/packages/12/97/f4d27c6fe0cb375a5eced4aabcaef22de74766fb80a3d5d2015139e54b22/coverage-7.13.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:37eee4e552a65866f15dedd917d5e5f3d59805994260720821e2c1b51ac3248f", size = 245241, upload-time = "2025-12-08T13:12:28.041Z" }, + { url = "https://files.pythonhosted.org/packages/0c/94/42f8ae7f633bf4c118bf1038d80472f9dade88961a466f290b81250f7ab7/coverage-7.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:62d7c4f13102148c78d7353c6052af6d899a7f6df66a32bddcc0c0eb7c5326f8", size = 245847, upload-time = "2025-12-08T13:12:29.337Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2f/6369ca22b6b6d933f4f4d27765d313d8914cc4cce84f82a16436b1a233db/coverage-7.13.0-cp310-cp310-win32.whl", hash = "sha256:24e4e56304fdb56f96f80eabf840eab043b3afea9348b88be680ec5986780a0f", size = 220573, upload-time = "2025-12-08T13:12:30.905Z" }, + { url = "https://files.pythonhosted.org/packages/f1/dc/a6a741e519acceaeccc70a7f4cfe5d030efc4b222595f0677e101af6f1f3/coverage-7.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:74c136e4093627cf04b26a35dab8cbfc9b37c647f0502fc313376e11726ba303", size = 221509, upload-time = "2025-12-08T13:12:32.09Z" }, + { url = "https://files.pythonhosted.org/packages/f1/dc/888bf90d8b1c3d0b4020a40e52b9f80957d75785931ec66c7dfaccc11c7d/coverage-7.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0dfa3855031070058add1a59fdfda0192fd3e8f97e7c81de0596c145dea51820", size = 218104, upload-time = "2025-12-08T13:12:33.333Z" }, + { url = "https://files.pythonhosted.org/packages/8d/ea/069d51372ad9c380214e86717e40d1a743713a2af191cfba30a0911b0a4a/coverage-7.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fdb6f54f38e334db97f72fa0c701e66d8479af0bc3f9bfb5b90f1c30f54500f", size = 218606, upload-time = "2025-12-08T13:12:34.498Z" }, + { url = "https://files.pythonhosted.org/packages/68/09/77b1c3a66c2aa91141b6c4471af98e5b1ed9b9e6d17255da5eb7992299e3/coverage-7.13.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7e442c013447d1d8d195be62852270b78b6e255b79b8675bad8479641e21fd96", size = 248999, upload-time = "2025-12-08T13:12:36.02Z" }, + { url = "https://files.pythonhosted.org/packages/0a/32/2e2f96e9d5691eaf1181d9040f850b8b7ce165ea10810fd8e2afa534cef7/coverage-7.13.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ed5630d946859de835a85e9a43b721123a8a44ec26e2830b296d478c7fd4259", size = 250925, upload-time = "2025-12-08T13:12:37.221Z" }, + { url = "https://files.pythonhosted.org/packages/7b/45/b88ddac1d7978859b9a39a8a50ab323186148f1d64bc068f86fc77706321/coverage-7.13.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f15a931a668e58087bc39d05d2b4bf4b14ff2875b49c994bbdb1c2217a8daeb", size = 253032, upload-time = "2025-12-08T13:12:38.763Z" }, + { url = "https://files.pythonhosted.org/packages/71/cb/e15513f94c69d4820a34b6bf3d2b1f9f8755fa6021be97c7065442d7d653/coverage-7.13.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:30a3a201a127ea57f7e14ba43c93c9c4be8b7d17a26e03bb49e6966d019eede9", size = 249134, upload-time = "2025-12-08T13:12:40.382Z" }, + { url = "https://files.pythonhosted.org/packages/09/61/d960ff7dc9e902af3310ce632a875aaa7860f36d2bc8fc8b37ee7c1b82a5/coverage-7.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7a485ff48fbd231efa32d58f479befce52dcb6bfb2a88bb7bf9a0b89b1bc8030", size = 250731, upload-time = "2025-12-08T13:12:41.992Z" }, + { url = "https://files.pythonhosted.org/packages/98/34/c7c72821794afc7c7c2da1db8f00c2c98353078aa7fb6b5ff36aac834b52/coverage-7.13.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:22486cdafba4f9e471c816a2a5745337742a617fef68e890d8baf9f3036d7833", size = 248795, upload-time = "2025-12-08T13:12:43.331Z" }, + { url = "https://files.pythonhosted.org/packages/0a/5b/e0f07107987a43b2def9aa041c614ddb38064cbf294a71ef8c67d43a0cdd/coverage-7.13.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:263c3dbccc78e2e331e59e90115941b5f53e85cfcc6b3b2fbff1fd4e3d2c6ea8", size = 248514, upload-time = "2025-12-08T13:12:44.546Z" }, + { url = "https://files.pythonhosted.org/packages/71/c2/c949c5d3b5e9fc6dd79e1b73cdb86a59ef14f3709b1d72bf7668ae12e000/coverage-7.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e5330fa0cc1f5c3c4c3bb8e101b742025933e7848989370a1d4c8c5e401ea753", size = 249424, upload-time = "2025-12-08T13:12:45.759Z" }, + { url = "https://files.pythonhosted.org/packages/11/f1/bbc009abd6537cec0dffb2cc08c17a7f03de74c970e6302db4342a6e05af/coverage-7.13.0-cp311-cp311-win32.whl", hash = "sha256:0f4872f5d6c54419c94c25dd6ae1d015deeb337d06e448cd890a1e89a8ee7f3b", size = 220597, upload-time = "2025-12-08T13:12:47.378Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f6/d9977f2fb51c10fbaed0718ce3d0a8541185290b981f73b1d27276c12d91/coverage-7.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51a202e0f80f241ccb68e3e26e19ab5b3bf0f813314f2c967642f13ebcf1ddfe", size = 221536, upload-time = "2025-12-08T13:12:48.7Z" }, + { url = "https://files.pythonhosted.org/packages/be/ad/3fcf43fd96fb43e337a3073dea63ff148dcc5c41ba7a14d4c7d34efb2216/coverage-7.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:d2a9d7f1c11487b1c69367ab3ac2d81b9b3721f097aa409a3191c3e90f8f3dd7", size = 220206, upload-time = "2025-12-08T13:12:50.365Z" }, + { url = "https://files.pythonhosted.org/packages/9b/f1/2619559f17f31ba00fc40908efd1fbf1d0a5536eb75dc8341e7d660a08de/coverage-7.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0b3d67d31383c4c68e19a88e28fc4c2e29517580f1b0ebec4a069d502ce1e0bf", size = 218274, upload-time = "2025-12-08T13:12:52.095Z" }, + { url = "https://files.pythonhosted.org/packages/2b/11/30d71ae5d6e949ff93b2a79a2c1b4822e00423116c5c6edfaeef37301396/coverage-7.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:581f086833d24a22c89ae0fe2142cfaa1c92c930adf637ddf122d55083fb5a0f", size = 218638, upload-time = "2025-12-08T13:12:53.418Z" }, + { url = "https://files.pythonhosted.org/packages/79/c2/fce80fc6ded8d77e53207489d6065d0fed75db8951457f9213776615e0f5/coverage-7.13.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0a3a30f0e257df382f5f9534d4ce3d4cf06eafaf5192beb1a7bd066cb10e78fb", size = 250129, upload-time = "2025-12-08T13:12:54.744Z" }, + { url = "https://files.pythonhosted.org/packages/5b/b6/51b5d1eb6fcbb9a1d5d6984e26cbe09018475c2922d554fd724dd0f056ee/coverage-7.13.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:583221913fbc8f53b88c42e8dbb8fca1d0f2e597cb190ce45916662b8b9d9621", size = 252885, upload-time = "2025-12-08T13:12:56.401Z" }, + { url = "https://files.pythonhosted.org/packages/0d/f8/972a5affea41de798691ab15d023d3530f9f56a72e12e243f35031846ff7/coverage-7.13.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f5d9bd30756fff3e7216491a0d6d520c448d5124d3d8e8f56446d6412499e74", size = 253974, upload-time = "2025-12-08T13:12:57.718Z" }, + { url = "https://files.pythonhosted.org/packages/8a/56/116513aee860b2c7968aa3506b0f59b22a959261d1dbf3aea7b4450a7520/coverage-7.13.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a23e5a1f8b982d56fa64f8e442e037f6ce29322f1f9e6c2344cd9e9f4407ee57", size = 250538, upload-time = "2025-12-08T13:12:59.254Z" }, + { url = "https://files.pythonhosted.org/packages/d6/75/074476d64248fbadf16dfafbf93fdcede389ec821f74ca858d7c87d2a98c/coverage-7.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9b01c22bc74a7fb44066aaf765224c0d933ddf1f5047d6cdfe4795504a4493f8", size = 251912, upload-time = "2025-12-08T13:13:00.604Z" }, + { url = "https://files.pythonhosted.org/packages/f2/d2/aa4f8acd1f7c06024705c12609d8698c51b27e4d635d717cd1934c9668e2/coverage-7.13.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:898cce66d0836973f48dda4e3514d863d70142bdf6dfab932b9b6a90ea5b222d", size = 250054, upload-time = "2025-12-08T13:13:01.892Z" }, + { url = "https://files.pythonhosted.org/packages/19/98/8df9e1af6a493b03694a1e8070e024e7d2cdc77adedc225a35e616d505de/coverage-7.13.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:3ab483ea0e251b5790c2aac03acde31bff0c736bf8a86829b89382b407cd1c3b", size = 249619, upload-time = "2025-12-08T13:13:03.236Z" }, + { url = "https://files.pythonhosted.org/packages/d8/71/f8679231f3353018ca66ef647fa6fe7b77e6bff7845be54ab84f86233363/coverage-7.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d84e91521c5e4cb6602fe11ece3e1de03b2760e14ae4fcf1a4b56fa3c801fcd", size = 251496, upload-time = "2025-12-08T13:13:04.511Z" }, + { url = "https://files.pythonhosted.org/packages/04/86/9cb406388034eaf3c606c22094edbbb82eea1fa9d20c0e9efadff20d0733/coverage-7.13.0-cp312-cp312-win32.whl", hash = "sha256:193c3887285eec1dbdb3f2bd7fbc351d570ca9c02ca756c3afbc71b3c98af6ef", size = 220808, upload-time = "2025-12-08T13:13:06.422Z" }, + { url = "https://files.pythonhosted.org/packages/1c/59/af483673df6455795daf5f447c2f81a3d2fcfc893a22b8ace983791f6f34/coverage-7.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:4f3e223b2b2db5e0db0c2b97286aba0036ca000f06aca9b12112eaa9af3d92ae", size = 221616, upload-time = "2025-12-08T13:13:07.95Z" }, + { url = "https://files.pythonhosted.org/packages/64/b0/959d582572b30a6830398c60dd419c1965ca4b5fb38ac6b7093a0d50ca8d/coverage-7.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:086cede306d96202e15a4b77ace8472e39d9f4e5f9fd92dd4fecdfb2313b2080", size = 220261, upload-time = "2025-12-08T13:13:09.581Z" }, + { url = "https://files.pythonhosted.org/packages/7c/cc/bce226595eb3bf7d13ccffe154c3c487a22222d87ff018525ab4dd2e9542/coverage-7.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:28ee1c96109974af104028a8ef57cec21447d42d0e937c0275329272e370ebcf", size = 218297, upload-time = "2025-12-08T13:13:10.977Z" }, + { url = "https://files.pythonhosted.org/packages/3b/9f/73c4d34600aae03447dff3d7ad1d0ac649856bfb87d1ca7d681cfc913f9e/coverage-7.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d1e97353dcc5587b85986cda4ff3ec98081d7e84dd95e8b2a6d59820f0545f8a", size = 218673, upload-time = "2025-12-08T13:13:12.562Z" }, + { url = "https://files.pythonhosted.org/packages/63/ab/8fa097db361a1e8586535ae5073559e6229596b3489ec3ef2f5b38df8cb2/coverage-7.13.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:99acd4dfdfeb58e1937629eb1ab6ab0899b131f183ee5f23e0b5da5cba2fec74", size = 249652, upload-time = "2025-12-08T13:13:13.909Z" }, + { url = "https://files.pythonhosted.org/packages/90/3a/9bfd4de2ff191feb37ef9465855ca56a6f2f30a3bca172e474130731ac3d/coverage-7.13.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ff45e0cd8451e293b63ced93161e189780baf444119391b3e7d25315060368a6", size = 252251, upload-time = "2025-12-08T13:13:15.553Z" }, + { url = "https://files.pythonhosted.org/packages/df/61/b5d8105f016e1b5874af0d7c67542da780ccd4a5f2244a433d3e20ceb1ad/coverage-7.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f4f72a85316d8e13234cafe0a9f81b40418ad7a082792fa4165bd7d45d96066b", size = 253492, upload-time = "2025-12-08T13:13:16.849Z" }, + { url = "https://files.pythonhosted.org/packages/f3/b8/0fad449981803cc47a4694768b99823fb23632150743f9c83af329bb6090/coverage-7.13.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:11c21557d0e0a5a38632cbbaca5f008723b26a89d70db6315523df6df77d6232", size = 249850, upload-time = "2025-12-08T13:13:18.142Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e9/8d68337c3125014d918cf4327d5257553a710a2995a6a6de2ac77e5aa429/coverage-7.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76541dc8d53715fb4f7a3a06b34b0dc6846e3c69bc6204c55653a85dd6220971", size = 251633, upload-time = "2025-12-08T13:13:19.56Z" }, + { url = "https://files.pythonhosted.org/packages/55/14/d4112ab26b3a1bc4b3c1295d8452dcf399ed25be4cf649002fb3e64b2d93/coverage-7.13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6e9e451dee940a86789134b6b0ffbe31c454ade3b849bb8a9d2cca2541a8e91d", size = 249586, upload-time = "2025-12-08T13:13:20.883Z" }, + { url = "https://files.pythonhosted.org/packages/2c/a9/22b0000186db663b0d82f86c2f1028099ae9ac202491685051e2a11a5218/coverage-7.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:5c67dace46f361125e6b9cace8fe0b729ed8479f47e70c89b838d319375c8137", size = 249412, upload-time = "2025-12-08T13:13:22.22Z" }, + { url = "https://files.pythonhosted.org/packages/a1/2e/42d8e0d9e7527fba439acdc6ed24a2b97613b1dc85849b1dd935c2cffef0/coverage-7.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f59883c643cb19630500f57016f76cfdcd6845ca8c5b5ea1f6e17f74c8e5f511", size = 251191, upload-time = "2025-12-08T13:13:23.899Z" }, + { url = "https://files.pythonhosted.org/packages/a4/af/8c7af92b1377fd8860536aadd58745119252aaaa71a5213e5a8e8007a9f5/coverage-7.13.0-cp313-cp313-win32.whl", hash = "sha256:58632b187be6f0be500f553be41e277712baa278147ecb7559983c6d9faf7ae1", size = 220829, upload-time = "2025-12-08T13:13:25.182Z" }, + { url = "https://files.pythonhosted.org/packages/58/f9/725e8bf16f343d33cbe076c75dc8370262e194ff10072c0608b8e5cf33a3/coverage-7.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:73419b89f812f498aca53f757dd834919b48ce4799f9d5cad33ca0ae442bdb1a", size = 221640, upload-time = "2025-12-08T13:13:26.836Z" }, + { url = "https://files.pythonhosted.org/packages/8a/ff/e98311000aa6933cc79274e2b6b94a2fe0fe3434fca778eba82003675496/coverage-7.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:eb76670874fdd6091eedcc856128ee48c41a9bbbb9c3f1c7c3cf169290e3ffd6", size = 220269, upload-time = "2025-12-08T13:13:28.116Z" }, + { url = "https://files.pythonhosted.org/packages/cf/cf/bbaa2e1275b300343ea865f7d424cc0a2e2a1df6925a070b2b2d5d765330/coverage-7.13.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6e63ccc6e0ad8986386461c3c4b737540f20426e7ec932f42e030320896c311a", size = 218990, upload-time = "2025-12-08T13:13:29.463Z" }, + { url = "https://files.pythonhosted.org/packages/21/1d/82f0b3323b3d149d7672e7744c116e9c170f4957e0c42572f0366dbb4477/coverage-7.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:494f5459ffa1bd45e18558cd98710c36c0b8fbfa82a5eabcbe671d80ecffbfe8", size = 219340, upload-time = "2025-12-08T13:13:31.524Z" }, + { url = "https://files.pythonhosted.org/packages/fb/e3/fe3fd4702a3832a255f4d43013eacb0ef5fc155a5960ea9269d8696db28b/coverage-7.13.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:06cac81bf10f74034e055e903f5f946e3e26fc51c09fc9f584e4a1605d977053", size = 260638, upload-time = "2025-12-08T13:13:32.965Z" }, + { url = "https://files.pythonhosted.org/packages/ad/01/63186cb000307f2b4da463f72af9b85d380236965574c78e7e27680a2593/coverage-7.13.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f2ffc92b46ed6e6760f1d47a71e56b5664781bc68986dbd1836b2b70c0ce2071", size = 262705, upload-time = "2025-12-08T13:13:34.378Z" }, + { url = "https://files.pythonhosted.org/packages/7c/a1/c0dacef0cc865f2455d59eed3548573ce47ed603205ffd0735d1d78b5906/coverage-7.13.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0602f701057c6823e5db1b74530ce85f17c3c5be5c85fc042ac939cbd909426e", size = 265125, upload-time = "2025-12-08T13:13:35.73Z" }, + { url = "https://files.pythonhosted.org/packages/ef/92/82b99223628b61300bd382c205795533bed021505eab6dd86e11fb5d7925/coverage-7.13.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:25dc33618d45456ccb1d37bce44bc78cf269909aa14c4db2e03d63146a8a1493", size = 259844, upload-time = "2025-12-08T13:13:37.69Z" }, + { url = "https://files.pythonhosted.org/packages/cf/2c/89b0291ae4e6cd59ef042708e1c438e2290f8c31959a20055d8768349ee2/coverage-7.13.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:71936a8b3b977ddd0b694c28c6a34f4fff2e9dd201969a4ff5d5fc7742d614b0", size = 262700, upload-time = "2025-12-08T13:13:39.525Z" }, + { url = "https://files.pythonhosted.org/packages/bf/f9/a5f992efae1996245e796bae34ceb942b05db275e4b34222a9a40b9fbd3b/coverage-7.13.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:936bc20503ce24770c71938d1369461f0c5320830800933bc3956e2a4ded930e", size = 260321, upload-time = "2025-12-08T13:13:41.172Z" }, + { url = "https://files.pythonhosted.org/packages/4c/89/a29f5d98c64fedbe32e2ac3c227fbf78edc01cc7572eee17d61024d89889/coverage-7.13.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:af0a583efaacc52ae2521f8d7910aff65cdb093091d76291ac5820d5e947fc1c", size = 259222, upload-time = "2025-12-08T13:13:43.282Z" }, + { url = "https://files.pythonhosted.org/packages/b3/c3/940fe447aae302a6701ee51e53af7e08b86ff6eed7631e5740c157ee22b9/coverage-7.13.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f1c23e24a7000da892a312fb17e33c5f94f8b001de44b7cf8ba2e36fbd15859e", size = 261411, upload-time = "2025-12-08T13:13:44.72Z" }, + { url = "https://files.pythonhosted.org/packages/eb/31/12a4aec689cb942a89129587860ed4d0fd522d5fda81237147fde554b8ae/coverage-7.13.0-cp313-cp313t-win32.whl", hash = "sha256:5f8a0297355e652001015e93be345ee54393e45dc3050af4a0475c5a2b767d46", size = 221505, upload-time = "2025-12-08T13:13:46.332Z" }, + { url = "https://files.pythonhosted.org/packages/65/8c/3b5fe3259d863572d2b0827642c50c3855d26b3aefe80bdc9eba1f0af3b0/coverage-7.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6abb3a4c52f05e08460bd9acf04fec027f8718ecaa0d09c40ffbc3fbd70ecc39", size = 222569, upload-time = "2025-12-08T13:13:47.79Z" }, + { url = "https://files.pythonhosted.org/packages/b0/39/f71fa8316a96ac72fc3908839df651e8eccee650001a17f2c78cdb355624/coverage-7.13.0-cp313-cp313t-win_arm64.whl", hash = "sha256:3ad968d1e3aa6ce5be295ab5fe3ae1bf5bb4769d0f98a80a0252d543a2ef2e9e", size = 220841, upload-time = "2025-12-08T13:13:49.243Z" }, + { url = "https://files.pythonhosted.org/packages/f8/4b/9b54bedda55421449811dcd5263a2798a63f48896c24dfb92b0f1b0845bd/coverage-7.13.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:453b7ec753cf5e4356e14fe858064e5520c460d3bbbcb9c35e55c0d21155c256", size = 218343, upload-time = "2025-12-08T13:13:50.811Z" }, + { url = "https://files.pythonhosted.org/packages/59/df/c3a1f34d4bba2e592c8979f924da4d3d4598b0df2392fbddb7761258e3dc/coverage-7.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:af827b7cbb303e1befa6c4f94fd2bf72f108089cfa0f8abab8f4ca553cf5ca5a", size = 218672, upload-time = "2025-12-08T13:13:52.284Z" }, + { url = "https://files.pythonhosted.org/packages/07/62/eec0659e47857698645ff4e6ad02e30186eb8afd65214fd43f02a76537cb/coverage-7.13.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9987a9e4f8197a1000280f7cc089e3ea2c8b3c0a64d750537809879a7b4ceaf9", size = 249715, upload-time = "2025-12-08T13:13:53.791Z" }, + { url = "https://files.pythonhosted.org/packages/23/2d/3c7ff8b2e0e634c1f58d095f071f52ed3c23ff25be524b0ccae8b71f99f8/coverage-7.13.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3188936845cd0cb114fa6a51842a304cdbac2958145d03be2377ec41eb285d19", size = 252225, upload-time = "2025-12-08T13:13:55.274Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ac/fb03b469d20e9c9a81093575003f959cf91a4a517b783aab090e4538764b/coverage-7.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2bdb3babb74079f021696cb46b8bb5f5661165c385d3a238712b031a12355be", size = 253559, upload-time = "2025-12-08T13:13:57.161Z" }, + { url = "https://files.pythonhosted.org/packages/29/62/14afa9e792383c66cc0a3b872a06ded6e4ed1079c7d35de274f11d27064e/coverage-7.13.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7464663eaca6adba4175f6c19354feea61ebbdd735563a03d1e472c7072d27bb", size = 249724, upload-time = "2025-12-08T13:13:58.692Z" }, + { url = "https://files.pythonhosted.org/packages/31/b7/333f3dab2939070613696ab3ee91738950f0467778c6e5a5052e840646b7/coverage-7.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8069e831f205d2ff1f3d355e82f511eb7c5522d7d413f5db5756b772ec8697f8", size = 251582, upload-time = "2025-12-08T13:14:00.642Z" }, + { url = "https://files.pythonhosted.org/packages/81/cb/69162bda9381f39b2287265d7e29ee770f7c27c19f470164350a38318764/coverage-7.13.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6fb2d5d272341565f08e962cce14cdf843a08ac43bd621783527adb06b089c4b", size = 249538, upload-time = "2025-12-08T13:14:02.556Z" }, + { url = "https://files.pythonhosted.org/packages/e0/76/350387b56a30f4970abe32b90b2a434f87d29f8b7d4ae40d2e8a85aacfb3/coverage-7.13.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5e70f92ef89bac1ac8a99b3324923b4749f008fdbd7aa9cb35e01d7a284a04f9", size = 249349, upload-time = "2025-12-08T13:14:04.015Z" }, + { url = "https://files.pythonhosted.org/packages/86/0d/7f6c42b8d59f4c7e43ea3059f573c0dcfed98ba46eb43c68c69e52ae095c/coverage-7.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4b5de7d4583e60d5fd246dd57fcd3a8aa23c6e118a8c72b38adf666ba8e7e927", size = 251011, upload-time = "2025-12-08T13:14:05.505Z" }, + { url = "https://files.pythonhosted.org/packages/d7/f1/4bb2dff379721bb0b5c649d5c5eaf438462cad824acf32eb1b7ca0c7078e/coverage-7.13.0-cp314-cp314-win32.whl", hash = "sha256:a6c6e16b663be828a8f0b6c5027d36471d4a9f90d28444aa4ced4d48d7d6ae8f", size = 221091, upload-time = "2025-12-08T13:14:07.127Z" }, + { url = "https://files.pythonhosted.org/packages/ba/44/c239da52f373ce379c194b0ee3bcc121020e397242b85f99e0afc8615066/coverage-7.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:0900872f2fdb3ee5646b557918d02279dc3af3dfb39029ac4e945458b13f73bc", size = 221904, upload-time = "2025-12-08T13:14:08.542Z" }, + { url = "https://files.pythonhosted.org/packages/89/1f/b9f04016d2a29c2e4a0307baefefad1a4ec5724946a2b3e482690486cade/coverage-7.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:3a10260e6a152e5f03f26db4a407c4c62d3830b9af9b7c0450b183615f05d43b", size = 220480, upload-time = "2025-12-08T13:14:10.958Z" }, + { url = "https://files.pythonhosted.org/packages/16/d4/364a1439766c8e8647860584171c36010ca3226e6e45b1753b1b249c5161/coverage-7.13.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9097818b6cc1cfb5f174e3263eba4a62a17683bcfe5c4b5d07f4c97fa51fbf28", size = 219074, upload-time = "2025-12-08T13:14:13.345Z" }, + { url = "https://files.pythonhosted.org/packages/ce/f4/71ba8be63351e099911051b2089662c03d5671437a0ec2171823c8e03bec/coverage-7.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0018f73dfb4301a89292c73be6ba5f58722ff79f51593352759c1790ded1cabe", size = 219342, upload-time = "2025-12-08T13:14:15.02Z" }, + { url = "https://files.pythonhosted.org/packages/5e/25/127d8ed03d7711a387d96f132589057213e3aef7475afdaa303412463f22/coverage-7.13.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:166ad2a22ee770f5656e1257703139d3533b4a0b6909af67c6b4a3adc1c98657", size = 260713, upload-time = "2025-12-08T13:14:16.907Z" }, + { url = "https://files.pythonhosted.org/packages/fd/db/559fbb6def07d25b2243663b46ba9eb5a3c6586c0c6f4e62980a68f0ee1c/coverage-7.13.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f6aaef16d65d1787280943f1c8718dc32e9cf141014e4634d64446702d26e0ff", size = 262825, upload-time = "2025-12-08T13:14:18.68Z" }, + { url = "https://files.pythonhosted.org/packages/37/99/6ee5bf7eff884766edb43bd8736b5e1c5144d0fe47498c3779326fe75a35/coverage-7.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e999e2dcc094002d6e2c7bbc1fb85b58ba4f465a760a8014d97619330cdbbbf3", size = 265233, upload-time = "2025-12-08T13:14:20.55Z" }, + { url = "https://files.pythonhosted.org/packages/d8/90/92f18fe0356ea69e1f98f688ed80cec39f44e9f09a1f26a1bbf017cc67f2/coverage-7.13.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:00c3d22cf6fb1cf3bf662aaaa4e563be8243a5ed2630339069799835a9cc7f9b", size = 259779, upload-time = "2025-12-08T13:14:22.367Z" }, + { url = "https://files.pythonhosted.org/packages/90/5d/b312a8b45b37a42ea7d27d7d3ff98ade3a6c892dd48d1d503e773503373f/coverage-7.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22ccfe8d9bb0d6134892cbe1262493a8c70d736b9df930f3f3afae0fe3ac924d", size = 262700, upload-time = "2025-12-08T13:14:24.309Z" }, + { url = "https://files.pythonhosted.org/packages/63/f8/b1d0de5c39351eb71c366f872376d09386640840a2e09b0d03973d791e20/coverage-7.13.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:9372dff5ea15930fea0445eaf37bbbafbc771a49e70c0aeed8b4e2c2614cc00e", size = 260302, upload-time = "2025-12-08T13:14:26.068Z" }, + { url = "https://files.pythonhosted.org/packages/aa/7c/d42f4435bc40c55558b3109a39e2d456cddcec37434f62a1f1230991667a/coverage-7.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:69ac2c492918c2461bc6ace42d0479638e60719f2a4ef3f0815fa2df88e9f940", size = 259136, upload-time = "2025-12-08T13:14:27.604Z" }, + { url = "https://files.pythonhosted.org/packages/b8/d3/23413241dc04d47cfe19b9a65b32a2edd67ecd0b817400c2843ebc58c847/coverage-7.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:739c6c051a7540608d097b8e13c76cfa85263ced467168dc6b477bae3df7d0e2", size = 261467, upload-time = "2025-12-08T13:14:29.09Z" }, + { url = "https://files.pythonhosted.org/packages/13/e6/6e063174500eee216b96272c0d1847bf215926786f85c2bd024cf4d02d2f/coverage-7.13.0-cp314-cp314t-win32.whl", hash = "sha256:fe81055d8c6c9de76d60c94ddea73c290b416e061d40d542b24a5871bad498b7", size = 221875, upload-time = "2025-12-08T13:14:31.106Z" }, + { url = "https://files.pythonhosted.org/packages/3b/46/f4fb293e4cbe3620e3ac2a3e8fd566ed33affb5861a9b20e3dd6c1896cbc/coverage-7.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:445badb539005283825959ac9fa4a28f712c214b65af3a2c464f1adc90f5fcbc", size = 222982, upload-time = "2025-12-08T13:14:33.1Z" }, + { url = "https://files.pythonhosted.org/packages/68/62/5b3b9018215ed9733fbd1ae3b2ed75c5de62c3b55377a52cae732e1b7805/coverage-7.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:de7f6748b890708578fc4b7bb967d810aeb6fcc9bff4bb77dbca77dab2f9df6a", size = 221016, upload-time = "2025-12-08T13:14:34.601Z" }, + { url = "https://files.pythonhosted.org/packages/8d/4c/1968f32fb9a2604645827e11ff84a31e59d532e01995f904723b4f5328b3/coverage-7.13.0-py3-none-any.whl", hash = "sha256:850d2998f380b1e266459ca5b47bc9e7daf9af1d070f66317972f382d46f1904", size = 210068, upload-time = "2025-12-08T13:14:36.236Z" }, ] [package.optional-dependencies] @@ -1107,7 +1107,7 @@ wheels = [ [[package]] name = "jupyter-client" -version = "8.6.3" +version = "8.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-core" }, @@ -1116,9 +1116,9 @@ dependencies = [ { name = "tornado" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/22/bf9f12fdaeae18019a468b68952a60fe6dbab5d67cd2a103cac7659b41ca/jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419", size = 342019, upload-time = "2024-09-17T10:44:17.613Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/27/d10de45e8ad4ce872372c4a3a37b7b35b6b064f6f023a5c14ffcced4d59d/jupyter_client-8.7.0.tar.gz", hash = "sha256:3357212d9cbe01209e59190f67a3a7e1f387a4f4e88d1e0433ad84d7b262531d", size = 344691, upload-time = "2025-12-09T18:37:01.953Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/11/85/b0394e0b6fcccd2c1eeefc230978a6f8cb0c5df1e4cd3e7625735a0d7d1e/jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f", size = 106105, upload-time = "2024-09-17T10:44:15.218Z" }, + { url = "https://files.pythonhosted.org/packages/bb/f5/fddaec430367be9d62a7ed125530e133bfd4a1c0350fe221149ee0f2b526/jupyter_client-8.7.0-py3-none-any.whl", hash = "sha256:3671a94fd25e62f5f2f554f5e95389c2294d89822378a5f2dd24353e1494a9e0", size = 106215, upload-time = "2025-12-09T18:37:00.024Z" }, ] [[package]] @@ -1498,7 +1498,6 @@ qiskit = [ [package.dev-dependencies] build = [ { name = "nanobind" }, - { name = "pybind11" }, { name = "scikit-build-core" }, { name = "setuptools-scm" }, ] @@ -1507,7 +1506,6 @@ dev = [ { name = "nanobind" }, { name = "nox" }, { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, - { name = "pybind11" }, { name = "pytest" }, { name = "pytest-console-scripts" }, { name = "pytest-cov" }, @@ -1553,7 +1551,6 @@ provides-extras = ["qiskit"] [package.metadata.requires-dev] build = [ { name = "nanobind", specifier = ">=2.10.1" }, - { name = "pybind11", specifier = ">=3.0.1" }, { name = "scikit-build-core", specifier = ">=0.11.6" }, { name = "setuptools-scm", specifier = ">=9.2.2" }, ] @@ -1563,7 +1560,6 @@ dev = [ { name = "nox", specifier = ">=2025.11.12" }, { name = "numpy", marker = "python_full_version >= '3.13'", specifier = ">=2.1" }, { name = "numpy", marker = "python_full_version >= '3.14'", specifier = ">=2.3.2" }, - { name = "pybind11", specifier = ">=3.0.1" }, { name = "pytest", specifier = ">=9.0.1" }, { name = "pytest-console-scripts", specifier = ">=1.4.1" }, { name = "pytest-cov", specifier = ">=7.0.0" }, @@ -2162,15 +2158,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, ] -[[package]] -name = "pybind11" -version = "3.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/7b/a6d8dcb83c457e24a9df1e4d8fd5fb8034d4bbc62f3c324681e8a9ba57c2/pybind11-3.0.1.tar.gz", hash = "sha256:9c0f40056a016da59bab516efb523089139fcc6f2ba7e4930854c61efb932051", size = 546914, upload-time = "2025-08-22T20:09:27.265Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/8a/37362fc2b949d5f733a8b0f2ff51ba423914cabefe69f1d1b6aab710f5fe/pybind11-3.0.1-py3-none-any.whl", hash = "sha256:aa8f0aa6e0a94d3b64adfc38f560f33f15e589be2175e103c0a33c6bce55ee89", size = 293611, upload-time = "2025-08-22T20:09:25.235Z" }, -] - [[package]] name = "pybtex" version = "0.25.1" @@ -3357,11 +3344,11 @@ wheels = [ [[package]] name = "urllib3" -version = "2.6.0" +version = "2.6.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1c/43/554c2569b62f49350597348fc3ac70f786e3c32e7f19d266e19817812dd3/urllib3-2.6.0.tar.gz", hash = "sha256:cb9bcef5a4b345d5da5d145dc3e30834f58e8018828cbc724d30b4cb7d4d49f1", size = 432585, upload-time = "2025-12-05T15:08:47.885Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/1d/0f3a93cca1ac5e8287842ed4eebbd0f7a991315089b1a0b01c7788aa7b63/urllib3-2.6.1.tar.gz", hash = "sha256:5379eb6e1aba4088bae84f8242960017ec8d8e3decf30480b3a1abdaa9671a3f", size = 432678, upload-time = "2025-12-08T15:25:26.773Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/1a/9ffe814d317c5224166b23e7c47f606d6e473712a2fad0f704ea9b99f246/urllib3-2.6.0-py3-none-any.whl", hash = "sha256:c90f7a39f716c572c4e3e58509581ebd83f9b59cced005b7db7ad2d22b0db99f", size = 131083, upload-time = "2025-12-05T15:08:45.983Z" }, + { url = "https://files.pythonhosted.org/packages/bc/56/190ceb8cb10511b730b564fb1e0293fa468363dbad26145c34928a60cb0c/urllib3-2.6.1-py3-none-any.whl", hash = "sha256:e67d06fe947c36a7ca39f4994b08d73922d40e6cca949907be05efa6fd75110b", size = 131138, upload-time = "2025-12-08T15:25:25.51Z" }, ] [[package]] From 05e1ee899f71fa05831f3107a8ac6cd233a95711 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Wed, 10 Dec 2025 10:34:42 +0100 Subject: [PATCH 008/144] Do not lint nanobind/stl includes --- bindings/dd/register_dd.cpp | 4 ++-- bindings/dd/register_dd_package.cpp | 4 ++-- bindings/dd/register_matrix_dds.cpp | 6 +++--- bindings/dd/register_vector_dds.cpp | 6 +++--- bindings/fomac/fomac.cpp | 12 ++++++------ .../ir/operations/register_compound_operation.cpp | 4 ++-- bindings/ir/operations/register_control.cpp | 4 ++-- .../ir/operations/register_if_else_operation.cpp | 4 ++-- .../ir/operations/register_non_unitary_operation.cpp | 4 ++-- bindings/ir/operations/register_operation.cpp | 6 +++--- .../ir/operations/register_standard_operation.cpp | 6 +++--- .../ir/operations/register_symbolic_operation.cpp | 8 ++++---- bindings/ir/register_permutation.cpp | 2 +- bindings/ir/register_quantum_computation.cpp | 10 +++++----- bindings/ir/register_registers.cpp | 2 +- bindings/ir/symbolic/register_expression.cpp | 4 ++-- bindings/ir/symbolic/register_term.cpp | 2 +- bindings/ir/symbolic/register_variable.cpp | 2 +- bindings/na/fomac/fomac.cpp | 4 ++-- 19 files changed, 47 insertions(+), 47 deletions(-) diff --git a/bindings/dd/register_dd.cpp b/bindings/dd/register_dd.cpp index 2a80a3e15e..91de56498d 100644 --- a/bindings/dd/register_dd.cpp +++ b/bindings/dd/register_dd.cpp @@ -21,8 +21,8 @@ #include #include #include -#include -#include +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include namespace mqt { diff --git a/bindings/dd/register_dd_package.cpp b/bindings/dd/register_dd_package.cpp index b188b58ab7..acf68debcd 100644 --- a/bindings/dd/register_dd_package.cpp +++ b/bindings/dd/register_dd_package.cpp @@ -27,8 +27,8 @@ #include #include #include -#include -#include +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include #include #include diff --git a/bindings/dd/register_matrix_dds.cpp b/bindings/dd/register_matrix_dds.cpp index c7910c3d26..edebfc782d 100644 --- a/bindings/dd/register_matrix_dds.cpp +++ b/bindings/dd/register_matrix_dds.cpp @@ -18,9 +18,9 @@ #include #include #include -#include -#include -#include +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include #include #include diff --git a/bindings/dd/register_vector_dds.cpp b/bindings/dd/register_vector_dds.cpp index d7715c38c8..434ff15a72 100644 --- a/bindings/dd/register_vector_dds.cpp +++ b/bindings/dd/register_vector_dds.cpp @@ -18,9 +18,9 @@ #include #include #include -#include -#include -#include +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include #include diff --git a/bindings/fomac/fomac.cpp b/bindings/fomac/fomac.cpp index 812de26557..5b8549e0dd 100644 --- a/bindings/fomac/fomac.cpp +++ b/bindings/fomac/fomac.cpp @@ -14,12 +14,12 @@ #include #include -#include -#include -#include -#include -#include -#include +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include #include #include diff --git a/bindings/ir/operations/register_compound_operation.cpp b/bindings/ir/operations/register_compound_operation.cpp index 622b25cbe4..12e56a112d 100644 --- a/bindings/ir/operations/register_compound_operation.cpp +++ b/bindings/ir/operations/register_compound_operation.cpp @@ -15,8 +15,8 @@ #include #include #include -#include -#include +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include #include #include diff --git a/bindings/ir/operations/register_control.cpp b/bindings/ir/operations/register_control.cpp index dfd2bc1dec..727c75990f 100644 --- a/bindings/ir/operations/register_control.cpp +++ b/bindings/ir/operations/register_control.cpp @@ -13,8 +13,8 @@ #include #include -#include -#include +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) namespace mqt { diff --git a/bindings/ir/operations/register_if_else_operation.cpp b/bindings/ir/operations/register_if_else_operation.cpp index 020eae5df8..783b252ff3 100644 --- a/bindings/ir/operations/register_if_else_operation.cpp +++ b/bindings/ir/operations/register_if_else_operation.cpp @@ -16,8 +16,8 @@ #include #include #include -#include -#include +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include #include diff --git a/bindings/ir/operations/register_non_unitary_operation.cpp b/bindings/ir/operations/register_non_unitary_operation.cpp index d48c915cac..a429365eb2 100644 --- a/bindings/ir/operations/register_non_unitary_operation.cpp +++ b/bindings/ir/operations/register_non_unitary_operation.cpp @@ -14,8 +14,8 @@ #include "ir/operations/Operation.hpp" #include -#include -#include +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include #include diff --git a/bindings/ir/operations/register_operation.cpp b/bindings/ir/operations/register_operation.cpp index 0f371b1ff4..13280cd4ec 100644 --- a/bindings/ir/operations/register_operation.cpp +++ b/bindings/ir/operations/register_operation.cpp @@ -12,9 +12,9 @@ #include "ir/operations/Operation.hpp" #include -#include -#include -#include +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include namespace mqt { diff --git a/bindings/ir/operations/register_standard_operation.cpp b/bindings/ir/operations/register_standard_operation.cpp index 537be99e80..bcb0a8b7dc 100644 --- a/bindings/ir/operations/register_standard_operation.cpp +++ b/bindings/ir/operations/register_standard_operation.cpp @@ -15,9 +15,9 @@ #include "ir/operations/StandardOperation.hpp" #include -#include -#include -#include +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include #include diff --git a/bindings/ir/operations/register_symbolic_operation.cpp b/bindings/ir/operations/register_symbolic_operation.cpp index 84d56103c3..ff2bfd9b3a 100644 --- a/bindings/ir/operations/register_symbolic_operation.cpp +++ b/bindings/ir/operations/register_symbolic_operation.cpp @@ -16,10 +16,10 @@ #include "ir/operations/SymbolicOperation.hpp" #include -#include -#include -#include -#include +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include namespace mqt { diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index cc7bce5130..8a4a085f9c 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include // NOLINT(misc-include-cleaner) #include namespace mqt { diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index 0d77d9b543..ec56833def 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -21,11 +21,11 @@ #include #include #include -#include -#include -#include -#include -#include +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include #include #include diff --git a/bindings/ir/register_registers.cpp b/bindings/ir/register_registers.cpp index b34086086d..e166b5433d 100644 --- a/bindings/ir/register_registers.cpp +++ b/bindings/ir/register_registers.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include // NOLINT(misc-include-cleaner) #include namespace mqt { diff --git a/bindings/ir/symbolic/register_expression.cpp b/bindings/ir/symbolic/register_expression.cpp index 53e611a4be..9140470fd3 100644 --- a/bindings/ir/symbolic/register_expression.cpp +++ b/bindings/ir/symbolic/register_expression.cpp @@ -14,8 +14,8 @@ #include #include #include -#include -#include +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include #include diff --git a/bindings/ir/symbolic/register_term.cpp b/bindings/ir/symbolic/register_term.cpp index 54fb6f5d9d..13a00bfed0 100644 --- a/bindings/ir/symbolic/register_term.cpp +++ b/bindings/ir/symbolic/register_term.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include // NOLINT(misc-include-cleaner) #include namespace mqt { diff --git a/bindings/ir/symbolic/register_variable.cpp b/bindings/ir/symbolic/register_variable.cpp index a7e8e2ec3f..22eafad9d9 100644 --- a/bindings/ir/symbolic/register_variable.cpp +++ b/bindings/ir/symbolic/register_variable.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include // NOLINT(misc-include-cleaner) #include namespace mqt { diff --git a/bindings/na/fomac/fomac.cpp b/bindings/na/fomac/fomac.cpp index dd16e12cbe..72d3eca052 100644 --- a/bindings/na/fomac/fomac.cpp +++ b/bindings/na/fomac/fomac.cpp @@ -15,8 +15,8 @@ #include #include -#include -#include +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include namespace mqt { From d7e77b4f970fb25a04adf27fdcbed39df9cb0493 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Wed, 10 Dec 2025 10:36:51 +0100 Subject: [PATCH 009/144] Do not lint comparison pairs --- bindings/fomac/fomac.cpp | 16 ++++++++-------- bindings/ir/operations/register_control.cpp | 4 ++-- bindings/ir/register_permutation.cpp | 4 ++-- bindings/ir/register_registers.cpp | 4 ++-- bindings/ir/symbolic/register_expression.cpp | 4 ++-- bindings/ir/symbolic/register_term.cpp | 4 ++-- bindings/ir/symbolic/register_variable.cpp | 4 ++-- bindings/na/fomac/fomac.cpp | 20 ++++++++++---------- 8 files changed, 30 insertions(+), 30 deletions(-) diff --git a/bindings/fomac/fomac.cpp b/bindings/fomac/fomac.cpp index 5b8549e0dd..0dd704d631 100644 --- a/bindings/fomac/fomac.cpp +++ b/bindings/fomac/fomac.cpp @@ -74,8 +74,8 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { job.def_prop_ro("program_format", &fomac::Session::Job::getProgramFormat); job.def_prop_ro("program", &fomac::Session::Job::getProgram); job.def_prop_ro("num_shots", &fomac::Session::Job::getNumShots); - job.def(nb::self == nb::self); - job.def(nb::self != nb::self); + job.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) + job.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) // JobStatus enum nb::enum_(job, "Status", "enum.Enum", @@ -144,8 +144,8 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { device.def("__repr__", [](const fomac::Session::Device& dev) { return ""; }); - device.def(nb::self == nb::self); - device.def(nb::self != nb::self); + device.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) + device.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) // Site class auto site = nb::class_(device, "Site"); @@ -165,8 +165,8 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { site.def("__repr__", [](const fomac::Session::Device::Site& s) { return ""; }); - site.def(nb::self == nb::self); - site.def(nb::self != nb::self); + site.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) + site.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) // Operation class auto operation = @@ -209,8 +209,8 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { operation.def("__repr__", [](const fomac::Session::Device::Operation& op) { return ""; }); - operation.def(nb::self == nb::self); - operation.def(nb::self != nb::self); + operation.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) + operation.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) #ifndef _WIN32 // Module-level function to add dynamic device libraries on non-Windows diff --git a/bindings/ir/operations/register_control.cpp b/bindings/ir/operations/register_control.cpp index 727c75990f..50e960fd58 100644 --- a/bindings/ir/operations/register_control.cpp +++ b/bindings/ir/operations/register_control.cpp @@ -36,8 +36,8 @@ void registerControl(const nb::module_& m) { control.def_rw("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(nb::self == nb::self); - control.def(nb::self != nb::self); + control.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) + control.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) control.def(nb::hash(nb::self)); nb::implicitly_convertible(); } diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index 8a4a085f9c..7dd102fe6a 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -66,8 +66,8 @@ void registerPermutation(nb::module_& m) { "item_iterator", p.begin(), p.end()); }, nb::keep_alive<0, 1>()) - .def(nb::self == nb::self) - .def(nb::self != nb::self) + .def(nb::self == nb::self) // NOLINT(misc-redundant-expression) + .def(nb::self != nb::self) // NOLINT(misc-redundant-expression) .def(nb::hash(nb::self)) .def("__str__", [](const qc::Permutation& p) { diff --git a/bindings/ir/register_registers.cpp b/bindings/ir/register_registers.cpp index e166b5433d..fe470d95f2 100644 --- a/bindings/ir/register_registers.cpp +++ b/bindings/ir/register_registers.cpp @@ -75,8 +75,8 @@ void registerRegisters(nb::module_& m) { .def_prop_ro( "end", [](const qc::ClassicalRegister& reg) { return reg.getEndIndex(); }) - .def(nb::self == nb::self) - .def(nb::self != nb::self) + .def(nb::self == nb::self) // NOLINT(misc-redundant-expression) + .def(nb::self != nb::self) // NOLINT(misc-redundant-expression) .def(nb::hash(nb::self)) .def("__getitem__", &qc::ClassicalRegister::getGlobalIndex, "key"_a) .def("__contains__", &qc::ClassicalRegister::contains) diff --git a/bindings/ir/symbolic/register_expression.cpp b/bindings/ir/symbolic/register_expression.cpp index 9140470fd3..aef8c65f81 100644 --- a/bindings/ir/symbolic/register_expression.cpp +++ b/bindings/ir/symbolic/register_expression.cpp @@ -94,8 +94,8 @@ void registerExpression(nb::module_& m) { .def("__rtruediv__", [](const sym::Expression& rhs, double lhs) { return rhs / lhs; }) // comparison operators - .def(nb::self == nb::self) - .def(nb::self != nb::self) + .def(nb::self == nb::self) // NOLINT(misc-redundant-expression) + .def(nb::self != nb::self) // NOLINT(misc-redundant-expression) .def(nb::hash(nb::self)) .def("__str__", [](const sym::Expression& expr) { diff --git a/bindings/ir/symbolic/register_term.cpp b/bindings/ir/symbolic/register_term.cpp index 13a00bfed0..d5072b6ba0 100644 --- a/bindings/ir/symbolic/register_term.cpp +++ b/bindings/ir/symbolic/register_term.cpp @@ -34,8 +34,8 @@ void registerTerm(nb::module_& m) { .def(double() * nb::self) .def(nb::self / double()) .def(double() / nb::self) - .def(nb::self == nb::self) - .def(nb::self != nb::self) + .def(nb::self == nb::self) // NOLINT(misc-redundant-expression) + .def(nb::self != nb::self) // NOLINT(misc-redundant-expression) .def(nb::hash(nb::self)) .def("__str__", [](const sym::Term& term) { diff --git a/bindings/ir/symbolic/register_variable.cpp b/bindings/ir/symbolic/register_variable.cpp index 22eafad9d9..0db8c8a18c 100644 --- a/bindings/ir/symbolic/register_variable.cpp +++ b/bindings/ir/symbolic/register_variable.cpp @@ -27,8 +27,8 @@ void registerVariable(nb::module_& m) { .def_prop_ro("name", &sym::Variable::getName) .def("__str__", &sym::Variable::getName) .def("__repr__", &sym::Variable::getName) - .def(nb::self == nb::self) - .def(nb::self != nb::self) + .def(nb::self == nb::self) // NOLINT(misc-redundant-expression) + .def(nb::self != nb::self) // NOLINT(misc-redundant-expression) .def(nb::hash(nb::self)) .def(nb::self < nb::self) .def(nb::self > nb::self); diff --git a/bindings/na/fomac/fomac.cpp b/bindings/na/fomac/fomac.cpp index 72d3eca052..e0945ebab5 100644 --- a/bindings/na/fomac/fomac.cpp +++ b/bindings/na/fomac/fomac.cpp @@ -49,8 +49,8 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { return ""; }); - vector.def(nb::self == nb::self); - vector.def(nb::self != nb::self); + vector.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) + vector.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) auto region = nb::class_(lattice, "Region"); @@ -61,16 +61,16 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { return ""; }); - size.def(nb::self == nb::self); - size.def(nb::self != nb::self); + size.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) + size.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) region.def_ro("origin", &na::Device::Region::origin); region.def_ro("size", &na::Device::Region::size); region.def("__repr__", [](const na::Device::Region& r) { return ""; }); - region.def(nb::self == nb::self); - region.def(nb::self != nb::self); + region.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) + region.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) lattice.def_ro("lattice_origin", &na::Device::Lattice::latticeOrigin); lattice.def_ro("lattice_vector_1", &na::Device::Lattice::latticeVector1); @@ -80,8 +80,8 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { lattice.def("__repr__", [](const na::Device::Lattice& l) { return ""; }); - lattice.def(nb::self == nb::self); - lattice.def(nb::self != nb::self); + lattice.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) + lattice.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) device.def_prop_ro("traps", &na::Session::Device::getTraps); device.def_prop_ro("t1", [](const na::Session::Device& dev) { @@ -93,8 +93,8 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { device.def("__repr__", [](const fomac::Session::Device& dev) { return ""; }); - device.def(nb::self == nb::self); - device.def(nb::self != nb::self); + device.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) + device.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) m.def("devices", &na::Session::getDevices); device.def_static("try_create_from_device", From d034b255da7b8e21744107e1cedcd14588f5491b Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Wed, 10 Dec 2025 10:53:12 +0100 Subject: [PATCH 010/144] Do not lint NB_MODULE --- bindings/dd/register_dd.cpp | 1 + bindings/fomac/fomac.cpp | 1 + bindings/ir/register_ir.cpp | 1 + bindings/na/fomac/fomac.cpp | 1 + 4 files changed, 4 insertions(+) diff --git a/bindings/dd/register_dd.cpp b/bindings/dd/register_dd.cpp index 91de56498d..7001b42ca0 100644 --- a/bindings/dd/register_dd.cpp +++ b/bindings/dd/register_dd.cpp @@ -41,6 +41,7 @@ Vector getVector(const dd::vEdge& v, dd::fp threshold = 0.); using Matrix = nb::ndarray, nb::ndim<2>>; Matrix getMatrix(const dd::mEdge& m, size_t numQubits, dd::fp threshold = 0.); +// NOLINTNEXTLINE NB_MODULE(MQT_CORE_MODULE_NAME, m) { // Vector Decision Diagrams registerVectorDDs(m); diff --git a/bindings/fomac/fomac.cpp b/bindings/fomac/fomac.cpp index 0dd704d631..c938a57092 100644 --- a/bindings/fomac/fomac.cpp +++ b/bindings/fomac/fomac.cpp @@ -30,6 +30,7 @@ namespace mqt { namespace nb = nanobind; using namespace nb::literals; +// NOLINTNEXTLINE NB_MODULE(MQT_CORE_MODULE_NAME, m) { // Session class auto session = nb::class_(m, "Session"); diff --git a/bindings/ir/register_ir.cpp b/bindings/ir/register_ir.cpp index 0aec88fe32..50ad9fd046 100644 --- a/bindings/ir/register_ir.cpp +++ b/bindings/ir/register_ir.cpp @@ -21,6 +21,7 @@ void registerOperations(nb::module_& m); void registerSymbolic(nb::module_& m); void registerQuantumComputation(nb::module_& m); +// NOLINTNEXTLINE NB_MODULE(MQT_CORE_MODULE_NAME, m) { registerPermutation(m); diff --git a/bindings/na/fomac/fomac.cpp b/bindings/na/fomac/fomac.cpp index e0945ebab5..dd78918609 100644 --- a/bindings/na/fomac/fomac.cpp +++ b/bindings/na/fomac/fomac.cpp @@ -34,6 +34,7 @@ template [[nodiscard]] auto repr(T c) -> std::string { } // namespace +// NOLINTNEXTLINE NB_MODULE(MQT_CORE_MODULE_NAME, m) { nb::module_::import_("mqt.core.fomac"); From fb71055947aeb429815124e1037a31eaf16d195e Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Wed, 10 Dec 2025 10:54:49 +0100 Subject: [PATCH 011/144] Fix inclusion errors --- bindings/dd/register_dd.cpp | 1 - bindings/dd/register_dd_package.cpp | 1 - bindings/dd/register_matrix_dds.cpp | 1 - bindings/ir/register_permutation.cpp | 1 + 4 files changed, 1 insertion(+), 3 deletions(-) diff --git a/bindings/dd/register_dd.cpp b/bindings/dd/register_dd.cpp index 7001b42ca0..e689404d8f 100644 --- a/bindings/dd/register_dd.cpp +++ b/bindings/dd/register_dd.cpp @@ -23,7 +23,6 @@ #include #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) -#include namespace mqt { diff --git a/bindings/dd/register_dd_package.cpp b/bindings/dd/register_dd_package.cpp index acf68debcd..c84bff0df7 100644 --- a/bindings/dd/register_dd_package.cpp +++ b/bindings/dd/register_dd_package.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include // NOLINT(misc-include-cleaner) diff --git a/bindings/dd/register_matrix_dds.cpp b/bindings/dd/register_matrix_dds.cpp index edebfc782d..f814b7ab02 100644 --- a/bindings/dd/register_matrix_dds.cpp +++ b/bindings/dd/register_matrix_dds.cpp @@ -23,7 +23,6 @@ #include // NOLINT(misc-include-cleaner) #include #include -#include namespace mqt { diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index 7dd102fe6a..f2373a9211 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -13,6 +13,7 @@ #include "ir/operations/Control.hpp" #include +#include #include #include #include From 82c6a2d030538c692b9691d30c961d9d9bbccd48 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Wed, 10 Dec 2025 11:00:40 +0100 Subject: [PATCH 012/144] Hopefully fix linter errors related to pointers --- bindings/dd/register_matrix_dds.cpp | 23 ++++++++++++++--------- bindings/dd/register_vector_dds.cpp | 10 ++++++---- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/bindings/dd/register_matrix_dds.cpp b/bindings/dd/register_matrix_dds.cpp index f814b7ab02..6972ebb3b6 100644 --- a/bindings/dd/register_matrix_dds.cpp +++ b/bindings/dd/register_matrix_dds.cpp @@ -35,22 +35,27 @@ using Matrix = nb::ndarray, nb::ndim<2>>; Matrix getMatrix(const dd::mEdge& m, const size_t numQubits, const dd::fp threshold = 0.) { if (numQubits == 0U) { - auto* data = new std::complex[1]; - data[0] = static_cast>(m.w); - nb::capsule owner( - data, [](void* p) noexcept { delete[] (std::complex*)p; }); + auto dataPtr = std::make_unique>(m.w); + auto* data = dataPtr.release(); + const nb::capsule owner(data, [](void* ptr) noexcept { + delete static_cast*>(ptr); + }); return Matrix(data, {1, 1}, owner); } const auto dim = 1ULL << numQubits; - auto* data = new std::complex[dim * dim]; + auto dataPtr = std::make_unique[]>(dim * dim); m.traverseMatrix( std::complex{1., 0.}, 0ULL, 0ULL, - [&data, dim](const std::size_t i, const std::size_t j, - const std::complex& c) { data[(i * dim) + j] = c; }, + [&dataPtr, dim](const std::size_t i, const std::size_t j, + const std::complex& c) { + dataPtr[(i * dim) + j] = c; + }, numQubits, threshold); - nb::capsule owner( - data, [](void* p) noexcept { delete[] (std::complex*)p; }); + auto* data = dataPtr.release(); + const nb::capsule owner(data, [](void* ptr) noexcept { + delete[] static_cast*>(ptr); + }); return Matrix(data, {dim, dim}, owner); } diff --git a/bindings/dd/register_vector_dds.cpp b/bindings/dd/register_vector_dds.cpp index 434ff15a72..d438faec93 100644 --- a/bindings/dd/register_vector_dds.cpp +++ b/bindings/dd/register_vector_dds.cpp @@ -34,10 +34,12 @@ using Vector = nb::ndarray, nb::ndim<1>>; // NOLINTNEXTLINE(misc-use-internal-linkage) Vector getVector(const dd::vEdge& v, const dd::fp threshold = 0.) { auto vec = v.getVector(threshold); - auto* data = new std::complex[vec.size()]; - std::copy(vec.begin(), vec.end(), data); - nb::capsule owner( - data, [](void* p) noexcept { delete[] (std::complex*)p; }); + auto dataPtr = std::make_unique[]>(vec.size()); + std::ranges::copy(vec, dataPtr.get()); + auto* data = dataPtr.release(); + const nb::capsule owner(data, [](void* ptr) noexcept { + delete[] static_cast*>(ptr); + }); return Vector(data, {vec.size()}, owner); } From 016f25064de226de1c17552b7fae91213303d661 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Wed, 10 Dec 2025 11:34:01 +0100 Subject: [PATCH 013/144] Target stable ABI --- cmake/AddMQTPythonBinding.cmake | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/cmake/AddMQTPythonBinding.cmake b/cmake/AddMQTPythonBinding.cmake index be9e890684..f27449a2da 100644 --- a/cmake/AddMQTPythonBinding.cmake +++ b/cmake/AddMQTPythonBinding.cmake @@ -55,43 +55,44 @@ function(add_mqt_python_binding package_name target_name) endfunction() function(add_mqt_python_binding_nanobind package_name target_name) - # parse the arguments cmake_parse_arguments(ARG "" "MODULE_NAME;INSTALL_DIR" "LINK_LIBS" ${ARGN}) set(SOURCES ${ARG_UNPARSED_ARGUMENTS}) - # declare the Python module nanobind_add_module( - # name of the extension + # Name of the extension ${target_name} - # source code goes here + # Target the stable ABI for Python 3.12+, which reduces the number of binary wheels + STABLE_ABI + # Suppress compiler warnings from the nanobind library + NB_SUPPRESS_WARNINGS + # Source files ${SOURCES}) - # set default "." for INSTALL_DIR - if(NOT ARG_INSTALL_DIR) - set(ARG_INSTALL_DIR ".") - endif() + # Set C++ standard + target_compile_features(${target_name} PRIVATE cxx_std_20) if(ARG_MODULE_NAME) - # the library name must be the same as the module name + # The library name must be the same as the module name set_target_properties(${target_name} PROPERTIES OUTPUT_NAME ${ARG_MODULE_NAME}) target_compile_definitions(${target_name} PRIVATE MQT_${package_name}_MODULE_NAME=${ARG_MODULE_NAME}) else() - # use the target name as the module name + # Use the target name as the module name target_compile_definitions(${target_name} PRIVATE MQT_${package_name}_MODULE_NAME=${target_name}) endif() - # add project libraries to the link libraries + # Add project libraries to the link libraries list(APPEND ARG_LINK_LIBS MQT::ProjectOptions MQT::ProjectWarnings) - # Set c++ standard - target_compile_features(${target_name} PRIVATE cxx_std_20) - - # link the required libraries target_link_libraries(${target_name} PRIVATE ${ARG_LINK_LIBS}) - # install directive for scikit-build-core + # Set default "." for INSTALL_DIR + if(NOT ARG_INSTALL_DIR) + set(ARG_INSTALL_DIR ".") + endif() + + # Install directive for scikit-build-core install( TARGETS ${target_name} DESTINATION ${ARG_INSTALL_DIR} From 1d5fbae61362d9ecd0dabea60423afedd4d8cb4a Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Wed, 10 Dec 2025 11:51:43 +0100 Subject: [PATCH 014/144] Enable stub generation --- cmake/AddMQTPythonBinding.cmake | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/cmake/AddMQTPythonBinding.cmake b/cmake/AddMQTPythonBinding.cmake index f27449a2da..3091ef8e6a 100644 --- a/cmake/AddMQTPythonBinding.cmake +++ b/cmake/AddMQTPythonBinding.cmake @@ -97,4 +97,18 @@ function(add_mqt_python_binding_nanobind package_name target_name) TARGETS ${target_name} DESTINATION ${ARG_INSTALL_DIR} COMPONENT ${MQT_${package_name}_TARGET_NAME}_Python) + + nanobind_add_stub( + ${target_name}_stub + INSTALL_TIME + RECURSIVE + MODULE + ${target_name} + OUTPUT + ${target_name}.pyi + OUTPUT_PATH + ${ARG_INSTALL_DIR} + MARKER_FILE + py.typed + INCLUDE_PRIVATE) endfunction() From d65c4bd408f417b449c94968b3e3092f18db703d Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Wed, 10 Dec 2025 11:55:56 +0100 Subject: [PATCH 015/144] Include memory --- bindings/dd/register_matrix_dds.cpp | 1 + bindings/dd/register_vector_dds.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/bindings/dd/register_matrix_dds.cpp b/bindings/dd/register_matrix_dds.cpp index 6972ebb3b6..fc8dce4464 100644 --- a/bindings/dd/register_matrix_dds.cpp +++ b/bindings/dd/register_matrix_dds.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include // NOLINT(misc-include-cleaner) diff --git a/bindings/dd/register_vector_dds.cpp b/bindings/dd/register_vector_dds.cpp index d438faec93..5e250a3149 100644 --- a/bindings/dd/register_vector_dds.cpp +++ b/bindings/dd/register_vector_dds.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include // NOLINT(misc-include-cleaner) From f33f33792221ad362e6340284bd65cb480ac6350 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Wed, 10 Dec 2025 12:16:46 +0100 Subject: [PATCH 016/144] Fix more linter errors --- bindings/dd/register_dd_package.cpp | 6 +++--- bindings/dd/register_vector_dds.cpp | 1 + bindings/fomac/fomac.cpp | 15 ++++++++++++--- .../ir/operations/register_compound_operation.cpp | 9 +++++---- bindings/ir/register_permutation.cpp | 2 +- bindings/ir/register_quantum_computation.cpp | 9 +++++---- bindings/ir/symbolic/register_variable.cpp | 4 ++-- 7 files changed, 29 insertions(+), 17 deletions(-) diff --git a/bindings/dd/register_dd_package.cpp b/bindings/dd/register_dd_package.cpp index c84bff0df7..da04cb94fe 100644 --- a/bindings/dd/register_dd_package.cpp +++ b/bindings/dd/register_dd_package.cpp @@ -164,7 +164,7 @@ void registerDDPackage(const nb::module_& m) { dd.def( "from_vector", [](dd::Package& p, const Vector& v) { - const auto length = static_cast(v.shape(0)); + const auto length = v.shape(0); if (length == 0) { return dd::vEdge::one(); } @@ -351,8 +351,8 @@ void registerDDPackage(const nb::module_& m) { dd.def( "from_matrix", [](dd::Package& p, const Matrix& m) { - const auto rows = static_cast(m.shape(0)); - const auto cols = static_cast(m.shape(1)); + const auto rows = m.shape(0); + const auto cols = m.shape(1); if (rows != cols) { throw std::invalid_argument("Matrix must be square."); } diff --git a/bindings/dd/register_vector_dds.cpp b/bindings/dd/register_vector_dds.cpp index 5e250a3149..2b3d8aa76d 100644 --- a/bindings/dd/register_vector_dds.cpp +++ b/bindings/dd/register_vector_dds.cpp @@ -22,6 +22,7 @@ #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) +#include #include #include diff --git a/bindings/fomac/fomac.cpp b/bindings/fomac/fomac.cpp index c938a57092..0c0afbcd5b 100644 --- a/bindings/fomac/fomac.cpp +++ b/bindings/fomac/fomac.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include namespace mqt { @@ -45,9 +46,17 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { std::optional custom1, std::optional custom2, std::optional custom3, std::optional custom4, std::optional custom5) { - fomac::SessionConfig config{token, authFile, authUrl, username, - password, projectId, custom1, custom2, - custom3, custom4, custom5}; + const fomac::SessionConfig config{.token = std::move(token), + .authFile = std::move(authFile), + .authUrl = std::move(authUrl), + .username = std::move(username), + .password = std::move(password), + .projectId = std::move(projectId), + .custom1 = std::move(custom1), + .custom2 = std::move(custom2), + .custom3 = std::move(custom3), + .custom4 = std::move(custom4), + .custom5 = std::move(custom5)}; new (self) fomac::Session(config); }, "token"_a = std::nullopt, "auth_file"_a = std::nullopt, diff --git a/bindings/ir/operations/register_compound_operation.cpp b/bindings/ir/operations/register_compound_operation.cpp index 12e56a112d..0256f499b9 100644 --- a/bindings/ir/operations/register_compound_operation.cpp +++ b/bindings/ir/operations/register_compound_operation.cpp @@ -70,7 +70,8 @@ void registerCompoundOperation(const nb::module_& m) { auto [start, stop, step, sliceLength] = slice.compute(op.size()); auto ops = std::vector(); ops.reserve(sliceLength); - for (std::size_t i = start; i < stop; i += step) { + for (std::size_t i = start; std::cmp_less(i, stop); + i += static_cast(step)) { ops.emplace_back(op.at(i).get()); } return ops; @@ -95,7 +96,7 @@ void registerCompoundOperation(const nb::module_& m) { "Length of slice and number of operations do not match."); } for (std::size_t i = 0; i < sliceLength; ++i) { - compOp[start] = ops[i]->clone(); + compOp[static_cast(start)] = ops[i]->clone(); start += step; } }, @@ -113,8 +114,8 @@ void registerCompoundOperation(const nb::module_& m) { auto [start, stop, step, sliceLength] = slice.compute(op.size()); // delete in reverse order to not invalidate indices for (std::size_t i = sliceLength; i > 0; --i) { - op.erase(op.begin() + - static_cast(start + ((i - 1) * step))); + op.erase(op.begin() + static_cast(start) + + ((i - 1) * static_cast(step))); } }, "index"_a) diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index f2373a9211..2ab18ee7ff 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -13,12 +13,12 @@ #include "ir/operations/Control.hpp" #include -#include #include #include #include #include // NOLINT(misc-include-cleaner) #include +#include namespace mqt { diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index ec56833def..74e875430a 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -104,7 +104,8 @@ void registerQuantumComputation(nb::module_& m) { auto [start, stop, step, sliceLength] = slice.compute(circ.getNops()); auto ops = std::vector(); ops.reserve(sliceLength); - for (std::size_t i = start; i < stop; i += step) { + for (size_t i = start; std::cmp_less(i, stop); + i += static_cast(step)) { ops.emplace_back(circ.at(i).get()); } return ops; @@ -128,7 +129,7 @@ void registerQuantumComputation(nb::module_& m) { "Length of slice and number of operations do not match."); } for (std::size_t i = 0; i < sliceLength; ++i) { - circ.at(start) = ops[i]->clone(); + circ.at(static_cast(start)) = ops[i]->clone(); start += step; } }, @@ -146,8 +147,8 @@ void registerQuantumComputation(nb::module_& m) { auto [start, stop, step, sliceLength] = slice.compute(circ.getNops()); // delete in reverse order to not invalidate indices for (std::size_t i = sliceLength; i > 0; --i) { - circ.erase(circ.begin() + - static_cast(start + ((i - 1) * step))); + circ.erase(circ.begin() + static_cast(start) + + ((i - 1) * static_cast(step))); } }, "index"_a); diff --git a/bindings/ir/symbolic/register_variable.cpp b/bindings/ir/symbolic/register_variable.cpp index 0db8c8a18c..b1692643ba 100644 --- a/bindings/ir/symbolic/register_variable.cpp +++ b/bindings/ir/symbolic/register_variable.cpp @@ -30,7 +30,7 @@ void registerVariable(nb::module_& m) { .def(nb::self == nb::self) // NOLINT(misc-redundant-expression) .def(nb::self != nb::self) // NOLINT(misc-redundant-expression) .def(nb::hash(nb::self)) - .def(nb::self < nb::self) - .def(nb::self > nb::self); + .def(nb::self < nb::self) // NOLINT(misc-redundant-expression) + .def(nb::self > nb::self); // NOLINT(misc-redundant-expression) } } // namespace mqt From ace60c0a04f5986e32ba6f349c50ae0d89e6ae53 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Wed, 10 Dec 2025 12:31:27 +0100 Subject: [PATCH 017/144] Check if not linting NB_MODULE is still necessary --- bindings/dd/register_dd.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/bindings/dd/register_dd.cpp b/bindings/dd/register_dd.cpp index e689404d8f..9ee0d2c5bc 100644 --- a/bindings/dd/register_dd.cpp +++ b/bindings/dd/register_dd.cpp @@ -40,7 +40,6 @@ Vector getVector(const dd::vEdge& v, dd::fp threshold = 0.); using Matrix = nb::ndarray, nb::ndim<2>>; Matrix getMatrix(const dd::mEdge& m, size_t numQubits, dd::fp threshold = 0.); -// NOLINTNEXTLINE NB_MODULE(MQT_CORE_MODULE_NAME, m) { // Vector Decision Diagrams registerVectorDDs(m); From e62daf8cbeddcfc9602a11de73a71394088065e2 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Wed, 10 Dec 2025 12:53:17 +0100 Subject: [PATCH 018/144] Maybe fix conversion errors bur probably not --- bindings/ir/operations/register_compound_operation.cpp | 10 +++++----- bindings/ir/register_quantum_computation.cpp | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bindings/ir/operations/register_compound_operation.cpp b/bindings/ir/operations/register_compound_operation.cpp index 0256f499b9..404e2a5902 100644 --- a/bindings/ir/operations/register_compound_operation.cpp +++ b/bindings/ir/operations/register_compound_operation.cpp @@ -70,9 +70,8 @@ void registerCompoundOperation(const nb::module_& m) { auto [start, stop, step, sliceLength] = slice.compute(op.size()); auto ops = std::vector(); ops.reserve(sliceLength); - for (std::size_t i = start; std::cmp_less(i, stop); - i += static_cast(step)) { - ops.emplace_back(op.at(i).get()); + for (auto i = start; i < stop; i += step) { + ops.emplace_back(op.at(static_cast(i)).get()); } return ops; }, @@ -114,8 +113,9 @@ void registerCompoundOperation(const nb::module_& m) { auto [start, stop, step, sliceLength] = slice.compute(op.size()); // delete in reverse order to not invalidate indices for (std::size_t i = sliceLength; i > 0; --i) { - op.erase(op.begin() + static_cast(start) + - ((i - 1) * static_cast(step))); + const auto offset = + static_cast(start + ((i - 1) * step)); + op.erase(op.begin() + offset); } }, "index"_a) diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index 74e875430a..e7afdf98d3 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -104,9 +104,8 @@ void registerQuantumComputation(nb::module_& m) { auto [start, stop, step, sliceLength] = slice.compute(circ.getNops()); auto ops = std::vector(); ops.reserve(sliceLength); - for (size_t i = start; std::cmp_less(i, stop); - i += static_cast(step)) { - ops.emplace_back(circ.at(i).get()); + for (auto i = start; i < stop; i += step) { + ops.emplace_back(circ.at(static_cast(i)).get()); } return ops; }, @@ -147,8 +146,9 @@ void registerQuantumComputation(nb::module_& m) { auto [start, stop, step, sliceLength] = slice.compute(circ.getNops()); // delete in reverse order to not invalidate indices for (std::size_t i = sliceLength; i > 0; --i) { - circ.erase(circ.begin() + static_cast(start) + - ((i - 1) * static_cast(step))); + const auto offset = + static_cast(start + ((i - 1) * step)); + circ.erase(circ.begin() + offset); } }, "index"_a); From 6fe1adad978a99f90386c9cf959c8ac553720df5 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Wed, 10 Dec 2025 12:54:22 +0100 Subject: [PATCH 019/144] Stop NOLINTing NB_MODULE --- bindings/fomac/fomac.cpp | 1 - bindings/ir/register_ir.cpp | 1 - bindings/na/fomac/fomac.cpp | 1 - 3 files changed, 3 deletions(-) diff --git a/bindings/fomac/fomac.cpp b/bindings/fomac/fomac.cpp index 0c0afbcd5b..9c9d6e3f44 100644 --- a/bindings/fomac/fomac.cpp +++ b/bindings/fomac/fomac.cpp @@ -31,7 +31,6 @@ namespace mqt { namespace nb = nanobind; using namespace nb::literals; -// NOLINTNEXTLINE NB_MODULE(MQT_CORE_MODULE_NAME, m) { // Session class auto session = nb::class_(m, "Session"); diff --git a/bindings/ir/register_ir.cpp b/bindings/ir/register_ir.cpp index 50ad9fd046..0aec88fe32 100644 --- a/bindings/ir/register_ir.cpp +++ b/bindings/ir/register_ir.cpp @@ -21,7 +21,6 @@ void registerOperations(nb::module_& m); void registerSymbolic(nb::module_& m); void registerQuantumComputation(nb::module_& m); -// NOLINTNEXTLINE NB_MODULE(MQT_CORE_MODULE_NAME, m) { registerPermutation(m); diff --git a/bindings/na/fomac/fomac.cpp b/bindings/na/fomac/fomac.cpp index dd78918609..e0945ebab5 100644 --- a/bindings/na/fomac/fomac.cpp +++ b/bindings/na/fomac/fomac.cpp @@ -34,7 +34,6 @@ template [[nodiscard]] auto repr(T c) -> std::string { } // namespace -// NOLINTNEXTLINE NB_MODULE(MQT_CORE_MODULE_NAME, m) { nb::module_::import_("mqt.core.fomac"); From 22023ffcb5aa6e9a603dd5ce77bf8032ecbcb008 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Wed, 10 Dec 2025 12:55:12 +0100 Subject: [PATCH 020/144] Check if not linting nanobind/stl includes is still necessary --- bindings/na/fomac/fomac.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/na/fomac/fomac.cpp b/bindings/na/fomac/fomac.cpp index e0945ebab5..098c9192cc 100644 --- a/bindings/na/fomac/fomac.cpp +++ b/bindings/na/fomac/fomac.cpp @@ -15,8 +15,8 @@ #include #include -#include // NOLINT(misc-include-cleaner) -#include // NOLINT(misc-include-cleaner) +#include +#include #include namespace mqt { From 35176144dbe7a15eb689a5a77ec1e78187ade558 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Wed, 10 Dec 2025 13:00:26 +0100 Subject: [PATCH 021/144] Revert "Check if not linting nanobind/stl includes is still necessary" This reverts commit 22023ffcb5aa6e9a603dd5ce77bf8032ecbcb008. --- bindings/na/fomac/fomac.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/na/fomac/fomac.cpp b/bindings/na/fomac/fomac.cpp index 098c9192cc..e0945ebab5 100644 --- a/bindings/na/fomac/fomac.cpp +++ b/bindings/na/fomac/fomac.cpp @@ -15,8 +15,8 @@ #include #include -#include -#include +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include namespace mqt { From 8f31a72431398f22c0817ff11864b806b02308fe Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Wed, 10 Dec 2025 13:01:57 +0100 Subject: [PATCH 022/144] Fix remaining fixable linter errors --- bindings/dd/register_vector_dds.cpp | 2 +- bindings/ir/operations/register_compound_operation.cpp | 9 +++++---- bindings/ir/register_quantum_computation.cpp | 7 ++++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/bindings/dd/register_vector_dds.cpp b/bindings/dd/register_vector_dds.cpp index 2b3d8aa76d..c27f489ec4 100644 --- a/bindings/dd/register_vector_dds.cpp +++ b/bindings/dd/register_vector_dds.cpp @@ -13,6 +13,7 @@ #include "dd/Export.hpp" #include "dd/Node.hpp" +#include #include #include #include @@ -22,7 +23,6 @@ #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) -#include #include #include diff --git a/bindings/ir/operations/register_compound_operation.cpp b/bindings/ir/operations/register_compound_operation.cpp index 404e2a5902..05c8c0db96 100644 --- a/bindings/ir/operations/register_compound_operation.cpp +++ b/bindings/ir/operations/register_compound_operation.cpp @@ -71,7 +71,7 @@ void registerCompoundOperation(const nb::module_& m) { auto ops = std::vector(); ops.reserve(sliceLength); for (auto i = start; i < stop; i += step) { - ops.emplace_back(op.at(static_cast(i)).get()); + ops.emplace_back(op.at(static_cast(i)).get()); } return ops; }, @@ -95,7 +95,7 @@ void registerCompoundOperation(const nb::module_& m) { "Length of slice and number of operations do not match."); } for (std::size_t i = 0; i < sliceLength; ++i) { - compOp[static_cast(start)] = ops[i]->clone(); + compOp[static_cast(start)] = ops[i]->clone(); start += step; } }, @@ -113,8 +113,9 @@ void registerCompoundOperation(const nb::module_& m) { auto [start, stop, step, sliceLength] = slice.compute(op.size()); // delete in reverse order to not invalidate indices for (std::size_t i = sliceLength; i > 0; --i) { - const auto offset = - static_cast(start + ((i - 1) * step)); + const auto offset = static_cast( + static_cast(start) + + ((i - 1) * static_cast(step))); op.erase(op.begin() + offset); } }, diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index e7afdf98d3..1d6b76854e 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -105,7 +105,7 @@ void registerQuantumComputation(nb::module_& m) { auto ops = std::vector(); ops.reserve(sliceLength); for (auto i = start; i < stop; i += step) { - ops.emplace_back(circ.at(static_cast(i)).get()); + ops.emplace_back(circ.at(static_cast(i)).get()); } return ops; }, @@ -128,7 +128,7 @@ void registerQuantumComputation(nb::module_& m) { "Length of slice and number of operations do not match."); } for (std::size_t i = 0; i < sliceLength; ++i) { - circ.at(static_cast(start)) = ops[i]->clone(); + circ.at(static_cast(start)) = ops[i]->clone(); start += step; } }, @@ -147,7 +147,8 @@ void registerQuantumComputation(nb::module_& m) { // delete in reverse order to not invalidate indices for (std::size_t i = sliceLength; i > 0; --i) { const auto offset = - static_cast(start + ((i - 1) * step)); + static_cast(static_cast(start) + + ((i - 1) * static_cast(step))); circ.erase(circ.begin() + offset); } }, From 99bb3a2726433ab4dad64088efbf17542e888d1a Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Wed, 10 Dec 2025 23:37:45 +0100 Subject: [PATCH 023/144] Include nanobind/stl/unique_ptr.h --- bindings/ir/operations/register_compound_operation.cpp | 5 +++-- bindings/ir/register_quantum_computation.cpp | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/bindings/ir/operations/register_compound_operation.cpp b/bindings/ir/operations/register_compound_operation.cpp index 05c8c0db96..0e48326057 100644 --- a/bindings/ir/operations/register_compound_operation.cpp +++ b/bindings/ir/operations/register_compound_operation.cpp @@ -15,8 +15,9 @@ #include #include #include -#include // NOLINT(misc-include-cleaner) -#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include #include #include diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index 1d6b76854e..50cb4258c5 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -23,6 +23,7 @@ #include #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) From dc85238ab40b86a3f3eb6645d2553f1d09d43fdf Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Wed, 10 Dec 2025 23:43:28 +0100 Subject: [PATCH 024/144] Move docstrings from operations.pyi to bindings files --- .../register_compound_operation.cpp | 86 +++++- bindings/ir/operations/register_control.cpp | 17 +- .../operations/register_if_else_operation.cpp | 68 +++-- .../register_non_unitary_operation.cpp | 22 +- bindings/ir/operations/register_operation.cpp | 141 ++++++++-- bindings/ir/operations/register_optype.cpp | 249 +++++++++++++++--- .../register_standard_operation.cpp | 14 +- .../register_symbolic_operation.cpp | 52 +++- 8 files changed, 541 insertions(+), 108 deletions(-) diff --git a/bindings/ir/operations/register_compound_operation.cpp b/bindings/ir/operations/register_compound_operation.cpp index 0e48326057..f7bbc71d3f 100644 --- a/bindings/ir/operations/register_compound_operation.cpp +++ b/bindings/ir/operations/register_compound_operation.cpp @@ -43,7 +43,16 @@ void registerCompoundOperation(const nb::module_& m) { return i; }; - nb::class_(m, "CompoundOperation") + nb::class_( + m, "CompoundOperation", R"pb(Compound quantum operation. + +This class is used to aggregate and group multiple operations into a single object. +This is useful for optimizations and for representing complex quantum functionality. +A :class:`CompoundOperation` can contain any number of operations, including other :class:`CompoundOperation`'s. + +Args: + ops: The operations that are part of the compound operation.)pb") + .def(nb::init<>()) .def( "__init__", @@ -57,14 +66,28 @@ void registerCompoundOperation(const nb::module_& m) { new (self) qc::CompoundOperation(std::move(uniqueOps)); }, "ops"_a) - .def("__len__", &qc::CompoundOperation::size) + + .def("__len__", &qc::CompoundOperation::size, + "The number of operations in the compound operation.") + .def( "__getitem__", [&wrap](const qc::CompoundOperation& op, DiffType i) { i = wrap(i, op.size()); return op.at(static_cast(i)).get(); }, - nb::rv_policy::reference_internal, "index"_a) + nb::rv_policy::reference_internal, "index"_a, + R"pb(Get the operation at the given index. + +Note: + This gives direct access to the operations in the compound operation + +Args: + index: The index of the operation to get. + +Returns: + The operation at the given index.)pb") + .def( "__getitem__", [](const qc::CompoundOperation& op, const nb::slice& slice) { @@ -76,7 +99,18 @@ void registerCompoundOperation(const nb::module_& m) { } return ops; }, - nb::rv_policy::reference_internal, "index"_a) + nb::rv_policy::reference_internal, "index"_a, + R"pb(Get the operations in the given slice. + +Note: + This gives direct access to the operations in the compound operation. + +Args: + index: The slice of the operations to get. + +Returns: + The operations in the given slice.)pb") + .def( "__setitem__", [&wrap](qc::CompoundOperation& compOp, DiffType i, @@ -84,7 +118,12 @@ void registerCompoundOperation(const nb::module_& m) { i = wrap(i, compOp.size()); compOp[static_cast(i)] = op.clone(); }, - "index"_a, "value"_a) + "index"_a, "value"_a, R"pb(Set the operation at the given index. + +Args: + index: The index of the operation to set. + value: The operation to set at the given index.)pb") + .def( "__setitem__", [](qc::CompoundOperation& compOp, const nb::slice& slice, @@ -100,14 +139,23 @@ void registerCompoundOperation(const nb::module_& m) { start += step; } }, - "index"_a, "value"_a) + "index"_a, "value"_a, R"pb(Set the operations in the given slice. + +Args: + index: The slice of operations to set. + value: The operations to set in the given slice.)pb") + .def( "__delitem__", [&wrap](qc::CompoundOperation& op, DiffType i) { i = wrap(i, op.size()); op.erase(op.begin() + i); }, - "index"_a) + "index"_a, R"pb(Delete the operation at the given index. + +Args: + index: The index of the operation to delete.)pb") + .def( "__delitem__", [](qc::CompoundOperation& op, const nb::slice& slice) { @@ -120,13 +168,18 @@ void registerCompoundOperation(const nb::module_& m) { op.erase(op.begin() + offset); } }, - "index"_a) + "index"_a, R"pb(Delete the operations in the given slice. + +Args: + index: The slice of operations to delete.)pb") + .def( "append", [](qc::CompoundOperation& compOp, const qc::Operation& op) { compOp.emplace_back(op.clone()); }, - "value"_a) + "value"_a, "Append an operation to the compound operation.") + .def( "insert", [](qc::CompoundOperation& compOp, const std::size_t idx, @@ -134,9 +187,18 @@ void registerCompoundOperation(const nb::module_& m) { compOp.insert(compOp.begin() + static_cast(idx), op.clone()); }, - "index"_a, "value"_a) - .def("empty", &qc::CompoundOperation::empty) - .def("clear", &qc::CompoundOperation::clear) + "index"_a, "value"_a, R"pb(Insert an operation at the given index. + +Args: + index: The index to insert the operation at. + value: The operation to insert.)pb") + + .def("empty", &qc::CompoundOperation::empty, + "Check if the compound operation is empty.") + + .def("clear", &qc::CompoundOperation::clear, + "Clear all operations in the compound operation.") + .def("__repr__", [](const qc::CompoundOperation& op) { std::stringstream ss; ss << "CompoundOperation([..." << op.size() << " ops...])"; diff --git a/bindings/ir/operations/register_control.cpp b/bindings/ir/operations/register_control.cpp index 50e960fd58..6920e058fb 100644 --- a/bindings/ir/operations/register_control.cpp +++ b/bindings/ir/operations/register_control.cpp @@ -23,7 +23,13 @@ using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) void registerControl(const nb::module_& m) { - auto control = nb::class_(m, "Control"); + auto control = nb::class_( + m, "Control", + R"pb(A control is a pair of a qubit and a type. The type can be either positive or negative. + +Args: + qubit: The qubit that is the control. + type_: The type of the control.)pb"); nb::enum_(control, "Type", "enum.Enum", "Enumeration of control types.") @@ -32,13 +38,18 @@ void registerControl(const nb::module_& m) { control.def(nb::init(), "qubit"_a, "type_"_a = qc::Control::Type::Pos); - control.def_rw("type_", &qc::Control::type); - control.def_rw("qubit", &qc::Control::qubit); + + control.def_rw("qubit", &qc::Control::qubit, + "The qubit that is the control."); + + control.def_rw("type_", &qc::Control::type, "The type of the control."); + control.def("__str__", [](const qc::Control& c) { return c.toString(); }); control.def("__repr__", [](const qc::Control& c) { return c.toString(); }); control.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) control.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) control.def(nb::hash(nb::self)); + nb::implicitly_convertible(); } diff --git a/bindings/ir/operations/register_if_else_operation.cpp b/bindings/ir/operations/register_if_else_operation.cpp index 783b252ff3..56b8d9db26 100644 --- a/bindings/ir/operations/register_if_else_operation.cpp +++ b/bindings/ir/operations/register_if_else_operation.cpp @@ -31,16 +31,28 @@ void registerIfElseOperation(const nb::module_& m) { nb::enum_( m, "ComparisonKind", "enum.Enum", "Enumeration of comparison types for classic-controlled operations.") - .value("eq", qc::ComparisonKind::Eq) - .value("neq", qc::ComparisonKind::Neq) - .value("lt", qc::ComparisonKind::Lt) - .value("leq", qc::ComparisonKind::Leq) - .value("gt", qc::ComparisonKind::Gt) - .value("geq", qc::ComparisonKind::Geq) + .value("eq", qc::ComparisonKind::Eq, "Equality comparison.") + .value("neq", qc::ComparisonKind::Neq, "Inequality comparison.") + .value("lt", qc::ComparisonKind::Lt, "Less-than comparison.") + .value("leq", qc::ComparisonKind::Leq, "Less-than-or-equal comparison.") + .value("gt", qc::ComparisonKind::Gt, "Greater-than comparison.") + .value("geq", qc::ComparisonKind::Geq, + "Greater-than-or-equal comparison.") .export_values(); - auto ifElse = - nb::class_(m, "IfElseOperation"); + auto ifElse = nb::class_( + m, "IfElseOperation", R"pb(If-else quantum operation. + +This class is used to represent an if-else operation. +The then operation is executed if the value of the classical register matches the expected value. +Otherwise, the else operation is executed. + +Args: + then_operation: The operation that is executed if the condition is met. + else_operation: The operation that is executed if the condition is not met. + control_register: The classical register that controls the operation. + expected_value: The expected value of the classical register. + comparison_kind: The kind of comparison (default is equality).)pb"); ifElse.def( "__init__", @@ -70,19 +82,41 @@ void registerIfElseOperation(const nb::module_& m) { }, "then_operation"_a, "else_operation"_a, "control_bit"_a, "expected_value"_a = 1U, "comparison_kind"_a = qc::ComparisonKind::Eq); + ifElse.def_prop_ro("then_operation", &qc::IfElseOperation::getThenOp, - nb::rv_policy::reference_internal); - ifElse.def_prop_ro("else_operation", &qc::IfElseOperation::getElseOp, - nb::rv_policy::reference_internal); + nb::rv_policy::reference_internal, + "The operation that is executed if the condition is met."); + + ifElse.def_prop_ro( + "else_operation", &qc::IfElseOperation::getElseOp, + nb::rv_policy::reference_internal, + "The operation that is executed if the condition is not met."); + ifElse.def_prop_ro("control_register", - &qc::IfElseOperation::getControlRegister); - ifElse.def_prop_ro("control_bit", &qc::IfElseOperation::getControlBit); + &qc::IfElseOperation::getControlRegister, + "The classical register that controls the operation."); + + ifElse.def_prop_ro("control_bit", &qc::IfElseOperation::getControlBit, + "The classical bit that controls the operation."); + ifElse.def_prop_ro("expected_value_register", - &qc::IfElseOperation::getExpectedValueRegister); + &qc::IfElseOperation::getExpectedValueRegister, + R"pb(The expected value of the classical register. + +The then-operation is executed if the value of the classical register matches the expected value based on the kind of comparison. +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.)pb"); + ifElse.def_prop_ro("expected_value_bit", - &qc::IfElseOperation::getExpectedValueBit); - ifElse.def_prop_ro("comparison_kind", - &qc::IfElseOperation::getComparisonKind); + &qc::IfElseOperation::getExpectedValueBit, + R"pb(The expected value of the classical bit. + +The then-operation is executed if the value of the classical bit matches the expected value based on the kind of comparison.)pb"); + + ifElse.def_prop_ro("comparison_kind", &qc::IfElseOperation::getComparisonKind, + R"pb(The kind of comparison. + +The then-operation is executed if the value of the control matches the expected value based on the kind of comparison.)pb"); + ifElse.def("__repr__", [](const qc::IfElseOperation& op) { std::stringstream ss; ss << "IfElseOperation(<...then-op...>, <...else-op...>, "; diff --git a/bindings/ir/operations/register_non_unitary_operation.cpp b/bindings/ir/operations/register_non_unitary_operation.cpp index a429365eb2..46647290d2 100644 --- a/bindings/ir/operations/register_non_unitary_operation.cpp +++ b/bindings/ir/operations/register_non_unitary_operation.cpp @@ -26,15 +26,29 @@ using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) void registerNonUnitaryOperation(const nb::module_& m) { - nb::class_(m, "NonUnitaryOperation") + nb::class_(m, "NonUnitaryOperation", + R"pb(Non-unitary operation. + +This class is used to represent all non-unitary operations, i.e., operations that are not reversible. +This includes measurements and resets. + +Args: + targets: The target qubit(s) of the operation. + classics: The classical bit(s) that are associated with the operation (only relevant for measurements). + op_type: The type of the operation.)pb") + .def(nb::init, std::vector>(), "targets"_a, "classics"_a) .def(nb::init(), "target"_a, "classic"_a) .def(nb::init, qc::OpType>(), "targets"_a, "op_type"_a = qc::OpType::Reset) - .def_prop_ro("classics", - nb::overload_cast<>(&qc::NonUnitaryOperation::getClassics, - nb::const_)) + + .def_prop_ro( + "classics", + nb::overload_cast<>(&qc::NonUnitaryOperation::getClassics, + nb::const_), + "The classical registers that are associated with the operation.") + .def("__repr__", [](const qc::NonUnitaryOperation& op) { std::stringstream ss; ss << "NonUnitaryOperation("; diff --git a/bindings/ir/operations/register_operation.cpp b/bindings/ir/operations/register_operation.cpp index 13280cd4ec..742f265da9 100644 --- a/bindings/ir/operations/register_operation.cpp +++ b/bindings/ir/operations/register_operation.cpp @@ -25,39 +25,134 @@ using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) void registerOperation(const nb::module_& m) { nb::class_(m, "Operation") - .def_prop_ro("name", &qc::Operation::getName) - .def_prop_rw("type_", &qc::Operation::getType, &qc::Operation::setGate) + .def_prop_ro("name", &qc::Operation::getName, + "The name of the operation.") + + .def_prop_rw("type_", &qc::Operation::getType, &qc::Operation::setGate, + "The type of the operation.") + .def_prop_rw( "targets", [](const qc::Operation& op) { return op.getTargets(); }, - &qc::Operation::setTargets) - .def_prop_ro("num_targets", &qc::Operation::getNtargets) + &qc::Operation::setTargets, R"pb(The targets of the operation. + +Note: + The notion of a target might not make sense for all types of operations.)pb") + + .def_prop_ro("num_targets", &qc::Operation::getNtargets, + "The number of targets of the operation.") + .def_prop_rw( "controls", [](const qc::Operation& op) { return op.getControls(); }, - &qc::Operation::setControls) - .def_prop_ro("num_controls", &qc::Operation::getNcontrols) - .def("add_control", &qc::Operation::addControl, "control"_a) - .def("add_controls", &qc::Operation::addControls, "controls"_a) - .def("clear_controls", &qc::Operation::clearControls) + &qc::Operation::setControls, R"pb(The controls of the operation. + +Note: + The notion of a control might not make sense for all types of operations.)pb") + + .def_prop_ro("num_controls", &qc::Operation::getNcontrols, + "The number of controls of the operation.") + + .def("add_control", &qc::Operation::addControl, "control"_a, + R"pb(Add a control to the operation. + +Args: + control: The control to add.)pb") + + .def("add_controls", &qc::Operation::addControls, "controls"_a, + R"pb(Add multiple controls to the operation. + +Args: + controls: The controls to add.)pb") + + .def("clear_controls", &qc::Operation::clearControls, + "Clear all controls of the operation.") + .def( "remove_control", [](qc::Operation& op, const qc::Control& c) { op.removeControl(c); }, - "control"_a) - .def("remove_controls", &qc::Operation::removeControls, "controls"_a) - .def("get_used_qubits", &qc::Operation::getUsedQubits) - .def("acts_on", &qc::Operation::actsOn, "qubit"_a) + "control"_a, R"pb(Remove a control from the operation. + +Args: + control: The control to remove.)pb") + + .def("remove_controls", &qc::Operation::removeControls, "controls"_a, + R"pb(Remove multiple controls from the operation. + +Args: + controls: The controls to remove.)pb") + + .def("get_used_qubits", &qc::Operation::getUsedQubits, + R"pb(Get the qubits that are used by the operation. + +Returns: + The set of qubits that are used by the operation.)pb") + + .def("acts_on", &qc::Operation::actsOn, "qubit"_a, + R"pb(Check if the operation acts on a specific qubit. + +Args: + qubit: The qubit to check. + +Returns: + True if the operation acts on the qubit, False otherwise.)pb") + .def_prop_rw( "parameter", [](const qc::Operation& op) { return op.getParameter(); }, - &qc::Operation::setParameter) - .def("is_unitary", &qc::Operation::isUnitary) - .def("is_standard_operation", &qc::Operation::isStandardOperation) - .def("is_compound_operation", &qc::Operation::isCompoundOperation) - .def("is_non_unitary_operation", &qc::Operation::isNonUnitaryOperation) - .def("is_if_else_operation", &qc::Operation::isIfElseOperation) - .def("is_symbolic_operation", &qc::Operation::isSymbolicOperation) - .def("is_controlled", &qc::Operation::isControlled) - .def("get_inverted", &qc::Operation::getInverted) - .def("invert", &qc::Operation::invert) + &qc::Operation::setParameter, R"pb(The parameters of the operation. + +Note: + The notion of a parameter might not make sense for all types of operations.)pb") + + .def("is_unitary", &qc::Operation::isUnitary, + R"pb(Check if the operation is unitary. + +Returns: + True if the operation is unitary, False otherwise.)pb") + + .def("is_standard_operation", &qc::Operation::isStandardOperation, + R"pb(Check if the operation is a :class:`StandardOperation`. + +Returns: + True if the operation is a :class:`StandardOperation`, False otherwise.)pb") + + .def("is_compound_operation", &qc::Operation::isCompoundOperation, + R"pb(Check if the operation is a :class:`CompoundOperation`. + +Returns: + True if the operation is a :class:`CompoundOperation`, False otherwise.)pb") + + .def("is_non_unitary_operation", &qc::Operation::isNonUnitaryOperation, + R"pb(Check if the operation is a :class:`NonUnitaryOperation`. + +Returns: + True if the operation is a :class:`NonUnitaryOperation`, False otherwise.)pb") + + .def("is_if_else_operation", &qc::Operation::isIfElseOperation, + R"pb(Check if the operation is a :class:`IfElseOperation`. + +Returns: + True if the operation is a :class:`IfElseOperation`, False otherwise.)pb") + + .def("is_symbolic_operation", &qc::Operation::isSymbolicOperation, + R"pb(Check if the operation is a :class:`SymbolicOperation`. + +Returns: + True if the operation is a :class:`SymbolicOperation`, False otherwise.)pb") + + .def("is_controlled", &qc::Operation::isControlled, + R"pb(Check if the operation is controlled. + +Returns: + True if the operation is controlled, False otherwise.)pb") + + .def("get_inverted", &qc::Operation::getInverted, + R"pb(Get the inverse of the operation. + +Returns: + The inverse of the operation.)pb") + + .def("invert", &qc::Operation::invert, "Invert the operation (in-place).") + .def("__eq__", [](const qc::Operation& op, const qc::Operation& other) { return op == other; }) .def("__ne__", [](const qc::Operation& op, diff --git a/bindings/ir/operations/register_optype.cpp b/bindings/ir/operations/register_optype.cpp index 69ed7dea9c..6a09be69cd 100644 --- a/bindings/ir/operations/register_optype.cpp +++ b/bindings/ir/operations/register_optype.cpp @@ -20,46 +20,215 @@ namespace nb = nanobind; void registerOptype(const nb::module_& m) { nb::enum_(m, "OpType", "enum.Enum", "Enumeration of operation types.") - .value("none", qc::OpType::None) - .value("gphase", qc::OpType::GPhase) - .value("i", qc::OpType::I) - .value("h", qc::OpType::H) - .value("x", qc::OpType::X) - .value("y", qc::OpType::Y) - .value("z", qc::OpType::Z) - .value("s", qc::OpType::S) - .value("sdg", qc::OpType::Sdg) - .value("t", qc::OpType::T) - .value("tdg", qc::OpType::Tdg) - .value("v", qc::OpType::V) - .value("vdg", qc::OpType::Vdg) - .value("u", qc::OpType::U) - .value("u2", qc::OpType::U2) - .value("p", qc::OpType::P) - .value("sx", qc::OpType::SX) - .value("sxdg", qc::OpType::SXdg) - .value("rx", qc::OpType::RX) - .value("ry", qc::OpType::RY) - .value("rz", qc::OpType::RZ) - .value("r", qc::OpType::R) - .value("swap", qc::OpType::SWAP) - .value("iswap", qc::OpType::iSWAP) - .value("iswapdg", qc::OpType::iSWAPdg) - .value("peres", qc::OpType::Peres) - .value("peresdg", qc::OpType::Peresdg) - .value("dcx", qc::OpType::DCX) - .value("ecr", qc::OpType::ECR) - .value("rxx", qc::OpType::RXX) - .value("ryy", qc::OpType::RYY) - .value("rzz", qc::OpType::RZZ) - .value("rzx", qc::OpType::RZX) - .value("xx_minus_yy", qc::OpType::XXminusYY) - .value("xx_plus_yy", qc::OpType::XXplusYY) - .value("compound", qc::OpType::Compound) - .value("measure", qc::OpType::Measure) - .value("reset", qc::OpType::Reset) - .value("barrier", qc::OpType::Barrier) - .value("if_else", qc::OpType::IfElse) + + .value("none", qc::OpType::None, R"pb(A placeholder operation. + +It is used to represent an operation that is not yet defined.)pb") + + .value("gphase", qc::OpType::GPhase, R"pb(A global phase operation. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.gphase`)pb") + + .value("i", qc::OpType::I, R"pb(An identity operation. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.i`)pb") + + .value("h", qc::OpType::H, R"pb(A Hadamard gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.h`)pb") + + .value("x", qc::OpType::X, R"pb(An X gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.x`)pb") + + .value("y", qc::OpType::Y, R"pb(A Y gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.y`)pb") + + .value("z", qc::OpType::Z, R"pb(A Z gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.z`)pb") + + .value("s", qc::OpType::S, R"pb(An S gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.s`)pb") + + .value("sdg", qc::OpType::Sdg, R"pb(An :math:`S^\dagger` gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.sdg`)pb") + + .value("t", qc::OpType::T, R"pb(A T gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.t`)pb") + + .value("tdg", qc::OpType::Tdg, R"pb(A :math:`T^\dagger` gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.tdg`)pb") + + .value("v", qc::OpType::V, R"pb(A V gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.v`)pb") + + .value("vdg", qc::OpType::Vdg, R"pb(A :math:`V^\dagger` gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.vdg`)pb") + + .value("u", qc::OpType::U, R"pb(A U gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.u`)pb") + + .value("u2", qc::OpType::U2, R"pb(A U2 gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.u2`)pb") + + .value("p", qc::OpType::P, R"pb(A phase gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.p`)pb") + + .value("sx", qc::OpType::SX, R"pb(A :math:`\sqrt{X}` gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.sx`)pb") + + .value("sxdg", qc::OpType::SXdg, R"pb(A :math:`\sqrt{X}^\dagger` gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.sxdg`)pb") + + .value("rx", qc::OpType::RX, R"pb(A :math:`R_x` gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.rx`)pb") + + .value("ry", qc::OpType::RY, R"pb(A :math:`R_y` gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.ry`)pb") + + .value("rz", qc::OpType::RZ, R"pb(A :math:`R_z` gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.rz`)pb") + + .value("r", qc::OpType::R, R"pb(An :math:`R` gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.r`)pb") + + .value("swap", qc::OpType::SWAP, R"pb(A SWAP gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.swap`)pb") + + .value("iswap", qc::OpType::iSWAP, R"pb(A iSWAP gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.iswap`)pb") + + .value("iswapdg", qc::OpType::iSWAPdg, + R"pb(A :math:`i\text{SWAP}^\dagger` gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.iswapdg`)pb") + + .value("peres", qc::OpType::Peres, R"pb(A Peres gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.peres`)pb") + + .value("peresdg", qc::OpType::Peresdg, + R"pb(A :math:`\text{Peres}^\dagger` gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.peresdg`)pb") + + .value("dcx", qc::OpType::DCX, R"pb(A DCX gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.dcx`)pb") + + .value("ecr", qc::OpType::ECR, R"pb(An ECR gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.ecr`)pb") + + .value("rxx", qc::OpType::RXX, R"pb(A :math:`R_{xx}` gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.rxx`)pb") + + .value("ryy", qc::OpType::RYY, R"pb(A :math:`R_{yy}` gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.ryy`)pb") + + .value("rzz", qc::OpType::RZZ, R"pb(A :math:`R_{zz}` gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.rzz`)pb") + + .value("rzx", qc::OpType::RZX, R"pb(A :math:`R_{zx}` gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.rzx`)pb") + + .value("xx_minus_yy", qc::OpType::XXminusYY, + R"pb(A :math:`R_{XX - YY}` gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.xx_minus_yy`)pb") + + .value("xx_plus_yy", qc::OpType::XXplusYY, + R"pb(A :math:`R_{XX + YY}` gate. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.xx_plus_yy`)pb") + + .value("compound", qc::OpType::Compound, R"pb(A compound operation. + +It is used to group multiple operations into a single operation. + +See also :class:`.CompoundOperation`)pb") + + .value("measure", qc::OpType::Measure, R"pb(A measurement operation. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.measure`)pb") + + .value("reset", qc::OpType::Reset, R"pb(A reset operation. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.reset`)pb") + + .value("barrier", qc::OpType::Barrier, R"pb(A barrier operation. + +It is used to separate operations in the circuit. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.barrier`)pb") + + .value("if_else", qc::OpType::IfElse, R"pb(An if-else operation. + +It is used to control the execution of an operation based on the value of a classical register. + +See Also: + :meth:`mqt.core.ir.QuantumComputation.if_else`)pb") + .export_values(); } diff --git a/bindings/ir/operations/register_standard_operation.cpp b/bindings/ir/operations/register_standard_operation.cpp index bcb0a8b7dc..e2dea6ec83 100644 --- a/bindings/ir/operations/register_standard_operation.cpp +++ b/bindings/ir/operations/register_standard_operation.cpp @@ -28,7 +28,19 @@ using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) void registerStandardOperation(const nb::module_& m) { - nb::class_(m, "StandardOperation") + nb::class_( + m, "StandardOperation", R"pb(Standard quantum operation. + +This class is used to represent all standard quantum operations, i.e., operations that are unitary. +This includes all possible quantum gates. +Such Operations are defined by their :class:`OpType`, the qubits (controls and targets) they act on, and their parameters. + +Args: + control: The control qubit(s) of the operation (if any). + target: The target qubit(s) of the operation. + op_type: The type of the operation. + params: The parameters of the operation (if any).)pb") + .def(nb::init<>()) .def(nb::init>(), "target"_a, "op_type"_a, "params"_a = std::vector{}) diff --git a/bindings/ir/operations/register_symbolic_operation.cpp b/bindings/ir/operations/register_symbolic_operation.cpp index ff2bfd9b3a..57fcbf9240 100644 --- a/bindings/ir/operations/register_symbolic_operation.cpp +++ b/bindings/ir/operations/register_symbolic_operation.cpp @@ -31,10 +31,20 @@ using namespace nb::literals; void registerSymbolicOperation(const nb::module_& m) { nb::class_( m, "SymbolicOperation", - "Class representing a symbolic operation." - "This encompasses all symbolic versions of `StandardOperation` that " - "involve (float) angle parameters.") - .def(nb::init<>(), "Create an empty symbolic operation.") + R"pb(Symbolic quantum operation. + +This class is used to represent quantum operations that are not yet fully defined. +This can be useful for representing operations that depend on parameters that are not yet known. +A :class:`SymbolicOperation` is defined by its :class:`OpType`, the qubits (controls and targets) it acts on, and its parameters. +The parameters can be either fixed values or symbolic expressions. + +Args: + controls: The control qubit(s) of the operation (if any). + targets: The target qubit(s) of the operation. + op_type: The type of the operation. + params: The parameters of the operation (if any).)pb") + + .def(nb::init<>()) .def(nb::init&>(), "target"_a, "op_type"_a, @@ -63,11 +73,37 @@ void registerSymbolicOperation(const nb::module_& m) { const std::vector&>(), "controls"_a, "target0"_a, "target1"_a, "op_type"_a, "params"_a = std::vector{}) - .def("get_parameter", &qc::SymbolicOperation::getParameter) - .def("get_parameters", &qc::SymbolicOperation::getParameters) + + .def("get_parameter", &qc::SymbolicOperation::getParameter, "index"_a, + R"pb(Get the parameter at the given index. + +Args: + index: The index of the parameter to get. + +Returns: + The parameter at the given index.)pb") + + .def("get_parameters", &qc::SymbolicOperation::getParameters, + R"pb(Get all parameters of the operation. + +Returns: + The parameters of the operation.)pb") + .def("get_instantiated_operation", - &qc::SymbolicOperation::getInstantiatedOperation, "assignment"_a) - .def("instantiate", &qc::SymbolicOperation::instantiate, "assignment"_a); + &qc::SymbolicOperation::getInstantiatedOperation, "assignment"_a, + R"pb(Get the instantiated operation. + +Args: + assignment: The assignment of the symbolic parameters. + +Returns: + The instantiated operation.)pb") + + .def("instantiate", &qc::SymbolicOperation::instantiate, "assignment"_a, + R"pb(Instantiate the operation (in-place). + +Args: + assignment: The assignment of the symbolic parameters.)pb"); } } // namespace mqt From 17b16eed0c3bf7ce63efa69a0be4036b25db8641 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Thu, 11 Dec 2025 00:03:43 +0100 Subject: [PATCH 025/144] Move docstrings from __init__.pyi to bindings files --- bindings/ir/register_permutation.cpp | 87 +- bindings/ir/register_quantum_computation.cpp | 1812 ++++++++++++++++-- 2 files changed, 1703 insertions(+), 196 deletions(-) diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index 2ab18ee7ff..3c95d42f09 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -27,8 +27,12 @@ using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) void registerPermutation(nb::module_& m) { - nb::class_(m, "Permutation") + nb::class_( + m, "Permutation", + "A class to represent a permutation of the qubits in a quantum circuit.") + .def(nb::init<>()) + .def( "__init__", [](qc::Permutation* self, const nb::dict& p) { @@ -39,37 +43,89 @@ void registerPermutation(nb::module_& m) { new (self) qc::Permutation(std::move(perm)); }, "perm"_a, "Create a permutation from a dictionary.") + .def("apply", nb::overload_cast(&qc::Permutation::apply, nb::const_), - "controls"_a) + "controls"_a, R"pb(Apply the permutation to a set of controls. + +Args: + controls: The set of controls to apply the permutation to. + +Returns: + The set of controls with the permutation applied.)pb") + .def("apply", nb::overload_cast(&qc::Permutation::apply, nb::const_), - "targets"_a) - .def("clear", [](qc::Permutation& p) { p.clear(); }) - .def("__getitem__", - [](const qc::Permutation& p, const qc::Qubit q) { return p.at(q); }) - .def("__setitem__", [](qc::Permutation& p, const qc::Qubit q, - const qc::Qubit r) { p[q] = r; }) - .def("__delitem__", - [](qc::Permutation& p, const qc::Qubit q) { p.erase(q); }) - .def("__len__", &qc::Permutation::size) + "targets"_a, R"pb(Apply the permutation to a list of targets. + +Args: + targets: The list of targets to apply the permutation to. + +Returns: + The list of targets with the permutation applied.)pb") + + .def( + "clear", [](qc::Permutation& p) { p.clear(); }, + "Clear the permutation of all indices and values.") + + .def( + "__getitem__", + [](const qc::Permutation& p, const qc::Qubit q) { return p.at(q); }, + "index"_a, R"pb(Get the value of the permutation at the given index. + +Args: + index: The index to get the value of the permutation at. + +Returns: + The value of the permutation at the given index.)pb") + + .def( + "__setitem__", + [](qc::Permutation& p, const qc::Qubit q, const qc::Qubit r) { + p[q] = r; + }, + "index"_a, "value"_a, + R"pb(Set the value of the permutation at the given index. + +Args: + index: The index to set the value of the permutation at. + value: The value to set the permutation at the given index to.)pb") + + .def( + "__delitem__", + [](qc::Permutation& p, const qc::Qubit q) { p.erase(q); }, "index"_a, + R"pb(Delete the value of the permutation at the given index. + + Args: + key: The index to delete the value of the permutation at.)pb") + + .def("__len__", &qc::Permutation::size, + "Return the number of indices in the permutation.") + .def("__iter__", [](const qc::Permutation& p) { - return nb::make_key_iterator(nb::type(), - "key_iterator", p.begin(), p.end()); + return nb::make_key_iterator( + nb::type(), "key_iterator", p.begin(), + p.end(), + "Return an iterator over the indices of the permutation."); }) + .def( "items", [](const qc::Permutation& p) { - return nb::make_iterator(nb::type(), - "item_iterator", p.begin(), p.end()); + return nb::make_iterator( + nb::type(), "item_iterator", p.begin(), + p.end(), + "Return an iterable over the items of the permutation."); }, nb::keep_alive<0, 1>()) + .def(nb::self == nb::self) // NOLINT(misc-redundant-expression) .def(nb::self != nb::self) // NOLINT(misc-redundant-expression) .def(nb::hash(nb::self)) + .def("__str__", [](const qc::Permutation& p) { std::stringstream ss; @@ -95,6 +151,7 @@ void registerPermutation(nb::module_& m) { ss << "})"; return ss.str(); }); + nb::implicitly_convertible(); } diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index 50cb4258c5..7661374b4e 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -52,7 +52,16 @@ void registerQuantumComputation(nb::module_& m) { return i; }; - auto qc = nb::class_(m, "QuantumComputation"); + auto qc = nb::class_( + m, "QuantumComputation", + R"pb(The main class for representing quantum computations within the MQT. + +Acts as mutable sequence of :class:`~mqt.core.ir.operations.Operation` objects, which represent the individual operations in the quantum computation. + +Args: + nq: The number of qubits in the quantum computation. + nc: The number of classical bits in the quantum computation. + seed: The seed to use for the internal random number generator.)pb"); ///--------------------------------------------------------------------------- /// \n Constructors \n @@ -61,32 +70,95 @@ void registerQuantumComputation(nb::module_& m) { "nc"_a = 0U, "seed"_a = 0U); // expose the static constructor from qasm strings or files - qc.def_static("from_qasm_str", &qasm3::Importer::imports, "qasm"_a); - qc.def_static("from_qasm", &qasm3::Importer::importf, "filename"_a); + qc.def_static("from_qasm_str", &qasm3::Importer::imports, "qasm"_a, + R"pb(Create a QuantumComputation object from an OpenQASM string. + +Args: + qasm: The OpenQASM string to create the QuantumComputation object from. + +Returns: + The QuantumComputation object created from the OpenQASM string.)pb"); + + qc.def_static("from_qasm", &qasm3::Importer::importf, "filename"_a, + R"pb(Create a QuantumComputation object from an OpenQASM file. + +Args: + filename: The filename of the OpenQASM file to create the QuantumComputation object from. + +Returns: + The QuantumComputation object created from the OpenQASM file.)pb"); ///--------------------------------------------------------------------------- /// \n General Properties \n ///--------------------------------------------------------------------------- qc.def_prop_rw("name", &qc::QuantumComputation::getName, - &qc::QuantumComputation::setName); - qc.def_prop_ro("num_qubits", &qc::QuantumComputation::getNqubits); - qc.def_prop_ro("num_ancilla_qubits", &qc::QuantumComputation::getNancillae); + &qc::QuantumComputation::setName, + "The name of the quantum computation."); + + qc.def_prop_ro("num_qubits", &qc::QuantumComputation::getNqubits, + "The total number of qubits in the quantum computation."); + + qc.def_prop_ro("num_ancilla_qubits", &qc::QuantumComputation::getNancillae, + R"pb(The number of ancilla qubits in the quantum computation. + +Note: + Ancilla qubits are qubits that always start in a fixed state (usually :math:`|0\\rangle`).)pb"); + qc.def_prop_ro("num_garbage_qubits", - &qc::QuantumComputation::getNgarbageQubits); - qc.def_prop_ro("num_measured_qubits", - &qc::QuantumComputation::getNmeasuredQubits); + &qc::QuantumComputation::getNgarbageQubits, + R"pb(The number of garbage qubits in the quantum computation. + +Note: + Garbage qubits are qubits whose final state is not relevant for the computation.)pb"); + + qc.def_prop_ro( + "num_measured_qubits", &qc::QuantumComputation::getNmeasuredQubits, + R"pb(The number of qubits that are measured in the quantum computation. + +Computed as :math:`| \text{qubits} | - | \text{garbage} |`.)pb"); + qc.def_prop_ro("num_data_qubits", - &qc::QuantumComputation::getNqubitsWithoutAncillae); - qc.def_prop_ro("num_classical_bits", &qc::QuantumComputation::getNcbits); - qc.def_prop_ro("num_ops", &qc::QuantumComputation::getNops); - qc.def("num_single_qubit_ops", &qc::QuantumComputation::getNsingleQubitOps); - qc.def("num_total_ops", &qc::QuantumComputation::getNindividualOps); - qc.def("depth", &qc::QuantumComputation::getDepth); + &qc::QuantumComputation::getNqubitsWithoutAncillae, + R"pb(The number of data qubits in the quantum computation. + +Computed as :math:`| \text{qubits} | - | \text{ancilla} |`.)pb"); + + qc.def_prop_ro("num_classical_bits", &qc::QuantumComputation::getNcbits, + "The number of classical bits in the quantum computation."); + + qc.def_prop_ro("num_ops", &qc::QuantumComputation::getNops, + "The number of operations in the quantum computation."); + + qc.def("num_single_qubit_ops", &qc::QuantumComputation::getNsingleQubitOps, + "Return the number of single-qubit operations in the quantum " + "computation."); + + qc.def("num_total_ops", &qc::QuantumComputation::getNindividualOps, + R"pb(Return the total number of operations in the quantum computation. + +Recursively counts sub-operations (e.g., from :class:`~mqt.core.ir.operations.CompoundOperation` objects).)pb"); + + qc.def("depth", &qc::QuantumComputation::getDepth, + "Return the depth of the quantum computation."); + qc.def_prop_rw("global_phase", &qc::QuantumComputation::getGlobalPhase, - &qc::QuantumComputation::gphase); - qc.def("invert", &qc::QuantumComputation::invert); - qc.def("to_operation", &qc::QuantumComputation::asOperation); + &qc::QuantumComputation::gphase, + "The global phase of the quantum computation."); + + qc.def("invert", &qc::QuantumComputation::invert, + "Invert the quantum computation in-place by inverting each operation " + "and reversing the order of operations."); + + qc.def("to_operation", &qc::QuantumComputation::asOperation, + R"pb(Convert the quantum computation to a single operation. + +This gives ownership of the operations to the resulting operation, so the quantum computation will be empty after this operation. + +When the quantum computation contains more than one operation, the resulting operation is a :class:`~mqt.core.ir.operations.CompoundOperation`. + +Returns: + The operation representing the quantum computation.)pb"); ///--------------------------------------------------------------------------- /// \n Mutable Sequence Interface \n @@ -98,7 +170,18 @@ void registerQuantumComputation(nb::module_& m) { i = wrap(i, circ.getNops()); return circ.at(static_cast(i)).get(); }, - nb::rv_policy::reference_internal, "index"_a); + nb::rv_policy::reference_internal, "index"_a, + R"pb(Get the operation at the given index. + +Note: + This gives write access to the operation at the given index. + +Args: + index: The index of the operation to get. + +Returns: + The operation at the given index.)pb"); + qc.def( "__getitem__", [](qc::QuantumComputation& circ, const nb::slice& slice) { @@ -110,7 +193,18 @@ void registerQuantumComputation(nb::module_& m) { } return ops; }, - nb::rv_policy::reference_internal, "index"_a); + nb::rv_policy::reference_internal, "index"_a, + R"pb(Get a slice of operations from the quantum computation. + +Note: + This gives write access to the operations in the given slice. + +Args: + index: The slice of operations to get. + +Returns: + The operations in the given slice.)pb"); + qc.def( "__setitem__", [&wrap](qc::QuantumComputation& circ, DiffType i, @@ -118,7 +212,12 @@ void registerQuantumComputation(nb::module_& m) { i = wrap(i, circ.getNops()); circ.at(static_cast(i)) = op.clone(); }, - "index"_a, "value"_a); + "index"_a, "value"_a, R"pb(Set the operation at the given index. + +Args: + index: The index of the operation to set. + value: The operation to set at the given index.)pb"); + qc.def( "__setitem__", [](qc::QuantumComputation& circ, const nb::slice& slice, @@ -133,14 +232,23 @@ void registerQuantumComputation(nb::module_& m) { start += step; } }, - "index"_a, "value"_a); + "index"_a, "value"_a, R"pb(Set the operations in the given slice. + +Args: + index: The slice of operations to set. + value: The operations to set in the given slice.)pb"); + qc.def( "__delitem__", [&wrap](qc::QuantumComputation& circ, DiffType i) { i = wrap(i, circ.getNops()); circ.erase(circ.begin() + i); }, - "index"_a); + "index"_a, R"pb(Delete the operation at the given index. + +Args: + index: The index of the operation to delete.)pb"); + qc.def( "__delitem__", [](qc::QuantumComputation& circ, const nb::slice& slice) { @@ -153,79 +261,214 @@ void registerQuantumComputation(nb::module_& m) { circ.erase(circ.begin() + offset); } }, - "index"_a); - qc.def("__len__", &qc::QuantumComputation::getNops); + "index"_a, R"pb(Delete the operations in the given slice. + +Args: + index: The slice of operations to delete.)pb"); + + qc.def("__len__", &qc::QuantumComputation::getNops, + "Return the number of operations in the quantum computation."); + qc.def( "insert", [](qc::QuantumComputation& circ, std::size_t idx, const qc::Operation& op) { circ.insert(circ.begin() + static_cast(idx), op.clone()); }, - "index"_a, "value"_a); + "index"_a, "value"_a, R"pb(Insert an operation at the given index. + +Args: + index: The index to insert the operation at. + value: The operation to insert.)pb"); + qc.def( "append", [](qc::QuantumComputation& circ, const qc::Operation& op) { circ.emplace_back(op.clone()); }, - "value"_a); - qc.def("reverse", &qc::QuantumComputation::reverse); - qc.def("clear", nb::overload_cast<>(&qc::QuantumComputation::reset)); + "value"_a, R"pb(Append an operation to the end of the quantum computation. + +Args: + value: The operation to append.)pb"); + + qc.def("reverse", &qc::QuantumComputation::reverse, + "Reverse the order of the operations in the quantum computation " + "(in-place)."); + + qc.def("clear", nb::overload_cast<>(&qc::QuantumComputation::reset), + "Clear the quantum computation of all operations."); ///--------------------------------------------------------------------------- /// \n (Qu)Bit Registers \n ///--------------------------------------------------------------------------- qc.def("add_qubit_register", &qc::QuantumComputation::addQubitRegister, "n"_a, - "name"_a = "q"); + "name"_a = "q", R"pb(Add a qubit register to the quantum computation. + +Args: + n: The number of qubits in the qubit register. + name: The name of the qubit register. + +Returns: + The qubit register added to the quantum computation.)pb"); + qc.def("add_classical_register", - &qc::QuantumComputation::addClassicalRegister, "n"_a, "name"_a = "c"); + &qc::QuantumComputation::addClassicalRegister, "n"_a, "name"_a = "c", + R"pb(Add a classical register to the quantum computation. + +Args: + n: The number of bits in the classical register. + name: The name of the classical register. + +Returns: + The classical register added to the quantum computation.)pb"); + qc.def("add_ancillary_register", - &qc::QuantumComputation::addAncillaryRegister, "n"_a, - "name"_a = "anc"); + &qc::QuantumComputation::addAncillaryRegister, "n"_a, "name"_a = "anc", + R"pb(Add an ancillary register to the quantum computation. + +Args: + n: The number of qubits in the ancillary register. + name: The name of the ancillary register. + +Returns: + The ancillary register added to the quantum computation.)pb"); + qc.def("unify_quantum_registers", - &qc::QuantumComputation::unifyQuantumRegisters, "name"_a = "q"); + &qc::QuantumComputation::unifyQuantumRegisters, "name"_a = "q", + R"pb(Unify all quantum registers in the quantum computation. - qc.def_prop_ro("qregs", &qc::QuantumComputation::getQuantumRegisters); - qc.def_prop_ro("cregs", &qc::QuantumComputation::getClassicalRegisters); - qc.def_prop_ro("ancregs", &qc::QuantumComputation::getAncillaRegisters); +Args: + name: The name of the unified quantum register. + +Returns: + The unified quantum register.)pb"); + + qc.def_prop_ro("qregs", &qc::QuantumComputation::getQuantumRegisters, + "The quantum registers in the quantum computation."); + + qc.def_prop_ro("cregs", &qc::QuantumComputation::getClassicalRegisters, + "The classical registers in the quantum computation."); + + qc.def_prop_ro("ancregs", &qc::QuantumComputation::getAncillaRegisters, + "The ancillary registers in the quantum computation."); ///--------------------------------------------------------------------------- /// \n Input Layout and Output Permutation \n ///--------------------------------------------------------------------------- - qc.def_rw("initial_layout", &qc::QuantumComputation::initialLayout); - qc.def_rw("output_permutation", &qc::QuantumComputation::outputPermutation); - qc.def("initialize_io_mapping", &qc::QuantumComputation::initializeIOMapping); + qc.def_rw("initial_layout", &qc::QuantumComputation::initialLayout, + R"pb(The initial layout of the qubits in the quantum computation. + +This is a permutation of the qubits in the quantum computation. +It is mainly used to track the mapping of circuit qubits to device qubits during quantum circuit compilation. +The keys are the device qubits (in which a compiled circuit is expressed in), and the values are the circuit qubits (in which the original quantum circuit is expressed in). + +Any operations in the quantum circuit are expected to be expressed in terms of the keys of the initial layout. + +Examples: + - If no initial layout is explicitly specified (which is the default), the initial layout is assumed to be the identity permutation. + - Assume a three-qubit circuit has been compiled to a four qubit device and circuit qubit 0 is mapped to device qubit 1, circuit qubit 1 is mapped to device qubit 2, and circuit qubit 2 is mapped to device qubit 3. + Then the initial layout is {1: 0, 2: 1, 3: 2}.)pb"); + + qc.def_rw( + "output_permutation", &qc::QuantumComputation::outputPermutation, + R"pb(The output permutation of the qubits in the quantum computation. + +This is a permutation of the qubits in the quantum computation. +It is mainly used to track where individual qubits end up at the end of the quantum computation, for example after a circuit has been compiled to a specific device and SWAP gates have been inserted, which permute the qubits. +The keys are the qubits in the circuit and the values are the actual qubits being measured. + +Similar to the initial layout, the keys in the output permutation are the qubits actually present in the circuit and the values are the qubits in the "original" circuit. + +Examples: + - If no output permutation is explicitly specified and the circuit does not contain measurements at the end, the output permutation is assumed to be the identity permutation. + - If the circuit contains measurements at the end, these measurements are used to infer the output permutation. + Assume a three-qubit circuit has been compiled to a four qubit device and, at the end of the circuit, circuit qubit 0 is measured into classical bit 2, circuit qubit 1 is measured into classical bit 1, and circuit qubit 3 is measured into classical bit 0. + Then the output permutation is {0: 2, 1: 1, 3: 0}.)pb"); + + qc.def("initialize_io_mapping", &qc::QuantumComputation::initializeIOMapping, + R"pb(Initialize the I/O mapping of the quantum computation. + +If no initial layout is explicitly specified, the initial layout is assumed to be the identity permutation. +If the circuit contains measurements at the end, these measurements are used to infer the output permutation.)pb"); ///--------------------------------------------------------------------------- /// \n Ancillary and Garbage Handling \n ///--------------------------------------------------------------------------- - qc.def_prop_ro("ancillary", - nb::overload_cast<>(&qc::QuantumComputation::getAncillary)); + qc.def_prop_ro( + "ancillary", nb::overload_cast<>(&qc::QuantumComputation::getAncillary), + "A list of booleans indicating whether each qubit is ancillary."); + qc.def("set_circuit_qubit_ancillary", - &qc::QuantumComputation::setLogicalQubitAncillary, "q"_a); + &qc::QuantumComputation::setLogicalQubitAncillary, "q"_a, + R"pb(Set a circuit (i.e., logical) qubit to be ancillary. + +Args: + q: The index of the circuit qubit to set as ancillary.)pb"); + qc.def("se_circuit_qubits_ancillary", &qc::QuantumComputation::setLogicalQubitsAncillary, "q_min"_a, - "q_max"_a); + "q_max"_a, + R"pb(Set a range of circuit (i.e., logical) qubits to be ancillary. + +Args: + 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.)pb"); + qc.def("is_circuit_qubit_ancillary", - &qc::QuantumComputation::logicalQubitIsAncillary, "q"_a); - qc.def_prop_ro("garbage", - nb::overload_cast<>(&qc::QuantumComputation::getGarbage)); + &qc::QuantumComputation::logicalQubitIsAncillary, "q"_a, + R"pb(Check if a circuit (i.e., logical) qubit is ancillary. + +Args: + q: The index of the circuit qubit to check. + +Returns: + True if the circuit qubit is ancillary, False otherwise.)pb"); + + qc.def_prop_ro( + "garbage", nb::overload_cast<>(&qc::QuantumComputation::getGarbage), + "A list of booleans indicating whether each qubit is garbage."); + qc.def("set_circuit_qubit_garbage", - &qc::QuantumComputation::setLogicalQubitGarbage, "q"_a); + &qc::QuantumComputation::setLogicalQubitGarbage, "q"_a, + R"pb(Set a circuit (i.e., logical) qubit to be garbage. + +Args: + q: The index of the circuit qubit to set as garbage.)pb"); + qc.def("set_circuit_qubits_garbage", - &qc::QuantumComputation::setLogicalQubitsGarbage, "q_min"_a, - "q_max"_a); + &qc::QuantumComputation::setLogicalQubitsGarbage, "q_min"_a, "q_max"_a, + R"pb(et a range of circuit (i.e., logical) qubits to be garbage. + +Args: + 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.)pb"); + qc.def("is_circuit_qubit_garbage", - &qc::QuantumComputation::logicalQubitIsGarbage, "q"_a); + &qc::QuantumComputation::logicalQubitIsGarbage, "q"_a, + R"pb(Check if a circuit (i.e., logical) qubit is garbage. + +Args: + q: The index of the circuit qubit to check. + +Returns: + True if the circuit qubit is garbage, False otherwise.)pb"); ///--------------------------------------------------------------------------- /// \n Symbolic Circuit Handling \n ///--------------------------------------------------------------------------- - qc.def_prop_ro("variables", &qc::QuantumComputation::getVariables); - qc.def("add_variable", &qc::QuantumComputation::addVariable, "var"_a); + qc.def_prop_ro("variables", &qc::QuantumComputation::getVariables, + "The set of variables in the quantum computation."); + + qc.def("add_variable", &qc::QuantumComputation::addVariable, "var"_a, + R"pb(Add a variable to the quantum computation. + +Args: + var: The variable to add.)pb"); + qc.def( "add_variables", [](qc::QuantumComputation& circ, @@ -234,37 +477,95 @@ void registerQuantumComputation(nb::module_& m) { circ.addVariable(var); } }, - "vars_"_a); - qc.def("is_variable_free", &qc::QuantumComputation::isVariableFree); - qc.def("instantiate", &qc::QuantumComputation::instantiate, "assignment"_a); - qc.def("instantiate_inplace", &qc::QuantumComputation::instantiateInplace, - "assignment"_a); + "vars_"_a, R"pb(Add multiple variables to the quantum computation. + +Args: + vars_: The variables to add.)pb"); + + qc.def("is_variable_free", &qc::QuantumComputation::isVariableFree, + R"pb(Check if the quantum computation is free of variables. + +Returns: + True if the quantum computation is free of variables, False otherwise.)pb"); + + qc.def( + "instantiate", &qc::QuantumComputation::instantiate, "assignment"_a, + R"pb(Instantiate the quantum computation with the given variable assignment. + +Args: + assignment: The variable assignment to instantiate the quantum computation with. + +Returns: + The instantiated quantum computation.)pb"); + + qc.def( + "instantiate_inplace", &qc::QuantumComputation::instantiateInplace, + "assignment"_a, + R"pb(Instantiate the quantum computation with the given variable assignment in-place. + +Args: + assignment: The variable assignment to instantiate the quantum computation with.)pb"); ///--------------------------------------------------------------------------- /// \n Output Handling \n ///--------------------------------------------------------------------------- - qc.def("qasm2_str", - [](const qc::QuantumComputation& circ) { return circ.toQASM(false); }); + qc.def( + "qasm2_str", + [](const qc::QuantumComputation& circ) { return circ.toQASM(false); }, + R"pb(Return the OpenQASM2 representation of the quantum computation as a string. + +Note: + This uses some custom extensions to OpenQASM 2.0 that allow for easier definition of multi-controlled gates. + These extensions might not be supported by all OpenQASM 2.0 parsers. + Consider using the :meth:`qasm3_str` method instead, which uses OpenQASM 3.0 that natively supports multi-controlled gates. + The export also assumes the bigger, non-standard `qelib1.inc` from Qiskit is available. + +Returns: + The OpenQASM2 representation of the quantum computation as a string.)pb"); + qc.def( "qasm2", [](const qc::QuantumComputation& circ, const std::string& filename) { circ.dump(filename, qc::Format::OpenQASM2); }, - "filename"_a); - qc.def("qasm3_str", - [](const qc::QuantumComputation& circ) { return circ.toQASM(true); }); + "filename"_a, + R"pb(Write the OpenQASM2 representation of the quantum computation to a file. + +See Also: + :meth:`qasm2_str` + +Args: + filename: The filename of the file to write the OpenQASM2 representation to.)pb"); + + qc.def( + "qasm3_str", + [](const qc::QuantumComputation& circ) { return circ.toQASM(true); }, + R"pb(Return the OpenQASM3 representation of the quantum computation as a string. + +Returns: + The OpenQASM3 representation of the quantum computation as a string.)pb"); + qc.def( "qasm3", [](const qc::QuantumComputation& circ, const std::string& filename) { circ.dump(filename, qc::Format::OpenQASM3); }, - "filename"_a); + "filename"_a, + R"pb(Write the OpenQASM3 representation of the quantum computation to a file. + +See Also: + :meth:`qasm3_str` + +Args: + filename: The filename of the file to write the OpenQASM3 representation to.)pb"); + qc.def("__str__", [](const qc::QuantumComputation& circ) { auto ss = std::stringstream(); circ.print(ss); return ss.str(); }); + qc.def("__repr__", [](const qc::QuantumComputation& circ) { auto ss = std::stringstream(); ss << "QuantumComputation(num_qubits=" << circ.getNqubits() @@ -278,131 +579,1200 @@ void registerQuantumComputation(nb::module_& m) { /// \n Operations \n ///--------------------------------------------------------------------------- -#define DEFINE_SINGLE_TARGET_OPERATION(op) \ - qc.def(#op, &qc::QuantumComputation::op, "q"_a); \ - qc.def("c" #op, &qc::QuantumComputation::c##op, "control"_a, "target"_a); \ - qc.def("mc" #op, &qc::QuantumComputation::mc##op, "controls"_a, "target"_a); - - DEFINE_SINGLE_TARGET_OPERATION(i) - DEFINE_SINGLE_TARGET_OPERATION(x) - DEFINE_SINGLE_TARGET_OPERATION(y) - DEFINE_SINGLE_TARGET_OPERATION(z) - DEFINE_SINGLE_TARGET_OPERATION(h) - DEFINE_SINGLE_TARGET_OPERATION(s) - DEFINE_SINGLE_TARGET_OPERATION(sdg) - DEFINE_SINGLE_TARGET_OPERATION(t) - DEFINE_SINGLE_TARGET_OPERATION(tdg) - DEFINE_SINGLE_TARGET_OPERATION(v) - DEFINE_SINGLE_TARGET_OPERATION(vdg) - DEFINE_SINGLE_TARGET_OPERATION(sx) - DEFINE_SINGLE_TARGET_OPERATION(sxdg) - -#define DEFINE_SINGLE_TARGET_SINGLE_PARAMETER_OPERATION(op, param) \ - qc.def(#op, &qc::QuantumComputation::op, nb::arg(#param), "q"_a); \ - qc.def("c" #op, &qc::QuantumComputation::c##op, nb::arg(#param), \ - "control"_a, "target"_a); \ - qc.def("mc" #op, &qc::QuantumComputation::mc##op, nb::arg(#param), \ - "controls"_a, "target"_a); - - DEFINE_SINGLE_TARGET_SINGLE_PARAMETER_OPERATION(rx, theta) - DEFINE_SINGLE_TARGET_SINGLE_PARAMETER_OPERATION(ry, theta) - DEFINE_SINGLE_TARGET_SINGLE_PARAMETER_OPERATION(rz, theta) - DEFINE_SINGLE_TARGET_SINGLE_PARAMETER_OPERATION(p, theta) - -#define DEFINE_SINGLE_TARGET_TWO_PARAMETER_OPERATION(op, param0, param1) \ - qc.def(#op, &qc::QuantumComputation::op, nb::arg(#param0), nb::arg(#param1), \ - "q"_a); \ - qc.def("c" #op, &qc::QuantumComputation::c##op, nb::arg(#param0), \ - nb::arg(#param1), "control"_a, "target"_a); \ - qc.def("mc" #op, &qc::QuantumComputation::mc##op, nb::arg(#param0), \ - nb::arg(#param1), "controls"_a, "target"_a); - - DEFINE_SINGLE_TARGET_TWO_PARAMETER_OPERATION(u2, phi, lambda_) - DEFINE_SINGLE_TARGET_TWO_PARAMETER_OPERATION(r, theta, phi) - -#define DEFINE_SINGLE_TARGET_THREE_PARAMETER_OPERATION(op, param0, param1, \ - param2) \ - qc.def(#op, &qc::QuantumComputation::op, nb::arg(#param0), nb::arg(#param1), \ - nb::arg(#param2), "q"_a); \ - qc.def("c" #op, &qc::QuantumComputation::c##op, nb::arg(#param0), \ - nb::arg(#param1), nb::arg(#param2), "control"_a, "target"_a); \ - qc.def("mc" #op, &qc::QuantumComputation::mc##op, nb::arg(#param0), \ - nb::arg(#param1), nb::arg(#param2), "controls"_a, "target"_a); - - DEFINE_SINGLE_TARGET_THREE_PARAMETER_OPERATION(u, theta, phi, lambda_) - -#define DEFINE_TWO_TARGET_OPERATION(op) \ - qc.def(#op, &qc::QuantumComputation::op, "target1"_a, "target2"_a); \ - qc.def("c" #op, &qc::QuantumComputation::c##op, "control"_a, "target1"_a, \ - "target2"_a); \ - qc.def("mc" #op, &qc::QuantumComputation::mc##op, "controls"_a, "target1"_a, \ - "target2"_a); - - DEFINE_TWO_TARGET_OPERATION(swap) - DEFINE_TWO_TARGET_OPERATION(dcx) - DEFINE_TWO_TARGET_OPERATION(ecr) - DEFINE_TWO_TARGET_OPERATION(iswap) - DEFINE_TWO_TARGET_OPERATION(peres) - DEFINE_TWO_TARGET_OPERATION(peresdg) - -#define DEFINE_TWO_TARGET_SINGLE_PARAMETER_OPERATION(op, param) \ - qc.def(#op, &qc::QuantumComputation::op, nb::arg(#param), "target1"_a, \ - "target2"_a); \ - qc.def("c" #op, &qc::QuantumComputation::c##op, nb::arg(#param), \ - "control"_a, "target1"_a, "target2"_a); \ - qc.def("mc" #op, &qc::QuantumComputation::mc##op, nb::arg(#param), \ - "controls"_a, "target1"_a, "target2"_a); - - DEFINE_TWO_TARGET_SINGLE_PARAMETER_OPERATION(rxx, theta) - DEFINE_TWO_TARGET_SINGLE_PARAMETER_OPERATION(ryy, theta) - DEFINE_TWO_TARGET_SINGLE_PARAMETER_OPERATION(rzz, theta) - DEFINE_TWO_TARGET_SINGLE_PARAMETER_OPERATION(rzx, theta) - -#define DEFINE_TWO_TARGET_TWO_PARAMETER_OPERATION(op, param0, param1) \ - qc.def(#op, &qc::QuantumComputation::op, nb::arg(#param0), nb::arg(#param1), \ - "target1"_a, "target2"_a); \ - qc.def("c" #op, &qc::QuantumComputation::c##op, nb::arg(#param0), \ - nb::arg(#param1), "control"_a, "target1"_a, "target2"_a); \ - qc.def("mc" #op, &qc::QuantumComputation::mc##op, nb::arg(#param0), \ - nb::arg(#param1), "controls"_a, "target1"_a, "target2"_a); - - DEFINE_TWO_TARGET_TWO_PARAMETER_OPERATION(xx_minus_yy, theta, beta) - DEFINE_TWO_TARGET_TWO_PARAMETER_OPERATION(xx_plus_yy, theta, beta) - -#undef DEFINE_SINGLE_TARGET_OPERATION -#undef DEFINE_SINGLE_TARGET_SINGLE_PARAMETER_OPERATION -#undef DEFINE_SINGLE_TARGET_TWO_PARAMETER_OPERATION -#undef DEFINE_SINGLE_TARGET_THREE_PARAMETER_OPERATION -#undef DEFINE_TWO_TARGET_OPERATION -#undef DEFINE_TWO_TARGET_SINGLE_PARAMETER_OPERATION -#undef DEFINE_TWO_TARGET_TWO_PARAMETER_OPERATION - - qc.def("gphase", &qc::QuantumComputation::gphase, "phase"_a); + // I + + qc.def("i", &qc::QuantumComputation::i, "q"_a, + R"pb(Apply an identity operation. + +.. math:: + I = \begin{pmatrix} 1 & 0 \\ 0 & 1 \end{pmatrix} + +Args: + q: The target qubit)pb"); + qc.def("ci", &qc::QuantumComputation::ci, "control"_a, "target"_a, + R"pb(Apply a controlled identity operation. + +See Also: + :meth:`i` + +Args: + control: The control qubit + target: The target qubit)pb"); + qc.def("mci", &qc::QuantumComputation::mci, "controls"_a, "target"_a, + R"pb(Apply a multi-controlled identity operation. + +See Also: + :meth:`i` + +Args: + controls: The control qubits + target: The target qubit)pb"); + + // X + + qc.def("x", &qc::QuantumComputation::x, "q"_a, + R"pb(Apply a Pauli-X gate. + +.. math:: + X = \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix} + +Args: + q: The target qubit)pb"); + qc.def("cx", &qc::QuantumComputation::cx, "control"_a, "target"_a, + R"pb(Apply a controlled Pauli-X (i.e., CNOT or CX) gate. + +See Also: + :meth:`x` + +Args: + control: The control qubit + target: The target qubit)pb"); + qc.def("mcx", &qc::QuantumComputation::mcx, "controls"_a, "target"_a, + R"pb(Apply a multi-controlled Pauli-X (i.e., Toffoli or MCX) gate. + +See Also: + :meth:`x` + +Args: + controls: The control qubits + target: The target qubit)pb"); + + // Y + + qc.def("y", &qc::QuantumComputation::y, "q"_a, + R"pb(Apply a Pauli-Y gate. + +.. math:: + Y = \begin{pmatrix} 0 & -i \\ i & 0 \end{pmatrix} + +Args: + q: The target qubit)pb"); + qc.def("cy", &qc::QuantumComputation::cy, "control"_a, "target"_a, + R"pb(Apply a controlled Pauli-Y gate. + +See Also: + :meth:`y` + +Args: + control: The control qubit + target: The target qubit)pb"); + qc.def("mcy", &qc::QuantumComputation::mcy, "controls"_a, "target"_a, + R"pb(Apply a multi-controlled Pauli-Y gate. + +See Also: + :meth:`y` + +Args: + controls: The control qubits + target: The target qubit)pb"); + + // Z + + qc.def("z", &qc::QuantumComputation::z, "q"_a, + R"pb(Apply a Pauli-Z gate. + +.. math:: + Z = \begin{pmatrix} 1 & 0 \\ 0 & -1 \end{pmatrix} + +Args: + q: The target qubit)pb"); + qc.def("cz", &qc::QuantumComputation::cz, "control"_a, "target"_a, + R"pb(Apply a controlled Pauli-Z gate. + +See Also: + :meth:`z` + +Args: + control: The control qubit + target: The target qubit)pb"); + qc.def("mcz", &qc::QuantumComputation::mcz, "controls"_a, "target"_a, + R"pb(Apply a multi-controlled Pauli-Z gate. + +See Also: + :meth:`z` + +Args: + controls: The control qubits + target: The target qubit)pb"); + + // H + + qc.def("h", &qc::QuantumComputation::h, "q"_a, + R"pb(Apply a Hadamard gate. + +.. math:: + H = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix} + +Args: + q: The target qubit)pb"); + qc.def("ch", &qc::QuantumComputation::ch, "control"_a, "target"_a, + R"pb(Apply a controlled Hadamard gate. + +See Also: + :meth:`h` + +Args: + control: The control qubit + target: The target qubit)pb"); + qc.def("mch", &qc::QuantumComputation::mch, "controls"_a, "target"_a, + R"pb(Apply a multi-controlled Hadamard gate. + +See Also: + :meth:`h` + +Args: + controls: The control qubits + target: The target qubit)pb"); + + // S + + qc.def("s", &qc::QuantumComputation::s, "q"_a, + R"pb(Apply an S (i.e., phase) gate. + +.. math:: + S = \begin{pmatrix} 1 & 0 \\ 0 & i \end{pmatrix} + +Args: + q: The target qubit)pb"); + qc.def("cs", &qc::QuantumComputation::cs, "control"_a, "target"_a, + R"pb(Apply a controlled S gate. + +See Also: + :meth:`s` + +Args: + control: The control qubit + target: The target qubit)pb"); + qc.def("mcs", &qc::QuantumComputation::mcs, "controls"_a, "target"_a, + R"pb(Apply a multi-controlled S gate. + +See Also: + :meth:`s` + +Args: + controls: The control qubits + target: The target qubit)pb"); + + // Sdg + + qc.def("sdg", &qc::QuantumComputation::sdg, "q"_a, + R"pb(Apply an :math:`S^\dagger` gate. + +.. math:: + S^\dagger = \begin{pmatrix} 1 & 0 \\ 0 & -i \end{pmatrix} + +Args: + q: The target qubit)pb"); + qc.def("csdg", &qc::QuantumComputation::csdg, "control"_a, "target"_a, + R"pb(Apply a controlled :math:`S^\dagger` gate. + +See Also: + :meth:`sdg` + +Args: + control: The control qubit + target: The target qubit)pb"); + qc.def("mcsdg", &qc::QuantumComputation::mcsdg, "controls"_a, "target"_a, + R"pb(Apply a multi-controlled :math:`S^\dagger` gate. + +See Also: + :meth:`sdg` + +Args: + controls: The control qubits + target: The target qubit)pb"); + + // T + + qc.def("t", &qc::QuantumComputation::t, "q"_a, + R"pb(Apply a T gate. + +.. math:: + T = \begin{pmatrix} 1 & 0 \\ 0 & e^{i \pi / 4} \end{pmatrix} + +Args: + q: The target qubit)pb"); + qc.def("ct", &qc::QuantumComputation::ct, "control"_a, "target"_a, + R"pb(Apply a controlled T gate. + +See Also: + :meth:`t` + +Args: + control: The control qubit + target: The target qubit)pb"); + qc.def("mct", &qc::QuantumComputation::mct, "controls"_a, "target"_a, + R"pb(Apply a multi-controlled T gate. + +See Also: + :meth:`t` + +Args: + controls: The control qubits + target: The target qubit)pb"); + + // Tdg + + qc.def("tdg", &qc::QuantumComputation::tdg, "q"_a, + R"pb(Apply a :math:`T^\dagger` gate. + +.. math:: + T^\dagger = \begin{pmatrix} 1 & 0 \\ 0 & e^{-i \pi / 4} \end{pmatrix} + +Args: + q: The target qubit)pb"); + qc.def("ctdg", &qc::QuantumComputation::ctdg, "control"_a, "target"_a, + R"pb(Apply a controlled :math:`T^\dagger` gate. + +See Also: + :meth:`tdg` + +Args: + control: The control qubit + target: The target qubit)pb"); + qc.def("mctdg", &qc::QuantumComputation::mctdg, "controls"_a, "target"_a, + R"pb(Apply a multi-controlled :math:`T^\dagger` gate. + +See Also: + :meth:`tdg` + +Args: + controls: The control qubits + target: The target qubit)pb"); + + // V + + qc.def("v", &qc::QuantumComputation::v, "q"_a, + R"pb(Apply a V gate. + +.. math:: + V = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & -i \\ -i & 1 \end{pmatrix} + +Args: + q: The target qubit)pb"); + qc.def("cv", &qc::QuantumComputation::cv, "control"_a, "target"_a, + R"pb(Apply a controlled V gate. + +See Also: + :meth:`v` + +Args: + control: The control qubit + target: The target qubit)pb"); + qc.def("mcv", &qc::QuantumComputation::mcv, "controls"_a, "target"_a, + R"pb(Apply a multi-controlled V gate. + +See Also: + :meth:`v` + +Args: + controls: The control qubits + target: The target qubit)pb"); + + // Vdg + + qc.def("vdg", &qc::QuantumComputation::vdg, "q"_a, + R"pb(Apply a :math:`V^\dagger` gate. + +.. math:: + V^\dagger = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & i \\ i & 1 \end{pmatrix} + +Args: + q: The target qubit)pb"); + qc.def("cvdg", &qc::QuantumComputation::cvdg, "control"_a, "target"_a, + R"pb(Apply a controlled :math:`V^\dagger` gate. + +See Also: + :meth:`vdg` + +Args: + control: The control qubit + target: The target qubit)pb"); + qc.def("mcvdg", &qc::QuantumComputation::mcvdg, "controls"_a, "target"_a, + R"pb(Apply a multi-controlled :math:`V^\dagger` gate. + +See Also: + :meth:`vdg` + +Args: + controls: The control qubits + target: The target qubit)pb"); + + // SX + + qc.def("sx", &qc::QuantumComputation::sx, "q"_a, + R"pb(Apply a :math:`\sqrt{X}` gate. + +.. math:: + \sqrt{X} = \frac{1}{2} \begin{pmatrix} 1 + i & 1 - i \\ 1 - i & 1 + i \end{pmatrix} + +Args: + q: The target qubit)pb"); + qc.def("csx", &qc::QuantumComputation::csx, "control"_a, "target"_a, + R"pb(Apply a controlled :math:`\sqrt{X}` gate. + +See Also: + :meth:`sx` + +Args: + control: The control qubit + target: The target qubit)pb"); + qc.def("mcsx", &qc::QuantumComputation::mcsx, "controls"_a, "target"_a, + R"pb(Apply a multi-controlled :math:`\sqrt{X}` gate. + +See Also: + :meth:`sx` + +Args: + controls: The control qubits + target: The target qubit)pb"); + + // SXdg + + qc.def("sxdg", &qc::QuantumComputation::sxdg, "q"_a, + R"pb(Apply a :math:`\sqrt{X}^\dagger` gate. + +.. math:: + \sqrt{X}^{\dagger} = \frac{1}{2} \begin{pmatrix} 1 - i & 1 + i \\ 1 + i & 1 - i \end{pmatrix} + +Args: + q: The target qubit)pb"); + qc.def("csxdg", &qc::QuantumComputation::csxdg, "control"_a, "target"_a, + R"pb(Apply a controlled :math:`\sqrt{X}^\dagger` gate. + +See Also: + :meth:`sxdg` + +Args: + control: The control qubit + target: The target qubit)pb"); + qc.def("mcsxdg", &qc::QuantumComputation::mcsxdg, "controls"_a, "target"_a, + R"pb(Apply a multi-controlled :math:`\sqrt{X}^\dagger` gate. + +See Also: + :meth:`sxdg` + +Args: + controls: The control qubits + target: The target qubit)pb"); + + // RX + + qc.def("rx", &qc::QuantumComputation::rx, "theta"_a, "q"_a, + R"pb(Apply an :math:`R_x(\theta)` gate. + +.. math:: + R_x(\theta) = e^{-i \theta X / 2} = \cos(\theta / 2) I - i \sin(\theta / 2) X + = \begin{pmatrix} \cos(\theta / 2) & -i \sin(\theta / 2) \\ -i \sin(\theta / 2) & \cos(\theta / 2) \end{pmatrix} + +Args: + theta: The rotation angle + q: The target qubit)pb"); + qc.def("crx", &qc::QuantumComputation::crx, "theta"_a, "control"_a, + "target"_a, + R"pb(Apply a controlled :math:`R_x(\theta)` gate. + +See Also: + :meth:`rx` + +Args: + theta: The rotation angle + control: The control qubit + target: The target qubit)pb"); + qc.def("mcrx", &qc::QuantumComputation::mcrx, "theta"_a, "controls"_a, + "target"_a, + R"pb(Apply a multi-controlled :math:`R_x(\theta)` gate. + +See Also: + :meth:`rx` + +Args: + theta: The rotation angle + controls: The control qubits + target: The target qubit)pb"); + + // RY + + qc.def("ry", &qc::QuantumComputation::ry, "theta"_a, "q"_a, + R"pb(Apply an :math:`R_y(\theta)` gate. + +.. math:: + R_y(\theta) = e^{-i \theta Y / 2} = \cos(\theta / 2) I - i \sin(\theta / 2) Y + = \begin{pmatrix} \cos(\theta / 2) & -\sin(\theta / 2) \\ \sin(\theta / 2) & \cos(\theta / 2) \end{pmatrix} + +Args: + theta: The rotation angle + q: The target qubit)pb"); + qc.def("cry", &qc::QuantumComputation::cry, "theta"_a, "control"_a, + "target"_a, + R"pb(Apply a controlled :math:`R_y(\theta)` gate. + +See Also: + :meth:`ry` + +Args: + theta: The rotation angle + control: The control qubit + target: The target qubit)pb"); + qc.def("mcry", &qc::QuantumComputation::mcry, "theta"_a, "controls"_a, + "target"_a, + R"pb(Apply a multi-controlled :math:`R_y(\theta)` gate. + +See Also: + :meth:`ry` + +Args: + theta: The rotation angle + controls: The control qubits + target: The target qubit)pb"); + + // RZ + + qc.def("rz", &qc::QuantumComputation::rz, "theta"_a, "q"_a, + R"pb(Apply an :math:`R_z(\theta)` gate. + +.. math:: + R_z(\theta) = e^{-i \theta Z / 2} = \begin{pmatrix} e^{-i \theta / 2} & 0 \\ 0 & e^{i \theta / 2} \end{pmatrix} + +Args: + theta: The rotation angle + q: The target qubit)pb"); + qc.def("crz", &qc::QuantumComputation::crz, "theta"_a, "control"_a, + "target"_a, + R"pb(Apply a controlled :math:`R_z(\theta)` gate. + +See Also: + :meth:`rz` + +Args: + theta: The rotation angle + control: The control qubit + target: The target qubit)pb"); + qc.def("mcrz", &qc::QuantumComputation::mcrz, "theta"_a, "controls"_a, + "target"_a, + R"pb(Apply a multi-controlled :math:`R_z(\theta)` gate. + +See Also: + :meth:`rz` + +Args: + theta: The rotation angle + controls: The control qubits + target: The target qubit)pb"); + + // P + + qc.def("p", &qc::QuantumComputation::rz, "theta"_a, "q"_a, + R"pb(Apply a phase gate. + +.. math:: + P(\theta) = \begin{pmatrix} 1 & 0 \\ 0 & e^{i \ theta} \end{pmatrix} + +Args: + theta: The rotation angle + q: The target qubit)pb"); + qc.def("cp", &qc::QuantumComputation::cp, "theta"_a, "control"_a, "target"_a, + R"pb(Apply a controlled phase gate. + +See Also: + :meth:`p` + +Args: + theta: The rotation angle + control: The control qubit + target: The target qubit)pb"); + qc.def("mcp", &qc::QuantumComputation::mcp, "theta"_a, "controls"_a, + "target"_a, + R"pb(Apply a multi-controlled phase gate. + +See Also: + :meth:`p` + +Args: + theta: The rotation angle + controls: The control qubits + target: The target qubit)pb"); + + // U2 + + qc.def("u2", &qc::QuantumComputation::u2, "phi"_a, "lambda_"_a, "q"_a, + R"pb(Apply a :math:`U_2(\phi, \lambda)` gate. + +.. math:: + U_2(\phi, \lambda) = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & -e^{i \lambda} \\ e^{i \phi} & e^{i (\phi + \lambda)} \end{pmatrix} + +Args: + phi: The rotation angle + lambda_: The rotation angle + q: The target qubit)pb"); + qc.def("cu2", &qc::QuantumComputation::cu2, "phi"_a, "lambda_"_a, "control"_a, + "target"_a, + R"pb(Apply a controlled :math:`U_2(\phi, \lambda)` gate. + +See Also: + :meth:`u2` + +Args: + phi: The rotation angle + lambda_: The rotation angle + control: The control qubit + target: The target qubit)pb"); + qc.def("mcu2", &qc::QuantumComputation::mcu2, "phi"_a, "lambda_"_a, + "controls"_a, "target"_a, + R"pb(Apply a multi-controlled :math:`U_2(\phi, \lambda)` gate. + +See Also: + :meth:`u2` + +Args: + phi: The rotation angle + lambda_: The rotation angle + controls: The control qubits + target: The target qubit)pb"); + + // R + + qc.def("r", &qc::QuantumComputation::r, "theta"_a, "phi"_a, "q"_a, + R"pb(Apply an :math:`R(\theta, \phi)` gate. + +.. math:: + R(\theta, \phi) = e^{-i \frac{\theta}{2} (\cos(\phi) X + \sin(\phi) Y)} + = \begin{pmatrix} \cos(\theta / 2) & -i e^{-i \phi} \sin(\theta / 2) \\ -i e^{i \phi} \sin(\theta / 2) & \cos(\theta / 2) \end{pmatrix} + +Args: + theta: The rotation angle + phi: The rotation angle + q: The target qubit)pb"); + qc.def("cr", &qc::QuantumComputation::cr, "theta"_a, "phi"_a, "control"_a, + "target"_a, + R"pb(Apply a controlled :math:`R(\theta, \phi)` gate. + +See Also: + :meth:`r` + +Args: + theta: The rotation angle + phi: The rotation angle + control: The control qubit + target: The target qubit)pb"); + qc.def("mcr", &qc::QuantumComputation::mcr, "theta"_a, "phi"_a, "controls"_a, + "target"_a, + R"pb(Apply a multi-controlled :math:`R(\theta, \phi)` gate. + +See Also: + :meth:`r` + +Args: + theta: The rotation angle + phi: The rotation angle + controls: The control qubits + target: The target qubit)pb"); + + // U + + qc.def("u", &qc::QuantumComputation::u, "theta"_a, "phi"_a, "lambda_"_a, + "q"_a, + R"pb(Apply a :math:`U(\theta, \phi, \lambda)` gate. + +.. math:: + U(\theta, \phi, \lambda) = \begin{pmatrix} \cos(\theta / 2) & -e^{i \lambda} \sin(\theta / 2) \\ e^{i \phi} \sin(\theta / 2) & e^{i (\phi + \lambda)}\cos(\theta / 2) \end{pmatrix} + +Args: + theta: The rotation angle + phi: The rotation angle + lambda_: The rotation angle + q: The target qubit)pb"); + qc.def("cu", &qc::QuantumComputation::cu, "theta"_a, "phi"_a, "lambda_"_a, + "control"_a, "target"_a, + R"pb(Apply a controlled :math:`U(\theta, \phi, \lambda)` gate. + +See Also: + :meth:`u` + +Args: + theta: The rotation angle + phi: The rotation angle + lambda_: The rotation angle + control: The control qubit + target: The target qubit)pb"); + qc.def("mcu", &qc::QuantumComputation::mcu, "theta"_a, "phi"_a, "lambda_"_a, + "controls"_a, "target"_a, + R"pb(Apply a multi-controlled :math:`U(\theta, \phi, \lambda)` gate. + +See Also: + :meth:`u` + +Args: + theta: The rotation angle + phi: The rotation angle + lambda_: The rotation angle + controls: The control qubits + target: The target qubit)pb"); + + // SWAP + + qc.def("swap", &qc::QuantumComputation::swap, "target1"_a, "target2"_a, + R"pb(Apply a SWAP gate. + +.. math:: + \text{SWAP} = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} + +Args: + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("cswap", &qc::QuantumComputation::cswap, "control"_a, "target1"_a, + "target2"_a, + R"pb(Apply a controlled SWAP gate. + +See Also: + :meth:`swap` + +Args: + control: The control qubit + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("mcswap", &qc::QuantumComputation::mcswap, "controls"_a, "target1"_a, + "target2"_a, + R"pb(Apply a multi-controlled SWAP gate. + +See Also: + :meth:`swap` + +Args: + controls: The control qubits + target1: The first target qubit + target2: The second target qubit)pb"); + + // DCX + + qc.def("dcx", &qc::QuantumComputation::dcx, "target1"_a, "target2"_a, + R"pb(Apply a DCX (i.e., double CNOT) gate. + +.. math:: + DCX = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \end{pmatrix} + +Args: + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("cdcx", &qc::QuantumComputation::cdcx, "control"_a, "target1"_a, + "target2"_a, + R"pb(Apply a controlled DCX gate. + +See Also: + :meth:`dcx` + +Args: + control: The control qubit + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("mcdcx", &qc::QuantumComputation::mcdcx, "controls"_a, "target1"_a, + "target2"_a, + R"pb(Apply a multi-controlled DCX gate. + +See Also: + :meth:`dcx` + +Args: + controls: The control qubits + target1: The first target qubit + target2: The second target qubit)pb"); + + // ECR + + qc.def("ecr", &qc::QuantumComputation::ecr, "target1"_a, "target2"_a, + R"pb(Apply a ECR (echoed cross-resonance) gate. + +.. math:: + ECR = \frac{1}{\sqrt{2}} \begin{pmatrix} 0 & 0 & 1 & i \\ 0 & 0 & i & 1 \\ 1 & -i & 0 & 0 \\ -i & 1 & 0 & 0 \end{pmatrix} + +Args: + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("cecr", &qc::QuantumComputation::cecr, "control"_a, "target1"_a, + "target2"_a, + R"pb(Apply a controlled ECR gate. + +See Also: + :meth:`ecr` + +Args: + control: The control qubit + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("mcecr", &qc::QuantumComputation::mcecr, "controls"_a, "target1"_a, + "target2"_a, + R"pb(Apply a multi-controlled ECR gate. + +See Also: + :meth:`ecr` + +Args: + controls: The control qubits + target1: The first target qubit + target2: The second target qubit)pb"); + + // iSWAP + + qc.def("iswap", &qc::QuantumComputation::iswap, "target1"_a, "target2"_a, + R"pb(Apply a :math:`i\text{SWAP}` gate. + +.. math:: + i\text{SWAP} = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & i & 0 \\ 0 & i & 0 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} + +Args: + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("ciswap", &qc::QuantumComputation::ciswap, "control"_a, "target1"_a, + "target2"_a, + R"pb(Apply a controlled :math:`i\text{SWAP}` gate. + +See Also: + :meth:`iswap` + +Args: + control: The control qubit + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("mciswap", &qc::QuantumComputation::mciswap, "controls"_a, "target1"_a, + "target2"_a, + R"pb(Apply a multi-controlled :math:`i\text{SWAP}` gate. + +See Also: + :meth:`iswap` + +Args: + controls: The control qubits + target1: The first target qubit + target2: The second target qubit)pb"); + + // iSWAPdg + + qc.def("iswapdg", &qc::QuantumComputation::iswapdg, "target1"_a, "target2"_a, + R"pb(Apply a :math:`i\text{SWAP}^\dagger` gate. + +.. math:: + i\text{SWAP}^\dagger = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & -i & 0 \\ 0 & -i & 0 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} + +Args: + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("ciswapdg", &qc::QuantumComputation::ciswapdg, "control"_a, + "target1"_a, "target2"_a, + R"pb(Apply a controlled :math:`i\text{SWAP}^\dagger` gate. + +See Also: + :meth:`iswapdg` + +Args: + control: The control qubit + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("mciswapdg", &qc::QuantumComputation::mciswapdg, "controls"_a, + "target1"_a, "target2"_a, + R"pb(Apply a multi-controlled :math:`i\text{SWAP}^\dagger` gate. + +See Also: + :meth:`iswapdg` + +Args: + controls: The control qubits + target1: The first target qubit + target2: The second target qubit)pb"); + + // Peres + + qc.def("peres", &qc::QuantumComputation::peres, "target1"_a, "target2"_a, + R"pb(Apply a Peres gate. + +.. math:: + \text{Peres} = \begin{pmatrix} 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \end{pmatrix} + +Args: + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("cperes", &qc::QuantumComputation::cperes, "control"_a, "target1"_a, + "target2"_a, + R"pb(Apply a controlled Peres gate. + +See Also: + :meth:`peres` + +Args: + control: The control qubit + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("mcperes", &qc::QuantumComputation::mcperes, "controls"_a, "target1"_a, + "target2"_a, + R"pb(Apply a multi-controlled Peres gate. + +See Also: + :meth:`peres` + +Args: + controls: The control qubits + target1: The first target qubit + target2: The second target qubit)pb"); + + // Peresdg + + qc.def("peresdg", &qc::QuantumComputation::peresdg, "target1"_a, "target2"_a, + R"pb(Apply a :math:`\text{Peres}^\dagger` gate. + +.. math:: + \text{Peres}^\dagger = \begin{pmatrix} 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \end{pmatrix} +Args: + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("cperesdg", &qc::QuantumComputation::cperesdg, "control"_a, + "target1"_a, "target2"_a, + R"pb(Apply a controlled :math:`\text{Peres}^\dagger` gate. + +See Also: + :meth:`peresdg` + +Args: + control: The control qubit + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("mcperesdg", &qc::QuantumComputation::mcperesdg, "controls"_a, + "target1"_a, "target2"_a, + R"pb(Apply a multi-controlled :math:`\text{Peres}^\dagger` gate. + +See Also: + :meth:`peresdg` + +Args: + controls: The control qubits + target1: The first target qubit + target2: The second target qubit)pb"); + + // RXX + + qc.def("rxx", &qc::QuantumComputation::rxx, "theta"_a, "target1"_a, + "target2"_a, + R"pb(Apply an :math:`R_{xx}(\theta)` gate. + +.. math:: + R_{xx}(\theta) = e^{-i \theta XX / 2} = \cos(\theta / 2) I \otimes I - i \sin(\theta / 2) X \otimes X + = \begin{pmatrix} \cos(\theta / 2) & 0 & 0 & -i \sin(\theta / 2) \\ + 0 & \cos(\theta / 2) & -i \sin(\theta / 2) & 0 \\ + 0 & -i \sin(\theta / 2) & \cos(\theta / 2) & 0 \\ + -i \sin(\theta / 2) & 0 & 0 & \cos(\theta / 2) \end{pmatrix} + +Args: + theta: The rotation angle + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("crxx", &qc::QuantumComputation::crxx, "theta"_a, "control"_a, + "target1"_a, "target2"_a, + R"pb(Apply a controlled :math:`R_{xx}(\theta)` gate. + +See Also: + :meth:`rxx` + +Args: + theta: The rotation angle + control: The control qubit + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("mcrxx", &qc::QuantumComputation::mcrxx, "theta"_a, "controls"_a, + "target1"_a, "target2"_a, + R"pb(Apply a multi-controlled :math:`R_{xx}(\theta)` gate. + +See Also: + :meth:`rxx` + +Args: + theta: The rotation angle + controls: The control qubits + target1: The first target qubit + target2: The second target qubit)pb"); + + // RYY + + qc.def("ryy", &qc::QuantumComputation::ryy, "theta"_a, "target1"_a, + "target2"_a, + R"pb(Apply an :math:`R_{yy}(\theta)` gate. + +.. math:: + R_{yy}(\theta) = e^{-i \theta YY / 2} = \cos(\theta / 2) I \otimes I - i \sin(\theta / 2) Y \otimes Y + = \begin{pmatrix} \cos(\theta / 2) & 0 & 0 & i \sin(\theta / 2) \\ + 0 & \cos(\theta / 2) & -i \sin(\theta / 2) & 0 \\ + 0 & -i \sin(\theta / 2) & \cos(\theta / 2) & 0 \\ + i \sin(\theta / 2) & 0 & 0 & \cos(\theta / 2) \end{pmatrix} + +Args: + theta: The rotation angle + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("cryy", &qc::QuantumComputation::cryy, "theta"_a, "control"_a, + "target1"_a, "target2"_a, + R"pb(Apply a controlled :math:`R_{yy}(\theta)` gate. + +See Also: + :meth:`ryy` + +Args: + theta: The rotation angle + control: The control qubit + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("mcryy", &qc::QuantumComputation::mcryy, "theta"_a, "controls"_a, + "target1"_a, "target2"_a, + R"pb(Apply a multi-controlled :math:`R_{yy}(\theta)` gate. + +See Also: + :meth:`ryy` + +Args: + theta: The rotation angle + controls: The control qubits + target1: The first target qubit + target2: The second target qubit)pb"); + + // RZX + + qc.def("rzx", &qc::QuantumComputation::rzx, "theta"_a, "target1"_a, + "target2"_a, + R"pb(Apply an :math:`R_{zx}(\theta)` gate. + +.. math:: + R_{zx}(\theta) = e^{-i \theta ZX / 2} = \cos(\theta / 2) I \otimes I - i \sin(\theta / 2) Z \otimes X + = \begin{pmatrix} \cos(\theta/2) & -i \sin(\theta/2) & 0 & 0 \\ + -i \sin(\theta/2) & \cos(\theta/2) & 0 & 0 \\ + 0 & 0 & \cos(\theta/2) & i \sin(\theta/2) \\ + 0 & 0 & i \sin(\theta/2) & \cos(\theta/2) \end{pmatrix} + +Args: + theta: The rotation angle + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("crzx", &qc::QuantumComputation::crzx, "theta"_a, "control"_a, + "target1"_a, "target2"_a, + R"pb(Apply a controlled :math:`R_{zx}(\theta)` gate. + +See Also: + :meth:`rzx` + +Args: + theta: The rotation angle + control: The control qubit + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("mcrzx", &qc::QuantumComputation::mcrzx, "theta"_a, "controls"_a, + "target1"_a, "target2"_a, + R"pb(Apply a multi-controlled :math:`R_{zx}(\theta)` gate. + +See Also: + :meth:`rzx` + +Args: + theta: The rotation angle + controls: The control qubits + target1: The first target qubit + target2: The second target qubit)pb"); + + // RZZ + + qc.def("rzz", &qc::QuantumComputation::rzz, "theta"_a, "target1"_a, + "target2"_a, + R"pb(Apply an :math:`R_{zz}(\theta)` gate. + +.. math:: + R_{zz}(\theta) = e^{-i \theta ZZ / 2} + = \begin{pmatrix} e^{-i \theta / 2} & 0 & 0 & 0 \\ + 0 & e^{i \theta / 2} & 0 & 0 \\ + 0 & 0 & e^{i \theta / 2} & 0 \\ + 0 & 0 & 0 & e^{-i \theta / 2} \end{pmatrix} + +Args: + theta: The rotation angle + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("crzz", &qc::QuantumComputation::crzz, "theta"_a, "control"_a, + "target1"_a, "target2"_a, + R"pb(Apply a controlled :math:`R_{zz}(\theta)` gate. + +See Also: + :meth:`rzz` + +Args: + theta: The rotation angle + control: The control qubit + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("mcrzz", &qc::QuantumComputation::mcrzz, "theta"_a, "controls"_a, + "target1"_a, "target2"_a, + R"pb(Apply a multi-controlled :math:`R_{zz}(\theta)` gate. + +See Also: + :meth:`rzz` + +Args: + theta: The rotation angle + controls: The control qubits + target1: The first target qubit + target2: The second target qubit)pb"); + + // XXMinusYY + + qc.def("xx_minus_yy", &qc::QuantumComputation::xx_minus_yy, "theta"_a, + "beta"_a, "target1"_a, "target2"_a, + R"pb(Apply an :math:`R_{XX - YY}(\theta, \beta)` gate. + +.. math:: + R_{XX - YY}(\theta, \beta) = R_{z_2}(\beta) \cdot e^{-i \frac{\theta}{2} \frac{XX - YY}{2}} \cdot R_{z_2}(-\beta) + = \begin{pmatrix} \cos(\theta / 2) & 0 & 0 & -i \sin(\theta / 2) e^{-i \beta} \\ + 0 & 1 & 0 & 0 \\ + 0 & 0 & 1 & 0 \\ + -i \sin(\theta / 2) e^{i \beta} & 0 & 0 & \cos(\theta / 2) \end{pmatrix} + +Args: + theta: The rotation angle + beta: The rotation angle + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("cxx_minus_yy", &qc::QuantumComputation::cxx_minus_yy, "theta"_a, + "beta"_a, "control"_a, "target1"_a, "target2"_a, + R"pb(Apply a controlled :math:`R_{XX - YY}(\theta, \beta)` gate. + +See Also: + :meth:`xx_minus_yy` + +Args: + theta: The rotation angle + control: The control qubit + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("mcxx_minus_yy", &qc::QuantumComputation::mcxx_minus_yy, "theta"_a, + "beta"_a, "controls"_a, "target1"_a, "target2"_a, + R"pb(Apply a multi-controlled :math:`R_{XX - YY}(\theta, \beta)` gate. + +See Also: + :meth:`xx_minus_yy` + +Args: + theta: The rotation angle + controls: The control qubits + target1: The first target qubit + target2: The second target qubit)pb"); + + // XXPlusYY + + qc.def("xx_plus_yy", &qc::QuantumComputation::xx_plus_yy, "theta"_a, "beta"_a, + "target1"_a, "target2"_a, + R"pb(Apply an :math:`R_{XX + YY}(\theta, \beta)` gate. + +.. math:: + R_{XX + YY}(\theta, \beta) = R_{z_1}(\beta) \cdot e^{-i \frac{\theta}{2} \frac{XX + YY}{2}} \cdot R_{z_1}(-\beta) + = \begin{pmatrix} 1 & 0 & 0 & 0 \\ + 0 & \cos(\theta / 2) & -i \sin(\theta / 2) e^{-i \beta} & 0 \\ + 0 & -i \sin(\theta / 2) e^{i \beta} & \cos(\theta / 2) & 0 \\ + 0 & 0 & 0 & 1 \end{pmatrix} + +Args: + theta: The rotation angle + beta: The rotation angle + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("cxx_plus_yy", &qc::QuantumComputation::cxx_plus_yy, "theta"_a, + "beta"_a, "control"_a, "target1"_a, "target2"_a, + R"pb(Apply a controlled :math:`R_{XX + YY}(\theta, \beta)` gate. + +See Also: + :meth:`xx_plus_yy` + +Args: + theta: The rotation angle + control: The control qubit + target1: The first target qubit + target2: The second target qubit)pb"); + qc.def("mcxx_plus_yy", &qc::QuantumComputation::mcxx_plus_yy, "theta"_a, + "beta"_a, "controls"_a, "target1"_a, "target2"_a, + R"pb(Apply a multi-controlled :math:`R_{XX + YY}(\theta, \beta)` gate. + +See Also: + :meth:`xx_plus_yy` + +Args: + theta: The rotation angle + controls: The control qubits + target1: The first target qubit + target2: The second target qubit)pb"); + + qc.def("gphase", &qc::QuantumComputation::gphase, "phase"_a, + R"pb(Apply a global phase gate. + +.. math:: + GPhase(\theta) = (e^{i \theta}) + +Args: + theta: The rotation angle)pb"); qc.def("measure", nb::overload_cast( &qc::QuantumComputation::measure), - "qubit"_a, "cbit"_a); + "qubit"_a, "cbit"_a, + R"pb(Measure a qubit and store the result in a classical bit. + +Args: + qubit: The qubit to measure + cbit: The classical bit to store the result)pb"); + qc.def("measure", nb::overload_cast&, const std::vector&>( &qc::QuantumComputation::measure), - "qubits"_a, "cbits"_a); + "qubits"_a, "cbits"_a, + R"pb(Measure multiple qubits and store the results in classical bits. + +This method is equivalent to calling :meth:`measure` multiple times. + +Args: + qubits: The qubits to measure + cbits: The classical bits to store the results)pb"); qc.def("measure_all", &qc::QuantumComputation::measureAll, nb::kw_only(), - "add_bits"_a = true); + "add_bits"_a = true, + R"pb(Measure all qubits and store the results in classical bits. + +Details: + If `add_bits` is `True`, a new classical register (named "`meas`") with the same size as the number of qubits will be added to the circuit and the results will be stored in it. + If `add_bits` is `False`, the classical register must already exist and have a sufficient number of bits to store the results. + +Args: + add_bits: Whether to explicitly add a classical register)pb"); qc.def("reset", nb::overload_cast(&qc::QuantumComputation::reset), - "q"_a); + "q"_a, R"pb(Add a reset operation to the circuit. + +Args: + q: The qubit to reset)pb"); + qc.def("reset", nb::overload_cast&>( &qc::QuantumComputation::reset), - "qubits"_a); + "qubits"_a, R"pb(Add a reset operation to the circuit. + +Args: + qubits: The qubits to reset)pb"); + + qc.def("barrier", nb::overload_cast<>(&qc::QuantumComputation::barrier), + "Add a barrier to the circuit."); - qc.def("barrier", nb::overload_cast<>(&qc::QuantumComputation::barrier)); qc.def("barrier", - nb::overload_cast(&qc::QuantumComputation::barrier), "q"_a); - qc.def("barrier", nb::overload_cast&>( - &qc::QuantumComputation::barrier)); + nb::overload_cast(&qc::QuantumComputation::barrier), "q"_a, + R"pb(Add a barrier to the circuit. + +Args: + q: The qubit to add the barrier to)pb"); + + qc.def("barrier", + nb::overload_cast&>( + &qc::QuantumComputation::barrier), + "qubits"_a, R"pb(Add a barrier to the circuit. + +Args: + qubits: The qubits to add the barrier to)pb"); qc.def( "if_else", @@ -418,7 +1788,16 @@ void registerQuantumComputation(nb::module_& m) { expectedValue, kind); }, "then_operation"_a, "else_operation"_a, "control_register"_a, - "expected_value"_a = 1U, "comparison_kind"_a = qc::ComparisonKind::Eq); + "expected_value"_a = 1U, "comparison_kind"_a = qc::ComparisonKind::Eq, + R"pb(Add an if-else operation to the circuit. + +Args: + then_operation: The operation to apply if the condition is met + else_operation: The operation to apply if the condition is not met + control_register: The classical register to check against + expected_value: The expected value of the control register + comparison_kind: The kind of comparison to perform)pb"); + qc.def( "if_else", [](qc::QuantumComputation& self, qc::Operation* thenOp, @@ -433,7 +1812,15 @@ void registerQuantumComputation(nb::module_& m) { expectedValue, kind); }, "then_operation"_a, "else_operation"_a, "control_bit"_a, - "expected_value"_a = 1U, "comparison_kind"_a = qc::ComparisonKind::Eq); + "expected_value"_a = 1U, "comparison_kind"_a = qc::ComparisonKind::Eq, + R"pb(Add an if-else operation to the circuit. + +Args: + then_operation: The operation to apply if the condition is met + else_operation: The operation to apply if the condition is not met + control_bit: The index of the classical bit to check against + expected_value: The expected value of the control bit + comparison_kind: The kind of comparison to perform)pb"); qc.def( "if_", @@ -443,7 +1830,17 @@ void registerQuantumComputation(nb::module_& m) { &qc::QuantumComputation::if_), "op_type"_a, "target"_a, "control_register"_a, "expected_value"_a = 1U, "comparison_kind"_a = qc::ComparisonKind::Eq, - "params"_a = std::vector{}); + "params"_a = std::vector{}, + R"pb(Add an if operartion to the circuit. + +Args: + op_type: The operation to apply + target: The target qubit + control_register: The classical register to check against + expected_value: The expected value of the control register + comparison_kind: The kind of comparison to perform + params: The parameters of the operation)pb"); + qc.def( "if_", nb::overload_cast{}); + "params"_a = std::vector{}, + R"pb(Add an if operartion to the circuit. + +Args: + op_type: The operation to apply + target: The target qubit + control: The control qubit + control_register: The classical register to check against + expected_value: The expected value of the control register + comparison_kind: The kind of comparison to perform + params: The parameters of the operation.)pb"); + qc.def( "if_", nb::overload_cast{}); + "params"_a = std::vector{}, + R"pb(Add an if operartion to the circuit. + + Args: + op_type: The operation to apply + target: The target qubit + controls: The control qubits + control_register: The classical register to check against + expected_value: The expected value of the control register + comparison_kind: The kind of comparison to perform + params: The parameters of the operation.)pb"); + qc.def("if_", nb::overload_cast{}); + "params"_a = std::vector{}, + R"pb(Add an if operartion to the circuit. + +Args: + op_type: The operation to apply + target: The target qubit + control_bit: The index of the classical bit to check against + expected_value: The expected value of the control bit + comparison_kind: The kind of comparison to perform + params: The parameters of the operation.)pb"); + qc.def("if_", nb::overload_cast{}); + "params"_a = std::vector{}, + R"pb(Add an if operartion to the circuit. + +Args: + op_type: The operation to apply + target: The target qubit + control: The control qubit + control_bit: The index of the classical bit to check against + expected_value: The expected value of the control bit + comparison_kind: The kind of comparison to perform + params: The parameters of the operation.)pb"); + qc.def( "if_", nb::overload_cast{}); + "params"_a = std::vector{}, + R"pb(Add an if operartion to the circuit. + +Args: + op_type: The operation to apply + target: The target qubit + controls: The control qubits + control_bit: The index of the classical bit to check against + expected_value: The expected value of the control bit + comparison_kind: The kind of comparison to perform + params: The parameters of the operation.)pb"); } } // namespace mqt From 0b540bd8b7801f67b4a9c55532a4c8ed2346f6c2 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Thu, 11 Dec 2025 21:00:57 +0100 Subject: [PATCH 026/144] Move docstrings from registers.pyi to bindings file --- bindings/ir/register_registers.cpp | 71 +++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 15 deletions(-) diff --git a/bindings/ir/register_registers.cpp b/bindings/ir/register_registers.cpp index fe470d95f2..7d8f9987eb 100644 --- a/bindings/ir/register_registers.cpp +++ b/bindings/ir/register_registers.cpp @@ -24,62 +24,103 @@ using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) void registerRegisters(nb::module_& m) { - nb::class_(m, "QuantumRegister") + nb::class_( + m, "QuantumRegister", R"pb(A class to represent a collection of qubits. + +Args: + start: The starting index of the quantum register. + size: The number of qubits in the quantum register. + name: The name of the quantum register. A name will be generated if not provided.)pb") + .def(nb::init(), "start"_a, "size"_a, "name"_a = "") - .def_prop_ro("name", - [](const qc::QuantumRegister& reg) { return reg.getName(); }) + + .def_prop_ro( + "name", [](const qc::QuantumRegister& reg) { return reg.getName(); }, + "The name of the quantum register.") + .def_prop_rw( "start", [](const qc::QuantumRegister& reg) { return reg.getStartIndex(); }, [](qc::QuantumRegister& reg, const qc::Qubit start) { reg.getStartIndex() = start; - }) + }, + "The index of the first qubit in the quantum register.") .def_prop_rw( "size", [](const qc::QuantumRegister& reg) { return reg.getSize(); }, [](qc::QuantumRegister& reg, const std::size_t size) { reg.getSize() = size; - }) + }, + "The number of qubits in the quantum register.") .def_prop_ro( "end", - [](const qc::QuantumRegister& reg) { return reg.getEndIndex(); }) + [](const qc::QuantumRegister& reg) { return reg.getEndIndex(); }, + "Index of the last qubit in the quantum register.") + .def(nb::self == nb::self) // NOLINT(misc-redundant-expression) .def(nb::self != nb::self) // NOLINT(misc-redundant-expression) .def(hash(nb::self)) - .def("__getitem__", &qc::QuantumRegister::getGlobalIndex, "key"_a) - .def("__contains__", &qc::QuantumRegister::contains) + + .def("__getitem__", &qc::QuantumRegister::getGlobalIndex, "key"_a, + "Get the qubit at the specified index.") + + .def("__contains__", &qc::QuantumRegister::contains, "item"_a, + "Check if the quantum register contains a qubit.") + .def("__repr__", [](const qc::QuantumRegister& reg) { return "QuantumRegister(name=" + reg.getName() + ", start=" + std::to_string(reg.getStartIndex()) + ", size=" + std::to_string(reg.getSize()) + ")"; }); - nb::class_(m, "ClassicalRegister") + nb::class_( + m, "ClassicalRegister", + R"pb(A class to represent a collection of classical bits. + +Args: + start: The starting index of the classical register. + size: The number of bits in the classical register. + name: The name of the classical register. A name will be generated if not provided.)pb") + .def(nb::init(), "start"_a, "size"_a, "name"_a = "") + .def_prop_ro( "name", - [](const qc::ClassicalRegister& reg) { return reg.getName(); }) + [](const qc::ClassicalRegister& reg) { return reg.getName(); }, + "The name of the classical register.") + .def_prop_rw( "start", [](const qc::ClassicalRegister& reg) { return reg.getStartIndex(); }, [](qc::ClassicalRegister& reg, const qc::Bit start) { reg.getStartIndex() = start; - }) + }, + "The index of the first bit in the classical register.") + .def_prop_rw( "size", [](const qc::ClassicalRegister& reg) { return reg.getSize(); }, [](qc::ClassicalRegister& reg, const std::size_t size) { reg.getSize() = size; - }) + }, + "The number of bits in the classical register.") + .def_prop_ro( "end", - [](const qc::ClassicalRegister& reg) { return reg.getEndIndex(); }) + [](const qc::ClassicalRegister& reg) { return reg.getEndIndex(); }, + "Index of the last bit in the classical register.") + .def(nb::self == nb::self) // NOLINT(misc-redundant-expression) .def(nb::self != nb::self) // NOLINT(misc-redundant-expression) .def(nb::hash(nb::self)) - .def("__getitem__", &qc::ClassicalRegister::getGlobalIndex, "key"_a) - .def("__contains__", &qc::ClassicalRegister::contains) + + .def("__getitem__", &qc::ClassicalRegister::getGlobalIndex, "key"_a, + "Get the bit at the specified index.") + + .def("__contains__", &qc::ClassicalRegister::contains, "item"_a, + "Check if the classical register contains a bit.") + .def("__repr__", [](const qc::ClassicalRegister& reg) { return "ClassicalRegister(name=" + reg.getName() + ", start=" + std::to_string(reg.getStartIndex()) + From 22d17ac77f7bc150be07ec89d5069b76ffc0cad3 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Thu, 11 Dec 2025 21:11:05 +0100 Subject: [PATCH 027/144] Move docstrings from symbolic.pyi to bindings files --- bindings/ir/symbolic/register_expression.cpp | 71 +++++++++++++++----- bindings/ir/symbolic/register_term.cpp | 41 +++++++++-- bindings/ir/symbolic/register_variable.cpp | 13 +++- 3 files changed, 100 insertions(+), 25 deletions(-) diff --git a/bindings/ir/symbolic/register_expression.cpp b/bindings/ir/symbolic/register_expression.cpp index aef8c65f81..5cad69e1de 100644 --- a/bindings/ir/symbolic/register_expression.cpp +++ b/bindings/ir/symbolic/register_expression.cpp @@ -26,7 +26,18 @@ using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) void registerExpression(nb::module_& m) { - nb::class_>(m, "Expression") + nb::class_>( + m, "Expression", + R"pb(A symbolic expression which consists of a sum of terms and a constant. + +The expression is of the form :math:`constant + term_1 + term_2 + \dots + term_n`. +Alternatively, an expression can be created with a single term and a constant or just a constant. + +Args: + terms: The list of terms. + constant: The constant.)pb") + + .def(nb::init(), "constant"_a = 0.0) .def( "__init__", [](sym::Expression* self, @@ -42,9 +53,10 @@ void registerExpression(nb::module_& m) { std::vector>{term}, constant); }, "term"_a, "constant"_a = 0.0) - .def(nb::init(), "constant"_a = 0.0) + .def_prop_rw("constant", &sym::Expression::getConst, - &sym::Expression::setConst) + &sym::Expression::setConst, + "The constant of the expression.") .def( "__iter__", [](const sym::Expression& expr) { @@ -53,22 +65,45 @@ void registerExpression(nb::module_& m) { expr.begin(), expr.end()); }, nb::keep_alive<0, 1>()) - .def("__getitem__", - [](const sym::Expression& expr, - const std::size_t idx) { - if (idx >= expr.numTerms()) { - throw nb::index_error(); - } - return expr.getTerms()[idx]; - }) - .def("is_zero", &sym::Expression::isZero) - .def("is_constant", &sym::Expression::isConstant) - .def("num_terms", &sym::Expression::numTerms) + + .def( + "__getitem__", + [](const sym::Expression& expr, + const std::size_t idx) { + if (idx >= expr.numTerms()) { + throw nb::index_error(); + } + return expr.getTerms()[idx]; + }, + "key"_a) + + .def("is_zero", &sym::Expression::isZero, + "Check if the expression is zero.") + + .def("is_constant", &sym::Expression::isConstant, + "Check if the expression is a constant.") + + .def("num_terms", &sym::Expression::numTerms, + "The number of terms in the expression.") + .def("__len__", &sym::Expression::numTerms) - .def_prop_ro("terms", &sym::Expression::getTerms) - .def_prop_ro("variables", &sym::Expression::getVariables) + + .def_prop_ro("terms", &sym::Expression::getTerms, + "The terms of the expression.") + + .def_prop_ro("variables", &sym::Expression::getVariables, + "The variables in the expression.") + .def("evaluate", &sym::Expression::evaluate, - "assignment"_a) + "assignment"_a, + R"pb(Evaluate the expression with a given variable assignment. + +Args: + assignment: The variable assignment. + +Returns: + The evaluated value of the expression.)pb") + // addition operators .def(nb::self + nb::self) .def(nb::self + double()) @@ -97,12 +132,14 @@ void registerExpression(nb::module_& m) { .def(nb::self == nb::self) // NOLINT(misc-redundant-expression) .def(nb::self != nb::self) // NOLINT(misc-redundant-expression) .def(nb::hash(nb::self)) + .def("__str__", [](const sym::Expression& expr) { std::stringstream ss; ss << expr; return ss.str(); }) + .def("__repr__", [](const sym::Expression& expr) { std::stringstream ss; ss << expr; diff --git a/bindings/ir/symbolic/register_term.cpp b/bindings/ir/symbolic/register_term.cpp index d5072b6ba0..d6a33d84cd 100644 --- a/bindings/ir/symbolic/register_term.cpp +++ b/bindings/ir/symbolic/register_term.cpp @@ -22,14 +22,41 @@ using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) void registerTerm(nb::module_& m) { - nb::class_>(m, "Term") + nb::class_>( + m, "Term", + R"pb(A symbolic term which consists of a variable with a given coefficient. + +Args: + variable: The variable of the term. + coefficient: The coefficient of the term.)pb") + .def(nb::init(), "variable"_a, "coefficient"_a = 1.0) - .def_prop_ro("variable", &sym::Term::getVar) - .def_prop_ro("coefficient", &sym::Term::getCoeff) - .def("has_zero_coefficient", &sym::Term::hasZeroCoeff) - .def("add_coefficient", &sym::Term::addCoeff, "coeff"_a) - .def("evaluate", &sym::Term::evaluate, "assignment"_a) + + .def_prop_ro("variable", &sym::Term::getVar, + "The variable of the term.") + + .def_prop_ro("coefficient", &sym::Term::getCoeff, + "The coefficient of the term.") + + .def("has_zero_coefficient", &sym::Term::hasZeroCoeff, + "Check if the coefficient of the term is zero.") + + .def("add_coefficient", &sym::Term::addCoeff, "coeff"_a, + R"pb(Add a coefficient to the coefficient of this term. + +Args: + coeff: The coefficient to add.)pb") + + .def("evaluate", &sym::Term::evaluate, "assignment"_a, + R"pb(Evaluate the term with a given variable assignment. + +Args: + assignment: The variable assignment. + +Returns: + The evaluated value of the term.)pb") + .def(nb::self * double()) .def(double() * nb::self) .def(nb::self / double()) @@ -37,12 +64,14 @@ void registerTerm(nb::module_& m) { .def(nb::self == nb::self) // NOLINT(misc-redundant-expression) .def(nb::self != nb::self) // NOLINT(misc-redundant-expression) .def(nb::hash(nb::self)) + .def("__str__", [](const sym::Term& term) { std::stringstream ss; ss << term; return ss.str(); }) + .def("__repr__", [](const sym::Term& term) { std::stringstream ss; ss << term; diff --git a/bindings/ir/symbolic/register_variable.cpp b/bindings/ir/symbolic/register_variable.cpp index b1692643ba..cd9bf7a33d 100644 --- a/bindings/ir/symbolic/register_variable.cpp +++ b/bindings/ir/symbolic/register_variable.cpp @@ -22,11 +22,20 @@ using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) void registerVariable(nb::module_& m) { - nb::class_(m, "Variable") + nb::class_(m, "Variable", R"pb(A symbolic 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. + +Args: + name: The name of the variable.)pb") + .def(nb::init(), "name"_a = "") - .def_prop_ro("name", &sym::Variable::getName) + + .def_prop_ro("name", &sym::Variable::getName, "The name of the variable.") + .def("__str__", &sym::Variable::getName) .def("__repr__", &sym::Variable::getName) + .def(nb::self == nb::self) // NOLINT(misc-redundant-expression) .def(nb::self != nb::self) // NOLINT(misc-redundant-expression) .def(nb::hash(nb::self)) From 85d7e4202ff7553ab204bcae6652cb5e82ae1af9 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Thu, 11 Dec 2025 21:42:49 +0100 Subject: [PATCH 028/144] Move docstrings from fomac.pyi to bindings file --- bindings/fomac/fomac.cpp | 357 ++++++++++++++++++++++++++++++++------- 1 file changed, 292 insertions(+), 65 deletions(-) diff --git a/bindings/fomac/fomac.cpp b/bindings/fomac/fomac.cpp index 9c9d6e3f44..7fff18e222 100644 --- a/bindings/fomac/fomac.cpp +++ b/bindings/fomac/fomac.cpp @@ -33,7 +33,12 @@ using namespace nb::literals; NB_MODULE(MQT_CORE_MODULE_NAME, m) { // Session class - auto session = nb::class_(m, "Session"); + auto session = nb::class_( + m, "Session", R"pb(A FoMaC session for managing QDMI devices. + +Allows creating isolated sessions with independent authentication settings. +All authentication parameters are optional and can be provided as keyword arguments to the constructor.)pb"); + session.def( "__init__", [](fomac::Session* self, std::optional token, @@ -63,26 +68,105 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { "password"_a = std::nullopt, "project_id"_a = std::nullopt, "custom1"_a = std::nullopt, "custom2"_a = std::nullopt, "custom3"_a = std::nullopt, "custom4"_a = std::nullopt, - "custom5"_a = std::nullopt); - session.def("get_devices", &fomac::Session::getDevices); + "custom5"_a = std::nullopt, + R"pb(Create a new FoMaC session with optional authentication. + +Args: + token: Authentication token + auth_file: Path to file containing authentication information + auth_url: URL to authentication server + username: Username for authentication + password: Password for authentication + project_id: Project ID for session + custom1: Custom configuration parameter 1 + custom2: Custom configuration parameter 2 + custom3: Custom configuration parameter 3 + custom4: Custom configuration parameter 4 + custom5: Custom configuration parameter 5 + +Raises: + RuntimeError: If auth_file does not exist + RuntimeError: If auth_url has invalid format + +Example: + >>> from mqt.core.fomac import Session + >>> # Session without authentication + >>> session = Session() + >>> devices = session.get_devices() + >>> + >>> # Session with token authentication + >>> session = Session(token="my_secret_token") + >>> devices = session.get_devices() + >>> + >>> # Session with file-based authentication + >>> session = Session(auth_file="/path/to/auth.json") + >>> devices = session.get_devices() + >>> + >>> # Session with multiple parameters + >>> session = Session( + ... auth_url="https://auth.example.com", username="user", password="pass", project_id="project-123" + ... ) + >>> devices = session.get_devices())pb"); + + session.def("get_devices", &fomac::Session::getDevices, + R"pb(Get available devices from this session. + +Returns: + List of available devices.)pb"); // Job class - auto job = nb::class_(m, "Job"); - job.def("check", &fomac::Session::Job::check); - job.def("wait", &fomac::Session::Job::wait, "timeout"_a = 0); - job.def("cancel", &fomac::Session::Job::cancel); - job.def("get_shots", &fomac::Session::Job::getShots); - job.def("get_counts", &fomac::Session::Job::getCounts); - job.def("get_dense_statevector", &fomac::Session::Job::getDenseStateVector); + auto job = nb::class_( + m, "Job", "A job represents a submitted quantum program execution."); + + job.def("check", &fomac::Session::Job::check, + "Returns the current status of the job."); + + job.def("wait", &fomac::Session::Job::wait, "timeout"_a = 0, + R"pb(Waits for the job to complete. + +Args: + timeout: The maximum time to wait in seconds. If 0, waits indefinitely. + +Returns: + True if the job completed within the timeout, False otherwise.)pb"); + + job.def("cancel", &fomac::Session::Job::cancel, "Cancels the job."); + + job.def("get_shots", &fomac::Session::Job::getShots, + "Returns the raw shot results from the job."); + + job.def("get_counts", &fomac::Session::Job::getCounts, + "Returns the measurement counts from the job."); + + job.def("get_dense_statevector", &fomac::Session::Job::getDenseStateVector, + "Returns the dense statevector from the job (typically only " + "available from simulator devices)."); + job.def("get_dense_probabilities", - &fomac::Session::Job::getDenseProbabilities); - job.def("get_sparse_statevector", &fomac::Session::Job::getSparseStateVector); + &fomac::Session::Job::getDenseProbabilities, + "Returns the dense probabilities from the job (typically only " + "available from simulator devices)."); + + job.def("get_sparse_statevector", &fomac::Session::Job::getSparseStateVector, + "Returns the sparse statevector from the job (typically only " + "available from simulator devices)."); + job.def("get_sparse_probabilities", - &fomac::Session::Job::getSparseProbabilities); - job.def_prop_ro("id", &fomac::Session::Job::getId); - job.def_prop_ro("program_format", &fomac::Session::Job::getProgramFormat); - job.def_prop_ro("program", &fomac::Session::Job::getProgram); - job.def_prop_ro("num_shots", &fomac::Session::Job::getNumShots); + &fomac::Session::Job::getSparseProbabilities, + "Returns the sparse probabilities from the job (typically only " + "available from simulator devices)."); + + job.def_prop_ro("id", &fomac::Session::Job::getId, "Returns the job ID."); + + job.def_prop_ro("program_format", &fomac::Session::Job::getProgramFormat, + "Returns the program format used for the job."); + + job.def_prop_ro("program", &fomac::Session::Job::getProgram, + "Returns the quantum program submitted for the job."); + + job.def_prop_ro("num_shots", &fomac::Session::Job::getNumShots, + "Returns the number of shots for the job."); + job.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) job.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) @@ -118,7 +202,11 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { .export_values(); // Device class - auto device = nb::class_(m, "Device"); + auto device = nb::class_( + m, "Device", + "A device represents a quantum device with its properties and " + "capabilities."); + nb::enum_(device, "Status", "enum.Enum", "Enumeration of device status.") .value("offline", QDMI_DEVICE_STATUS_OFFLINE) @@ -128,96 +216,195 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { .value("maintenance", QDMI_DEVICE_STATUS_MAINTENANCE) .value("calibration", QDMI_DEVICE_STATUS_CALIBRATION) .export_values(); - device.def("name", &fomac::Session::Device::getName); - device.def("version", &fomac::Session::Device::getVersion); - device.def("status", &fomac::Session::Device::getStatus); - device.def("library_version", &fomac::Session::Device::getLibraryVersion); - device.def("qubits_num", &fomac::Session::Device::getQubitsNum); - device.def("sites", &fomac::Session::Device::getSites); - device.def("regular_sites", &fomac::Session::Device::getRegularSites); - device.def("zones", &fomac::Session::Device::getZones); - device.def("operations", &fomac::Session::Device::getOperations); - device.def("coupling_map", &fomac::Session::Device::getCouplingMap); - device.def("needs_calibration", &fomac::Session::Device::getNeedsCalibration); - device.def("length_unit", &fomac::Session::Device::getLengthUnit); + + device.def("name", &fomac::Session::Device::getName, + "Returns the name of the device."); + + device.def("version", &fomac::Session::Device::getVersion, + "Returns the version of the device."); + + device.def("status", &fomac::Session::Device::getStatus, + "Returns the current status of the device."); + + device.def("library_version", &fomac::Session::Device::getLibraryVersion, + "Returns the version of the library used to define the device."); + + device.def("qubits_num", &fomac::Session::Device::getQubitsNum, + "Returns the number of qubits available on the device."); + + device.def("sites", &fomac::Session::Device::getSites, + "Returns the list of all sites (zone and regular sites) available " + "on the device."); + + device.def("regular_sites", &fomac::Session::Device::getRegularSites, + "Returns the list of regular sites (without zone sites) available " + "on the device."); + + device.def("zones", &fomac::Session::Device::getZones, + "Returns the list of zone sites (without regular sites) available " + "on the device."); + + device.def("operations", &fomac::Session::Device::getOperations, + "Returns the list of operations supported by the device."); + + device.def("coupling_map", &fomac::Session::Device::getCouplingMap, + "Returns the coupling map of the device as a list of site pairs."); + + device.def("needs_calibration", &fomac::Session::Device::getNeedsCalibration, + "Returns whether the device needs calibration."); + + device.def("length_unit", &fomac::Session::Device::getLengthUnit, + "Returns the unit of length used by the device."); + device.def("length_scale_factor", - &fomac::Session::Device::getLengthScaleFactor); - device.def("duration_unit", &fomac::Session::Device::getDurationUnit); + &fomac::Session::Device::getLengthScaleFactor, + "Returns the scale factor for length used by the device."); + + device.def("duration_unit", &fomac::Session::Device::getDurationUnit, + "Returns the unit of duration used by the device."); + device.def("duration_scale_factor", - &fomac::Session::Device::getDurationScaleFactor); - device.def("min_atom_distance", &fomac::Session::Device::getMinAtomDistance); + &fomac::Session::Device::getDurationScaleFactor, + "Returns the scale factor for duration used by the device."); + + device.def("min_atom_distance", &fomac::Session::Device::getMinAtomDistance, + "Returns the minimum atom distance on the device."); + device.def("supported_program_formats", - &fomac::Session::Device::getSupportedProgramFormats); + &fomac::Session::Device::getSupportedProgramFormats, + "Returns the list of program formats supported by the device."); + device.def("submit_job", &fomac::Session::Device::submitJob, "program"_a, - "program_format"_a, "num_shots"_a); + "program_format"_a, "num_shots"_a, "Submits a job to the device."); + device.def("__repr__", [](const fomac::Session::Device& dev) { return ""; }); + device.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) device.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) // Site class - auto site = nb::class_(device, "Site"); - site.def("index", &fomac::Session::Device::Site::getIndex); - site.def("t1", &fomac::Session::Device::Site::getT1); - site.def("t2", &fomac::Session::Device::Site::getT2); - site.def("name", &fomac::Session::Device::Site::getName); - site.def("x_coordinate", &fomac::Session::Device::Site::getXCoordinate); - site.def("y_coordinate", &fomac::Session::Device::Site::getYCoordinate); - site.def("z_coordinate", &fomac::Session::Device::Site::getZCoordinate); - site.def("is_zone", &fomac::Session::Device::Site::isZone); - site.def("x_extent", &fomac::Session::Device::Site::getXExtent); - site.def("y_extent", &fomac::Session::Device::Site::getYExtent); - site.def("z_extent", &fomac::Session::Device::Site::getZExtent); - site.def("module_index", &fomac::Session::Device::Site::getModuleIndex); - site.def("submodule_index", &fomac::Session::Device::Site::getSubmoduleIndex); + auto site = nb::class_( + device, "Site", + "A site represents a potential qubit location on a quantum device."); + + site.def("index", &fomac::Session::Device::Site::getIndex, + "Returns the index of the site."); + + site.def("t1", &fomac::Session::Device::Site::getT1, + "Returns the T1 coherence time of the site."); + + site.def("t2", &fomac::Session::Device::Site::getT2, + "Returns the T2 coherence time of the site."); + + site.def("name", &fomac::Session::Device::Site::getName, + "Returns the name of the site."); + + site.def("x_coordinate", &fomac::Session::Device::Site::getXCoordinate, + "Returns the x coordinate of the site."); + + site.def("y_coordinate", &fomac::Session::Device::Site::getYCoordinate, + "Returns the y coordinate of the site."); + + site.def("z_coordinate", &fomac::Session::Device::Site::getZCoordinate, + "Returns the z coordinate of the site."); + + site.def("is_zone", &fomac::Session::Device::Site::isZone, + "Returns whether the site is a zone."); + + site.def("x_extent", &fomac::Session::Device::Site::getXExtent, + "Returns the x extent of the site."); + + site.def("y_extent", &fomac::Session::Device::Site::getYExtent, + "Returns the y extent of the site."); + + site.def("z_extent", &fomac::Session::Device::Site::getZExtent, + "Returns the z extent of the site."); + + site.def("module_index", &fomac::Session::Device::Site::getModuleIndex, + "Returns the index of the module the site belongs to."); + + site.def("submodule_index", &fomac::Session::Device::Site::getSubmoduleIndex, + "Returns the index of the submodule the site belongs to."); + site.def("__repr__", [](const fomac::Session::Device::Site& s) { return ""; }); + site.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) site.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) // Operation class - auto operation = - nb::class_(device, "Operation"); + auto operation = nb::class_( + device, "Operation", + "An operation represents a quantum operation that can be performed on a " + "quantum device."); + operation.def("name", &fomac::Session::Device::Operation::getName, "sites"_a = std::vector{}, - "params"_a = std::vector{}); + "params"_a = std::vector{}, + "Returns the name of the operation."); + operation.def("qubits_num", &fomac::Session::Device::Operation::getQubitsNum, "sites"_a = std::vector{}, - "params"_a = std::vector{}); + "params"_a = std::vector{}, + "Returns the number of qubits the operation acts on."); + operation.def("parameters_num", &fomac::Session::Device::Operation::getParametersNum, "sites"_a = std::vector{}, - "params"_a = std::vector{}); + "params"_a = std::vector{}, + "Returns the number of parameters the operation has."); + operation.def("duration", &fomac::Session::Device::Operation::getDuration, "sites"_a = std::vector{}, - "params"_a = std::vector{}); + "params"_a = std::vector{}, + "Returns the duration of the operation."); + operation.def("fidelity", &fomac::Session::Device::Operation::getFidelity, "sites"_a = std::vector{}, - "params"_a = std::vector{}); + "params"_a = std::vector{}, + "Returns the fidelity of the operation."); + operation.def("interaction_radius", &fomac::Session::Device::Operation::getInteractionRadius, "sites"_a = std::vector{}, - "params"_a = std::vector{}); + "params"_a = std::vector{}, + "Returns the interaction radius of the operation."); + operation.def("blocking_radius", &fomac::Session::Device::Operation::getBlockingRadius, "sites"_a = std::vector{}, - "params"_a = std::vector{}); + "params"_a = std::vector{}, + "Returns the blocking radius of the operation."); + operation.def("idling_fidelity", &fomac::Session::Device::Operation::getIdlingFidelity, "sites"_a = std::vector{}, - "params"_a = std::vector{}); - operation.def("is_zoned", &fomac::Session::Device::Operation::isZoned); - operation.def("sites", &fomac::Session::Device::Operation::getSites); - operation.def("site_pairs", &fomac::Session::Device::Operation::getSitePairs); + "params"_a = std::vector{}, + "Returns the idling fidelity of the operation."); + + operation.def("is_zoned", &fomac::Session::Device::Operation::isZoned, + "Returns whether the operation is zoned."); + + operation.def("sites", &fomac::Session::Device::Operation::getSites, + "Returns the list of sites the operation can be performed on."); + + operation.def("site_pairs", &fomac::Session::Device::Operation::getSitePairs, + "Returns the list of site pairs the local 2-qubit operation " + "can be performed on."); + operation.def("mean_shuttling_speed", &fomac::Session::Device::Operation::getMeanShuttlingSpeed, "sites"_a = std::vector{}, - "params"_a = std::vector{}); + "params"_a = std::vector{}, + "Returns the mean shuttling speed of the operation."); + operation.def("__repr__", [](const fomac::Session::Device::Operation& op) { return ""; }); + operation.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) operation.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) @@ -259,7 +446,47 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { "auth_url"_a = std::nullopt, "username"_a = std::nullopt, "password"_a = std::nullopt, "custom1"_a = std::nullopt, "custom2"_a = std::nullopt, "custom3"_a = std::nullopt, - "custom4"_a = std::nullopt, "custom5"_a = std::nullopt); + "custom4"_a = std::nullopt, "custom5"_a = std::nullopt, + R"pb(Load a dynamic device library into the QDMI driver. + +This function loads a shared library (.so, .dll, or .dylib) that implements a QDMI device interface and makes it available for use in sessions. + +Note: + This function is only available on non-Windows platforms. + +Args: + library_path: Path to the shared library file to load. + prefix: Function prefix used by the library (e.g., "MY_DEVICE"). + base_url: Optional base URL for the device API endpoint. + token: Optional authentication token. + auth_file: Optional path to authentication file. + auth_url: Optional authentication server URL. + username: Optional username for authentication. + password: Optional password for authentication. + custom1: Optional custom configuration parameter 1. + custom2: Optional custom configuration parameter 2. + custom3: Optional custom configuration parameter 3. + custom4: Optional custom configuration parameter 4. + custom5: Optional custom configuration parameter 5. + +Returns: + Device: The newly loaded device that can be used to create backends. + +Raises: + RuntimeError: If library loading fails or configuration is invalid. + +Examples: + Load a device library with configuration: + + >>> import mqt.core.fomac as fomac + >>> device = fomac.add_dynamic_device_library( + ... "/path/to/libmy_device.so", "MY_DEVICE", base_url="http://localhost:8080", custom1="API_V2" + ... ) + + Now the device can be used directly: + + >>> from mqt.core.plugins.qiskit import QDMIBackend + >>> backend = QDMIBackend(device=device))pb"); #endif // _WIN32 } From a76def30ed21102d54722e0a98f5268ea5f4600f Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Thu, 11 Dec 2025 21:50:42 +0100 Subject: [PATCH 029/144] Move docstrings from na/fomac.pyi to bindings file --- bindings/na/fomac/fomac.cpp | 81 ++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 27 deletions(-) diff --git a/bindings/na/fomac/fomac.cpp b/bindings/na/fomac/fomac.cpp index e0945ebab5..219dbc732f 100644 --- a/bindings/na/fomac/fomac.cpp +++ b/bindings/na/fomac/fomac.cpp @@ -37,14 +37,16 @@ template [[nodiscard]] auto repr(T c) -> std::string { NB_MODULE(MQT_CORE_MODULE_NAME, m) { nb::module_::import_("mqt.core.fomac"); - auto device = - nb::class_(m, "Device"); + auto device = nb::class_( + m, "Device", "Represents a device with a lattice of traps."); - auto lattice = nb::class_(device, "Lattice"); + auto lattice = nb::class_( + device, "Lattice", "Represents a lattice of traps in the device."); - auto vector = nb::class_(lattice, "Vector"); - vector.def_ro("x", &na::Device::Vector::x); - vector.def_ro("y", &na::Device::Vector::y); + auto vector = nb::class_(lattice, "Vector", + "Represents a 2D vector."); + vector.def_ro("x", &na::Device::Vector::x, "The x-coordinate of the vector."); + vector.def_ro("y", &na::Device::Vector::y, "The y-coordinate of the vector."); vector.def("__repr__", [](const na::Device::Vector& v) { return ""; @@ -52,11 +54,15 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { vector.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) vector.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) - auto region = nb::class_(lattice, "Region"); + auto region = nb::class_( + lattice, "Region", "Represents a region in the device."); - auto size = nb::class_(region, "Size"); - size.def_ro("width", &na::Device::Region::Size::width); - size.def_ro("height", &na::Device::Region::Size::height); + auto size = nb::class_( + region, "Size", "Represents the size of a region."); + size.def_ro("width", &na::Device::Region::Size::width, + "The width of the region."); + size.def_ro("height", &na::Device::Region::Size::height, + "The height of the region."); size.def("__repr__", [](const na::Device::Region::Size& s) { return ""; @@ -64,41 +70,62 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { size.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) size.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) - region.def_ro("origin", &na::Device::Region::origin); - region.def_ro("size", &na::Device::Region::size); + region.def_ro("origin", &na::Device::Region::origin, + "The origin of the region."); + region.def_ro("size", &na::Device::Region::size, "The size of the region."); region.def("__repr__", [](const na::Device::Region& r) { return ""; }); region.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) region.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) - lattice.def_ro("lattice_origin", &na::Device::Lattice::latticeOrigin); - lattice.def_ro("lattice_vector_1", &na::Device::Lattice::latticeVector1); - lattice.def_ro("lattice_vector_2", &na::Device::Lattice::latticeVector2); - lattice.def_ro("sublattice_offsets", &na::Device::Lattice::sublatticeOffsets); - lattice.def_ro("extent", &na::Device::Lattice::extent); + lattice.def_ro("lattice_origin", &na::Device::Lattice::latticeOrigin, + "The origin of the lattice."); + lattice.def_ro("lattice_vector_1", &na::Device::Lattice::latticeVector1, + "The first lattice vector."); + lattice.def_ro("lattice_vector_2", &na::Device::Lattice::latticeVector2, + "The second lattice vector."); + lattice.def_ro("sublattice_offsets", &na::Device::Lattice::sublatticeOffsets, + "The offsets of the sublattices."); + lattice.def_ro("extent", &na::Device::Lattice::extent, + "The extent of the lattice."); lattice.def("__repr__", [](const na::Device::Lattice& l) { return ""; }); lattice.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) lattice.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) - device.def_prop_ro("traps", &na::Session::Device::getTraps); - device.def_prop_ro("t1", [](const na::Session::Device& dev) { - return dev.getDecoherenceTimes().t1; - }); - device.def_prop_ro("t2", [](const na::Session::Device& dev) { - return dev.getDecoherenceTimes().t2; - }); + device.def_prop_ro("traps", &na::Session::Device::getTraps, + "The list of trap positions in the device."); + device.def_prop_ro( + "t1", + [](const na::Session::Device& dev) { + return dev.getDecoherenceTimes().t1; + }, + "The T1 time of the device."); + device.def_prop_ro( + "t2", + [](const na::Session::Device& dev) { + return dev.getDecoherenceTimes().t2; + }, + "The T2 time of the device."); device.def("__repr__", [](const fomac::Session::Device& dev) { return ""; }); + device.def_static("try_create_from_device", + &na::Session::Device::tryCreateFromDevice, "device"_a, + R"pb(Create NA FoMaC Device from generic FoMaC Device. + +Args: + device: The generic FoMaC Device to convert. + +Returns: + The converted NA FoMaC Device or None if the conversion is not possible.)pb"); device.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) device.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) - m.def("devices", &na::Session::getDevices); - device.def_static("try_create_from_device", - &na::Session::Device::tryCreateFromDevice, "device"_a); + m.def("devices", &na::Session::getDevices, + "Returns a list of available devices."); } } // namespace mqt From fe9aee1640606eed03dc7962e516b1efee124569 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Thu, 11 Dec 2025 21:59:51 +0100 Subject: [PATCH 030/144] Move docstrings from dd.pyi to bindings files --- bindings/dd/register_dd.cpp | 104 +++++- bindings/dd/register_dd_package.cpp | 489 +++++++++++++++++++++++++--- bindings/dd/register_matrix_dds.cpp | 72 +++- bindings/dd/register_vector_dds.cpp | 62 +++- 4 files changed, 655 insertions(+), 72 deletions(-) diff --git a/bindings/dd/register_dd.cpp b/bindings/dd/register_dd.cpp index 9ee0d2c5bc..d429edbb4d 100644 --- a/bindings/dd/register_dd.cpp +++ b/bindings/dd/register_dd.cpp @@ -54,7 +54,29 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { "sample", [](const qc::QuantumComputation& qc, const size_t shots = 1024U, const size_t seed = 0U) { return dd::sample(qc, shots, seed); }, - "qc"_a, "shots"_a = 1024U, "seed"_a = 0U); + "qc"_a, "shots"_a = 1024U, "seed"_a = 0U, + R"pb(Sample from the output distribution of a quantum computation. + +This function classically simulates the quantum computation and repeatedly samples from the output distribution. +It supports mid-circuit measurements, resets, and classical control. + +Args: + qc: The quantum computation. + shots: The number of samples to take. + If the quantum computation contains no mid-circuit measurements or resets, the circuit is simulated once and the samples are drawn from the final state. + Otherwise, the circuit is simulated once for each sample. + Defaults to 1024. + seed: The seed for the random number generator. + If set to a specific non-zero value, the simulation is deterministic. + If set to 0, the RNG is randomly seeded. + Defaults to 0. + +Returns: + A histogram of the samples. + Each sample is a bitstring representing the measurement outcomes of the qubits in the quantum computation. + The leftmost bit corresponds to the most significant qubit, that is, the qubit with the highest index (big-endian). + If the circuit contains measurements, only the qubits that are actively measured are included in the output distribution. + Otherwise, all qubits in the circuit are measured.)pb"); m.def( "simulate_statevector", @@ -64,7 +86,25 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { const auto sim = dd::simulate(qc, in, *dd); return getVector(sim); }, - "qc"_a); + "qc"_a, + R"pb(Simulate the quantum computation and return the final state vector. + +This function classically simulates the quantum computation and returns the state vector of the final state. +It does not support measurements, resets, or classical control. + +Since the state vector is guaranteed to be exponentially large in the number of qubits, this function is only suitable for small quantum computations. +Consider using the :func:`~mqt.core.dd.simulate` or the :func:`~mqt.core.dd.sample` functions, which never explicitly construct the state vector, for larger quantum computations. + +Notes: + This function internally constructs a :class:`~mqt.core.dd.DDPackage`, creates the zero state, and simulates the quantum computation via the :func:`simulate` function. + The state vector is then extracted from the resulting DD via the :meth:`~mqt.core.dd.VectorDD.get_vector` method. + The resulting :class:`~mqt.core.dd.Vector` can be converted to a NumPy array without copying the data by calling :func:`numpy.array` with the `copy=False` argument. + +Args: + qc: The quantum computation. Must only contain unitary operations. + +Returns: + The state vector of the final state.)pb"); m.def( "build_unitary", @@ -74,9 +114,47 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { : dd::buildFunctionality(qc, *dd); return getMatrix(u, qc.getNqubits()); }, - "qc"_a, "recursive"_a = false); + "qc"_a, "recursive"_a = false, + R"pb(Build a unitary matrix representation of a quantum computation. + +This function builds a matrix representation of the unitary representing the functionality of a quantum computation. +This function does not support measurements, resets, or classical control, as the corresponding operations are non-unitary. + +Since the unitary matrix is guaranteed to be exponentially large in the number of qubits, this function is only suitable for small quantum computations. +Consider using the :func:`~mqt.core.dd.build_functionality` function, which never explicitly constructs the unitary matrix, for larger quantum computations. + +Notes: + This function internally constructs a :class:`~mqt.core.dd.DDPackage`, creates the identity matrix, and builds the unitary matrix via the :func:`~mqt.core.dd.build_functionality` function. + The unitary matrix is then extracted from the resulting DD via the :meth:`~mqt.core.dd.MatrixDD.get_matrix` method. + The resulting :class:`~mqt.core.dd.Matrix` can be converted to a NumPy array without copying the data by calling :func:`numpy.array` with the `copy=False` argument. + +Args: + qc: The quantum computation. Must only contain unitary operations. + recursive: Whether to build the unitary matrix recursively. + If set to True, the unitary matrix is built recursively by pairwise grouping the operations of the quantum computation. + If set to False, the unitary matrix is built by sequentially applying the operations of the quantum computation to the identity matrix. + Defaults to False. + +Returns: + The unitary matrix representing the functionality of the quantum computation.)pb"); + + m.def("simulate", &dd::simulate, "qc"_a, "initial_state"_a, "dd_package"_a, + R"pb(Simulate a quantum computation. + +This function classically simulates a quantum computation for a given initial state and returns the final state (represented as a DD). +Compared to the `sample` function, this function does not support measurements, resets, or classical control. +It only supports unitary operations. + +The simulation is effectively computed by sequentially applying the operations of the quantum computation to the initial state. + +Args: + qc: The quantum computation. Must only contain unitary operations. + initial_state: The initial state as a DD. Must have the same number of qubits as the quantum computation. + The reference count of the initial state is decremented during the simulation, so the caller must ensure that the initial state has a non-zero reference count. + dd_package: The DD package. Must be configured with a sufficient number of qubits to accommodate the quantum computation. - m.def("simulate", &dd::simulate, "qc"_a, "initial_state"_a, "dd_package"_a); +Returns: + The final state as a DD. The reference count of the final state is non-zero and must be manually decremented by the caller if it is no longer needed.)pb"); m.def( "build_functionality", @@ -87,7 +165,23 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { } return dd::buildFunctionality(qc, p); }, - "qc"_a, "dd_package"_a, "recursive"_a = false); + "qc"_a, "dd_package"_a, "recursive"_a = false, + R"pb(Build a functional representation of a quantum computation. + +This function builds a matrix DD representation of the unitary representing the functionality of a quantum computation. +This function does not support measurements, resets, or classical control, as the corresponding operations are non-unitary. + +Args: + qc: The quantum computation. + Must only contain unitary operations. + dd_package: The DD package. Must be configured with a sufficient number of qubits to accommodate the quantum computation. + recursive: Whether to build the functionality matrix recursively. + If set to True, the functionality matrix is built recursively by pairwise grouping the operations of the quantum computation. + If set to False, the functionality matrix is built by sequentially applying the operations of the quantum computation to the identity matrix. + Defaults to False. + +Returns: + The functionality as a DD. The reference count of the result is non-zero and must be manually decremented by the caller if it is no longer needed.)pb"); } } // namespace mqt diff --git a/bindings/dd/register_dd_package.cpp b/bindings/dd/register_dd_package.cpp index da04cb94fe..c63d3f2212 100644 --- a/bindings/dd/register_dd_package.cpp +++ b/bindings/dd/register_dd_package.cpp @@ -90,16 +90,45 @@ dd::mCachedEdge makeDDFromMatrix(dd::Package& p, const Matrix& m, // NOLINTNEXTLINE(misc-use-internal-linkage) void registerDDPackage(const nb::module_& m) { - auto dd = nb::class_(m, "DDPackage"); + auto dd = nb::class_( + m, "DDPackage", + R"pb(The central manager for performing computations on decision diagrams. + +It drives all computation on decision diagrams and maintains the necessary data structures for this purpose. +Specifically, it + +- manages the memory for the decision diagram nodes (Memory Manager), +- ensures the canonical representation of decision diagrams (Unique Table), +- ensures the efficiency of decision diagram operations (Compute Table), +- provides methods for creating quantum states and operations from various sources, +- provides methods for various operations on quantum states and operations, and +- provides means for reference counting and garbage collection. + +Notes: + It is undefined behavior to pass VectorDD or MatrixDD objects that were created with a different DDPackage to the methods of the DDPackage. + +Args: + num_qubits: The maximum number of qubits that the DDPackage can handle. + Mainly influences the size of the unique tables. + Can be adjusted dynamically using the `resize` method. + Since resizing the DDPackage can be expensive, it is recommended to choose a value that is large enough for the quantum computations that are to be performed, but not unnecessarily large. + Default is 32.)pb"); // Constructor dd.def(nb::init(), "num_qubits"_a = dd::Package::DEFAULT_QUBITS); // Resizing the package - dd.def("resize", &dd::Package::resize, "num_qubits"_a); + dd.def("resize", &dd::Package::resize, "num_qubits"_a, + R"pb(Resize the DDPackage to accommodate a different number of qubits. + +Args: + num_qubits: The new number of qubits. + Must be greater than zero. + It is undefined behavior to resize the DDPackage to a smaller number of qubits and then perform operations on decision diagrams that are associated with qubits that are no longer present.)pb"); // Getting the number of qubits the package is configured for - dd.def_prop_ro("max_qubits", &dd::Package::qubits); + dd.def_prop_ro("max_qubits", &dd::Package::qubits, + "The maximum number of qubits that the DDPackage can handle."); ///------------------------------------------------------------------------/// /// Vector DD Generation @@ -112,7 +141,16 @@ void registerDDPackage(const nb::module_& m) { }, "num_qubits"_a, // keep the DD package alive while the returned vector DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), + R"pb(Create the DD for the zero state :math:`| 0 \ldots 0 \rangle`. + +Args: + num_qubits: The number of qubits. + Must not be greater than the number of qubits the DDPackage is configured with. + +Returns: + The DD for the zero state. + The resulting state is guaranteed to have its reference count increased.)pb"); dd.def( "computational_basis_state", @@ -122,16 +160,37 @@ void registerDDPackage(const nb::module_& m) { }, "num_qubits"_a, "state"_a, // keep the DD package alive while the returned vector DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), + R"pb(Create the DD for the computational basis state :math:`| b_{n - 1} \ldots b_0 \rangle`. + +Args: + num_qubits: The number of qubits. + Must not be greater than the number of qubits the DDPackage is configured with. + state: The state as a list of booleans. + Must be at least `num_qubits` long. + +Returns: + The DD for the computational basis state. + The resulting state is guaranteed to have its reference count increased.)pb"); nb::enum_(m, "BasisStates", "enum.Enum", "Enumeration of basis states.") - .value("zero", dd::BasisStates::zero) - .value("one", dd::BasisStates::one) - .value("plus", dd::BasisStates::plus) - .value("minus", dd::BasisStates::minus) - .value("right", dd::BasisStates::right) - .value("left", dd::BasisStates::left); + .value("zero", dd::BasisStates::zero, + "The computational basis state :math:`|0\rangle`.") + .value("one", dd::BasisStates::one, + "The computational basis state :math:`|1\rangle`.") + .value( + "plus", dd::BasisStates::plus, + R"pb(The superposition state :math:`|+\rangle = \frac{1}{\sqrt{2}} (|0\rangle + |1\rangle)`.)pb") + .value( + "minus", dd::BasisStates::minus, + R"pb(The superposition state :math:`|-\rangle = \frac{1}{\sqrt{2}} (|0\rangle - |1\rangle)`.)pb") + .value( + "right", dd::BasisStates::right, + R"pb(The superposition state :math:`|R\rangle = \frac{1}{\sqrt{2}} (|0\rangle - i |1\rangle)`.)pb") + .value( + "left", dd::BasisStates::left, + R"pb(The superposition state :math:`|L\rangle = \frac{1}{\sqrt{2}} (|0\rangle + i |1\rangle)`.)pb"); dd.def( "basis_state", @@ -141,7 +200,18 @@ void registerDDPackage(const nb::module_& m) { }, "num_qubits"_a, "state"_a, // keep the DD package alive while the returned vector DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), + R"pb(Create the DD for the basis state :math:`| B_{n - 1} \ldots B_0 \rangle`, where :math:`B_i \in \{0, 1, +\, -\, L, R\}`. + +Args: + num_qubits: The number of qubits. + Must not be greater than the number of qubits the DDPackage is configured with. + state: The state as an iterable of :class:`BasisStates`. + Must be at least `num_qubits` long. + +Returns: + The DD for the basis state. + The resulting state is guaranteed to have its reference count increased.)pb"); dd.def( "ghz_state", @@ -150,7 +220,16 @@ void registerDDPackage(const nb::module_& m) { }, "num_qubits"_a, // keep the DD package alive while the returned vector DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), + R"pb(Create the DD for the GHZ state :math:`\frac{1}{\sqrt{2}} (| 0 \ldots 0 \rangle + |1 \ldots 1 \rangle)`. + +Args: + num_qubits: The number of qubits. + Must not be greater than the number of qubits the DDPackage is configured with. + +Returns: + The DD for the GHZ state. + The resulting state is guaranteed to have its reference count increased.)pb"); dd.def( "w_state", @@ -159,7 +238,19 @@ void registerDDPackage(const nb::module_& m) { }, "num_qubits"_a, // keep the DD package alive while the returned vector DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), + R"pb(Create the DD for the W state :math:`|W\rangle`. + +.. math:: + |W\rangle = \frac{1}{\sqrt{n}} (| 100 \ldots 0 \rangle + | 010 \ldots 0 \rangle + \ldots + | 000 \ldots 1 \rangle) + +Args: + num_qubits: The number of qubits. + Must not be greater than the number of qubits the DDPackage is configured with. + +Returns: + The DD for the W state. + The resulting state is guaranteed to have its reference count increased.)pb"); dd.def( "from_vector", @@ -185,7 +276,16 @@ void registerDDPackage(const nb::module_& m) { }, "state"_a, // keep the DD package alive while the returned vector DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), R"pb(Create a DD from a state vector. + +Args: + state: The state vector. + Must have a length that is a power of 2. + Must not require more qubits than the DDPackage is configured with. + +Returns: + The DD for the vector. + The resulting state is guaranteed to have its reference count increased.)pb"); dd.def( "apply_unitary_operation", @@ -195,7 +295,19 @@ void registerDDPackage(const nb::module_& m) { }, "vec"_a, "operation"_a, "permutation"_a = qc::Permutation{}, // keep the DD package alive while the returned vector DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), R"pb(Apply a unitary operation to the DD. + +Notes: + Automatically manages the reference count of the input and output DDs. + The input DD must have a non-zero reference count. + +Args: + vec: The input DD. + operation: The operation. Must be unitary. + permutation: The permutation of the qubits. Defaults to the identity permutation. + +Returns: + The resulting DD.)pb"); dd.def( "apply_measurement", @@ -211,7 +323,20 @@ void registerDDPackage(const nb::module_& m) { "vec"_a, "operation"_a, "measurements"_a, "permutation"_a = qc::Permutation{}, // keep the DD package alive while the returned vector DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), R"pb(Apply a measurement to the DD. + +Notes: + Automatically manages the reference count of the input and output DDs. + The input DD must have a non-zero reference count + +Args: + vec: The input DD. + operation: The measurement operation. + measurements: A list of bits with existing measurement outcomes. + permutation: The permutation of the qubits. Defaults to the identity permutation. + +Returns: + The resulting DD after the measurement as well as the updated measurement outcomes..)pb"); dd.def( "apply_reset", @@ -222,7 +347,19 @@ void registerDDPackage(const nb::module_& m) { }, "vec"_a, "operation"_a, "permutation"_a = qc::Permutation{}, // keep the DD package alive while the returned vector DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), R"pb(Apply a reset to the DD. + +Notes: + Automatically manages the reference count of the input and output DDs. + The input DD must have a non-zero reference count. + +Args: + vec: The input DD. + operation: The reset operation. + permutation: The permutation of the qubits. Defaults to the identity permutation. + +Returns: + The resulting DD after the reset.)pb"); dd.def( "apply_if_else_operation", @@ -234,7 +371,21 @@ void registerDDPackage(const nb::module_& m) { "vec"_a, "operation"_a, "measurements"_a, "permutation"_a = qc::Permutation{}, // keep the DD package alive while the returned vector DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), + R"pb(Apply a classically controlled operation to the DD. + +Notes: + Automatically manages the reference count of the input and output DDs. + The input DD must have a non-zero reference count. + +Args: + vec: The input DD. + operation: The classically controlled operation. + measurements: A list of bits with stored measurement outcomes. + permutation: The permutation of the qubits. Defaults to the identity permutation. + +Returns: + The resulting DD after the operation.)pb"); dd.def( "measure_collapsing", @@ -242,7 +393,18 @@ void registerDDPackage(const nb::module_& m) { static std::mt19937_64 rng(std::random_device{}()); return p.measureOneCollapsing(v, q, rng); }, - "vec"_a, "qubit"_a); + "vec"_a, "qubit"_a, R"pb(Measure a qubit and collapse the DD. + +Notes: + Automatically manages the reference count of the input and output DDs. + The input DD must have a non-zero reference count. + +Args: + vec: The input DD. + qubit: The qubit to measure. + +Returns: + The measurement outcome.)pb"); dd.def( "measure_all", @@ -250,9 +412,24 @@ void registerDDPackage(const nb::module_& m) { static std::mt19937_64 rng(std::random_device{}()); return p.measureAll(v, collapse, rng); }, - "vec"_a, "collapse"_a = false); + "vec"_a, "collapse"_a = false, R"pb(Measure all qubits. - dd.def_static("identity", &dd::Package::makeIdent); +Notes: + Automatically manages the reference count of the input and output DDs. + The input DD must have a non-zero reference count. + +Args: + vec: The input DD. + collapse: Whether to collapse the DD. + +Returns: + The measurement outcome.)pb"); + + dd.def_static("identity", &dd::Package::makeIdent, + R"pb(Create the DD for the identity matrix :math:`I`. + +Returns: + The DD for the identity matrix.)pb"); dd.def( "single_qubit_gate", @@ -264,7 +441,14 @@ void registerDDPackage(const nb::module_& m) { }, "matrix"_a, "target"_a, // keep the DD package alive while the returned matrix DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), R"pb(Create the DD for a single-qubit gate. + +Args: + matrix: The :math:`2 \times 2` matrix representing the single-qubit gate. + target: The target qubit. + +Returns: + The DD for the single-qubit gate.)pb"); dd.def( "controlled_single_qubit_gate", @@ -278,7 +462,16 @@ void registerDDPackage(const nb::module_& m) { }, "matrix"_a, "control"_a, "target"_a, // keep the DD package alive while the returned matrix DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), + R"pb(Create the DD for a controlled single-qubit gate. + +Args: + matrix: The :math:`2 \times 2` matrix representing the single-qubit gate. + control: The control qubit. + target: The target qubit. + +Returns: + The DD for the controlled single-qubit gate.)pb"); dd.def( "multi_controlled_single_qubit_gate", @@ -292,7 +485,16 @@ void registerDDPackage(const nb::module_& m) { }, "matrix"_a, "controls"_a, "target"_a, // keep the DD package alive while the returned matrix DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), + R"pb(Create the DD for a multi-controlled single-qubit gate. + +Args: + matrix: The :math:`2 \times 2` matrix representing the single-qubit gate. + controls: The control qubits. + target: The target qubit. + +Returns: + The DD for the multi-controlled single-qubit gate.)pb"); dd.def( "two_qubit_gate", @@ -310,7 +512,15 @@ void registerDDPackage(const nb::module_& m) { }, "matrix"_a, "target0"_a, "target1"_a, // keep the DD package alive while the returned matrix DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), R"pb(Create the DD for a two-qubit gate. + +Args: + matrix: The :math:`4 \times 4` matrix representing the two-qubit gate. + target0: The first target qubit. + target1: The second target qubit. + +Returns: + The DD for the two-qubit gate.)pb"); dd.def( "controlled_two_qubit_gate", @@ -328,7 +538,17 @@ void registerDDPackage(const nb::module_& m) { }, "matrix"_a, "control"_a, "target0"_a, "target1"_a, // keep the DD package alive while the returned matrix DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), + R"pb(Create the DD for a controlled two-qubit gate. + +Args: + matrix: The :math:`4 \times 4` matrix representing the two-qubit gate. + control: The control qubit. + target0: The first target qubit. + target1: The second target qubit. + +Returns: + The DD for the controlled two-qubit gate.)pb"); dd.def( "multi_controlled_two_qubit_gate", @@ -346,7 +566,17 @@ void registerDDPackage(const nb::module_& m) { }, "matrix"_a, "controls"_a, "target0"_a, "target1"_a, // keep the DD package alive while the returned matrix DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), + R"pb(Create the DD for a multi-controlled two-qubit gate. + +Args: + matrix: The :math:`4 \times 4` matrix representing the two-qubit gate. + controls: The control qubits. + target0: The first target qubit. + target1: The second target qubit. + +Returns: + The DD for the multi-controlled two-qubit gate.)pb"); dd.def( "from_matrix", @@ -372,7 +602,13 @@ void registerDDPackage(const nb::module_& m) { }, "matrix"_a, // keep the DD package alive while the returned matrix DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), R"pb(Create a DD from a matrix. + +Args: + matrix: The matrix. Must be square and have a size that is a power of 2. + +Returns: + The DD for the matrix.)pb"); dd.def( "from_operation", @@ -384,14 +620,34 @@ void registerDDPackage(const nb::module_& m) { }, "operation"_a, "invert"_a = false, // keep the DD package alive while the returned matrix DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), R"pb(Create a DD from an operation. + +Args: + operation: The operation. Must be unitary. + invert: Whether to get the inverse of the operation. + +Returns: + The DD for the operation.)pb"); // Reference counting and garbage collection - dd.def("inc_ref_vec", &dd::Package::incRef, "vec"_a); - dd.def("inc_ref_mat", &dd::Package::incRef, "mat"_a); - dd.def("dec_ref_vec", &dd::Package::decRef, "vec"_a); - dd.def("dec_ref_mat", &dd::Package::decRef, "mat"_a); - dd.def("garbage_collect", &dd::Package::garbageCollect, "force"_a = false); + dd.def("inc_ref_vec", &dd::Package::incRef, "vec"_a, + "Increment the reference count of a vector."); + dd.def("inc_ref_mat", &dd::Package::incRef, "mat"_a, + "Decrement the reference count of a vector."); + dd.def("dec_ref_vec", &dd::Package::decRef, "vec"_a, + "Increment the reference count of a matrix."); + dd.def("dec_ref_mat", &dd::Package::decRef, "mat"_a, + "Decrement the reference count of a matrix."); + dd.def("garbage_collect", &dd::Package::garbageCollect, "force"_a = false, + R"pb(Perform garbage collection on the DDPackage. + +Args: + force: Whether to force garbage collection. + If set to True, garbage collection is performed regardless of the current memory usage. + If set to False, garbage collection is only performed if the memory usage exceeds a certain threshold. + +Returns: + Whether any nodes were collected during garbage collection.)pb"); // Operations on DDs dd.def("vector_add", @@ -399,22 +655,64 @@ void registerDDPackage(const nb::module_& m) { const dd::vEdge&, const dd::vEdge&)>(&dd::Package::add), "lhs"_a, "rhs"_a, // keep the DD package alive while the returned vector DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), R"pb(Add two vectors. + +Notes: + It is the caller's responsibility to update the reference count of the input and output vectors after the operation. + + Both vectors must have the same number of qubits. + +Args: + lhs: The left vector. + rhs: The right vector. + +Returns: + The sum of the two vectors.)pb"); dd.def("matrix_add", static_cast(&dd::Package::add), "lhs"_a, "rhs"_a, // keep the DD package alive while the returned matrix DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), R"pb(Add two matrices. + +Notes: + It is the caller's responsibility to update the reference count of the input and output matrices after the operation. + + Both matrices must have the same number of qubits. + +Args: + lhs: The left matrix. + rhs: The right matrix. + +Returns: + The sum of the two matrices.)pb"); dd.def("conjugate", &dd::Package::conjugate, "vec"_a, // keep the DD package alive while the returned vector DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), R"pb(Conjugate a vector. + +Notes: + It is the caller's responsibility to update the reference count of the input and output vectors after the operation. + +Args: + vec: The vector. + +Returns: + The conjugated vector.)pb"); dd.def("conjugate_transpose", &dd::Package::conjugateTranspose, "mat"_a, // keep the DD package alive while the returned matrix DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), R"pb(Conjugate transpose a matrix. + +Notes: + It is the caller's responsibility to update the reference count of the input and output matrices after the operation. + +Args: + mat: The matrix. + +Returns: + The conjugate transposed matrix.)pb"); dd.def( "matrix_vector_multiply", @@ -423,7 +721,19 @@ void registerDDPackage(const nb::module_& m) { }, "mat"_a, "vec"_a, // keep the DD package alive while the returned vector DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), R"pb(Multiply a matrix with a vector. + +Notes: + It is the caller's responsibility to update the reference count of the input and output matrices after the operation. + + The vector must have at least as many qubits as the matrix non-trivially acts on. + +Args: + mat: The matrix. + vec: The vector. + +Returns: + The product of the matrix and the vector.)pb"); dd.def( "matrix_multiply", @@ -432,19 +742,62 @@ void registerDDPackage(const nb::module_& m) { }, "lhs"_a, "rhs"_a, // keep the DD package alive while the returned matrix DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), R"pb(Multiply two matrices. + + Notes: + It is the caller's responsibility to update the reference count of the input and output matrices after the operation. + + Args: + lhs: The left matrix. + rhs: The right matrix. + + Returns: + The product of the two matrices.)pb"); dd.def( "inner_product", [](dd::Package& p, const dd::vEdge& lhs, const dd::vEdge& rhs) { return std::complex{p.innerProduct(lhs, rhs)}; }, - "lhs"_a, "rhs"_a); + "lhs"_a, "rhs"_a, R"pb(Compute the inner product of two vectors. + +Notes: + Both vectors must have the same number of qubits. + +Args: + lhs: The left vector. + rhs: The right vector. + +Returns: + The inner product of the two vectors.)pb"); + + dd.def("fidelity", &dd::Package::fidelity, "lhs"_a, "rhs"_a, + R"pb(Compute the fidelity of two vectors. - dd.def("fidelity", &dd::Package::fidelity, "lhs"_a, "rhs"_a); +Notes: + Both vectors must have the same number of qubits. + +Args: + lhs: The left vector. + rhs: The right vector. + +Returns: + The fidelity of the two vectors.)pb"); dd.def("expectation_value", &dd::Package::expectationValue, "observable"_a, - "state"_a); + "state"_a, R"pb(Compute the expectation value of an observable. + +Notes: + The state must have at least as many qubits as the observable non-trivially acts on. + + The method computes :math:`\langle \psi | O | \psi \rangle` as :math:`\langle \psi | (O | \psi \rangle)`. + +Args: + observable: The observable. + state: The state. + +Returns: + The expectation value of the observable.)pb"); dd.def("vector_kronecker", static_cast()); + nb::keep_alive<0, 1>(), + R"pb(Compute the Kronecker product of two vectors. + +Notes: + It is the caller's responsibility to update the reference count of the input and output vectors after the operation. + +Args: + top: The top vector. + bottom: The bottom vector. + bottom_num_qubits: The number of qubits of the bottom vector. + increment_index: Whether to increment the indexes of the top vector. + +Returns: + The Kronecker product of the two vectors.)pb"); dd.def("matrix_kronecker", static_cast()); + nb::keep_alive<0, 1>(), + R"pb(Compute the Kronecker product of two matrices. + +Notes: + It is the caller's responsibility to update the reference count of the input and output matrices after the operation. + +Args: + top: The top matrix. + bottom: The bottom matrix. + bottom_num_qubits: The number of qubits of the bottom matrix. + increment_index: Whether to increment the indexes of the top matrix. + +Returns: + The Kronecker product of the two matrices.)pb"); dd.def("partial_trace", &dd::Package::partialTrace, "mat"_a, "eliminate"_a, // keep the DD package alive while the returned matrix DD is alive. - nb::keep_alive<0, 1>()); + nb::keep_alive<0, 1>(), R"pb(Compute the partial trace of a matrix. + +Args: + mat: The matrix. + eliminate: The qubits to eliminate. Must be at least as long as the number of qubits of the matrix. + +Returns: + The partial trace of the matrix.)pb"); dd.def( "trace", [](dd::Package& p, const dd::mEdge& mat, const size_t numQubits) { return std::complex{p.trace(mat, numQubits)}; }, - "mat"_a, "num_qubits"_a); + "mat"_a, "num_qubits"_a, R"pb(Compute the trace of a matrix. + +Args: + mat: The matrix. + num_qubits: The number of qubits of the matrix. + +Returns: + The trace of the matrix.)pb"); } } // namespace mqt diff --git a/bindings/dd/register_matrix_dds.cpp b/bindings/dd/register_matrix_dds.cpp index fc8dce4464..c903c933eb 100644 --- a/bindings/dd/register_matrix_dds.cpp +++ b/bindings/dd/register_matrix_dds.cpp @@ -62,21 +62,50 @@ Matrix getMatrix(const dd::mEdge& m, const size_t numQubits, // NOLINTNEXTLINE(misc-use-internal-linkage) void registerMatrixDDs(const nb::module_& m) { - auto mat = nb::class_(m, "MatrixDD"); + auto mat = nb::class_( + m, "MatrixDD", "A class representing a matrix decision diagram (DD)."); + + mat.def("is_terminal", &dd::mEdge::isTerminal, + "Check if the DD is a terminal node."); + mat.def("is_zero_terminal", &dd::mEdge::isZeroTerminal, + "Check if the DD is a zero terminal node."); - mat.def("is_terminal", &dd::mEdge::isTerminal); - mat.def("is_zero_terminal", &dd::mEdge::isZeroTerminal); mat.def("is_identity", &dd::mEdge::isIdentity<>, - "up_to_global_phase"_a = true); + "up_to_global_phase"_a = true, + R"pb(Check if the DD represents the identity matrix. + +Args: + up_to_global_phase: Whether to ignore global phase. + +Returns: + Whether the DD represents the identity matrix.)pb"); - mat.def("size", nb::overload_cast<>(&dd::mEdge::size, nb::const_)); + mat.def("size", nb::overload_cast<>(&dd::mEdge::size, nb::const_), + "Get the size of the DD by traversing it once."); mat.def("get_entry", &dd::mEdge::getValueByIndex<>, "num_qubits"_a, "row"_a, - "col"_a); + "col"_a, "Get the entry of the matrix by row and column index."); + mat.def("get_entry_by_path", &dd::mEdge::getValueByPath, "num_qubits"_a, - "decisions"_a); + "decisions"_a, R"pb(Get the entry of the matrix by decisions. + +Args: + num_qubits: The number of qubits. + decisions: The decisions as a string of `0`, `1`, `2`, or `3`, where decisions[i]` corresponds to the successor to follow at level `i` of the DD. + Must be at least `num_qubits` long. + +Returns: + The entry of the matrix.)pb"); - mat.def("get_matrix", &getMatrix, "num_qubits"_a, "threshold"_a = 0.); + mat.def("get_matrix", &getMatrix, "num_qubits"_a, "threshold"_a = 0., + R"pb(Get the matrix represented by the DD. + +Args: + num_qubits: The number of qubits. + threshold: The threshold for not including entries in the matrix. Defaults to 0.0. + +Returns: + The matrix.)pb"); mat.def( "to_dot", @@ -88,7 +117,18 @@ void registerMatrixDDs(const nb::module_& m) { return os.str(); }, "colored"_a = true, "edge_labels"_a = false, "classic"_a = false, - "memory"_a = false, "format_as_polar"_a = true); + "memory"_a = false, "format_as_polar"_a = true, + R"pb(Convert the DD to a DOT graph that can be plotted via Graphviz. + +Args: + colored: Whether to use colored edge weights + edge_labels: Whether to include edge weights as labels. + classic: Whether to use the classic DD visualization style. + memory: Whether to include memory information. For debugging purposes only. + format_as_polar: Whether to format the edge weights in polar coordinates. + +Returns: + The DOT graph.)pb"); mat.def( "to_svg", @@ -103,6 +143,18 @@ void registerMatrixDDs(const nb::module_& m) { true, formatAsPolar); }, "filename"_a, "colored"_a = true, "edge_labels"_a = false, - "classic"_a = false, "memory"_a = false, "format_as_polar"_a = true); + "classic"_a = false, "memory"_a = false, "format_as_polar"_a = true, + R"pb(Convert the DD to an SVG file that can be viewed in a browser. + +Requires the `dot` command from Graphviz to be installed and available in the PATH. + +Args: + filename: The filename of the SVG file. Any file extension will be replaced by `.dot` and then `.svg`. + colored: Whether to use colored edge weights. + edge_labels: Whether to include edge weights as labels. + classic: Whether to use the classic DD visualization style. + memory: Whether to include memory information. For debugging purposes only. + show: Whether to open the SVG file in the default browser. + format_as_polar: Whether to format the edge weights in polar coordinates.)pb"); } } // namespace mqt diff --git a/bindings/dd/register_vector_dds.cpp b/bindings/dd/register_vector_dds.cpp index c27f489ec4..bac024cd8d 100644 --- a/bindings/dd/register_vector_dds.cpp +++ b/bindings/dd/register_vector_dds.cpp @@ -47,19 +47,24 @@ Vector getVector(const dd::vEdge& v, const dd::fp threshold = 0.) { // NOLINTNEXTLINE(misc-use-internal-linkage) void registerVectorDDs(const nb::module_& m) { - auto vec = nb::class_(m, "VectorDD"); + auto vec = nb::class_( + m, "VectorDD", "A class representing a vector decision diagram (DD)."); - vec.def("is_terminal", &dd::vEdge::isTerminal); - vec.def("is_zero_terminal", &dd::vEdge::isZeroTerminal); + vec.def("is_terminal", &dd::vEdge::isTerminal, + "Check if the DD is a terminal node."); - vec.def("size", nb::overload_cast<>(&dd::vEdge::size, nb::const_)); + vec.def("is_zero_terminal", &dd::vEdge::isZeroTerminal, + "Check if the DD is a zero terminal node."); + + vec.def("size", nb::overload_cast<>(&dd::vEdge::size, nb::const_), + "Get the size of the DD by traversing it once."); vec.def( "__getitem__", [](const dd::vEdge& v, const size_t idx) { return v.getValueByIndex(idx); }, - "index"_a); + "index"_a, "Get the amplitude of a basis state by index."); vec.def( "get_amplitude", @@ -67,9 +72,25 @@ void registerVectorDDs(const nb::module_& m) { const std::string& decisions) { return v.getValueByPath(numQubits, decisions); }, - "num_qubits"_a, "decisions"_a); + "num_qubits"_a, "decisions"_a, + R"pb(Get the amplitude of a basis state by decisions. + +Args: + num_qubits: The number of qubits. + decisions: The decisions as a string of bits (`0` or `1`), where `decisions[i]` corresponds to the successor to follow at level `i` of the DD. + Must be at least `num_qubits` long. + +Returns: + The amplitude of the basis state.)pb"); + + vec.def("get_vector", &getVector, "threshold"_a = 0., + R"pb(Get the state vector represented by the DD. + +Args: + threshold: The threshold for not including amplitudes in the state vector. Defaults to 0.0. - vec.def("get_vector", &getVector, "threshold"_a = 0.); +Returns: + The state vector.)pb"); vec.def( "to_dot", @@ -81,7 +102,18 @@ void registerVectorDDs(const nb::module_& m) { return os.str(); }, "colored"_a = true, "edge_labels"_a = false, "classic"_a = false, - "memory"_a = false, "format_as_polar"_a = true); + "memory"_a = false, "format_as_polar"_a = true, + R"pb(Convert the DD to a DOT graph that can be plotted via Graphviz. + +Args: + colored: Whether to use colored edge weights + edge_labels: Whether to include edge weights as labels. + classic: Whether to use the classic DD visualization style. + memory: Whether to include memory information. For debugging purposes only. + format_as_polar: Whether to format the edge weights in polar coordinates. + +Returns: + The DOT graph.)pb"); vec.def( "to_svg", @@ -96,7 +128,19 @@ void registerVectorDDs(const nb::module_& m) { true, formatAsPolar); }, "filename"_a, "colored"_a = true, "edge_labels"_a = false, - "classic"_a = false, "memory"_a = false, "format_as_polar"_a = true); + "classic"_a = false, "memory"_a = false, "format_as_polar"_a = true, + R"pb(Convert the DD to an SVG file that can be viewed in a browser. + +Requires the `dot` command from Graphviz to be installed and available in the PATH. + +Args: + filename: The filename of the SVG file. Any file extension will be replaced by `.dot` and then `.svg`. + colored: Whether to use colored edge weights. + edge_labels: Whether to include edge weights as labels. + classic: Whether to use the classic DD visualization style. + memory: Whether to include memory information. For debugging purposes only. + show: Whether to open the SVG file in the default browser. + format_as_polar: Whether to format the edge weights in polar coordinates.)pb"); } } // namespace mqt From 16903e0562897ab7fedcaf8ceca9e71917193812 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Thu, 11 Dec 2025 23:01:29 +0100 Subject: [PATCH 031/144] Remove old stub files --- bindings/dd/CMakeLists.txt | 8 - bindings/ir/CMakeLists.txt | 9 - bindings/na/CMakeLists.txt | 9 - python/mqt/core/dd.pyi | 1016 -------------- python/mqt/core/fomac.pyi | 341 ----- python/mqt/core/ir/__init__.pyi | 2078 ----------------------------- python/mqt/core/ir/operations.pyi | 1011 -------------- python/mqt/core/ir/registers.pyi | 91 -- python/mqt/core/ir/symbolic.pyi | 177 --- python/mqt/core/na/__init__.py | 12 - python/mqt/core/na/fomac.pyi | 117 -- 11 files changed, 4869 deletions(-) delete mode 100644 python/mqt/core/dd.pyi delete mode 100644 python/mqt/core/fomac.pyi delete mode 100644 python/mqt/core/ir/__init__.pyi delete mode 100644 python/mqt/core/ir/operations.pyi delete mode 100644 python/mqt/core/ir/registers.pyi delete mode 100644 python/mqt/core/ir/symbolic.pyi delete mode 100644 python/mqt/core/na/__init__.py delete mode 100644 python/mqt/core/na/fomac.pyi diff --git a/bindings/dd/CMakeLists.txt b/bindings/dd/CMakeLists.txt index aa149adc3c..050729e332 100644 --- a/bindings/dd/CMakeLists.txt +++ b/bindings/dd/CMakeLists.txt @@ -21,12 +21,4 @@ if(NOT TARGET ${MQT_CORE_TARGET_NAME}-dd-bindings) . LINK_LIBS MQT::CoreDD) - - # install the Python stub file in editable mode for better IDE support - if(SKBUILD_STATE STREQUAL "editable") - install( - FILES ${PROJECT_SOURCE_DIR}/python/mqt/core/dd.pyi - DESTINATION . - COMPONENT ${MQT_CORE_TARGET_NAME}_Python) - endif() endif() diff --git a/bindings/ir/CMakeLists.txt b/bindings/ir/CMakeLists.txt index b560e19675..23a1329279 100644 --- a/bindings/ir/CMakeLists.txt +++ b/bindings/ir/CMakeLists.txt @@ -22,13 +22,4 @@ if(NOT TARGET ${MQT_CORE_TARGET_NAME}-ir-bindings) LINK_LIBS MQT::CoreIR MQT::CoreQASM) - - # install the Python stub files in editable mode for better IDE support - if(SKBUILD_STATE STREQUAL "editable") - file(GLOB_RECURSE IR_PYI_FILES ${PROJECT_SOURCE_DIR}/python/mqt/core/ir/*.pyi) - install( - FILES ${IR_PYI_FILES} - DESTINATION ./ir - COMPONENT ${MQT_CORE_TARGET_NAME}_Python) - endif() endif() diff --git a/bindings/na/CMakeLists.txt b/bindings/na/CMakeLists.txt index 923f9fb7c5..609fbf66e4 100644 --- a/bindings/na/CMakeLists.txt +++ b/bindings/na/CMakeLists.txt @@ -7,12 +7,3 @@ # Licensed under the MIT License add_subdirectory(fomac) - -# install the Python stub files in editable mode for better IDE support -if(SKBUILD_STATE STREQUAL "editable") - file(GLOB_RECURSE PYI_FILES ${PROJECT_SOURCE_DIR}/python/mqt/core/na/*.pyi) - install( - FILES ${PYI_FILES} - DESTINATION ./na - COMPONENT ${MQT_CORE_TARGET_NAME}_Python) -endif() diff --git a/python/mqt/core/dd.pyi b/python/mqt/core/dd.pyi deleted file mode 100644 index 40c5021ff1..0000000000 --- a/python/mqt/core/dd.pyi +++ /dev/null @@ -1,1016 +0,0 @@ -# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM -# Copyright (c) 2025 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -"""MQT Core DD - The MQT Decision Diagram Package.""" - -from collections.abc import Iterable -from enum import Enum - -import numpy as np -import numpy.typing as npt - -from .ir import Permutation, QuantumComputation -from .ir.operations import Control, IfElseOperation, NonUnitaryOperation, Operation - -__all__ = [ - "BasisStates", - "DDPackage", - "Matrix", - "MatrixDD", - "Vector", - "VectorDD", - "build_functionality", - "build_unitary", - "sample", - "simulate", - "simulate_statevector", -] - -def sample(qc: QuantumComputation, shots: int = 1024, seed: int = 0) -> dict[str, int]: - """Sample from the output distribution of a quantum computation. - - This function classically simulates the quantum computation and repeatedly - samples from the output distribution. It supports mid-circuit measurements, - resets, and classical control. - - Args: - qc: The quantum computation. - shots: The number of samples to take. If the quantum computation contains - no mid-circuit measurements or resets, the circuit is simulated - once and the samples are drawn from the final state. Otherwise, - the circuit is simulated once for each sample. Defaults to 1024. - seed: The seed for the random number generator. If set to a specific - non-zero value, the simulation is deterministic. If set to 0, the - RNG is randomly seeded. Defaults to 0. - - Returns: - A histogram of the samples. Each sample is a bitstring representing the - measurement outcomes of the qubits in the quantum computation. The - leftmost bit corresponds to the most significant qubit, that is, the - qubit with the highest index (big-endian). If the circuit contains - measurements, only the qubits that are actively measured are included in - the output distribution. Otherwise, all qubits in the circuit are measured. - """ - -def simulate_statevector(qc: QuantumComputation) -> Vector: - """Simulate the quantum computation and return the final state vector. - - This function classically simulates the quantum computation and returns the - state vector of the final state. - It does not support measurements, resets, or classical control. - - Since the state vector is guaranteed to be exponentially large in the number - of qubits, this function is only suitable for small quantum computations. - Consider using the :func:`~mqt.core.dd.simulate` or the - :func:`~mqt.core.dd.sample` functions, which never explicitly construct - the state vector, for larger quantum computations. - - Args: - qc: The quantum computation. - Must only contain unitary operations. - - Returns: - The state vector of the final state. - - Notes: - This function internally constructs a :class:`~mqt.core.dd.DDPackage`, creates the - zero state, and simulates the quantum computation via the :func:`simulate` - function. - The state vector is then extracted from the resulting DD via the :meth:`~mqt.core.dd.VectorDD.get_vector` - method. - The resulting :class:`~mqt.core.dd.Vector` can be converted to a NumPy array without copying - the data by calling :func:`numpy.array` with the `copy=False` argument. - """ - -def build_unitary(qc: QuantumComputation, recursive: bool = False) -> Matrix: - """Build a unitary matrix representation of a quantum computation. - - This function builds a matrix representation of the unitary representing the - functionality of a quantum computation. - This function does not support measurements, resets, or classical control, - as the corresponding operations are non-unitary. - - Since the unitary matrix is guaranteed to be exponentially large in the number - of qubits, this function is only suitable for small quantum computations. - Consider using the :func:`~mqt.core.dd.build_functionality` function, which - never explicitly constructs the unitary matrix, for larger quantum computations. - - Args: - qc: The quantum computation. - Must only contain unitary operations. - recursive: Whether to build the unitary matrix recursively. - If set to True, the unitary matrix is built recursively by - pairwise grouping the operations of the quantum computation. - If set to False, the unitary matrix is built by sequentially - applying the operations of the quantum computation to the - identity matrix. - Defaults to False. - - Returns: - The unitary matrix representing the functionality of the quantum computation. - - Notes: - This function internally constructs a :class:`~mqt.core.dd.DDPackage`, creates the - identity matrix, and builds the unitary matrix via the :func:`~mqt.core.dd.build_functionality` - function. - The unitary matrix is then extracted from the resulting DD via the :meth:`~mqt.core.dd.MatrixDD.get_matrix` - method. - The resulting :class:`~mqt.core.dd.Matrix` can be converted to a NumPy array without copying - the data by calling :func:`numpy.array` with the `copy=False` argument. - """ - -def simulate(qc: QuantumComputation, initial_state: VectorDD, dd_package: DDPackage) -> VectorDD: - """Simulate a quantum computation. - - This function classically simulates a quantum computation for a given initial - state and returns the final state (represented as a DD). Compared to the - `sample` function, this function does not support measurements, resets, or - classical control. It only supports unitary operations. - - The simulation is effectively computed by sequentially applying the operations - of the quantum computation to the initial state. - - Args: - qc: The quantum computation. - Must only contain unitary operations. - initial_state: The initial state as a DD. Must have the same number of qubits - as the quantum computation. The reference count of the initial - state is decremented during the simulation, so the caller must - ensure that the initial state has a non-zero reference count. - dd_package: The DD package. Must be configured with a sufficient number of - qubits to accommodate the quantum computation. - - Returns: - The final state as a DD. The reference count of the final state is non-zero - and must be manually decremented by the caller if it is no longer needed. - """ - -def build_functionality(qc: QuantumComputation, dd_package: DDPackage, recursive: bool = False) -> MatrixDD: - """Build a functional representation of a quantum computation. - - This function builds a matrix DD representation of the unitary representing - the functionality of a quantum computation. This function does not support - measurements, resets, or classical control, as the corresponding operations - are non-unitary. - - Args: - qc: The quantum computation. - Must only contain unitary operations. - dd_package: The DD package. - Must be configured with a sufficient number of qubits to - accommodate the quantum computation. - recursive: Whether to build the functionality matrix recursively. If set - to True, the functionality matrix is built recursively by - pairwise grouping the operations of the quantum computation. - If set to False, the functionality matrix is built by - sequentially applying the operations of the quantum - computation to the identity matrix. Defaults to False. - - Returns: - The functionality as a DD. The reference count of the result is non-zero - and must be manually decremented by the caller if it is no longer needed. - """ - -class DDPackage: - """The central manager for performing computations on decision diagrams. - - It drives all computation on decision diagrams and maintains the necessary - data structures for this purpose. - Specifically, it - - - manages the memory for the decision diagram nodes (Memory Manager), - - ensures the canonical representation of decision diagrams (Unique Table), - - ensures the efficiency of decision diagram operations (Compute Table), - - provides methods for creating quantum states and operations from various sources, - - provides methods for various operations on quantum states and operations, and - - provides means for reference counting and garbage collection. - - Args: - num_qubits: The maximum number of qubits that the DDPackage can handle. - Mainly influences the size of the unique tables. - Can be adjusted dynamically using the `resize` method. - Since resizing the DDPackage can be expensive, it is recommended - to choose a value that is large enough for the quantum computations - that are to be performed, but not unnecessarily large. - Default is 32. - - Notes: - It is undefined behavior to pass VectorDD or MatrixDD objects that were - created with a different DDPackage to the methods of the DDPackage. - - """ - - def __init__(self, num_qubits: int = 32) -> None: ... - def resize(self, num_qubits: int) -> None: - """Resize the DDPackage to accommodate a different number of qubits. - - Args: - num_qubits: The new number of qubits. - Must be greater than zero. - It is undefined behavior to resize the DDPackage to a - smaller number of qubits and then perform operations - on decision diagrams that are associated with qubits - that are no longer present. - """ - - @property - def max_qubits(self) -> int: - """The maximum number of qubits that the DDPackage can handle.""" - - def zero_state(self, num_qubits: int) -> VectorDD: - r"""Create the DD for the zero state :math:`|0\ldots 0\rangle`. - - Args: - num_qubits: The number of qubits. - Must not be greater than the number of qubits the DDPackage is configured with. - - Returns: - The DD for the zero state. - The resulting state is guaranteed to have its reference count increased. - """ - - def computational_basis_state(self, num_qubits: int, state: list[bool]) -> VectorDD: - r"""Create the DD for the computational basis state :math:`|b_{n-1} \ldots b_0\rangle`. - - Args: - num_qubits: The number of qubits. - Must not be greater than the number of qubits the DDPackage is configured with. - state: The state as a list of booleans. - Must be at least `num_qubits` long. - - Returns: - The DD for the computational basis state. - The resulting state is guaranteed to have its reference count increased. - """ - - def basis_state(self, num_qubits: int, state: Iterable[BasisStates]) -> VectorDD: - r"""Create the DD for the basis state :math:`|B_{n-1} \ldots B_0\rangle`, where :math:`B_i\in\{0,1,+\,-,L,R\}`. - - Args: - num_qubits: The number of qubits. - Must not be greater than the number of qubits the DDPackage is configured with. - state: The state as an iterable of :class:`BasisStates`. - Must be at least `num_qubits` long. - - Returns: - The DD for the basis state. - The resulting state is guaranteed to have its reference count increased. - """ - - def ghz_state(self, num_qubits: int) -> VectorDD: - r"""Create the DD for the GHZ state :math:`\frac{1}{\sqrt{2}}(|0\ldots 0\rangle + |1\ldots 1\rangle)`. - - Args: - num_qubits: The number of qubits. - Must not be greater than the number of qubits the DDPackage is configured with. - - Returns: - The DD for the GHZ state. - The resulting state is guaranteed to have its reference count increased. - """ - - def w_state(self, num_qubits: int) -> VectorDD: - r"""Create the DD for the W state :math:`|W\rangle`. - - :math:`|W\rangle = \frac{1}{\sqrt{n}}(|100\ldots 0\rangle + |010\ldots 0\rangle + \ldots + |000\ldots 1\rangle)` - - Args: - num_qubits: The number of qubits. - Must not be greater than the number of qubits the DDPackage is configured with. - - Returns: - The DD for the W state. - The resulting state is guaranteed to have its reference count increased. - """ - - def from_vector(self, state: npt.NDArray[np.cdouble]) -> VectorDD: - """Create a DD from a state vector. - - Args: - state: The state vector. - Must have a length that is a power of 2. - Must not require more qubits than the DDPackage is configured with. - - Returns: - The DD for the vector. - The resulting state is guaranteed to have its reference count increased. - """ - - def apply_unitary_operation(self, vec: VectorDD, operation: Operation, permutation: Permutation = ...) -> VectorDD: - """Apply a unitary operation to the DD. - - Args: - vec: The input DD. - operation: The operation. - Must be unitary. - permutation: The permutation of the qubits. - Defaults to the identity permutation. - - Returns: - The resulting DD. - - Notes: - Automatically manages the reference count of the input and output DDs. - The input DD must have a non-zero reference count. - """ - - def apply_measurement( - self, - vec: VectorDD, - operation: NonUnitaryOperation, - measurements: list[bool], - permutation: Permutation = ..., - ) -> tuple[VectorDD, list[bool]]: - """Apply a measurement to the DD. - - Args: - vec: The input DD. - operation: The measurement operation. - measurements: A list of bits with existing measurement outcomes. - permutation: The permutation of the qubits. - Defaults to the identity permutation. - - Returns: - The resulting DD after the measurement as well as the updated measurement outcomes. - - Notes: - Automatically manages the reference count of the input and output DDs. - The input DD must have a non-zero reference count. - """ - - def apply_reset(self, vec: VectorDD, operation: NonUnitaryOperation, permutation: Permutation = ...) -> VectorDD: - """Apply a reset to the DD. - - Args: - vec: The input DD. - operation: The reset operation. - permutation: The permutation of the qubits. - Defaults to the identity permutation. - - Returns: - The resulting DD after the reset. - - Notes: - Automatically manages the reference count of the input and output DDs. - The input DD must have a non-zero reference count. - """ - - def apply_if_else_operation( - self, - vec: VectorDD, - operation: IfElseOperation, - measurements: list[bool], - permutation: Permutation = ..., - ) -> VectorDD: - """Apply a classically controlled operation to the DD. - - Args: - vec: The input DD. - operation: The classically controlled operation. - measurements: A list of bits with stored measurement outcomes. - permutation: The permutation of the qubits. Defaults to the identity permutation. - - Returns: - The resulting DD after the operation. - - Notes: - Automatically manages the reference count of the input and output DDs. - The input DD must have a non-zero reference count. - """ - - def measure_collapsing(self, vec: VectorDD, qubit: int) -> str: - """Measure a qubit and collapse the DD. - - Args: - vec: The input DD. - qubit: The qubit to measure. - - Returns: - The measurement outcome. - - Notes: - Automatically manages the reference count of the input and output DDs. - The input DD must have a non-zero reference count. - """ - - def measure_all(self, vec: VectorDD, collapse: bool = False) -> str: - """Measure all qubits. - - Args: - vec: The input DD. - collapse: Whether to collapse the DD. - - Returns: - The measurement outcome. - - Notes: - Automatically manages the reference count of the input and output DDs. - The input DD must have a non-zero reference count. - """ - - @staticmethod - def identity() -> MatrixDD: - r"""Create the DD for the identity matrix :math:`I`. - - Returns: - The DD for the identity matrix. - """ - - def single_qubit_gate( - self, - matrix: npt.NDArray[np.cdouble], - target: int, - ) -> MatrixDD: - r"""Create the DD for a single-qubit gate. - - Args: - matrix: The :math:`2\times 2` matrix representing the single-qubit gate. - target: The target qubit. - - Returns: - The DD for the single-qubit gate. - """ - - def controlled_single_qubit_gate( - self, matrix: npt.NDArray[np.cdouble], control: Control | int, target: int - ) -> MatrixDD: - r"""Create the DD for a controlled single-qubit gate. - - Args: - matrix: The :math:`2\times 2` matrix representing the single-qubit gate. - control: The control qubit. - target: The target qubit. - - Returns: - The DD for the controlled single-qubit gate. - """ - - def multi_controlled_single_qubit_gate( - self, - matrix: npt.NDArray[np.cdouble], - controls: set[Control | int], - target: int, - ) -> MatrixDD: - r"""Create the DD for a multi-controlled single-qubit gate. - - Args: - matrix: The :math:`2\times 2` matrix representing the single-qubit gate. - controls: The control qubits. - target: The target qubit. - - Returns: - The DD for the multi-controlled single-qubit gate. - """ - - def two_qubit_gate( - self, - matrix: npt.NDArray[np.cdouble], - target0: int, - target1: int, - ) -> MatrixDD: - r"""Create the DD for a two-qubit gate. - - Args: - matrix: The :math:`4\times 4` matrix representing the two-qubit gate. - target0: The first target qubit. - target1: The second target qubit. - - Returns: - The DD for the two-qubit gate. - """ - - def controlled_two_qubit_gate( - self, - matrix: npt.NDArray[np.cdouble], - control: Control | int, - target0: int, - target1: int, - ) -> MatrixDD: - r"""Create the DD for a controlled two-qubit gate. - - Args: - matrix: The :math:`4\times 4` matrix representing the two-qubit gate. - control: The control qubit. - target0: The first target qubit. - target1: The second target qubit. - - Returns: - The DD for the controlled two-qubit gate. - """ - - def multi_controlled_two_qubit_gate( - self, - matrix: npt.NDArray[np.cdouble], - controls: set[Control | int], - target0: int, - target1: int, - ) -> MatrixDD: - r"""Create the DD for a multi-controlled two-qubit gate. - - Args: - matrix: The :math:`4\times 4` matrix representing the two-qubit gate. - controls: The control qubits. - target0: The first target qubit. - target1: The second target qubit. - - Returns: - The DD for the multi-controlled two-qubit gate. - """ - - def from_matrix(self, matrix: npt.NDArray[np.cdouble]) -> MatrixDD: - """Create a DD from a matrix. - - Args: - matrix: The matrix. - Must be square and have a size that is a power of 2. - - Returns: - The DD for the matrix. - """ - - def from_operation(self, operation: Operation, invert: bool = False) -> MatrixDD: - """Create a DD from an operation. - - Args: - operation: The operation. - Must be unitary. - invert: Whether to get the inverse of the operation. - - Returns: - The DD for the operation. - """ - - def inc_ref_vec(self, vec: VectorDD) -> None: - """Increment the reference count of a vector.""" - - def dec_ref_vec(self, vec: VectorDD) -> None: - """Decrement the reference count of a vector.""" - - def inc_ref_mat(self, mat: MatrixDD) -> None: - """Increment the reference count of a matrix.""" - - def dec_ref_mat(self, mat: MatrixDD) -> None: - """Decrement the reference count of a matrix.""" - - def garbage_collect(self, force: bool = False) -> bool: - """Perform garbage collection on the DDPackage. - - Args: - force: Whether to force garbage collection. - If set to True, garbage collection is performed regardless - of the current memory usage. If set to False, garbage collection - is only performed if the memory usage exceeds a certain threshold. - - Returns: - Whether any nodes were collected during garbage collection. - """ - - def vector_add(self, lhs: VectorDD, rhs: VectorDD) -> VectorDD: - """Add two vectors. - - Args: - lhs: The left vector. - rhs: The right vector. - - Returns: - The sum of the two vectors. - - Notes: - It is the caller's responsibility to update the reference count of the - input and output vectors after the operation. - - Both vectors must have the same number of qubits. - """ - - def matrix_add(self, lhs: MatrixDD, rhs: MatrixDD) -> MatrixDD: - """Add two matrices. - - Args: - lhs: The left matrix. - rhs: The right matrix. - - Returns: - The sum of the two matrices. - - Notes: - It is the caller's responsibility to update the reference count of the - input and output matrices after the operation. - - Both matrices must have the same number of qubits. - """ - - def conjugate(self, vec: VectorDD) -> VectorDD: - """Conjugate a vector. - - Args: - vec: The vector. - - Returns: - The conjugated vector. - - Notes: - It is the caller's responsibility to update the reference count of the - input and output vectors after the operation. - """ - - def conjugate_transpose(self, mat: MatrixDD) -> MatrixDD: - """Conjugate transpose a matrix. - - Args: - mat: The matrix. - - Returns: - The conjugate transposed matrix. - - Notes: - It is the caller's responsibility to update the reference count of the - input and output matrices after the operation. - """ - - def matrix_vector_multiply(self, mat: MatrixDD, vec: VectorDD) -> VectorDD: - """Multiply a matrix with a vector. - - Args: - mat: The matrix. - vec: The vector. - - Returns: - The product of the matrix and the vector. - - Notes: - It is the caller's responsibility to update the reference count of the - input and output matrices after the operation. - - The vector must have at least as many qubits as the matrix non-trivially acts on. - """ - - def matrix_multiply(self, lhs: MatrixDD, rhs: MatrixDD) -> MatrixDD: - """Multiply two matrices. - - Args: - lhs: The left matrix. - rhs: The right matrix. - - Returns: - The product of the two matrices. - - Notes: - It is the caller's responsibility to update the reference count of the - input and output matrices after the operation. - """ - - def inner_product(self, lhs: VectorDD, rhs: VectorDD) -> complex: - """Compute the inner product of two vectors. - - Args: - lhs: The left vector. - rhs: The right vector. - - Returns: - The inner product of the two vectors. - - Notes: - Both vectors must have the same number of qubits. - """ - - def fidelity(self, lhs: VectorDD, rhs: VectorDD) -> float: - """Compute the fidelity of two vectors. - - Args: - lhs: The left vector. - rhs: The right vector. - - Returns: - The fidelity of the two vectors. - - Notes: - Both vectors must have the same number of qubits. - """ - - def expectation_value(self, observable: MatrixDD, state: VectorDD) -> float: - r"""Compute the expectation value of an observable. - - Args: - observable: The observable. - state: The state. - - Returns: - The expectation value of the observable. - - Notes: - The state must have at least as many qubits as the observable non-trivially acts on. - - The method computes :math:`\langle \psi | O | \psi \rangle` as - :math:`\langle \psi | (O | \psi \rangle)`. - """ - - def vector_kronecker( - self, top: VectorDD, bottom: VectorDD, bottom_num_qubits: int, increment_index: bool = True - ) -> VectorDD: - """Compute the Kronecker product of two vectors. - - Args: - top: The top vector. - bottom: The bottom vector. - bottom_num_qubits: The number of qubits of the bottom vector. - increment_index: Whether to increment the indexes of the top vector. - - Returns: - The Kronecker product of the two vectors. - - Notes: - It is the caller's responsibility to update the reference count of the - input and output vectors after the operation. - """ - - def matrix_kronecker( - self, top: MatrixDD, bottom: MatrixDD, bottom_num_qubits: int, increment_index: bool = True - ) -> MatrixDD: - """Compute the Kronecker product of two matrices. - - Args: - top: The top matrix. - bottom: The bottom matrix. - bottom_num_qubits: The number of qubits of the bottom matrix. - increment_index: Whether to increment the indexes of the top matrix. - - Returns: - The Kronecker product of the two matrices. - - Notes: - It is the caller's responsibility to update the reference count of the - input and output matrices after the operation. - """ - - def partial_trace(self, mat: MatrixDD, eliminate: list[bool]) -> MatrixDD: - """Compute the partial trace of a matrix. - - Args: - mat: The matrix. - eliminate: The qubits to eliminate. - Must be at least as long as the number of qubits of the matrix. - - Returns: - The partial trace of the matrix. - """ - - def trace(self, mat: MatrixDD, num_qubits: int) -> complex: - """Compute the trace of a matrix. - - Args: - mat: The matrix. - num_qubits: The number of qubits of the matrix. - - Returns: - The trace of the matrix. - """ - -class VectorDD: - """A class representing a vector decision diagram (DD).""" - - def is_terminal(self) -> bool: - """Check if the DD is a terminal node.""" - - def is_zero_terminal(self) -> bool: - """Check if the DD is a zero terminal node.""" - - def size(self) -> int: - """Get the size of the DD by traversing it once.""" - - def __getitem__(self, index: int) -> complex: - """Get the amplitude of a basis state by index.""" - - def get_amplitude(self, num_qubits: int, decisions: str) -> complex: - """Get the amplitude of a basis state by decisions. - - Args: - num_qubits: The number of qubits. - decisions: The decisions as a string of bits (`0` or `1`), where - `decisions[i]` corresponds to the successor to follow at level `i` of the DD. - Must be at least `num_qubits` long. - - Returns: - The amplitude of the basis state. - """ - - def get_vector(self, threshold: float = 0.0) -> Vector: - """Get the state vector represented by the DD. - - Args: - threshold: The threshold for not including amplitudes in the state vector. - Defaults to 0.0. - - Returns: - The state vector. - """ - - def to_dot( - self, - colored: bool = True, - edge_labels: bool = False, - classic: bool = False, - memory: bool = False, - format_as_polar: bool = True, - ) -> str: - """Convert the DD to a DOT graph that can be plotted via Graphviz. - - Args: - colored: Whether to use colored edge weights - edge_labels: Whether to include edge weights as labels. - classic: Whether to use the classic DD visualization style. - memory: Whether to include memory information. - For debugging purposes only. - format_as_polar: Whether to format the edge weights in polar coordinates. - - Returns: - The DOT graph. - """ - - def to_svg( - self, - filename: str, - colored: bool = True, - edge_labels: bool = False, - classic: bool = False, - memory: bool = False, - format_as_polar: bool = True, - ) -> None: - """Convert the DD to an SVG file that can be viewed in a browser. - - Requires the `dot` command from Graphviz to be installed and available in the PATH. - - Args: - filename: The filename of the SVG file. - Any file extension will be replaced by `.dot` and then `.svg`. - colored: Whether to use colored edge weights. - edge_labels: Whether to include edge weights as labels. - classic: Whether to use the classic DD visualization style. - memory: Whether to include memory information. - For debugging purposes only. - show: Whether to open the SVG file in the default browser. - format_as_polar: Whether to format the edge weights in polar coordinates. - """ - -class MatrixDD: - """A class representing a matrix decision diagram (DD).""" - - def is_terminal(self) -> bool: - """Check if the DD is a terminal node.""" - - def is_zero_terminal(self) -> bool: - """Check if the DD is a zero terminal node.""" - - def is_identity(self, up_to_global_phase: bool = True) -> bool: - """Check if the DD represents the identity matrix. - - Args: - up_to_global_phase: Whether to ignore global phase. - - Returns: - Whether the DD represents the identity matrix. - """ - - def size(self) -> int: - """Get the size of the DD by traversing it once.""" - - def get_entry(self, num_qubits: int, row: int, col: int) -> complex: - """Get the entry of the matrix by row and column index.""" - - def get_entry_by_path(self, num_qubits: int, decisions: str) -> complex: - """Get the entry of the matrix by decisions. - - Args: - num_qubits: The number of qubits. - decisions: The decisions as a string of `0`, `1`, `2`, or `3`, where - `decisions[i]` corresponds to the successor to follow at level `i` of the DD. - Must be at least `num_qubits` long. - - Returns: - The entry of the matrix. - """ - - def get_matrix(self, num_qubits: int, threshold: float = 0.0) -> Matrix: - """Get the matrix represented by the DD. - - Args: - num_qubits: The number of qubits. - threshold: The threshold for not including entries in the matrix. - Defaults to 0.0. - - Returns: - The matrix. - """ - - def to_dot( - self, - colored: bool = True, - edge_labels: bool = False, - classic: bool = False, - memory: bool = False, - format_as_polar: bool = True, - ) -> str: - """Convert the DD to a DOT graph that can be plotted via Graphviz. - - Args: - colored: Whether to use colored edge weights - edge_labels: Whether to include edge weights as labels. - classic: Whether to use the classic DD visualization style. - memory: Whether to include memory information. - For debugging purposes only. - format_as_polar: Whether to format the edge weights in polar coordinates. - - Returns: - The DOT graph. - """ - - def to_svg( - self, - filename: str, - colored: bool = True, - edge_labels: bool = False, - classic: bool = False, - memory: bool = False, - format_as_polar: bool = True, - ) -> None: - """Convert the DD to an SVG file that can be viewed in a browser. - - Requires the `dot` command from Graphviz to be installed and available in the PATH. - - Args: - filename: The filename of the SVG file. - Any file extension will be replaced by `.dot` and then `.svg`. - colored: Whether to use colored edge weights. - edge_labels: Whether to include edge weights as labels. - classic: Whether to use the classic DD visualization style. - memory: Whether to include memory information. - For debugging purposes only. - show: Whether to open the SVG file in the default browser. - format_as_polar: Whether to format the edge weights in polar coordinates. - """ - -class BasisStates(Enum): - """Enumeration of basis states.""" - - zero = ... - r"""The computational basis state :math:`|0\rangle`.""" - one = ... - r"""The computational basis state :math:`|1\rangle`.""" - plus = ... - r"""The superposition state :math:`|+\rangle = \frac{1}{\sqrt{2}}(|0\rangle + |1\rangle)`.""" - minus = ... - r"""The superposition state :math:`|-\rangle = \frac{1}{\sqrt{2}}(|0\rangle - |1\rangle)`.""" - left = ... - r"""The rotational superposition state :math:`|L\rangle = \frac{1}{\sqrt{2}}(|0\rangle + i|1\rangle)`.""" - right = ... - r"""The rotational superposition state :math:`|R\rangle = \frac{1}{\sqrt{2}}(|0\rangle - i|1\rangle)`.""" - -class Vector: - """A class representing a vector of complex numbers. - - Implements the buffer protocol so that the underlying memory can be accessed - and easily converted to a NumPy array without copying. - - Examples: - >>> from mqt.core.dd import DDPackage - ... import numpy as np - ... - ... zero_state = DDPackage(2).zero_state(2) - ... vec = np.array(zero_state.get_vector(), copy=False) - ... print(vec) - [1.+0.j 0.+0.j 0.+0.j 0.+0.j] - - """ - - def __buffer__(self, flags: int, /) -> memoryview: - """Return a buffer object that exposes the underlying memory of the object.""" - - def __release_buffer__(self, buffer: memoryview, /) -> None: - """Release the buffer object that exposes the underlying memory of the object.""" - -class Matrix: - """A class representing a matrix of complex numbers. - - Implements the buffer protocol so that the underlying memory can be accessed - and easily converted to a NumPy array without copying. - - Examples: - >>> from mqt.core.dd import DDPackage - ... import numpy as np - ... - ... identity = DDPackage(1).identity() - ... mat = np.array(identity.get_matrix(1), copy=False) - ... print(mat) - [[1.+0.j 0.+0.j] - [0.+0.j 1.+0.j]] - """ - - def __buffer__(self, flags: int, /) -> memoryview: - """Return a buffer object that exposes the underlying memory of the object.""" - - def __release_buffer__(self, buffer: memoryview, /) -> None: - """Release the buffer object that exposes the underlying memory of the object.""" diff --git a/python/mqt/core/fomac.pyi b/python/mqt/core/fomac.pyi deleted file mode 100644 index 4b23b8909a..0000000000 --- a/python/mqt/core/fomac.pyi +++ /dev/null @@ -1,341 +0,0 @@ -# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM -# Copyright (c) 2025 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -import sys -from collections.abc import Iterable -from enum import Enum - -__all__ = [ - "Device", - "Job", - "ProgramFormat", - "Session", -] - -# add_dynamic_device_library is only available on non-Windows platforms -if sys.platform != "win32": - __all__ += ["add_dynamic_device_library"] - -class ProgramFormat(Enum): - """Enumeration of program formats.""" - - QASM2 = ... - QASM3 = ... - QIR_BASE_STRING = ... - QIR_BASE_MODULE = ... - QIR_ADAPTIVE_STRING = ... - QIR_ADAPTIVE_MODULE = ... - CALIBRATION = ... - QPY = ... - IQM_JSON = ... - CUSTOM1 = ... - CUSTOM2 = ... - CUSTOM3 = ... - CUSTOM4 = ... - CUSTOM5 = ... - -class Session: - """A FoMaC session for managing QDMI devices. - - Allows creating isolated sessions with independent authentication settings. - All authentication parameters are optional and can be provided as keyword - arguments to the constructor. - """ - - def __init__( - self, - *, - token: str | None = None, - auth_file: str | None = None, - auth_url: str | None = None, - username: str | None = None, - password: str | None = None, - project_id: str | None = None, - custom1: str | None = None, - custom2: str | None = None, - custom3: str | None = None, - custom4: str | None = None, - custom5: str | None = None, - ) -> None: - """Create a new FoMaC session with optional authentication. - - Args: - token: Authentication token - auth_file: Path to file containing authentication information - auth_url: URL to authentication server - username: Username for authentication - password: Password for authentication - project_id: Project ID for session - custom1: Custom configuration parameter 1 - custom2: Custom configuration parameter 2 - custom3: Custom configuration parameter 3 - custom4: Custom configuration parameter 4 - custom5: Custom configuration parameter 5 - - Raises: - RuntimeError: If auth_file does not exist - RuntimeError: If auth_url has invalid format - - Example: - >>> from mqt.core.fomac import Session - >>> # Session without authentication - >>> session = Session() - >>> devices = session.get_devices() - >>> - >>> # Session with token authentication - >>> session = Session(token="my_secret_token") - >>> devices = session.get_devices() - >>> - >>> # Session with file-based authentication - >>> session = Session(auth_file="/path/to/auth.json") - >>> devices = session.get_devices() - >>> - >>> # Session with multiple parameters - >>> session = Session( - ... auth_url="https://auth.example.com", username="user", password="pass", project_id="project-123" - ... ) - >>> devices = session.get_devices() - """ - - def get_devices(self) -> list[Device]: - """Get available devices from this session. - - Returns: - List of available devices - """ - -class Job: - """A job represents a submitted quantum program execution.""" - - class Status(Enum): - """Enumeration of job status.""" - - CREATED = ... - SUBMITTED = ... - QUEUED = ... - RUNNING = ... - DONE = ... - CANCELED = ... - FAILED = ... - - def check(self) -> Job.Status: - """Returns the current status of the job.""" - def wait(self, timeout: int = 0) -> bool: - """Waits for the job to complete. - - Args: - timeout: The maximum time to wait in seconds. If 0, waits indefinitely. - - Returns: - True if the job completed within the timeout, False otherwise. - """ - def cancel(self) -> None: - """Cancels the job.""" - def get_shots(self) -> list[str]: - """Returns the raw shot results from the job.""" - def get_counts(self) -> dict[str, int]: - """Returns the measurement counts from the job.""" - def get_dense_statevector(self) -> list[complex]: - """Returns the dense statevector from the job (typically only available from simulator devices).""" - def get_sparse_statevector(self) -> dict[str, complex]: - """Returns the sparse statevector from the job (typically only available from simulator devices).""" - def get_dense_probabilities(self) -> list[float]: - """Returns the dense probabilities from the job (typically only available from simulator devices).""" - def get_sparse_probabilities(self) -> dict[str, float]: - """Returns the sparse probabilities from the job (typically only available from simulator devices).""" - def __eq__(self, other: object) -> bool: - """Checks if two jobs are equal.""" - def __ne__(self, other: object) -> bool: - """Checks if two jobs are not equal.""" - @property - def id(self) -> str: - """Returns the job ID.""" - @property - def program_format(self) -> ProgramFormat: - """Returns the program format used for the job.""" - @property - def program(self) -> str: - """Returns the quantum program submitted for the job.""" - @property - def num_shots(self) -> int: - """Returns the number of shots for the job.""" - -class Device: - """A device represents a quantum device with its properties and capabilities.""" - class Status(Enum): - offline = ... - idle = ... - busy = ... - error = ... - maintenance = ... - calibration = ... - - class Site: - """A site represents a potential qubit location on a quantum device.""" - def index(self) -> int: - """Returns the index of the site.""" - def t1(self) -> int | None: - """Returns the T1 coherence time of the site.""" - def t2(self) -> int | None: - """Returns the T2 coherence time of the site.""" - def name(self) -> str | None: - """Returns the name of the site.""" - def x_coordinate(self) -> int | None: - """Returns the x coordinate of the site.""" - def y_coordinate(self) -> int | None: - """Returns the y coordinate of the site.""" - def z_coordinate(self) -> int | None: - """Returns the z coordinate of the site.""" - def is_zone(self) -> bool: - """Returns whether the site is a zone.""" - def x_extent(self) -> int | None: - """Returns the x extent of the site.""" - def y_extent(self) -> int | None: - """Returns the y extent of the site.""" - def z_extent(self) -> int | None: - """Returns the z extent of the site.""" - def module_index(self) -> int | None: - """Returns the index of the module the site belongs to.""" - def submodule_index(self) -> int | None: - """Returns the index of the submodule the site belongs to.""" - def __eq__(self, other: object) -> bool: - """Checks if two sites are equal.""" - def __ne__(self, other: object) -> bool: - """Checks if two sites are not equal.""" - - class Operation: - """An operation represents a quantum operation that can be performed on a quantum device.""" - def name(self, sites: Iterable[Device.Site] = ..., params: Iterable[float] = ...) -> str: - """Returns the name of the operation.""" - def qubits_num(self, sites: Iterable[Device.Site] = ..., params: Iterable[float] = ...) -> int | None: - """Returns the number of qubits the operation acts on.""" - def parameters_num(self, sites: Iterable[Device.Site] = ..., params: Iterable[float] = ...) -> int: - """Returns the number of parameters the operation has.""" - def duration(self, sites: Iterable[Device.Site] = ..., params: Iterable[float] = ...) -> int | None: - """Returns the duration of the operation.""" - def fidelity(self, sites: Iterable[Device.Site] = ..., params: Iterable[float] = ...) -> float | None: - """Returns the fidelity of the operation.""" - def interaction_radius(self, sites: Iterable[Device.Site] = ..., params: Iterable[float] = ...) -> int | None: - """Returns the interaction radius of the operation.""" - def blocking_radius(self, sites: Iterable[Device.Site] = ..., params: Iterable[float] = ...) -> int | None: - """Returns the blocking radius of the operation.""" - def idling_fidelity(self, sites: Iterable[Device.Site] = ..., params: Iterable[float] = ...) -> float | None: - """Returns the idling fidelity of the operation.""" - def is_zoned(self) -> bool: - """Returns whether the operation is zoned.""" - def sites(self) -> list[Device.Site] | None: - """Returns the list of sites the operation can be performed on.""" - def site_pairs(self) -> list[tuple[Device.Site, Device.Site]] | None: - """Returns the list of site pairs the local 2-qubit operation can be performed on.""" - def mean_shuttling_speed(self, sites: Iterable[Device.Site] = ..., params: Iterable[float] = ...) -> int | None: - """Returns the mean shuttling speed of the operation.""" - def __eq__(self, other: object) -> bool: - """Checks if two operations are equal.""" - def __ne__(self, other: object) -> bool: - """Checks if two operations are not equal.""" - - def name(self) -> str: - """Returns the name of the device.""" - def version(self) -> str: - """Returns the version of the device.""" - def status(self) -> Device.Status: - """Returns the current status of the device.""" - def library_version(self) -> str: - """Returns the version of the library used to define the device.""" - def qubits_num(self) -> int: - """Returns the number of qubits available on the device.""" - def sites(self) -> list[Site]: - """Returns the list of all sites (zone and regular sites) available on the device.""" - def regular_sites(self) -> list[Site]: - """Returns the list of regular sites (without zone sites) available on the device.""" - def zones(self) -> list[Site]: - """Returns the list of zone sites (without regular sites) available on the device.""" - def operations(self) -> list[Operation]: - """Returns the list of operations supported by the device.""" - def coupling_map(self) -> list[tuple[Site, Site]] | None: - """Returns the coupling map of the device as a list of site pairs.""" - def needs_calibration(self) -> int | None: - """Returns whether the device needs calibration.""" - def length_unit(self) -> str | None: - """Returns the unit of length used by the device.""" - def length_scale_factor(self) -> float | None: - """Returns the scale factor for length used by the device.""" - def duration_unit(self) -> str | None: - """Returns the unit of duration used by the device.""" - def duration_scale_factor(self) -> float | None: - """Returns the scale factor for duration used by the device.""" - def min_atom_distance(self) -> int | None: - """Returns the minimum atom distance on the device.""" - def supported_program_formats(self) -> list[ProgramFormat]: - """Returns the list of program formats supported by the device.""" - def submit_job(self, program: str, program_format: ProgramFormat, num_shots: int) -> Job: - """Submits a job to the device.""" - def __eq__(self, other: object) -> bool: - """Checks if two devices are equal.""" - def __ne__(self, other: object) -> bool: - """Checks if two devices are not equal.""" - -# Dynamic library loading is only available on non-Windows platforms -if sys.platform != "win32": - def add_dynamic_device_library( - library_path: str, - prefix: str, - *, - base_url: str | None = None, - token: str | None = None, - auth_file: str | None = None, - auth_url: str | None = None, - username: str | None = None, - password: str | None = None, - custom1: str | None = None, - custom2: str | None = None, - custom3: str | None = None, - custom4: str | None = None, - custom5: str | None = None, - ) -> Device: - """Load a dynamic device library into the QDMI driver. - - This function loads a shared library (.so, .dll, or .dylib) that implements - a QDMI device interface and makes it available for use in sessions. - - Note: This function is only available on non-Windows platforms. - - Args: - library_path: Path to the shared library file to load. - prefix: Function prefix used by the library (e.g., "MY_DEVICE"). - base_url: Optional base URL for the device API endpoint. - token: Optional authentication token. - auth_file: Optional path to authentication file. - auth_url: Optional authentication server URL. - username: Optional username for authentication. - password: Optional password for authentication. - custom1: Optional custom configuration parameter 1. - custom2: Optional custom configuration parameter 2. - custom3: Optional custom configuration parameter 3. - custom4: Optional custom configuration parameter 4. - custom5: Optional custom configuration parameter 5. - - Returns: - Device: The newly loaded device that can be used to create backends. - - Raises: - RuntimeError: If library loading fails or configuration is invalid. - - Examples: - Load a device library with configuration: - - >>> import mqt.core.fomac as fomac - >>> device = fomac.add_dynamic_device_library( - ... "/path/to/libmy_device.so", "MY_DEVICE", base_url="http://localhost:8080", custom1="API_V2" - ... ) - - Now the device can be used directly: - - >>> from mqt.core.plugins.qiskit import QDMIBackend - >>> backend = QDMIBackend(device=device) - """ diff --git a/python/mqt/core/ir/__init__.pyi b/python/mqt/core/ir/__init__.pyi deleted file mode 100644 index 7f46fa37f1..0000000000 --- a/python/mqt/core/ir/__init__.pyi +++ /dev/null @@ -1,2078 +0,0 @@ -# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM -# Copyright (c) 2025 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -"""MQT Core IR - The MQT Core Intermediate Representation (IR) module.""" - -from collections.abc import ItemsView, Iterable, Iterator, Mapping, MutableMapping, MutableSequence, Sequence -from os import PathLike -from typing import overload - -from .operations import ComparisonKind, Control, Operation, OpType -from .registers import ClassicalRegister, QuantumRegister -from .symbolic import Expression, Variable - -__all__ = [ - "Permutation", - "QuantumComputation", -] - -class Permutation(MutableMapping[int, int]): - """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, permutation: dict[int, int] | None = None) -> None: - """Initialize the permutation.""" - - def __getitem__(self, idx: int) -> int: - """Get the value of the permutation at the given index. - - Args: - idx: The index to get the value of the permutation at. - - Returns: - The value of the permutation at the given index. - """ - - def __setitem__(self, idx: int, val: int) -> None: - """Set the value of the permutation at the given index. - - Args: - 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, 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) -> Iterator[int]: - """Return an iterator over the indices of the permutation.""" - - def items(self) -> ItemsView[int, int]: - """Return an iterable over the items of the permutation.""" - - def __len__(self) -> int: - """Return the number of indices in the permutation.""" - - def __eq__(self, other: object) -> bool: - """Check if the permutation is equal to another permutation.""" - - def __ne__(self, other: object) -> bool: - """Check if the permutation is not equal to another permutation.""" - - def __hash__(self) -> int: - """Return the hash of the permutation.""" - - def clear(self) -> None: - """Clear the permutation of all indices and values.""" - - @overload - def apply(self, controls: set[Control]) -> set[Control]: - """Apply the permutation to a set of controls. - - Args: - controls: The set of controls to apply the permutation to. - - Returns: - The set of controls with the permutation applied. - """ - - @overload - def apply(self, targets: list[int]) -> list[int]: - """Apply the permutation to a list of targets. - - Args: - targets: The list of targets to apply the permutation to. - - Returns: - The list of targets with the permutation applied. - """ - -class QuantumComputation(MutableSequence[Operation]): - """The main class for representing quantum computations within the MQT. - - Acts as mutable sequence of :class:`~mqt.core.ir.operations.Operation` objects, - which represent the individual operations in the quantum computation. - - Args: - nq: The number of qubits in the quantum computation. - nc: The number of classical bits in the quantum computation. - seed: The seed to use for the internal random number generator. - """ - - # -------------------------------------------------------------------------- - # Constructors - # -------------------------------------------------------------------------- - def __init__(self, nq: int = 0, nc: int = 0, seed: int = 0) -> None: ... - @staticmethod - def from_qasm_str(qasm: str) -> QuantumComputation: - """Create a QuantumComputation object from an OpenQASM string. - - Args: - qasm: The OpenQASM string to create the QuantumComputation object from. - - Returns: - The QuantumComputation object created from the OpenQASM string. - """ - @staticmethod - def from_qasm(filename: str) -> QuantumComputation: - """Create a QuantumComputation object from an OpenQASM file. - - Args: - filename: The filename of the OpenQASM file to create the QuantumComputation object from. - - Returns: - The QuantumComputation object created from the OpenQASM file. - """ - - # -------------------------------------------------------------------------- - # General Properties - # -------------------------------------------------------------------------- - - name: str - """ - The name of the quantum computation. - """ - global_phase: float - """ - The global phase of the quantum computation. - """ - - @property - def num_qubits(self) -> int: - """The total number of qubits in the quantum computation.""" - - @property - def num_ancilla_qubits(self) -> int: - r"""The number of ancilla qubits in the quantum computation. - - Note: - Ancilla qubits are qubits that always start in a fixed state (usually :math:`|0\\rangle`). - """ - - @property - def num_garbage_qubits(self) -> int: - """The number of garbage qubits in the quantum computation. - - Note: - Garbage qubits are qubits whose final state is not relevant for the computation. - """ - - @property - def num_measured_qubits(self) -> int: - """The number of qubits that are measured in the quantum computation. - - Computed as :math:`|qubits| - |garbage|`. - """ - - @property - def num_data_qubits(self) -> int: - """The number of data qubits in the quantum computation. - - Computed as :math:`|qubits| - |ancilla|`. - """ - - @property - def num_classical_bits(self) -> int: - """The number of classical bits in the quantum computation.""" - - @property - def num_ops(self) -> int: - """The number of operations in the quantum computation.""" - - def num_single_qubit_ops(self) -> int: - """Return the number of single-qubit operations in the quantum computation.""" - - def num_total_ops(self) -> int: - """Return the total number of operations in the quantum computation. - - Recursively counts sub-operations (e.g., from :class:`~mqt.core.ir.operations.CompoundOperation` objects). - """ - - def depth(self) -> int: - """Return the depth of the quantum computation.""" - - def invert(self) -> None: - """Invert the quantum computation in-place by inverting each operation and reversing the order of operations.""" - - def to_operation(self) -> Operation: - """Convert the quantum computation to a single operation. - - This gives ownership of the operations to the resulting operation, - so the quantum computation will be empty after this operation. - - When the quantum computation contains more than one operation, - the resulting operation is a :class:`~mqt.core.ir.operations.CompoundOperation`. - - Returns: - The operation representing the quantum computation. - """ - - # -------------------------------------------------------------------------- - # Mutable Sequence Interface - # -------------------------------------------------------------------------- - - def __len__(self) -> int: - """Return the number of operations in the quantum computation.""" - - @overload - def __getitem__(self, index: int) -> Operation: - """Get the operation at the given index. - - Note: - This gives write access to the operation at the given index. - - Args: - index: The index of the operation to get. - - Returns: - The operation at the given index. - """ - - @overload - def __getitem__(self, index: slice) -> list[Operation]: - """Get a slice of operations from the quantum computation. - - Note: - This gives write access to the operations in the given slice. - - Args: - index: The slice of operations to get. - - Returns: - The operations in the given slice. - """ - - @overload - def __setitem__(self, index: int, value: Operation) -> None: - """Set the operation at the given index. - - Args: - index: The index of the operation to set. - value: The operation to set at the given index. - """ - - @overload - def __setitem__(self, index: slice, value: Iterable[Operation]) -> None: - """Set the operations in the given slice. - - Args: - index: The slice of operations to set. - value: The operations to set in the given slice. - """ - - @overload - def __delitem__(self, index: int) -> None: - """Delete the operation at the given index. - - Args: - index: The index of the operation to delete. - """ - - @overload - def __delitem__(self, index: slice) -> None: - """Delete the operations in the given slice. - - Args: - index: The slice of operations to delete. - """ - - def insert(self, index: int, value: Operation) -> None: - """Insert an operation at the given index. - - Args: - index: The index to insert the operation at. - value: The operation to insert. - """ - - def append(self, value: Operation) -> None: - """Append an operation to the end of the quantum computation. - - Args: - value: The operation to append. - """ - - def reverse(self) -> None: - """Reverse the order of the operations in the quantum computation (in-place).""" - - def clear(self) -> None: - """Clear the quantum computation of all operations.""" - - # -------------------------------------------------------------------------- - # (Qu)Bit Registers - # -------------------------------------------------------------------------- - - def add_ancillary_register(self, n: int, name: str = "anc") -> QuantumRegister: - """Add an ancillary register to the quantum computation. - - Args: - n: The number of qubits in the ancillary register. - name: The name of the ancillary register. - - Returns: - The ancillary register added to the quantum computation. - """ - - def add_classical_register(self, n: int, name: str = "c") -> ClassicalRegister: - """Add a classical register to the quantum computation. - - Args: - n: The number of bits in the classical register. - name: The name of the classical register. - - Returns: - The classical register added to the quantum computation. - """ - - def add_qubit_register(self, n: int, name: str = "q") -> QuantumRegister: - """Add a qubit register to the quantum computation. - - Args: - n: The number of qubits in the qubit register. - name: The name of the qubit register. - - Returns: - The qubit register added to the quantum computation. - """ - - def unify_quantum_registers(self, name: str = "q") -> QuantumRegister: - """Unify all quantum registers in the quantum computation. - - Args: - name: The name of the unified quantum register. - - Returns: - The unified quantum register. - """ - - @property - def qregs(self) -> dict[str, QuantumRegister]: - """The quantum registers in the quantum computation.""" - - @property - def cregs(self) -> dict[str, ClassicalRegister]: - """The classical registers in the quantum computation.""" - - @property - def ancregs(self) -> dict[str, QuantumRegister]: - """The ancillary registers in the quantum computation.""" - - # -------------------------------------------------------------------------- - # Initial Layout and Output Permutation - # -------------------------------------------------------------------------- - - initial_layout: Permutation - """ - The initial layout of the qubits in the quantum computation. - - This is a permutation of the qubits in the quantum computation. It is mainly - used to track the mapping of circuit qubits to device qubits during quantum - circuit compilation. The keys are the device qubits (in which a compiled circuit - is expressed in), and the values are the circuit qubits (in which the original - quantum circuit is expressed in). - - Any operations in the quantum circuit are expected to be expressed in terms - of the keys of the initial layout. - - Examples: - - If no initial layout is explicitly specified (which is the default), - the initial layout is assumed to be the identity permutation. - - Assume a three-qubit circuit has been compiled to a four qubit device - and circuit qubit 0 is mapped to device qubit 1, circuit qubit 1 is - mapped to device qubit 2, and circuit qubit 2 is mapped to device qubit 3. - Then the initial layout is {1: 0, 2: 1, 3: 2}. - - """ - output_permutation: Permutation - """ - The output permutation of the qubits in the quantum computation. - - This is a permutation of the qubits in the quantum computation. It is mainly - used to track where individual qubits end up at the end of the quantum computation, - for example after a circuit has been compiled to a specific device and SWAP - gates have been inserted, which permute the qubits. The keys are the qubits - in the circuit and the values are the actual qubits being measured. - - Similar to the initial layout, the keys in the output permutation are the - qubits actually present in the circuit and the values are the qubits in the - "original" circuit. - - Examples: - - If no output permutation is explicitly specified and the circuit does - not contain measurements at the end, the output permutation is assumed - to be the identity permutation. - - If the circuit contains measurements at the end, these measurements - are used to infer the output permutation. Assume a three-qubit circuit - has been compiled to a four qubit device and, at the end of the circuit, - circuit qubit 0 is measured into classical bit 2, circuit qubit 1 is - measured into classical bit 1, and circuit qubit 3 is measured into - classical bit 0. Then the output permutation is {0: 2, 1: 1, 3: 0}. - """ - - def initialize_io_mapping(self) -> None: - """Initialize the I/O mapping of the quantum computation. - - If no initial layout is explicitly specified, the initial layout is assumed - to be the identity permutation. If the circuit contains measurements at the - end, these measurements are used to infer the output permutation. - """ - - # -------------------------------------------------------------------------- - # Ancilla and Garbage Handling - # -------------------------------------------------------------------------- - - @property - def ancillary(self) -> list[bool]: - """A list of booleans indicating whether each qubit is ancillary.""" - - def set_circuit_qubit_ancillary(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, q_min: int, q_max: int) -> None: - """Set a range of circuit (i.e., logical) qubits to be ancillary. - - Args: - 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, q: int) -> bool: - """Check if a circuit (i.e., logical) qubit is ancillary. - - Args: - q: The index of the circuit qubit to check. - - Returns: - True if the circuit qubit is ancillary, False otherwise. - """ - - @property - def garbage(self) -> list[bool]: - """A list of booleans indicating whether each qubit is garbage.""" - - def set_circuit_qubit_garbage(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, q_min: int, q_max: int) -> None: - """Set a range of circuit (i.e., logical) qubits to be garbage. - - Args: - 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, q: int) -> bool: - """Check if a circuit (i.e., logical) qubit is garbage. - - Args: - q: The index of the circuit qubit to check. - - Returns: - True if the circuit qubit is garbage, False otherwise. - """ - - # -------------------------------------------------------------------------- - # Symbolic Circuit Handling - # -------------------------------------------------------------------------- - - @property - def variables(self) -> set[Variable]: - """The set of variables in the quantum computation.""" - - def add_variable(self, var: Expression | float) -> None: - """Add a variable to the quantum computation. - - Args: - var: The variable to add. - """ - - def add_variables(self, vars_: Sequence[Expression | float]) -> None: - """Add multiple variables to the quantum computation. - - Args: - vars_: The variables to add. - """ - - def is_variable_free(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, assignment: Mapping[Variable, float]) -> QuantumComputation: - """Instantiate the quantum computation with the given variable assignment. - - Args: - assignment: The variable assignment to instantiate the quantum computation with. - - Returns: - The instantiated quantum computation. - """ - - def instantiate_inplace(self, assignment: Mapping[Variable, float]) -> None: - """Instantiate the quantum computation with the given variable assignment in-place. - - Args: - assignment: The variable assignment to instantiate the quantum computation with. - """ - - # -------------------------------------------------------------------------- - # Output Handling - # -------------------------------------------------------------------------- - - def qasm2_str(self) -> str: - """Return the OpenQASM2 representation of the quantum computation as a string. - - Note: - This uses some custom extensions to OpenQASM 2.0 that allow for easier - definition of multi-controlled gates. These extensions might not be - supported by all OpenQASM 2.0 parsers. Consider using the :meth:`qasm3_str` - method instead, which uses OpenQASM 3.0 that natively supports - multi-controlled gates. The export also assumes the bigger, non-standard - `qelib1.inc` from Qiskit is available. - - Returns: - The OpenQASM2 representation of the quantum computation as a string. - """ - - def qasm2(self, filename: PathLike[str] | str) -> None: - """Write the OpenQASM2 representation of the quantum computation to a file. - - See Also: - :meth:`qasm2_str` - - Args: - filename: The filename of the file to write the OpenQASM2 representation to. - """ - - def qasm3_str(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, filename: PathLike[str] | str) -> None: - """Write the OpenQASM3 representation of the quantum computation to a file. - - See Also: - :meth:`qasm3_str` - - Args: - filename: The filename of the file to write the OpenQASM3 representation to. - """ - - # -------------------------------------------------------------------------- - # Operations - # -------------------------------------------------------------------------- - - def i(self, q: int) -> None: - r"""Apply an identity operation. - - .. math:: - I = \begin{pmatrix} 1 & 0 \\ 0 & 1 \end{pmatrix} - - Args: - q: The target qubit - """ - - def ci(self, control: Control | int, target: int) -> None: - """Apply a controlled identity operation. - - Args: - control: The control qubit - target: The target qubit - - See Also: - :meth:`i` - """ - - def mci(self, controls: set[Control | int], target: int) -> None: - """Apply a multi-controlled identity operation. - - Args: - controls: The control qubits - target: The target qubit - - See Also: - :meth:`i` - """ - - def x(self, q: int) -> None: - r"""Apply a Pauli-X gate. - - .. math:: - X = \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix} - - Args: - q: The target qubit - """ - - def cx(self, control: Control | int, target: int) -> None: - """Apply a controlled Pauli-X (CNOT / CX) gate. - - Args: - control: The control qubit - target: The target qubit - - See Also: - :meth:`x` - """ - - def mcx(self, controls: set[Control | int], target: int) -> None: - """Apply a multi-controlled Pauli-X (Toffoli / MCX) gate. - - Args: - controls: The control qubits - target: The target qubit - - See Also: - :meth:`x` - """ - - def y(self, q: int) -> None: - r"""Apply a Pauli-Y gate. - - .. math:: - Y = \begin{pmatrix} 0 & -i \\ i & 0 \end{pmatrix} - - Args: - q: The target qubit - """ - - def cy(self, control: Control | int, target: int) -> None: - """Apply a controlled Pauli-Y gate. - - Args: - control: The control qubit - target: The target qubit - - See Also: - :meth:`y` - """ - - def mcy(self, controls: set[Control | int], target: int) -> None: - """Apply a multi-controlled Pauli-Y gate. - - Args: - controls: The control qubits - target: The target qubit - - See Also: - :meth:`y` - """ - - def z(self, q: int) -> None: - r"""Apply a Pauli-Z gate. - - .. math:: - Z = \begin{pmatrix} 1 & 0 \\ 0 & -1 \end{pmatrix} - - Args: - q: The target qubit - """ - - def cz(self, control: Control | int, target: int) -> None: - """Apply a controlled Pauli-Z (CZ) gate. - - Args: - control: The control qubit - target: The target qubit - - See Also: - :meth:`z` - """ - - def mcz(self, controls: set[Control | int], target: int) -> None: - """Apply a multi-controlled Pauli-Z (MCZ) gate. - - Args: - controls: The control qubits - target: The target qubit - - See Also: - :meth:`z` - """ - - def h(self, q: int) -> None: - r"""Apply a Hadamard gate. - - .. math:: - H = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix} - - Args: - q: The target qubit - """ - - def ch(self, control: Control | int, target: int) -> None: - """Apply a controlled Hadamard gate. - - Args: - control: The control qubit - target: The target qubit - - See Also: - :meth:`h` - """ - - def mch(self, controls: set[Control | int], target: int) -> None: - """Apply a multi-controlled Hadamard gate. - - Args: - controls: The control qubits - target: The target qubit - - See Also: - :meth:`h` - """ - - def s(self, q: int) -> None: - r"""Apply an S gate (phase gate). - - .. math:: - S = \begin{pmatrix} 1 & 0 \\ 0 & i \end{pmatrix} - - Args: - q: The target qubit - """ - - def cs(self, control: Control | int, target: int) -> None: - """Apply a controlled S gate (CS gate). - - Args: - control: The control qubit - target: The target qubit - - See Also: - :meth:`s` - """ - - def mcs(self, controls: set[Control | int], target: int) -> None: - """Apply a multi-controlled S gate. - - Args: - controls: The control qubits - target: The target qubit - - See Also: - :meth:`s` - """ - - def sdg(self, q: int) -> None: - r"""Apply an :math:`S^{\dagger}` gate. - - .. math:: - S^{\dagger} = \begin{pmatrix} 1 & 0 \\ 0 & -i \end{pmatrix} - - Args: - q: The target qubit - """ - - def csdg(self, control: Control | int, target: int) -> None: - r"""Apply a controlled :math:`S^{\dagger}` gate. - - Args: - control: The control qubit - target: The target qubit - - See Also: - :meth:`sdg` - """ - - def mcsdg(self, controls: set[Control | int], target: int) -> None: - r"""Apply a multi-controlled :math:`S^{\dagger}` gate. - - Args: - controls: The control qubits - target: The target qubit - - See Also: - :meth:`sdg` - """ - - def t(self, q: int) -> None: - r"""Apply a T gate. - - .. math:: - T = \begin{pmatrix} 1 & 0 \\ 0 & e^{i\pi/4} \end{pmatrix} - - Args: - q: The target qubit - """ - - def ct(self, control: Control | int, target: int) -> None: - """Apply a controlled T gate. - - Args: - control: The control qubit - target: The target qubit - - See Also: - :meth:`t` - """ - - def mct(self, controls: set[Control | int], target: int) -> None: - """Apply a multi-controlled T gate. - - Args: - controls: The control qubits - target: The target qubit - - See Also: - :meth:`t` - """ - - def tdg(self, q: int) -> None: - r"""Apply a :math:`T^{\dagger}` gate. - - .. math:: - T^{\dagger} = \begin{pmatrix} 1 & 0 \\ 0 & e^{-i\pi/4} \end{pmatrix} - - Args: - q: The target qubit - """ - - def ctdg(self, control: Control | int, target: int) -> None: - r"""Apply a controlled :math:`T^{\dagger}` gate. - - Args: - control: The control qubit - target: The target qubit - - See Also: - :meth:`tdg` - """ - - def mctdg(self, controls: set[Control | int], target: int) -> None: - r"""Apply a multi-controlled :math:`T^{\dagger}` gate. - - Args: - controls: The control qubits - target: The target qubit - - See Also: - :meth:`tdg` - """ - - def v(self, q: int) -> None: - r"""Apply a V gate. - - .. math:: - V = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & -i \\ -i & 1 \end{pmatrix} - - Args: - q: The target qubit - """ - - def cv(self, control: Control | int, target: int) -> None: - """Apply a controlled V gate. - - Args: - control: The control qubit - target: The target qubit - - See Also: - :meth:`v` - """ - - def mcv(self, controls: set[Control | int], target: int) -> None: - """Apply a multi-controlled V gate. - - Args: - controls: The control qubits - target: The target qubit - - See Also: - :meth:`v` - """ - - def vdg(self, q: int) -> None: - r"""Apply a :math:`V^{\dagger}` gate. - - .. math:: - V^{\dagger} = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & i \\ i & 1 \end{pmatrix} - - Args: - q: The target qubit - """ - - def cvdg(self, control: Control | int, target: int) -> None: - r"""Apply a controlled :math:`V^{\dagger}` gate. - - Args: - control: The control qubit - target: The target qubit - - See Also: - :meth:`vdg` - """ - - def mcvdg(self, controls: set[Control | int], target: int) -> None: - r"""Apply a multi-controlled :math:`V^{\dagger}` gate. - - Args: - controls: The control qubits - target: The target qubit - - See Also: - :meth:`vdg` - """ - - def sx(self, q: int) -> None: - r"""Apply a :math:`\sqrt{X}` gate. - - .. math:: - \sqrt{X} = \frac{1}{2} \begin{pmatrix} 1 + i & 1 - i \\ 1 - i & 1 + i \end{pmatrix} - - Args: - q: The target qubit - """ - - def csx(self, control: Control | int, target: int) -> None: - r"""Apply a controlled :math:`\sqrt{X}` gate. - - Args: - control: The control qubit - target: The target qubit - - See Also: - :meth:`sx` - """ - - def mcsx(self, controls: set[Control | int], target: int) -> None: - r"""Apply a multi-controlled :math:`\sqrt{X}` gate. - - Args: - controls: The control qubits - target: The target qubit - - See Also: - :meth:`sx` - """ - - def sxdg(self, q: int) -> None: - r"""Apply a :math:`\sqrt{X}^{\dagger}` gate. - - .. math:: - \sqrt{X}^{\dagger} = \frac{1}{2} \begin{pmatrix} 1 - i & 1 + i \\ 1 + i & 1 - i \end{pmatrix} - - Args: - q: The target qubit - """ - - def csxdg(self, control: Control | int, target: int) -> None: - r"""Apply a controlled :math:`\sqrt{X}^{\dagger}` gate. - - Args: - control: The control qubit - target: The target qubit - - See Also: - :meth:`sxdg` - """ - - def mcsxdg(self, controls: set[Control | int], target: int) -> None: - r"""Apply a multi-controlled :math:`\sqrt{X}^{\dagger}` gate. - - Args: - controls: The control qubits - target: The target qubit - - See Also: - :meth:`sxdg` - """ - - def r(self, theta: float | Expression, phi: float | Expression, q: int) -> None: - r"""Apply an :math:`R(\theta, \phi)` gate. - - .. math:: - R(\theta, \phi) = e^{-i\frac{\theta}{2}(\cos(\phi)X+\sin(\phi)Y)} - = \begin{pmatrix} \cos(\theta/2) & -i e^{-i\phi} \sin(\theta/2) \\ - -i e^{i\phi} \sin(\theta/2) & \cos(\theta/2) \end{pmatrix} - - Args: - theta: The rotation angle :math:`\theta` - phi: The angle specifying the rotation axis given by :math:`\cos(\phi)X+\sin(\phi)Y` - q: The target qubit - """ - - def cr(self, theta: float | Expression, phi: float | Expression, control: Control | int, target: int) -> None: - r"""Apply a controlled :math:`R(\theta, \phi)` gate. - - Args: - theta: The rotation angle :math:`\theta` - phi: The angle specifying the rotation axis given by :math:`\cos(\phi)X+\sin(\phi)Y` - control: The control qubit - target: The target qubit - - See Also: - :meth:`r` - """ - - def mcr( - self, theta: float | Expression, phi: float | Expression, controls: set[Control | int], target: int - ) -> None: - r"""Apply a multi-controlled :math:`R(\theta, \phi)` gate. - - Args: - theta: The rotation angle :math:`\theta` - phi: The angle specifying the rotation axis given by :math:`\cos(\phi)X+\sin(\phi)Y` - controls: The control qubits - target: The target qubit - - See Also: - :meth:`r` - """ - - def rx(self, theta: float | Expression, q: int) -> None: - r"""Apply an :math:`R_x(\theta)` gate. - - .. math:: - R_x(\theta) = e^{-i\theta X/2} = \cos(\theta/2) I - i \sin(\theta/2) X - = \begin{pmatrix} \cos(\theta/2) & -i \sin(\theta/2) \\ -i \sin(\theta/2) & \cos(\theta/2) \end{pmatrix} - - Args: - theta: The rotation angle - q: The target qubit - """ - - def crx(self, theta: float | Expression, control: Control | int, target: int) -> None: - r"""Apply a controlled :math:`R_x(\theta)` gate. - - Args: - theta: The rotation angle - control: The control qubit - target: The target qubit - - See Also: - :meth:`rx` - """ - - def mcrx(self, theta: float | Expression, controls: set[Control | int], target: int) -> None: - r"""Apply a multi-controlled :math:`R_x(\theta)` gate. - - Args: - theta: The rotation angle - controls: The control qubits - target: The target qubit - - See Also: - :meth:`rx` - """ - - def ry(self, theta: float | Expression, q: int) -> None: - r"""Apply an :math:`R_y(\theta)` gate. - - .. math:: - R_y(\theta) = e^{-i\theta Y/2} = \cos(\theta/2) I - i \sin(\theta/2) Y - = \begin{pmatrix} \cos(\theta/2) & -\sin(\theta/2) \\ \sin(\theta/2) & \cos(\theta/2) \end{pmatrix} - - Args: - theta: The rotation angle - q: The target qubit - """ - - def cry(self, theta: float | Expression, control: Control | int, target: int) -> None: - r"""Apply a controlled :math:`R_y(\theta)` gate. - - Args: - theta: The rotation angle - control: The control qubit - target: The target qubit - - See Also: - :meth:`ry` - """ - - def mcry(self, theta: float | Expression, controls: set[Control | int], target: int) -> None: - r"""Apply a multi-controlled :math:`R_y(\theta)` gate. - - Args: - theta: The rotation angle - controls: The control qubits - target: The target qubit - - See Also: - :meth:`ry` - """ - - def rz(self, theta: float | Expression, q: int) -> None: - r"""Apply an :math:`R_z(\theta)` gate. - - .. math:: - R_z(\theta) = e^{-i\theta Z/2} = \begin{pmatrix} e^{-i\theta/2} & 0 \\ 0 & e^{i\theta/2} \end{pmatrix} - - Args: - theta: The rotation angle - q: The target qubit - """ - - def crz(self, theta: float | Expression, control: Control | int, target: int) -> None: - r"""Apply a controlled :math:`R_z(\theta)` gate. - - Args: - theta: The rotation angle - control: The control qubit - target: The target qubit - - See Also: - :meth:`rz` - """ - - def mcrz(self, theta: float | Expression, controls: set[Control | int], target: int) -> None: - r"""Apply a multi-controlled :math:`R_z(\theta)` gate. - - Args: - theta: The rotation angle - controls: The control qubits - target: The target qubit - - See Also: - :meth:`rz` - """ - - def p(self, theta: float | Expression, q: int) -> None: - r"""Apply a phase gate. - - .. math:: - P(\theta) = \begin{pmatrix} 1 & 0 \\ 0 & e^{i\theta} \end{pmatrix} - - Args: - theta: The rotation angle - q: The target qubit - """ - - def cp(self, theta: float | Expression, control: Control | int, target: int) -> None: - """Apply a controlled phase gate. - - Args: - theta: The rotation angle - control: The control qubit - target: The target qubit - - See Also: - :meth:`p` - """ - - def mcp(self, theta: float | Expression, controls: set[Control | int], target: int) -> None: - """Apply a multi-controlled phase gate. - - Args: - theta: The rotation angle - controls: The control qubits - target: The target qubit - - See Also: - :meth:`p` - """ - - def u2(self, phi: float | Expression, lambda_: float | Expression, q: int) -> None: - r"""Apply a :math:`U_2(\phi, \lambda)` gate. - - .. math:: - U_2(\phi, \lambda) = - \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & -e^{i\lambda} \\ e^{i\phi} & e^{i(\phi + \lambda)} \end{pmatrix} - - Args: - phi: The rotation angle - lambda_: The rotation angle - q: The target qubit - """ - - def cu2(self, phi: float | Expression, lambda_: float | Expression, control: Control | int, target: int) -> None: - r"""Apply a controlled :math:`U_2(\phi, \lambda)` gate. - - Args: - phi: The rotation angle - lambda_: The rotation angle - control: The control qubit - target: The target qubit - - See Also: - :meth:`u2` - """ - - def mcu2( - self, - phi: float | Expression, - lambda_: float | Expression, - controls: set[Control | int], - target: int, - ) -> None: - r"""Apply a multi-controlled :math:`U_2(\phi, \lambda)` gate. - - Args: - phi: The rotation angle - lambda_: The rotation angle - controls: The control qubits - target: The target qubit - - See Also: - :meth:`u2` - """ - - def u(self, theta: float | Expression, phi: float | Expression, lambda_: float | Expression, q: int) -> None: - r"""Apply a :math:`U(\theta, \phi, \lambda)` gate. - - .. math:: - U(\theta, \phi, \lambda) = - \begin{pmatrix} \cos(\theta/2) & -e^{i\lambda}\sin(\theta/2) \\ - e^{i\phi}\sin(\theta/2) & e^{i(\phi + \lambda)}\cos(\theta/2) \end{pmatrix} - - Args: - theta: The rotation angle - phi: The rotation angle - lambda_: The rotation angle - q: The target qubit - """ - - def cu( - self, - theta: float | Expression, - phi: float | Expression, - lambda_: float | Expression, - control: Control | int, - target: int, - ) -> None: - r"""Apply a controlled :math:`U(\theta, \phi, \lambda)` gate. - - Args: - theta: The rotation angle - phi: The rotation angle - lambda_: The rotation angle - control: The control qubit - target: The target qubit - - See Also: - :meth:`u` - """ - - def mcu( - self, - theta: float | Expression, - phi: float | Expression, - lambda_: float | Expression, - controls: set[Control | int], - target: int, - ) -> None: - r"""Apply a multi-controlled :math:`U(\theta, \phi, \lambda)` gate. - - Args: - theta: The rotation angle - phi: The rotation angle - lambda_: The rotation angle - controls: The control qubits - target: The target qubit - - See Also: - :meth:`u` - """ - - def swap(self, target1: int, target2: int) -> None: - r"""Apply a SWAP gate. - - .. math:: - SWAP = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} - - Args: - target1: The first target qubit - target2: The second target qubit - """ - - def cswap(self, control: Control | int, target1: int, target2: int) -> None: - """Apply a controlled SWAP gate. - - Args: - control: The control qubit - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`swap` - """ - - def mcswap(self, controls: set[Control | int], target1: int, target2: int) -> None: - """Apply a multi-controlled SWAP gate. - - Args: - controls: The control qubits - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`swap` - """ - - def dcx(self, target1: int, target2: int) -> None: - r"""Apply a DCX (double CNOT) gate. - - .. math:: - DCX = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \end{pmatrix} - - Args: - target1: The first target qubit - target2: The second target qubit - """ - - def cdcx(self, control: Control | int, target1: int, target2: int) -> None: - """Apply a controlled DCX (double CNOT) gate. - - Args: - control: The control qubit - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`dcx` - """ - - def mcdcx(self, controls: set[Control | int], target1: int, target2: int) -> None: - """Apply a multi-controlled DCX (double CNOT) gate. - - Args: - controls: The control qubits - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`dcx` - """ - - def ecr(self, target1: int, target2: int) -> None: - r"""Apply an ECR (echoed cross-resonance) gate. - - .. math:: - ECR = \frac{1}{\sqrt{2}} - \begin{pmatrix} 0 & 0 & 1 & i \\ 0 & 0 & i & 1 \\ 1 & -i & 0 & 0 \\ -i & 1 & 0 & 0 \end{pmatrix} - - Args: - target1: The first target qubit - target2: The second target qubit - """ - - def cecr(self, control: Control | int, target1: int, target2: int) -> None: - """Apply a controlled ECR (echoed cross-resonance) gate. - - Args: - control: The control qubit - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`ecr` - """ - - def mcecr(self, controls: set[Control | int], target1: int, target2: int) -> None: - """Apply a multi-controlled ECR (echoed cross-resonance) gate. - - Args: - controls: The control qubits - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`ecr` - """ - - def iswap(self, target1: int, target2: int) -> None: - r"""Apply an iSWAP gate. - - .. math:: - iSWAP = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & i & 0 \\ 0 & i & 0 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} - - Args: - target1: The first target qubit - target2: The second target qubit - """ - - def ciswap(self, control: Control | int, target1: int, target2: int) -> None: - """Apply a controlled iSWAP gate. - - Args: - control: The control qubit - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`iswap` - """ - - def mciswap(self, controls: set[Control | int], target1: int, target2: int) -> None: - """Apply a multi-controlled iSWAP gate. - - Args: - controls: The control qubits - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`iswap` - """ - - def iswapdg(self, target1: int, target2: int) -> None: - r"""Apply an :math:`iSWAP^{\dagger}` gate. - - .. math:: - iSWAP^{\dagger} = - \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & -i & 0 \\ 0 & -i & 0 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} - - Args: - target1: The first target qubit - target2: The second target qubit - """ - - def ciswapdg(self, control: Control | int, target1: int, target2: int) -> None: - r"""Apply a controlled :math:`iSWAP^{\dagger}` gate. - - Args: - control: The control qubit - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`iswapdg` - """ - - def mciswapdg(self, controls: set[Control | int], target1: int, target2: int) -> None: - r"""Apply a multi-controlled :math:`iSWAP^{\dagger}` gate. - - Args: - controls: The control qubits - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`iswapdg` - """ - - def peres(self, target1: int, target2: int) -> None: - r"""Apply a Peres gate. - - .. math:: - Peres = \begin{pmatrix} 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \end{pmatrix} - - Args: - target1: The first target qubit - target2: The second target qubit - """ - - def cperes(self, control: Control | int, target1: int, target2: int) -> None: - """Apply a controlled Peres gate. - - Args: - control: The control qubit - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`peres` - """ - - def mcperes(self, controls: set[Control | int], target1: int, target2: int) -> None: - """Apply a multi-controlled Peres gate. - - Args: - controls: The control qubits - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`peres` - """ - - def peresdg(self, target1: int, target2: int) -> None: - r"""Apply a :math:`Peres^{\dagger}` gate. - - .. math:: - Peres^{\dagger} = - \begin{pmatrix} 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 1 & 0 & 0 \\ 1 & 0 & 0 & 0 \end{pmatrix} - - Args: - target1: The first target qubit - target2: The second target qubit - """ - - def cperesdg(self, control: Control | int, target1: int, target2: int) -> None: - r"""Apply a controlled :math:`Peres^{\dagger}` gate. - - Args: - control: The control qubit - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`peresdg` - """ - - def mcperesdg(self, controls: set[Control | int], target1: int, target2: int) -> None: - r"""Apply a multi-controlled :math:`Peres^{\dagger}` gate. - - Args: - controls: The control qubits - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`peresdg` - """ - - def rxx(self, theta: float | Expression, target1: int, target2: int) -> None: - r"""Apply an :math:`R_{xx}(\theta)` gate. - - .. math:: - R_{xx}(\theta) = e^{-i\theta XX/2} = \cos(\theta/2) I\otimes I - i \sin(\theta/2) X \otimes X - = \begin{pmatrix} \cos(\theta/2) & 0 & 0 & -i \sin(\theta/2) \\ - 0 & \cos(\theta/2) & -i \sin(\theta/2) & 0 \\ - 0 & -i \sin(\theta/2) & \cos(\theta/2) & 0 \\ - -i \sin(\theta/2) & 0 & 0 & \cos(\theta/2) \end{pmatrix} - - Args: - theta: The rotation angle - target1: The first target qubit - target2: The second target qubit - """ - - def crxx(self, theta: float | Expression, control: Control | int, target1: int, target2: int) -> None: - r"""Apply a controlled :math:`R_{xx}(\theta)` gate. - - Args: - theta: The rotation angle - control: The control qubit - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`rxx` - """ - - def mcrxx(self, theta: float | Expression, controls: set[Control | int], target1: int, target2: int) -> None: - r"""Apply a multi-controlled :math:`R_{xx}(\theta)` gate. - - Args: - theta: The rotation angle - controls: The control qubits - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`rxx` - """ - - def ryy(self, theta: float | Expression, target1: int, target2: int) -> None: - r"""Apply an :math:`R_{yy}(\theta)` gate. - - .. math:: - R_{yy}(\theta) = e^{-i\theta YY/2} = \cos(\theta/2) I\otimes I - i \sin(\theta/2) Y \otimes Y - = \begin{pmatrix} \cos(\theta/2) & 0 & 0 & i \sin(\theta/2) \\ - 0 & \cos(\theta/2) & -i \sin(\theta/2) & 0 \\ - 0 & -i \sin(\theta/2) & \cos(\theta/2) & 0 \\ - i \sin(\theta/2) & 0 & 0 & \cos(\theta/2) \end{pmatrix} - - Args: - theta: The rotation angle - target1: The first target qubit - target2: The second target qubit - """ - - def cryy(self, theta: float | Expression, control: Control | int, target1: int, target2: int) -> None: - r"""Apply a controlled :math:`R_{yy}(\theta)` gate. - - Args: - theta: The rotation angle - control: The control qubit - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`ryy` - """ - - def mcryy(self, theta: float | Expression, controls: set[Control | int], target1: int, target2: int) -> None: - r"""Apply a multi-controlled :math:`R_{yy}(\theta)` gate. - - Args: - theta: The rotation angle - controls: The control qubits - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`ryy` - """ - - def rzz(self, theta: float | Expression, target1: int, target2: int) -> None: - r"""Apply an :math:`R_{zz}(\theta)` gate. - - .. math:: - R_{zz}(\theta) = e^{-i\theta ZZ/2} = - \begin{pmatrix} e^{-i\theta/2} & 0 & 0 & 0 \\ - 0 & e^{i\theta/2} & 0 & 0 \\ - 0 & 0 & e^{i\theta/2} & 0 \\ - 0 & 0 & 0 & e^{-i\theta/2} \end{pmatrix} - - Args: - theta: The rotation angle - target1: The first target qubit - target2: The second target qubit - """ - - def crzz(self, theta: float | Expression, control: Control | int, target1: int, target2: int) -> None: - r"""Apply a controlled :math:`R_{zz}(\theta)` gate. - - Args: - theta: The rotation angle - control: The control qubit - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`rzz` - """ - - def mcrzz(self, theta: float | Expression, controls: set[Control | int], target1: int, target2: int) -> None: - r"""Apply a multi-controlled :math:`R_{zz}(\theta)` gate. - - Args: - theta: The rotation angle - controls: The control qubits - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`rzz` - """ - - def rzx(self, theta: float | Expression, target1: int, target2: int) -> None: - r"""Apply an :math:`R_{zx}(\theta)` gate. - - .. math:: - R_{zx}(\theta) = e^{-i\theta ZX/2} = \cos(\theta/2) I\otimes I - i \sin(\theta/2) Z \otimes X = - \begin{pmatrix} \cos(\theta/2) & -i \sin(\theta/2) & 0 & 0 \\ - -i \sin(\theta/2) & \cos(\theta/2) & 0 & 0 \\ - 0 & 0 & \cos(\theta/2) & i \sin(\theta/2) \\ - 0 & 0 & i \sin(\theta/2) & \cos(\theta/2) \end{pmatrix} - - Args: - theta: The rotation angle - target1: The first target qubit - target2: The second target qubit - """ - - def crzx(self, theta: float | Expression, control: Control | int, target1: int, target2: int) -> None: - r"""Apply a controlled :math:`R_{zx}(\theta)` gate. - - Args: - theta: The rotation angle - control: The control qubit - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`rzx` - """ - - def mcrzx(self, theta: float | Expression, controls: set[Control | int], target1: int, target2: int) -> None: - r"""Apply a multi-controlled :math:`R_{zx}(\theta)` gate. - - Args: - theta: The rotation angle - controls: The control qubits - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`rzx` - """ - - def xx_minus_yy(self, theta: float | Expression, beta: float | Expression, target1: int, target2: int) -> None: - r"""Apply an :math:`R_{XX - YY}(\theta, \beta)` gate. - - .. math:: - R_{XX - YY}(\theta, \beta) - = R_{z_2}(\beta) \cdot e^{-i\frac{\theta}{2} \frac{XX-YY}{2}} \cdot R_{z_2}(-\beta) - = \begin{pmatrix} \cos(\theta/2) & 0 & 0 & -i \sin(\theta/2) e^{-i\beta} \\ - 0 & 1 & 0 & 0 \\ - 0 & 0 & 1 & 0 \\ - -i \sin(\theta/2) e^{i\beta} & 0 & 0 & \cos(\theta/2) \end{pmatrix} - - Args: - theta: The rotation angle - beta: The rotation angle - target1: The first target qubit - target2: The second target qubit - """ - - def cxx_minus_yy( - self, - theta: float | Expression, - beta: float | Expression, - control: Control | int, - target1: int, - target2: int, - ) -> None: - r"""Apply a controlled :math:`R_{XX - YY}(\theta, \beta)` gate. - - Args: - theta: The rotation angle - beta: The rotation angle - control: The control qubit - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`xx_minus_yy` - """ - - def mcxx_minus_yy( - self, - theta: float | Expression, - beta: float | Expression, - controls: set[Control | int], - target1: int, - target2: int, - ) -> None: - r"""Apply a multi-controlled :math:`R_{XX - YY}(\theta, \beta)` gate. - - Args: - theta: The rotation angle - beta: The rotation angle - controls: The control qubits - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`xx_minus_yy` - """ - - def xx_plus_yy(self, theta: float | Expression, beta: float | Expression, target1: int, target2: int) -> None: - r"""Apply an :math:`R_{XX + YY}(\theta, \beta)` gate. - - .. math:: - R_{XX + YY}(\theta, \beta) - = R_{z_1}(\beta) \cdot e^{-i\frac{\theta}{2} \frac{XX+YY}{2}} \cdot R_{z_1}(-\beta) - = \begin{pmatrix} 1 & 0 & 0 & 0 \\ - 0 & \cos(\theta/2) & -i \sin(\theta/2) e^{-i\beta} & 0 \\ - 0 & -i \sin(\theta/2) e^{i\beta} & \cos(\theta/2) & 0 \\ - 0 & 0 & 0 & 1 \end{pmatrix} - - Args: - theta: The rotation angle - beta: The rotation angle - target1: The first target qubit - target2: The second target qubit - """ - - def cxx_plus_yy( - self, - theta: float | Expression, - beta: float | Expression, - control: Control | int, - target1: int, - target2: int, - ) -> None: - r"""Apply a controlled :math:`R_{XX + YY}(\theta, \beta)` gate. - - Args: - theta: The rotation angle - beta: The rotation angle - control: The control qubit - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`xx_plus_yy` - """ - - def mcxx_plus_yy( - self, - theta: float | Expression, - beta: float | Expression, - controls: set[Control | int], - target1: int, - target2: int, - ) -> None: - r"""Apply a multi-controlled :math:`R_{XX + YY}(\theta, \beta)` gate. - - Args: - theta: The rotation angle - beta: The rotation angle - controls: The control qubits - target1: The first target qubit - target2: The second target qubit - - See Also: - :meth:`xx_plus_yy` - """ - - def gphase(self, theta: float) -> None: - r"""Apply a global phase gate. - - .. math:: - GPhase(\theta) = (e^{i\theta}) - - Args: - theta: The rotation angle - """ - - @overload - def measure(self, qubit: int, cbit: int) -> None: - """Measure a qubit and store the result in a classical bit. - - Args: - qubit: The qubit to measure - cbit: The classical bit to store the result - """ - - @overload - def measure(self, qubits: Sequence[int], cbits: Sequence[int]) -> None: - """Measure multiple qubits and store the results in classical bits. - - This method is equivalent to calling :meth:`measure` multiple times. - - Args: - qubits: The qubits to measure - cbits: The classical bits to store the results - """ - - def measure_all(self, *, add_bits: bool = True) -> None: - """Measure all qubits and store the results in classical bits. - - Details: - If `add_bits` is `True`, a new classical register (named "`meas`") with - the same size as the number of qubits will be added to the circuit - and the results will be stored in it. If `add_bits` is `False`, the - classical register must already exist and have a sufficient number - of bits to store the results. - - Args: - add_bits: Whether to explicitly add a classical register - """ - - @overload - def reset(self, q: int) -> None: - """Add a reset operation to the circuit. - - Args: - q: The qubit to reset - """ - - @overload - def reset(self, qubits: Sequence[int]) -> None: - """Add a reset operation to the circuit. - - Args: - qubits: The qubits to reset - """ - - @overload - def barrier(self) -> None: - """Add a barrier to the circuit.""" - - @overload - def barrier(self, q: int) -> None: - """Add a barrier to the circuit. - - Args: - q: The qubit to add the barrier to - """ - - @overload - def barrier(self, qubits: Sequence[int]) -> None: - """Add a barrier to the circuit. - - Args: - qubits: The qubits to add the barrier to - """ - - @overload - def if_else( - self, - then_operation: Operation, - else_operation: Operation | None, - control_register: ClassicalRegister, - expected_value: int = 1, - comparison_kind: ComparisonKind = ComparisonKind.eq, - ) -> None: - """Add an if-else operation to the circuit. - - Args: - then_operation: The operation to apply if the condition is met - else_operation: The operation to apply if the condition is not met - control_register: The classical register to check against - expected_value: The expected value of the control register - comparison_kind: The kind of comparison to perform - """ - - @overload - def if_else( - self, - then_operation: Operation, - else_operation: Operation | None, - control_bit: int, - expected_value: int = 1, - comparison_kind: ComparisonKind = ComparisonKind.eq, - ) -> None: - """Add an if-else operation to the circuit. - - Args: - then_operation: The operation to apply if the condition is met - else_operation: The operation to apply if the condition is not met - control_bit: The index of the classical bit to check against - expected_value: The expected value of the control bit - comparison_kind: The kind of comparison to perform - """ - - @overload - def if_( - self, - op_type: OpType, - target: int, - control_register: ClassicalRegister, - expected_value: int = 1, - comparison_kind: ComparisonKind = ComparisonKind.eq, - params: Sequence[float] = (), - ) -> None: - """Add an if operartion to the circuit. - - Args: - op_type: The operation to apply - target: The target qubit - control_register: The classical register to check against - expected_value: The expected value of the control register - comparison_kind: The kind of comparison to perform - params: The parameters of the operation - """ - - @overload - def if_( - self, - op_type: OpType, - target: int, - control: Control | int, - control_register: ClassicalRegister, - expected_value: int = 1, - comparison_kind: ComparisonKind = ComparisonKind.eq, - params: Sequence[float] = (), - ) -> None: - """Add a classic-controlled operation to the circuit. - - Args: - op_type: The operation to apply - target: The target qubit - control: The control qubit - control_register: The classical register to check against - expected_value: The expected value of the control register - comparison_kind: The kind of comparison to perform - params: The parameters of the operation. - """ - - @overload - def if_( - self, - op_type: OpType, - target: int, - controls: set[Control | int], - control_register: ClassicalRegister, - expected_value: int = 1, - comparison_kind: ComparisonKind = ComparisonKind.eq, - params: Sequence[float] = (), - ) -> None: - """Add a classic-controlled operation to the circuit. - - Args: - op_type: The operation to apply - target: The target qubit - controls: The control qubits - control_register: The classical register to check against - expected_value: The expected value of the control register - comparison_kind: The kind of comparison to perform - params: The parameters of the operation. - """ - - @overload - def if_( - self, - op_type: OpType, - target: int, - control_bit: int, - expected_value: bool = True, - comparison_kind: ComparisonKind = ComparisonKind.eq, - params: Sequence[float] = (), - ) -> None: - """Add a classic-controlled operation to the circuit. - - Args: - op_type: The operation to apply - target: The target qubit - control_bit: The index of the classical bit to check against - expected_value: The expected value of the control bit - comparison_kind: The kind of comparison to perform - params: The parameters of the operation. - """ - - @overload - def if_( - self, - op_type: OpType, - target: int, - control: Control | int, - control_bit: int, - expected_value: bool = True, - comparison_kind: ComparisonKind = ComparisonKind.eq, - params: Sequence[float] = (), - ) -> None: - """Add a classic-controlled operation to the circuit. - - Args: - op_type: The operation to apply - target: The target qubit - control: The control qubit - control_bit: The index of the classical bit to check against - expected_value: The expected value of the control bit - comparison_kind: The kind of comparison to perform - params: The parameters of the operation. - """ - - @overload - def if_( - self, - op_type: OpType, - target: int, - controls: set[Control | int], - control_bit: int, - expected_value: bool = True, - comparison_kind: ComparisonKind = ComparisonKind.eq, - params: Sequence[float] = (), - ) -> None: - """Add a classic-controlled operation to the circuit. - - Args: - op_type: The operation to apply - target: The target qubit - controls: The control qubits - control_bit: The index of the classical bit to check against - expected_value: The expected value of the control bit - comparison_kind: The kind of comparison to perform - params: The parameters of the operation. - """ diff --git a/python/mqt/core/ir/operations.pyi b/python/mqt/core/ir/operations.pyi deleted file mode 100644 index 8fdfe614e3..0000000000 --- a/python/mqt/core/ir/operations.pyi +++ /dev/null @@ -1,1011 +0,0 @@ -# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM -# Copyright (c) 2025 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -from abc import ABC, abstractmethod -from collections.abc import Iterable, Mapping, MutableSequence, Sequence -from enum import Enum -from typing import overload - -from .registers import ClassicalRegister -from .symbolic import Expression, Variable - -__all__ = [ - "ComparisonKind", - "CompoundOperation", - "Control", - "IfElseOperation", - "NonUnitaryOperation", - "OpType", - "Operation", - "StandardOperation", - "SymbolicOperation", -] - -class Control: - """A control is a pair of a qubit and a type. The type can be either positive or negative. - - Args: - qubit: The qubit that is the control. - type_: The type of the control (default is positive). - """ - - class Type(Enum): - """Enumeration of control types. - - It can be either positive or negative. - """ - - Neg = ... - r"""A negative control. - - The operation that is controlled on this qubit is only executed if the qubit is in the :math:`|0\rangle` state. - """ - Pos = ... - r"""A positive control. - - The operation that is controlled on this qubit is only executed if the qubit is in the :math:`|1\rangle` state. - """ - - qubit: int - type_: Type - - def __init__(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, other: object) -> bool: - """Check if two controls are equal.""" - - def __ne__(self, other: object) -> bool: - """Check if two controls are not equal.""" - - def __hash__(self) -> int: - """Get the hash of the control.""" - -class OpType(Enum): - """Enumeration of operation types.""" - - barrier = ... - """ - A barrier operation. It is used to separate operations in the circuit. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.barrier` - """ - if_else = ... - """ - An if-else operation. - - Used to control the execution of an operation based on the value of a classical register. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.if_else` - """ - compound = ... - """ - A compound operation. It is used to group multiple operations into a single operation. - - See Also: - :class:`.CompoundOperation` - """ - dcx = ... - """ - A DCX gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.dcx` - """ - ecr = ... - """ - An ECR gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.ecr` - """ - gphase = ... - """ - A global phase operation. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.gphase` - """ - h = ... - """ - A Hadamard gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.h` - """ - i = ... - """ - An identity operation. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.i` - """ - iswap = ... - """ - An iSWAP gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.iswap` - """ - iswapdg = ... - r""" - An :math:`i\text{SWAP}^\dagger` gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.iswapdg` - """ - measure = ... - """ - A measurement operation. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.measure` - """ - none = ... - """ - A placeholder operation. It is used to represent an operation that is not yet defined. - """ - peres = ... - """ - A Peres gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.peres` - """ - peresdg = ... - r""" - A :math:`\text{Peres}^\dagger` gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.peresdg` - """ - p = ... - """ - A phase gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.p` - """ - reset = ... - """ - A reset operation. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.reset` - """ - r = ... - r""" - An :math:`R` gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.r` - """ - rx = ... - r""" - An :math:`R_x` gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.rx` - """ - rxx = ... - r""" - An :math:`R_{xx}` gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.rxx` - """ - ry = ... - r""" - An :math:`R_y` gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.ry` - """ - ryy = ... - r""" - An :math:`R_{yy}` gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.ryy` - """ - rz = ... - r""" - An :math:`R_z` gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.rz` - """ - rzx = ... - r""" - An :math:`R_{zx}` gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.rzx` - """ - rzz = ... - r""" - An :math:`R_{zz}` gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.rzz` - """ - s = ... - """ - An S gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.s` - """ - sdg = ... - r""" - An :math:`S^\dagger` gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.sdg` - """ - swap = ... - """ - A SWAP gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.swap` - """ - sx = ... - r""" - A :math:`\sqrt{X}` gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.sx` - """ - sxdg = ... - r""" - A :math:`\sqrt{X}^\dagger` gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.sxdg` - """ - t = ... - """ - A T gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.t` - """ - tdg = ... - r""" - A :math:`T^\dagger` gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.tdg` - """ - u2 = ... - """ - A U2 gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.u2` - """ - u = ... - """ - A U gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.u` - """ - v = ... - """ - A V gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.v` - """ - vdg = ... - r""" - A :math:`V^\dagger` gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.vdg` - """ - x = ... - """ - An X gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.x` - """ - xx_minus_yy = ... - r""" - An :math:`R_{XX - YY}` gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.xx_minus_yy` - """ - xx_plus_yy = ... - r""" - An :math:`R_{XX + YY}` gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.xx_plus_yy` - """ - y = ... - """ - A Y gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.y` - """ - z = ... - """ - A Z gate. - - See Also: - :meth:`mqt.core.ir.QuantumComputation.z` - """ - -class Operation(ABC): - """An abstract base class for operations that can be added to a :class:`~mqt.core.ir.QuantumComputation`.""" - - type_: OpType - """ - The type of the operation. - """ - controls: set[Control] - """ - The controls of the operation. - - Note: - The notion of a control might not make sense for all types of operations. - """ - targets: list[int] - """ - The targets of the operation. - - Note: - The notion of a target might not make sense for all types of operations. - """ - parameter: list[float] - """ - The parameters of the operation. - - Note: - The notion of a parameter might not make sense for all types of operations. - """ - - @property - def name(self) -> str: - """The name of the operation.""" - - @property - def num_targets(self) -> int: - """The number of targets of the operation.""" - - @property - def num_controls(self) -> int: - """The number of controls of the operation.""" - - @abstractmethod - def add_control(self, control: Control) -> None: - """Add a control to the operation. - - Args: - control: The control to add. - """ - - def add_controls(self, controls: set[Control]) -> None: - """Add multiple controls to the operation. - - Args: - controls: The controls to add. - """ - - @abstractmethod - def clear_controls(self) -> None: - """Clear all controls of the operation.""" - - @abstractmethod - def remove_control(self, control: Control) -> None: - """Remove a control from the operation. - - Args: - control: The control to remove. - """ - - def remove_controls(self, controls: set[Control]) -> None: - """Remove multiple controls from the operation. - - Args: - controls: The controls to remove. - """ - - def acts_on(self, qubit: int) -> bool: - """Check if the operation acts on a specific qubit. - - Args: - qubit: The qubit to check. - - Returns: - True if the operation acts on the qubit, False otherwise. - """ - - def get_used_qubits(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_if_else_operation(self) -> bool: - """Check if the operation is a :class:`IfElseOperation`. - - Returns: - True if the operation is a :class:`IfElseOperation`, False otherwise. - """ - - def is_compound_operation(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) -> bool: - """Check if the operation is controlled. - - Returns: - True if the operation is controlled, False otherwise. - """ - - def is_non_unitary_operation(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) -> 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) -> bool: - """Check if the operation is a :class:`SymbolicOperation`. - - Returns: - True if the operation is a :class:`SymbolicOperation`, False otherwise. - """ - - def is_unitary(self) -> bool: - """Check if the operation is unitary. - - Returns: - True if the operation is unitary, False otherwise. - """ - - def get_inverted(self) -> Operation: - """Get the inverse of the operation. - - Returns: - The inverse of the operation. - """ - - @abstractmethod - def invert(self) -> None: - """Invert the operation (in-place).""" - - def __eq__(self, other: object) -> bool: ... - def __hash__(self) -> int: ... - def __ne__(self, other: object) -> bool: ... - -class StandardOperation(Operation): - """Standard quantum operation. - - This class is used to represent all standard quantum operations, i.e., - operations that are unitary. This includes all possible quantum gates. - Such Operations are defined by their :class:`OpType`, the qubits (controls - and targets) they act on, and their parameters. - - Args: - control: The control qubit(s) of the operation (if any). - target: The target qubit(s) of the operation. - op_type: The type of the operation. - params: The parameters of the operation (if any). - """ - - @overload - def __init__(self) -> None: ... - @overload - def __init__( - self, - target: int, - op_type: OpType, - params: Sequence[float] | None = None, - ) -> None: ... - @overload - def __init__( - self, - targets: Sequence[int], - op_type: OpType, - params: Sequence[float] | None = None, - ) -> None: ... - @overload - def __init__( - self, - control: Control, - target: int, - op_type: OpType, - params: Sequence[float] | None = None, - ) -> None: ... - @overload - def __init__( - self, - control: Control, - targets: Sequence[int], - op_type: OpType, - params: Sequence[float] | None = None, - ) -> None: ... - @overload - def __init__( - self, - controls: set[Control], - target: int, - op_type: OpType, - params: Sequence[float] | None = None, - ) -> None: ... - @overload - def __init__( - self, - controls: set[Control], - targets: Sequence[int], - op_type: OpType, - params: Sequence[float] | None = None, - ) -> None: ... - @overload - def __init__( - self, - controls: set[Control], - target0: int, - target1: int, - op_type: OpType, - params: Sequence[float] | None = None, - ) -> None: ... - def add_control(self, control: Control) -> None: - """Add a control to the operation. - - :class:`StandardOperation` supports arbitrarily many controls per operation. - - Args: - control: The control to add. - """ - - def clear_controls(self) -> None: - """Clear all controls of the operation.""" - - def remove_control(self, control: Control) -> None: - """Remove a control from the operation. - - Args: - control: The control to remove. - """ - - def invert(self) -> None: - """Invert the operation (in-place). - - Since any :class:`StandardOperation` is unitary, the inverse is simply the - conjugate transpose of the operation's matrix representation. - """ - -class NonUnitaryOperation(Operation): - """Non-unitary operation. - - This class is used to represent all non-unitary operations, i.e., operations - that are not reversible. This includes measurements and resets. - - Args: - targets: The target qubit(s) of the 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) -> list[int]: - """The classical registers that are associated with the operation.""" - - @overload - def __init__(self, targets: Sequence[int], classics: Sequence[int]) -> None: ... - @overload - def __init__(self, target: int, classic: int) -> None: ... - @overload - def __init__(self, targets: Sequence[int], op_type: OpType = ...) -> None: ... - def add_control(self, control: Control) -> None: - """Adding controls to a non-unitary operation is not supported.""" - - def clear_controls(self) -> None: - """Cannot clear controls of a non-unitary operation.""" - - def remove_control(self, control: Control) -> None: - """Removing controls from a non-unitary operation is not supported.""" - - def invert(self) -> None: - """Non-unitary operations are, per definition, not invertible.""" - -class CompoundOperation(Operation, MutableSequence[Operation]): - """Compound quantum operation. - - This class is used to aggregate and group multiple operations into a single - object. This is useful for optimizations and for representing complex - quantum functionality. A :class:`CompoundOperation` can contain any number - of operations, including other :class:`CompoundOperation`'s. - - Args: - ops: The operations that are part of the compound operation. - """ - - @overload - def __init__(self) -> None: ... - @overload - def __init__(self, ops: Sequence[Operation]) -> None: ... - def __len__(self) -> int: - """The number of operations in the compound operation.""" - - @overload - def __getitem__(self, index: int) -> Operation: - """Get the operation at the given index. - - Args: - index: The index of the operation to get. - - Returns: - The operation at the given index. - - Notes: - This gives direct access to the operations in the compound operation. - """ - - @overload - def __getitem__(self, index: slice) -> list[Operation]: - """Get the operations in the given slice. - - Args: - index: The slice of the operations to get. - - Returns: - The operations in the given slice. - - Notes: - This gives direct access to the operations in the compound operation. - """ - - @overload - def __setitem__(self, index: int, value: Operation) -> None: - """Set the operation at the given index. - - Args: - index: The index of the operation to set. - value: The operation to set at the given index. - """ - - @overload - def __setitem__(self, index: slice, value: Iterable[Operation]) -> None: - """Set the operations in the given slice. - - Args: - index: The slice of operations to set. - value: The operations to set in the given slice. - """ - - @overload - def __delitem__(self, index: int) -> None: - """Delete the operation at the given index. - - Args: - index: The index of the operation to delete. - """ - - @overload - def __delitem__(self, index: slice) -> None: - """Delete the operations in the given slice. - - Args: - index: The slice of operations to delete. - """ - - def insert(self, index: int, value: Operation) -> None: - """Insert an operation at the given index. - - Args: - index: The index to insert the operation at. - value: The operation to insert. - """ - - def append(self, value: Operation) -> None: - """Append an operation to the compound operation.""" - - def empty(self) -> bool: - """Check if the compound operation is empty.""" - - def clear(self) -> None: - """Clear all operations in the compound operation.""" - - def add_control(self, control: Control) -> None: - """Add a control to the operation. - - This will add the control to all operations in the compound operation. - Additionally, the control is added to the compound operation itself to - keep track of all controls that are applied to the compound operation. - - Args: - control: The control to add. - """ - - def clear_controls(self) -> None: - """Clear all controls of the operation. - - This will clear all controls that have been tracked in the compound - operation itself and will clear these controls of all operations that are - part of the compound operation. - """ - - def remove_control(self, control: Control) -> None: - """Remove a control from the operation. - - This will remove the control from all operations in the compound operation. - Additionally, the control is removed from the compound operation itself to - keep track of all controls that are applied to the compound operation. - - Args: - control: The control to remove. - """ - - def invert(self) -> None: - """Invert the operation (in-place). - - This will invert all operations in the compound operation and reverse - the order of the operations. This only works if all operations in the - compound operation are invertible and will throw an error otherwise. - """ - -class SymbolicOperation(StandardOperation): - """Symbolic quantum operation. - - This class is used to represent quantum operations that are not yet fully - defined. This can be useful for representing operations that depend on - parameters that are not yet known. A :class:`SymbolicOperation` is defined - by its :class:`OpType`, the qubits (controls and targets) it acts on, and - its parameters. The parameters can be either fixed values or symbolic - expressions. - - Args: - controls: The control qubit(s) of the operation (if any). - targets: The target qubit(s) of the operation. - op_type: The type of the operation. - params: The parameters of the operation (if any). - """ - - @overload - def __init__(self) -> None: ... - @overload - def __init__( - self, - target: int, - op_type: OpType, - params: Sequence[Expression | float] | None = None, - ) -> None: ... - @overload - def __init__( - self, - targets: Sequence[int], - op_type: OpType, - params: Sequence[Expression | float] | None = None, - ) -> None: ... - @overload - def __init__( - self, - control: Control, - target: int, - op_type: OpType, - params: Sequence[Expression | float] | None = None, - ) -> None: ... - @overload - def __init__( - self, - control: Control, - targets: Sequence[int], - op_type: OpType, - params: Sequence[Expression | float] | None = None, - ) -> None: ... - @overload - def __init__( - self, - controls: set[Control], - target: int, - op_type: OpType, - params: Sequence[Expression | float] | None = None, - ) -> None: ... - @overload - def __init__( - self, - controls: set[Control], - targets: Sequence[int], - op_type: OpType, - params: Sequence[Expression | float] | None = None, - ) -> None: ... - @overload - def __init__( - self, - controls: set[Control], - target0: int, - target1: int, - op_type: OpType, - params: Sequence[Expression | float] | None = None, - ) -> None: ... - def get_parameter(self, idx: int) -> Expression | float: - """Get the parameter at the given index. - - Args: - idx: The index of the parameter to get. - - Returns: - The parameter at the given index. - """ - - def get_parameters(self) -> list[Expression | float]: - """Get all parameters of the operation. - - Returns: - The parameters of the operation. - """ - - def get_instantiated_operation(self, assignment: Mapping[Variable, float]) -> StandardOperation: - """Get the instantiated operation. - - Args: - assignment: The assignment of the symbolic parameters. - - Returns: - The instantiated operation. - """ - - def instantiate(self, assignment: Mapping[Variable, float]) -> None: - """Instantiate the operation (in-place). - - Args: - assignment: The assignment of the symbolic parameters. - """ - -class ComparisonKind(Enum): - """Enumeration of comparison types for classic-controlled operations.""" - - eq = ... - """Equality comparison.""" - neq = ... - """Inequality comparison.""" - lt = ... - """Less than comparison.""" - leq = ... - """Less than or equal comparison.""" - gt = ... - """Greater than comparison.""" - geq = ... - """Greater than or equal comparison.""" - -class IfElseOperation(Operation): - """If-else quantum operation. - - This class is used to represent an if-else operation. The then operation is executed if the - value of the classical register matches the expected value. Otherwise, the else operation is executed. - - Args: - then_operation: The operation that is executed if the condition is met. - else_operation: The operation that is executed if the condition is not met. - control_register: The classical register that controls the operation. - expected_value: The expected value of the classical register. - comparison_kind: The kind of comparison (default is equality). - """ - - @overload - def __init__( - self, - if_operation: Operation, - else_operation: Operation | None, - control_register: ClassicalRegister, - expected_value: int = 1, - comparison_kind: ComparisonKind = ..., - ) -> None: ... - @overload - def __init__( - self, - if_operation: Operation, - else_operation: Operation | None, - control_bit: int, - expected_value: bool = True, - comparison_kind: ComparisonKind = ..., - ) -> None: ... - @property - def then_operation(self) -> Operation: - """The operation that is executed if the condition is met.""" - - @property - def else_operation(self) -> Operation | None: - """The operation that is executed if the condition is not met.""" - - @property - def control_register(self) -> ClassicalRegister | None: - """The classical register that controls the operation.""" - - @property - def control_bit(self) -> int | None: - """The classical bit that controls the operation.""" - - @property - def expected_value_register(self) -> int: - """The expected value of the classical register. - - The then-operation is executed if the value of the classical register matches the expected value based on the - kind of comparison. - 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. - """ - - @property - def expected_value_bit(self) -> bool: - """The expected value of the classical register. - - The then-operation is executed if the value of the classical bit matches the expected value based on the - kind of comparison. - """ - - @property - def comparison_kind(self) -> ComparisonKind: - """The kind of comparison. - - The then-operation is executed if the value of the classical register matches the expected value based on the - kind of comparison. - """ - - def add_control(self, control: Control) -> None: - """Adds a control to the underlying operation. - - Args: - control: The control to add. - - See Also: - :meth:`Operation.add_control` - """ - - def clear_controls(self) -> None: - """Clears the controls of the underlying operation. - - See Also: - :meth:`Operation.clear_controls` - """ - - def remove_control(self, control: Control) -> None: - """Removes a control from the underlying operation. - - Args: - control: The control to remove. - - See Also: - :meth:`Operation.remove_control` - """ - - def invert(self) -> None: - """Inverts the underlying operation. - - See Also: - :meth:`Operation.invert` - """ diff --git a/python/mqt/core/ir/registers.pyi b/python/mqt/core/ir/registers.pyi deleted file mode 100644 index c9f1be9969..0000000000 --- a/python/mqt/core/ir/registers.pyi +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM -# Copyright (c) 2025 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -__all__ = ["ClassicalRegister", "QuantumRegister"] - -class QuantumRegister: - """A class to represent a collection of qubits. - - Args: - start: The starting index of the quantum register. - size: The number of qubits in the quantum register. - name: The name of the quantum register. A name will be generated if not provided. - """ - - def __init__(self, start: int, size: int, name: str = "") -> None: ... - @property - def start(self) -> int: - """The index of the first qubit in the quantum register.""" - - @property - def end(self) -> int: - """Index of the last qubit in the quantum register.""" - - @property - def size(self) -> int: - """The number of qubits in the quantum register.""" - - @property - def name(self) -> str: - """The name of the quantum register.""" - - def __eq__(self, other: object) -> bool: - """Check if the quantum register is equal to another quantum register.""" - - def __ne__(self, other: object) -> bool: - """Check if the quantum register is not equal to another quantum register.""" - - def __hash__(self) -> int: - """Return the hash of the quantum register.""" - - def __getitem__(self, key: int) -> int: - """Get the qubit at the specified index.""" - - def __contains__(self, qubit: int) -> bool: - """Check if the quantum register contains a qubit.""" - -class ClassicalRegister: - """A class to represent a collection of classical bits. - - Args: - start: The starting index of the classical register. - size: The number of bits in the classical register. - name: The name of the classical register. A name will be generated if not provided. - """ - - def __init__(self, start: int, size: int, name: str = "") -> None: ... - @property - def start(self) -> int: - """The index of the first bit in the classical register.""" - - @property - def end(self) -> int: - """Index of the last bit in the classical register.""" - - @property - def size(self) -> int: - """The number of bits in the classical register.""" - - @property - def name(self) -> str: - """The name of the classical register.""" - - def __eq__(self, other: object) -> bool: - """Check if the classical register is equal to another classical register.""" - - def __ne__(self, other: object) -> bool: - """Check if the classical register is not equal to another classical register.""" - - def __hash__(self) -> int: - """Return the hash of the classical register.""" - - def __getitem__(self, key: int) -> int: - """Get the bit at the specified index.""" - - def __contains__(self, bit: int) -> bool: - """Check if the classical register contains a bit.""" diff --git a/python/mqt/core/ir/symbolic.pyi b/python/mqt/core/ir/symbolic.pyi deleted file mode 100644 index 2c1cd34b86..0000000000 --- a/python/mqt/core/ir/symbolic.pyi +++ /dev/null @@ -1,177 +0,0 @@ -# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM -# Copyright (c) 2025 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -from collections.abc import Iterator, Mapping, Sequence -from typing import overload - -__all__ = ["Expression", "Term", "Variable"] - -class Variable: - """A symbolic variable. - - Args: - name: The name of the 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, arg0: object) -> bool: ... - def __gt__(self, arg0: Variable) -> bool: ... - def __hash__(self) -> int: ... - def __init__(self, name: str = "") -> None: ... - def __lt__(self, arg0: Variable) -> bool: ... - def __ne__(self, arg0: object) -> bool: ... - @property - def name(self) -> str: - """The name of the variable.""" - -class Term: - """A symbolic term which consists of a variable with a given coefficient. - - Args: - variable: The variable of the term. - coefficient: The coefficient of the term. - """ - - def __eq__(self, arg0: object) -> bool: ... - def __hash__(self) -> int: ... - def __init__(self, variable: Variable, coefficient: float = 1.0) -> None: ... - def __mul__(self, arg0: float) -> Term: ... - def __ne__(self, arg0: object) -> bool: ... - def __rmul__(self, arg0: float) -> Term: ... - def __rtruediv__(self, arg0: float) -> Term: ... - def __truediv__(self, arg0: float) -> Term: ... - def add_coefficient(self, coeff: float) -> None: - """Add a coefficient to the coefficient of this term. - - Args: - coeff: The coefficient to add. - """ - - def evaluate(self, assignment: Mapping[Variable, float]) -> float: - """Evaluate the term with a given variable assignment. - - Args: - assignment: The variable assignment. - - Returns: - The evaluated value of the term. - """ - - def has_zero_coefficient(self) -> bool: - """Check if the coefficient of the term is zero.""" - - @property - def coefficient(self) -> float: - """The coefficient of the term.""" - - @property - def variable(self) -> Variable: - """The variable of the term.""" - -class Expression: - r"""A symbolic expression which consists of a sum of terms and a constant. - - The expression is of the form :math:`constant + term_1 + term_2 + \dots + term_n`. - - Args: - terms: The list of terms. - constant: The constant. - - Alternatively, an expression can be created with a single term and a constant or just a constant. - """ - - constant: float - """ - The constant of the expression. - """ - - @overload - def __add__(self, arg0: Expression) -> Expression: ... - @overload - def __add__(self, arg0: Term) -> Expression: ... - @overload - def __add__(self, arg0: float) -> Expression: ... - def __eq__(self, arg0: object) -> bool: ... - def __getitem__(self, idx: int) -> Term: ... - def __hash__(self) -> int: ... - @overload - def __init__(self, terms: Sequence[Term], constant: float = 0.0) -> None: - """Create an expression with a given list of terms and a constant. - - Args: - terms: The list of terms. - constant: The constant. - """ - - @overload - def __init__(self, term: Term, constant: float = 0.0) -> None: - """Create an expression with a given term and a constant. - - Args: - term: The term. - constant: The constant. - """ - - @overload - def __init__(self, constant: float = 0.0) -> None: - """Create an expression with a given constant. - - Args: - constant: The constant. - """ - - def __iter__(self) -> Iterator[Term]: ... - def __len__(self) -> int: ... - def __mul__(self, arg0: float) -> Expression: ... - def __ne__(self, arg0: object) -> bool: ... - @overload - def __radd__(self, arg0: Term) -> Expression: ... - @overload - def __radd__(self, arg0: float) -> Expression: ... - def __rmul__(self, arg0: float) -> Expression: ... - @overload - def __rsub__(self, arg0: Term) -> Expression: ... - @overload - def __rsub__(self, arg0: float) -> Expression: ... - def __rtruediv__(self, arg0: float) -> Expression: ... - @overload - def __sub__(self, arg0: Expression) -> Expression: ... - @overload - def __sub__(self, arg0: Term) -> Expression: ... - @overload - def __sub__(self, arg0: float) -> Expression: ... - def __truediv__(self, arg0: float) -> Expression: ... - def evaluate(self, assignment: Mapping[Variable, float]) -> float: - """Evaluate the expression with a given variable assignment. - - Args: - assignment: The variable assignment. - - Returns: - The evaluated value of the expression. - """ - - def is_constant(self) -> bool: - """Check if the expression is a constant.""" - - def is_zero(self) -> bool: - """Check if the expression is zero.""" - - def num_terms(self) -> int: - """The number of terms in the expression.""" - - @property - def terms(self) -> list[Term]: - """The terms of the expression.""" - - @property - def variables(self) -> set[Variable]: - """The variables in the expression.""" diff --git a/python/mqt/core/na/__init__.py b/python/mqt/core/na/__init__.py deleted file mode 100644 index 4f663baa34..0000000000 --- a/python/mqt/core/na/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM -# Copyright (c) 2025 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -"""MQT Core NA - The MQT Core neutral atom module. - -This module contains all neutral atom related functionality of MQT Core. -""" diff --git a/python/mqt/core/na/fomac.pyi b/python/mqt/core/na/fomac.pyi deleted file mode 100644 index 4757aa46e4..0000000000 --- a/python/mqt/core/na/fomac.pyi +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM -# Copyright (c) 2025 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -"""Reconstruction of NADevice from QDMI's Device class.""" - -from collections.abc import Iterable - -from ..fomac import Device as GenericDevice - -__all__ = ["Device"] - -class Device(GenericDevice): - """Represents a device with a lattice of traps.""" - - class Vector: - """Represents a 2D vector.""" - - x: int - """ - The x-coordinate of the vector. - """ - y: int - """ - The y-coordinate of the vector. - """ - def __eq__(self, other: object) -> bool: ... - def __ne__(self, other: object) -> bool: ... - - class Region: - """Represents a region in the device.""" - - origin: Device.Vector - """ - The origin of the region. - """ - - class Size: - """Represents the size of a region.""" - - width: int - """ - The width of the region. - """ - height: int - """ - The height of the region. - """ - def __eq__(self, other: object) -> bool: ... - def __ne__(self, other: object) -> bool: ... - - size: Size - """ - The size of the region. - """ - - def __eq__(self, other: object) -> bool: ... - def __ne__(self, other: object) -> bool: ... - - class Lattice: - """Represents a lattice of traps in the device.""" - - lattice_origin: Device.Vector - """ - The origin of the lattice. - """ - lattice_vector_1: Device.Vector - """ - The first lattice vector. - """ - lattice_vector_2: Device.Vector - """ - The second lattice vector. - """ - sublattice_offsets: Iterable[Device.Vector] - """ - The offsets of the sublattices. - """ - extent: Device.Region - """ - The extent of the lattice. - """ - def __eq__(self, other: object) -> bool: ... - def __ne__(self, other: object) -> bool: ... - - traps: Iterable[Device.Lattice] - """ - The list of trap positions in the device. - """ - t1: int - """ - The T1 time of the device. - """ - t2: int - """ - The T2 time of the device. - """ - - @classmethod - def try_create_from_device(cls, device: GenericDevice) -> Device | None: - """Create NA FoMaC Device from generic FoMaC Device. - - Args: - device: The generic FoMaC Device to convert. - - Returns: - The converted NA FoMaC Device or None if the conversion is not possible. - """ - def __eq__(self, other: object) -> bool: ... - def __ne__(self, other: object) -> bool: ... - -def devices() -> Iterable[Device]: - """Returns a list of available devices.""" From 979c066a3c86a52e5cae758ba70f7ad60ca35b04 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Fri, 12 Dec 2025 00:46:47 +0100 Subject: [PATCH 032/144] Fix stub generation --- bindings/dd/CMakeLists.txt | 13 +++++++++++++ bindings/fomac/CMakeLists.txt | 13 +++++++++++++ bindings/ir/CMakeLists.txt | 19 +++++++++++++++++++ bindings/na/fomac/CMakeLists.txt | 13 +++++++++++++ cmake/AddMQTPythonBinding.cmake | 14 -------------- 5 files changed, 58 insertions(+), 14 deletions(-) diff --git a/bindings/dd/CMakeLists.txt b/bindings/dd/CMakeLists.txt index 050729e332..315083d4ea 100644 --- a/bindings/dd/CMakeLists.txt +++ b/bindings/dd/CMakeLists.txt @@ -21,4 +21,17 @@ if(NOT TARGET ${MQT_CORE_TARGET_NAME}-dd-bindings) . LINK_LIBS MQT::CoreDD) + + nanobind_add_stub( + ${MQT_CORE_TARGET_NAME}-dd-bindings_stub + INSTALL_TIME + MODULE + mqt.core.dd + OUTPUT + ${PROJECT_SOURCE_DIR}/python/mqt/core/dd.pyi + PYTHON_PATH + ${PROJECT_SOURCE_DIR}/python + MARKER_FILE + py.typed + INCLUDE_PRIVATE) endif() diff --git a/bindings/fomac/CMakeLists.txt b/bindings/fomac/CMakeLists.txt index 179c3c02fa..fb331f45c6 100644 --- a/bindings/fomac/CMakeLists.txt +++ b/bindings/fomac/CMakeLists.txt @@ -23,4 +23,17 @@ if(NOT TARGET ${TARGET_NAME}) . LINK_LIBS MQT::CoreFoMaC) + + nanobind_add_stub( + ${TARGET_NAME}_stub + INSTALL_TIME + MODULE + mqt.core.fomac + OUTPUT + ${PROJECT_SOURCE_DIR}/python/mqt/core/fomac.pyi + PYTHON_PATH + ${PROJECT_SOURCE_DIR}/python + MARKER_FILE + py.typed + INCLUDE_PRIVATE) endif() diff --git a/bindings/ir/CMakeLists.txt b/bindings/ir/CMakeLists.txt index 23a1329279..29ecbfdbbd 100644 --- a/bindings/ir/CMakeLists.txt +++ b/bindings/ir/CMakeLists.txt @@ -22,4 +22,23 @@ if(NOT TARGET ${MQT_CORE_TARGET_NAME}-ir-bindings) LINK_LIBS MQT::CoreIR MQT::CoreQASM) + + nanobind_add_stub( + ${MQT_CORE_TARGET_NAME}-ir-bindings_stub + INSTALL_TIME + MODULE + mqt.core.ir + RECURSIVE + OUTPUT_PATH + ${PROJECT_SOURCE_DIR}/python/mqt/core/ + OUTPUT + ${PROJECT_SOURCE_DIR}/python/mqt/core/ir/__init__.pyi + ${PROJECT_SOURCE_DIR}/python/mqt/core/ir/operations.pyi + ${PROJECT_SOURCE_DIR}/python/mqt/core/ir/registers.pyi + ${PROJECT_SOURCE_DIR}/python/mqt/core/ir/symbolic.pyi + PYTHON_PATH + ${PROJECT_SOURCE_DIR}/python + MARKER_FILE + py.typed + INCLUDE_PRIVATE) endif() diff --git a/bindings/na/fomac/CMakeLists.txt b/bindings/na/fomac/CMakeLists.txt index 8ee95e3500..843710fb96 100644 --- a/bindings/na/fomac/CMakeLists.txt +++ b/bindings/na/fomac/CMakeLists.txt @@ -23,4 +23,17 @@ if(NOT TARGET ${TARGET_NAME}) ./na LINK_LIBS MQT::CoreNAFoMaC) + + nanobind_add_stub( + ${TARGET_NAME}_stub + INSTALL_TIME + MODULE + mqt.core.fomac + OUTPUT + ${PROJECT_SOURCE_DIR}/python/mqt/core/na/fomac.pyi + PYTHON_PATH + ${PROJECT_SOURCE_DIR}/python + MARKER_FILE + py.typed + INCLUDE_PRIVATE) endif() diff --git a/cmake/AddMQTPythonBinding.cmake b/cmake/AddMQTPythonBinding.cmake index 3091ef8e6a..f27449a2da 100644 --- a/cmake/AddMQTPythonBinding.cmake +++ b/cmake/AddMQTPythonBinding.cmake @@ -97,18 +97,4 @@ function(add_mqt_python_binding_nanobind package_name target_name) TARGETS ${target_name} DESTINATION ${ARG_INSTALL_DIR} COMPONENT ${MQT_${package_name}_TARGET_NAME}_Python) - - nanobind_add_stub( - ${target_name}_stub - INSTALL_TIME - RECURSIVE - MODULE - ${target_name} - OUTPUT - ${target_name}.pyi - OUTPUT_PATH - ${ARG_INSTALL_DIR} - MARKER_FILE - py.typed - INCLUDE_PRIVATE) endfunction() From 8159443cc9ccfc03801c2aa19eb5c773a722b3a0 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Fri, 12 Dec 2025 02:22:15 +0100 Subject: [PATCH 033/144] Use build-time stubs for now --- bindings/dd/CMakeLists.txt | 1 - bindings/fomac/CMakeLists.txt | 1 - bindings/ir/CMakeLists.txt | 1 - bindings/na/fomac/CMakeLists.txt | 1 - 4 files changed, 4 deletions(-) diff --git a/bindings/dd/CMakeLists.txt b/bindings/dd/CMakeLists.txt index 315083d4ea..f599a3b972 100644 --- a/bindings/dd/CMakeLists.txt +++ b/bindings/dd/CMakeLists.txt @@ -24,7 +24,6 @@ if(NOT TARGET ${MQT_CORE_TARGET_NAME}-dd-bindings) nanobind_add_stub( ${MQT_CORE_TARGET_NAME}-dd-bindings_stub - INSTALL_TIME MODULE mqt.core.dd OUTPUT diff --git a/bindings/fomac/CMakeLists.txt b/bindings/fomac/CMakeLists.txt index fb331f45c6..56b10761cf 100644 --- a/bindings/fomac/CMakeLists.txt +++ b/bindings/fomac/CMakeLists.txt @@ -26,7 +26,6 @@ if(NOT TARGET ${TARGET_NAME}) nanobind_add_stub( ${TARGET_NAME}_stub - INSTALL_TIME MODULE mqt.core.fomac OUTPUT diff --git a/bindings/ir/CMakeLists.txt b/bindings/ir/CMakeLists.txt index 29ecbfdbbd..8152d5c0d5 100644 --- a/bindings/ir/CMakeLists.txt +++ b/bindings/ir/CMakeLists.txt @@ -25,7 +25,6 @@ if(NOT TARGET ${MQT_CORE_TARGET_NAME}-ir-bindings) nanobind_add_stub( ${MQT_CORE_TARGET_NAME}-ir-bindings_stub - INSTALL_TIME MODULE mqt.core.ir RECURSIVE diff --git a/bindings/na/fomac/CMakeLists.txt b/bindings/na/fomac/CMakeLists.txt index 843710fb96..37b70b14e0 100644 --- a/bindings/na/fomac/CMakeLists.txt +++ b/bindings/na/fomac/CMakeLists.txt @@ -26,7 +26,6 @@ if(NOT TARGET ${TARGET_NAME}) nanobind_add_stub( ${TARGET_NAME}_stub - INSTALL_TIME MODULE mqt.core.fomac OUTPUT From a93dd3a31809b1bf5752dd0e72b70ed4d50ea9a2 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Fri, 12 Dec 2025 02:23:17 +0100 Subject: [PATCH 034/144] Begin to improve typing --- bindings/dd/CMakeLists.txt | 3 +- bindings/dd/register_dd_package.cpp | 3 ++ bindings/fomac/CMakeLists.txt | 3 +- bindings/fomac/fomac.cpp | 24 ++++++++++----- bindings/ir/CMakeLists.txt | 3 +- bindings/ir/operations/register_control.cpp | 7 +++-- .../register_symbolic_operation.cpp | 9 +++--- bindings/ir/register_permutation.cpp | 8 +++-- bindings/ir/register_quantum_computation.cpp | 1 + bindings/ir/register_registers.cpp | 14 ++++++--- bindings/ir/symbolic/register_expression.cpp | 11 ++++--- bindings/ir/symbolic/register_term.cpp | 6 ++-- bindings/ir/symbolic/register_variable.cpp | 6 ++-- bindings/na/fomac/CMakeLists.txt | 3 +- bindings/na/fomac/fomac.cpp | 30 ++++++++++++------- 15 files changed, 85 insertions(+), 46 deletions(-) diff --git a/bindings/dd/CMakeLists.txt b/bindings/dd/CMakeLists.txt index f599a3b972..7703cf004b 100644 --- a/bindings/dd/CMakeLists.txt +++ b/bindings/dd/CMakeLists.txt @@ -31,6 +31,5 @@ if(NOT TARGET ${MQT_CORE_TARGET_NAME}-dd-bindings) PYTHON_PATH ${PROJECT_SOURCE_DIR}/python MARKER_FILE - py.typed - INCLUDE_PRIVATE) + py.typed) endif() diff --git a/bindings/dd/register_dd_package.cpp b/bindings/dd/register_dd_package.cpp index c63d3f2212..58bfe1f882 100644 --- a/bindings/dd/register_dd_package.cpp +++ b/bindings/dd/register_dd_package.cpp @@ -27,6 +27,9 @@ #include #include #include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) #include #include diff --git a/bindings/fomac/CMakeLists.txt b/bindings/fomac/CMakeLists.txt index 56b10761cf..7793fd26e7 100644 --- a/bindings/fomac/CMakeLists.txt +++ b/bindings/fomac/CMakeLists.txt @@ -33,6 +33,5 @@ if(NOT TARGET ${TARGET_NAME}) PYTHON_PATH ${PROJECT_SOURCE_DIR}/python MARKER_FILE - py.typed - INCLUDE_PRIVATE) + py.typed) endif() diff --git a/bindings/fomac/fomac.cpp b/bindings/fomac/fomac.cpp index 7fff18e222..d52d2af416 100644 --- a/bindings/fomac/fomac.cpp +++ b/bindings/fomac/fomac.cpp @@ -167,8 +167,10 @@ All authentication parameters are optional and can be provided as keyword argume job.def_prop_ro("num_shots", &fomac::Session::Job::getNumShots, "Returns the number of shots for the job."); - job.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) - job.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) + job.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __eq__(self, arg: object, /) -> bool")); + job.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __ne__(self, arg: object, /) -> bool")); // JobStatus enum nb::enum_(job, "Status", "enum.Enum", @@ -281,8 +283,10 @@ All authentication parameters are optional and can be provided as keyword argume return ""; }); - device.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) - device.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) + job.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __eq__(self, arg: object, /) -> bool")); + job.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __ne__(self, arg: object, /) -> bool")); // Site class auto site = nb::class_( @@ -332,8 +336,10 @@ All authentication parameters are optional and can be provided as keyword argume return ""; }); - site.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) - site.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) + job.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __eq__(self, arg: object, /) -> bool")); + job.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __ne__(self, arg: object, /) -> bool")); // Operation class auto operation = nb::class_( @@ -405,8 +411,10 @@ All authentication parameters are optional and can be provided as keyword argume return ""; }); - operation.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) - operation.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) + job.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __eq__(self, arg: object, /) -> bool")); + job.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __ne__(self, arg: object, /) -> bool")); #ifndef _WIN32 // Module-level function to add dynamic device libraries on non-Windows diff --git a/bindings/ir/CMakeLists.txt b/bindings/ir/CMakeLists.txt index 8152d5c0d5..bda98fc4fd 100644 --- a/bindings/ir/CMakeLists.txt +++ b/bindings/ir/CMakeLists.txt @@ -38,6 +38,5 @@ if(NOT TARGET ${MQT_CORE_TARGET_NAME}-ir-bindings) PYTHON_PATH ${PROJECT_SOURCE_DIR}/python MARKER_FILE - py.typed - INCLUDE_PRIVATE) + py.typed) endif() diff --git a/bindings/ir/operations/register_control.cpp b/bindings/ir/operations/register_control.cpp index 6920e058fb..fde494bf0c 100644 --- a/bindings/ir/operations/register_control.cpp +++ b/bindings/ir/operations/register_control.cpp @@ -46,8 +46,11 @@ void registerControl(const nb::module_& m) { control.def("__str__", [](const qc::Control& c) { return c.toString(); }); control.def("__repr__", [](const qc::Control& c) { return c.toString(); }); - control.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) - control.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) + + control.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __eq__(self, arg: object, /) -> bool")); + control.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __ne__(self, arg: object, /) -> bool")); control.def(nb::hash(nb::self)); nb::implicitly_convertible(); diff --git a/bindings/ir/operations/register_symbolic_operation.cpp b/bindings/ir/operations/register_symbolic_operation.cpp index 57fcbf9240..9aa0dc734e 100644 --- a/bindings/ir/operations/register_symbolic_operation.cpp +++ b/bindings/ir/operations/register_symbolic_operation.cpp @@ -16,10 +16,11 @@ #include "ir/operations/SymbolicOperation.hpp" #include -#include // NOLINT(misc-include-cleaner) -#include // NOLINT(misc-include-cleaner) -#include // NOLINT(misc-include-cleaner) -#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include namespace mqt { diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index 3c95d42f09..02d43e32d5 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -16,7 +16,9 @@ #include #include #include +#include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include #include @@ -122,8 +124,10 @@ void registerPermutation(nb::module_& m) { }, nb::keep_alive<0, 1>()) - .def(nb::self == nb::self) // NOLINT(misc-redundant-expression) - .def(nb::self != nb::self) // NOLINT(misc-redundant-expression) + .def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __eq__(self, arg: object, /) -> bool")) + .def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __ne__(self, arg: object, /) -> bool")) .def(nb::hash(nb::self)) .def("__str__", diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index 7661374b4e..bab6a7ab7e 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -25,6 +25,7 @@ #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) #include diff --git a/bindings/ir/register_registers.cpp b/bindings/ir/register_registers.cpp index 7d8f9987eb..19b86b378c 100644 --- a/bindings/ir/register_registers.cpp +++ b/bindings/ir/register_registers.cpp @@ -57,8 +57,10 @@ void registerRegisters(nb::module_& m) { [](const qc::QuantumRegister& reg) { return reg.getEndIndex(); }, "Index of the last qubit in the quantum register.") - .def(nb::self == nb::self) // NOLINT(misc-redundant-expression) - .def(nb::self != nb::self) // NOLINT(misc-redundant-expression) + .def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __eq__(self, arg: object, /) -> bool")) + .def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __ne__(self, arg: object, /) -> bool")) .def(hash(nb::self)) .def("__getitem__", &qc::QuantumRegister::getGlobalIndex, "key"_a, @@ -111,8 +113,12 @@ void registerRegisters(nb::module_& m) { [](const qc::ClassicalRegister& reg) { return reg.getEndIndex(); }, "Index of the last bit in the classical register.") - .def(nb::self == nb::self) // NOLINT(misc-redundant-expression) - .def(nb::self != nb::self) // NOLINT(misc-redundant-expression) + // NOLINTNEXTLINE(misc-redundant-expression) + .def(nb::self == nb::self, + nb::sig("def __eq__(self, arg: object, /) -> bool")) + // NOLINTNEXTLINE(misc-redundant-expression) + .def(nb::self != nb::self, + nb::sig("def __ne__(self, arg: object, /) -> bool")) .def(nb::hash(nb::self)) .def("__getitem__", &qc::ClassicalRegister::getGlobalIndex, "key"_a, diff --git a/bindings/ir/symbolic/register_expression.cpp b/bindings/ir/symbolic/register_expression.cpp index 5cad69e1de..993c89877e 100644 --- a/bindings/ir/symbolic/register_expression.cpp +++ b/bindings/ir/symbolic/register_expression.cpp @@ -14,8 +14,9 @@ #include #include #include -#include // NOLINT(misc-include-cleaner) -#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include #include @@ -129,8 +130,10 @@ Alternatively, an expression can be created with a single term and a constant or .def("__rtruediv__", [](const sym::Expression& rhs, double lhs) { return rhs / lhs; }) // comparison operators - .def(nb::self == nb::self) // NOLINT(misc-redundant-expression) - .def(nb::self != nb::self) // NOLINT(misc-redundant-expression) + .def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __eq__(self, arg: object, /) -> bool")) + .def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __ne__(self, arg: object, /) -> bool")) .def(nb::hash(nb::self)) .def("__str__", diff --git a/bindings/ir/symbolic/register_term.cpp b/bindings/ir/symbolic/register_term.cpp index d6a33d84cd..3d1b3d2214 100644 --- a/bindings/ir/symbolic/register_term.cpp +++ b/bindings/ir/symbolic/register_term.cpp @@ -61,8 +61,10 @@ void registerTerm(nb::module_& m) { .def(double() * nb::self) .def(nb::self / double()) .def(double() / nb::self) - .def(nb::self == nb::self) // NOLINT(misc-redundant-expression) - .def(nb::self != nb::self) // NOLINT(misc-redundant-expression) + .def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __eq__(self, arg: object, /) -> bool")) + .def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __ne__(self, arg: object, /) -> bool")) .def(nb::hash(nb::self)) .def("__str__", diff --git a/bindings/ir/symbolic/register_variable.cpp b/bindings/ir/symbolic/register_variable.cpp index cd9bf7a33d..b8620f606e 100644 --- a/bindings/ir/symbolic/register_variable.cpp +++ b/bindings/ir/symbolic/register_variable.cpp @@ -36,8 +36,10 @@ void registerVariable(nb::module_& m) { .def("__str__", &sym::Variable::getName) .def("__repr__", &sym::Variable::getName) - .def(nb::self == nb::self) // NOLINT(misc-redundant-expression) - .def(nb::self != nb::self) // NOLINT(misc-redundant-expression) + .def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __eq__(self, arg: object, /) -> bool")) + .def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __ne__(self, arg: object, /) -> bool")) .def(nb::hash(nb::self)) .def(nb::self < nb::self) // NOLINT(misc-redundant-expression) .def(nb::self > nb::self); // NOLINT(misc-redundant-expression) diff --git a/bindings/na/fomac/CMakeLists.txt b/bindings/na/fomac/CMakeLists.txt index 37b70b14e0..130062cb1c 100644 --- a/bindings/na/fomac/CMakeLists.txt +++ b/bindings/na/fomac/CMakeLists.txt @@ -33,6 +33,5 @@ if(NOT TARGET ${TARGET_NAME}) PYTHON_PATH ${PROJECT_SOURCE_DIR}/python MARKER_FILE - py.typed - INCLUDE_PRIVATE) + py.typed) endif() diff --git a/bindings/na/fomac/fomac.cpp b/bindings/na/fomac/fomac.cpp index 219dbc732f..03f5241cac 100644 --- a/bindings/na/fomac/fomac.cpp +++ b/bindings/na/fomac/fomac.cpp @@ -51,8 +51,10 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { return ""; }); - vector.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) - vector.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) + vector.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __eq__(self, arg: object, /) -> bool")); + vector.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __ne__(self, arg: object, /) -> bool")); auto region = nb::class_( lattice, "Region", "Represents a region in the device."); @@ -67,8 +69,10 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { return ""; }); - size.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) - size.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) + size.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __eq__(self, arg: object, /) -> bool")); + size.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __ne__(self, arg: object, /) -> bool")); region.def_ro("origin", &na::Device::Region::origin, "The origin of the region."); @@ -76,8 +80,10 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { region.def("__repr__", [](const na::Device::Region& r) { return ""; }); - region.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) - region.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) + region.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __eq__(self, arg: object, /) -> bool")); + region.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __ne__(self, arg: object, /) -> bool")); lattice.def_ro("lattice_origin", &na::Device::Lattice::latticeOrigin, "The origin of the lattice."); @@ -92,8 +98,10 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { lattice.def("__repr__", [](const na::Device::Lattice& l) { return ""; }); - lattice.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) - lattice.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) + lattice.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __eq__(self, arg: object, /) -> bool")); + lattice.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __ne__(self, arg: object, /) -> bool")); device.def_prop_ro("traps", &na::Session::Device::getTraps, "The list of trap positions in the device."); @@ -121,8 +129,10 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { Returns: The converted NA FoMaC Device or None if the conversion is not possible.)pb"); - device.def(nb::self == nb::self); // NOLINT(misc-redundant-expression) - device.def(nb::self != nb::self); // NOLINT(misc-redundant-expression) + device.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __eq__(self, arg: object, /) -> bool")); + device.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __ne__(self, arg: object, /) -> bool")); m.def("devices", &na::Session::getDevices, "Returns a list of available devices."); From 6c968f3244ab7ef1586b55b3b3b6df48c26a7513 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Fri, 12 Dec 2025 14:33:21 +0100 Subject: [PATCH 035/144] Fix install-time stub generation --- bindings/CMakeLists.txt | 52 ++++++++++++++++++++++++++++++++ bindings/dd/CMakeLists.txt | 11 ------- bindings/dd/register_dd.cpp | 2 ++ bindings/fomac/CMakeLists.txt | 11 ------- bindings/ir/CMakeLists.txt | 17 ----------- bindings/na/fomac/CMakeLists.txt | 11 ------- 6 files changed, 54 insertions(+), 50 deletions(-) diff --git a/bindings/CMakeLists.txt b/bindings/CMakeLists.txt index 4d11f4075a..9afdc69cdb 100644 --- a/bindings/CMakeLists.txt +++ b/bindings/CMakeLists.txt @@ -17,3 +17,55 @@ add_subdirectory(na) # add the FoMaC bindings package add_subdirectory(fomac) + +if(SKBUILD_STATE STREQUAL "editable") + nanobind_add_stub( + ir_stub + INSTALL_TIME + RECURSIVE + MODULE + mqt.core.ir + OUTPUT + ir/__init__.pyi + ir/operations.pyi + ir/registers.pyi + ir/symbolic.pyi + PYTHON_PATH + ${CMAKE_INSTALL_PREFIX}/../..) + + nanobind_add_stub( + dd_stub + INSTALL_TIME + MODULE + mqt.core.dd + OUTPUT + dd.pyi + PYTHON_PATH + ${CMAKE_INSTALL_PREFIX}/../..) + + nanobind_add_stub( + fomac_stub + INSTALL_TIME + MODULE + mqt.core.fomac + OUTPUT + fomac.pyi + PYTHON_PATH + ${CMAKE_INSTALL_PREFIX}/../..) + + nanobind_add_stub( + na_fomac_stub + INSTALL_TIME + MODULE + mqt.core.na.fomac + OUTPUT + na/fomac.pyi + PYTHON_PATH + ${CMAKE_INSTALL_PREFIX}/../..) + + file(GLOB_RECURSE PYI_FILES ${CMAKE_INSTALL_PREFIX}/*.pyi) + install( + FILES ${PYI_FILES} + DESTINATION . + COMPONENT ${MQT_CORE_TARGET_NAME}_Python) +endif() diff --git a/bindings/dd/CMakeLists.txt b/bindings/dd/CMakeLists.txt index 7703cf004b..050729e332 100644 --- a/bindings/dd/CMakeLists.txt +++ b/bindings/dd/CMakeLists.txt @@ -21,15 +21,4 @@ if(NOT TARGET ${MQT_CORE_TARGET_NAME}-dd-bindings) . LINK_LIBS MQT::CoreDD) - - nanobind_add_stub( - ${MQT_CORE_TARGET_NAME}-dd-bindings_stub - MODULE - mqt.core.dd - OUTPUT - ${PROJECT_SOURCE_DIR}/python/mqt/core/dd.pyi - PYTHON_PATH - ${PROJECT_SOURCE_DIR}/python - MARKER_FILE - py.typed) endif() diff --git a/bindings/dd/register_dd.cpp b/bindings/dd/register_dd.cpp index d429edbb4d..f9ce5bd43c 100644 --- a/bindings/dd/register_dd.cpp +++ b/bindings/dd/register_dd.cpp @@ -41,6 +41,8 @@ using Matrix = nb::ndarray, nb::ndim<2>>; Matrix getMatrix(const dd::mEdge& m, size_t numQubits, dd::fp threshold = 0.); NB_MODULE(MQT_CORE_MODULE_NAME, m) { + nb::module_::import_("mqt.core.ir"); + // Vector Decision Diagrams registerVectorDDs(m); diff --git a/bindings/fomac/CMakeLists.txt b/bindings/fomac/CMakeLists.txt index 7793fd26e7..179c3c02fa 100644 --- a/bindings/fomac/CMakeLists.txt +++ b/bindings/fomac/CMakeLists.txt @@ -23,15 +23,4 @@ if(NOT TARGET ${TARGET_NAME}) . LINK_LIBS MQT::CoreFoMaC) - - nanobind_add_stub( - ${TARGET_NAME}_stub - MODULE - mqt.core.fomac - OUTPUT - ${PROJECT_SOURCE_DIR}/python/mqt/core/fomac.pyi - PYTHON_PATH - ${PROJECT_SOURCE_DIR}/python - MARKER_FILE - py.typed) endif() diff --git a/bindings/ir/CMakeLists.txt b/bindings/ir/CMakeLists.txt index bda98fc4fd..23a1329279 100644 --- a/bindings/ir/CMakeLists.txt +++ b/bindings/ir/CMakeLists.txt @@ -22,21 +22,4 @@ if(NOT TARGET ${MQT_CORE_TARGET_NAME}-ir-bindings) LINK_LIBS MQT::CoreIR MQT::CoreQASM) - - nanobind_add_stub( - ${MQT_CORE_TARGET_NAME}-ir-bindings_stub - MODULE - mqt.core.ir - RECURSIVE - OUTPUT_PATH - ${PROJECT_SOURCE_DIR}/python/mqt/core/ - OUTPUT - ${PROJECT_SOURCE_DIR}/python/mqt/core/ir/__init__.pyi - ${PROJECT_SOURCE_DIR}/python/mqt/core/ir/operations.pyi - ${PROJECT_SOURCE_DIR}/python/mqt/core/ir/registers.pyi - ${PROJECT_SOURCE_DIR}/python/mqt/core/ir/symbolic.pyi - PYTHON_PATH - ${PROJECT_SOURCE_DIR}/python - MARKER_FILE - py.typed) endif() diff --git a/bindings/na/fomac/CMakeLists.txt b/bindings/na/fomac/CMakeLists.txt index 130062cb1c..8ee95e3500 100644 --- a/bindings/na/fomac/CMakeLists.txt +++ b/bindings/na/fomac/CMakeLists.txt @@ -23,15 +23,4 @@ if(NOT TARGET ${TARGET_NAME}) ./na LINK_LIBS MQT::CoreNAFoMaC) - - nanobind_add_stub( - ${TARGET_NAME}_stub - MODULE - mqt.core.fomac - OUTPUT - ${PROJECT_SOURCE_DIR}/python/mqt/core/na/fomac.pyi - PYTHON_PATH - ${PROJECT_SOURCE_DIR}/python - MARKER_FILE - py.typed) endif() From fe96d7437077ed422ecc918b8b83ff22e5477ece Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Fri, 12 Dec 2025 17:42:44 +0100 Subject: [PATCH 036/144] Fix Site comparison --- bindings/fomac/fomac.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bindings/fomac/fomac.cpp b/bindings/fomac/fomac.cpp index d52d2af416..f149f16444 100644 --- a/bindings/fomac/fomac.cpp +++ b/bindings/fomac/fomac.cpp @@ -336,10 +336,10 @@ All authentication parameters are optional and can be provided as keyword argume return ""; }); - job.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) - nb::sig("def __eq__(self, arg: object, /) -> bool")); - job.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) - nb::sig("def __ne__(self, arg: object, /) -> bool")); + site.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __eq__(self, arg: object, /) -> bool")); + site.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __ne__(self, arg: object, /) -> bool")); // Operation class auto operation = nb::class_( From d44c0c3ebdf6bbdcc2db874cee444cecb161000a Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Fri, 12 Dec 2025 18:32:27 +0100 Subject: [PATCH 037/144] Add py.typed markers and include privates again --- bindings/CMakeLists.txt | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/bindings/CMakeLists.txt b/bindings/CMakeLists.txt index 9afdc69cdb..8e0b0b1836 100644 --- a/bindings/CMakeLists.txt +++ b/bindings/CMakeLists.txt @@ -31,7 +31,10 @@ if(SKBUILD_STATE STREQUAL "editable") ir/registers.pyi ir/symbolic.pyi PYTHON_PATH - ${CMAKE_INSTALL_PREFIX}/../..) + ${CMAKE_INSTALL_PREFIX}/../.. + MARKER_FILE + ir/py.typed + INCLUDE_PRIVATE) nanobind_add_stub( dd_stub @@ -41,7 +44,10 @@ if(SKBUILD_STATE STREQUAL "editable") OUTPUT dd.pyi PYTHON_PATH - ${CMAKE_INSTALL_PREFIX}/../..) + ${CMAKE_INSTALL_PREFIX}/../.. + MARKER_FILE + py.typed + INCLUDE_PRIVATE) nanobind_add_stub( fomac_stub @@ -51,7 +57,10 @@ if(SKBUILD_STATE STREQUAL "editable") OUTPUT fomac.pyi PYTHON_PATH - ${CMAKE_INSTALL_PREFIX}/../..) + ${CMAKE_INSTALL_PREFIX}/../.. + MARKER_FILE + py.typed + INCLUDE_PRIVATE) nanobind_add_stub( na_fomac_stub @@ -61,11 +70,20 @@ if(SKBUILD_STATE STREQUAL "editable") OUTPUT na/fomac.pyi PYTHON_PATH - ${CMAKE_INSTALL_PREFIX}/../..) + ${CMAKE_INSTALL_PREFIX}/../.. + MARKER_FILE + na/py.typed + INCLUDE_PRIVATE) file(GLOB_RECURSE PYI_FILES ${CMAKE_INSTALL_PREFIX}/*.pyi) install( FILES ${PYI_FILES} DESTINATION . COMPONENT ${MQT_CORE_TARGET_NAME}_Python) + + file(GLOB_RECURSE PY_TYPED_FILES ${CMAKE_INSTALL_PREFIX}/py.typed) + install( + FILES ${PY_TYPED_FILES} + DESTINATION . + COMPONENT ${MQT_CORE_TARGET_NAME}_Python) endif() From cb350b69501f4af7d0a56b2187eb81d2e1dfc0a9 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sat, 13 Dec 2025 17:07:25 +0100 Subject: [PATCH 038/144] Add script for generating stub files --- bindings/CMakeLists.txt | 9 +- bindings/fomac/fomac.cpp | 16 +- bindings/generate-stubs.sh | 66 + bindings/ir/operations/register_operation.cpp | 23 +- bindings/ir/register_permutation.cpp | 2 +- bindings/ir/register_quantum_computation.cpp | 6 +- bindings/ir/symbolic/register_expression.cpp | 1 + bindings/ir/symbolic/register_term.cpp | 3 +- bindings/na/fomac/fomac.cpp | 5 +- pyproject.toml | 2 +- python/mqt/core/dd.pyi | 912 ++++++++ python/mqt/core/fomac.pyi | 455 ++++ python/mqt/core/ir/__init__.pyi | 2034 +++++++++++++++++ python/mqt/core/ir/operations.pyi | 979 ++++++++ python/mqt/core/ir/registers.pyi | 85 + python/mqt/core/ir/symbolic.pyi | 157 ++ python/mqt/core/na/fomac.pyi | 109 + 17 files changed, 4835 insertions(+), 29 deletions(-) create mode 100755 bindings/generate-stubs.sh create mode 100644 python/mqt/core/dd.pyi create mode 100644 python/mqt/core/fomac.pyi create mode 100644 python/mqt/core/ir/__init__.pyi create mode 100644 python/mqt/core/ir/operations.pyi create mode 100644 python/mqt/core/ir/registers.pyi create mode 100644 python/mqt/core/ir/symbolic.pyi create mode 100644 python/mqt/core/na/fomac.pyi diff --git a/bindings/CMakeLists.txt b/bindings/CMakeLists.txt index 8e0b0b1836..bd28ed70d0 100644 --- a/bindings/CMakeLists.txt +++ b/bindings/CMakeLists.txt @@ -6,17 +6,10 @@ # # Licensed under the MIT License -# add the IR bindings package add_subdirectory(ir) - -# add the DD bindings package add_subdirectory(dd) - -# add the NA bindings package -add_subdirectory(na) - -# add the FoMaC bindings package add_subdirectory(fomac) +add_subdirectory(na) if(SKBUILD_STATE STREQUAL "editable") nanobind_add_stub( diff --git a/bindings/fomac/fomac.cpp b/bindings/fomac/fomac.cpp index f149f16444..f86ce68619 100644 --- a/bindings/fomac/fomac.cpp +++ b/bindings/fomac/fomac.cpp @@ -283,10 +283,10 @@ All authentication parameters are optional and can be provided as keyword argume return ""; }); - job.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) - nb::sig("def __eq__(self, arg: object, /) -> bool")); - job.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) - nb::sig("def __ne__(self, arg: object, /) -> bool")); + device.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __eq__(self, arg: object, /) -> bool")); + device.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __ne__(self, arg: object, /) -> bool")); // Site class auto site = nb::class_( @@ -411,10 +411,10 @@ All authentication parameters are optional and can be provided as keyword argume return ""; }); - job.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) - nb::sig("def __eq__(self, arg: object, /) -> bool")); - job.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) - nb::sig("def __ne__(self, arg: object, /) -> bool")); + operation.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __eq__(self, arg: object, /) -> bool")); + operation.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __ne__(self, arg: object, /) -> bool")); #ifndef _WIN32 // Module-level function to add dynamic device libraries on non-Windows diff --git a/bindings/generate-stubs.sh b/bindings/generate-stubs.sh new file mode 100755 index 0000000000..cad1041b97 --- /dev/null +++ b/bindings/generate-stubs.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +files=( + "./python/mqt/core/ir/__init__.pyi" + "./python/mqt/core/ir/operations.pyi" + "./python/mqt/core/ir/registers.pyi" + "./python/mqt/core/ir/symbolic.pyi" + "./python/mqt/core/dd.pyi" + "./python/mqt/core/fomac.pyi" + "./python/mqt/core/na/fomac.pyi" +) + +# Check if all files exist +for file in ${files[@]}; do + if [ ! -f "$file" ]; then + echo "Error: $file does not exist. Are you running this script from the root directory?" + exit 1 + fi +done + +# Ensure that the most recent version of mqt.core is installed +uv sync + +# Remove the existing stub files +for file in ${files[@]}; do + rm -f "$file" +done + +# Generate new stub files +python -m nanobind.stubgen -m mqt.core.ir -o python/mqt/core/ir/__init__.pyi -P +python -m nanobind.stubgen -m mqt.core.ir.operations -o python/mqt/core/ir/operations.pyi -P +python -m nanobind.stubgen -m mqt.core.ir.registers -o python/mqt/core/ir/registers.pyi -P +python -m nanobind.stubgen -m mqt.core.ir.symbolic -o python/mqt/core/ir/symbolic.pyi -P +python -m nanobind.stubgen -m mqt.core.dd -o python/mqt/core/dd.pyi -P +python -m nanobind.stubgen -m mqt.core.fomac -o python/mqt/core/fomac.pyi -P +python -m nanobind.stubgen -m mqt.core.na.fomac -o python/mqt/core/na/fomac.pyi -P + +# Remove private Enum members from the stub files +for file in ${files[@]}; do + if [[ "$OSTYPE" == "darwin"* ]]; then + sed -i '' '/_hashable_values_:/d' "$file" + sed -i '' '/_unhashable_values_map_:/d' "$file" + else + sed -i '/_hashable_values_:/d' "$file" + sed -i '/_unhashable_values_map_:/d' "$file" + fi +done + +# Add license headers to the generated stub files +for file in ${files[@]}; do + prek license-tools --files "$file" +done + +# Run ruff twice to ensure all formatting issues are resolved +uvx ruff format +uvx ruff check + +uvx ruff format +uvx ruff check diff --git a/bindings/ir/operations/register_operation.cpp b/bindings/ir/operations/register_operation.cpp index 742f265da9..ebda6fca4a 100644 --- a/bindings/ir/operations/register_operation.cpp +++ b/bindings/ir/operations/register_operation.cpp @@ -12,9 +12,10 @@ #include "ir/operations/Operation.hpp" #include -#include // NOLINT(misc-include-cleaner) -#include // NOLINT(misc-include-cleaner) -#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include namespace mqt { @@ -153,10 +154,18 @@ void registerOperation(const nb::module_& m) { .def("invert", &qc::Operation::invert, "Invert the operation (in-place).") - .def("__eq__", [](const qc::Operation& op, - const qc::Operation& other) { return op == other; }) - .def("__ne__", [](const qc::Operation& op, - const qc::Operation& other) { return op != other; }) + .def( + "__eq__", + [](const qc::Operation& op, const qc::Operation& other) { + return op == other; + }, + nb::arg("other").sig("object")) + .def( + "__ne__", + [](const qc::Operation& op, const qc::Operation& other) { + return op != other; + }, + nb::arg("other").sig("object")) .def("__hash__", [](const qc::Operation& op) { return std::hash{}(op); diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index 02d43e32d5..72314e1b28 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -101,7 +101,7 @@ void registerPermutation(nb::module_& m) { R"pb(Delete the value of the permutation at the given index. Args: - key: The index to delete the value of the permutation at.)pb") + index: The index to delete the value of the permutation at.)pb") .def("__len__", &qc::Permutation::size, "Return the number of indices in the permutation.") diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index bab6a7ab7e..df370ee474 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -1642,6 +1642,7 @@ See Also: Args: theta: The rotation angle + beta: The rotation angle control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); @@ -1654,6 +1655,7 @@ See Also: Args: theta: The rotation angle + beta: The rotation angle controls: The control qubits target1: The first target qubit target2: The second target qubit)pb"); @@ -1685,6 +1687,7 @@ See Also: Args: theta: The rotation angle + beta: The rotation angle control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); @@ -1697,6 +1700,7 @@ See Also: Args: theta: The rotation angle + beta: The rotation angle controls: The control qubits target1: The first target qubit target2: The second target qubit)pb"); @@ -1708,7 +1712,7 @@ See Also: GPhase(\theta) = (e^{i \theta}) Args: - theta: The rotation angle)pb"); + phase: The rotation angle)pb"); qc.def("measure", nb::overload_cast( diff --git a/bindings/ir/symbolic/register_expression.cpp b/bindings/ir/symbolic/register_expression.cpp index 993c89877e..21acd1264c 100644 --- a/bindings/ir/symbolic/register_expression.cpp +++ b/bindings/ir/symbolic/register_expression.cpp @@ -16,6 +16,7 @@ #include #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) #include #include diff --git a/bindings/ir/symbolic/register_term.cpp b/bindings/ir/symbolic/register_term.cpp index 3d1b3d2214..f3ed571435 100644 --- a/bindings/ir/symbolic/register_term.cpp +++ b/bindings/ir/symbolic/register_term.cpp @@ -12,7 +12,8 @@ #include #include -#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include namespace mqt { diff --git a/bindings/na/fomac/fomac.cpp b/bindings/na/fomac/fomac.cpp index 03f5241cac..54e4f7daf7 100644 --- a/bindings/na/fomac/fomac.cpp +++ b/bindings/na/fomac/fomac.cpp @@ -15,8 +15,9 @@ #include #include -#include // NOLINT(misc-include-cleaner) -#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include namespace mqt { diff --git a/pyproject.toml b/pyproject.toml index f5bd654001..b8181e94ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -230,7 +230,7 @@ known-first-party = ["mqt.core"] "test/python/**" = ["T20", "INP001"] "docs/**" = ["T20", "INP001"] "noxfile.py" = ["T20", "TID251"] -"*.pyi" = ["D418", "DOC202", "PYI011", "PYI021"] +"*.pyi" = ["D418", "DOC202", "E501", "F821", "PYI011", "PYI021", "RUF012"] "*.ipynb" = [ "D", # pydocstyle "E402", # Allow imports to appear anywhere in Jupyter notebooks diff --git a/python/mqt/core/dd.pyi b/python/mqt/core/dd.pyi new file mode 100644 index 0000000000..897ad6e66d --- /dev/null +++ b/python/mqt/core/dd.pyi @@ -0,0 +1,912 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +import enum +from collections.abc import Sequence +from collections.abc import Set as AbstractSet +from typing import Annotated + +import numpy as np +from numpy.typing import NDArray + +import mqt.core.ir +import mqt.core.ir.operations + +class VectorDD: + """A class representing a vector decision diagram (DD).""" + + def is_terminal(self) -> bool: + """Check if the DD is a terminal node.""" + + def is_zero_terminal(self) -> bool: + """Check if the DD is a zero terminal node.""" + + def size(self) -> int: + """Get the size of the DD by traversing it once.""" + + def __getitem__(self, index: int) -> complex: + """Get the amplitude of a basis state by index.""" + + def get_amplitude(self, num_qubits: int, decisions: str) -> complex: + """Get the amplitude of a basis state by decisions. + + Args: + num_qubits: The number of qubits. + decisions: The decisions as a string of bits (`0` or `1`), where `decisions[i]` corresponds to the successor to follow at level `i` of the DD. + Must be at least `num_qubits` long. + + Returns: + The amplitude of the basis state. + """ + + def get_vector(self, threshold: float = 0.0) -> Annotated[NDArray[np.complex128], {"shape": (None,)}]: + """Get the state vector represented by the DD. + + Args: + threshold: The threshold for not including amplitudes in the state vector. Defaults to 0.0. + + Returns: + The state vector. + """ + + def to_dot( + self, + colored: bool = True, + edge_labels: bool = False, + classic: bool = False, + memory: bool = False, + format_as_polar: bool = True, + ) -> str: + """Convert the DD to a DOT graph that can be plotted via Graphviz. + + Args: + colored: Whether to use colored edge weights + edge_labels: Whether to include edge weights as labels. + classic: Whether to use the classic DD visualization style. + memory: Whether to include memory information. For debugging purposes only. + format_as_polar: Whether to format the edge weights in polar coordinates. + + Returns: + The DOT graph. + """ + + def to_svg( + self, + filename: str, + colored: bool = True, + edge_labels: bool = False, + classic: bool = False, + memory: bool = False, + format_as_polar: bool = True, + ) -> None: + """Convert the DD to an SVG file that can be viewed in a browser. + + Requires the `dot` command from Graphviz to be installed and available in the PATH. + + Args: + filename: The filename of the SVG file. Any file extension will be replaced by `.dot` and then `.svg`. + colored: Whether to use colored edge weights. + edge_labels: Whether to include edge weights as labels. + classic: Whether to use the classic DD visualization style. + memory: Whether to include memory information. For debugging purposes only. + show: Whether to open the SVG file in the default browser. + format_as_polar: Whether to format the edge weights in polar coordinates. + """ + +class MatrixDD: + """A class representing a matrix decision diagram (DD).""" + + def is_terminal(self) -> bool: + """Check if the DD is a terminal node.""" + + def is_zero_terminal(self) -> bool: + """Check if the DD is a zero terminal node.""" + + def is_identity(self, up_to_global_phase: bool = True) -> bool: + """Check if the DD represents the identity matrix. + + Args: + up_to_global_phase: Whether to ignore global phase. + + Returns: + Whether the DD represents the identity matrix. + """ + + def size(self) -> int: + """Get the size of the DD by traversing it once.""" + + def get_entry(self, num_qubits: int, row: int, col: int) -> complex: + """Get the entry of the matrix by row and column index.""" + + def get_entry_by_path(self, num_qubits: int, decisions: str) -> complex: + """Get the entry of the matrix by decisions. + + Args: + num_qubits: The number of qubits. + decisions: The decisions as a string of `0`, `1`, `2`, or `3`, where decisions[i]` corresponds to the successor to follow at level `i` of the DD. + Must be at least `num_qubits` long. + + Returns: + The entry of the matrix. + """ + + def get_matrix( + self, num_qubits: int, threshold: float = 0.0 + ) -> Annotated[NDArray[np.complex128], {"shape": (None, None)}]: + """Get the matrix represented by the DD. + + Args: + num_qubits: The number of qubits. + threshold: The threshold for not including entries in the matrix. Defaults to 0.0. + + Returns: + The matrix. + """ + + def to_dot( + self, + colored: bool = True, + edge_labels: bool = False, + classic: bool = False, + memory: bool = False, + format_as_polar: bool = True, + ) -> str: + """Convert the DD to a DOT graph that can be plotted via Graphviz. + + Args: + colored: Whether to use colored edge weights + edge_labels: Whether to include edge weights as labels. + classic: Whether to use the classic DD visualization style. + memory: Whether to include memory information. For debugging purposes only. + format_as_polar: Whether to format the edge weights in polar coordinates. + + Returns: + The DOT graph. + """ + + def to_svg( + self, + filename: str, + colored: bool = True, + edge_labels: bool = False, + classic: bool = False, + memory: bool = False, + format_as_polar: bool = True, + ) -> None: + """Convert the DD to an SVG file that can be viewed in a browser. + + Requires the `dot` command from Graphviz to be installed and available in the PATH. + + Args: + filename: The filename of the SVG file. Any file extension will be replaced by `.dot` and then `.svg`. + colored: Whether to use colored edge weights. + edge_labels: Whether to include edge weights as labels. + classic: Whether to use the classic DD visualization style. + memory: Whether to include memory information. For debugging purposes only. + show: Whether to open the SVG file in the default browser. + format_as_polar: Whether to format the edge weights in polar coordinates. + """ + +class DDPackage: + """The central manager for performing computations on decision diagrams. + + It drives all computation on decision diagrams and maintains the necessary data structures for this purpose. + Specifically, it + + - manages the memory for the decision diagram nodes (Memory Manager), + - ensures the canonical representation of decision diagrams (Unique Table), + - ensures the efficiency of decision diagram operations (Compute Table), + - provides methods for creating quantum states and operations from various sources, + - provides methods for various operations on quantum states and operations, and + - provides means for reference counting and garbage collection. + + Notes: + It is undefined behavior to pass VectorDD or MatrixDD objects that were created with a different DDPackage to the methods of the DDPackage. + + Args: + num_qubits: The maximum number of qubits that the DDPackage can handle. + Mainly influences the size of the unique tables. + Can be adjusted dynamically using the `resize` method. + Since resizing the DDPackage can be expensive, it is recommended to choose a value that is large enough for the quantum computations that are to be performed, but not unnecessarily large. + Default is 32. + """ + + def __init__(self, num_qubits: int = 32) -> None: ... + def resize(self, num_qubits: int) -> None: + """Resize the DDPackage to accommodate a different number of qubits. + + Args: + num_qubits: The new number of qubits. + Must be greater than zero. + It is undefined behavior to resize the DDPackage to a smaller number of qubits and then perform operations on decision diagrams that are associated with qubits that are no longer present. + """ + + @property + def max_qubits(self) -> int: + """The maximum number of qubits that the DDPackage can handle.""" + + def zero_state(self, num_qubits: int) -> VectorDD: + r"""Create the DD for the zero state :math:`| 0 \ldots 0 \rangle`. + + Args: + num_qubits: The number of qubits. + Must not be greater than the number of qubits the DDPackage is configured with. + + Returns: + The DD for the zero state. + The resulting state is guaranteed to have its reference count increased. + """ + + def computational_basis_state(self, num_qubits: int, state: Sequence[bool]) -> VectorDD: + r"""Create the DD for the computational basis state :math:`| b_{n - 1} \ldots b_0 \rangle`. + + Args: + num_qubits: The number of qubits. + Must not be greater than the number of qubits the DDPackage is configured with. + state: The state as a list of booleans. + Must be at least `num_qubits` long. + + Returns: + The DD for the computational basis state. + The resulting state is guaranteed to have its reference count increased. + """ + + def basis_state(self, num_qubits: int, state: Sequence[BasisStates]) -> VectorDD: + r"""Create the DD for the basis state :math:`| B_{n - 1} \ldots B_0 \rangle`, where :math:`B_i \in \{0, 1, +\, -\, L, R\}`. + + Args: + num_qubits: The number of qubits. + Must not be greater than the number of qubits the DDPackage is configured with. + state: The state as an iterable of :class:`BasisStates`. + Must be at least `num_qubits` long. + + Returns: + The DD for the basis state. + The resulting state is guaranteed to have its reference count increased. + """ + + def ghz_state(self, num_qubits: int) -> VectorDD: + r"""Create the DD for the GHZ state :math:`\frac{1}{\sqrt{2}} (| 0 \ldots 0 \rangle + |1 \ldots 1 \rangle)`. + + Args: + num_qubits: The number of qubits. + Must not be greater than the number of qubits the DDPackage is configured with. + + Returns: + The DD for the GHZ state. + The resulting state is guaranteed to have its reference count increased. + """ + + def w_state(self, num_qubits: int) -> VectorDD: + r"""Create the DD for the W state :math:`|W\rangle`. + + .. math:: + |W\rangle = \frac{1}{\sqrt{n}} (| 100 \ldots 0 \rangle + | 010 \ldots 0 \rangle + \ldots + | 000 \ldots 1 \rangle) + + Args: + num_qubits: The number of qubits. + Must not be greater than the number of qubits the DDPackage is configured with. + + Returns: + The DD for the W state. + The resulting state is guaranteed to have its reference count increased. + """ + + def from_vector(self, state: Annotated[NDArray[np.complex128], {"shape": (None,)}]) -> VectorDD: + """Create a DD from a state vector. + + Args: + state: The state vector. + Must have a length that is a power of 2. + Must not require more qubits than the DDPackage is configured with. + + Returns: + The DD for the vector. + The resulting state is guaranteed to have its reference count increased. + """ + + def apply_unitary_operation( + self, vec: VectorDD, operation: mqt.core.ir.operations.Operation, permutation: mqt.core.ir.Permutation = ... + ) -> VectorDD: + """Apply a unitary operation to the DD. + + Notes: + Automatically manages the reference count of the input and output DDs. + The input DD must have a non-zero reference count. + + Args: + vec: The input DD. + operation: The operation. Must be unitary. + permutation: The permutation of the qubits. Defaults to the identity permutation. + + Returns: + The resulting DD. + """ + + def apply_measurement( + self, + vec: VectorDD, + operation: mqt.core.ir.operations.NonUnitaryOperation, + measurements: Sequence[bool], + permutation: mqt.core.ir.Permutation = ..., + ) -> tuple[VectorDD, list[bool]]: + """Apply a measurement to the DD. + + Notes: + Automatically manages the reference count of the input and output DDs. + The input DD must have a non-zero reference count + + Args: + vec: The input DD. + operation: The measurement operation. + measurements: A list of bits with existing measurement outcomes. + permutation: The permutation of the qubits. Defaults to the identity permutation. + + Returns: + The resulting DD after the measurement as well as the updated measurement outcomes.. + """ + + def apply_reset( + self, + vec: VectorDD, + operation: mqt.core.ir.operations.NonUnitaryOperation, + permutation: mqt.core.ir.Permutation = ..., + ) -> VectorDD: + """Apply a reset to the DD. + + Notes: + Automatically manages the reference count of the input and output DDs. + The input DD must have a non-zero reference count. + + Args: + vec: The input DD. + operation: The reset operation. + permutation: The permutation of the qubits. Defaults to the identity permutation. + + Returns: + The resulting DD after the reset. + """ + + def apply_if_else_operation( + self, + vec: VectorDD, + operation: mqt.core.ir.operations.IfElseOperation, + measurements: Sequence[bool], + permutation: mqt.core.ir.Permutation = ..., + ) -> VectorDD: + """Apply a classically controlled operation to the DD. + + Notes: + Automatically manages the reference count of the input and output DDs. + The input DD must have a non-zero reference count. + + Args: + vec: The input DD. + operation: The classically controlled operation. + measurements: A list of bits with stored measurement outcomes. + permutation: The permutation of the qubits. Defaults to the identity permutation. + + Returns: + The resulting DD after the operation. + """ + + def measure_collapsing(self, vec: VectorDD, qubit: int) -> str: + """Measure a qubit and collapse the DD. + + Notes: + Automatically manages the reference count of the input and output DDs. + The input DD must have a non-zero reference count. + + Args: + vec: The input DD. + qubit: The qubit to measure. + + Returns: + The measurement outcome. + """ + + def measure_all(self, vec: VectorDD, collapse: bool = False) -> str: + """Measure all qubits. + + Notes: + Automatically manages the reference count of the input and output DDs. + The input DD must have a non-zero reference count. + + Args: + vec: The input DD. + collapse: Whether to collapse the DD. + + Returns: + The measurement outcome. + """ + + @staticmethod + def identity() -> MatrixDD: + """Create the DD for the identity matrix :math:`I`. + + Returns: + The DD for the identity matrix. + """ + + def single_qubit_gate( + self, matrix: Annotated[NDArray[np.complex128], {"shape": (None, None)}], target: int + ) -> MatrixDD: + r"""Create the DD for a single-qubit gate. + + Args: + matrix: The :math:`2 \times 2` matrix representing the single-qubit gate. + target: The target qubit. + + Returns: + The DD for the single-qubit gate. + """ + + def controlled_single_qubit_gate( + self, + matrix: Annotated[NDArray[np.complex128], {"shape": (None, None)}], + control: mqt.core.ir.operations.Control, + target: int, + ) -> MatrixDD: + r"""Create the DD for a controlled single-qubit gate. + + Args: + matrix: The :math:`2 \times 2` matrix representing the single-qubit gate. + control: The control qubit. + target: The target qubit. + + Returns: + The DD for the controlled single-qubit gate. + """ + + def multi_controlled_single_qubit_gate( + self, + matrix: Annotated[NDArray[np.complex128], {"shape": (None, None)}], + controls: AbstractSet[mqt.core.ir.operations.Control], + target: int, + ) -> MatrixDD: + r"""Create the DD for a multi-controlled single-qubit gate. + + Args: + matrix: The :math:`2 \times 2` matrix representing the single-qubit gate. + controls: The control qubits. + target: The target qubit. + + Returns: + The DD for the multi-controlled single-qubit gate. + """ + + def two_qubit_gate( + self, matrix: Annotated[NDArray[np.complex128], {"shape": (None, None)}], target0: int, target1: int + ) -> MatrixDD: + r"""Create the DD for a two-qubit gate. + + Args: + matrix: The :math:`4 \times 4` matrix representing the two-qubit gate. + target0: The first target qubit. + target1: The second target qubit. + + Returns: + The DD for the two-qubit gate. + """ + + def controlled_two_qubit_gate( + self, + matrix: Annotated[NDArray[np.complex128], {"shape": (None, None)}], + control: mqt.core.ir.operations.Control, + target0: int, + target1: int, + ) -> MatrixDD: + r"""Create the DD for a controlled two-qubit gate. + + Args: + matrix: The :math:`4 \times 4` matrix representing the two-qubit gate. + control: The control qubit. + target0: The first target qubit. + target1: The second target qubit. + + Returns: + The DD for the controlled two-qubit gate. + """ + + def multi_controlled_two_qubit_gate( + self, + matrix: Annotated[NDArray[np.complex128], {"shape": (None, None)}], + controls: AbstractSet[mqt.core.ir.operations.Control], + target0: int, + target1: int, + ) -> MatrixDD: + r"""Create the DD for a multi-controlled two-qubit gate. + + Args: + matrix: The :math:`4 \times 4` matrix representing the two-qubit gate. + controls: The control qubits. + target0: The first target qubit. + target1: The second target qubit. + + Returns: + The DD for the multi-controlled two-qubit gate. + """ + + def from_matrix(self, matrix: Annotated[NDArray[np.complex128], {"shape": (None, None)}]) -> MatrixDD: + """Create a DD from a matrix. + + Args: + matrix: The matrix. Must be square and have a size that is a power of 2. + + Returns: + The DD for the matrix. + """ + + def from_operation(self, operation: mqt.core.ir.operations.Operation, invert: bool = False) -> MatrixDD: + """Create a DD from an operation. + + Args: + operation: The operation. Must be unitary. + invert: Whether to get the inverse of the operation. + + Returns: + The DD for the operation. + """ + + def inc_ref_vec(self, vec: VectorDD) -> None: + """Increment the reference count of a vector.""" + + def inc_ref_mat(self, mat: MatrixDD) -> None: + """Decrement the reference count of a vector.""" + + def dec_ref_vec(self, vec: VectorDD) -> None: + """Increment the reference count of a matrix.""" + + def dec_ref_mat(self, mat: MatrixDD) -> None: + """Decrement the reference count of a matrix.""" + + def garbage_collect(self, force: bool = False) -> bool: + """Perform garbage collection on the DDPackage. + + Args: + force: Whether to force garbage collection. + If set to True, garbage collection is performed regardless of the current memory usage. + If set to False, garbage collection is only performed if the memory usage exceeds a certain threshold. + + Returns: + Whether any nodes were collected during garbage collection. + """ + + def vector_add(self, lhs: VectorDD, rhs: VectorDD) -> VectorDD: + """Add two vectors. + + Notes: + It is the caller's responsibility to update the reference count of the input and output vectors after the operation. + + Both vectors must have the same number of qubits. + + Args: + lhs: The left vector. + rhs: The right vector. + + Returns: + The sum of the two vectors. + """ + + def matrix_add(self, lhs: MatrixDD, rhs: MatrixDD) -> MatrixDD: + """Add two matrices. + + Notes: + It is the caller's responsibility to update the reference count of the input and output matrices after the operation. + + Both matrices must have the same number of qubits. + + Args: + lhs: The left matrix. + rhs: The right matrix. + + Returns: + The sum of the two matrices. + """ + + def conjugate(self, vec: VectorDD) -> VectorDD: + """Conjugate a vector. + + Notes: + It is the caller's responsibility to update the reference count of the input and output vectors after the operation. + + Args: + vec: The vector. + + Returns: + The conjugated vector. + """ + + def conjugate_transpose(self, mat: MatrixDD) -> MatrixDD: + """Conjugate transpose a matrix. + + Notes: + It is the caller's responsibility to update the reference count of the input and output matrices after the operation. + + Args: + mat: The matrix. + + Returns: + The conjugate transposed matrix. + """ + + def matrix_vector_multiply(self, mat: MatrixDD, vec: VectorDD) -> VectorDD: + """Multiply a matrix with a vector. + + Notes: + It is the caller's responsibility to update the reference count of the input and output matrices after the operation. + + The vector must have at least as many qubits as the matrix non-trivially acts on. + + Args: + mat: The matrix. + vec: The vector. + + Returns: + The product of the matrix and the vector. + """ + + def matrix_multiply(self, lhs: MatrixDD, rhs: MatrixDD) -> MatrixDD: + """Multiply two matrices. + + Notes: + It is the caller's responsibility to update the reference count of the input and output matrices after the operation. + + Args: + lhs: The left matrix. + rhs: The right matrix. + + Returns: + The product of the two matrices. + """ + + def inner_product(self, lhs: VectorDD, rhs: VectorDD) -> complex: + """Compute the inner product of two vectors. + + Notes: + Both vectors must have the same number of qubits. + + Args: + lhs: The left vector. + rhs: The right vector. + + Returns: + The inner product of the two vectors. + """ + + def fidelity(self, lhs: VectorDD, rhs: VectorDD) -> float: + """Compute the fidelity of two vectors. + + Notes: + Both vectors must have the same number of qubits. + + Args: + lhs: The left vector. + rhs: The right vector. + + Returns: + The fidelity of the two vectors. + """ + + def expectation_value(self, observable: MatrixDD, state: VectorDD) -> float: + r"""Compute the expectation value of an observable. + + Notes: + The state must have at least as many qubits as the observable non-trivially acts on. + + The method computes :math:`\langle \psi | O | \psi \rangle` as :math:`\langle \psi | (O | \psi \rangle)`. + + Args: + observable: The observable. + state: The state. + + Returns: + The expectation value of the observable. + """ + + def vector_kronecker( + self, top: VectorDD, bottom: VectorDD, bottom_num_qubits: int, increment_index: bool = True + ) -> VectorDD: + """Compute the Kronecker product of two vectors. + + Notes: + It is the caller's responsibility to update the reference count of the input and output vectors after the operation. + + Args: + top: The top vector. + bottom: The bottom vector. + bottom_num_qubits: The number of qubits of the bottom vector. + increment_index: Whether to increment the indexes of the top vector. + + Returns: + The Kronecker product of the two vectors. + """ + + def matrix_kronecker( + self, top: MatrixDD, bottom: MatrixDD, bottom_num_qubits: int, increment_index: bool = True + ) -> MatrixDD: + """Compute the Kronecker product of two matrices. + + Notes: + It is the caller's responsibility to update the reference count of the input and output matrices after the operation. + + Args: + top: The top matrix. + bottom: The bottom matrix. + bottom_num_qubits: The number of qubits of the bottom matrix. + increment_index: Whether to increment the indexes of the top matrix. + + Returns: + The Kronecker product of the two matrices. + """ + + def partial_trace(self, mat: MatrixDD, eliminate: Sequence[bool]) -> MatrixDD: + """Compute the partial trace of a matrix. + + Args: + mat: The matrix. + eliminate: The qubits to eliminate. Must be at least as long as the number of qubits of the matrix. + + Returns: + The partial trace of the matrix. + """ + + def trace(self, mat: MatrixDD, num_qubits: int) -> complex: + """Compute the trace of a matrix. + + Args: + mat: The matrix. + num_qubits: The number of qubits of the matrix. + + Returns: + The trace of the matrix. + """ + +class BasisStates(enum.Enum): + """Enumeration of basis states.""" + + zero = 0 + """The computational basis state :math:`|0 + angle`.""" + + one = 1 + """The computational basis state :math:`|1 + angle`.""" + + plus = 2 + r""" + The superposition state :math:`|+\rangle = \frac{1}{\sqrt{2}} (|0\rangle + |1\rangle)`. + """ + + minus = 3 + r""" + The superposition state :math:`|-\rangle = \frac{1}{\sqrt{2}} (|0\rangle - |1\rangle)`. + """ + + right = 4 + r""" + The superposition state :math:`|R\rangle = \frac{1}{\sqrt{2}} (|0\rangle - i |1\rangle)`. + """ + + left = 5 + r""" + The superposition state :math:`|L\rangle = \frac{1}{\sqrt{2}} (|0\rangle + i |1\rangle)`. + """ + +def sample(qc: mqt.core.ir.QuantumComputation, shots: int = 1024, seed: int = 0) -> dict[str, int]: + """Sample from the output distribution of a quantum computation. + + This function classically simulates the quantum computation and repeatedly samples from the output distribution. + It supports mid-circuit measurements, resets, and classical control. + + Args: + qc: The quantum computation. + shots: The number of samples to take. + If the quantum computation contains no mid-circuit measurements or resets, the circuit is simulated once and the samples are drawn from the final state. + Otherwise, the circuit is simulated once for each sample. + Defaults to 1024. + seed: The seed for the random number generator. + If set to a specific non-zero value, the simulation is deterministic. + If set to 0, the RNG is randomly seeded. + Defaults to 0. + + Returns: + A histogram of the samples. + Each sample is a bitstring representing the measurement outcomes of the qubits in the quantum computation. + The leftmost bit corresponds to the most significant qubit, that is, the qubit with the highest index (big-endian). + If the circuit contains measurements, only the qubits that are actively measured are included in the output distribution. + Otherwise, all qubits in the circuit are measured. + """ + +def simulate_statevector( + qc: mqt.core.ir.QuantumComputation, +) -> Annotated[NDArray[np.complex128], {"shape": (None,)}]: + """Simulate the quantum computation and return the final state vector. + + This function classically simulates the quantum computation and returns the state vector of the final state. + It does not support measurements, resets, or classical control. + + Since the state vector is guaranteed to be exponentially large in the number of qubits, this function is only suitable for small quantum computations. + Consider using the :func:`~mqt.core.dd.simulate` or the :func:`~mqt.core.dd.sample` functions, which never explicitly construct the state vector, for larger quantum computations. + + Notes: + This function internally constructs a :class:`~mqt.core.dd.DDPackage`, creates the zero state, and simulates the quantum computation via the :func:`simulate` function. + The state vector is then extracted from the resulting DD via the :meth:`~mqt.core.dd.VectorDD.get_vector` method. + The resulting :class:`~mqt.core.dd.Vector` can be converted to a NumPy array without copying the data by calling :func:`numpy.array` with the `copy=False` argument. + + Args: + qc: The quantum computation. Must only contain unitary operations. + + Returns: + The state vector of the final state. + """ + +def build_unitary( + qc: mqt.core.ir.QuantumComputation, recursive: bool = False +) -> Annotated[NDArray[np.complex128], {"shape": (None, None)}]: + """Build a unitary matrix representation of a quantum computation. + + This function builds a matrix representation of the unitary representing the functionality of a quantum computation. + This function does not support measurements, resets, or classical control, as the corresponding operations are non-unitary. + + Since the unitary matrix is guaranteed to be exponentially large in the number of qubits, this function is only suitable for small quantum computations. + Consider using the :func:`~mqt.core.dd.build_functionality` function, which never explicitly constructs the unitary matrix, for larger quantum computations. + + Notes: + This function internally constructs a :class:`~mqt.core.dd.DDPackage`, creates the identity matrix, and builds the unitary matrix via the :func:`~mqt.core.dd.build_functionality` function. + The unitary matrix is then extracted from the resulting DD via the :meth:`~mqt.core.dd.MatrixDD.get_matrix` method. + The resulting :class:`~mqt.core.dd.Matrix` can be converted to a NumPy array without copying the data by calling :func:`numpy.array` with the `copy=False` argument. + + Args: + qc: The quantum computation. Must only contain unitary operations. + recursive: Whether to build the unitary matrix recursively. + If set to True, the unitary matrix is built recursively by pairwise grouping the operations of the quantum computation. + If set to False, the unitary matrix is built by sequentially applying the operations of the quantum computation to the identity matrix. + Defaults to False. + + Returns: + The unitary matrix representing the functionality of the quantum computation. + """ + +def simulate(qc: mqt.core.ir.QuantumComputation, initial_state: VectorDD, dd_package: DDPackage) -> VectorDD: + """Simulate a quantum computation. + + This function classically simulates a quantum computation for a given initial state and returns the final state (represented as a DD). + Compared to the `sample` function, this function does not support measurements, resets, or classical control. + It only supports unitary operations. + + The simulation is effectively computed by sequentially applying the operations of the quantum computation to the initial state. + + Args: + qc: The quantum computation. Must only contain unitary operations. + initial_state: The initial state as a DD. Must have the same number of qubits as the quantum computation. + The reference count of the initial state is decremented during the simulation, so the caller must ensure that the initial state has a non-zero reference count. + dd_package: The DD package. Must be configured with a sufficient number of qubits to accommodate the quantum computation. + + Returns: + The final state as a DD. The reference count of the final state is non-zero and must be manually decremented by the caller if it is no longer needed. + """ + +def build_functionality(qc: mqt.core.ir.QuantumComputation, dd_package: DDPackage, recursive: bool = False) -> MatrixDD: + """Build a functional representation of a quantum computation. + + This function builds a matrix DD representation of the unitary representing the functionality of a quantum computation. + This function does not support measurements, resets, or classical control, as the corresponding operations are non-unitary. + + Args: + qc: The quantum computation. + Must only contain unitary operations. + dd_package: The DD package. Must be configured with a sufficient number of qubits to accommodate the quantum computation. + recursive: Whether to build the functionality matrix recursively. + If set to True, the functionality matrix is built recursively by pairwise grouping the operations of the quantum computation. + If set to False, the functionality matrix is built by sequentially applying the operations of the quantum computation to the identity matrix. + Defaults to False. + + Returns: + The functionality as a DD. The reference count of the result is non-zero and must be manually decremented by the caller if it is no longer needed. + """ diff --git a/python/mqt/core/fomac.pyi b/python/mqt/core/fomac.pyi new file mode 100644 index 0000000000..3160e292b9 --- /dev/null +++ b/python/mqt/core/fomac.pyi @@ -0,0 +1,455 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +import enum +from collections.abc import Sequence + +class Session: + """A FoMaC session for managing QDMI devices. + + Allows creating isolated sessions with independent authentication settings. + All authentication parameters are optional and can be provided as keyword arguments to the constructor. + """ + + def __init__( + self, + token: str | None = None, + auth_file: str | None = None, + auth_url: str | None = None, + username: str | None = None, + password: str | None = None, + project_id: str | None = None, + custom1: str | None = None, + custom2: str | None = None, + custom3: str | None = None, + custom4: str | None = None, + custom5: str | None = None, + ) -> None: + """Create a new FoMaC session with optional authentication. + + Args: + token: Authentication token + auth_file: Path to file containing authentication information + auth_url: URL to authentication server + username: Username for authentication + password: Password for authentication + project_id: Project ID for session + custom1: Custom configuration parameter 1 + custom2: Custom configuration parameter 2 + custom3: Custom configuration parameter 3 + custom4: Custom configuration parameter 4 + custom5: Custom configuration parameter 5 + + Raises: + RuntimeError: If auth_file does not exist + RuntimeError: If auth_url has invalid format + + Example: + >>> from mqt.core.fomac import Session + >>> # Session without authentication + >>> session = Session() + >>> devices = session.get_devices() + >>> + >>> # Session with token authentication + >>> session = Session(token="my_secret_token") + >>> devices = session.get_devices() + >>> + >>> # Session with file-based authentication + >>> session = Session(auth_file="/path/to/auth.json") + >>> devices = session.get_devices() + >>> + >>> # Session with multiple parameters + >>> session = Session( + ... auth_url="https://auth.example.com", username="user", password="pass", project_id="project-123" + ... ) + >>> devices = session.get_devices() + """ + + def get_devices(self) -> list[Device]: + """Get available devices from this session. + + Returns: + List of available devices. + """ + +class Job: + """A job represents a submitted quantum program execution.""" + + def check(self) -> Job.Status: + """Returns the current status of the job.""" + + def wait(self, timeout: int = 0) -> bool: + """Waits for the job to complete. + + Args: + timeout: The maximum time to wait in seconds. If 0, waits indefinitely. + + Returns: + True if the job completed within the timeout, False otherwise. + """ + + def cancel(self) -> None: + """Cancels the job.""" + + def get_shots(self) -> list[str]: + """Returns the raw shot results from the job.""" + + def get_counts(self) -> dict[str, int]: + """Returns the measurement counts from the job.""" + + def get_dense_statevector(self) -> list[complex]: + """Returns the dense statevector from the job (typically only available from simulator devices).""" + + def get_dense_probabilities(self) -> list[float]: + """Returns the dense probabilities from the job (typically only available from simulator devices).""" + + def get_sparse_statevector(self) -> dict[str, complex]: + """Returns the sparse statevector from the job (typically only available from simulator devices).""" + + def get_sparse_probabilities(self) -> dict[str, float]: + """Returns the sparse probabilities from the job (typically only available from simulator devices).""" + + @property + def id(self) -> str: + """Returns the job ID.""" + + @property + def program_format(self) -> ProgramFormat: + """Returns the program format used for the job.""" + + @property + def program(self) -> str: + """Returns the quantum program submitted for the job.""" + + @property + def num_shots(self) -> int: + """Returns the number of shots for the job.""" + + def __eq__(self, arg: object, /) -> bool: ... + def __ne__(self, arg: object, /) -> bool: ... + + class Status(enum.Enum): + """Enumeration of job status.""" + + CREATED = 0 + + SUBMITTED = 1 + + QUEUED = 2 + + RUNNING = 3 + + DONE = 4 + + CANCELED = 5 + + FAILED = 6 + + CREATED: Job.Status = ... + + SUBMITTED: Job.Status = ... + + QUEUED: Job.Status = ... + + RUNNING: Job.Status = ... + + DONE: Job.Status = ... + + CANCELED: Job.Status = ... + + FAILED: Job.Status = ... + +class ProgramFormat(enum.Enum): + """Enumeration of program formats.""" + + QASM2 = 0 + + QASM3 = 1 + + QIR_BASE_STRING = 2 + + QIR_BASE_MODULE = 3 + + QIR_ADAPTIVE_STRING = 4 + + QIR_ADAPTIVE_MODULE = 5 + + CALIBRATION = 6 + + QPY = 7 + + IQM_JSON = 8 + + CUSTOM1 = 999999995 + + CUSTOM2 = 999999996 + + CUSTOM3 = 999999997 + + CUSTOM4 = 999999998 + + CUSTOM5 = 999999999 + +QASM2: ProgramFormat = ... + +QASM3: ProgramFormat = ... + +QIR_BASE_STRING: ProgramFormat = ... + +QIR_BASE_MODULE: ProgramFormat = ... + +QIR_ADAPTIVE_STRING: ProgramFormat = ... + +QIR_ADAPTIVE_MODULE: ProgramFormat = ... + +CALIBRATION: ProgramFormat = ... + +QPY: ProgramFormat = ... + +IQM_JSON: ProgramFormat = ... + +CUSTOM1: ProgramFormat = ... + +CUSTOM2: ProgramFormat = ... + +CUSTOM3: ProgramFormat = ... + +CUSTOM4: ProgramFormat = ... + +CUSTOM5: ProgramFormat = ... + +class Device: + """A device represents a quantum device with its properties and capabilities.""" + + class Status(enum.Enum): + """Enumeration of device status.""" + + offline = 0 + + idle = 1 + + busy = 2 + + error = 3 + + maintenance = 4 + + calibration = 5 + + offline: Device.Status = ... + + idle: Device.Status = ... + + busy: Device.Status = ... + + error: Device.Status = ... + + maintenance: Device.Status = ... + + calibration: Device.Status = ... + + def name(self) -> str: + """Returns the name of the device.""" + + def version(self) -> str: + """Returns the version of the device.""" + + def status(self) -> Device.Status: + """Returns the current status of the device.""" + + def library_version(self) -> str: + """Returns the version of the library used to define the device.""" + + def qubits_num(self) -> int: + """Returns the number of qubits available on the device.""" + + def sites(self) -> list[Device.Site]: + """Returns the list of all sites (zone and regular sites) available on the device.""" + + def regular_sites(self) -> list[Device.Site]: + """Returns the list of regular sites (without zone sites) available on the device.""" + + def zones(self) -> list[Device.Site]: + """Returns the list of zone sites (without regular sites) available on the device.""" + + def operations(self) -> list[Device.Operation]: + """Returns the list of operations supported by the device.""" + + def coupling_map(self) -> list[tuple[Device.Site, Device.Site]] | None: + """Returns the coupling map of the device as a list of site pairs.""" + + def needs_calibration(self) -> int | None: + """Returns whether the device needs calibration.""" + + def length_unit(self) -> str | None: + """Returns the unit of length used by the device.""" + + def length_scale_factor(self) -> float | None: + """Returns the scale factor for length used by the device.""" + + def duration_unit(self) -> str | None: + """Returns the unit of duration used by the device.""" + + def duration_scale_factor(self) -> float | None: + """Returns the scale factor for duration used by the device.""" + + def min_atom_distance(self) -> int | None: + """Returns the minimum atom distance on the device.""" + + def supported_program_formats(self) -> list[ProgramFormat]: + """Returns the list of program formats supported by the device.""" + + def submit_job(self, program: str, program_format: ProgramFormat, num_shots: int) -> Job: + """Submits a job to the device.""" + + def __eq__(self, arg: object, /) -> bool: ... + def __ne__(self, arg: object, /) -> bool: ... + + class Site: + """A site represents a potential qubit location on a quantum device.""" + + def index(self) -> int: + """Returns the index of the site.""" + + def t1(self) -> int | None: + """Returns the T1 coherence time of the site.""" + + def t2(self) -> int | None: + """Returns the T2 coherence time of the site.""" + + def name(self) -> str | None: + """Returns the name of the site.""" + + def x_coordinate(self) -> int | None: + """Returns the x coordinate of the site.""" + + def y_coordinate(self) -> int | None: + """Returns the y coordinate of the site.""" + + def z_coordinate(self) -> int | None: + """Returns the z coordinate of the site.""" + + def is_zone(self) -> bool: + """Returns whether the site is a zone.""" + + def x_extent(self) -> int | None: + """Returns the x extent of the site.""" + + def y_extent(self) -> int | None: + """Returns the y extent of the site.""" + + def z_extent(self) -> int | None: + """Returns the z extent of the site.""" + + def module_index(self) -> int | None: + """Returns the index of the module the site belongs to.""" + + def submodule_index(self) -> int | None: + """Returns the index of the submodule the site belongs to.""" + + def __eq__(self, arg: object, /) -> bool: ... + def __ne__(self, arg: object, /) -> bool: ... + + class Operation: + """An operation represents a quantum operation that can be performed on a quantum device.""" + + def name(self, sites: Sequence[Device.Site] = [], params: Sequence[float] = []) -> str: + """Returns the name of the operation.""" + + def qubits_num(self, sites: Sequence[Device.Site] = [], params: Sequence[float] = []) -> int | None: + """Returns the number of qubits the operation acts on.""" + + def parameters_num(self, sites: Sequence[Device.Site] = [], params: Sequence[float] = []) -> int: + """Returns the number of parameters the operation has.""" + + def duration(self, sites: Sequence[Device.Site] = [], params: Sequence[float] = []) -> int | None: + """Returns the duration of the operation.""" + + def fidelity(self, sites: Sequence[Device.Site] = [], params: Sequence[float] = []) -> float | None: + """Returns the fidelity of the operation.""" + + def interaction_radius(self, sites: Sequence[Device.Site] = [], params: Sequence[float] = []) -> int | None: + """Returns the interaction radius of the operation.""" + + def blocking_radius(self, sites: Sequence[Device.Site] = [], params: Sequence[float] = []) -> int | None: + """Returns the blocking radius of the operation.""" + + def idling_fidelity(self, sites: Sequence[Device.Site] = [], params: Sequence[float] = []) -> float | None: + """Returns the idling fidelity of the operation.""" + + def is_zoned(self) -> bool: + """Returns whether the operation is zoned.""" + + def sites(self) -> list[Device.Site] | None: + """Returns the list of sites the operation can be performed on.""" + + def site_pairs(self) -> list[tuple[Device.Site, Device.Site]] | None: + """Returns the list of site pairs the local 2-qubit operation can be performed on.""" + + def mean_shuttling_speed(self, sites: Sequence[Device.Site] = [], params: Sequence[float] = []) -> int | None: + """Returns the mean shuttling speed of the operation.""" + + def __eq__(self, arg: object, /) -> bool: ... + def __ne__(self, arg: object, /) -> bool: ... + +def add_dynamic_device_library( + library_path: str, + prefix: str, + base_url: str | None = None, + token: str | None = None, + auth_file: str | None = None, + auth_url: str | None = None, + username: str | None = None, + password: str | None = None, + custom1: str | None = None, + custom2: str | None = None, + custom3: str | None = None, + custom4: str | None = None, + custom5: str | None = None, +) -> Device: + """Load a dynamic device library into the QDMI driver. + + This function loads a shared library (.so, .dll, or .dylib) that implements a QDMI device interface and makes it available for use in sessions. + + Note: + This function is only available on non-Windows platforms. + + Args: + library_path: Path to the shared library file to load. + prefix: Function prefix used by the library (e.g., "MY_DEVICE"). + base_url: Optional base URL for the device API endpoint. + token: Optional authentication token. + auth_file: Optional path to authentication file. + auth_url: Optional authentication server URL. + username: Optional username for authentication. + password: Optional password for authentication. + custom1: Optional custom configuration parameter 1. + custom2: Optional custom configuration parameter 2. + custom3: Optional custom configuration parameter 3. + custom4: Optional custom configuration parameter 4. + custom5: Optional custom configuration parameter 5. + + Returns: + Device: The newly loaded device that can be used to create backends. + + Raises: + RuntimeError: If library loading fails or configuration is invalid. + + Examples: + Load a device library with configuration: + + >>> import mqt.core.fomac as fomac + >>> device = fomac.add_dynamic_device_library( + ... "/path/to/libmy_device.so", "MY_DEVICE", base_url="http://localhost:8080", custom1="API_V2" + ... ) + + Now the device can be used directly: + + >>> from mqt.core.plugins.qiskit import QDMIBackend + >>> backend = QDMIBackend(device=device) + """ diff --git a/python/mqt/core/ir/__init__.pyi b/python/mqt/core/ir/__init__.pyi new file mode 100644 index 0000000000..92dc049f74 --- /dev/null +++ b/python/mqt/core/ir/__init__.pyi @@ -0,0 +1,2034 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +from collections.abc import Iterator, Mapping, Sequence +from collections.abc import Set as AbstractSet +from typing import overload + +from . import operations as operations +from . import registers as registers +from . import symbolic as symbolic + +class Permutation: + """A class to represent a permutation of the qubits in a quantum circuit.""" + + @overload + def __init__(self) -> None: ... + @overload + def __init__(self, perm: dict) -> None: + """Create a permutation from a dictionary.""" + + @overload + def apply(self, controls: AbstractSet[operations.Control]) -> set[operations.Control]: + """Apply the permutation to a set of controls. + + Args: + controls: The set of controls to apply the permutation to. + + Returns: + The set of controls with the permutation applied. + """ + + @overload + def apply(self, targets: Sequence[int]) -> list[int]: + """Apply the permutation to a list of targets. + + Args: + targets: The list of targets to apply the permutation to. + + Returns: + The list of targets with the permutation applied. + """ + + def clear(self) -> None: + """Clear the permutation of all indices and values.""" + + def __getitem__(self, index: int) -> int: + """Get the value of the permutation at the given index. + + Args: + index: The index to get the value of the permutation at. + + Returns: + The value of the permutation at the given index. + """ + + def __setitem__(self, index: int, value: int) -> None: + """Set the value of the permutation at the given index. + + Args: + index: The index to set the value of the permutation at. + value: The value to set the permutation at the given index to. + """ + + def __delitem__(self, index: int) -> None: + """Delete the value of the permutation at the given index. + + Args: + index: The index to delete the value of the permutation at. + """ + + def __len__(self) -> int: + """Return the number of indices in the permutation.""" + + def __iter__(self) -> Iterator[int]: ... + def items(self) -> Iterator[tuple[int, int]]: ... + def __eq__(self, arg: object, /) -> bool: ... + def __ne__(self, arg: object, /) -> bool: ... + def __hash__(self) -> int: ... + +class QuantumComputation: + """The main class for representing quantum computations within the MQT. + + Acts as mutable sequence of :class:`~mqt.core.ir.operations.Operation` objects, which represent the individual operations in the quantum computation. + + Args: + nq: The number of qubits in the quantum computation. + nc: The number of classical bits in the quantum computation. + seed: The seed to use for the internal random number generator. + """ + + def __init__(self, nq: int = 0, nc: int = 0, seed: int = 0) -> None: ... + @staticmethod + def from_qasm_str(qasm: str) -> QuantumComputation: + """Create a QuantumComputation object from an OpenQASM string. + + Args: + qasm: The OpenQASM string to create the QuantumComputation object from. + + Returns: + The QuantumComputation object created from the OpenQASM string. + """ + + @staticmethod + def from_qasm(filename: str) -> QuantumComputation: + """Create a QuantumComputation object from an OpenQASM file. + + Args: + filename: The filename of the OpenQASM file to create the QuantumComputation object from. + + Returns: + The QuantumComputation object created from the OpenQASM file. + """ + + @property + def name(self) -> str: + """The name of the quantum computation.""" + + @name.setter + def name(self, arg: str, /) -> None: ... + @property + def num_qubits(self) -> int: + """The total number of qubits in the quantum computation.""" + + @property + def num_ancilla_qubits(self) -> int: + r"""The number of ancilla qubits in the quantum computation. + + Note: + Ancilla qubits are qubits that always start in a fixed state (usually :math:`|0\\rangle`). + """ + + @property + def num_garbage_qubits(self) -> int: + """The number of garbage qubits in the quantum computation. + + Note: + Garbage qubits are qubits whose final state is not relevant for the computation. + """ + + @property + def num_measured_qubits(self) -> int: + r"""The number of qubits that are measured in the quantum computation. + + Computed as :math:`| \text{qubits} | - | \text{garbage} |`. + """ + + @property + def num_data_qubits(self) -> int: + r"""The number of data qubits in the quantum computation. + + Computed as :math:`| \text{qubits} | - | \text{ancilla} |`. + """ + + @property + def num_classical_bits(self) -> int: + """The number of classical bits in the quantum computation.""" + + @property + def num_ops(self) -> int: + """The number of operations in the quantum computation.""" + + def num_single_qubit_ops(self) -> int: + """Return the number of single-qubit operations in the quantum computation.""" + + def num_total_ops(self) -> int: + """Return the total number of operations in the quantum computation. + + Recursively counts sub-operations (e.g., from :class:`~mqt.core.ir.operations.CompoundOperation` objects). + """ + + def depth(self) -> int: + """Return the depth of the quantum computation.""" + + @property + def global_phase(self) -> float: + """The global phase of the quantum computation.""" + + @global_phase.setter + def global_phase(self, arg: float, /) -> None: ... + def invert(self) -> None: + """Invert the quantum computation in-place by inverting each operation and reversing the order of operations.""" + + def to_operation(self) -> operations.Operation: + """Convert the quantum computation to a single operation. + + This gives ownership of the operations to the resulting operation, so the quantum computation will be empty after this operation. + + When the quantum computation contains more than one operation, the resulting operation is a :class:`~mqt.core.ir.operations.CompoundOperation`. + + Returns: + The operation representing the quantum computation. + """ + + @overload + def __getitem__(self, index: int) -> operations.Operation: + """Get the operation at the given index. + + Note: + This gives write access to the operation at the given index. + + Args: + index: The index of the operation to get. + + Returns: + The operation at the given index. + """ + + @overload + def __getitem__(self, index: slice) -> list[operations.Operation]: + """Get a slice of operations from the quantum computation. + + Note: + This gives write access to the operations in the given slice. + + Args: + index: The slice of operations to get. + + Returns: + The operations in the given slice. + """ + + @overload + def __setitem__(self, index: int, value: operations.Operation) -> None: + """Set the operation at the given index. + + Args: + index: The index of the operation to set. + value: The operation to set at the given index. + """ + + @overload + def __setitem__(self, index: slice, value: Sequence[operations.Operation]) -> None: + """Set the operations in the given slice. + + Args: + index: The slice of operations to set. + value: The operations to set in the given slice. + """ + + @overload + def __delitem__(self, index: int) -> None: + """Delete the operation at the given index. + + Args: + index: The index of the operation to delete. + """ + + @overload + def __delitem__(self, index: slice) -> None: + """Delete the operations in the given slice. + + Args: + index: The slice of operations to delete. + """ + + def __len__(self) -> int: + """Return the number of operations in the quantum computation.""" + + def insert(self, index: int, value: operations.Operation) -> None: + """Insert an operation at the given index. + + Args: + index: The index to insert the operation at. + value: The operation to insert. + """ + + def append(self, value: operations.Operation) -> None: + """Append an operation to the end of the quantum computation. + + Args: + value: The operation to append. + """ + + def reverse(self) -> None: + """Reverse the order of the operations in the quantum computation (in-place).""" + + def clear(self) -> None: + """Clear the quantum computation of all operations.""" + + def add_qubit_register(self, n: int, name: str = "q") -> registers.QuantumRegister: + """Add a qubit register to the quantum computation. + + Args: + n: The number of qubits in the qubit register. + name: The name of the qubit register. + + Returns: + The qubit register added to the quantum computation. + """ + + def add_classical_register(self, n: int, name: str = "c") -> registers.ClassicalRegister: + """Add a classical register to the quantum computation. + + Args: + n: The number of bits in the classical register. + name: The name of the classical register. + + Returns: + The classical register added to the quantum computation. + """ + + def add_ancillary_register(self, n: int, name: str = "anc") -> registers.QuantumRegister: + """Add an ancillary register to the quantum computation. + + Args: + n: The number of qubits in the ancillary register. + name: The name of the ancillary register. + + Returns: + The ancillary register added to the quantum computation. + """ + + def unify_quantum_registers(self, name: str = "q") -> registers.QuantumRegister: + """Unify all quantum registers in the quantum computation. + + Args: + name: The name of the unified quantum register. + + Returns: + The unified quantum register. + """ + + @property + def qregs(self) -> dict[str, registers.QuantumRegister]: + """The quantum registers in the quantum computation.""" + + @property + def cregs(self) -> dict[str, registers.ClassicalRegister]: + """The classical registers in the quantum computation.""" + + @property + def ancregs(self) -> dict[str, registers.QuantumRegister]: + """The ancillary registers in the quantum computation.""" + + @property + def initial_layout(self) -> Permutation: + """The initial layout of the qubits in the quantum computation. + + This is a permutation of the qubits in the quantum computation. + It is mainly used to track the mapping of circuit qubits to device qubits during quantum circuit compilation. + The keys are the device qubits (in which a compiled circuit is expressed in), and the values are the circuit qubits (in which the original quantum circuit is expressed in). + + Any operations in the quantum circuit are expected to be expressed in terms of the keys of the initial layout. + + Examples: + - If no initial layout is explicitly specified (which is the default), the initial layout is assumed to be the identity permutation. + - Assume a three-qubit circuit has been compiled to a four qubit device and circuit qubit 0 is mapped to device qubit 1, circuit qubit 1 is mapped to device qubit 2, and circuit qubit 2 is mapped to device qubit 3. + Then the initial layout is {1: 0, 2: 1, 3: 2}. + """ + + @initial_layout.setter + def initial_layout(self, arg: Permutation, /) -> None: ... + @property + def output_permutation(self) -> Permutation: + """The output permutation of the qubits in the quantum computation. + + This is a permutation of the qubits in the quantum computation. + It is mainly used to track where individual qubits end up at the end of the quantum computation, for example after a circuit has been compiled to a specific device and SWAP gates have been inserted, which permute the qubits. + The keys are the qubits in the circuit and the values are the actual qubits being measured. + + Similar to the initial layout, the keys in the output permutation are the qubits actually present in the circuit and the values are the qubits in the "original" circuit. + + Examples: + - If no output permutation is explicitly specified and the circuit does not contain measurements at the end, the output permutation is assumed to be the identity permutation. + - If the circuit contains measurements at the end, these measurements are used to infer the output permutation. + Assume a three-qubit circuit has been compiled to a four qubit device and, at the end of the circuit, circuit qubit 0 is measured into classical bit 2, circuit qubit 1 is measured into classical bit 1, and circuit qubit 3 is measured into classical bit 0. + Then the output permutation is {0: 2, 1: 1, 3: 0}. + """ + + @output_permutation.setter + def output_permutation(self, arg: Permutation, /) -> None: ... + def initialize_io_mapping(self) -> None: + """Initialize the I/O mapping of the quantum computation. + + If no initial layout is explicitly specified, the initial layout is assumed to be the identity permutation. + If the circuit contains measurements at the end, these measurements are used to infer the output permutation. + """ + + @property + def ancillary(self) -> list[bool]: + """A list of booleans indicating whether each qubit is ancillary.""" + + def set_circuit_qubit_ancillary(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 se_circuit_qubits_ancillary(self, q_min: int, q_max: int) -> None: + """Set a range of circuit (i.e., logical) qubits to be ancillary. + + Args: + 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, q: int) -> bool: + """Check if a circuit (i.e., logical) qubit is ancillary. + + Args: + q: The index of the circuit qubit to check. + + Returns: + True if the circuit qubit is ancillary, False otherwise. + """ + + @property + def garbage(self) -> list[bool]: + """A list of booleans indicating whether each qubit is garbage.""" + + def set_circuit_qubit_garbage(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, q_min: int, q_max: int) -> None: + """Et a range of circuit (i.e., logical) qubits to be garbage. + + Args: + 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, q: int) -> bool: + """Check if a circuit (i.e., logical) qubit is garbage. + + Args: + q: The index of the circuit qubit to check. + + Returns: + True if the circuit qubit is garbage, False otherwise. + """ + + @property + def variables(self) -> set[symbolic.Variable]: + """The set of variables in the quantum computation.""" + + def add_variable(self, var: symbolic.Expression | float) -> None: + """Add a variable to the quantum computation. + + Args: + var: The variable to add. + """ + + def add_variables(self, vars_: Sequence[symbolic.Expression | float]) -> None: + """Add multiple variables to the quantum computation. + + Args: + vars_: The variables to add. + """ + + def is_variable_free(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, assignment: Mapping[symbolic.Variable, float]) -> QuantumComputation: + """Instantiate the quantum computation with the given variable assignment. + + Args: + assignment: The variable assignment to instantiate the quantum computation with. + + Returns: + The instantiated quantum computation. + """ + + def instantiate_inplace(self, assignment: Mapping[symbolic.Variable, float]) -> None: + """Instantiate the quantum computation with the given variable assignment in-place. + + Args: + assignment: The variable assignment to instantiate the quantum computation with. + """ + + def qasm2_str(self) -> str: + """Return the OpenQASM2 representation of the quantum computation as a string. + + Note: + This uses some custom extensions to OpenQASM 2.0 that allow for easier definition of multi-controlled gates. + These extensions might not be supported by all OpenQASM 2.0 parsers. + Consider using the :meth:`qasm3_str` method instead, which uses OpenQASM 3.0 that natively supports multi-controlled gates. + The export also assumes the bigger, non-standard `qelib1.inc` from Qiskit is available. + + Returns: + The OpenQASM2 representation of the quantum computation as a string. + """ + + def qasm2(self, filename: str) -> None: + """Write the OpenQASM2 representation of the quantum computation to a file. + + See Also: + :meth:`qasm2_str` + + Args: + filename: The filename of the file to write the OpenQASM2 representation to. + """ + + def qasm3_str(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, filename: str) -> None: + """Write the OpenQASM3 representation of the quantum computation to a file. + + See Also: + :meth:`qasm3_str` + + Args: + filename: The filename of the file to write the OpenQASM3 representation to. + """ + + def i(self, q: int) -> None: + r"""Apply an identity operation. + + .. math:: + I = \begin{pmatrix} 1 & 0 \\ 0 & 1 \end{pmatrix} + + Args: + q: The target qubit + """ + + def ci(self, control: operations.Control, target: int) -> None: + """Apply a controlled identity operation. + + See Also: + :meth:`i` + + Args: + control: The control qubit + target: The target qubit + """ + + def mci(self, controls: AbstractSet[operations.Control], target: int) -> None: + """Apply a multi-controlled identity operation. + + See Also: + :meth:`i` + + Args: + controls: The control qubits + target: The target qubit + """ + + def x(self, q: int) -> None: + r"""Apply a Pauli-X gate. + + .. math:: + X = \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix} + + Args: + q: The target qubit + """ + + def cx(self, control: operations.Control, target: int) -> None: + """Apply a controlled Pauli-X (i.e., CNOT or CX) gate. + + See Also: + :meth:`x` + + Args: + control: The control qubit + target: The target qubit + """ + + def mcx(self, controls: AbstractSet[operations.Control], target: int) -> None: + """Apply a multi-controlled Pauli-X (i.e., Toffoli or MCX) gate. + + See Also: + :meth:`x` + + Args: + controls: The control qubits + target: The target qubit + """ + + def y(self, q: int) -> None: + r"""Apply a Pauli-Y gate. + + .. math:: + Y = \begin{pmatrix} 0 & -i \\ i & 0 \end{pmatrix} + + Args: + q: The target qubit + """ + + def cy(self, control: operations.Control, target: int) -> None: + """Apply a controlled Pauli-Y gate. + + See Also: + :meth:`y` + + Args: + control: The control qubit + target: The target qubit + """ + + def mcy(self, controls: AbstractSet[operations.Control], target: int) -> None: + """Apply a multi-controlled Pauli-Y gate. + + See Also: + :meth:`y` + + Args: + controls: The control qubits + target: The target qubit + """ + + def z(self, q: int) -> None: + r"""Apply a Pauli-Z gate. + + .. math:: + Z = \begin{pmatrix} 1 & 0 \\ 0 & -1 \end{pmatrix} + + Args: + q: The target qubit + """ + + def cz(self, control: operations.Control, target: int) -> None: + """Apply a controlled Pauli-Z gate. + + See Also: + :meth:`z` + + Args: + control: The control qubit + target: The target qubit + """ + + def mcz(self, controls: AbstractSet[operations.Control], target: int) -> None: + """Apply a multi-controlled Pauli-Z gate. + + See Also: + :meth:`z` + + Args: + controls: The control qubits + target: The target qubit + """ + + def h(self, q: int) -> None: + r"""Apply a Hadamard gate. + + .. math:: + H = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix} + + Args: + q: The target qubit + """ + + def ch(self, control: operations.Control, target: int) -> None: + """Apply a controlled Hadamard gate. + + See Also: + :meth:`h` + + Args: + control: The control qubit + target: The target qubit + """ + + def mch(self, controls: AbstractSet[operations.Control], target: int) -> None: + """Apply a multi-controlled Hadamard gate. + + See Also: + :meth:`h` + + Args: + controls: The control qubits + target: The target qubit + """ + + def s(self, q: int) -> None: + r"""Apply an S (i.e., phase) gate. + + .. math:: + S = \begin{pmatrix} 1 & 0 \\ 0 & i \end{pmatrix} + + Args: + q: The target qubit + """ + + def cs(self, control: operations.Control, target: int) -> None: + """Apply a controlled S gate. + + See Also: + :meth:`s` + + Args: + control: The control qubit + target: The target qubit + """ + + def mcs(self, controls: AbstractSet[operations.Control], target: int) -> None: + """Apply a multi-controlled S gate. + + See Also: + :meth:`s` + + Args: + controls: The control qubits + target: The target qubit + """ + + def sdg(self, q: int) -> None: + r"""Apply an :math:`S^\dagger` gate. + + .. math:: + S^\dagger = \begin{pmatrix} 1 & 0 \\ 0 & -i \end{pmatrix} + + Args: + q: The target qubit + """ + + def csdg(self, control: operations.Control, target: int) -> None: + r"""Apply a controlled :math:`S^\dagger` gate. + + See Also: + :meth:`sdg` + + Args: + control: The control qubit + target: The target qubit + """ + + def mcsdg(self, controls: AbstractSet[operations.Control], target: int) -> None: + r"""Apply a multi-controlled :math:`S^\dagger` gate. + + See Also: + :meth:`sdg` + + Args: + controls: The control qubits + target: The target qubit + """ + + def t(self, q: int) -> None: + r"""Apply a T gate. + + .. math:: + T = \begin{pmatrix} 1 & 0 \\ 0 & e^{i \pi / 4} \end{pmatrix} + + Args: + q: The target qubit + """ + + def ct(self, control: operations.Control, target: int) -> None: + """Apply a controlled T gate. + + See Also: + :meth:`t` + + Args: + control: The control qubit + target: The target qubit + """ + + def mct(self, controls: AbstractSet[operations.Control], target: int) -> None: + """Apply a multi-controlled T gate. + + See Also: + :meth:`t` + + Args: + controls: The control qubits + target: The target qubit + """ + + def tdg(self, q: int) -> None: + r"""Apply a :math:`T^\dagger` gate. + + .. math:: + T^\dagger = \begin{pmatrix} 1 & 0 \\ 0 & e^{-i \pi / 4} \end{pmatrix} + + Args: + q: The target qubit + """ + + def ctdg(self, control: operations.Control, target: int) -> None: + r"""Apply a controlled :math:`T^\dagger` gate. + + See Also: + :meth:`tdg` + + Args: + control: The control qubit + target: The target qubit + """ + + def mctdg(self, controls: AbstractSet[operations.Control], target: int) -> None: + r"""Apply a multi-controlled :math:`T^\dagger` gate. + + See Also: + :meth:`tdg` + + Args: + controls: The control qubits + target: The target qubit + """ + + def v(self, q: int) -> None: + r"""Apply a V gate. + + .. math:: + V = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & -i \\ -i & 1 \end{pmatrix} + + Args: + q: The target qubit + """ + + def cv(self, control: operations.Control, target: int) -> None: + """Apply a controlled V gate. + + See Also: + :meth:`v` + + Args: + control: The control qubit + target: The target qubit + """ + + def mcv(self, controls: AbstractSet[operations.Control], target: int) -> None: + """Apply a multi-controlled V gate. + + See Also: + :meth:`v` + + Args: + controls: The control qubits + target: The target qubit + """ + + def vdg(self, q: int) -> None: + r"""Apply a :math:`V^\dagger` gate. + + .. math:: + V^\dagger = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & i \\ i & 1 \end{pmatrix} + + Args: + q: The target qubit + """ + + def cvdg(self, control: operations.Control, target: int) -> None: + r"""Apply a controlled :math:`V^\dagger` gate. + + See Also: + :meth:`vdg` + + Args: + control: The control qubit + target: The target qubit + """ + + def mcvdg(self, controls: AbstractSet[operations.Control], target: int) -> None: + r"""Apply a multi-controlled :math:`V^\dagger` gate. + + See Also: + :meth:`vdg` + + Args: + controls: The control qubits + target: The target qubit + """ + + def sx(self, q: int) -> None: + r"""Apply a :math:`\sqrt{X}` gate. + + .. math:: + \sqrt{X} = \frac{1}{2} \begin{pmatrix} 1 + i & 1 - i \\ 1 - i & 1 + i \end{pmatrix} + + Args: + q: The target qubit + """ + + def csx(self, control: operations.Control, target: int) -> None: + r"""Apply a controlled :math:`\sqrt{X}` gate. + + See Also: + :meth:`sx` + + Args: + control: The control qubit + target: The target qubit + """ + + def mcsx(self, controls: AbstractSet[operations.Control], target: int) -> None: + r"""Apply a multi-controlled :math:`\sqrt{X}` gate. + + See Also: + :meth:`sx` + + Args: + controls: The control qubits + target: The target qubit + """ + + def sxdg(self, q: int) -> None: + r"""Apply a :math:`\sqrt{X}^\dagger` gate. + + .. math:: + \sqrt{X}^{\dagger} = \frac{1}{2} \begin{pmatrix} 1 - i & 1 + i \\ 1 + i & 1 - i \end{pmatrix} + + Args: + q: The target qubit + """ + + def csxdg(self, control: operations.Control, target: int) -> None: + r"""Apply a controlled :math:`\sqrt{X}^\dagger` gate. + + See Also: + :meth:`sxdg` + + Args: + control: The control qubit + target: The target qubit + """ + + def mcsxdg(self, controls: AbstractSet[operations.Control], target: int) -> None: + r"""Apply a multi-controlled :math:`\sqrt{X}^\dagger` gate. + + See Also: + :meth:`sxdg` + + Args: + controls: The control qubits + target: The target qubit + """ + + def rx(self, theta: symbolic.Expression | float, q: int) -> None: + r"""Apply an :math:`R_x(\theta)` gate. + + .. math:: + R_x(\theta) = e^{-i \theta X / 2} = \cos(\theta / 2) I - i \sin(\theta / 2) X + = \begin{pmatrix} \cos(\theta / 2) & -i \sin(\theta / 2) \\ -i \sin(\theta / 2) & \cos(\theta / 2) \end{pmatrix} + + Args: + theta: The rotation angle + q: The target qubit + """ + + def crx(self, theta: symbolic.Expression | float, control: operations.Control, target: int) -> None: + r"""Apply a controlled :math:`R_x(\theta)` gate. + + See Also: + :meth:`rx` + + Args: + theta: The rotation angle + control: The control qubit + target: The target qubit + """ + + def mcrx(self, theta: symbolic.Expression | float, controls: AbstractSet[operations.Control], target: int) -> None: + r"""Apply a multi-controlled :math:`R_x(\theta)` gate. + + See Also: + :meth:`rx` + + Args: + theta: The rotation angle + controls: The control qubits + target: The target qubit + """ + + def ry(self, theta: symbolic.Expression | float, q: int) -> None: + r"""Apply an :math:`R_y(\theta)` gate. + + .. math:: + R_y(\theta) = e^{-i \theta Y / 2} = \cos(\theta / 2) I - i \sin(\theta / 2) Y + = \begin{pmatrix} \cos(\theta / 2) & -\sin(\theta / 2) \\ \sin(\theta / 2) & \cos(\theta / 2) \end{pmatrix} + + Args: + theta: The rotation angle + q: The target qubit + """ + + def cry(self, theta: symbolic.Expression | float, control: operations.Control, target: int) -> None: + r"""Apply a controlled :math:`R_y(\theta)` gate. + + See Also: + :meth:`ry` + + Args: + theta: The rotation angle + control: The control qubit + target: The target qubit + """ + + def mcry(self, theta: symbolic.Expression | float, controls: AbstractSet[operations.Control], target: int) -> None: + r"""Apply a multi-controlled :math:`R_y(\theta)` gate. + + See Also: + :meth:`ry` + + Args: + theta: The rotation angle + controls: The control qubits + target: The target qubit + """ + + def rz(self, theta: symbolic.Expression | float, q: int) -> None: + r"""Apply an :math:`R_z(\theta)` gate. + + .. math:: + R_z(\theta) = e^{-i \theta Z / 2} = \begin{pmatrix} e^{-i \theta / 2} & 0 \\ 0 & e^{i \theta / 2} \end{pmatrix} + + Args: + theta: The rotation angle + q: The target qubit + """ + + def crz(self, theta: symbolic.Expression | float, control: operations.Control, target: int) -> None: + r"""Apply a controlled :math:`R_z(\theta)` gate. + + See Also: + :meth:`rz` + + Args: + theta: The rotation angle + control: The control qubit + target: The target qubit + """ + + def mcrz(self, theta: symbolic.Expression | float, controls: AbstractSet[operations.Control], target: int) -> None: + r"""Apply a multi-controlled :math:`R_z(\theta)` gate. + + See Also: + :meth:`rz` + + Args: + theta: The rotation angle + controls: The control qubits + target: The target qubit + """ + + def p(self, theta: symbolic.Expression | float, q: int) -> None: + r"""Apply a phase gate. + + .. math:: + P(\theta) = \begin{pmatrix} 1 & 0 \\ 0 & e^{i \ theta} \end{pmatrix} + + Args: + theta: The rotation angle + q: The target qubit + """ + + def cp(self, theta: symbolic.Expression | float, control: operations.Control, target: int) -> None: + """Apply a controlled phase gate. + + See Also: + :meth:`p` + + Args: + theta: The rotation angle + control: The control qubit + target: The target qubit + """ + + def mcp(self, theta: symbolic.Expression | float, controls: AbstractSet[operations.Control], target: int) -> None: + """Apply a multi-controlled phase gate. + + See Also: + :meth:`p` + + Args: + theta: The rotation angle + controls: The control qubits + target: The target qubit + """ + + def u2(self, phi: symbolic.Expression | float, lambda_: symbolic.Expression | float, q: int) -> None: + r"""Apply a :math:`U_2(\phi, \lambda)` gate. + + .. math:: + U_2(\phi, \lambda) = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & -e^{i \lambda} \\ e^{i \phi} & e^{i (\phi + \lambda)} \end{pmatrix} + + Args: + phi: The rotation angle + lambda_: The rotation angle + q: The target qubit + """ + + def cu2( + self, + phi: symbolic.Expression | float, + lambda_: symbolic.Expression | float, + control: operations.Control, + target: int, + ) -> None: + r"""Apply a controlled :math:`U_2(\phi, \lambda)` gate. + + See Also: + :meth:`u2` + + Args: + phi: The rotation angle + lambda_: The rotation angle + control: The control qubit + target: The target qubit + """ + + def mcu2( + self, + phi: symbolic.Expression | float, + lambda_: symbolic.Expression | float, + controls: AbstractSet[operations.Control], + target: int, + ) -> None: + r"""Apply a multi-controlled :math:`U_2(\phi, \lambda)` gate. + + See Also: + :meth:`u2` + + Args: + phi: The rotation angle + lambda_: The rotation angle + controls: The control qubits + target: The target qubit + """ + + def r(self, theta: symbolic.Expression | float, phi: symbolic.Expression | float, q: int) -> None: + r"""Apply an :math:`R(\theta, \phi)` gate. + + .. math:: + R(\theta, \phi) = e^{-i \frac{\theta}{2} (\cos(\phi) X + \sin(\phi) Y)} + = \begin{pmatrix} \cos(\theta / 2) & -i e^{-i \phi} \sin(\theta / 2) \\ -i e^{i \phi} \sin(\theta / 2) & \cos(\theta / 2) \end{pmatrix} + + Args: + theta: The rotation angle + phi: The rotation angle + q: The target qubit + """ + + def cr( + self, + theta: symbolic.Expression | float, + phi: symbolic.Expression | float, + control: operations.Control, + target: int, + ) -> None: + r"""Apply a controlled :math:`R(\theta, \phi)` gate. + + See Also: + :meth:`r` + + Args: + theta: The rotation angle + phi: The rotation angle + control: The control qubit + target: The target qubit + """ + + def mcr( + self, + theta: symbolic.Expression | float, + phi: symbolic.Expression | float, + controls: AbstractSet[operations.Control], + target: int, + ) -> None: + r"""Apply a multi-controlled :math:`R(\theta, \phi)` gate. + + See Also: + :meth:`r` + + Args: + theta: The rotation angle + phi: The rotation angle + controls: The control qubits + target: The target qubit + """ + + def u( + self, + theta: symbolic.Expression | float, + phi: symbolic.Expression | float, + lambda_: symbolic.Expression | float, + q: int, + ) -> None: + r"""Apply a :math:`U(\theta, \phi, \lambda)` gate. + + .. math:: + U(\theta, \phi, \lambda) = \begin{pmatrix} \cos(\theta / 2) & -e^{i \lambda} \sin(\theta / 2) \\ e^{i \phi} \sin(\theta / 2) & e^{i (\phi + \lambda)}\cos(\theta / 2) \end{pmatrix} + + Args: + theta: The rotation angle + phi: The rotation angle + lambda_: The rotation angle + q: The target qubit + """ + + def cu( + self, + theta: symbolic.Expression | float, + phi: symbolic.Expression | float, + lambda_: symbolic.Expression | float, + control: operations.Control, + target: int, + ) -> None: + r"""Apply a controlled :math:`U(\theta, \phi, \lambda)` gate. + + See Also: + :meth:`u` + + Args: + theta: The rotation angle + phi: The rotation angle + lambda_: The rotation angle + control: The control qubit + target: The target qubit + """ + + def mcu( + self, + theta: symbolic.Expression | float, + phi: symbolic.Expression | float, + lambda_: symbolic.Expression | float, + controls: AbstractSet[operations.Control], + target: int, + ) -> None: + r"""Apply a multi-controlled :math:`U(\theta, \phi, \lambda)` gate. + + See Also: + :meth:`u` + + Args: + theta: The rotation angle + phi: The rotation angle + lambda_: The rotation angle + controls: The control qubits + target: The target qubit + """ + + def swap(self, target1: int, target2: int) -> None: + r"""Apply a SWAP gate. + + .. math:: + \text{SWAP} = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} + + Args: + target1: The first target qubit + target2: The second target qubit + """ + + def cswap(self, control: operations.Control, target1: int, target2: int) -> None: + """Apply a controlled SWAP gate. + + See Also: + :meth:`swap` + + Args: + control: The control qubit + target1: The first target qubit + target2: The second target qubit + """ + + def mcswap(self, controls: AbstractSet[operations.Control], target1: int, target2: int) -> None: + """Apply a multi-controlled SWAP gate. + + See Also: + :meth:`swap` + + Args: + controls: The control qubits + target1: The first target qubit + target2: The second target qubit + """ + + def dcx(self, target1: int, target2: int) -> None: + r"""Apply a DCX (i.e., double CNOT) gate. + + .. math:: + DCX = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \end{pmatrix} + + Args: + target1: The first target qubit + target2: The second target qubit + """ + + def cdcx(self, control: operations.Control, target1: int, target2: int) -> None: + """Apply a controlled DCX gate. + + See Also: + :meth:`dcx` + + Args: + control: The control qubit + target1: The first target qubit + target2: The second target qubit + """ + + def mcdcx(self, controls: AbstractSet[operations.Control], target1: int, target2: int) -> None: + """Apply a multi-controlled DCX gate. + + See Also: + :meth:`dcx` + + Args: + controls: The control qubits + target1: The first target qubit + target2: The second target qubit + """ + + def ecr(self, target1: int, target2: int) -> None: + r"""Apply a ECR (echoed cross-resonance) gate. + + .. math:: + ECR = \frac{1}{\sqrt{2}} \begin{pmatrix} 0 & 0 & 1 & i \\ 0 & 0 & i & 1 \\ 1 & -i & 0 & 0 \\ -i & 1 & 0 & 0 \end{pmatrix} + + Args: + target1: The first target qubit + target2: The second target qubit + """ + + def cecr(self, control: operations.Control, target1: int, target2: int) -> None: + """Apply a controlled ECR gate. + + See Also: + :meth:`ecr` + + Args: + control: The control qubit + target1: The first target qubit + target2: The second target qubit + """ + + def mcecr(self, controls: AbstractSet[operations.Control], target1: int, target2: int) -> None: + """Apply a multi-controlled ECR gate. + + See Also: + :meth:`ecr` + + Args: + controls: The control qubits + target1: The first target qubit + target2: The second target qubit + """ + + def iswap(self, target1: int, target2: int) -> None: + r"""Apply a :math:`i\text{SWAP}` gate. + + .. math:: + i\text{SWAP} = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & i & 0 \\ 0 & i & 0 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} + + Args: + target1: The first target qubit + target2: The second target qubit + """ + + def ciswap(self, control: operations.Control, target1: int, target2: int) -> None: + r"""Apply a controlled :math:`i\text{SWAP}` gate. + + See Also: + :meth:`iswap` + + Args: + control: The control qubit + target1: The first target qubit + target2: The second target qubit + """ + + def mciswap(self, controls: AbstractSet[operations.Control], target1: int, target2: int) -> None: + r"""Apply a multi-controlled :math:`i\text{SWAP}` gate. + + See Also: + :meth:`iswap` + + Args: + controls: The control qubits + target1: The first target qubit + target2: The second target qubit + """ + + def iswapdg(self, target1: int, target2: int) -> None: + r"""Apply a :math:`i\text{SWAP}^\dagger` gate. + + .. math:: + i\text{SWAP}^\dagger = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & -i & 0 \\ 0 & -i & 0 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} + + Args: + target1: The first target qubit + target2: The second target qubit + """ + + def ciswapdg(self, control: operations.Control, target1: int, target2: int) -> None: + r"""Apply a controlled :math:`i\text{SWAP}^\dagger` gate. + + See Also: + :meth:`iswapdg` + + Args: + control: The control qubit + target1: The first target qubit + target2: The second target qubit + """ + + def mciswapdg(self, controls: AbstractSet[operations.Control], target1: int, target2: int) -> None: + r"""Apply a multi-controlled :math:`i\text{SWAP}^\dagger` gate. + + See Also: + :meth:`iswapdg` + + Args: + controls: The control qubits + target1: The first target qubit + target2: The second target qubit + """ + + def peres(self, target1: int, target2: int) -> None: + r"""Apply a Peres gate. + + .. math:: + \text{Peres} = \begin{pmatrix} 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \end{pmatrix} + + Args: + target1: The first target qubit + target2: The second target qubit + """ + + def cperes(self, control: operations.Control, target1: int, target2: int) -> None: + """Apply a controlled Peres gate. + + See Also: + :meth:`peres` + + Args: + control: The control qubit + target1: The first target qubit + target2: The second target qubit + """ + + def mcperes(self, controls: AbstractSet[operations.Control], target1: int, target2: int) -> None: + """Apply a multi-controlled Peres gate. + + See Also: + :meth:`peres` + + Args: + controls: The control qubits + target1: The first target qubit + target2: The second target qubit + """ + + def peresdg(self, target1: int, target2: int) -> None: + r"""Apply a :math:`\text{Peres}^\dagger` gate. + + .. math:: + \text{Peres}^\dagger = \begin{pmatrix} 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \end{pmatrix} + + Args: + target1: The first target qubit + target2: The second target qubit + """ + + def cperesdg(self, control: operations.Control, target1: int, target2: int) -> None: + r"""Apply a controlled :math:`\text{Peres}^\dagger` gate. + + See Also: + :meth:`peresdg` + + Args: + control: The control qubit + target1: The first target qubit + target2: The second target qubit + """ + + def mcperesdg(self, controls: AbstractSet[operations.Control], target1: int, target2: int) -> None: + r"""Apply a multi-controlled :math:`\text{Peres}^\dagger` gate. + + See Also: + :meth:`peresdg` + + Args: + controls: The control qubits + target1: The first target qubit + target2: The second target qubit + """ + + def rxx(self, theta: symbolic.Expression | float, target1: int, target2: int) -> None: + r"""Apply an :math:`R_{xx}(\theta)` gate. + + .. math:: + R_{xx}(\theta) = e^{-i \theta XX / 2} = \cos(\theta / 2) I \otimes I - i \sin(\theta / 2) X \otimes X + = \begin{pmatrix} \cos(\theta / 2) & 0 & 0 & -i \sin(\theta / 2) \\ + 0 & \cos(\theta / 2) & -i \sin(\theta / 2) & 0 \\ + 0 & -i \sin(\theta / 2) & \cos(\theta / 2) & 0 \\ + -i \sin(\theta / 2) & 0 & 0 & \cos(\theta / 2) \end{pmatrix} + + Args: + theta: The rotation angle + target1: The first target qubit + target2: The second target qubit + """ + + def crxx(self, theta: symbolic.Expression | float, control: operations.Control, target1: int, target2: int) -> None: + r"""Apply a controlled :math:`R_{xx}(\theta)` gate. + + See Also: + :meth:`rxx` + + Args: + theta: The rotation angle + control: The control qubit + target1: The first target qubit + target2: The second target qubit + """ + + def mcrxx( + self, theta: symbolic.Expression | float, controls: AbstractSet[operations.Control], target1: int, target2: int + ) -> None: + r"""Apply a multi-controlled :math:`R_{xx}(\theta)` gate. + + See Also: + :meth:`rxx` + + Args: + theta: The rotation angle + controls: The control qubits + target1: The first target qubit + target2: The second target qubit + """ + + def ryy(self, theta: symbolic.Expression | float, target1: int, target2: int) -> None: + r"""Apply an :math:`R_{yy}(\theta)` gate. + + .. math:: + R_{yy}(\theta) = e^{-i \theta YY / 2} = \cos(\theta / 2) I \otimes I - i \sin(\theta / 2) Y \otimes Y + = \begin{pmatrix} \cos(\theta / 2) & 0 & 0 & i \sin(\theta / 2) \\ + 0 & \cos(\theta / 2) & -i \sin(\theta / 2) & 0 \\ + 0 & -i \sin(\theta / 2) & \cos(\theta / 2) & 0 \\ + i \sin(\theta / 2) & 0 & 0 & \cos(\theta / 2) \end{pmatrix} + + Args: + theta: The rotation angle + target1: The first target qubit + target2: The second target qubit + """ + + def cryy(self, theta: symbolic.Expression | float, control: operations.Control, target1: int, target2: int) -> None: + r"""Apply a controlled :math:`R_{yy}(\theta)` gate. + + See Also: + :meth:`ryy` + + Args: + theta: The rotation angle + control: The control qubit + target1: The first target qubit + target2: The second target qubit + """ + + def mcryy( + self, theta: symbolic.Expression | float, controls: AbstractSet[operations.Control], target1: int, target2: int + ) -> None: + r"""Apply a multi-controlled :math:`R_{yy}(\theta)` gate. + + See Also: + :meth:`ryy` + + Args: + theta: The rotation angle + controls: The control qubits + target1: The first target qubit + target2: The second target qubit + """ + + def rzx(self, theta: symbolic.Expression | float, target1: int, target2: int) -> None: + r"""Apply an :math:`R_{zx}(\theta)` gate. + + .. math:: + R_{zx}(\theta) = e^{-i \theta ZX / 2} = \cos(\theta / 2) I \otimes I - i \sin(\theta / 2) Z \otimes X + = \begin{pmatrix} \cos(\theta/2) & -i \sin(\theta/2) & 0 & 0 \\ + -i \sin(\theta/2) & \cos(\theta/2) & 0 & 0 \\ + 0 & 0 & \cos(\theta/2) & i \sin(\theta/2) \\ + 0 & 0 & i \sin(\theta/2) & \cos(\theta/2) \end{pmatrix} + + Args: + theta: The rotation angle + target1: The first target qubit + target2: The second target qubit + """ + + def crzx(self, theta: symbolic.Expression | float, control: operations.Control, target1: int, target2: int) -> None: + r"""Apply a controlled :math:`R_{zx}(\theta)` gate. + + See Also: + :meth:`rzx` + + Args: + theta: The rotation angle + control: The control qubit + target1: The first target qubit + target2: The second target qubit + """ + + def mcrzx( + self, theta: symbolic.Expression | float, controls: AbstractSet[operations.Control], target1: int, target2: int + ) -> None: + r"""Apply a multi-controlled :math:`R_{zx}(\theta)` gate. + + See Also: + :meth:`rzx` + + Args: + theta: The rotation angle + controls: The control qubits + target1: The first target qubit + target2: The second target qubit + """ + + def rzz(self, theta: symbolic.Expression | float, target1: int, target2: int) -> None: + r"""Apply an :math:`R_{zz}(\theta)` gate. + + .. math:: + R_{zz}(\theta) = e^{-i \theta ZZ / 2} + = \begin{pmatrix} e^{-i \theta / 2} & 0 & 0 & 0 \\ + 0 & e^{i \theta / 2} & 0 & 0 \\ + 0 & 0 & e^{i \theta / 2} & 0 \\ + 0 & 0 & 0 & e^{-i \theta / 2} \end{pmatrix} + + Args: + theta: The rotation angle + target1: The first target qubit + target2: The second target qubit + """ + + def crzz(self, theta: symbolic.Expression | float, control: operations.Control, target1: int, target2: int) -> None: + r"""Apply a controlled :math:`R_{zz}(\theta)` gate. + + See Also: + :meth:`rzz` + + Args: + theta: The rotation angle + control: The control qubit + target1: The first target qubit + target2: The second target qubit + """ + + def mcrzz( + self, theta: symbolic.Expression | float, controls: AbstractSet[operations.Control], target1: int, target2: int + ) -> None: + r"""Apply a multi-controlled :math:`R_{zz}(\theta)` gate. + + See Also: + :meth:`rzz` + + Args: + theta: The rotation angle + controls: The control qubits + target1: The first target qubit + target2: The second target qubit + """ + + def xx_minus_yy( + self, theta: symbolic.Expression | float, beta: symbolic.Expression | float, target1: int, target2: int + ) -> None: + r"""Apply an :math:`R_{XX - YY}(\theta, \beta)` gate. + + .. math:: + R_{XX - YY}(\theta, \beta) = R_{z_2}(\beta) \cdot e^{-i \frac{\theta}{2} \frac{XX - YY}{2}} \cdot R_{z_2}(-\beta) + = \begin{pmatrix} \cos(\theta / 2) & 0 & 0 & -i \sin(\theta / 2) e^{-i \beta} \\ + 0 & 1 & 0 & 0 \\ + 0 & 0 & 1 & 0 \\ + -i \sin(\theta / 2) e^{i \beta} & 0 & 0 & \cos(\theta / 2) \end{pmatrix} + + Args: + theta: The rotation angle + beta: The rotation angle + target1: The first target qubit + target2: The second target qubit + """ + + def cxx_minus_yy( + self, + theta: symbolic.Expression | float, + beta: symbolic.Expression | float, + control: operations.Control, + target1: int, + target2: int, + ) -> None: + r"""Apply a controlled :math:`R_{XX - YY}(\theta, \beta)` gate. + + See Also: + :meth:`xx_minus_yy` + + Args: + theta: The rotation angle + beta: The rotation angle + control: The control qubit + target1: The first target qubit + target2: The second target qubit + """ + + def mcxx_minus_yy( + self, + theta: symbolic.Expression | float, + beta: symbolic.Expression | float, + controls: AbstractSet[operations.Control], + target1: int, + target2: int, + ) -> None: + r"""Apply a multi-controlled :math:`R_{XX - YY}(\theta, \beta)` gate. + + See Also: + :meth:`xx_minus_yy` + + Args: + theta: The rotation angle + beta: The rotation angle + controls: The control qubits + target1: The first target qubit + target2: The second target qubit + """ + + def xx_plus_yy( + self, theta: symbolic.Expression | float, beta: symbolic.Expression | float, target1: int, target2: int + ) -> None: + r"""Apply an :math:`R_{XX + YY}(\theta, \beta)` gate. + + .. math:: + R_{XX + YY}(\theta, \beta) = R_{z_1}(\beta) \cdot e^{-i \frac{\theta}{2} \frac{XX + YY}{2}} \cdot R_{z_1}(-\beta) + = \begin{pmatrix} 1 & 0 & 0 & 0 \\ + 0 & \cos(\theta / 2) & -i \sin(\theta / 2) e^{-i \beta} & 0 \\ + 0 & -i \sin(\theta / 2) e^{i \beta} & \cos(\theta / 2) & 0 \\ + 0 & 0 & 0 & 1 \end{pmatrix} + + Args: + theta: The rotation angle + beta: The rotation angle + target1: The first target qubit + target2: The second target qubit + """ + + def cxx_plus_yy( + self, + theta: symbolic.Expression | float, + beta: symbolic.Expression | float, + control: operations.Control, + target1: int, + target2: int, + ) -> None: + r"""Apply a controlled :math:`R_{XX + YY}(\theta, \beta)` gate. + + See Also: + :meth:`xx_plus_yy` + + Args: + theta: The rotation angle + beta: The rotation angle + control: The control qubit + target1: The first target qubit + target2: The second target qubit + """ + + def mcxx_plus_yy( + self, + theta: symbolic.Expression | float, + beta: symbolic.Expression | float, + controls: AbstractSet[operations.Control], + target1: int, + target2: int, + ) -> None: + r"""Apply a multi-controlled :math:`R_{XX + YY}(\theta, \beta)` gate. + + See Also: + :meth:`xx_plus_yy` + + Args: + theta: The rotation angle + beta: The rotation angle + controls: The control qubits + target1: The first target qubit + target2: The second target qubit + """ + + def gphase(self, phase: float) -> None: + r"""Apply a global phase gate. + + .. math:: + GPhase(\theta) = (e^{i \theta}) + + Args: + phase: The rotation angle + """ + + @overload + def measure(self, qubit: int, cbit: int) -> None: + """Measure a qubit and store the result in a classical bit. + + Args: + qubit: The qubit to measure + cbit: The classical bit to store the result + """ + + @overload + def measure(self, qubits: Sequence[int], cbits: Sequence[int]) -> None: + """Measure multiple qubits and store the results in classical bits. + + This method is equivalent to calling :meth:`measure` multiple times. + + Args: + qubits: The qubits to measure + cbits: The classical bits to store the results + """ + + def measure_all(self, *, add_bits: bool = True) -> None: + """Measure all qubits and store the results in classical bits. + + Details: + If `add_bits` is `True`, a new classical register (named "`meas`") with the same size as the number of qubits will be added to the circuit and the results will be stored in it. + If `add_bits` is `False`, the classical register must already exist and have a sufficient number of bits to store the results. + + Args: + add_bits: Whether to explicitly add a classical register + """ + + @overload + def reset(self, q: int) -> None: + """Add a reset operation to the circuit. + + Args: + q: The qubit to reset + """ + + @overload + def reset(self, qubits: Sequence[int]) -> None: + """Add a reset operation to the circuit. + + Args: + qubits: The qubits to reset + """ + + @overload + def barrier(self) -> None: + """Add a barrier to the circuit.""" + + @overload + def barrier(self, q: int) -> None: + """Add a barrier to the circuit. + + Args: + q: The qubit to add the barrier to + """ + + @overload + def barrier(self, qubits: Sequence[int]) -> None: + """Add a barrier to the circuit. + + Args: + qubits: The qubits to add the barrier to + """ + + @overload + def if_else( + self, + then_operation: operations.Operation, + else_operation: operations.Operation, + control_register: registers.ClassicalRegister, + expected_value: int = 1, + comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, + ) -> None: + """Add an if-else operation to the circuit. + + Args: + then_operation: The operation to apply if the condition is met + else_operation: The operation to apply if the condition is not met + control_register: The classical register to check against + expected_value: The expected value of the control register + comparison_kind: The kind of comparison to perform + """ + + @overload + def if_else( + self, + then_operation: operations.Operation, + else_operation: operations.Operation, + control_bit: int, + expected_value: int = 1, + comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, + ) -> None: + """Add an if-else operation to the circuit. + + Args: + then_operation: The operation to apply if the condition is met + else_operation: The operation to apply if the condition is not met + control_bit: The index of the classical bit to check against + expected_value: The expected value of the control bit + comparison_kind: The kind of comparison to perform + """ + + @overload + def if_( + self, + op_type: operations.OpType, + target: int, + control_register: registers.ClassicalRegister, + expected_value: int = 1, + comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, + params: Sequence[float] = [], + ) -> None: + """Add an if operartion to the circuit. + + Args: + op_type: The operation to apply + target: The target qubit + control_register: The classical register to check against + expected_value: The expected value of the control register + comparison_kind: The kind of comparison to perform + params: The parameters of the operation + """ + + @overload + def if_( + self, + op_type: operations.OpType, + target: int, + control: operations.Control, + control_register: registers.ClassicalRegister, + expected_value: int = 1, + comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, + params: Sequence[float] = [], + ) -> None: + """Add an if operartion to the circuit. + + Args: + op_type: The operation to apply + target: The target qubit + control: The control qubit + control_register: The classical register to check against + expected_value: The expected value of the control register + comparison_kind: The kind of comparison to perform + params: The parameters of the operation. + """ + + @overload + def if_( + self, + op_type: operations.OpType, + target: int, + controls: AbstractSet[operations.Control], + control_register: registers.ClassicalRegister, + expected_value: int = 1, + comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, + params: Sequence[float] = [], + ) -> None: + """Add an if operartion to the circuit. + + Args: + op_type: The operation to apply + target: The target qubit + controls: The control qubits + control_register: The classical register to check against + expected_value: The expected value of the control register + comparison_kind: The kind of comparison to perform + params: The parameters of the operation. + """ + + @overload + def if_( + self, + op_type: operations.OpType, + target: int, + control_bit: int, + expected_value: bool = True, + comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, + params: Sequence[float] = [], + ) -> None: + """Add an if operartion to the circuit. + + Args: + op_type: The operation to apply + target: The target qubit + control_bit: The index of the classical bit to check against + expected_value: The expected value of the control bit + comparison_kind: The kind of comparison to perform + params: The parameters of the operation. + """ + + @overload + def if_( + self, + op_type: operations.OpType, + target: int, + control: operations.Control, + control_bit: int, + expected_value: bool = True, + comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, + params: Sequence[float] = [], + ) -> None: + """Add an if operartion to the circuit. + + Args: + op_type: The operation to apply + target: The target qubit + control: The control qubit + control_bit: The index of the classical bit to check against + expected_value: The expected value of the control bit + comparison_kind: The kind of comparison to perform + params: The parameters of the operation. + """ + + @overload + def if_( + self, + op_type: operations.OpType, + target: int, + controls: AbstractSet[operations.Control], + control_bit: int, + expected_value: bool = True, + comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, + params: Sequence[float] = [], + ) -> None: + """Add an if operartion to the circuit. + + Args: + op_type: The operation to apply + target: The target qubit + controls: The control qubits + control_bit: The index of the classical bit to check against + expected_value: The expected value of the control bit + comparison_kind: The kind of comparison to perform + params: The parameters of the operation. + """ diff --git a/python/mqt/core/ir/operations.pyi b/python/mqt/core/ir/operations.pyi new file mode 100644 index 0000000000..65ed946fe5 --- /dev/null +++ b/python/mqt/core/ir/operations.pyi @@ -0,0 +1,979 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +import enum +from collections.abc import Mapping, Sequence +from collections.abc import Set as AbstractSet +from typing import overload + +import mqt.core.ir.registers +import mqt.core.ir.symbolic + +class OpType(enum.Enum): + """Enumeration of operation types.""" + + none = 0 + """ + A placeholder operation. + + It is used to represent an operation that is not yet defined. + """ + + gphase = 4 + """ + A global phase operation. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.gphase` + """ + + i = 10 + """ + An identity operation. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.i` + """ + + h = 16 + """ + A Hadamard gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.h` + """ + + x = 20 + """ + An X gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.x` + """ + + y = 24 + """ + A Y gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.y` + """ + + z = 30 + """ + A Z gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.z` + """ + + s = 34 + """ + An S gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.s` + """ + + sdg = 35 + r""" + An :math:`S^\dagger` gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.sdg` + """ + + t = 38 + """ + A T gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.t` + """ + + tdg = 39 + r""" + A :math:`T^\dagger` gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.tdg` + """ + + v = 40 + """ + A V gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.v` + """ + + vdg = 41 + r""" + A :math:`V^\dagger` gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.vdg` + """ + + u = 44 + """ + A U gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.u` + """ + + u2 = 48 + """ + A U2 gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.u2` + """ + + p = 54 + """ + A phase gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.p` + """ + + sx = 56 + r""" + A :math:`\sqrt{X}` gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.sx` + """ + + sxdg = 57 + r""" + A :math:`\sqrt{X}^\dagger` gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.sxdg` + """ + + rx = 60 + """ + A :math:`R_x` gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.rx` + """ + + ry = 64 + """ + A :math:`R_y` gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.ry` + """ + + rz = 70 + """ + A :math:`R_z` gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.rz` + """ + + r = 164 + """ + An :math:`R` gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.r` + """ + + swap = 72 + """ + A SWAP gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.swap` + """ + + iswap = 76 + """ + A iSWAP gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.iswap` + """ + + iswapdg = 77 + r""" + A :math:`i\text{SWAP}^\dagger` gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.iswapdg` + """ + + peres = 80 + """ + A Peres gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.peres` + """ + + peresdg = 81 + r""" + A :math:`\text{Peres}^\dagger` gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.peresdg` + """ + + dcx = 84 + """ + A DCX gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.dcx` + """ + + ecr = 88 + """ + An ECR gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.ecr` + """ + + rxx = 92 + """ + A :math:`R_{xx}` gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.rxx` + """ + + ryy = 96 + """ + A :math:`R_{yy}` gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.ryy` + """ + + rzz = 102 + """ + A :math:`R_{zz}` gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.rzz` + """ + + rzx = 104 + """ + A :math:`R_{zx}` gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.rzx` + """ + + xx_minus_yy = 108 + """ + A :math:`R_{XX - YY}` gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.xx_minus_yy` + """ + + xx_plus_yy = 112 + """ + A :math:`R_{XX + YY}` gate. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.xx_plus_yy` + """ + + compound = 116 + """ + A compound operation. + + It is used to group multiple operations into a single operation. + + See also :class:`.CompoundOperation` + """ + + measure = 120 + """ + A measurement operation. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.measure` + """ + + reset = 124 + """ + A reset operation. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.reset` + """ + + barrier = 14 + """ + A barrier operation. + + It is used to separate operations in the circuit. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.barrier` + """ + + if_else = 128 + """ + An if-else operation. + + It is used to control the execution of an operation based on the value of a classical register. + + See Also: + :meth:`mqt.core.ir.QuantumComputation.if_else` + """ + +none: OpType = ... + +gphase: OpType = ... + +i: OpType = ... + +h: OpType = ... + +x: OpType = ... + +y: OpType = ... + +z: OpType = ... + +s: OpType = ... + +sdg: OpType = ... + +t: OpType = ... + +tdg: OpType = ... + +v: OpType = ... + +vdg: OpType = ... + +u: OpType = ... + +u2: OpType = ... + +p: OpType = ... + +sx: OpType = ... + +sxdg: OpType = ... + +rx: OpType = ... + +ry: OpType = ... + +rz: OpType = ... + +r: OpType = ... + +swap: OpType = ... + +iswap: OpType = ... + +iswapdg: OpType = ... + +peres: OpType = ... + +peresdg: OpType = ... + +dcx: OpType = ... + +ecr: OpType = ... + +rxx: OpType = ... + +ryy: OpType = ... + +rzz: OpType = ... + +rzx: OpType = ... + +xx_minus_yy: OpType = ... + +xx_plus_yy: OpType = ... + +compound: OpType = ... + +measure: OpType = ... + +reset: OpType = ... + +barrier: OpType = ... + +if_else: OpType = ... + +class Control: + """A control is a pair of a qubit and a type. The type can be either positive or negative. + + Args: + qubit: The qubit that is the control. + type_: The type of the control. + """ + + def __init__(self, qubit: int, type_: Control.Type = Control.Type.Pos) -> None: ... + + class Type(enum.Enum): + """Enumeration of control types.""" + + Pos = 1 + + Neg = 0 + + @property + def qubit(self) -> int: + """The qubit that is the control.""" + + @qubit.setter + def qubit(self, arg: int, /) -> None: ... + @property + def type_(self) -> Control.Type: + """The type of the control.""" + + @type_.setter + def type_(self, arg: Control.Type, /) -> None: ... + def __eq__(self, arg: object, /) -> bool: ... + def __ne__(self, arg: object, /) -> bool: ... + def __hash__(self) -> int: ... + +class Operation: + @property + def name(self) -> str: + """The name of the operation.""" + + @property + def type_(self) -> OpType: + """The type of the operation.""" + + @type_.setter + def type_(self, arg: OpType, /) -> None: ... + @property + def targets(self) -> list[int]: + """The targets of the operation. + + Note: + The notion of a target might not make sense for all types of operations. + """ + + @targets.setter + def targets(self, arg: Sequence[int], /) -> None: ... + @property + def num_targets(self) -> int: + """The number of targets of the operation.""" + + @property + def controls(self) -> set[Control]: + """The controls of the operation. + + Note: + The notion of a control might not make sense for all types of operations. + """ + + @controls.setter + def controls(self, arg: AbstractSet[Control], /) -> None: ... + @property + def num_controls(self) -> int: + """The number of controls of the operation.""" + + def add_control(self, control: Control) -> None: + """Add a control to the operation. + + Args: + control: The control to add. + """ + + def add_controls(self, controls: AbstractSet[Control]) -> None: + """Add multiple controls to the operation. + + Args: + controls: The controls to add. + """ + + def clear_controls(self) -> None: + """Clear all controls of the operation.""" + + def remove_control(self, control: Control) -> None: + """Remove a control from the operation. + + Args: + control: The control to remove. + """ + + def remove_controls(self, controls: AbstractSet[Control]) -> None: + """Remove multiple controls from the operation. + + Args: + controls: The controls to remove. + """ + + def get_used_qubits(self) -> set[int]: + """Get the qubits that are used by the operation. + + Returns: + The set of qubits that are used by the operation. + """ + + def acts_on(self, qubit: int) -> bool: + """Check if the operation acts on a specific qubit. + + Args: + qubit: The qubit to check. + + Returns: + True if the operation acts on the qubit, False otherwise. + """ + + @property + def parameter(self) -> list[float]: + """The parameters of the operation. + + Note: + The notion of a parameter might not make sense for all types of operations. + """ + + @parameter.setter + def parameter(self, arg: Sequence[float], /) -> None: ... + def is_unitary(self) -> bool: + """Check if the operation is unitary. + + Returns: + True if the operation is unitary, False otherwise. + """ + + def is_standard_operation(self) -> bool: + """Check if the operation is a :class:`StandardOperation`. + + Returns: + True if the operation is a :class:`StandardOperation`, False otherwise. + """ + + def is_compound_operation(self) -> bool: + """Check if the operation is a :class:`CompoundOperation`. + + Returns: + True if the operation is a :class:`CompoundOperation`, False otherwise. + """ + + def is_non_unitary_operation(self) -> bool: + """Check if the operation is a :class:`NonUnitaryOperation`. + + Returns: + True if the operation is a :class:`NonUnitaryOperation`, False otherwise. + """ + + def is_if_else_operation(self) -> bool: + """Check if the operation is a :class:`IfElseOperation`. + + Returns: + True if the operation is a :class:`IfElseOperation`, False otherwise. + """ + + def is_symbolic_operation(self) -> bool: + """Check if the operation is a :class:`SymbolicOperation`. + + Returns: + True if the operation is a :class:`SymbolicOperation`, False otherwise. + """ + + def is_controlled(self) -> bool: + """Check if the operation is controlled. + + Returns: + True if the operation is controlled, False otherwise. + """ + + def get_inverted(self) -> Operation: + """Get the inverse of the operation. + + Returns: + The inverse of the operation. + """ + + def invert(self) -> None: + """Invert the operation (in-place).""" + + def __eq__(self, other: Operation) -> bool: ... + def __ne__(self, other: Operation) -> bool: ... + def __hash__(self) -> int: ... + +class StandardOperation(Operation): + """Standard quantum operation. + + This class is used to represent all standard quantum operations, i.e., operations that are unitary. + This includes all possible quantum gates. + Such Operations are defined by their :class:`OpType`, the qubits (controls and targets) they act on, and their parameters. + + Args: + control: The control qubit(s) of the operation (if any). + target: The target qubit(s) of the operation. + op_type: The type of the operation. + params: The parameters of the operation (if any). + """ + + @overload + def __init__(self) -> None: ... + @overload + def __init__(self, target: int, op_type: OpType, params: Sequence[float] = []) -> None: ... + @overload + def __init__(self, targets: Sequence[int], op_type: OpType, params: Sequence[float] = []) -> None: ... + @overload + def __init__(self, control: Control, target: int, op_type: OpType, params: Sequence[float] = []) -> None: ... + @overload + def __init__( + self, control: Control, targets: Sequence[int], op_type: OpType, params: Sequence[float] = [] + ) -> None: ... + @overload + def __init__( + self, controls: AbstractSet[Control], target: int, op_type: OpType, params: Sequence[float] = [] + ) -> None: ... + @overload + def __init__( + self, controls: AbstractSet[Control], targets: Sequence[int], op_type: OpType, params: Sequence[float] = [] + ) -> None: ... + @overload + def __init__( + self, controls: AbstractSet[Control], target0: int, target1: int, op_type: OpType, params: Sequence[float] = [] + ) -> None: ... + +class CompoundOperation(Operation): + """Compound quantum operation. + + This class is used to aggregate and group multiple operations into a single object. + This is useful for optimizations and for representing complex quantum functionality. + A :class:`CompoundOperation` can contain any number of operations, including other :class:`CompoundOperation`'s. + + Args: + ops: The operations that are part of the compound operation. + """ + + @overload + def __init__(self) -> None: ... + @overload + def __init__(self, ops: Sequence[Operation]) -> None: ... + def __len__(self) -> int: + """The number of operations in the compound operation.""" + + @overload + def __getitem__(self, index: int) -> Operation: + """Get the operation at the given index. + + Note: + This gives direct access to the operations in the compound operation + + Args: + index: The index of the operation to get. + + Returns: + The operation at the given index. + """ + + @overload + def __getitem__(self, index: slice) -> list[Operation]: + """Get the operations in the given slice. + + Note: + This gives direct access to the operations in the compound operation. + + Args: + index: The slice of the operations to get. + + Returns: + The operations in the given slice. + """ + + @overload + def __setitem__(self, index: int, value: Operation) -> None: + """Set the operation at the given index. + + Args: + index: The index of the operation to set. + value: The operation to set at the given index. + """ + + @overload + def __setitem__(self, index: slice, value: Sequence[Operation]) -> None: + """Set the operations in the given slice. + + Args: + index: The slice of operations to set. + value: The operations to set in the given slice. + """ + + @overload + def __delitem__(self, index: int) -> None: + """Delete the operation at the given index. + + Args: + index: The index of the operation to delete. + """ + + @overload + def __delitem__(self, index: slice) -> None: + """Delete the operations in the given slice. + + Args: + index: The slice of operations to delete. + """ + + def append(self, value: Operation) -> None: + """Append an operation to the compound operation.""" + + def insert(self, index: int, value: Operation) -> None: + """Insert an operation at the given index. + + Args: + index: The index to insert the operation at. + value: The operation to insert. + """ + + def empty(self) -> bool: + """Check if the compound operation is empty.""" + + def clear(self) -> None: + """Clear all operations in the compound operation.""" + +class NonUnitaryOperation(Operation): + """Non-unitary operation. + + This class is used to represent all non-unitary operations, i.e., operations that are not reversible. + This includes measurements and resets. + + Args: + targets: The target qubit(s) of the operation. + classics: The classical bit(s) that are associated with the operation (only relevant for measurements). + op_type: The type of the operation. + """ + + @overload + def __init__(self, targets: Sequence[int], classics: Sequence[int]) -> None: ... + @overload + def __init__(self, target: int, classic: int) -> None: ... + @overload + def __init__(self, targets: Sequence[int], op_type: OpType = OpType.reset) -> None: ... + @property + def classics(self) -> list[int]: + """The classical registers that are associated with the operation.""" + +class SymbolicOperation(StandardOperation): + """Symbolic quantum operation. + + This class is used to represent quantum operations that are not yet fully defined. + This can be useful for representing operations that depend on parameters that are not yet known. + A :class:`SymbolicOperation` is defined by its :class:`OpType`, the qubits (controls and targets) it acts on, and its parameters. + The parameters can be either fixed values or symbolic expressions. + + Args: + controls: The control qubit(s) of the operation (if any). + targets: The target qubit(s) of the operation. + op_type: The type of the operation. + params: The parameters of the operation (if any). + """ + + @overload + def __init__(self) -> None: ... + @overload + def __init__( + self, target: int, op_type: OpType, params: Sequence[mqt.core.ir.symbolic.Expression | float] = [] + ) -> None: ... + @overload + def __init__( + self, targets: Sequence[int], op_type: OpType, params: Sequence[mqt.core.ir.symbolic.Expression | float] = [] + ) -> None: ... + @overload + def __init__( + self, + control: Control, + target: int, + op_type: OpType, + params: Sequence[mqt.core.ir.symbolic.Expression | float] = [], + ) -> None: ... + @overload + def __init__( + self, + control: Control, + targets: Sequence[int], + op_type: OpType, + params: Sequence[mqt.core.ir.symbolic.Expression | float] = [], + ) -> None: ... + @overload + def __init__( + self, + controls: AbstractSet[Control], + target: int, + op_type: OpType, + params: Sequence[mqt.core.ir.symbolic.Expression | float] = [], + ) -> None: ... + @overload + def __init__( + self, + controls: AbstractSet[Control], + targets: Sequence[int], + op_type: OpType, + params: Sequence[mqt.core.ir.symbolic.Expression | float] = [], + ) -> None: ... + @overload + def __init__( + self, + controls: AbstractSet[Control], + target0: int, + target1: int, + op_type: OpType, + params: Sequence[mqt.core.ir.symbolic.Expression | float] = [], + ) -> None: ... + def get_parameter(self, index: int) -> mqt.core.ir.symbolic.Expression | float: + """Get the parameter at the given index. + + Args: + index: The index of the parameter to get. + + Returns: + The parameter at the given index. + """ + + def get_parameters(self) -> list[mqt.core.ir.symbolic.Expression | float]: + """Get all parameters of the operation. + + Returns: + The parameters of the operation. + """ + + def get_instantiated_operation( + self, assignment: Mapping[mqt.core.ir.symbolic.Variable, float] + ) -> StandardOperation: + """Get the instantiated operation. + + Args: + assignment: The assignment of the symbolic parameters. + + Returns: + The instantiated operation. + """ + + def instantiate(self, assignment: Mapping[mqt.core.ir.symbolic.Variable, float]) -> None: + """Instantiate the operation (in-place). + + Args: + assignment: The assignment of the symbolic parameters. + """ + +class ComparisonKind(enum.Enum): + """Enumeration of comparison types for classic-controlled operations.""" + + eq = 0 + """Equality comparison.""" + + neq = 1 + """Inequality comparison.""" + + lt = 2 + """Less-than comparison.""" + + leq = 3 + """Less-than-or-equal comparison.""" + + gt = 4 + """Greater-than comparison.""" + + geq = 5 + """Greater-than-or-equal comparison.""" + +eq: ComparisonKind = ... + +neq: ComparisonKind = ... + +lt: ComparisonKind = ... + +leq: ComparisonKind = ... + +gt: ComparisonKind = ... + +geq: ComparisonKind = ... + +class IfElseOperation(Operation): + """If-else quantum operation. + + This class is used to represent an if-else operation. + The then operation is executed if the value of the classical register matches the expected value. + Otherwise, the else operation is executed. + + Args: + then_operation: The operation that is executed if the condition is met. + else_operation: The operation that is executed if the condition is not met. + control_register: The classical register that controls the operation. + expected_value: The expected value of the classical register. + comparison_kind: The kind of comparison (default is equality). + """ + + @overload + def __init__( + self, + then_operation: Operation, + else_operation: Operation, + control_register: mqt.core.ir.registers.ClassicalRegister, + expected_value: int = 1, + comparison_kind: ComparisonKind = ComparisonKind.eq, + ) -> None: ... + @overload + def __init__( + self, + then_operation: Operation, + else_operation: Operation, + control_bit: int, + expected_value: int = 1, + comparison_kind: ComparisonKind = ComparisonKind.eq, + ) -> None: ... + @property + def then_operation(self) -> Operation: + """The operation that is executed if the condition is met.""" + + @property + def else_operation(self) -> Operation: + """The operation that is executed if the condition is not met.""" + + @property + def control_register(self) -> mqt.core.ir.registers.ClassicalRegister | None: + """The classical register that controls the operation.""" + + @property + def control_bit(self) -> int | None: + """The classical bit that controls the operation.""" + + @property + def expected_value_register(self) -> int: + """The expected value of the classical register. + + The then-operation is executed if the value of the classical register matches the expected value based on the kind of comparison. + 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. + """ + + @property + def expected_value_bit(self) -> bool: + """The expected value of the classical bit. + + The then-operation is executed if the value of the classical bit matches the expected value based on the kind of comparison. + """ + + @property + def comparison_kind(self) -> ComparisonKind: + """The kind of comparison. + + The then-operation is executed if the value of the control matches the expected value based on the kind of comparison. + """ diff --git a/python/mqt/core/ir/registers.pyi b/python/mqt/core/ir/registers.pyi new file mode 100644 index 0000000000..a224a4a7ef --- /dev/null +++ b/python/mqt/core/ir/registers.pyi @@ -0,0 +1,85 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +class QuantumRegister: + """A class to represent a collection of qubits. + + Args: + start: The starting index of the quantum register. + size: The number of qubits in the quantum register. + name: The name of the quantum register. A name will be generated if not provided. + """ + + def __init__(self, start: int, size: int, name: str = "") -> None: ... + @property + def name(self) -> str: + """The name of the quantum register.""" + + @property + def start(self) -> int: + """The index of the first qubit in the quantum register.""" + + @start.setter + def start(self, arg: int, /) -> None: ... + @property + def size(self) -> int: + """The number of qubits in the quantum register.""" + + @size.setter + def size(self, arg: int, /) -> None: ... + @property + def end(self) -> int: + """Index of the last qubit in the quantum register.""" + + def __eq__(self, arg: object, /) -> bool: ... + def __ne__(self, arg: object, /) -> bool: ... + def __hash__(self) -> int: ... + def __getitem__(self, key: int) -> int: + """Get the qubit at the specified index.""" + + def __contains__(self, item: int) -> bool: + """Check if the quantum register contains a qubit.""" + +class ClassicalRegister: + """A class to represent a collection of classical bits. + + Args: + start: The starting index of the classical register. + size: The number of bits in the classical register. + name: The name of the classical register. A name will be generated if not provided. + """ + + def __init__(self, start: int, size: int, name: str = "") -> None: ... + @property + def name(self) -> str: + """The name of the classical register.""" + + @property + def start(self) -> int: + """The index of the first bit in the classical register.""" + + @start.setter + def start(self, arg: int, /) -> None: ... + @property + def size(self) -> int: + """The number of bits in the classical register.""" + + @size.setter + def size(self, arg: int, /) -> None: ... + @property + def end(self) -> int: + """Index of the last bit in the classical register.""" + + def __eq__(self, arg: object, /) -> bool: ... + def __ne__(self, arg: object, /) -> bool: ... + def __hash__(self) -> int: ... + def __getitem__(self, key: int) -> int: + """Get the bit at the specified index.""" + + def __contains__(self, item: int) -> bool: + """Check if the classical register contains a bit.""" diff --git a/python/mqt/core/ir/symbolic.pyi b/python/mqt/core/ir/symbolic.pyi new file mode 100644 index 0000000000..8b02920cb0 --- /dev/null +++ b/python/mqt/core/ir/symbolic.pyi @@ -0,0 +1,157 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +from collections.abc import Iterator, Mapping, Sequence +from typing import overload + +class Variable: + """A symbolic 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. + + Args: + name: The name of the variable. + """ + + def __init__(self, name: str = "") -> None: ... + @property + def name(self) -> str: + """The name of the variable.""" + + def __eq__(self, arg: object, /) -> bool: ... + def __ne__(self, arg: object, /) -> bool: ... + def __hash__(self) -> int: ... + def __lt__(self, arg: Variable, /) -> bool: ... + def __gt__(self, arg: Variable, /) -> bool: ... + +class Term: + """A symbolic term which consists of a variable with a given coefficient. + + Args: + variable: The variable of the term. + coefficient: The coefficient of the term. + """ + + def __init__(self, variable: Variable, coefficient: float = 1.0) -> None: ... + @property + def variable(self) -> Variable: + """The variable of the term.""" + + @property + def coefficient(self) -> float: + """The coefficient of the term.""" + + def has_zero_coefficient(self) -> bool: + """Check if the coefficient of the term is zero.""" + + def add_coefficient(self, coeff: float) -> None: + """Add a coefficient to the coefficient of this term. + + Args: + coeff: The coefficient to add. + """ + + def evaluate(self, assignment: Mapping[Variable, float]) -> float: + """Evaluate the term with a given variable assignment. + + Args: + assignment: The variable assignment. + + Returns: + The evaluated value of the term. + """ + + def __mul__(self, arg: float, /) -> Term: ... + def __rmul__(self, arg: float, /) -> Term: ... + def __truediv__(self, arg: float, /) -> Term: ... + def __rtruediv__(self, arg: float, /) -> Term: ... + def __eq__(self, arg: object, /) -> bool: ... + def __ne__(self, arg: object, /) -> bool: ... + def __hash__(self) -> int: ... + +class Expression: + r"""A symbolic expression which consists of a sum of terms and a constant. + + The expression is of the form :math:`constant + term_1 + term_2 + \dots + term_n`. + Alternatively, an expression can be created with a single term and a constant or just a constant. + + Args: + terms: The list of terms. + constant: The constant. + """ + + @overload + def __init__(self, constant: float = 0.0) -> None: ... + @overload + def __init__(self, terms: Sequence[Term], constant: float = 0.0) -> None: ... + @overload + def __init__(self, term: Term, constant: float = 0.0) -> None: ... + @property + def constant(self) -> float: + """The constant of the expression.""" + + @constant.setter + def constant(self, arg: float, /) -> None: ... + def __iter__(self) -> Iterator[Term]: ... + def __getitem__(self, key: int) -> Term: ... + def is_zero(self) -> bool: + """Check if the expression is zero.""" + + def is_constant(self) -> bool: + """Check if the expression is a constant.""" + + def num_terms(self) -> int: + """The number of terms in the expression.""" + + def __len__(self) -> int: ... + @property + def terms(self) -> list[Term]: + """The terms of the expression.""" + + @property + def variables(self) -> set[Variable]: + """The variables in the expression.""" + + def evaluate(self, assignment: Mapping[Variable, float]) -> float: + """Evaluate the expression with a given variable assignment. + + Args: + assignment: The variable assignment. + + Returns: + The evaluated value of the expression. + """ + + @overload + def __add__(self, arg: Expression, /) -> Expression: ... + @overload + def __add__(self, arg: float, /) -> Expression: ... + @overload + def __add__(self, arg: Term, /) -> Expression: ... + @overload + def __radd__(self, arg: Term, /) -> Expression: ... + @overload + def __radd__(self, arg: float, /) -> Expression: ... + @overload + def __sub__(self, arg: Expression, /) -> Expression: ... + @overload + def __sub__(self, arg: float, /) -> Expression: ... + @overload + def __sub__(self, arg: Term, /) -> Expression: ... + @overload + def __rsub__(self, arg: float, /) -> Expression: ... + @overload + def __rsub__(self, arg: Term, /) -> Expression: ... + def __mul__(self, arg: float, /) -> Expression: ... + def __rmul__(self, arg: float, /) -> Expression: ... + def __truediv__(self, arg: float, /) -> Expression: ... + def __rtruediv__(self, arg: float, /) -> Expression: ... + def __eq__(self, arg: object, /) -> bool: ... + def __ne__(self, arg: object, /) -> bool: ... + def __hash__(self) -> int: ... diff --git a/python/mqt/core/na/fomac.pyi b/python/mqt/core/na/fomac.pyi new file mode 100644 index 0000000000..458bc5bdbc --- /dev/null +++ b/python/mqt/core/na/fomac.pyi @@ -0,0 +1,109 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +import mqt.core.fomac + +class Device(mqt.core.fomac.Device): + """Represents a device with a lattice of traps.""" + + class Lattice: + """Represents a lattice of traps in the device.""" + + class Vector: + """Represents a 2D vector.""" + + @property + def x(self) -> int: + """The x-coordinate of the vector.""" + + @property + def y(self) -> int: + """The y-coordinate of the vector.""" + + def __eq__(self, arg: object, /) -> bool: ... + def __ne__(self, arg: object, /) -> bool: ... + + class Region: + """Represents a region in the device.""" + + class Size: + """Represents the size of a region.""" + + @property + def width(self) -> int: + """The width of the region.""" + + @property + def height(self) -> int: + """The height of the region.""" + + def __eq__(self, arg: object, /) -> bool: ... + def __ne__(self, arg: object, /) -> bool: ... + + @property + def origin(self) -> Device.Lattice.Vector: + """The origin of the region.""" + + @property + def size(self) -> Device.Lattice.Region.Size: + """The size of the region.""" + + def __eq__(self, arg: object, /) -> bool: ... + def __ne__(self, arg: object, /) -> bool: ... + + @property + def lattice_origin(self) -> Device.Lattice.Vector: + """The origin of the lattice.""" + + @property + def lattice_vector_1(self) -> Device.Lattice.Vector: + """The first lattice vector.""" + + @property + def lattice_vector_2(self) -> Device.Lattice.Vector: + """The second lattice vector.""" + + @property + def sublattice_offsets(self) -> list[Device.Lattice.Vector]: + """The offsets of the sublattices.""" + + @property + def extent(self) -> Device.Lattice.Region: + """The extent of the lattice.""" + + def __eq__(self, arg: object, /) -> bool: ... + def __ne__(self, arg: object, /) -> bool: ... + + @property + def traps(self) -> list[Device.Lattice]: + """The list of trap positions in the device.""" + + @property + def t1(self) -> int: + """The T1 time of the device.""" + + @property + def t2(self) -> int: + """The T2 time of the device.""" + + @staticmethod + def try_create_from_device(device: mqt.core.fomac.Device) -> Device | None: + """Create NA FoMaC Device from generic FoMaC Device. + + Args: + device: The generic FoMaC Device to convert. + + Returns: + The converted NA FoMaC Device or None if the conversion is not possible. + """ + + def __eq__(self, arg: object, /) -> bool: ... + def __ne__(self, arg: object, /) -> bool: ... + +def devices() -> list[Device]: + """Returns a list of available devices.""" From ad2e5766aa4679aec63ae457caf396dc8cae4df7 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 14 Dec 2025 00:19:23 +0100 Subject: [PATCH 039/144] Fix Operation comparison --- bindings/ir/operations/register_operation.cpp | 23 ++++++------------- python/mqt/core/ir/operations.pyi | 4 ++-- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/bindings/ir/operations/register_operation.cpp b/bindings/ir/operations/register_operation.cpp index ebda6fca4a..7bbd6fcb6b 100644 --- a/bindings/ir/operations/register_operation.cpp +++ b/bindings/ir/operations/register_operation.cpp @@ -12,6 +12,7 @@ #include "ir/operations/Operation.hpp" #include +#include #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) @@ -154,22 +155,12 @@ void registerOperation(const nb::module_& m) { .def("invert", &qc::Operation::invert, "Invert the operation (in-place).") - .def( - "__eq__", - [](const qc::Operation& op, const qc::Operation& other) { - return op == other; - }, - nb::arg("other").sig("object")) - .def( - "__ne__", - [](const qc::Operation& op, const qc::Operation& other) { - return op != other; - }, - nb::arg("other").sig("object")) - .def("__hash__", - [](const qc::Operation& op) { - return std::hash{}(op); - }) + .def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __eq__(self, arg: object, /) -> bool")) + .def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + nb::sig("def __ne__(self, arg: object, /) -> bool")) + .def(hash(nb::self)) + .def("__repr__", [](const qc::Operation& op) { std::ostringstream oss; oss << "Operation(type=" << op.getType() << ", ...)"; diff --git a/python/mqt/core/ir/operations.pyi b/python/mqt/core/ir/operations.pyi index 65ed946fe5..3a9b5d6798 100644 --- a/python/mqt/core/ir/operations.pyi +++ b/python/mqt/core/ir/operations.pyi @@ -610,8 +610,8 @@ class Operation: def invert(self) -> None: """Invert the operation (in-place).""" - def __eq__(self, other: Operation) -> bool: ... - def __ne__(self, other: Operation) -> bool: ... + def __eq__(self, arg: object, /) -> bool: ... + def __ne__(self, arg: object, /) -> bool: ... def __hash__(self) -> int: ... class StandardOperation(Operation): From 8f6120ebda221d2d394a7bd9261568452d7407b7 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 14 Dec 2025 00:25:30 +0100 Subject: [PATCH 040/144] Fix remaining typing issues --- bindings/dd/register_dd_package.cpp | 44 +- .../register_compound_operation.cpp | 11 +- bindings/ir/operations/register_control.cpp | 2 +- .../operations/register_if_else_operation.cpp | 7 +- bindings/ir/register_permutation.cpp | 9 +- bindings/ir/register_quantum_computation.cpp | 684 +++++++++++++----- pyproject.toml | 2 +- python/mqt/core/dd.pyi | 8 +- python/mqt/core/ir/__init__.pyi | 174 +++-- python/mqt/core/ir/operations.pyi | 12 +- 10 files changed, 684 insertions(+), 269 deletions(-) diff --git a/bindings/dd/register_dd_package.cpp b/bindings/dd/register_dd_package.cpp index 58bfe1f882..8e878841c2 100644 --- a/bindings/dd/register_dd_package.cpp +++ b/bindings/dd/register_dd_package.cpp @@ -30,6 +30,7 @@ #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) #include #include @@ -44,7 +45,11 @@ using namespace nb::literals; using Vector = nb::ndarray, nb::ndim<1>>; using Matrix = nb::ndarray, nb::ndim<2>>; +using Control = std::variant; +using Controls = std::set>; + namespace { + /// Recursive helper function to create a vector DD from a numpy array dd::vCachedEdge makeDDFromVector(dd::Package& p, const Vector& v, const size_t startIdx, const size_t endIdx, @@ -89,6 +94,25 @@ dd::mCachedEdge makeDDFromMatrix(dd::Package& p, const Matrix& m, makeDDFromMatrix(p, m, rowHalf, rowEnd, colStart, colHalf, level - 1), makeDDFromMatrix(p, m, rowHalf, rowEnd, colHalf, colEnd, level - 1)}); } + +/// Helper function to convert Control variant to qc::Control +qc::Control getControl(const Control& control) { + if (std::holds_alternative(control)) { + return std::get(control); + } else { + return static_cast(std::get(control)); + } +} + +/// Helper function to convert Controls variant to qc::Controls +qc::Controls getControls(const Controls& controls) { + qc::Controls result; + for (const auto& control : controls) { + result.insert(getControl(control)); + } + return result; +} + } // namespace // NOLINTNEXTLINE(misc-use-internal-linkage) @@ -455,13 +479,13 @@ Specifically, it dd.def( "controlled_single_qubit_gate", - [](dd::Package& p, const Matrix& m, const qc::Control& control, + [](dd::Package& p, const Matrix& m, const Control& control, const dd::Qubit target) { if (m.ndim() != 2 || m.shape(0) != 2 || m.shape(1) != 2) { throw std::invalid_argument("Matrix must be 2x2."); } - return p.makeGateDD({m(0, 0), m(0, 1), m(1, 0), m(1, 1)}, control, - target); + return p.makeGateDD({m(0, 0), m(0, 1), m(1, 0), m(1, 1)}, + getControl(control), target); }, "matrix"_a, "control"_a, "target"_a, // keep the DD package alive while the returned matrix DD is alive. @@ -478,13 +502,13 @@ Specifically, it dd.def( "multi_controlled_single_qubit_gate", - [](dd::Package& p, const Matrix& m, const qc::Controls& controls, + [](dd::Package& p, const Matrix& m, const Controls& controls, const dd::Qubit target) { if (m.ndim() != 2 || m.shape(0) != 2 || m.shape(1) != 2) { throw std::invalid_argument("Matrix must be 2x2."); } - return p.makeGateDD({m(0, 0), m(0, 1), m(1, 0), m(1, 1)}, controls, - target); + return p.makeGateDD({m(0, 0), m(0, 1), m(1, 0), m(1, 1)}, + getControls(controls), target); }, "matrix"_a, "controls"_a, "target"_a, // keep the DD package alive while the returned matrix DD is alive. @@ -527,7 +551,7 @@ Specifically, it dd.def( "controlled_two_qubit_gate", - [](dd::Package& p, const Matrix& m, const qc::Control& control, + [](dd::Package& p, const Matrix& m, const Control& control, const dd::Qubit target0, const dd::Qubit target1) { if (m.ndim() != 2 || m.shape(0) != 4 || m.shape(1) != 4) { throw std::invalid_argument("Matrix must be 4x4."); @@ -537,7 +561,7 @@ Specifically, it {m(1, 0), m(1, 1), m(1, 2), m(1, 3)}, {m(2, 0), m(2, 1), m(2, 2), m(2, 3)}, {m(3, 0), m(3, 1), m(3, 2), m(3, 3)}}, - control, target0, target1); + getControl(control), target0, target1); }, "matrix"_a, "control"_a, "target0"_a, "target1"_a, // keep the DD package alive while the returned matrix DD is alive. @@ -555,7 +579,7 @@ Specifically, it dd.def( "multi_controlled_two_qubit_gate", - [](dd::Package& p, const Matrix& m, const qc::Controls& controls, + [](dd::Package& p, const Matrix& m, const Controls& controls, const dd::Qubit target0, const dd::Qubit target1) { if (m.ndim() != 2 || m.shape(0) != 4 || m.shape(1) != 4) { throw std::invalid_argument("Matrix must be 4x4."); @@ -565,7 +589,7 @@ Specifically, it {m(1, 0), m(1, 1), m(1, 2), m(1, 3)}, {m(2, 0), m(2, 1), m(2, 2), m(2, 3)}, {m(3, 0), m(3, 1), m(3, 2), m(3, 3)}}, - controls, target0, target1); + getControls(controls), target0, target1); }, "matrix"_a, "controls"_a, "target0"_a, "target1"_a, // keep the DD package alive while the returned matrix DD is alive. diff --git a/bindings/ir/operations/register_compound_operation.cpp b/bindings/ir/operations/register_compound_operation.cpp index f7bbc71d3f..3acc21c941 100644 --- a/bindings/ir/operations/register_compound_operation.cpp +++ b/bindings/ir/operations/register_compound_operation.cpp @@ -44,7 +44,11 @@ void registerCompoundOperation(const nb::module_& m) { }; nb::class_( - m, "CompoundOperation", R"pb(Compound quantum operation. + m, "CompoundOperation", + nb::sig( + "class CompoundOperation(mqt.core.ir.operations.Operation, " + "collections.abc.MutableSequence[mqt.core.ir.operations.Operation])"), + R"pb(Compound quantum operation. This class is used to aggregate and group multiple operations into a single object. This is useful for optimizations and for representing complex quantum functionality. @@ -139,7 +143,10 @@ A :class:`CompoundOperation` can contain any number of operations, including oth start += step; } }, - "index"_a, "value"_a, R"pb(Set the operations in the given slice. + nb::sig("def __setitem__(self, index: slice, value: " + "collections.abc.Iterable[mqt.core.ir.operations.Operation]) " + "-> None"), + R"pb(Set the operations in the given slice. Args: index: The slice of operations to set. diff --git a/bindings/ir/operations/register_control.cpp b/bindings/ir/operations/register_control.cpp index fde494bf0c..60bade2438 100644 --- a/bindings/ir/operations/register_control.cpp +++ b/bindings/ir/operations/register_control.cpp @@ -37,7 +37,7 @@ void registerControl(const nb::module_& m) { .value("Neg", qc::Control::Type::Neg); control.def(nb::init(), "qubit"_a, - "type_"_a = qc::Control::Type::Pos); + "type_"_a.sig("...") = qc::Control::Type::Pos); control.def_rw("qubit", &qc::Control::qubit, "The qubit that is the control."); diff --git a/bindings/ir/operations/register_if_else_operation.cpp b/bindings/ir/operations/register_if_else_operation.cpp index 56b8d9db26..34f213a319 100644 --- a/bindings/ir/operations/register_if_else_operation.cpp +++ b/bindings/ir/operations/register_if_else_operation.cpp @@ -66,8 +66,9 @@ Otherwise, the else operation is executed. new (self) qc::IfElseOperation(std::move(thenPtr), std::move(elsePtr), controlReg, expectedVal, kind); }, - "then_operation"_a, "else_operation"_a, "control_register"_a, - "expected_value"_a = 1U, "comparison_kind"_a = qc::ComparisonKind::Eq); + "then_operation"_a, nb::arg("else_operation").none(true), + "control_register"_a, "expected_value"_a = 1U, + "comparison_kind"_a = qc::ComparisonKind::Eq); ifElse.def( "__init__", [](qc::IfElseOperation* self, qc::Operation* thenOp, @@ -80,7 +81,7 @@ Otherwise, the else operation is executed. new (self) qc::IfElseOperation(std::move(thenPtr), std::move(elsePtr), controlBit, expectedVal, kind); }, - "then_operation"_a, "else_operation"_a, "control_bit"_a, + "then_operation"_a, nb::arg("else_operation").none(true), "control_bit"_a, "expected_value"_a = 1U, "comparison_kind"_a = qc::ComparisonKind::Eq); ifElse.def_prop_ro("then_operation", &qc::IfElseOperation::getThenOp, diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index 72314e1b28..941698d938 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -31,13 +31,15 @@ using namespace nb::literals; void registerPermutation(nb::module_& m) { nb::class_( m, "Permutation", + nb::sig("class Permutation(collections.abc.MutableMapping[int, int])"), "A class to represent a permutation of the qubits in a quantum circuit.") .def(nb::init<>()) .def( "__init__", - [](qc::Permutation* self, const nb::dict& p) { + [](qc::Permutation* self, + const nb::typed& p) { qc::Permutation perm; for (const auto& [key, value] : p) { perm[nb::cast(key)] = nb::cast(value); @@ -100,8 +102,8 @@ void registerPermutation(nb::module_& m) { [](qc::Permutation& p, const qc::Qubit q) { p.erase(q); }, "index"_a, R"pb(Delete the value of the permutation at the given index. - Args: - index: The index to delete the value of the permutation at.)pb") +Args: + index: The index to delete the value of the permutation at.)pb") .def("__len__", &qc::Permutation::size, "Return the number of indices in the permutation.") @@ -122,6 +124,7 @@ void registerPermutation(nb::module_& m) { p.end(), "Return an iterable over the items of the permutation."); }, + nb::sig("def items(self) -> collections.abc.ItemsView[int, int]"), nb::keep_alive<0, 1>()) .def(nb::self == nb::self, // NOLINT(misc-redundant-expression) diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index df370ee474..16cd42ee49 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -41,6 +41,31 @@ using namespace nb::literals; using DiffType = std::vector>::difference_type; using SizeType = std::vector>::size_type; +using Control = std::variant; +using Controls = std::set>; + +namespace { + +/// Helper function to convert Control variant to qc::Control +qc::Control getControl(const Control& control) { + if (std::holds_alternative(control)) { + return std::get(control); + } else { + return static_cast(std::get(control)); + } +} + +/// Helper function to convert Controls variant to qc::Controls +qc::Controls getControls(const Controls& controls) { + qc::Controls result; + for (const auto& control : controls) { + result.insert(getControl(control)); + } + return result; +} + +} // namespace + // NOLINTNEXTLINE(misc-use-internal-linkage) void registerQuantumComputation(nb::module_& m) { auto wrap = [](DiffType i, const SizeType size) { @@ -55,6 +80,9 @@ void registerQuantumComputation(nb::module_& m) { auto qc = nb::class_( m, "QuantumComputation", + nb::sig("class " + "QuantumComputation(collections.abc.MutableSequence[mqt.core.ir." + "operations.Operation])"), R"pb(The main class for representing quantum computations within the MQT. Acts as mutable sequence of :class:`~mqt.core.ir.operations.Operation` objects, which represent the individual operations in the quantum computation. @@ -233,7 +261,10 @@ When the quantum computation contains more than one operation, the resulting ope start += step; } }, - "index"_a, "value"_a, R"pb(Set the operations in the given slice. + nb::sig("def __setitem__(self, index: slice, value: " + "collections.abc.Iterable[mqt.core.ir.operations.Operation]) -> " + "None"), + R"pb(Set the operations in the given slice. Args: index: The slice of operations to set. @@ -619,8 +650,12 @@ See Also: Args: q: The target qubit)pb"); - qc.def("cx", &qc::QuantumComputation::cx, "control"_a, "target"_a, - R"pb(Apply a controlled Pauli-X (i.e., CNOT or CX) gate. + qc.def( + "cx", + [](qc::QuantumComputation& circ, const Control& control, + qc::Qubit target) { return circ.cx(getControl(control), target); }, + "control"_a, "target"_a, + R"pb(Apply a controlled Pauli-X (i.e., CNOT or CX) gate. See Also: :meth:`x` @@ -628,8 +663,12 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def("mcx", &qc::QuantumComputation::mcx, "controls"_a, "target"_a, - R"pb(Apply a multi-controlled Pauli-X (i.e., Toffoli or MCX) gate. + qc.def( + "mcx", + [](qc::QuantumComputation& circ, const Controls& controls, + qc::Qubit target) { return circ.mcx(getControls(controls), target); }, + "controls"_a, "target"_a, + R"pb(Apply a multi-controlled Pauli-X (i.e., Toffoli or MCX) gate. See Also: :meth:`x` @@ -648,8 +687,12 @@ See Also: Args: q: The target qubit)pb"); - qc.def("cy", &qc::QuantumComputation::cy, "control"_a, "target"_a, - R"pb(Apply a controlled Pauli-Y gate. + qc.def( + "cy", + [](qc::QuantumComputation& circ, const Control& control, + qc::Qubit target) { return circ.cy(getControl(control), target); }, + "control"_a, "target"_a, + R"pb(Apply a controlled Pauli-Y gate. See Also: :meth:`y` @@ -657,8 +700,12 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def("mcy", &qc::QuantumComputation::mcy, "controls"_a, "target"_a, - R"pb(Apply a multi-controlled Pauli-Y gate. + qc.def( + "mcy", + [](qc::QuantumComputation& circ, const Controls& controls, + qc::Qubit target) { return circ.mcy(getControls(controls), target); }, + "controls"_a, "target"_a, + R"pb(Apply a multi-controlled Pauli-Y gate. See Also: :meth:`y` @@ -677,8 +724,12 @@ See Also: Args: q: The target qubit)pb"); - qc.def("cz", &qc::QuantumComputation::cz, "control"_a, "target"_a, - R"pb(Apply a controlled Pauli-Z gate. + qc.def( + "cz", + [](qc::QuantumComputation& circ, const Control& control, + qc::Qubit target) { return circ.cz(getControl(control), target); }, + "control"_a, "target"_a, + R"pb(Apply a controlled Pauli-Z gate. See Also: :meth:`z` @@ -686,8 +737,12 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def("mcz", &qc::QuantumComputation::mcz, "controls"_a, "target"_a, - R"pb(Apply a multi-controlled Pauli-Z gate. + qc.def( + "mcz", + [](qc::QuantumComputation& circ, const Controls& controls, + qc::Qubit target) { return circ.mcz(getControls(controls), target); }, + "controls"_a, "target"_a, + R"pb(Apply a multi-controlled Pauli-Z gate. See Also: :meth:`z` @@ -706,8 +761,12 @@ See Also: Args: q: The target qubit)pb"); - qc.def("ch", &qc::QuantumComputation::ch, "control"_a, "target"_a, - R"pb(Apply a controlled Hadamard gate. + qc.def( + "ch", + [](qc::QuantumComputation& circ, const Control& control, + qc::Qubit target) { return circ.ch(getControl(control), target); }, + "control"_a, "target"_a, + R"pb(Apply a controlled Hadamard gate. See Also: :meth:`h` @@ -715,8 +774,12 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def("mch", &qc::QuantumComputation::mch, "controls"_a, "target"_a, - R"pb(Apply a multi-controlled Hadamard gate. + qc.def( + "mch", + [](qc::QuantumComputation& circ, const Controls& controls, + qc::Qubit target) { return circ.mch(getControls(controls), target); }, + "controls"_a, "target"_a, + R"pb(Apply a multi-controlled Hadamard gate. See Also: :meth:`h` @@ -735,8 +798,12 @@ See Also: Args: q: The target qubit)pb"); - qc.def("cs", &qc::QuantumComputation::cs, "control"_a, "target"_a, - R"pb(Apply a controlled S gate. + qc.def( + "cs", + [](qc::QuantumComputation& circ, const Control& control, + qc::Qubit target) { return circ.cs(getControl(control), target); }, + "control"_a, "target"_a, + R"pb(Apply a controlled S gate. See Also: :meth:`s` @@ -744,8 +811,12 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def("mcs", &qc::QuantumComputation::mcs, "controls"_a, "target"_a, - R"pb(Apply a multi-controlled S gate. + qc.def( + "mcs", + [](qc::QuantumComputation& circ, const Controls& controls, + qc::Qubit target) { return circ.mcs(getControls(controls), target); }, + "controls"_a, "target"_a, + R"pb(Apply a multi-controlled S gate. See Also: :meth:`s` @@ -764,8 +835,12 @@ See Also: Args: q: The target qubit)pb"); - qc.def("csdg", &qc::QuantumComputation::csdg, "control"_a, "target"_a, - R"pb(Apply a controlled :math:`S^\dagger` gate. + qc.def( + "csdg", + [](qc::QuantumComputation& circ, const Control& control, + qc::Qubit target) { return circ.csdg(getControl(control), target); }, + "control"_a, "target"_a, + R"pb(Apply a controlled :math:`S^\dagger` gate. See Also: :meth:`sdg` @@ -773,8 +848,14 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def("mcsdg", &qc::QuantumComputation::mcsdg, "controls"_a, "target"_a, - R"pb(Apply a multi-controlled :math:`S^\dagger` gate. + qc.def( + "mcsdg", + [](qc::QuantumComputation& circ, const Controls& controls, + qc::Qubit target) { + return circ.mcsdg(getControls(controls), target); + }, + "controls"_a, "target"_a, + R"pb(Apply a multi-controlled :math:`S^\dagger` gate. See Also: :meth:`sdg` @@ -793,8 +874,12 @@ See Also: Args: q: The target qubit)pb"); - qc.def("ct", &qc::QuantumComputation::ct, "control"_a, "target"_a, - R"pb(Apply a controlled T gate. + qc.def( + "ct", + [](qc::QuantumComputation& circ, const Control& control, + qc::Qubit target) { return circ.ct(getControl(control), target); }, + "control"_a, "target"_a, + R"pb(Apply a controlled T gate. See Also: :meth:`t` @@ -802,8 +887,12 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def("mct", &qc::QuantumComputation::mct, "controls"_a, "target"_a, - R"pb(Apply a multi-controlled T gate. + qc.def( + "mct", + [](qc::QuantumComputation& circ, const Controls& controls, + qc::Qubit target) { return circ.mct(getControls(controls), target); }, + "controls"_a, "target"_a, + R"pb(Apply a multi-controlled T gate. See Also: :meth:`t` @@ -822,8 +911,12 @@ See Also: Args: q: The target qubit)pb"); - qc.def("ctdg", &qc::QuantumComputation::ctdg, "control"_a, "target"_a, - R"pb(Apply a controlled :math:`T^\dagger` gate. + qc.def( + "ctdg", + [](qc::QuantumComputation& circ, const Control& control, + qc::Qubit target) { return circ.ctdg(getControl(control), target); }, + "control"_a, "target"_a, + R"pb(Apply a controlled :math:`T^\dagger` gate. See Also: :meth:`tdg` @@ -831,8 +924,14 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def("mctdg", &qc::QuantumComputation::mctdg, "controls"_a, "target"_a, - R"pb(Apply a multi-controlled :math:`T^\dagger` gate. + qc.def( + "mctdg", + [](qc::QuantumComputation& circ, const Controls& controls, + qc::Qubit target) { + return circ.mctdg(getControls(controls), target); + }, + "controls"_a, "target"_a, + R"pb(Apply a multi-controlled :math:`T^\dagger` gate. See Also: :meth:`tdg` @@ -851,8 +950,12 @@ See Also: Args: q: The target qubit)pb"); - qc.def("cv", &qc::QuantumComputation::cv, "control"_a, "target"_a, - R"pb(Apply a controlled V gate. + qc.def( + "cv", + [](qc::QuantumComputation& circ, const Control& control, + qc::Qubit target) { return circ.cv(getControl(control), target); }, + "control"_a, "target"_a, + R"pb(Apply a controlled V gate. See Also: :meth:`v` @@ -860,8 +963,12 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def("mcv", &qc::QuantumComputation::mcv, "controls"_a, "target"_a, - R"pb(Apply a multi-controlled V gate. + qc.def( + "mcv", + [](qc::QuantumComputation& circ, const Controls& controls, + qc::Qubit target) { return circ.mcv(getControls(controls), target); }, + "controls"_a, "target"_a, + R"pb(Apply a multi-controlled V gate. See Also: :meth:`v` @@ -880,8 +987,12 @@ See Also: Args: q: The target qubit)pb"); - qc.def("cvdg", &qc::QuantumComputation::cvdg, "control"_a, "target"_a, - R"pb(Apply a controlled :math:`V^\dagger` gate. + qc.def( + "cvdg", + [](qc::QuantumComputation& circ, const Control& control, + qc::Qubit target) { return circ.cvdg(getControl(control), target); }, + "control"_a, "target"_a, + R"pb(Apply a controlled :math:`V^\dagger` gate. See Also: :meth:`vdg` @@ -889,8 +1000,14 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def("mcvdg", &qc::QuantumComputation::mcvdg, "controls"_a, "target"_a, - R"pb(Apply a multi-controlled :math:`V^\dagger` gate. + qc.def( + "mcvdg", + [](qc::QuantumComputation& circ, const Controls& controls, + qc::Qubit target) { + return circ.mcvdg(getControls(controls), target); + }, + "controls"_a, "target"_a, + R"pb(Apply a multi-controlled :math:`V^\dagger` gate. See Also: :meth:`vdg` @@ -909,8 +1026,12 @@ See Also: Args: q: The target qubit)pb"); - qc.def("csx", &qc::QuantumComputation::csx, "control"_a, "target"_a, - R"pb(Apply a controlled :math:`\sqrt{X}` gate. + qc.def( + "csx", + [](qc::QuantumComputation& circ, const Control& control, + qc::Qubit target) { return circ.csx(getControl(control), target); }, + "control"_a, "target"_a, + R"pb(Apply a controlled :math:`\sqrt{X}` gate. See Also: :meth:`sx` @@ -918,8 +1039,12 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def("mcsx", &qc::QuantumComputation::mcsx, "controls"_a, "target"_a, - R"pb(Apply a multi-controlled :math:`\sqrt{X}` gate. + qc.def( + "mcsx", + [](qc::QuantumComputation& circ, const Controls& controls, + qc::Qubit target) { return circ.mcsx(getControls(controls), target); }, + "controls"_a, "target"_a, + R"pb(Apply a multi-controlled :math:`\sqrt{X}` gate. See Also: :meth:`sx` @@ -938,8 +1063,12 @@ See Also: Args: q: The target qubit)pb"); - qc.def("csxdg", &qc::QuantumComputation::csxdg, "control"_a, "target"_a, - R"pb(Apply a controlled :math:`\sqrt{X}^\dagger` gate. + qc.def( + "csxdg", + [](qc::QuantumComputation& circ, const Control& control, + qc::Qubit target) { return circ.csxdg(getControl(control), target); }, + "control"_a, "target"_a, + R"pb(Apply a controlled :math:`\sqrt{X}^\dagger` gate. See Also: :meth:`sxdg` @@ -947,8 +1076,14 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def("mcsxdg", &qc::QuantumComputation::mcsxdg, "controls"_a, "target"_a, - R"pb(Apply a multi-controlled :math:`\sqrt{X}^\dagger` gate. + qc.def( + "mcsxdg", + [](qc::QuantumComputation& circ, const Controls& controls, + qc::Qubit target) { + return circ.mcsxdg(getControls(controls), target); + }, + "controls"_a, "target"_a, + R"pb(Apply a multi-controlled :math:`\sqrt{X}^\dagger` gate. See Also: :meth:`sxdg` @@ -969,9 +1104,14 @@ See Also: Args: theta: The rotation angle q: The target qubit)pb"); - qc.def("crx", &qc::QuantumComputation::crx, "theta"_a, "control"_a, - "target"_a, - R"pb(Apply a controlled :math:`R_x(\theta)` gate. + qc.def( + "crx", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + const Control& control, qc::Qubit target) { + return circ.crx(theta, getControl(control), target); + }, + "theta"_a, "control"_a, "target"_a, + R"pb(Apply a controlled :math:`R_x(\theta)` gate. See Also: :meth:`rx` @@ -980,9 +1120,14 @@ See Also: theta: The rotation angle control: The control qubit target: The target qubit)pb"); - qc.def("mcrx", &qc::QuantumComputation::mcrx, "theta"_a, "controls"_a, - "target"_a, - R"pb(Apply a multi-controlled :math:`R_x(\theta)` gate. + qc.def( + "mcrx", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + const Controls& controls, qc::Qubit target) { + return circ.mcrx(theta, getControls(controls), target); + }, + "theta"_a, "controls"_a, "target"_a, + R"pb(Apply a multi-controlled :math:`R_x(\theta)` gate. See Also: :meth:`rx` @@ -1004,9 +1149,14 @@ See Also: Args: theta: The rotation angle q: The target qubit)pb"); - qc.def("cry", &qc::QuantumComputation::cry, "theta"_a, "control"_a, - "target"_a, - R"pb(Apply a controlled :math:`R_y(\theta)` gate. + qc.def( + "cry", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + const Control& control, qc::Qubit target) { + return circ.cry(theta, getControl(control), target); + }, + "theta"_a, "control"_a, "target"_a, + R"pb(Apply a controlled :math:`R_y(\theta)` gate. See Also: :meth:`ry` @@ -1015,9 +1165,14 @@ See Also: theta: The rotation angle control: The control qubit target: The target qubit)pb"); - qc.def("mcry", &qc::QuantumComputation::mcry, "theta"_a, "controls"_a, - "target"_a, - R"pb(Apply a multi-controlled :math:`R_y(\theta)` gate. + qc.def( + "mcry", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + const Controls& controls, qc::Qubit target) { + return circ.mcry(theta, getControls(controls), target); + }, + "theta"_a, "controls"_a, "target"_a, + R"pb(Apply a multi-controlled :math:`R_y(\theta)` gate. See Also: :meth:`ry` @@ -1038,9 +1193,14 @@ See Also: Args: theta: The rotation angle q: The target qubit)pb"); - qc.def("crz", &qc::QuantumComputation::crz, "theta"_a, "control"_a, - "target"_a, - R"pb(Apply a controlled :math:`R_z(\theta)` gate. + qc.def( + "crz", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + const Control& control, qc::Qubit target) { + return circ.crz(theta, getControl(control), target); + }, + "theta"_a, "control"_a, "target"_a, + R"pb(Apply a controlled :math:`R_z(\theta)` gate. See Also: :meth:`rz` @@ -1049,9 +1209,14 @@ See Also: theta: The rotation angle control: The control qubit target: The target qubit)pb"); - qc.def("mcrz", &qc::QuantumComputation::mcrz, "theta"_a, "controls"_a, - "target"_a, - R"pb(Apply a multi-controlled :math:`R_z(\theta)` gate. + qc.def( + "mcrz", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + const Controls& controls, qc::Qubit target) { + return circ.mcrx(theta, getControls(controls), target); + }, + "theta"_a, "controls"_a, "target"_a, + R"pb(Apply a multi-controlled :math:`R_z(\theta)` gate. See Also: :meth:`rz` @@ -1072,8 +1237,14 @@ See Also: Args: theta: The rotation angle q: The target qubit)pb"); - qc.def("cp", &qc::QuantumComputation::cp, "theta"_a, "control"_a, "target"_a, - R"pb(Apply a controlled phase gate. + qc.def( + "cp", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + const Control& control, qc::Qubit target) { + return circ.cp(theta, getControl(control), target); + }, + "theta"_a, "control"_a, "target"_a, + R"pb(Apply a controlled phase gate. See Also: :meth:`p` @@ -1082,9 +1253,14 @@ See Also: theta: The rotation angle control: The control qubit target: The target qubit)pb"); - qc.def("mcp", &qc::QuantumComputation::mcp, "theta"_a, "controls"_a, - "target"_a, - R"pb(Apply a multi-controlled phase gate. + qc.def( + "mcp", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + const Controls& controls, qc::Qubit target) { + return circ.mcp(theta, getControls(controls), target); + }, + "theta"_a, "controls"_a, "target"_a, + R"pb(Apply a multi-controlled phase gate. See Also: :meth:`p` @@ -1106,9 +1282,15 @@ See Also: phi: The rotation angle lambda_: The rotation angle q: The target qubit)pb"); - qc.def("cu2", &qc::QuantumComputation::cu2, "phi"_a, "lambda_"_a, "control"_a, - "target"_a, - R"pb(Apply a controlled :math:`U_2(\phi, \lambda)` gate. + qc.def( + "cu2", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& phi, + qc::SymbolOrNumber& lambda_, const Control& control, + qc::Qubit target) { + return circ.cu2(phi, lambda_, getControl(control), target); + }, + "phi"_a, "lambda_"_a, "control"_a, "target"_a, + R"pb(Apply a controlled :math:`U_2(\phi, \lambda)` gate. See Also: :meth:`u2` @@ -1118,9 +1300,15 @@ See Also: lambda_: The rotation angle control: The control qubit target: The target qubit)pb"); - qc.def("mcu2", &qc::QuantumComputation::mcu2, "phi"_a, "lambda_"_a, - "controls"_a, "target"_a, - R"pb(Apply a multi-controlled :math:`U_2(\phi, \lambda)` gate. + qc.def( + "mcu2", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& phi, + qc::SymbolOrNumber& lambda_, const Controls& controls, + qc::Qubit target) { + return circ.mcu2(phi, lambda_, getControls(controls), target); + }, + "phi"_a, "lambda_"_a, "controls"_a, "target"_a, + R"pb(Apply a multi-controlled :math:`U_2(\phi, \lambda)` gate. See Also: :meth:`u2` @@ -1144,9 +1332,14 @@ See Also: theta: The rotation angle phi: The rotation angle q: The target qubit)pb"); - qc.def("cr", &qc::QuantumComputation::cr, "theta"_a, "phi"_a, "control"_a, - "target"_a, - R"pb(Apply a controlled :math:`R(\theta, \phi)` gate. + qc.def( + "cr", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + qc::SymbolOrNumber& phi, const Control& control, qc::Qubit target) { + return circ.cr(theta, phi, getControl(control), target); + }, + "theta"_a, "phi"_a, "control"_a, "target"_a, + R"pb(Apply a controlled :math:`R(\theta, \phi)` gate. See Also: :meth:`r` @@ -1156,9 +1349,14 @@ See Also: phi: The rotation angle control: The control qubit target: The target qubit)pb"); - qc.def("mcr", &qc::QuantumComputation::mcr, "theta"_a, "phi"_a, "controls"_a, - "target"_a, - R"pb(Apply a multi-controlled :math:`R(\theta, \phi)` gate. + qc.def( + "mcr", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + qc::SymbolOrNumber& phi, const Controls& controls, qc::Qubit target) { + return circ.mcr(theta, phi, getControls(controls), target); + }, + "theta"_a, "phi"_a, "controls"_a, "target"_a, + R"pb(Apply a multi-controlled :math:`R(\theta, \phi)` gate. See Also: :meth:`r` @@ -1183,9 +1381,15 @@ See Also: phi: The rotation angle lambda_: The rotation angle q: The target qubit)pb"); - qc.def("cu", &qc::QuantumComputation::cu, "theta"_a, "phi"_a, "lambda_"_a, - "control"_a, "target"_a, - R"pb(Apply a controlled :math:`U(\theta, \phi, \lambda)` gate. + qc.def( + "cu", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + qc::SymbolOrNumber& phi, qc::SymbolOrNumber& lambda, + const Control& control, qc::Qubit target) { + return circ.cu(theta, phi, lambda, getControl(control), target); + }, + "theta"_a, "phi"_a, "lambda_"_a, "control"_a, "target"_a, + R"pb(Apply a controlled :math:`U(\theta, \phi, \lambda)` gate. See Also: :meth:`u` @@ -1196,9 +1400,15 @@ See Also: lambda_: The rotation angle control: The control qubit target: The target qubit)pb"); - qc.def("mcu", &qc::QuantumComputation::mcu, "theta"_a, "phi"_a, "lambda_"_a, - "controls"_a, "target"_a, - R"pb(Apply a multi-controlled :math:`U(\theta, \phi, \lambda)` gate. + qc.def( + "mcu", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + qc::SymbolOrNumber& phi, qc::SymbolOrNumber& lambda, + const Controls& controls, qc::Qubit target) { + return circ.mcu(theta, phi, lambda, getControls(controls), target); + }, + "theta"_a, "phi"_a, "lambda_"_a, "controls"_a, "target"_a, + R"pb(Apply a multi-controlled :math:`U(\theta, \phi, \lambda)` gate. See Also: :meth:`u` @@ -1221,9 +1431,14 @@ See Also: Args: target1: The first target qubit target2: The second target qubit)pb"); - qc.def("cswap", &qc::QuantumComputation::cswap, "control"_a, "target1"_a, - "target2"_a, - R"pb(Apply a controlled SWAP gate. + qc.def( + "cswap", + [](qc::QuantumComputation& circ, const Control& control, + qc::Qubit target1, qc::Qubit target2) { + return circ.cswap(getControl(control), target1, target2); + }, + "control"_a, "target1"_a, "target2"_a, + R"pb(Apply a controlled SWAP gate. See Also: :meth:`swap` @@ -1232,9 +1447,14 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def("mcswap", &qc::QuantumComputation::mcswap, "controls"_a, "target1"_a, - "target2"_a, - R"pb(Apply a multi-controlled SWAP gate. + qc.def( + "mcswap", + [](qc::QuantumComputation& circ, const Controls& controls, + qc::Qubit target1, qc::Qubit target2) { + return circ.mcswap(getControls(controls), target1, target2); + }, + "controls"_a, "target1"_a, "target2"_a, + R"pb(Apply a multi-controlled SWAP gate. See Also: :meth:`swap` @@ -1255,9 +1475,14 @@ See Also: Args: target1: The first target qubit target2: The second target qubit)pb"); - qc.def("cdcx", &qc::QuantumComputation::cdcx, "control"_a, "target1"_a, - "target2"_a, - R"pb(Apply a controlled DCX gate. + qc.def( + "cdcx", + [](qc::QuantumComputation& circ, const Control& control, + qc::Qubit target1, qc::Qubit target2) { + return circ.cdcx(getControl(control), target1, target2); + }, + "control"_a, "target1"_a, "target2"_a, + R"pb(Apply a controlled DCX gate. See Also: :meth:`dcx` @@ -1266,9 +1491,14 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def("mcdcx", &qc::QuantumComputation::mcdcx, "controls"_a, "target1"_a, - "target2"_a, - R"pb(Apply a multi-controlled DCX gate. + qc.def( + "mcdcx", + [](qc::QuantumComputation& circ, const Controls& controls, + qc::Qubit target1, qc::Qubit target2) { + return circ.mcdcx(getControls(controls), target1, target2); + }, + "controls"_a, "target1"_a, "target2"_a, + R"pb(Apply a multi-controlled DCX gate. See Also: :meth:`dcx` @@ -1289,9 +1519,14 @@ See Also: Args: target1: The first target qubit target2: The second target qubit)pb"); - qc.def("cecr", &qc::QuantumComputation::cecr, "control"_a, "target1"_a, - "target2"_a, - R"pb(Apply a controlled ECR gate. + qc.def( + "cecr", + [](qc::QuantumComputation& circ, const Control& control, + qc::Qubit target1, qc::Qubit target2) { + return circ.cecr(getControl(control), target1, target2); + }, + "control"_a, "target1"_a, "target2"_a, + R"pb(Apply a controlled ECR gate. See Also: :meth:`ecr` @@ -1300,9 +1535,14 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def("mcecr", &qc::QuantumComputation::mcecr, "controls"_a, "target1"_a, - "target2"_a, - R"pb(Apply a multi-controlled ECR gate. + qc.def( + "mcecr", + [](qc::QuantumComputation& circ, const Controls& controls, + qc::Qubit target1, qc::Qubit target2) { + return circ.mcecr(getControls(controls), target1, target2); + }, + "controls"_a, "target1"_a, "target2"_a, + R"pb(Apply a multi-controlled ECR gate. See Also: :meth:`ecr` @@ -1323,9 +1563,14 @@ See Also: Args: target1: The first target qubit target2: The second target qubit)pb"); - qc.def("ciswap", &qc::QuantumComputation::ciswap, "control"_a, "target1"_a, - "target2"_a, - R"pb(Apply a controlled :math:`i\text{SWAP}` gate. + qc.def( + "ciswap", + [](qc::QuantumComputation& circ, const Control& control, + qc::Qubit target1, qc::Qubit target2) { + return circ.ciswap(getControl(control), target1, target2); + }, + "control"_a, "target1"_a, "target2"_a, + R"pb(Apply a controlled :math:`i\text{SWAP}` gate. See Also: :meth:`iswap` @@ -1334,9 +1579,14 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def("mciswap", &qc::QuantumComputation::mciswap, "controls"_a, "target1"_a, - "target2"_a, - R"pb(Apply a multi-controlled :math:`i\text{SWAP}` gate. + qc.def( + "mciswap", + [](qc::QuantumComputation& circ, const Controls& controls, + qc::Qubit target1, qc::Qubit target2) { + return circ.mciswap(getControls(controls), target1, target2); + }, + "controls"_a, "target1"_a, "target2"_a, + R"pb(Apply a multi-controlled :math:`i\text{SWAP}` gate. See Also: :meth:`iswap` @@ -1357,9 +1607,14 @@ See Also: Args: target1: The first target qubit target2: The second target qubit)pb"); - qc.def("ciswapdg", &qc::QuantumComputation::ciswapdg, "control"_a, - "target1"_a, "target2"_a, - R"pb(Apply a controlled :math:`i\text{SWAP}^\dagger` gate. + qc.def( + "ciswapdg", + [](qc::QuantumComputation& circ, const Control& control, + qc::Qubit target1, qc::Qubit target2) { + return circ.ciswapdg(getControl(control), target1, target2); + }, + "control"_a, "target1"_a, "target2"_a, + R"pb(Apply a controlled :math:`i\text{SWAP}^\dagger` gate. See Also: :meth:`iswapdg` @@ -1368,9 +1623,14 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def("mciswapdg", &qc::QuantumComputation::mciswapdg, "controls"_a, - "target1"_a, "target2"_a, - R"pb(Apply a multi-controlled :math:`i\text{SWAP}^\dagger` gate. + qc.def( + "mciswapdg", + [](qc::QuantumComputation& circ, const Controls& controls, + qc::Qubit target1, qc::Qubit target2) { + return circ.mciswapdg(getControls(controls), target1, target2); + }, + "controls"_a, "target1"_a, "target2"_a, + R"pb(Apply a multi-controlled :math:`i\text{SWAP}^\dagger` gate. See Also: :meth:`iswapdg` @@ -1391,9 +1651,14 @@ See Also: Args: target1: The first target qubit target2: The second target qubit)pb"); - qc.def("cperes", &qc::QuantumComputation::cperes, "control"_a, "target1"_a, - "target2"_a, - R"pb(Apply a controlled Peres gate. + qc.def( + "cperes", + [](qc::QuantumComputation& circ, const Control& control, + qc::Qubit target1, qc::Qubit target2) { + return circ.cperes(getControl(control), target1, target2); + }, + "control"_a, "target1"_a, "target2"_a, + R"pb(Apply a controlled Peres gate. See Also: :meth:`peres` @@ -1402,9 +1667,14 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def("mcperes", &qc::QuantumComputation::mcperes, "controls"_a, "target1"_a, - "target2"_a, - R"pb(Apply a multi-controlled Peres gate. + qc.def( + "mcperes", + [](qc::QuantumComputation& circ, const Controls& controls, + qc::Qubit target1, qc::Qubit target2) { + return circ.mcperes(getControls(controls), target1, target2); + }, + "controls"_a, "target1"_a, "target2"_a, + R"pb(Apply a multi-controlled Peres gate. See Also: :meth:`peres` @@ -1424,9 +1694,14 @@ See Also: Args: target1: The first target qubit target2: The second target qubit)pb"); - qc.def("cperesdg", &qc::QuantumComputation::cperesdg, "control"_a, - "target1"_a, "target2"_a, - R"pb(Apply a controlled :math:`\text{Peres}^\dagger` gate. + qc.def( + "cperesdg", + [](qc::QuantumComputation& circ, const Control& control, + qc::Qubit target1, qc::Qubit target2) { + return circ.cperesdg(getControl(control), target1, target2); + }, + "control"_a, "target1"_a, "target2"_a, + R"pb(Apply a controlled :math:`\text{Peres}^\dagger` gate. See Also: :meth:`peresdg` @@ -1435,9 +1710,14 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def("mcperesdg", &qc::QuantumComputation::mcperesdg, "controls"_a, - "target1"_a, "target2"_a, - R"pb(Apply a multi-controlled :math:`\text{Peres}^\dagger` gate. + qc.def( + "mcperesdg", + [](qc::QuantumComputation& circ, const Controls& controls, + qc::Qubit target1, qc::Qubit target2) { + return circ.mcperesdg(getControls(controls), target1, target2); + }, + "controls"_a, "target1"_a, "target2"_a, + R"pb(Apply a multi-controlled :math:`\text{Peres}^\dagger` gate. See Also: :meth:`peresdg` @@ -1464,9 +1744,14 @@ See Also: theta: The rotation angle target1: The first target qubit target2: The second target qubit)pb"); - qc.def("crxx", &qc::QuantumComputation::crxx, "theta"_a, "control"_a, - "target1"_a, "target2"_a, - R"pb(Apply a controlled :math:`R_{xx}(\theta)` gate. + qc.def( + "crxx", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + const Control& control, qc::Qubit target1, qc::Qubit target2) { + return circ.crxx(theta, getControl(control), target1, target2); + }, + "theta"_a, "control"_a, "target1"_a, "target2"_a, + R"pb(Apply a controlled :math:`R_{xx}(\theta)` gate. See Also: :meth:`rxx` @@ -1476,9 +1761,14 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def("mcrxx", &qc::QuantumComputation::mcrxx, "theta"_a, "controls"_a, - "target1"_a, "target2"_a, - R"pb(Apply a multi-controlled :math:`R_{xx}(\theta)` gate. + qc.def( + "mcrxx", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + const Controls& controls, qc::Qubit target1, qc::Qubit target2) { + return circ.mcrxx(theta, getControls(controls), target1, target2); + }, + "theta"_a, "controls"_a, "target1"_a, "target2"_a, + R"pb(Apply a multi-controlled :math:`R_{xx}(\theta)` gate. See Also: :meth:`rxx` @@ -1506,9 +1796,14 @@ See Also: theta: The rotation angle target1: The first target qubit target2: The second target qubit)pb"); - qc.def("cryy", &qc::QuantumComputation::cryy, "theta"_a, "control"_a, - "target1"_a, "target2"_a, - R"pb(Apply a controlled :math:`R_{yy}(\theta)` gate. + qc.def( + "cryy", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + const Control& control, qc::Qubit target1, qc::Qubit target2) { + return circ.cryy(theta, getControl(control), target1, target2); + }, + "theta"_a, "control"_a, "target1"_a, "target2"_a, + R"pb(Apply a controlled :math:`R_{yy}(\theta)` gate. See Also: :meth:`ryy` @@ -1518,9 +1813,14 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def("mcryy", &qc::QuantumComputation::mcryy, "theta"_a, "controls"_a, - "target1"_a, "target2"_a, - R"pb(Apply a multi-controlled :math:`R_{yy}(\theta)` gate. + qc.def( + "mcryy", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + const Controls& controls, qc::Qubit target1, qc::Qubit target2) { + return circ.mcryy(theta, getControls(controls), target1, target2); + }, + "theta"_a, "controls"_a, "target1"_a, "target2"_a, + R"pb(Apply a multi-controlled :math:`R_{yy}(\theta)` gate. See Also: :meth:`ryy` @@ -1548,9 +1848,14 @@ See Also: theta: The rotation angle target1: The first target qubit target2: The second target qubit)pb"); - qc.def("crzx", &qc::QuantumComputation::crzx, "theta"_a, "control"_a, - "target1"_a, "target2"_a, - R"pb(Apply a controlled :math:`R_{zx}(\theta)` gate. + qc.def( + "crzx", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + const Control& control, qc::Qubit target1, qc::Qubit target2) { + return circ.crzx(theta, getControl(control), target1, target2); + }, + "theta"_a, "control"_a, "target1"_a, "target2"_a, + R"pb(Apply a controlled :math:`R_{zx}(\theta)` gate. See Also: :meth:`rzx` @@ -1560,9 +1865,14 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def("mcrzx", &qc::QuantumComputation::mcrzx, "theta"_a, "controls"_a, - "target1"_a, "target2"_a, - R"pb(Apply a multi-controlled :math:`R_{zx}(\theta)` gate. + qc.def( + "mcrzx", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + const Controls& controls, qc::Qubit target1, qc::Qubit target2) { + return circ.mcrzx(theta, getControls(controls), target1, target2); + }, + "theta"_a, "controls"_a, "target1"_a, "target2"_a, + R"pb(Apply a multi-controlled :math:`R_{zx}(\theta)` gate. See Also: :meth:`rzx` @@ -1590,9 +1900,14 @@ See Also: theta: The rotation angle target1: The first target qubit target2: The second target qubit)pb"); - qc.def("crzz", &qc::QuantumComputation::crzz, "theta"_a, "control"_a, - "target1"_a, "target2"_a, - R"pb(Apply a controlled :math:`R_{zz}(\theta)` gate. + qc.def( + "crzz", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + const Control& control, qc::Qubit target1, qc::Qubit target2) { + return circ.crzz(theta, getControl(control), target1, target2); + }, + "theta"_a, "control"_a, "target1"_a, "target2"_a, + R"pb(Apply a controlled :math:`R_{zz}(\theta)` gate. See Also: :meth:`rzz` @@ -1602,9 +1917,14 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def("mcrzz", &qc::QuantumComputation::mcrzz, "theta"_a, "controls"_a, - "target1"_a, "target2"_a, - R"pb(Apply a multi-controlled :math:`R_{zz}(\theta)` gate. + qc.def( + "mcrzz", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + const Controls& controls, qc::Qubit target1, qc::Qubit target2) { + return circ.mcrzz(theta, getControls(controls), target1, target2); + }, + "theta"_a, "controls"_a, "target1"_a, "target2"_a, + R"pb(Apply a multi-controlled :math:`R_{zz}(\theta)` gate. See Also: :meth:`rzz` @@ -1633,9 +1953,16 @@ See Also: beta: The rotation angle target1: The first target qubit target2: The second target qubit)pb"); - qc.def("cxx_minus_yy", &qc::QuantumComputation::cxx_minus_yy, "theta"_a, - "beta"_a, "control"_a, "target1"_a, "target2"_a, - R"pb(Apply a controlled :math:`R_{XX - YY}(\theta, \beta)` gate. + qc.def( + "cxx_minus_yy", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + qc::SymbolOrNumber& beta, const Control& control, qc::Qubit target1, + qc::Qubit target2) { + return circ.cxx_minus_yy(theta, beta, getControl(control), target1, + target2); + }, + "theta"_a, "beta"_a, "control"_a, "target1"_a, "target2"_a, + R"pb(Apply a controlled :math:`R_{XX - YY}(\theta, \beta)` gate. See Also: :meth:`xx_minus_yy` @@ -1646,9 +1973,16 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def("mcxx_minus_yy", &qc::QuantumComputation::mcxx_minus_yy, "theta"_a, - "beta"_a, "controls"_a, "target1"_a, "target2"_a, - R"pb(Apply a multi-controlled :math:`R_{XX - YY}(\theta, \beta)` gate. + qc.def( + "mcxx_minus_yy", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + qc::SymbolOrNumber& beta, const Controls& controls, qc::Qubit target1, + qc::Qubit target2) { + return circ.mcxx_minus_yy(theta, beta, getControls(controls), target1, + target2); + }, + "theta"_a, "beta"_a, "controls"_a, "target1"_a, "target2"_a, + R"pb(Apply a multi-controlled :math:`R_{XX - YY}(\theta, \beta)` gate. See Also: :meth:`xx_minus_yy` @@ -1678,9 +2012,16 @@ See Also: beta: The rotation angle target1: The first target qubit target2: The second target qubit)pb"); - qc.def("cxx_plus_yy", &qc::QuantumComputation::cxx_plus_yy, "theta"_a, - "beta"_a, "control"_a, "target1"_a, "target2"_a, - R"pb(Apply a controlled :math:`R_{XX + YY}(\theta, \beta)` gate. + qc.def( + "cxx_plus_yy", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + qc::SymbolOrNumber& beta, const Control& control, qc::Qubit target1, + qc::Qubit target2) { + return circ.cxx_plus_yy(theta, beta, getControl(control), target1, + target2); + }, + "theta"_a, "beta"_a, "control"_a, "target1"_a, "target2"_a, + R"pb(Apply a controlled :math:`R_{XX + YY}(\theta, \beta)` gate. See Also: :meth:`xx_plus_yy` @@ -1691,9 +2032,16 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def("mcxx_plus_yy", &qc::QuantumComputation::mcxx_plus_yy, "theta"_a, - "beta"_a, "controls"_a, "target1"_a, "target2"_a, - R"pb(Apply a multi-controlled :math:`R_{XX + YY}(\theta, \beta)` gate. + qc.def( + "mcxx_plus_yy", + [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, + qc::SymbolOrNumber& beta, const Controls& controls, qc::Qubit target1, + qc::Qubit target2) { + return circ.mcxx_plus_yy(theta, beta, getControls(controls), target1, + target2); + }, + "theta"_a, "beta"_a, "controls"_a, "target1"_a, "target2"_a, + R"pb(Apply a multi-controlled :math:`R_{XX + YY}(\theta, \beta)` gate. See Also: :meth:`xx_plus_yy` diff --git a/pyproject.toml b/pyproject.toml index b8181e94ba..866f196565 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -230,7 +230,7 @@ known-first-party = ["mqt.core"] "test/python/**" = ["T20", "INP001"] "docs/**" = ["T20", "INP001"] "noxfile.py" = ["T20", "TID251"] -"*.pyi" = ["D418", "DOC202", "E501", "F821", "PYI011", "PYI021", "RUF012"] +"*.pyi" = ["D418", "DOC202", "E501", "PYI011", "PYI021"] "*.ipynb" = [ "D", # pydocstyle "E402", # Allow imports to appear anywhere in Jupyter notebooks diff --git a/python/mqt/core/dd.pyi b/python/mqt/core/dd.pyi index 897ad6e66d..4a19494382 100644 --- a/python/mqt/core/dd.pyi +++ b/python/mqt/core/dd.pyi @@ -449,7 +449,7 @@ class DDPackage: def controlled_single_qubit_gate( self, matrix: Annotated[NDArray[np.complex128], {"shape": (None, None)}], - control: mqt.core.ir.operations.Control, + control: mqt.core.ir.operations.Control | int, target: int, ) -> MatrixDD: r"""Create the DD for a controlled single-qubit gate. @@ -466,7 +466,7 @@ class DDPackage: def multi_controlled_single_qubit_gate( self, matrix: Annotated[NDArray[np.complex128], {"shape": (None, None)}], - controls: AbstractSet[mqt.core.ir.operations.Control], + controls: AbstractSet[mqt.core.ir.operations.Control | int], target: int, ) -> MatrixDD: r"""Create the DD for a multi-controlled single-qubit gate. @@ -497,7 +497,7 @@ class DDPackage: def controlled_two_qubit_gate( self, matrix: Annotated[NDArray[np.complex128], {"shape": (None, None)}], - control: mqt.core.ir.operations.Control, + control: mqt.core.ir.operations.Control | int, target0: int, target1: int, ) -> MatrixDD: @@ -516,7 +516,7 @@ class DDPackage: def multi_controlled_two_qubit_gate( self, matrix: Annotated[NDArray[np.complex128], {"shape": (None, None)}], - controls: AbstractSet[mqt.core.ir.operations.Control], + controls: AbstractSet[mqt.core.ir.operations.Control | int], target0: int, target1: int, ) -> MatrixDD: diff --git a/python/mqt/core/ir/__init__.pyi b/python/mqt/core/ir/__init__.pyi index 92dc049f74..1d307eb120 100644 --- a/python/mqt/core/ir/__init__.pyi +++ b/python/mqt/core/ir/__init__.pyi @@ -6,7 +6,7 @@ # # Licensed under the MIT License -from collections.abc import Iterator, Mapping, Sequence +from collections.abc import ItemsView, Iterable, Iterator, Mapping, MutableMapping, MutableSequence, Sequence from collections.abc import Set as AbstractSet from typing import overload @@ -14,13 +14,13 @@ from . import operations as operations from . import registers as registers from . import symbolic as symbolic -class Permutation: +class Permutation(MutableMapping[int, int]): """A class to represent a permutation of the qubits in a quantum circuit.""" @overload def __init__(self) -> None: ... @overload - def __init__(self, perm: dict) -> None: + def __init__(self, perm: dict[int, int]) -> None: """Create a permutation from a dictionary.""" @overload @@ -70,19 +70,19 @@ class Permutation: """Delete the value of the permutation at the given index. Args: - index: The index to delete the value of the permutation at. + index: The index to delete the value of the permutation at. """ def __len__(self) -> int: """Return the number of indices in the permutation.""" def __iter__(self) -> Iterator[int]: ... - def items(self) -> Iterator[tuple[int, int]]: ... + def items(self) -> ItemsView[int, int]: ... def __eq__(self, arg: object, /) -> bool: ... def __ne__(self, arg: object, /) -> bool: ... def __hash__(self) -> int: ... -class QuantumComputation: +class QuantumComputation(MutableSequence[operations.Operation]): """The main class for representing quantum computations within the MQT. Acts as mutable sequence of :class:`~mqt.core.ir.operations.Operation` objects, which represent the individual operations in the quantum computation. @@ -234,7 +234,7 @@ class QuantumComputation: """ @overload - def __setitem__(self, index: slice, value: Sequence[operations.Operation]) -> None: + def __setitem__(self, index: slice, value: Iterable[operations.Operation]) -> None: """Set the operations in the given slice. Args: @@ -563,7 +563,7 @@ class QuantumComputation: q: The target qubit """ - def cx(self, control: operations.Control, target: int) -> None: + def cx(self, control: operations.Control | int, target: int) -> None: """Apply a controlled Pauli-X (i.e., CNOT or CX) gate. See Also: @@ -574,7 +574,7 @@ class QuantumComputation: target: The target qubit """ - def mcx(self, controls: AbstractSet[operations.Control], target: int) -> None: + def mcx(self, controls: AbstractSet[operations.Control | int], target: int) -> None: """Apply a multi-controlled Pauli-X (i.e., Toffoli or MCX) gate. See Also: @@ -595,7 +595,7 @@ class QuantumComputation: q: The target qubit """ - def cy(self, control: operations.Control, target: int) -> None: + def cy(self, control: operations.Control | int, target: int) -> None: """Apply a controlled Pauli-Y gate. See Also: @@ -606,7 +606,7 @@ class QuantumComputation: target: The target qubit """ - def mcy(self, controls: AbstractSet[operations.Control], target: int) -> None: + def mcy(self, controls: AbstractSet[operations.Control | int], target: int) -> None: """Apply a multi-controlled Pauli-Y gate. See Also: @@ -627,7 +627,7 @@ class QuantumComputation: q: The target qubit """ - def cz(self, control: operations.Control, target: int) -> None: + def cz(self, control: operations.Control | int, target: int) -> None: """Apply a controlled Pauli-Z gate. See Also: @@ -638,7 +638,7 @@ class QuantumComputation: target: The target qubit """ - def mcz(self, controls: AbstractSet[operations.Control], target: int) -> None: + def mcz(self, controls: AbstractSet[operations.Control | int], target: int) -> None: """Apply a multi-controlled Pauli-Z gate. See Also: @@ -659,7 +659,7 @@ class QuantumComputation: q: The target qubit """ - def ch(self, control: operations.Control, target: int) -> None: + def ch(self, control: operations.Control | int, target: int) -> None: """Apply a controlled Hadamard gate. See Also: @@ -670,7 +670,7 @@ class QuantumComputation: target: The target qubit """ - def mch(self, controls: AbstractSet[operations.Control], target: int) -> None: + def mch(self, controls: AbstractSet[operations.Control | int], target: int) -> None: """Apply a multi-controlled Hadamard gate. See Also: @@ -691,7 +691,7 @@ class QuantumComputation: q: The target qubit """ - def cs(self, control: operations.Control, target: int) -> None: + def cs(self, control: operations.Control | int, target: int) -> None: """Apply a controlled S gate. See Also: @@ -702,7 +702,7 @@ class QuantumComputation: target: The target qubit """ - def mcs(self, controls: AbstractSet[operations.Control], target: int) -> None: + def mcs(self, controls: AbstractSet[operations.Control | int], target: int) -> None: """Apply a multi-controlled S gate. See Also: @@ -723,7 +723,7 @@ class QuantumComputation: q: The target qubit """ - def csdg(self, control: operations.Control, target: int) -> None: + def csdg(self, control: operations.Control | int, target: int) -> None: r"""Apply a controlled :math:`S^\dagger` gate. See Also: @@ -734,7 +734,7 @@ class QuantumComputation: target: The target qubit """ - def mcsdg(self, controls: AbstractSet[operations.Control], target: int) -> None: + def mcsdg(self, controls: AbstractSet[operations.Control | int], target: int) -> None: r"""Apply a multi-controlled :math:`S^\dagger` gate. See Also: @@ -755,7 +755,7 @@ class QuantumComputation: q: The target qubit """ - def ct(self, control: operations.Control, target: int) -> None: + def ct(self, control: operations.Control | int, target: int) -> None: """Apply a controlled T gate. See Also: @@ -766,7 +766,7 @@ class QuantumComputation: target: The target qubit """ - def mct(self, controls: AbstractSet[operations.Control], target: int) -> None: + def mct(self, controls: AbstractSet[operations.Control | int], target: int) -> None: """Apply a multi-controlled T gate. See Also: @@ -787,7 +787,7 @@ class QuantumComputation: q: The target qubit """ - def ctdg(self, control: operations.Control, target: int) -> None: + def ctdg(self, control: operations.Control | int, target: int) -> None: r"""Apply a controlled :math:`T^\dagger` gate. See Also: @@ -798,7 +798,7 @@ class QuantumComputation: target: The target qubit """ - def mctdg(self, controls: AbstractSet[operations.Control], target: int) -> None: + def mctdg(self, controls: AbstractSet[operations.Control | int], target: int) -> None: r"""Apply a multi-controlled :math:`T^\dagger` gate. See Also: @@ -819,7 +819,7 @@ class QuantumComputation: q: The target qubit """ - def cv(self, control: operations.Control, target: int) -> None: + def cv(self, control: operations.Control | int, target: int) -> None: """Apply a controlled V gate. See Also: @@ -830,7 +830,7 @@ class QuantumComputation: target: The target qubit """ - def mcv(self, controls: AbstractSet[operations.Control], target: int) -> None: + def mcv(self, controls: AbstractSet[operations.Control | int], target: int) -> None: """Apply a multi-controlled V gate. See Also: @@ -851,7 +851,7 @@ class QuantumComputation: q: The target qubit """ - def cvdg(self, control: operations.Control, target: int) -> None: + def cvdg(self, control: operations.Control | int, target: int) -> None: r"""Apply a controlled :math:`V^\dagger` gate. See Also: @@ -862,7 +862,7 @@ class QuantumComputation: target: The target qubit """ - def mcvdg(self, controls: AbstractSet[operations.Control], target: int) -> None: + def mcvdg(self, controls: AbstractSet[operations.Control | int], target: int) -> None: r"""Apply a multi-controlled :math:`V^\dagger` gate. See Also: @@ -883,7 +883,7 @@ class QuantumComputation: q: The target qubit """ - def csx(self, control: operations.Control, target: int) -> None: + def csx(self, control: operations.Control | int, target: int) -> None: r"""Apply a controlled :math:`\sqrt{X}` gate. See Also: @@ -894,7 +894,7 @@ class QuantumComputation: target: The target qubit """ - def mcsx(self, controls: AbstractSet[operations.Control], target: int) -> None: + def mcsx(self, controls: AbstractSet[operations.Control | int], target: int) -> None: r"""Apply a multi-controlled :math:`\sqrt{X}` gate. See Also: @@ -915,7 +915,7 @@ class QuantumComputation: q: The target qubit """ - def csxdg(self, control: operations.Control, target: int) -> None: + def csxdg(self, control: operations.Control | int, target: int) -> None: r"""Apply a controlled :math:`\sqrt{X}^\dagger` gate. See Also: @@ -926,7 +926,7 @@ class QuantumComputation: target: The target qubit """ - def mcsxdg(self, controls: AbstractSet[operations.Control], target: int) -> None: + def mcsxdg(self, controls: AbstractSet[operations.Control | int], target: int) -> None: r"""Apply a multi-controlled :math:`\sqrt{X}^\dagger` gate. See Also: @@ -949,7 +949,7 @@ class QuantumComputation: q: The target qubit """ - def crx(self, theta: symbolic.Expression | float, control: operations.Control, target: int) -> None: + def crx(self, theta: symbolic.Expression | float, control: operations.Control | int, target: int) -> None: r"""Apply a controlled :math:`R_x(\theta)` gate. See Also: @@ -961,7 +961,9 @@ class QuantumComputation: target: The target qubit """ - def mcrx(self, theta: symbolic.Expression | float, controls: AbstractSet[operations.Control], target: int) -> None: + def mcrx( + self, theta: symbolic.Expression | float, controls: AbstractSet[operations.Control | int], target: int + ) -> None: r"""Apply a multi-controlled :math:`R_x(\theta)` gate. See Also: @@ -985,7 +987,7 @@ class QuantumComputation: q: The target qubit """ - def cry(self, theta: symbolic.Expression | float, control: operations.Control, target: int) -> None: + def cry(self, theta: symbolic.Expression | float, control: operations.Control | int, target: int) -> None: r"""Apply a controlled :math:`R_y(\theta)` gate. See Also: @@ -997,7 +999,9 @@ class QuantumComputation: target: The target qubit """ - def mcry(self, theta: symbolic.Expression | float, controls: AbstractSet[operations.Control], target: int) -> None: + def mcry( + self, theta: symbolic.Expression | float, controls: AbstractSet[operations.Control | int], target: int + ) -> None: r"""Apply a multi-controlled :math:`R_y(\theta)` gate. See Also: @@ -1020,7 +1024,7 @@ class QuantumComputation: q: The target qubit """ - def crz(self, theta: symbolic.Expression | float, control: operations.Control, target: int) -> None: + def crz(self, theta: symbolic.Expression | float, control: operations.Control | int, target: int) -> None: r"""Apply a controlled :math:`R_z(\theta)` gate. See Also: @@ -1032,7 +1036,9 @@ class QuantumComputation: target: The target qubit """ - def mcrz(self, theta: symbolic.Expression | float, controls: AbstractSet[operations.Control], target: int) -> None: + def mcrz( + self, theta: symbolic.Expression | float, controls: AbstractSet[operations.Control | int], target: int + ) -> None: r"""Apply a multi-controlled :math:`R_z(\theta)` gate. See Also: @@ -1055,7 +1061,7 @@ class QuantumComputation: q: The target qubit """ - def cp(self, theta: symbolic.Expression | float, control: operations.Control, target: int) -> None: + def cp(self, theta: symbolic.Expression | float, control: operations.Control | int, target: int) -> None: """Apply a controlled phase gate. See Also: @@ -1067,7 +1073,9 @@ class QuantumComputation: target: The target qubit """ - def mcp(self, theta: symbolic.Expression | float, controls: AbstractSet[operations.Control], target: int) -> None: + def mcp( + self, theta: symbolic.Expression | float, controls: AbstractSet[operations.Control | int], target: int + ) -> None: """Apply a multi-controlled phase gate. See Also: @@ -1095,7 +1103,7 @@ class QuantumComputation: self, phi: symbolic.Expression | float, lambda_: symbolic.Expression | float, - control: operations.Control, + control: operations.Control | int, target: int, ) -> None: r"""Apply a controlled :math:`U_2(\phi, \lambda)` gate. @@ -1114,7 +1122,7 @@ class QuantumComputation: self, phi: symbolic.Expression | float, lambda_: symbolic.Expression | float, - controls: AbstractSet[operations.Control], + controls: AbstractSet[operations.Control | int], target: int, ) -> None: r"""Apply a multi-controlled :math:`U_2(\phi, \lambda)` gate. @@ -1146,7 +1154,7 @@ class QuantumComputation: self, theta: symbolic.Expression | float, phi: symbolic.Expression | float, - control: operations.Control, + control: operations.Control | int, target: int, ) -> None: r"""Apply a controlled :math:`R(\theta, \phi)` gate. @@ -1165,7 +1173,7 @@ class QuantumComputation: self, theta: symbolic.Expression | float, phi: symbolic.Expression | float, - controls: AbstractSet[operations.Control], + controls: AbstractSet[operations.Control | int], target: int, ) -> None: r"""Apply a multi-controlled :math:`R(\theta, \phi)` gate. @@ -1204,7 +1212,7 @@ class QuantumComputation: theta: symbolic.Expression | float, phi: symbolic.Expression | float, lambda_: symbolic.Expression | float, - control: operations.Control, + control: operations.Control | int, target: int, ) -> None: r"""Apply a controlled :math:`U(\theta, \phi, \lambda)` gate. @@ -1225,7 +1233,7 @@ class QuantumComputation: theta: symbolic.Expression | float, phi: symbolic.Expression | float, lambda_: symbolic.Expression | float, - controls: AbstractSet[operations.Control], + controls: AbstractSet[operations.Control | int], target: int, ) -> None: r"""Apply a multi-controlled :math:`U(\theta, \phi, \lambda)` gate. @@ -1252,7 +1260,7 @@ class QuantumComputation: target2: The second target qubit """ - def cswap(self, control: operations.Control, target1: int, target2: int) -> None: + def cswap(self, control: operations.Control | int, target1: int, target2: int) -> None: """Apply a controlled SWAP gate. See Also: @@ -1264,7 +1272,7 @@ class QuantumComputation: target2: The second target qubit """ - def mcswap(self, controls: AbstractSet[operations.Control], target1: int, target2: int) -> None: + def mcswap(self, controls: AbstractSet[operations.Control | int], target1: int, target2: int) -> None: """Apply a multi-controlled SWAP gate. See Also: @@ -1287,7 +1295,7 @@ class QuantumComputation: target2: The second target qubit """ - def cdcx(self, control: operations.Control, target1: int, target2: int) -> None: + def cdcx(self, control: operations.Control | int, target1: int, target2: int) -> None: """Apply a controlled DCX gate. See Also: @@ -1299,7 +1307,7 @@ class QuantumComputation: target2: The second target qubit """ - def mcdcx(self, controls: AbstractSet[operations.Control], target1: int, target2: int) -> None: + def mcdcx(self, controls: AbstractSet[operations.Control | int], target1: int, target2: int) -> None: """Apply a multi-controlled DCX gate. See Also: @@ -1322,7 +1330,7 @@ class QuantumComputation: target2: The second target qubit """ - def cecr(self, control: operations.Control, target1: int, target2: int) -> None: + def cecr(self, control: operations.Control | int, target1: int, target2: int) -> None: """Apply a controlled ECR gate. See Also: @@ -1334,7 +1342,7 @@ class QuantumComputation: target2: The second target qubit """ - def mcecr(self, controls: AbstractSet[operations.Control], target1: int, target2: int) -> None: + def mcecr(self, controls: AbstractSet[operations.Control | int], target1: int, target2: int) -> None: """Apply a multi-controlled ECR gate. See Also: @@ -1357,7 +1365,7 @@ class QuantumComputation: target2: The second target qubit """ - def ciswap(self, control: operations.Control, target1: int, target2: int) -> None: + def ciswap(self, control: operations.Control | int, target1: int, target2: int) -> None: r"""Apply a controlled :math:`i\text{SWAP}` gate. See Also: @@ -1369,7 +1377,7 @@ class QuantumComputation: target2: The second target qubit """ - def mciswap(self, controls: AbstractSet[operations.Control], target1: int, target2: int) -> None: + def mciswap(self, controls: AbstractSet[operations.Control | int], target1: int, target2: int) -> None: r"""Apply a multi-controlled :math:`i\text{SWAP}` gate. See Also: @@ -1392,7 +1400,7 @@ class QuantumComputation: target2: The second target qubit """ - def ciswapdg(self, control: operations.Control, target1: int, target2: int) -> None: + def ciswapdg(self, control: operations.Control | int, target1: int, target2: int) -> None: r"""Apply a controlled :math:`i\text{SWAP}^\dagger` gate. See Also: @@ -1404,7 +1412,7 @@ class QuantumComputation: target2: The second target qubit """ - def mciswapdg(self, controls: AbstractSet[operations.Control], target1: int, target2: int) -> None: + def mciswapdg(self, controls: AbstractSet[operations.Control | int], target1: int, target2: int) -> None: r"""Apply a multi-controlled :math:`i\text{SWAP}^\dagger` gate. See Also: @@ -1427,7 +1435,7 @@ class QuantumComputation: target2: The second target qubit """ - def cperes(self, control: operations.Control, target1: int, target2: int) -> None: + def cperes(self, control: operations.Control | int, target1: int, target2: int) -> None: """Apply a controlled Peres gate. See Also: @@ -1439,7 +1447,7 @@ class QuantumComputation: target2: The second target qubit """ - def mcperes(self, controls: AbstractSet[operations.Control], target1: int, target2: int) -> None: + def mcperes(self, controls: AbstractSet[operations.Control | int], target1: int, target2: int) -> None: """Apply a multi-controlled Peres gate. See Also: @@ -1462,7 +1470,7 @@ class QuantumComputation: target2: The second target qubit """ - def cperesdg(self, control: operations.Control, target1: int, target2: int) -> None: + def cperesdg(self, control: operations.Control | int, target1: int, target2: int) -> None: r"""Apply a controlled :math:`\text{Peres}^\dagger` gate. See Also: @@ -1474,7 +1482,7 @@ class QuantumComputation: target2: The second target qubit """ - def mcperesdg(self, controls: AbstractSet[operations.Control], target1: int, target2: int) -> None: + def mcperesdg(self, controls: AbstractSet[operations.Control | int], target1: int, target2: int) -> None: r"""Apply a multi-controlled :math:`\text{Peres}^\dagger` gate. See Also: @@ -1502,7 +1510,9 @@ class QuantumComputation: target2: The second target qubit """ - def crxx(self, theta: symbolic.Expression | float, control: operations.Control, target1: int, target2: int) -> None: + def crxx( + self, theta: symbolic.Expression | float, control: operations.Control | int, target1: int, target2: int + ) -> None: r"""Apply a controlled :math:`R_{xx}(\theta)` gate. See Also: @@ -1516,7 +1526,11 @@ class QuantumComputation: """ def mcrxx( - self, theta: symbolic.Expression | float, controls: AbstractSet[operations.Control], target1: int, target2: int + self, + theta: symbolic.Expression | float, + controls: AbstractSet[operations.Control | int], + target1: int, + target2: int, ) -> None: r"""Apply a multi-controlled :math:`R_{xx}(\theta)` gate. @@ -1546,7 +1560,9 @@ class QuantumComputation: target2: The second target qubit """ - def cryy(self, theta: symbolic.Expression | float, control: operations.Control, target1: int, target2: int) -> None: + def cryy( + self, theta: symbolic.Expression | float, control: operations.Control | int, target1: int, target2: int + ) -> None: r"""Apply a controlled :math:`R_{yy}(\theta)` gate. See Also: @@ -1560,7 +1576,11 @@ class QuantumComputation: """ def mcryy( - self, theta: symbolic.Expression | float, controls: AbstractSet[operations.Control], target1: int, target2: int + self, + theta: symbolic.Expression | float, + controls: AbstractSet[operations.Control | int], + target1: int, + target2: int, ) -> None: r"""Apply a multi-controlled :math:`R_{yy}(\theta)` gate. @@ -1590,7 +1610,9 @@ class QuantumComputation: target2: The second target qubit """ - def crzx(self, theta: symbolic.Expression | float, control: operations.Control, target1: int, target2: int) -> None: + def crzx( + self, theta: symbolic.Expression | float, control: operations.Control | int, target1: int, target2: int + ) -> None: r"""Apply a controlled :math:`R_{zx}(\theta)` gate. See Also: @@ -1604,7 +1626,11 @@ class QuantumComputation: """ def mcrzx( - self, theta: symbolic.Expression | float, controls: AbstractSet[operations.Control], target1: int, target2: int + self, + theta: symbolic.Expression | float, + controls: AbstractSet[operations.Control | int], + target1: int, + target2: int, ) -> None: r"""Apply a multi-controlled :math:`R_{zx}(\theta)` gate. @@ -1634,7 +1660,9 @@ class QuantumComputation: target2: The second target qubit """ - def crzz(self, theta: symbolic.Expression | float, control: operations.Control, target1: int, target2: int) -> None: + def crzz( + self, theta: symbolic.Expression | float, control: operations.Control | int, target1: int, target2: int + ) -> None: r"""Apply a controlled :math:`R_{zz}(\theta)` gate. See Also: @@ -1648,7 +1676,11 @@ class QuantumComputation: """ def mcrzz( - self, theta: symbolic.Expression | float, controls: AbstractSet[operations.Control], target1: int, target2: int + self, + theta: symbolic.Expression | float, + controls: AbstractSet[operations.Control | int], + target1: int, + target2: int, ) -> None: r"""Apply a multi-controlled :math:`R_{zz}(\theta)` gate. @@ -1685,7 +1717,7 @@ class QuantumComputation: self, theta: symbolic.Expression | float, beta: symbolic.Expression | float, - control: operations.Control, + control: operations.Control | int, target1: int, target2: int, ) -> None: @@ -1706,7 +1738,7 @@ class QuantumComputation: self, theta: symbolic.Expression | float, beta: symbolic.Expression | float, - controls: AbstractSet[operations.Control], + controls: AbstractSet[operations.Control | int], target1: int, target2: int, ) -> None: @@ -1746,7 +1778,7 @@ class QuantumComputation: self, theta: symbolic.Expression | float, beta: symbolic.Expression | float, - control: operations.Control, + control: operations.Control | int, target1: int, target2: int, ) -> None: @@ -1767,7 +1799,7 @@ class QuantumComputation: self, theta: symbolic.Expression | float, beta: symbolic.Expression | float, - controls: AbstractSet[operations.Control], + controls: AbstractSet[operations.Control | int], target1: int, target2: int, ) -> None: diff --git a/python/mqt/core/ir/operations.pyi b/python/mqt/core/ir/operations.pyi index 3a9b5d6798..b403b1fcd0 100644 --- a/python/mqt/core/ir/operations.pyi +++ b/python/mqt/core/ir/operations.pyi @@ -7,7 +7,7 @@ # Licensed under the MIT License import enum -from collections.abc import Mapping, Sequence +from collections.abc import Iterable, Mapping, MutableSequence, Sequence from collections.abc import Set as AbstractSet from typing import overload @@ -429,7 +429,7 @@ class Control: type_: The type of the control. """ - def __init__(self, qubit: int, type_: Control.Type = Control.Type.Pos) -> None: ... + def __init__(self, qubit: int, type_: Control.Type = ...) -> None: ... class Type(enum.Enum): """Enumeration of control types.""" @@ -653,7 +653,7 @@ class StandardOperation(Operation): self, controls: AbstractSet[Control], target0: int, target1: int, op_type: OpType, params: Sequence[float] = [] ) -> None: ... -class CompoundOperation(Operation): +class CompoundOperation(Operation, MutableSequence[Operation]): """Compound quantum operation. This class is used to aggregate and group multiple operations into a single object. @@ -709,7 +709,7 @@ class CompoundOperation(Operation): """ @overload - def __setitem__(self, index: slice, value: Sequence[Operation]) -> None: + def __setitem__(self, index: slice, value: Iterable[Operation]) -> None: """Set the operations in the given slice. Args: @@ -926,7 +926,7 @@ class IfElseOperation(Operation): def __init__( self, then_operation: Operation, - else_operation: Operation, + else_operation: Operation | None, control_register: mqt.core.ir.registers.ClassicalRegister, expected_value: int = 1, comparison_kind: ComparisonKind = ComparisonKind.eq, @@ -935,7 +935,7 @@ class IfElseOperation(Operation): def __init__( self, then_operation: Operation, - else_operation: Operation, + else_operation: Operation | None, control_bit: int, expected_value: int = 1, comparison_kind: ComparisonKind = ComparisonKind.eq, From bfb40cad2d72350eece0da0fa7bba679c2a1f2b7 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 14 Dec 2025 12:14:54 +0100 Subject: [PATCH 041/144] Fix linter errors that I know how to fix --- bindings/dd/register_dd_package.cpp | 6 +- bindings/ir/register_quantum_computation.cpp | 235 +++++++++---------- 2 files changed, 112 insertions(+), 129 deletions(-) diff --git a/bindings/dd/register_dd_package.cpp b/bindings/dd/register_dd_package.cpp index 8e878841c2..0626fa5596 100644 --- a/bindings/dd/register_dd_package.cpp +++ b/bindings/dd/register_dd_package.cpp @@ -14,6 +14,7 @@ #include "dd/Operations.hpp" #include "dd/Package.hpp" #include "dd/StateGeneration.hpp" +#include "ir/Definitions.hpp" #include "ir/Permutation.hpp" #include "ir/operations/Control.hpp" #include "ir/operations/IfElseOperation.hpp" @@ -33,8 +34,10 @@ #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) #include +#include #include #include +#include #include namespace mqt { @@ -99,9 +102,8 @@ dd::mCachedEdge makeDDFromMatrix(dd::Package& p, const Matrix& m, qc::Control getControl(const Control& control) { if (std::holds_alternative(control)) { return std::get(control); - } else { + } else return static_cast(std::get(control)); - } } /// Helper function to convert Controls variant to qc::Controls diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index 16cd42ee49..fd08362612 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -28,9 +28,11 @@ #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) +#include #include #include #include +#include #include namespace mqt { @@ -50,9 +52,8 @@ namespace { qc::Control getControl(const Control& control) { if (std::holds_alternative(control)) { return std::get(control); - } else { - return static_cast(std::get(control)); } + return static_cast(std::get(control)); } /// Helper function to convert Controls variant to qc::Controls @@ -653,7 +654,7 @@ See Also: qc.def( "cx", [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { return circ.cx(getControl(control), target); }, + qc::Qubit target) { circ.cx(getControl(control), target); }, "control"_a, "target"_a, R"pb(Apply a controlled Pauli-X (i.e., CNOT or CX) gate. @@ -666,7 +667,7 @@ See Also: qc.def( "mcx", [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { return circ.mcx(getControls(controls), target); }, + qc::Qubit target) { circ.mcx(getControls(controls), target); }, "controls"_a, "target"_a, R"pb(Apply a multi-controlled Pauli-X (i.e., Toffoli or MCX) gate. @@ -690,7 +691,7 @@ See Also: qc.def( "cy", [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { return circ.cy(getControl(control), target); }, + qc::Qubit target) { circ.cy(getControl(control), target); }, "control"_a, "target"_a, R"pb(Apply a controlled Pauli-Y gate. @@ -703,7 +704,7 @@ See Also: qc.def( "mcy", [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { return circ.mcy(getControls(controls), target); }, + qc::Qubit target) { circ.mcy(getControls(controls), target); }, "controls"_a, "target"_a, R"pb(Apply a multi-controlled Pauli-Y gate. @@ -727,7 +728,7 @@ See Also: qc.def( "cz", [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { return circ.cz(getControl(control), target); }, + qc::Qubit target) { circ.cz(getControl(control), target); }, "control"_a, "target"_a, R"pb(Apply a controlled Pauli-Z gate. @@ -740,7 +741,7 @@ See Also: qc.def( "mcz", [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { return circ.mcz(getControls(controls), target); }, + qc::Qubit target) { circ.mcz(getControls(controls), target); }, "controls"_a, "target"_a, R"pb(Apply a multi-controlled Pauli-Z gate. @@ -764,7 +765,7 @@ See Also: qc.def( "ch", [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { return circ.ch(getControl(control), target); }, + qc::Qubit target) { circ.ch(getControl(control), target); }, "control"_a, "target"_a, R"pb(Apply a controlled Hadamard gate. @@ -777,7 +778,7 @@ See Also: qc.def( "mch", [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { return circ.mch(getControls(controls), target); }, + qc::Qubit target) { circ.mch(getControls(controls), target); }, "controls"_a, "target"_a, R"pb(Apply a multi-controlled Hadamard gate. @@ -801,7 +802,7 @@ See Also: qc.def( "cs", [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { return circ.cs(getControl(control), target); }, + qc::Qubit target) { circ.cs(getControl(control), target); }, "control"_a, "target"_a, R"pb(Apply a controlled S gate. @@ -814,7 +815,7 @@ See Also: qc.def( "mcs", [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { return circ.mcs(getControls(controls), target); }, + qc::Qubit target) { circ.mcs(getControls(controls), target); }, "controls"_a, "target"_a, R"pb(Apply a multi-controlled S gate. @@ -838,7 +839,7 @@ See Also: qc.def( "csdg", [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { return circ.csdg(getControl(control), target); }, + qc::Qubit target) { circ.csdg(getControl(control), target); }, "control"_a, "target"_a, R"pb(Apply a controlled :math:`S^\dagger` gate. @@ -851,9 +852,7 @@ See Also: qc.def( "mcsdg", [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { - return circ.mcsdg(getControls(controls), target); - }, + qc::Qubit target) { circ.mcsdg(getControls(controls), target); }, "controls"_a, "target"_a, R"pb(Apply a multi-controlled :math:`S^\dagger` gate. @@ -877,7 +876,7 @@ See Also: qc.def( "ct", [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { return circ.ct(getControl(control), target); }, + qc::Qubit target) { circ.ct(getControl(control), target); }, "control"_a, "target"_a, R"pb(Apply a controlled T gate. @@ -890,7 +889,7 @@ See Also: qc.def( "mct", [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { return circ.mct(getControls(controls), target); }, + qc::Qubit target) { circ.mct(getControls(controls), target); }, "controls"_a, "target"_a, R"pb(Apply a multi-controlled T gate. @@ -914,7 +913,7 @@ See Also: qc.def( "ctdg", [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { return circ.ctdg(getControl(control), target); }, + qc::Qubit target) { circ.ctdg(getControl(control), target); }, "control"_a, "target"_a, R"pb(Apply a controlled :math:`T^\dagger` gate. @@ -927,9 +926,7 @@ See Also: qc.def( "mctdg", [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { - return circ.mctdg(getControls(controls), target); - }, + qc::Qubit target) { circ.mctdg(getControls(controls), target); }, "controls"_a, "target"_a, R"pb(Apply a multi-controlled :math:`T^\dagger` gate. @@ -953,7 +950,7 @@ See Also: qc.def( "cv", [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { return circ.cv(getControl(control), target); }, + qc::Qubit target) { circ.cv(getControl(control), target); }, "control"_a, "target"_a, R"pb(Apply a controlled V gate. @@ -966,7 +963,7 @@ See Also: qc.def( "mcv", [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { return circ.mcv(getControls(controls), target); }, + qc::Qubit target) { circ.mcv(getControls(controls), target); }, "controls"_a, "target"_a, R"pb(Apply a multi-controlled V gate. @@ -990,7 +987,7 @@ See Also: qc.def( "cvdg", [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { return circ.cvdg(getControl(control), target); }, + qc::Qubit target) { circ.cvdg(getControl(control), target); }, "control"_a, "target"_a, R"pb(Apply a controlled :math:`V^\dagger` gate. @@ -1003,9 +1000,7 @@ See Also: qc.def( "mcvdg", [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { - return circ.mcvdg(getControls(controls), target); - }, + qc::Qubit target) { circ.mcvdg(getControls(controls), target); }, "controls"_a, "target"_a, R"pb(Apply a multi-controlled :math:`V^\dagger` gate. @@ -1029,7 +1024,7 @@ See Also: qc.def( "csx", [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { return circ.csx(getControl(control), target); }, + qc::Qubit target) { circ.csx(getControl(control), target); }, "control"_a, "target"_a, R"pb(Apply a controlled :math:`\sqrt{X}` gate. @@ -1042,7 +1037,7 @@ See Also: qc.def( "mcsx", [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { return circ.mcsx(getControls(controls), target); }, + qc::Qubit target) { circ.mcsx(getControls(controls), target); }, "controls"_a, "target"_a, R"pb(Apply a multi-controlled :math:`\sqrt{X}` gate. @@ -1066,7 +1061,7 @@ See Also: qc.def( "csxdg", [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { return circ.csxdg(getControl(control), target); }, + qc::Qubit target) { circ.csxdg(getControl(control), target); }, "control"_a, "target"_a, R"pb(Apply a controlled :math:`\sqrt{X}^\dagger` gate. @@ -1079,9 +1074,7 @@ See Also: qc.def( "mcsxdg", [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { - return circ.mcsxdg(getControls(controls), target); - }, + qc::Qubit target) { circ.mcsxdg(getControls(controls), target); }, "controls"_a, "target"_a, R"pb(Apply a multi-controlled :math:`\sqrt{X}^\dagger` gate. @@ -1107,9 +1100,8 @@ See Also: qc.def( "crx", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Control& control, qc::Qubit target) { - return circ.crx(theta, getControl(control), target); - }, + const Control& control, + qc::Qubit target) { circ.crx(theta, getControl(control), target); }, "theta"_a, "control"_a, "target"_a, R"pb(Apply a controlled :math:`R_x(\theta)` gate. @@ -1123,9 +1115,8 @@ See Also: qc.def( "mcrx", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Controls& controls, qc::Qubit target) { - return circ.mcrx(theta, getControls(controls), target); - }, + const Controls& controls, + qc::Qubit target) { circ.mcrx(theta, getControls(controls), target); }, "theta"_a, "controls"_a, "target"_a, R"pb(Apply a multi-controlled :math:`R_x(\theta)` gate. @@ -1152,9 +1143,8 @@ See Also: qc.def( "cry", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Control& control, qc::Qubit target) { - return circ.cry(theta, getControl(control), target); - }, + const Control& control, + qc::Qubit target) { circ.cry(theta, getControl(control), target); }, "theta"_a, "control"_a, "target"_a, R"pb(Apply a controlled :math:`R_y(\theta)` gate. @@ -1168,9 +1158,8 @@ See Also: qc.def( "mcry", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Controls& controls, qc::Qubit target) { - return circ.mcry(theta, getControls(controls), target); - }, + const Controls& controls, + qc::Qubit target) { circ.mcry(theta, getControls(controls), target); }, "theta"_a, "controls"_a, "target"_a, R"pb(Apply a multi-controlled :math:`R_y(\theta)` gate. @@ -1196,9 +1185,8 @@ See Also: qc.def( "crz", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Control& control, qc::Qubit target) { - return circ.crz(theta, getControl(control), target); - }, + const Control& control, + qc::Qubit target) { circ.crz(theta, getControl(control), target); }, "theta"_a, "control"_a, "target"_a, R"pb(Apply a controlled :math:`R_z(\theta)` gate. @@ -1212,9 +1200,8 @@ See Also: qc.def( "mcrz", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Controls& controls, qc::Qubit target) { - return circ.mcrx(theta, getControls(controls), target); - }, + const Controls& controls, + qc::Qubit target) { circ.mcrx(theta, getControls(controls), target); }, "theta"_a, "controls"_a, "target"_a, R"pb(Apply a multi-controlled :math:`R_z(\theta)` gate. @@ -1240,9 +1227,8 @@ See Also: qc.def( "cp", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Control& control, qc::Qubit target) { - return circ.cp(theta, getControl(control), target); - }, + const Control& control, + qc::Qubit target) { circ.cp(theta, getControl(control), target); }, "theta"_a, "control"_a, "target"_a, R"pb(Apply a controlled phase gate. @@ -1256,9 +1242,8 @@ See Also: qc.def( "mcp", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Controls& controls, qc::Qubit target) { - return circ.mcp(theta, getControls(controls), target); - }, + const Controls& controls, + qc::Qubit target) { circ.mcp(theta, getControls(controls), target); }, "theta"_a, "controls"_a, "target"_a, R"pb(Apply a multi-controlled phase gate. @@ -1285,9 +1270,8 @@ See Also: qc.def( "cu2", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& phi, - qc::SymbolOrNumber& lambda_, const Control& control, - qc::Qubit target) { - return circ.cu2(phi, lambda_, getControl(control), target); + qc::SymbolOrNumber& lambda, const Control& control, qc::Qubit target) { + circ.cu2(phi, lambda, getControl(control), target); }, "phi"_a, "lambda_"_a, "control"_a, "target"_a, R"pb(Apply a controlled :math:`U_2(\phi, \lambda)` gate. @@ -1303,9 +1287,9 @@ See Also: qc.def( "mcu2", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& phi, - qc::SymbolOrNumber& lambda_, const Controls& controls, + qc::SymbolOrNumber& lambda, const Controls& controls, qc::Qubit target) { - return circ.mcu2(phi, lambda_, getControls(controls), target); + circ.mcu2(phi, lambda, getControls(controls), target); }, "phi"_a, "lambda_"_a, "controls"_a, "target"_a, R"pb(Apply a multi-controlled :math:`U_2(\phi, \lambda)` gate. @@ -1336,7 +1320,7 @@ See Also: "cr", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, qc::SymbolOrNumber& phi, const Control& control, qc::Qubit target) { - return circ.cr(theta, phi, getControl(control), target); + circ.cr(theta, phi, getControl(control), target); }, "theta"_a, "phi"_a, "control"_a, "target"_a, R"pb(Apply a controlled :math:`R(\theta, \phi)` gate. @@ -1353,7 +1337,7 @@ See Also: "mcr", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, qc::SymbolOrNumber& phi, const Controls& controls, qc::Qubit target) { - return circ.mcr(theta, phi, getControls(controls), target); + circ.mcr(theta, phi, getControls(controls), target); }, "theta"_a, "phi"_a, "controls"_a, "target"_a, R"pb(Apply a multi-controlled :math:`R(\theta, \phi)` gate. @@ -1386,7 +1370,7 @@ See Also: [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, qc::SymbolOrNumber& phi, qc::SymbolOrNumber& lambda, const Control& control, qc::Qubit target) { - return circ.cu(theta, phi, lambda, getControl(control), target); + circ.cu(theta, phi, lambda, getControl(control), target); }, "theta"_a, "phi"_a, "lambda_"_a, "control"_a, "target"_a, R"pb(Apply a controlled :math:`U(\theta, \phi, \lambda)` gate. @@ -1405,7 +1389,7 @@ See Also: [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, qc::SymbolOrNumber& phi, qc::SymbolOrNumber& lambda, const Controls& controls, qc::Qubit target) { - return circ.mcu(theta, phi, lambda, getControls(controls), target); + circ.mcu(theta, phi, lambda, getControls(controls), target); }, "theta"_a, "phi"_a, "lambda_"_a, "controls"_a, "target"_a, R"pb(Apply a multi-controlled :math:`U(\theta, \phi, \lambda)` gate. @@ -1434,8 +1418,8 @@ See Also: qc.def( "cswap", [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target1, qc::Qubit target2) { - return circ.cswap(getControl(control), target1, target2); + qc::Qubit target0, qc::Qubit target1) { + circ.cswap(getControl(control), target0, target1); }, "control"_a, "target1"_a, "target2"_a, R"pb(Apply a controlled SWAP gate. @@ -1450,8 +1434,8 @@ See Also: qc.def( "mcswap", [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target1, qc::Qubit target2) { - return circ.mcswap(getControls(controls), target1, target2); + qc::Qubit target0, qc::Qubit target1) { + circ.mcswap(getControls(controls), target0, target1); }, "controls"_a, "target1"_a, "target2"_a, R"pb(Apply a multi-controlled SWAP gate. @@ -1478,8 +1462,8 @@ See Also: qc.def( "cdcx", [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target1, qc::Qubit target2) { - return circ.cdcx(getControl(control), target1, target2); + qc::Qubit target0, qc::Qubit target1) { + circ.cdcx(getControl(control), target0, target1); }, "control"_a, "target1"_a, "target2"_a, R"pb(Apply a controlled DCX gate. @@ -1494,8 +1478,8 @@ See Also: qc.def( "mcdcx", [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target1, qc::Qubit target2) { - return circ.mcdcx(getControls(controls), target1, target2); + qc::Qubit target0, qc::Qubit target1) { + circ.mcdcx(getControls(controls), target0, target1); }, "controls"_a, "target1"_a, "target2"_a, R"pb(Apply a multi-controlled DCX gate. @@ -1522,8 +1506,8 @@ See Also: qc.def( "cecr", [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target1, qc::Qubit target2) { - return circ.cecr(getControl(control), target1, target2); + qc::Qubit target0, qc::Qubit target1) { + circ.cecr(getControl(control), target0, target1); }, "control"_a, "target1"_a, "target2"_a, R"pb(Apply a controlled ECR gate. @@ -1538,8 +1522,8 @@ See Also: qc.def( "mcecr", [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target1, qc::Qubit target2) { - return circ.mcecr(getControls(controls), target1, target2); + qc::Qubit target0, qc::Qubit target1) { + circ.mcecr(getControls(controls), target0, target1); }, "controls"_a, "target1"_a, "target2"_a, R"pb(Apply a multi-controlled ECR gate. @@ -1566,8 +1550,8 @@ See Also: qc.def( "ciswap", [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target1, qc::Qubit target2) { - return circ.ciswap(getControl(control), target1, target2); + qc::Qubit target0, qc::Qubit target1) { + circ.ciswap(getControl(control), target0, target1); }, "control"_a, "target1"_a, "target2"_a, R"pb(Apply a controlled :math:`i\text{SWAP}` gate. @@ -1582,8 +1566,8 @@ See Also: qc.def( "mciswap", [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target1, qc::Qubit target2) { - return circ.mciswap(getControls(controls), target1, target2); + qc::Qubit target0, qc::Qubit target1) { + circ.mciswap(getControls(controls), target0, target1); }, "controls"_a, "target1"_a, "target2"_a, R"pb(Apply a multi-controlled :math:`i\text{SWAP}` gate. @@ -1610,8 +1594,8 @@ See Also: qc.def( "ciswapdg", [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target1, qc::Qubit target2) { - return circ.ciswapdg(getControl(control), target1, target2); + qc::Qubit target0, qc::Qubit target1) { + circ.ciswapdg(getControl(control), target0, target1); }, "control"_a, "target1"_a, "target2"_a, R"pb(Apply a controlled :math:`i\text{SWAP}^\dagger` gate. @@ -1626,8 +1610,8 @@ See Also: qc.def( "mciswapdg", [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target1, qc::Qubit target2) { - return circ.mciswapdg(getControls(controls), target1, target2); + qc::Qubit target0, qc::Qubit target1) { + circ.mciswapdg(getControls(controls), target0, target1); }, "controls"_a, "target1"_a, "target2"_a, R"pb(Apply a multi-controlled :math:`i\text{SWAP}^\dagger` gate. @@ -1654,8 +1638,8 @@ See Also: qc.def( "cperes", [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target1, qc::Qubit target2) { - return circ.cperes(getControl(control), target1, target2); + qc::Qubit target0, qc::Qubit target1) { + circ.cperes(getControl(control), target0, target1); }, "control"_a, "target1"_a, "target2"_a, R"pb(Apply a controlled Peres gate. @@ -1670,8 +1654,8 @@ See Also: qc.def( "mcperes", [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target1, qc::Qubit target2) { - return circ.mcperes(getControls(controls), target1, target2); + qc::Qubit target0, qc::Qubit target1) { + circ.mcperes(getControls(controls), target0, target1); }, "controls"_a, "target1"_a, "target2"_a, R"pb(Apply a multi-controlled Peres gate. @@ -1697,8 +1681,8 @@ See Also: qc.def( "cperesdg", [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target1, qc::Qubit target2) { - return circ.cperesdg(getControl(control), target1, target2); + qc::Qubit target0, qc::Qubit target1) { + circ.cperesdg(getControl(control), target0, target1); }, "control"_a, "target1"_a, "target2"_a, R"pb(Apply a controlled :math:`\text{Peres}^\dagger` gate. @@ -1713,8 +1697,8 @@ See Also: qc.def( "mcperesdg", [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target1, qc::Qubit target2) { - return circ.mcperesdg(getControls(controls), target1, target2); + qc::Qubit target0, qc::Qubit target1) { + circ.mcperesdg(getControls(controls), target0, target1); }, "controls"_a, "target1"_a, "target2"_a, R"pb(Apply a multi-controlled :math:`\text{Peres}^\dagger` gate. @@ -1747,8 +1731,8 @@ See Also: qc.def( "crxx", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Control& control, qc::Qubit target1, qc::Qubit target2) { - return circ.crxx(theta, getControl(control), target1, target2); + const Control& control, qc::Qubit target0, qc::Qubit target1) { + circ.crxx(theta, getControl(control), target0, target1); }, "theta"_a, "control"_a, "target1"_a, "target2"_a, R"pb(Apply a controlled :math:`R_{xx}(\theta)` gate. @@ -1764,8 +1748,8 @@ See Also: qc.def( "mcrxx", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Controls& controls, qc::Qubit target1, qc::Qubit target2) { - return circ.mcrxx(theta, getControls(controls), target1, target2); + const Controls& controls, qc::Qubit target0, qc::Qubit target1) { + circ.mcrxx(theta, getControls(controls), target0, target1); }, "theta"_a, "controls"_a, "target1"_a, "target2"_a, R"pb(Apply a multi-controlled :math:`R_{xx}(\theta)` gate. @@ -1799,8 +1783,8 @@ See Also: qc.def( "cryy", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Control& control, qc::Qubit target1, qc::Qubit target2) { - return circ.cryy(theta, getControl(control), target1, target2); + const Control& control, qc::Qubit target0, qc::Qubit target1) { + circ.cryy(theta, getControl(control), target0, target1); }, "theta"_a, "control"_a, "target1"_a, "target2"_a, R"pb(Apply a controlled :math:`R_{yy}(\theta)` gate. @@ -1816,8 +1800,8 @@ See Also: qc.def( "mcryy", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Controls& controls, qc::Qubit target1, qc::Qubit target2) { - return circ.mcryy(theta, getControls(controls), target1, target2); + const Controls& controls, qc::Qubit target0, qc::Qubit target1) { + circ.mcryy(theta, getControls(controls), target0, target1); }, "theta"_a, "controls"_a, "target1"_a, "target2"_a, R"pb(Apply a multi-controlled :math:`R_{yy}(\theta)` gate. @@ -1851,8 +1835,8 @@ See Also: qc.def( "crzx", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Control& control, qc::Qubit target1, qc::Qubit target2) { - return circ.crzx(theta, getControl(control), target1, target2); + const Control& control, qc::Qubit target0, qc::Qubit target1) { + circ.crzx(theta, getControl(control), target0, target1); }, "theta"_a, "control"_a, "target1"_a, "target2"_a, R"pb(Apply a controlled :math:`R_{zx}(\theta)` gate. @@ -1868,8 +1852,8 @@ See Also: qc.def( "mcrzx", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Controls& controls, qc::Qubit target1, qc::Qubit target2) { - return circ.mcrzx(theta, getControls(controls), target1, target2); + const Controls& controls, qc::Qubit target0, qc::Qubit target1) { + circ.mcrzx(theta, getControls(controls), target0, target1); }, "theta"_a, "controls"_a, "target1"_a, "target2"_a, R"pb(Apply a multi-controlled :math:`R_{zx}(\theta)` gate. @@ -1903,8 +1887,8 @@ See Also: qc.def( "crzz", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Control& control, qc::Qubit target1, qc::Qubit target2) { - return circ.crzz(theta, getControl(control), target1, target2); + const Control& control, qc::Qubit target0, qc::Qubit target1) { + circ.crzz(theta, getControl(control), target0, target1); }, "theta"_a, "control"_a, "target1"_a, "target2"_a, R"pb(Apply a controlled :math:`R_{zz}(\theta)` gate. @@ -1920,8 +1904,8 @@ See Also: qc.def( "mcrzz", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Controls& controls, qc::Qubit target1, qc::Qubit target2) { - return circ.mcrzz(theta, getControls(controls), target1, target2); + const Controls& controls, qc::Qubit target0, qc::Qubit target1) { + circ.mcrzz(theta, getControls(controls), target0, target1); }, "theta"_a, "controls"_a, "target1"_a, "target2"_a, R"pb(Apply a multi-controlled :math:`R_{zz}(\theta)` gate. @@ -1956,10 +1940,9 @@ See Also: qc.def( "cxx_minus_yy", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - qc::SymbolOrNumber& beta, const Control& control, qc::Qubit target1, - qc::Qubit target2) { - return circ.cxx_minus_yy(theta, beta, getControl(control), target1, - target2); + qc::SymbolOrNumber& beta, const Control& control, qc::Qubit target0, + qc::Qubit target1) { + circ.cxx_minus_yy(theta, beta, getControl(control), target0, target1); }, "theta"_a, "beta"_a, "control"_a, "target1"_a, "target2"_a, R"pb(Apply a controlled :math:`R_{XX - YY}(\theta, \beta)` gate. @@ -1976,10 +1959,10 @@ See Also: qc.def( "mcxx_minus_yy", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - qc::SymbolOrNumber& beta, const Controls& controls, qc::Qubit target1, - qc::Qubit target2) { - return circ.mcxx_minus_yy(theta, beta, getControls(controls), target1, - target2); + qc::SymbolOrNumber& beta, const Controls& controls, qc::Qubit target0, + qc::Qubit target1) { + circ.mcxx_minus_yy(theta, beta, getControls(controls), target0, + target1); }, "theta"_a, "beta"_a, "controls"_a, "target1"_a, "target2"_a, R"pb(Apply a multi-controlled :math:`R_{XX - YY}(\theta, \beta)` gate. @@ -2015,10 +1998,9 @@ See Also: qc.def( "cxx_plus_yy", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - qc::SymbolOrNumber& beta, const Control& control, qc::Qubit target1, - qc::Qubit target2) { - return circ.cxx_plus_yy(theta, beta, getControl(control), target1, - target2); + qc::SymbolOrNumber& beta, const Control& control, qc::Qubit target0, + qc::Qubit target1) { + circ.cxx_plus_yy(theta, beta, getControl(control), target0, target1); }, "theta"_a, "beta"_a, "control"_a, "target1"_a, "target2"_a, R"pb(Apply a controlled :math:`R_{XX + YY}(\theta, \beta)` gate. @@ -2035,10 +2017,9 @@ See Also: qc.def( "mcxx_plus_yy", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - qc::SymbolOrNumber& beta, const Controls& controls, qc::Qubit target1, - qc::Qubit target2) { - return circ.mcxx_plus_yy(theta, beta, getControls(controls), target1, - target2); + qc::SymbolOrNumber& beta, const Controls& controls, qc::Qubit target0, + qc::Qubit target1) { + circ.mcxx_plus_yy(theta, beta, getControls(controls), target0, target1); }, "theta"_a, "beta"_a, "controls"_a, "target1"_a, "target2"_a, R"pb(Apply a multi-controlled :math:`R_{XX + YY}(\theta, \beta)` gate. From 28e4262140d1ea71cc74d52587fd44fb6a8a04b9 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 14 Dec 2025 12:27:18 +0100 Subject: [PATCH 042/144] Improve Permutation bindings --- bindings/dd/register_dd_package.cpp | 4 +-- bindings/ir/register_permutation.cpp | 50 +++++++++++++++++++++++----- python/mqt/core/ir/__init__.pyi | 10 ++++-- 3 files changed, 50 insertions(+), 14 deletions(-) diff --git a/bindings/dd/register_dd_package.cpp b/bindings/dd/register_dd_package.cpp index 0626fa5596..ef58b27eea 100644 --- a/bindings/dd/register_dd_package.cpp +++ b/bindings/dd/register_dd_package.cpp @@ -102,8 +102,8 @@ dd::mCachedEdge makeDDFromMatrix(dd::Package& p, const Matrix& m, qc::Control getControl(const Control& control) { if (std::holds_alternative(control)) { return std::get(control); - } else - return static_cast(std::get(control)); + } + return static_cast(std::get(control)); } /// Helper function to convert Controls variant to qc::Controls diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index 941698d938..f09862a9b8 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -16,23 +16,53 @@ #include #include #include -#include // NOLINT(misc-include-cleaner) -#include // NOLINT(misc-include-cleaner) -#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include #include #include +#include namespace mqt { namespace nb = nanobind; using namespace nb::literals; +using Control = std::variant; +using Controls = std::set>; + +namespace { + +/// Helper function to convert Control variant to qc::Control +qc::Control getControl(const Control& control) { + if (std::holds_alternative(control)) { + return std::get(control); + } + return static_cast(std::get(control)); +} + +/// Helper function to convert Controls variant to qc::Controls +qc::Controls getControls(const Controls& controls) { + qc::Controls result; + for (const auto& control : controls) { + result.insert(getControl(control)); + } + return result; +} + +} // namespace + // NOLINTNEXTLINE(misc-use-internal-linkage) void registerPermutation(nb::module_& m) { nb::class_( m, "Permutation", nb::sig("class Permutation(collections.abc.MutableMapping[int, int])"), - "A class to represent a permutation of the qubits in a quantum circuit.") + R"pb(A class to represent a permutation of the qubits in a quantum circuit. + +Args: + permutation: The permutation to initialize the object with.)pb") .def(nb::init<>()) @@ -46,12 +76,14 @@ void registerPermutation(nb::module_& m) { } new (self) qc::Permutation(std::move(perm)); }, - "perm"_a, "Create a permutation from a dictionary.") + "permutation"_a, "Create a permutation from a dictionary.") - .def("apply", - nb::overload_cast(&qc::Permutation::apply, - nb::const_), - "controls"_a, R"pb(Apply the permutation to a set of controls. + .def( + "apply", + [](const qc::Permutation& p, const Controls& controls) { + return p.apply(getControls(controls)); + }, + "controls"_a, R"pb(Apply the permutation to a set of controls. Args: controls: The set of controls to apply the permutation to. diff --git a/python/mqt/core/ir/__init__.pyi b/python/mqt/core/ir/__init__.pyi index 1d307eb120..5cb5296c93 100644 --- a/python/mqt/core/ir/__init__.pyi +++ b/python/mqt/core/ir/__init__.pyi @@ -15,16 +15,20 @@ from . import registers as registers from . import symbolic as symbolic 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. + """ @overload def __init__(self) -> None: ... @overload - def __init__(self, perm: dict[int, int]) -> None: + def __init__(self, permutation: dict[int, int]) -> None: """Create a permutation from a dictionary.""" @overload - def apply(self, controls: AbstractSet[operations.Control]) -> set[operations.Control]: + def apply(self, controls: AbstractSet[operations.Control | int]) -> set[operations.Control]: """Apply the permutation to a set of controls. Args: From 63b77d3142192e0a2210cf92db541ffb8b14c95e Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 14 Dec 2025 12:32:58 +0100 Subject: [PATCH 043/144] Enable free threading --- cmake/AddMQTPythonBinding.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/AddMQTPythonBinding.cmake b/cmake/AddMQTPythonBinding.cmake index f27449a2da..283b58b5b4 100644 --- a/cmake/AddMQTPythonBinding.cmake +++ b/cmake/AddMQTPythonBinding.cmake @@ -63,6 +63,8 @@ function(add_mqt_python_binding_nanobind package_name target_name) ${target_name} # Target the stable ABI for Python 3.12+, which reduces the number of binary wheels STABLE_ABI + # Enable free-threaded support + FREE_THREADED # Suppress compiler warnings from the nanobind library NB_SUPPRESS_WARNINGS # Source files From b1e5dea208550b01fbd613b28705478ba38ddaf6 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 14 Dec 2025 12:55:45 +0100 Subject: [PATCH 044/144] Improve docstrings --- bindings/dd/register_dd_package.cpp | 24 ++++++++-------- bindings/dd/register_vector_dds.cpp | 1 - bindings/ir/register_quantum_computation.cpp | 30 ++++++++++---------- python/mqt/core/dd.pyi | 21 ++++++-------- python/mqt/core/ir/__init__.pyi | 28 +++++++++--------- 5 files changed, 50 insertions(+), 54 deletions(-) diff --git a/bindings/dd/register_dd_package.cpp b/bindings/dd/register_dd_package.cpp index ef58b27eea..adcec0f729 100644 --- a/bindings/dd/register_dd_package.cpp +++ b/bindings/dd/register_dd_package.cpp @@ -205,9 +205,9 @@ Specifically, it nb::enum_(m, "BasisStates", "enum.Enum", "Enumeration of basis states.") .value("zero", dd::BasisStates::zero, - "The computational basis state :math:`|0\rangle`.") + R"pb(The computational basis state :math:`|0\rangle`.)pb") .value("one", dd::BasisStates::one, - "The computational basis state :math:`|1\rangle`.") + R"pb(The computational basis state :math:`|1\rangle`.)pb") .value( "plus", dd::BasisStates::plus, R"pb(The superposition state :math:`|+\rangle = \frac{1}{\sqrt{2}} (|0\rangle + |1\rangle)`.)pb") @@ -365,7 +365,7 @@ Specifically, it permutation: The permutation of the qubits. Defaults to the identity permutation. Returns: - The resulting DD after the measurement as well as the updated measurement outcomes..)pb"); + The resulting DD after the measurement as well as the updated measurement outcomes.)pb"); dd.def( "apply_reset", @@ -662,9 +662,9 @@ Specifically, it dd.def("inc_ref_vec", &dd::Package::incRef, "vec"_a, "Increment the reference count of a vector."); dd.def("inc_ref_mat", &dd::Package::incRef, "mat"_a, - "Decrement the reference count of a vector."); - dd.def("dec_ref_vec", &dd::Package::decRef, "vec"_a, "Increment the reference count of a matrix."); + dd.def("dec_ref_vec", &dd::Package::decRef, "vec"_a, + "Decrement the reference count of a vector."); dd.def("dec_ref_mat", &dd::Package::decRef, "mat"_a, "Decrement the reference count of a matrix."); dd.def("garbage_collect", &dd::Package::garbageCollect, "force"_a = false, @@ -773,15 +773,15 @@ Specifically, it // keep the DD package alive while the returned matrix DD is alive. nb::keep_alive<0, 1>(), R"pb(Multiply two matrices. - Notes: - It is the caller's responsibility to update the reference count of the input and output matrices after the operation. +Notes: + It is the caller's responsibility to update the reference count of the input and output matrices after the operation. - Args: - lhs: The left matrix. - rhs: The right matrix. +Args: + lhs: The left matrix. + rhs: The right matrix. - Returns: - The product of the two matrices.)pb"); +Returns: + The product of the two matrices.)pb"); dd.def( "inner_product", diff --git a/bindings/dd/register_vector_dds.cpp b/bindings/dd/register_vector_dds.cpp index bac024cd8d..da9f6f4cf8 100644 --- a/bindings/dd/register_vector_dds.cpp +++ b/bindings/dd/register_vector_dds.cpp @@ -139,7 +139,6 @@ Requires the `dot` command from Graphviz to be installed and available in the PA edge_labels: Whether to include edge weights as labels. classic: Whether to use the classic DD visualization style. memory: Whether to include memory information. For debugging purposes only. - show: Whether to open the SVG file in the default browser. format_as_polar: Whether to format the edge weights in polar coordinates.)pb"); } diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index fd08362612..1b3f605628 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -473,7 +473,7 @@ If the circuit contains measurements at the end, these measurements are used to qc.def("set_circuit_qubits_garbage", &qc::QuantumComputation::setLogicalQubitsGarbage, "q_min"_a, "q_max"_a, - R"pb(et a range of circuit (i.e., logical) qubits to be garbage. + R"pb(Set a range of circuit (i.e., logical) qubits to be garbage. Args: q_min: The minimum index of the circuit qubits to set as garbage. @@ -2165,7 +2165,7 @@ This method is equivalent to calling :meth:`measure` multiple times. "op_type"_a, "target"_a, "control_register"_a, "expected_value"_a = 1U, "comparison_kind"_a = qc::ComparisonKind::Eq, "params"_a = std::vector{}, - R"pb(Add an if operartion to the circuit. + R"pb(Add an if operation to the circuit. Args: op_type: The operation to apply @@ -2184,7 +2184,7 @@ This method is equivalent to calling :meth:`measure` multiple times. "op_type"_a, "target"_a, "control"_a, "control_register"_a, "expected_value"_a = 1U, "comparison_kind"_a = qc::ComparisonKind::Eq, "params"_a = std::vector{}, - R"pb(Add an if operartion to the circuit. + R"pb(Add an if operation to the circuit. Args: op_type: The operation to apply @@ -2204,16 +2204,16 @@ This method is equivalent to calling :meth:`measure` multiple times. "op_type"_a, "target"_a, "controls"_a, "control_register"_a, "expected_value"_a = 1U, "comparison_kind"_a = qc::ComparisonKind::Eq, "params"_a = std::vector{}, - R"pb(Add an if operartion to the circuit. + R"pb(Add an if operation to the circuit. - Args: - op_type: The operation to apply - target: The target qubit - controls: The control qubits - control_register: The classical register to check against - expected_value: The expected value of the control register - comparison_kind: The kind of comparison to perform - params: The parameters of the operation.)pb"); +Args: + op_type: The operation to apply + target: The target qubit + controls: The control qubits + control_register: The classical register to check against + expected_value: The expected value of the control register + comparison_kind: The kind of comparison to perform + params: The parameters of the operation.)pb"); qc.def("if_", nb::overload_cast{}, - R"pb(Add an if operartion to the circuit. + R"pb(Add an if operation to the circuit. Args: op_type: The operation to apply @@ -2242,7 +2242,7 @@ This method is equivalent to calling :meth:`measure` multiple times. "expected_value"_a = true, "comparison_kind"_a = qc::ComparisonKind::Eq, "params"_a = std::vector{}, - R"pb(Add an if operartion to the circuit. + R"pb(Add an if operation to the circuit. Args: op_type: The operation to apply @@ -2262,7 +2262,7 @@ This method is equivalent to calling :meth:`measure` multiple times. "op_type"_a, "target"_a, "controls"_a, "control_bit"_a, "expected_value"_a = true, "comparison_kind"_a = qc::ComparisonKind::Eq, "params"_a = std::vector{}, - R"pb(Add an if operartion to the circuit. + R"pb(Add an if operation to the circuit. Args: op_type: The operation to apply diff --git a/python/mqt/core/dd.pyi b/python/mqt/core/dd.pyi index 4a19494382..af7de8eeca 100644 --- a/python/mqt/core/dd.pyi +++ b/python/mqt/core/dd.pyi @@ -94,7 +94,6 @@ class VectorDD: edge_labels: Whether to include edge weights as labels. classic: Whether to use the classic DD visualization style. memory: Whether to include memory information. For debugging purposes only. - show: Whether to open the SVG file in the default browser. format_as_polar: Whether to format the edge weights in polar coordinates. """ @@ -348,7 +347,7 @@ class DDPackage: permutation: The permutation of the qubits. Defaults to the identity permutation. Returns: - The resulting DD after the measurement as well as the updated measurement outcomes.. + The resulting DD after the measurement as well as the updated measurement outcomes. """ def apply_reset( @@ -557,10 +556,10 @@ class DDPackage: """Increment the reference count of a vector.""" def inc_ref_mat(self, mat: MatrixDD) -> None: - """Decrement the reference count of a vector.""" + """Increment the reference count of a matrix.""" def dec_ref_vec(self, vec: VectorDD) -> None: - """Increment the reference count of a matrix.""" + """Decrement the reference count of a vector.""" def dec_ref_mat(self, mat: MatrixDD) -> None: """Decrement the reference count of a matrix.""" @@ -655,14 +654,14 @@ class DDPackage: """Multiply two matrices. Notes: - It is the caller's responsibility to update the reference count of the input and output matrices after the operation. + It is the caller's responsibility to update the reference count of the input and output matrices after the operation. Args: - lhs: The left matrix. - rhs: The right matrix. + lhs: The left matrix. + rhs: The right matrix. Returns: - The product of the two matrices. + The product of the two matrices. """ def inner_product(self, lhs: VectorDD, rhs: VectorDD) -> complex: @@ -771,12 +770,10 @@ class BasisStates(enum.Enum): """Enumeration of basis states.""" zero = 0 - """The computational basis state :math:`|0 - angle`.""" + r"""The computational basis state :math:`|0\rangle`.""" one = 1 - """The computational basis state :math:`|1 - angle`.""" + r"""The computational basis state :math:`|1\rangle`.""" plus = 2 r""" diff --git a/python/mqt/core/ir/__init__.pyi b/python/mqt/core/ir/__init__.pyi index 5cb5296c93..9a263acf5d 100644 --- a/python/mqt/core/ir/__init__.pyi +++ b/python/mqt/core/ir/__init__.pyi @@ -426,7 +426,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): """ def set_circuit_qubits_garbage(self, q_min: int, q_max: int) -> None: - """Et a range of circuit (i.e., logical) qubits to be garbage. + """Set a range of circuit (i.e., logical) qubits to be garbage. Args: q_min: The minimum index of the circuit qubits to set as garbage. @@ -1945,7 +1945,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, params: Sequence[float] = [], ) -> None: - """Add an if operartion to the circuit. + """Add an if operation to the circuit. Args: op_type: The operation to apply @@ -1967,7 +1967,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, params: Sequence[float] = [], ) -> None: - """Add an if operartion to the circuit. + """Add an if operation to the circuit. Args: op_type: The operation to apply @@ -1990,16 +1990,16 @@ class QuantumComputation(MutableSequence[operations.Operation]): comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, params: Sequence[float] = [], ) -> None: - """Add an if operartion to the circuit. + """Add an if operation to the circuit. Args: - op_type: The operation to apply - target: The target qubit - controls: The control qubits - control_register: The classical register to check against - expected_value: The expected value of the control register - comparison_kind: The kind of comparison to perform - params: The parameters of the operation. + op_type: The operation to apply + target: The target qubit + controls: The control qubits + control_register: The classical register to check against + expected_value: The expected value of the control register + comparison_kind: The kind of comparison to perform + params: The parameters of the operation. """ @overload @@ -2012,7 +2012,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, params: Sequence[float] = [], ) -> None: - """Add an if operartion to the circuit. + """Add an if operation to the circuit. Args: op_type: The operation to apply @@ -2034,7 +2034,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, params: Sequence[float] = [], ) -> None: - """Add an if operartion to the circuit. + """Add an if operation to the circuit. Args: op_type: The operation to apply @@ -2057,7 +2057,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, params: Sequence[float] = [], ) -> None: - """Add an if operartion to the circuit. + """Add an if operation to the circuit. Args: op_type: The operation to apply From 3730bf709a35e5e291dccc56ec412acfcad88c7d Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 14 Dec 2025 13:00:44 +0100 Subject: [PATCH 045/144] Improve stub-generation script --- bindings/generate-stubs.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bindings/generate-stubs.sh b/bindings/generate-stubs.sh index cad1041b97..4eb2a81313 100755 --- a/bindings/generate-stubs.sh +++ b/bindings/generate-stubs.sh @@ -18,7 +18,7 @@ files=( ) # Check if all files exist -for file in ${files[@]}; do +for file in "${files[@]}"; do if [ ! -f "$file" ]; then echo "Error: $file does not exist. Are you running this script from the root directory?" exit 1 @@ -29,7 +29,7 @@ done uv sync # Remove the existing stub files -for file in ${files[@]}; do +for file in "${files[@]}"; do rm -f "$file" done @@ -43,7 +43,7 @@ python -m nanobind.stubgen -m mqt.core.fomac -o python/mqt/core/fomac.pyi -P python -m nanobind.stubgen -m mqt.core.na.fomac -o python/mqt/core/na/fomac.pyi -P # Remove private Enum members from the stub files -for file in ${files[@]}; do +for file in "${files[@]}"; do if [[ "$OSTYPE" == "darwin"* ]]; then sed -i '' '/_hashable_values_:/d' "$file" sed -i '' '/_unhashable_values_map_:/d' "$file" @@ -54,7 +54,7 @@ for file in ${files[@]}; do done # Add license headers to the generated stub files -for file in ${files[@]}; do +for file in "${files[@]}"; do prek license-tools --files "$file" done From 338bdc44fb8106f4d973b3247e24236650d125a6 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 14 Dec 2025 13:10:44 +0100 Subject: [PATCH 046/144] Fix enum definitions --- bindings/dd/register_dd_package.cpp | 3 +-- bindings/fomac/fomac.cpp | 7 +++---- bindings/ir/operations/register_control.cpp | 3 +-- bindings/ir/operations/register_if_else_operation.cpp | 2 +- bindings/ir/operations/register_optype.cpp | 3 +-- 5 files changed, 7 insertions(+), 11 deletions(-) diff --git a/bindings/dd/register_dd_package.cpp b/bindings/dd/register_dd_package.cpp index adcec0f729..4018bfd3f7 100644 --- a/bindings/dd/register_dd_package.cpp +++ b/bindings/dd/register_dd_package.cpp @@ -202,8 +202,7 @@ Specifically, it The DD for the computational basis state. The resulting state is guaranteed to have its reference count increased.)pb"); - nb::enum_(m, "BasisStates", "enum.Enum", - "Enumeration of basis states.") + nb::enum_(m, "BasisStates", "Enumeration of basis states.") .value("zero", dd::BasisStates::zero, R"pb(The computational basis state :math:`|0\rangle`.)pb") .value("one", dd::BasisStates::one, diff --git a/bindings/fomac/fomac.cpp b/bindings/fomac/fomac.cpp index f86ce68619..d80de8f79d 100644 --- a/bindings/fomac/fomac.cpp +++ b/bindings/fomac/fomac.cpp @@ -173,8 +173,7 @@ All authentication parameters are optional and can be provided as keyword argume nb::sig("def __ne__(self, arg: object, /) -> bool")); // JobStatus enum - nb::enum_(job, "Status", "enum.Enum", - "Enumeration of job status.") + nb::enum_(job, "Status", "Enumeration of job status.") .value("CREATED", QDMI_JOB_STATUS_CREATED) .value("SUBMITTED", QDMI_JOB_STATUS_SUBMITTED) .value("QUEUED", QDMI_JOB_STATUS_QUEUED) @@ -185,7 +184,7 @@ All authentication parameters are optional and can be provided as keyword argume .export_values(); // ProgramFormat enum - nb::enum_(m, "ProgramFormat", "enum.Enum", + nb::enum_(m, "ProgramFormat", "Enumeration of program formats.") .value("QASM2", QDMI_PROGRAM_FORMAT_QASM2) .value("QASM3", QDMI_PROGRAM_FORMAT_QASM3) @@ -209,7 +208,7 @@ All authentication parameters are optional and can be provided as keyword argume "A device represents a quantum device with its properties and " "capabilities."); - nb::enum_(device, "Status", "enum.Enum", + nb::enum_(device, "Status", "Enumeration of device status.") .value("offline", QDMI_DEVICE_STATUS_OFFLINE) .value("idle", QDMI_DEVICE_STATUS_IDLE) diff --git a/bindings/ir/operations/register_control.cpp b/bindings/ir/operations/register_control.cpp index 60bade2438..7c6fb819cd 100644 --- a/bindings/ir/operations/register_control.cpp +++ b/bindings/ir/operations/register_control.cpp @@ -31,8 +31,7 @@ void registerControl(const nb::module_& m) { qubit: The qubit that is the control. type_: The type of the control.)pb"); - nb::enum_(control, "Type", "enum.Enum", - "Enumeration of control types.") + nb::enum_(control, "Type", "Enumeration of control types.") .value("Pos", qc::Control::Type::Pos) .value("Neg", qc::Control::Type::Neg); diff --git a/bindings/ir/operations/register_if_else_operation.cpp b/bindings/ir/operations/register_if_else_operation.cpp index 34f213a319..4b9c0e5387 100644 --- a/bindings/ir/operations/register_if_else_operation.cpp +++ b/bindings/ir/operations/register_if_else_operation.cpp @@ -29,7 +29,7 @@ using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) void registerIfElseOperation(const nb::module_& m) { nb::enum_( - m, "ComparisonKind", "enum.Enum", + m, "ComparisonKind", "Enumeration of comparison types for classic-controlled operations.") .value("eq", qc::ComparisonKind::Eq, "Equality comparison.") .value("neq", qc::ComparisonKind::Neq, "Inequality comparison.") diff --git a/bindings/ir/operations/register_optype.cpp b/bindings/ir/operations/register_optype.cpp index 6a09be69cd..01f9c254d2 100644 --- a/bindings/ir/operations/register_optype.cpp +++ b/bindings/ir/operations/register_optype.cpp @@ -18,8 +18,7 @@ namespace nb = nanobind; // NOLINTNEXTLINE(misc-use-internal-linkage) void registerOptype(const nb::module_& m) { - nb::enum_(m, "OpType", "enum.Enum", - "Enumeration of operation types.") + nb::enum_(m, "OpType", "Enumeration of operation types.") .value("none", qc::OpType::None, R"pb(A placeholder operation. From adb2eda043eda05084e13905b034ed49c532d350 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 14 Dec 2025 13:11:20 +0100 Subject: [PATCH 047/144] Use nb::hash --- bindings/ir/operations/register_operation.cpp | 2 +- bindings/ir/register_registers.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/ir/operations/register_operation.cpp b/bindings/ir/operations/register_operation.cpp index 7bbd6fcb6b..312609856f 100644 --- a/bindings/ir/operations/register_operation.cpp +++ b/bindings/ir/operations/register_operation.cpp @@ -159,7 +159,7 @@ void registerOperation(const nb::module_& m) { nb::sig("def __eq__(self, arg: object, /) -> bool")) .def(nb::self != nb::self, // NOLINT(misc-redundant-expression) nb::sig("def __ne__(self, arg: object, /) -> bool")) - .def(hash(nb::self)) + .def(nb::hash(nb::self)) .def("__repr__", [](const qc::Operation& op) { std::ostringstream oss; diff --git a/bindings/ir/register_registers.cpp b/bindings/ir/register_registers.cpp index 19b86b378c..5b177cc3fe 100644 --- a/bindings/ir/register_registers.cpp +++ b/bindings/ir/register_registers.cpp @@ -61,7 +61,7 @@ void registerRegisters(nb::module_& m) { nb::sig("def __eq__(self, arg: object, /) -> bool")) .def(nb::self != nb::self, // NOLINT(misc-redundant-expression) nb::sig("def __ne__(self, arg: object, /) -> bool")) - .def(hash(nb::self)) + .def(nb::hash(nb::self)) .def("__getitem__", &qc::QuantumRegister::getGlobalIndex, "key"_a, "Get the qubit at the specified index.") From 78ebb46afae0a31c3deb71814976cbc63578288c Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 14 Dec 2025 13:12:00 +0100 Subject: [PATCH 048/144] Call mcrz and not mcrx --- bindings/ir/register_quantum_computation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index 1b3f605628..7869bcfdd4 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -1201,7 +1201,7 @@ See Also: "mcrz", [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, const Controls& controls, - qc::Qubit target) { circ.mcrx(theta, getControls(controls), target); }, + qc::Qubit target) { circ.mcrz(theta, getControls(controls), target); }, "theta"_a, "controls"_a, "target"_a, R"pb(Apply a multi-controlled :math:`R_z(\theta)` gate. From 11c3629f0fe733769f78d8086dc300bb5726fa0e Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 14 Dec 2025 13:13:05 +0100 Subject: [PATCH 049/144] Add nb::keep_alive to Permutation.__iter__() --- bindings/ir/register_permutation.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index f09862a9b8..e6df8aeef2 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -140,13 +140,14 @@ void registerPermutation(nb::module_& m) { .def("__len__", &qc::Permutation::size, "Return the number of indices in the permutation.") - .def("__iter__", - [](const qc::Permutation& p) { - return nb::make_key_iterator( - nb::type(), "key_iterator", p.begin(), - p.end(), - "Return an iterator over the indices of the permutation."); - }) + .def( + "__iter__", + [](const qc::Permutation& p) { + return nb::make_key_iterator( + nb::type(), "key_iterator", p.begin(), p.end(), + "Return an iterator over the indices of the permutation."); + }, + nb::keep_alive<0, 1>()) .def( "items", From 3cd92bd8a324cadfe248c4e64c28ce0733d041de Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 14 Dec 2025 13:15:50 +0100 Subject: [PATCH 050/144] Check for nullptr in CompoundOperation.__init__() --- bindings/ir/operations/register_compound_operation.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bindings/ir/operations/register_compound_operation.cpp b/bindings/ir/operations/register_compound_operation.cpp index 3acc21c941..c38aeead56 100644 --- a/bindings/ir/operations/register_compound_operation.cpp +++ b/bindings/ir/operations/register_compound_operation.cpp @@ -65,6 +65,9 @@ A :class:`CompoundOperation` can contain any number of operations, including oth std::vector> uniqueOps; uniqueOps.reserve(ops.size()); for (const auto& op : ops) { + if (op == nullptr) { + throw std::runtime_error("ops must not contain None."); + } uniqueOps.emplace_back(op->clone()); } new (self) qc::CompoundOperation(std::move(uniqueOps)); From 0c120cd4493cec8f20e5e2e0ffe772e090105d48 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 14 Dec 2025 13:32:50 +0100 Subject: [PATCH 051/144] Improve IfElseOperation bindings --- bindings/ir/operations/register_if_else_operation.cpp | 6 ++++-- python/mqt/core/ir/operations.pyi | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/bindings/ir/operations/register_if_else_operation.cpp b/bindings/ir/operations/register_if_else_operation.cpp index 4b9c0e5387..a6177dbabd 100644 --- a/bindings/ir/operations/register_if_else_operation.cpp +++ b/bindings/ir/operations/register_if_else_operation.cpp @@ -72,7 +72,7 @@ Otherwise, the else operation is executed. ifElse.def( "__init__", [](qc::IfElseOperation* self, qc::Operation* thenOp, - qc::Operation* elseOp, qc::Bit controlBit, std::uint64_t expectedVal, + qc::Operation* elseOp, qc::Bit controlBit, bool expectedVal, qc::ComparisonKind kind) { std::unique_ptr thenPtr = thenOp ? thenOp->clone() : nullptr; @@ -82,7 +82,7 @@ Otherwise, the else operation is executed. controlBit, expectedVal, kind); }, "then_operation"_a, nb::arg("else_operation").none(true), "control_bit"_a, - "expected_value"_a = 1U, "comparison_kind"_a = qc::ComparisonKind::Eq); + "expected_value"_a = true, "comparison_kind"_a = qc::ComparisonKind::Eq); ifElse.def_prop_ro("then_operation", &qc::IfElseOperation::getThenOp, nb::rv_policy::reference_internal, @@ -91,6 +91,8 @@ Otherwise, the else operation is executed. ifElse.def_prop_ro( "else_operation", &qc::IfElseOperation::getElseOp, nb::rv_policy::reference_internal, + nb::sig("def else_operation(self) -> " + "mqt.core.ir.operations.Operation | None"), "The operation that is executed if the condition is not met."); ifElse.def_prop_ro("control_register", diff --git a/python/mqt/core/ir/operations.pyi b/python/mqt/core/ir/operations.pyi index b403b1fcd0..62d4ec30f6 100644 --- a/python/mqt/core/ir/operations.pyi +++ b/python/mqt/core/ir/operations.pyi @@ -937,7 +937,7 @@ class IfElseOperation(Operation): then_operation: Operation, else_operation: Operation | None, control_bit: int, - expected_value: int = 1, + expected_value: bool = True, comparison_kind: ComparisonKind = ComparisonKind.eq, ) -> None: ... @property @@ -945,7 +945,7 @@ class IfElseOperation(Operation): """The operation that is executed if the condition is met.""" @property - def else_operation(self) -> Operation: + def else_operation(self) -> Operation | None: """The operation that is executed if the condition is not met.""" @property From 5fcb397d6fd360ced15b27992ae6470a37673ded Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 14 Dec 2025 13:33:17 +0100 Subject: [PATCH 052/144] Remove print statement --- test/python/dd/test_vector_dds.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/python/dd/test_vector_dds.py b/test/python/dd/test_vector_dds.py index 2871ff1fca..c9e2d5cc4d 100644 --- a/test/python/dd/test_vector_dds.py +++ b/test/python/dd/test_vector_dds.py @@ -96,6 +96,5 @@ def test_from_vector() -> None: vec /= np.linalg.norm(vec) dd = p.from_vector(vec) vec2 = dd.get_vector() - print(vec2) assert np.allclose(vec, vec2) p.dec_ref_vec(dd) From 7f61e6abf87dcc1ec329a4b3dfbe83ee382d83d4 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 14 Dec 2025 13:47:53 +0100 Subject: [PATCH 053/144] Remove dead install() calls --- bindings/CMakeLists.txt | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/bindings/CMakeLists.txt b/bindings/CMakeLists.txt index bd28ed70d0..2de4c5690d 100644 --- a/bindings/CMakeLists.txt +++ b/bindings/CMakeLists.txt @@ -25,9 +25,11 @@ if(SKBUILD_STATE STREQUAL "editable") ir/symbolic.pyi PYTHON_PATH ${CMAKE_INSTALL_PREFIX}/../.. + INCLUDE_PRIVATE MARKER_FILE ir/py.typed - INCLUDE_PRIVATE) + COMPONENT + ${MQT_CORE_TARGET_NAME}_Python) nanobind_add_stub( dd_stub @@ -38,9 +40,11 @@ if(SKBUILD_STATE STREQUAL "editable") dd.pyi PYTHON_PATH ${CMAKE_INSTALL_PREFIX}/../.. + INCLUDE_PRIVATE MARKER_FILE py.typed - INCLUDE_PRIVATE) + COMPONENT + ${MQT_CORE_TARGET_NAME}_Python) nanobind_add_stub( fomac_stub @@ -51,9 +55,11 @@ if(SKBUILD_STATE STREQUAL "editable") fomac.pyi PYTHON_PATH ${CMAKE_INSTALL_PREFIX}/../.. + INCLUDE_PRIVATE MARKER_FILE py.typed - INCLUDE_PRIVATE) + COMPONENT + ${MQT_CORE_TARGET_NAME}_Python) nanobind_add_stub( na_fomac_stub @@ -64,19 +70,9 @@ if(SKBUILD_STATE STREQUAL "editable") na/fomac.pyi PYTHON_PATH ${CMAKE_INSTALL_PREFIX}/../.. + INCLUDE_PRIVATE MARKER_FILE na/py.typed - INCLUDE_PRIVATE) - - file(GLOB_RECURSE PYI_FILES ${CMAKE_INSTALL_PREFIX}/*.pyi) - install( - FILES ${PYI_FILES} - DESTINATION . - COMPONENT ${MQT_CORE_TARGET_NAME}_Python) - - file(GLOB_RECURSE PY_TYPED_FILES ${CMAKE_INSTALL_PREFIX}/py.typed) - install( - FILES ${PY_TYPED_FILES} - DESTINATION . - COMPONENT ${MQT_CORE_TARGET_NAME}_Python) + COMPONENT + ${MQT_CORE_TARGET_NAME}_Python) endif() From 0d3fc39c6bbdc4d986df38df5aaf205279ca5a72 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 14 Dec 2025 16:11:42 +0100 Subject: [PATCH 054/144] Fix more docstrings --- bindings/dd/register_matrix_dds.cpp | 3 +-- .../ir/operations/register_non_unitary_operation.cpp | 9 ++++----- python/mqt/core/dd.pyi | 3 +-- python/mqt/core/ir/operations.pyi | 2 +- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/bindings/dd/register_matrix_dds.cpp b/bindings/dd/register_matrix_dds.cpp index c903c933eb..0ff240f9c4 100644 --- a/bindings/dd/register_matrix_dds.cpp +++ b/bindings/dd/register_matrix_dds.cpp @@ -91,7 +91,7 @@ void registerMatrixDDs(const nb::module_& m) { Args: num_qubits: The number of qubits. - decisions: The decisions as a string of `0`, `1`, `2`, or `3`, where decisions[i]` corresponds to the successor to follow at level `i` of the DD. + decisions: The decisions as a string of `0`, `1`, `2`, or `3`, where `decisions[i]` corresponds to the successor to follow at level `i` of the DD. Must be at least `num_qubits` long. Returns: @@ -154,7 +154,6 @@ Requires the `dot` command from Graphviz to be installed and available in the PA edge_labels: Whether to include edge weights as labels. classic: Whether to use the classic DD visualization style. memory: Whether to include memory information. For debugging purposes only. - show: Whether to open the SVG file in the default browser. format_as_polar: Whether to format the edge weights in polar coordinates.)pb"); } } // namespace mqt diff --git a/bindings/ir/operations/register_non_unitary_operation.cpp b/bindings/ir/operations/register_non_unitary_operation.cpp index 46647290d2..cf096d0d14 100644 --- a/bindings/ir/operations/register_non_unitary_operation.cpp +++ b/bindings/ir/operations/register_non_unitary_operation.cpp @@ -43,11 +43,10 @@ This includes measurements and resets. .def(nb::init, qc::OpType>(), "targets"_a, "op_type"_a = qc::OpType::Reset) - .def_prop_ro( - "classics", - nb::overload_cast<>(&qc::NonUnitaryOperation::getClassics, - nb::const_), - "The classical registers that are associated with the operation.") + .def_prop_ro("classics", + nb::overload_cast<>(&qc::NonUnitaryOperation::getClassics, + nb::const_), + "The classical bits that are associated with the operation.") .def("__repr__", [](const qc::NonUnitaryOperation& op) { std::stringstream ss; diff --git a/python/mqt/core/dd.pyi b/python/mqt/core/dd.pyi index af7de8eeca..a257ee7a55 100644 --- a/python/mqt/core/dd.pyi +++ b/python/mqt/core/dd.pyi @@ -127,7 +127,7 @@ class MatrixDD: Args: num_qubits: The number of qubits. - decisions: The decisions as a string of `0`, `1`, `2`, or `3`, where decisions[i]` corresponds to the successor to follow at level `i` of the DD. + decisions: The decisions as a string of `0`, `1`, `2`, or `3`, where `decisions[i]` corresponds to the successor to follow at level `i` of the DD. Must be at least `num_qubits` long. Returns: @@ -187,7 +187,6 @@ class MatrixDD: edge_labels: Whether to include edge weights as labels. classic: Whether to use the classic DD visualization style. memory: Whether to include memory information. For debugging purposes only. - show: Whether to open the SVG file in the default browser. format_as_polar: Whether to format the edge weights in polar coordinates. """ diff --git a/python/mqt/core/ir/operations.pyi b/python/mqt/core/ir/operations.pyi index 62d4ec30f6..86ec09e48d 100644 --- a/python/mqt/core/ir/operations.pyi +++ b/python/mqt/core/ir/operations.pyi @@ -770,7 +770,7 @@ class NonUnitaryOperation(Operation): def __init__(self, targets: Sequence[int], op_type: OpType = OpType.reset) -> None: ... @property def classics(self) -> list[int]: - """The classical registers that are associated with the operation.""" + """The classical bits that are associated with the operation.""" class SymbolicOperation(StandardOperation): """Symbolic quantum operation. From 1a729f4e78fb67d0e9d649c1a342ac7da2dcc6d2 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 14 Dec 2025 16:17:40 +0100 Subject: [PATCH 055/144] Further improve stub-generation script --- bindings/generate-stubs.sh | 19 ++++++++----------- python/mqt/core/dd.pyi | 4 +--- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/bindings/generate-stubs.sh b/bindings/generate-stubs.sh index 4eb2a81313..4dfeac3d57 100755 --- a/bindings/generate-stubs.sh +++ b/bindings/generate-stubs.sh @@ -34,13 +34,13 @@ for file in "${files[@]}"; do done # Generate new stub files -python -m nanobind.stubgen -m mqt.core.ir -o python/mqt/core/ir/__init__.pyi -P -python -m nanobind.stubgen -m mqt.core.ir.operations -o python/mqt/core/ir/operations.pyi -P -python -m nanobind.stubgen -m mqt.core.ir.registers -o python/mqt/core/ir/registers.pyi -P -python -m nanobind.stubgen -m mqt.core.ir.symbolic -o python/mqt/core/ir/symbolic.pyi -P -python -m nanobind.stubgen -m mqt.core.dd -o python/mqt/core/dd.pyi -P -python -m nanobind.stubgen -m mqt.core.fomac -o python/mqt/core/fomac.pyi -P -python -m nanobind.stubgen -m mqt.core.na.fomac -o python/mqt/core/na/fomac.pyi -P +uv run --no-sync -m nanobind.stubgen -m mqt.core.ir -o python/mqt/core/ir/__init__.pyi -P +uv run --no-sync -m nanobind.stubgen -m mqt.core.ir.operations -o python/mqt/core/ir/operations.pyi -P +uv run --no-sync -m nanobind.stubgen -m mqt.core.ir.registers -o python/mqt/core/ir/registers.pyi -P +uv run --no-sync -m nanobind.stubgen -m mqt.core.ir.symbolic -o python/mqt/core/ir/symbolic.pyi -P +uv run --no-sync -m nanobind.stubgen -m mqt.core.dd -o python/mqt/core/dd.pyi -P +uv run --no-sync -m nanobind.stubgen -m mqt.core.fomac -o python/mqt/core/fomac.pyi -P +uv run --no-sync -m nanobind.stubgen -m mqt.core.na.fomac -o python/mqt/core/na/fomac.pyi -P # Remove private Enum members from the stub files for file in "${files[@]}"; do @@ -58,9 +58,6 @@ for file in "${files[@]}"; do prek license-tools --files "$file" done -# Run ruff twice to ensure all formatting issues are resolved -uvx ruff format +# Run ruff uvx ruff check - uvx ruff format -uvx ruff check diff --git a/python/mqt/core/dd.pyi b/python/mqt/core/dd.pyi index a257ee7a55..971b0f7b95 100644 --- a/python/mqt/core/dd.pyi +++ b/python/mqt/core/dd.pyi @@ -819,9 +819,7 @@ def sample(qc: mqt.core.ir.QuantumComputation, shots: int = 1024, seed: int = 0) Otherwise, all qubits in the circuit are measured. """ -def simulate_statevector( - qc: mqt.core.ir.QuantumComputation, -) -> Annotated[NDArray[np.complex128], {"shape": (None,)}]: +def simulate_statevector(qc: mqt.core.ir.QuantumComputation) -> Annotated[NDArray[np.complex128], {"shape": (None,)}]: """Simulate the quantum computation and return the final state vector. This function classically simulates the quantum computation and returns the state vector of the final state. From 266ec5d5428d5c2f345269572817c5dacfa0aa6c Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 14 Dec 2025 16:23:07 +0100 Subject: [PATCH 056/144] Improve custom Control type --- bindings/dd/register_dd_package.cpp | 7 ++++--- bindings/ir/register_permutation.cpp | 7 ++++--- bindings/ir/register_quantum_computation.cpp | 6 +++--- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/bindings/dd/register_dd_package.cpp b/bindings/dd/register_dd_package.cpp index 4018bfd3f7..da6874d138 100644 --- a/bindings/dd/register_dd_package.cpp +++ b/bindings/dd/register_dd_package.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include // NOLINT(misc-include-cleaner) @@ -48,8 +49,8 @@ using namespace nb::literals; using Vector = nb::ndarray, nb::ndim<1>>; using Matrix = nb::ndarray, nb::ndim<2>>; -using Control = std::variant; -using Controls = std::set>; +using Control = std::variant; +using Controls = std::set>; namespace { @@ -103,7 +104,7 @@ qc::Control getControl(const Control& control) { if (std::holds_alternative(control)) { return std::get(control); } - return static_cast(std::get(control)); + return std::get(control); } /// Helper function to convert Controls variant to qc::Controls diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index e6df8aeef2..c39b7f5f10 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -12,6 +12,7 @@ #include "ir/Permutation.hpp" #include "ir/operations/Control.hpp" +#include #include #include #include @@ -30,8 +31,8 @@ namespace mqt { namespace nb = nanobind; using namespace nb::literals; -using Control = std::variant; -using Controls = std::set>; +using Control = std::variant; +using Controls = std::set>; namespace { @@ -40,7 +41,7 @@ qc::Control getControl(const Control& control) { if (std::holds_alternative(control)) { return std::get(control); } - return static_cast(std::get(control)); + return std::get(control); } /// Helper function to convert Controls variant to qc::Controls diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index 7869bcfdd4..b6160f516f 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -43,8 +43,8 @@ using namespace nb::literals; using DiffType = std::vector>::difference_type; using SizeType = std::vector>::size_type; -using Control = std::variant; -using Controls = std::set>; +using Control = std::variant; +using Controls = std::set>; namespace { @@ -53,7 +53,7 @@ qc::Control getControl(const Control& control) { if (std::holds_alternative(control)) { return std::get(control); } - return static_cast(std::get(control)); + return std::get(control); } /// Helper function to convert Controls variant to qc::Controls From 011c986fe03b15d89fa6cb2f234ef078bb3a0308 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 14 Dec 2025 16:35:39 +0100 Subject: [PATCH 057/144] Simplify Expression constructor --- bindings/ir/symbolic/register_expression.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/bindings/ir/symbolic/register_expression.cpp b/bindings/ir/symbolic/register_expression.cpp index 21acd1264c..386c2cb462 100644 --- a/bindings/ir/symbolic/register_expression.cpp +++ b/bindings/ir/symbolic/register_expression.cpp @@ -40,13 +40,8 @@ Alternatively, an expression can be created with a single term and a constant or constant: The constant.)pb") .def(nb::init(), "constant"_a = 0.0) - .def( - "__init__", - [](sym::Expression* self, - const std::vector>& terms, double constant) { - new (self) sym::Expression(terms, constant); - }, - "terms"_a, "constant"_a = 0.0) + .def(nb::init>&, double>(), "terms"_a, + "constant"_a = 0.0) .def( "__init__", [](sym::Expression* self, From 52e02bf4e26851ce26ccd18c1a1f914e05262479 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 14 Dec 2025 16:39:18 +0100 Subject: [PATCH 058/144] Always mark nb::module_ as const --- bindings/ir/register_ir.cpp | 10 +++++----- bindings/ir/register_operations.cpp | 2 +- bindings/ir/register_permutation.cpp | 2 +- bindings/ir/register_quantum_computation.cpp | 2 +- bindings/ir/register_registers.cpp | 2 +- bindings/ir/register_symbolic.cpp | 8 ++++---- bindings/ir/symbolic/register_expression.cpp | 2 +- bindings/ir/symbolic/register_term.cpp | 2 +- bindings/ir/symbolic/register_variable.cpp | 2 +- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/bindings/ir/register_ir.cpp b/bindings/ir/register_ir.cpp index 0aec88fe32..814df6edb9 100644 --- a/bindings/ir/register_ir.cpp +++ b/bindings/ir/register_ir.cpp @@ -15,11 +15,11 @@ namespace mqt { namespace nb = nanobind; // forward declarations -void registerRegisters(nb::module_& m); -void registerPermutation(nb::module_& m); -void registerOperations(nb::module_& m); -void registerSymbolic(nb::module_& m); -void registerQuantumComputation(nb::module_& m); +void registerRegisters(const nb::module_& m); +void registerPermutation(const nb::module_& m); +void registerOperations(const nb::module_& m); +void registerSymbolic(const nb::module_& m); +void registerQuantumComputation(const nb::module_& m); NB_MODULE(MQT_CORE_MODULE_NAME, m) { registerPermutation(m); diff --git a/bindings/ir/register_operations.cpp b/bindings/ir/register_operations.cpp index 4fc8e3fe6f..48c41fbd0e 100644 --- a/bindings/ir/register_operations.cpp +++ b/bindings/ir/register_operations.cpp @@ -25,7 +25,7 @@ void registerSymbolicOperation(const nb::module_& m); void registerIfElseOperation(const nb::module_& m); // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerOperations(nb::module_& m) { +void registerOperations(const nb::module_& m) { registerOptype(m); registerControl(m); registerOperation(m); diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index c39b7f5f10..d241eccf59 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -56,7 +56,7 @@ qc::Controls getControls(const Controls& controls) { } // namespace // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerPermutation(nb::module_& m) { +void registerPermutation(const nb::module_& m) { nb::class_( m, "Permutation", nb::sig("class Permutation(collections.abc.MutableMapping[int, int])"), diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index b6160f516f..a46bccd7d1 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -68,7 +68,7 @@ qc::Controls getControls(const Controls& controls) { } // namespace // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerQuantumComputation(nb::module_& m) { +void registerQuantumComputation(const nb::module_& m) { auto wrap = [](DiffType i, const SizeType size) { if (i < 0) { i += static_cast(size); diff --git a/bindings/ir/register_registers.cpp b/bindings/ir/register_registers.cpp index 5b177cc3fe..af195fa797 100644 --- a/bindings/ir/register_registers.cpp +++ b/bindings/ir/register_registers.cpp @@ -23,7 +23,7 @@ namespace nb = nanobind; using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerRegisters(nb::module_& m) { +void registerRegisters(const nb::module_& m) { nb::class_( m, "QuantumRegister", R"pb(A class to represent a collection of qubits. diff --git a/bindings/ir/register_symbolic.cpp b/bindings/ir/register_symbolic.cpp index 7935b7bf3a..cd8eefe5f5 100644 --- a/bindings/ir/register_symbolic.cpp +++ b/bindings/ir/register_symbolic.cpp @@ -15,12 +15,12 @@ namespace mqt { namespace nb = nanobind; // forward declarations -void registerVariable(nb::module_& m); -void registerTerm(nb::module_& m); -void registerExpression(nb::module_& m); +void registerVariable(const nb::module_& m); +void registerTerm(const nb::module_& m); +void registerExpression(const nb::module_& m); // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerSymbolic(nb::module_& m) { +void registerSymbolic(const nb::module_& m) { registerVariable(m); registerTerm(m); registerExpression(m); diff --git a/bindings/ir/symbolic/register_expression.cpp b/bindings/ir/symbolic/register_expression.cpp index 386c2cb462..189fd9b73d 100644 --- a/bindings/ir/symbolic/register_expression.cpp +++ b/bindings/ir/symbolic/register_expression.cpp @@ -27,7 +27,7 @@ namespace nb = nanobind; using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerExpression(nb::module_& m) { +void registerExpression(const nb::module_& m) { nb::class_>( m, "Expression", R"pb(A symbolic expression which consists of a sum of terms and a constant. diff --git a/bindings/ir/symbolic/register_term.cpp b/bindings/ir/symbolic/register_term.cpp index f3ed571435..e377fd78f6 100644 --- a/bindings/ir/symbolic/register_term.cpp +++ b/bindings/ir/symbolic/register_term.cpp @@ -22,7 +22,7 @@ namespace nb = nanobind; using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerTerm(nb::module_& m) { +void registerTerm(const nb::module_& m) { nb::class_>( m, "Term", R"pb(A symbolic term which consists of a variable with a given coefficient. diff --git a/bindings/ir/symbolic/register_variable.cpp b/bindings/ir/symbolic/register_variable.cpp index b8620f606e..9c95e8503f 100644 --- a/bindings/ir/symbolic/register_variable.cpp +++ b/bindings/ir/symbolic/register_variable.cpp @@ -21,7 +21,7 @@ namespace nb = nanobind; using namespace nb::literals; // NOLINTNEXTLINE(misc-use-internal-linkage) -void registerVariable(nb::module_& m) { +void registerVariable(const nb::module_& m) { nb::class_(m, "Variable", R"pb(A symbolic 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. From a0ccd144cf342e66fe344046d9c3c726d2f027d9 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 14 Dec 2025 16:58:16 +0100 Subject: [PATCH 059/144] Fix implementations of __getitem__ --- bindings/dd/register_vector_dds.cpp | 13 ++++++-- .../register_compound_operation.cpp | 6 ++-- bindings/ir/register_quantum_computation.cpp | 6 ++-- bindings/ir/register_registers.cpp | 30 ++++++++++++++++--- bindings/ir/symbolic/register_expression.cpp | 11 ++++--- python/mqt/core/dd.pyi | 2 +- 6 files changed, 52 insertions(+), 16 deletions(-) diff --git a/bindings/dd/register_vector_dds.cpp b/bindings/dd/register_vector_dds.cpp index da9f6f4cf8..d802d18e73 100644 --- a/bindings/dd/register_vector_dds.cpp +++ b/bindings/dd/register_vector_dds.cpp @@ -61,10 +61,17 @@ void registerVectorDDs(const nb::module_& m) { vec.def( "__getitem__", - [](const dd::vEdge& v, const size_t idx) { - return v.getValueByIndex(idx); + [](const dd::vEdge& v, nb::ssize_t idx) { + const auto n = static_cast(v.size()); + if (idx < 0) { + idx += n; + } + if (idx < 0 || idx >= n) { + throw nb::index_error(); + } + return v.getValueByIndex(static_cast(idx)); }, - "index"_a, "Get the amplitude of a basis state by index."); + "key"_a, "Get the amplitude of a basis state by index."); vec.def( "get_amplitude", diff --git a/bindings/ir/operations/register_compound_operation.cpp b/bindings/ir/operations/register_compound_operation.cpp index c38aeead56..e16b263987 100644 --- a/bindings/ir/operations/register_compound_operation.cpp +++ b/bindings/ir/operations/register_compound_operation.cpp @@ -101,8 +101,10 @@ A :class:`CompoundOperation` can contain any number of operations, including oth auto [start, stop, step, sliceLength] = slice.compute(op.size()); auto ops = std::vector(); ops.reserve(sliceLength); - for (auto i = start; i < stop; i += step) { - ops.emplace_back(op.at(static_cast(i)).get()); + for (std::size_t i = 0; i < sliceLength; ++i) { + auto idx = static_cast(start) + + static_cast(i) * step; + ops.emplace_back(op.at(static_cast(idx)).get()); } return ops; }, diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index a46bccd7d1..aac2807195 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -218,8 +218,10 @@ When the quantum computation contains more than one operation, the resulting ope auto [start, stop, step, sliceLength] = slice.compute(circ.getNops()); auto ops = std::vector(); ops.reserve(sliceLength); - for (auto i = start; i < stop; i += step) { - ops.emplace_back(circ.at(static_cast(i)).get()); + for (std::size_t i = 0; i < sliceLength; ++i) { + auto idx = + static_cast(start) + static_cast(i) * step; + ops.emplace_back(circ.at(static_cast(idx)).get()); } return ops; }, diff --git a/bindings/ir/register_registers.cpp b/bindings/ir/register_registers.cpp index af195fa797..e35a4c6f9e 100644 --- a/bindings/ir/register_registers.cpp +++ b/bindings/ir/register_registers.cpp @@ -63,8 +63,19 @@ void registerRegisters(const nb::module_& m) { nb::sig("def __ne__(self, arg: object, /) -> bool")) .def(nb::hash(nb::self)) - .def("__getitem__", &qc::QuantumRegister::getGlobalIndex, "key"_a, - "Get the qubit at the specified index.") + .def( + "__getitem__", + [](const qc::QuantumRegister& reg, nb::ssize_t idx) { + const auto n = static_cast(reg.getSize()); + if (idx < 0) { + idx += n; + } + if (idx < 0 || idx >= n) { + throw nb::index_error(); + } + return reg.getGlobalIndex(static_cast(idx)); + }, + "key"_a, "Get the qubit at the specified index.") .def("__contains__", &qc::QuantumRegister::contains, "item"_a, "Check if the quantum register contains a qubit.") @@ -121,8 +132,19 @@ void registerRegisters(const nb::module_& m) { nb::sig("def __ne__(self, arg: object, /) -> bool")) .def(nb::hash(nb::self)) - .def("__getitem__", &qc::ClassicalRegister::getGlobalIndex, "key"_a, - "Get the bit at the specified index.") + .def( + "__getitem__", + [](const qc::ClassicalRegister& reg, nb::ssize_t idx) { + auto n = static_cast(reg.getSize()); + if (idx < 0) { + idx += n; + } + if (idx < 0 || idx >= n) { + throw nb::index_error(); + } + return reg.getGlobalIndex(static_cast(idx)); + }, + "key"_a, "Get the bit at the specified index.") .def("__contains__", &qc::ClassicalRegister::contains, "item"_a, "Check if the classical register contains a bit.") diff --git a/bindings/ir/symbolic/register_expression.cpp b/bindings/ir/symbolic/register_expression.cpp index 189fd9b73d..7baa9681a8 100644 --- a/bindings/ir/symbolic/register_expression.cpp +++ b/bindings/ir/symbolic/register_expression.cpp @@ -65,12 +65,15 @@ Alternatively, an expression can be created with a single term and a constant or .def( "__getitem__", - [](const sym::Expression& expr, - const std::size_t idx) { - if (idx >= expr.numTerms()) { + [](const sym::Expression& expr, nb::ssize_t idx) { + const auto n = static_cast(expr.numTerms()); + if (idx < 0) { + idx += n; + } + if (idx < 0 || idx >= n) { throw nb::index_error(); } - return expr.getTerms()[idx]; + return expr.getTerms()[static_cast(idx)]; }, "key"_a) diff --git a/python/mqt/core/dd.pyi b/python/mqt/core/dd.pyi index 971b0f7b95..8cb302563c 100644 --- a/python/mqt/core/dd.pyi +++ b/python/mqt/core/dd.pyi @@ -29,7 +29,7 @@ class VectorDD: def size(self) -> int: """Get the size of the DD by traversing it once.""" - def __getitem__(self, index: int) -> complex: + def __getitem__(self, key: int) -> complex: """Get the amplitude of a basis state by index.""" def get_amplitude(self, num_qubits: int, decisions: str) -> complex: From e5b62f4f151791eec635eca584dce74261c9ff07 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Mon, 15 Dec 2025 01:42:52 +0100 Subject: [PATCH 060/144] Configure stub-generation script to fail on errors --- bindings/generate-stubs.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bindings/generate-stubs.sh b/bindings/generate-stubs.sh index 4dfeac3d57..6b16fdeecb 100755 --- a/bindings/generate-stubs.sh +++ b/bindings/generate-stubs.sh @@ -7,6 +7,8 @@ # # Licensed under the MIT License +set -euo pipefail + files=( "./python/mqt/core/ir/__init__.pyi" "./python/mqt/core/ir/operations.pyi" @@ -53,6 +55,8 @@ for file in "${files[@]}"; do fi done +set +e + # Add license headers to the generated stub files for file in "${files[@]}"; do prek license-tools --files "$file" From f5f2cdec1278a555f70202b4f06be358f9ff1c45 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Mon, 15 Dec 2025 02:42:38 +0100 Subject: [PATCH 061/144] Fix linter errors --- bindings/dd/register_dd_package.cpp | 1 - bindings/ir/operations/register_compound_operation.cpp | 2 +- bindings/ir/register_ir.cpp | 6 +++--- bindings/ir/register_quantum_computation.cpp | 2 +- bindings/ir/register_registers.cpp | 4 ++-- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/bindings/dd/register_dd_package.cpp b/bindings/dd/register_dd_package.cpp index da6874d138..16f7df49c8 100644 --- a/bindings/dd/register_dd_package.cpp +++ b/bindings/dd/register_dd_package.cpp @@ -14,7 +14,6 @@ #include "dd/Operations.hpp" #include "dd/Package.hpp" #include "dd/StateGeneration.hpp" -#include "ir/Definitions.hpp" #include "ir/Permutation.hpp" #include "ir/operations/Control.hpp" #include "ir/operations/IfElseOperation.hpp" diff --git a/bindings/ir/operations/register_compound_operation.cpp b/bindings/ir/operations/register_compound_operation.cpp index e16b263987..1d7fd94673 100644 --- a/bindings/ir/operations/register_compound_operation.cpp +++ b/bindings/ir/operations/register_compound_operation.cpp @@ -103,7 +103,7 @@ A :class:`CompoundOperation` can contain any number of operations, including oth ops.reserve(sliceLength); for (std::size_t i = 0; i < sliceLength; ++i) { auto idx = static_cast(start) + - static_cast(i) * step; + (static_cast(i) * step); ops.emplace_back(op.at(static_cast(idx)).get()); } return ops; diff --git a/bindings/ir/register_ir.cpp b/bindings/ir/register_ir.cpp index 814df6edb9..084006b3b9 100644 --- a/bindings/ir/register_ir.cpp +++ b/bindings/ir/register_ir.cpp @@ -24,13 +24,13 @@ void registerQuantumComputation(const nb::module_& m); NB_MODULE(MQT_CORE_MODULE_NAME, m) { registerPermutation(m); - nb::module_ symbolic = m.def_submodule("symbolic"); + const nb::module_ symbolic = m.def_submodule("symbolic"); registerSymbolic(symbolic); - nb::module_ registers = m.def_submodule("registers"); + const nb::module_ registers = m.def_submodule("registers"); registerRegisters(registers); - nb::module_ operations = m.def_submodule("operations"); + const nb::module_ operations = m.def_submodule("operations"); registerOperations(operations); registerQuantumComputation(m); diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index aac2807195..9bc968363f 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -220,7 +220,7 @@ When the quantum computation contains more than one operation, the resulting ope ops.reserve(sliceLength); for (std::size_t i = 0; i < sliceLength; ++i) { auto idx = - static_cast(start) + static_cast(i) * step; + static_cast(start) + (static_cast(i) * step); ops.emplace_back(circ.at(static_cast(idx)).get()); } return ops; diff --git a/bindings/ir/register_registers.cpp b/bindings/ir/register_registers.cpp index e35a4c6f9e..f0b90f09b2 100644 --- a/bindings/ir/register_registers.cpp +++ b/bindings/ir/register_registers.cpp @@ -73,7 +73,7 @@ void registerRegisters(const nb::module_& m) { if (idx < 0 || idx >= n) { throw nb::index_error(); } - return reg.getGlobalIndex(static_cast(idx)); + return reg.getGlobalIndex(static_cast(idx)); }, "key"_a, "Get the qubit at the specified index.") @@ -142,7 +142,7 @@ void registerRegisters(const nb::module_& m) { if (idx < 0 || idx >= n) { throw nb::index_error(); } - return reg.getGlobalIndex(static_cast(idx)); + return reg.getGlobalIndex(static_cast(idx)); }, "key"_a, "Get the bit at the specified index.") From 0bfd882eb49e2f136a66ee0d2ff47827fe518def Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Mon, 15 Dec 2025 02:55:11 +0100 Subject: [PATCH 062/144] Fix __delitem__() implementations --- .../operations/register_compound_operation.cpp | 17 +++++++++++------ bindings/ir/register_quantum_computation.cpp | 17 +++++++++++------ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/bindings/ir/operations/register_compound_operation.cpp b/bindings/ir/operations/register_compound_operation.cpp index 1d7fd94673..f83068832f 100644 --- a/bindings/ir/operations/register_compound_operation.cpp +++ b/bindings/ir/operations/register_compound_operation.cpp @@ -11,6 +11,7 @@ #include "ir/operations/CompoundOperation.hpp" #include "ir/operations/Operation.hpp" +#include #include #include #include @@ -172,12 +173,16 @@ A :class:`CompoundOperation` can contain any number of operations, including oth "__delitem__", [](qc::CompoundOperation& op, const nb::slice& slice) { auto [start, stop, step, sliceLength] = slice.compute(op.size()); - // delete in reverse order to not invalidate indices - for (std::size_t i = sliceLength; i > 0; --i) { - const auto offset = static_cast( - static_cast(start) + - ((i - 1) * static_cast(step))); - op.erase(op.begin() + offset); + // Delete in reverse order to not invalidate indices + std::vector indices; + indices.reserve(sliceLength); + for (std::size_t i = 0; i < sliceLength; ++i) { + indices.emplace_back(static_cast(start) + + static_cast(i) * step); + } + std::sort(indices.begin(), indices.end(), std::greater()); + for (const auto idx : indices) { + op.erase(op.begin() + idx); } }, "index"_a, R"pb(Delete the operations in the given slice. diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index 9bc968363f..dd0afde2c8 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -17,6 +17,7 @@ #include "ir/operations/Operation.hpp" #include "qasm3/Importer.hpp" +#include #include #include #include @@ -288,12 +289,16 @@ When the quantum computation contains more than one operation, the resulting ope "__delitem__", [](qc::QuantumComputation& circ, const nb::slice& slice) { auto [start, stop, step, sliceLength] = slice.compute(circ.getNops()); - // delete in reverse order to not invalidate indices - for (std::size_t i = sliceLength; i > 0; --i) { - const auto offset = - static_cast(static_cast(start) + - ((i - 1) * static_cast(step))); - circ.erase(circ.begin() + offset); + // Delete in reverse order to not invalidate indices + std::vector indices; + indices.reserve(sliceLength); + for (std::size_t i = 0; i < sliceLength; ++i) { + indices.emplace_back(static_cast(start) + + static_cast(i) * step); + } + std::sort(indices.begin(), indices.end(), std::greater()); + for (const auto idx : indices) { + circ.erase(circ.begin() + idx); } }, "index"_a, R"pb(Delete the operations in the given slice. From 6750774e33bcd3298c4502777fb154b5426427d0 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Mon, 15 Dec 2025 03:08:13 +0100 Subject: [PATCH 063/144] Fix function name --- bindings/ir/register_quantum_computation.cpp | 2 +- python/mqt/core/ir/__init__.pyi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index dd0afde2c8..78094a4bd5 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -448,7 +448,7 @@ If the circuit contains measurements at the end, these measurements are used to Args: q: The index of the circuit qubit to set as ancillary.)pb"); - qc.def("se_circuit_qubits_ancillary", + qc.def("set_circuit_qubits_ancillary", &qc::QuantumComputation::setLogicalQubitsAncillary, "q_min"_a, "q_max"_a, R"pb(Set a range of circuit (i.e., logical) qubits to be ancillary. diff --git a/python/mqt/core/ir/__init__.pyi b/python/mqt/core/ir/__init__.pyi index 9a263acf5d..757bbef393 100644 --- a/python/mqt/core/ir/__init__.pyi +++ b/python/mqt/core/ir/__init__.pyi @@ -396,7 +396,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): q: The index of the circuit qubit to set as ancillary. """ - def se_circuit_qubits_ancillary(self, q_min: int, q_max: int) -> None: + def set_circuit_qubits_ancillary(self, q_min: int, q_max: int) -> None: """Set a range of circuit (i.e., logical) qubits to be ancillary. Args: From 5119eaba88af6627e6345c56860875e1da378056 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Mon, 15 Dec 2025 03:10:43 +0100 Subject: [PATCH 064/144] Improve Control helpers --- bindings/dd/register_dd_package.cpp | 4 ++-- bindings/ir/register_permutation.cpp | 4 ++-- bindings/ir/register_quantum_computation.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bindings/dd/register_dd_package.cpp b/bindings/dd/register_dd_package.cpp index 16f7df49c8..7d6dd379b9 100644 --- a/bindings/dd/register_dd_package.cpp +++ b/bindings/dd/register_dd_package.cpp @@ -49,7 +49,7 @@ using Vector = nb::ndarray, nb::ndim<1>>; using Matrix = nb::ndarray, nb::ndim<2>>; using Control = std::variant; -using Controls = std::set>; +using Controls = std::set; namespace { @@ -103,7 +103,7 @@ qc::Control getControl(const Control& control) { if (std::holds_alternative(control)) { return std::get(control); } - return std::get(control); + return static_cast(std::get(control)); } /// Helper function to convert Controls variant to qc::Controls diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index d241eccf59..7b61e53fb4 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -32,7 +32,7 @@ namespace nb = nanobind; using namespace nb::literals; using Control = std::variant; -using Controls = std::set>; +using Controls = std::set; namespace { @@ -41,7 +41,7 @@ qc::Control getControl(const Control& control) { if (std::holds_alternative(control)) { return std::get(control); } - return std::get(control); + return static_cast(std::get(control)); } /// Helper function to convert Controls variant to qc::Controls diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index 78094a4bd5..1de216a51e 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -45,7 +45,7 @@ using DiffType = std::vector>::difference_type; using SizeType = std::vector>::size_type; using Control = std::variant; -using Controls = std::set>; +using Controls = std::set; namespace { @@ -54,7 +54,7 @@ qc::Control getControl(const Control& control) { if (std::holds_alternative(control)) { return std::get(control); } - return std::get(control); + return static_cast(std::get(control)); } /// Helper function to convert Controls variant to qc::Controls From 2002e4624aa5b96059751373b3fa2d78174220f9 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Mon, 15 Dec 2025 03:33:39 +0100 Subject: [PATCH 065/144] Fix linter errors --- bindings/ir/operations/register_compound_operation.cpp | 5 +++-- bindings/ir/register_quantum_computation.cpp | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/bindings/ir/operations/register_compound_operation.cpp b/bindings/ir/operations/register_compound_operation.cpp index f83068832f..de499fa37f 100644 --- a/bindings/ir/operations/register_compound_operation.cpp +++ b/bindings/ir/operations/register_compound_operation.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include // NOLINT(misc-include-cleaner) @@ -178,9 +179,9 @@ A :class:`CompoundOperation` can contain any number of operations, including oth indices.reserve(sliceLength); for (std::size_t i = 0; i < sliceLength; ++i) { indices.emplace_back(static_cast(start) + - static_cast(i) * step); + (static_cast(i) * step)); } - std::sort(indices.begin(), indices.end(), std::greater()); + std::ranges::sort(indices.begin(), indices.end(), std::greater<>()); for (const auto idx : indices) { op.erase(op.begin() + idx); } diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index 1de216a51e..523f53846a 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include // NOLINT(misc-include-cleaner) @@ -294,9 +295,9 @@ When the quantum computation contains more than one operation, the resulting ope indices.reserve(sliceLength); for (std::size_t i = 0; i < sliceLength; ++i) { indices.emplace_back(static_cast(start) + - static_cast(i) * step); + (static_cast(i) * step)); } - std::sort(indices.begin(), indices.end(), std::greater()); + std::ranges::sort(indices.begin(), indices.end(), std::greater<>()); for (const auto idx : indices) { circ.erase(circ.begin() + idx); } From ea6ed14bbbbb2db27d165c0dae341af6b2949df4 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 00:10:40 +0100 Subject: [PATCH 066/144] Verify Operations lists --- bindings/ir/operations/register_compound_operation.cpp | 6 +++--- bindings/ir/register_quantum_computation.cpp | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/bindings/ir/operations/register_compound_operation.cpp b/bindings/ir/operations/register_compound_operation.cpp index de499fa37f..33074a3cd5 100644 --- a/bindings/ir/operations/register_compound_operation.cpp +++ b/bindings/ir/operations/register_compound_operation.cpp @@ -12,6 +12,7 @@ #include "ir/operations/Operation.hpp" #include +#include #include #include #include @@ -67,9 +68,7 @@ A :class:`CompoundOperation` can contain any number of operations, including oth std::vector> uniqueOps; uniqueOps.reserve(ops.size()); for (const auto& op : ops) { - if (op == nullptr) { - throw std::runtime_error("ops must not contain None."); - } + assert(op != nullptr && "ops must not contain nullptr"); uniqueOps.emplace_back(op->clone()); } new (self) qc::CompoundOperation(std::move(uniqueOps)); @@ -146,6 +145,7 @@ A :class:`CompoundOperation` can contain any number of operations, including oth "Length of slice and number of operations do not match."); } for (std::size_t i = 0; i < sliceLength; ++i) { + assert(ops[i] != nullptr && "ops must not contain nullptr"); compOp[static_cast(start)] = ops[i]->clone(); start += step; } diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index 523f53846a..7833f010b0 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -18,6 +18,7 @@ #include "qasm3/Importer.hpp" #include +#include #include #include #include @@ -262,6 +263,7 @@ When the quantum computation contains more than one operation, the resulting ope "Length of slice and number of operations do not match."); } for (std::size_t i = 0; i < sliceLength; ++i) { + assert(ops[i] != nullptr && "ops must not contain nullptr"); circ.at(static_cast(start)) = ops[i]->clone(); start += step; } From 7d251092c4558a8e1c1c9a51a37419106a3317c6 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 00:13:08 +0100 Subject: [PATCH 067/144] Make qubit and type_ read-only --- bindings/ir/operations/register_control.cpp | 4 ++-- python/mqt/core/ir/operations.pyi | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/bindings/ir/operations/register_control.cpp b/bindings/ir/operations/register_control.cpp index 7c6fb819cd..9c3f868f93 100644 --- a/bindings/ir/operations/register_control.cpp +++ b/bindings/ir/operations/register_control.cpp @@ -38,10 +38,10 @@ void registerControl(const nb::module_& m) { control.def(nb::init(), "qubit"_a, "type_"_a.sig("...") = qc::Control::Type::Pos); - control.def_rw("qubit", &qc::Control::qubit, + control.def_ro("qubit", &qc::Control::qubit, "The qubit that is the control."); - control.def_rw("type_", &qc::Control::type, "The type of the control."); + control.def_ro("type_", &qc::Control::type, "The type of the control."); control.def("__str__", [](const qc::Control& c) { return c.toString(); }); control.def("__repr__", [](const qc::Control& c) { return c.toString(); }); diff --git a/python/mqt/core/ir/operations.pyi b/python/mqt/core/ir/operations.pyi index 86ec09e48d..a7db8b01ee 100644 --- a/python/mqt/core/ir/operations.pyi +++ b/python/mqt/core/ir/operations.pyi @@ -442,14 +442,10 @@ class Control: def qubit(self) -> int: """The qubit that is the control.""" - @qubit.setter - def qubit(self, arg: int, /) -> None: ... @property def type_(self) -> Control.Type: """The type of the control.""" - @type_.setter - def type_(self, arg: Control.Type, /) -> None: ... def __eq__(self, arg: object, /) -> bool: ... def __ne__(self, arg: object, /) -> bool: ... def __hash__(self) -> int: ... From b8b353252eead6fe62a8852a3e24ed335ab75d5b Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 00:17:47 +0100 Subject: [PATCH 068/144] Limit num_qubits in getMatrix() --- bindings/dd/register_matrix_dds.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bindings/dd/register_matrix_dds.cpp b/bindings/dd/register_matrix_dds.cpp index 0ff240f9c4..71d1e72067 100644 --- a/bindings/dd/register_matrix_dds.cpp +++ b/bindings/dd/register_matrix_dds.cpp @@ -35,6 +35,10 @@ using Matrix = nb::ndarray, nb::ndim<2>>; // NOLINTNEXTLINE(misc-use-internal-linkage) Matrix getMatrix(const dd::mEdge& m, const size_t numQubits, const dd::fp threshold = 0.) { + if (numQubits > 20U) { + throw nb::value_error("num_qubits exceeds practical limit of 20"); + } + if (numQubits == 0U) { auto dataPtr = std::make_unique>(m.w); auto* data = dataPtr.release(); From fb9b79fdeb1adcdd25c85ce4f38962989b3090f8 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 00:22:35 +0100 Subject: [PATCH 069/144] Make Permutation unhashable --- bindings/ir/register_permutation.cpp | 1 - python/mqt/core/ir/__init__.pyi | 1 - 2 files changed, 2 deletions(-) diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index 7b61e53fb4..e940163245 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -165,7 +165,6 @@ void registerPermutation(const nb::module_& m) { nb::sig("def __eq__(self, arg: object, /) -> bool")) .def(nb::self != nb::self, // NOLINT(misc-redundant-expression) nb::sig("def __ne__(self, arg: object, /) -> bool")) - .def(nb::hash(nb::self)) .def("__str__", [](const qc::Permutation& p) { diff --git a/python/mqt/core/ir/__init__.pyi b/python/mqt/core/ir/__init__.pyi index 757bbef393..c6a5b84169 100644 --- a/python/mqt/core/ir/__init__.pyi +++ b/python/mqt/core/ir/__init__.pyi @@ -84,7 +84,6 @@ class Permutation(MutableMapping[int, int]): def items(self) -> ItemsView[int, int]: ... def __eq__(self, arg: object, /) -> bool: ... def __ne__(self, arg: object, /) -> bool: ... - def __hash__(self) -> int: ... class QuantumComputation(MutableSequence[operations.Operation]): """The main class for representing quantum computations within the MQT. From 3b0360fc187bda887def0c165bbd15afcccc5a2c Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 00:39:18 +0100 Subject: [PATCH 070/144] Fix symbolic right divisions --- bindings/ir/symbolic/register_expression.cpp | 2 -- bindings/ir/symbolic/register_term.cpp | 4 +++- python/mqt/core/ir/symbolic.pyi | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/bindings/ir/symbolic/register_expression.cpp b/bindings/ir/symbolic/register_expression.cpp index 7baa9681a8..12f3fa0d78 100644 --- a/bindings/ir/symbolic/register_expression.cpp +++ b/bindings/ir/symbolic/register_expression.cpp @@ -126,8 +126,6 @@ Alternatively, an expression can be created with a single term and a constant or .def(double() * nb::self) // division operators .def(nb::self / double()) - .def("__rtruediv__", [](const sym::Expression& rhs, - double lhs) { return rhs / lhs; }) // comparison operators .def(nb::self == nb::self, // NOLINT(misc-redundant-expression) nb::sig("def __eq__(self, arg: object, /) -> bool")) diff --git a/bindings/ir/symbolic/register_term.cpp b/bindings/ir/symbolic/register_term.cpp index e377fd78f6..eb2634cbf5 100644 --- a/bindings/ir/symbolic/register_term.cpp +++ b/bindings/ir/symbolic/register_term.cpp @@ -61,7 +61,9 @@ void registerTerm(const nb::module_& m) { .def(nb::self * double()) .def(double() * nb::self) .def(nb::self / double()) - .def(double() / nb::self) + .def("__rtruediv__", + [](const sym::Term& lhs, double rhs) { return rhs / lhs; }) + .def(nb::self == nb::self, // NOLINT(misc-redundant-expression) nb::sig("def __eq__(self, arg: object, /) -> bool")) .def(nb::self != nb::self, // NOLINT(misc-redundant-expression) diff --git a/python/mqt/core/ir/symbolic.pyi b/python/mqt/core/ir/symbolic.pyi index 8b02920cb0..877d43427d 100644 --- a/python/mqt/core/ir/symbolic.pyi +++ b/python/mqt/core/ir/symbolic.pyi @@ -151,7 +151,6 @@ class Expression: def __mul__(self, arg: float, /) -> Expression: ... def __rmul__(self, arg: float, /) -> Expression: ... def __truediv__(self, arg: float, /) -> Expression: ... - def __rtruediv__(self, arg: float, /) -> Expression: ... def __eq__(self, arg: object, /) -> bool: ... def __ne__(self, arg: object, /) -> bool: ... def __hash__(self) -> int: ... From b5793f34b6b3c0ce01108b352886720ed6cb47d9 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 00:40:15 +0100 Subject: [PATCH 071/144] Add nb::keep_alive to Device.submit_job() --- bindings/fomac/fomac.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bindings/fomac/fomac.cpp b/bindings/fomac/fomac.cpp index d80de8f79d..ad3c0aed67 100644 --- a/bindings/fomac/fomac.cpp +++ b/bindings/fomac/fomac.cpp @@ -276,7 +276,8 @@ All authentication parameters are optional and can be provided as keyword argume "Returns the list of program formats supported by the device."); device.def("submit_job", &fomac::Session::Device::submitJob, "program"_a, - "program_format"_a, "num_shots"_a, "Submits a job to the device."); + "program_format"_a, "num_shots"_a, nb::keep_alive<0, 1>(), + "Submits a job to the device."); device.def("__repr__", [](const fomac::Session::Device& dev) { return ""; From ccc036d3f361122eb1dfbd9bb04be016300c5fe8 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 00:47:23 +0100 Subject: [PATCH 072/144] Fix capitalization of Device.Status members --- bindings/fomac/fomac.cpp | 12 ++++++------ python/mqt/core/fomac.pyi | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/bindings/fomac/fomac.cpp b/bindings/fomac/fomac.cpp index ad3c0aed67..6138d2a130 100644 --- a/bindings/fomac/fomac.cpp +++ b/bindings/fomac/fomac.cpp @@ -210,12 +210,12 @@ All authentication parameters are optional and can be provided as keyword argume nb::enum_(device, "Status", "Enumeration of device status.") - .value("offline", QDMI_DEVICE_STATUS_OFFLINE) - .value("idle", QDMI_DEVICE_STATUS_IDLE) - .value("busy", QDMI_DEVICE_STATUS_BUSY) - .value("error", QDMI_DEVICE_STATUS_ERROR) - .value("maintenance", QDMI_DEVICE_STATUS_MAINTENANCE) - .value("calibration", QDMI_DEVICE_STATUS_CALIBRATION) + .value("OFFLINE", QDMI_DEVICE_STATUS_OFFLINE) + .value("IDLE", QDMI_DEVICE_STATUS_IDLE) + .value("BUSY", QDMI_DEVICE_STATUS_BUSY) + .value("ERROR", QDMI_DEVICE_STATUS_ERROR) + .value("MAINTENANCE", QDMI_DEVICE_STATUS_MAINTENANCE) + .value("CALIBRATION", QDMI_DEVICE_STATUS_CALIBRATION) .export_values(); device.def("name", &fomac::Session::Device::getName, diff --git a/python/mqt/core/fomac.pyi b/python/mqt/core/fomac.pyi index 3160e292b9..5edb85f3cf 100644 --- a/python/mqt/core/fomac.pyi +++ b/python/mqt/core/fomac.pyi @@ -229,29 +229,29 @@ class Device: class Status(enum.Enum): """Enumeration of device status.""" - offline = 0 + OFFLINE = 0 - idle = 1 + IDLE = 1 - busy = 2 + BUSY = 2 - error = 3 + ERROR = 3 - maintenance = 4 + MAINTENANCE = 4 - calibration = 5 + CALIBRATION = 5 - offline: Device.Status = ... + OFFLINE: Device.Status = ... - idle: Device.Status = ... + IDLE: Device.Status = ... - busy: Device.Status = ... + BUSY: Device.Status = ... - error: Device.Status = ... + ERROR: Device.Status = ... - maintenance: Device.Status = ... + MAINTENANCE: Device.Status = ... - calibration: Device.Status = ... + CALIBRATION: Device.Status = ... def name(self) -> str: """Returns the name of the device.""" From 474fce8ace6e59d0368974d0394d8758d6309bc7 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 01:06:23 +0100 Subject: [PATCH 073/144] Initialize lists using ... --- bindings/fomac/fomac.cpp | 60 ++++++++++--------- .../register_standard_operation.cpp | 15 ++--- .../register_symbolic_operation.cpp | 15 +++-- bindings/ir/register_quantum_computation.cpp | 12 ++-- python/mqt/core/fomac.pyi | 18 +++--- python/mqt/core/ir/__init__.pyi | 12 ++-- python/mqt/core/ir/operations.pyi | 28 ++++----- 7 files changed, 83 insertions(+), 77 deletions(-) diff --git a/bindings/fomac/fomac.cpp b/bindings/fomac/fomac.cpp index 6138d2a130..621a5f8b0c 100644 --- a/bindings/fomac/fomac.cpp +++ b/bindings/fomac/fomac.cpp @@ -348,48 +348,53 @@ All authentication parameters are optional and can be provided as keyword argume "quantum device."); operation.def("name", &fomac::Session::Device::Operation::getName, - "sites"_a = std::vector{}, - "params"_a = std::vector{}, + "sites"_a.sig("...") = + std::vector{}, + "params"_a.sig("...") = std::vector{}, "Returns the name of the operation."); operation.def("qubits_num", &fomac::Session::Device::Operation::getQubitsNum, - "sites"_a = std::vector{}, - "params"_a = std::vector{}, + "sites"_a.sig("...") = + std::vector{}, + "params"_a.sig("...") = std::vector{}, "Returns the number of qubits the operation acts on."); - operation.def("parameters_num", - &fomac::Session::Device::Operation::getParametersNum, - "sites"_a = std::vector{}, - "params"_a = std::vector{}, - "Returns the number of parameters the operation has."); + operation.def( + "parameters_num", &fomac::Session::Device::Operation::getParametersNum, + "sites"_a.sig("...") = std::vector{}, + "params"_a.sig("...") = std::vector{}, + "Returns the number of parameters the operation has."); operation.def("duration", &fomac::Session::Device::Operation::getDuration, - "sites"_a = std::vector{}, - "params"_a = std::vector{}, + "sites"_a.sig("...") = + std::vector{}, + "params"_a.sig("...") = std::vector{}, "Returns the duration of the operation."); operation.def("fidelity", &fomac::Session::Device::Operation::getFidelity, - "sites"_a = std::vector{}, - "params"_a = std::vector{}, + "sites"_a.sig("...") = + std::vector{}, + "params"_a.sig("...") = std::vector{}, "Returns the fidelity of the operation."); operation.def("interaction_radius", &fomac::Session::Device::Operation::getInteractionRadius, - "sites"_a = std::vector{}, - "params"_a = std::vector{}, + "sites"_a.sig("...") = + std::vector{}, + "params"_a.sig("...") = std::vector{}, "Returns the interaction radius of the operation."); - operation.def("blocking_radius", - &fomac::Session::Device::Operation::getBlockingRadius, - "sites"_a = std::vector{}, - "params"_a = std::vector{}, - "Returns the blocking radius of the operation."); + operation.def( + "blocking_radius", &fomac::Session::Device::Operation::getBlockingRadius, + "sites"_a.sig("...") = std::vector{}, + "params"_a.sig("...") = std::vector{}, + "Returns the blocking radius of the operation."); - operation.def("idling_fidelity", - &fomac::Session::Device::Operation::getIdlingFidelity, - "sites"_a = std::vector{}, - "params"_a = std::vector{}, - "Returns the idling fidelity of the operation."); + operation.def( + "idling_fidelity", &fomac::Session::Device::Operation::getIdlingFidelity, + "sites"_a.sig("...") = std::vector{}, + "params"_a.sig("...") = std::vector{}, + "Returns the idling fidelity of the operation."); operation.def("is_zoned", &fomac::Session::Device::Operation::isZoned, "Returns whether the operation is zoned."); @@ -403,8 +408,9 @@ All authentication parameters are optional and can be provided as keyword argume operation.def("mean_shuttling_speed", &fomac::Session::Device::Operation::getMeanShuttlingSpeed, - "sites"_a = std::vector{}, - "params"_a = std::vector{}, + "sites"_a.sig("...") = + std::vector{}, + "params"_a.sig("...") = std::vector{}, "Returns the mean shuttling speed of the operation."); operation.def("__repr__", [](const fomac::Session::Device::Operation& op) { diff --git a/bindings/ir/operations/register_standard_operation.cpp b/bindings/ir/operations/register_standard_operation.cpp index e2dea6ec83..9a5ec678f4 100644 --- a/bindings/ir/operations/register_standard_operation.cpp +++ b/bindings/ir/operations/register_standard_operation.cpp @@ -43,29 +43,30 @@ Such Operations are defined by their :class:`OpType`, the qubits (controls and t .def(nb::init<>()) .def(nb::init>(), "target"_a, - "op_type"_a, "params"_a = std::vector{}) + "op_type"_a, "params"_a.sig("...") = std::vector{}) .def(nb::init>(), - "targets"_a, "op_type"_a, "params"_a = std::vector{}) + "targets"_a, "op_type"_a, + "params"_a.sig("...") = std::vector{}) .def(nb::init&>(), "control"_a, "target"_a, "op_type"_a, - "params"_a = std::vector{}) + "params"_a.sig("...") = std::vector{}) .def(nb::init&>(), "control"_a, "targets"_a, "op_type"_a, - "params"_a = std::vector{}) + "params"_a.sig("...") = std::vector{}) .def(nb::init&>(), "controls"_a, "target"_a, "op_type"_a, - "params"_a = std::vector{}) + "params"_a.sig("...") = std::vector{}) .def(nb::init>(), "controls"_a, "targets"_a, "op_type"_a, - "params"_a = std::vector{}) + "params"_a.sig("...") = std::vector{}) .def(nb::init>(), "controls"_a, "target0"_a, "target1"_a, "op_type"_a, - "params"_a = std::vector{}) + "params"_a.sig("...") = std::vector{}) .def("__repr__", [](const qc::StandardOperation& op) { std::stringstream ss; ss << "StandardOperation("; diff --git a/bindings/ir/operations/register_symbolic_operation.cpp b/bindings/ir/operations/register_symbolic_operation.cpp index 9aa0dc734e..5da9e1516a 100644 --- a/bindings/ir/operations/register_symbolic_operation.cpp +++ b/bindings/ir/operations/register_symbolic_operation.cpp @@ -49,32 +49,31 @@ The parameters can be either fixed values or symbolic expressions. .def(nb::init&>(), "target"_a, "op_type"_a, - "params"_a = std::vector{}) + "params"_a.sig("...") = std::vector{}) .def(nb::init&>(), "targets"_a, "op_type"_a, - "params"_a = std::vector{}) + "params"_a.sig("...") = std::vector{}) .def(nb::init&>(), "control"_a, "target"_a, "op_type"_a, - "params"_a = std::vector{}) + "params"_a.sig("...") = std::vector{}) .def(nb::init&>(), "control"_a, "targets"_a, "op_type"_a, - "params"_a = std::vector{}) + "params"_a.sig("...") = std::vector{}) .def(nb::init&>(), "controls"_a, "target"_a, "op_type"_a, - "params"_a = std::vector{}) + "params"_a.sig("...") = std::vector{}) .def(nb::init&>(), "controls"_a, "targets"_a, "op_type"_a, - "params"_a = std::vector{}) + "params"_a.sig("...") = std::vector{}) .def(nb::init&>(), "controls"_a, "target0"_a, "target1"_a, "op_type"_a, - "params"_a = std::vector{}) - + "params"_a.sig("...") = std::vector{}) .def("get_parameter", &qc::SymbolicOperation::getParameter, "index"_a, R"pb(Get the parameter at the given index. diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index 7833f010b0..9ee91822e1 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -2174,7 +2174,7 @@ This method is equivalent to calling :meth:`measure` multiple times. &qc::QuantumComputation::if_), "op_type"_a, "target"_a, "control_register"_a, "expected_value"_a = 1U, "comparison_kind"_a = qc::ComparisonKind::Eq, - "params"_a = std::vector{}, + "params"_a.sig("...") = std::vector{}, R"pb(Add an if operation to the circuit. Args: @@ -2193,7 +2193,7 @@ This method is equivalent to calling :meth:`measure` multiple times. &qc::QuantumComputation::if_), "op_type"_a, "target"_a, "control"_a, "control_register"_a, "expected_value"_a = 1U, "comparison_kind"_a = qc::ComparisonKind::Eq, - "params"_a = std::vector{}, + "params"_a.sig("...") = std::vector{}, R"pb(Add an if operation to the circuit. Args: @@ -2213,7 +2213,7 @@ This method is equivalent to calling :meth:`measure` multiple times. &qc::QuantumComputation::if_), "op_type"_a, "target"_a, "controls"_a, "control_register"_a, "expected_value"_a = 1U, "comparison_kind"_a = qc::ComparisonKind::Eq, - "params"_a = std::vector{}, + "params"_a.sig("...") = std::vector{}, R"pb(Add an if operation to the circuit. Args: @@ -2232,7 +2232,7 @@ This method is equivalent to calling :meth:`measure` multiple times. &qc::QuantumComputation::if_), "op_type"_a, "target"_a, "control_bit"_a, "expected_value"_a = true, "comparison_kind"_a = qc::ComparisonKind::Eq, - "params"_a = std::vector{}, + "params"_a.sig("...") = std::vector{}, R"pb(Add an if operation to the circuit. Args: @@ -2251,7 +2251,7 @@ This method is equivalent to calling :meth:`measure` multiple times. "op_type"_a, "target"_a, "control"_a, "control_bit"_a, "expected_value"_a = true, "comparison_kind"_a = qc::ComparisonKind::Eq, - "params"_a = std::vector{}, + "params"_a.sig("...") = std::vector{}, R"pb(Add an if operation to the circuit. Args: @@ -2271,7 +2271,7 @@ This method is equivalent to calling :meth:`measure` multiple times. &qc::QuantumComputation::if_), "op_type"_a, "target"_a, "controls"_a, "control_bit"_a, "expected_value"_a = true, "comparison_kind"_a = qc::ComparisonKind::Eq, - "params"_a = std::vector{}, + "params"_a.sig("...") = std::vector{}, R"pb(Add an if operation to the circuit. Args: diff --git a/python/mqt/core/fomac.pyi b/python/mqt/core/fomac.pyi index 5edb85f3cf..a89882a2b3 100644 --- a/python/mqt/core/fomac.pyi +++ b/python/mqt/core/fomac.pyi @@ -358,28 +358,28 @@ class Device: class Operation: """An operation represents a quantum operation that can be performed on a quantum device.""" - def name(self, sites: Sequence[Device.Site] = [], params: Sequence[float] = []) -> str: + def name(self, sites: Sequence[Device.Site] = ..., params: Sequence[float] = ...) -> str: """Returns the name of the operation.""" - def qubits_num(self, sites: Sequence[Device.Site] = [], params: Sequence[float] = []) -> int | None: + def qubits_num(self, sites: Sequence[Device.Site] = ..., params: Sequence[float] = ...) -> int | None: """Returns the number of qubits the operation acts on.""" - def parameters_num(self, sites: Sequence[Device.Site] = [], params: Sequence[float] = []) -> int: + def parameters_num(self, sites: Sequence[Device.Site] = ..., params: Sequence[float] = ...) -> int: """Returns the number of parameters the operation has.""" - def duration(self, sites: Sequence[Device.Site] = [], params: Sequence[float] = []) -> int | None: + def duration(self, sites: Sequence[Device.Site] = ..., params: Sequence[float] = ...) -> int | None: """Returns the duration of the operation.""" - def fidelity(self, sites: Sequence[Device.Site] = [], params: Sequence[float] = []) -> float | None: + def fidelity(self, sites: Sequence[Device.Site] = ..., params: Sequence[float] = ...) -> float | None: """Returns the fidelity of the operation.""" - def interaction_radius(self, sites: Sequence[Device.Site] = [], params: Sequence[float] = []) -> int | None: + def interaction_radius(self, sites: Sequence[Device.Site] = ..., params: Sequence[float] = ...) -> int | None: """Returns the interaction radius of the operation.""" - def blocking_radius(self, sites: Sequence[Device.Site] = [], params: Sequence[float] = []) -> int | None: + def blocking_radius(self, sites: Sequence[Device.Site] = ..., params: Sequence[float] = ...) -> int | None: """Returns the blocking radius of the operation.""" - def idling_fidelity(self, sites: Sequence[Device.Site] = [], params: Sequence[float] = []) -> float | None: + def idling_fidelity(self, sites: Sequence[Device.Site] = ..., params: Sequence[float] = ...) -> float | None: """Returns the idling fidelity of the operation.""" def is_zoned(self) -> bool: @@ -391,7 +391,7 @@ class Device: def site_pairs(self) -> list[tuple[Device.Site, Device.Site]] | None: """Returns the list of site pairs the local 2-qubit operation can be performed on.""" - def mean_shuttling_speed(self, sites: Sequence[Device.Site] = [], params: Sequence[float] = []) -> int | None: + def mean_shuttling_speed(self, sites: Sequence[Device.Site] = ..., params: Sequence[float] = ...) -> int | None: """Returns the mean shuttling speed of the operation.""" def __eq__(self, arg: object, /) -> bool: ... diff --git a/python/mqt/core/ir/__init__.pyi b/python/mqt/core/ir/__init__.pyi index c6a5b84169..e25221af25 100644 --- a/python/mqt/core/ir/__init__.pyi +++ b/python/mqt/core/ir/__init__.pyi @@ -1942,7 +1942,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): control_register: registers.ClassicalRegister, expected_value: int = 1, comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, - params: Sequence[float] = [], + params: Sequence[float] = ..., ) -> None: """Add an if operation to the circuit. @@ -1964,7 +1964,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): control_register: registers.ClassicalRegister, expected_value: int = 1, comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, - params: Sequence[float] = [], + params: Sequence[float] = ..., ) -> None: """Add an if operation to the circuit. @@ -1987,7 +1987,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): control_register: registers.ClassicalRegister, expected_value: int = 1, comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, - params: Sequence[float] = [], + params: Sequence[float] = ..., ) -> None: """Add an if operation to the circuit. @@ -2009,7 +2009,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): control_bit: int, expected_value: bool = True, comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, - params: Sequence[float] = [], + params: Sequence[float] = ..., ) -> None: """Add an if operation to the circuit. @@ -2031,7 +2031,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): control_bit: int, expected_value: bool = True, comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, - params: Sequence[float] = [], + params: Sequence[float] = ..., ) -> None: """Add an if operation to the circuit. @@ -2054,7 +2054,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): control_bit: int, expected_value: bool = True, comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, - params: Sequence[float] = [], + params: Sequence[float] = ..., ) -> None: """Add an if operation to the circuit. diff --git a/python/mqt/core/ir/operations.pyi b/python/mqt/core/ir/operations.pyi index a7db8b01ee..a182d887bd 100644 --- a/python/mqt/core/ir/operations.pyi +++ b/python/mqt/core/ir/operations.pyi @@ -627,26 +627,26 @@ class StandardOperation(Operation): @overload def __init__(self) -> None: ... @overload - def __init__(self, target: int, op_type: OpType, params: Sequence[float] = []) -> None: ... + def __init__(self, target: int, op_type: OpType, params: Sequence[float] = ...) -> None: ... @overload - def __init__(self, targets: Sequence[int], op_type: OpType, params: Sequence[float] = []) -> None: ... + def __init__(self, targets: Sequence[int], op_type: OpType, params: Sequence[float] = ...) -> None: ... @overload - def __init__(self, control: Control, target: int, op_type: OpType, params: Sequence[float] = []) -> None: ... + def __init__(self, control: Control, target: int, op_type: OpType, params: Sequence[float] = ...) -> None: ... @overload def __init__( - self, control: Control, targets: Sequence[int], op_type: OpType, params: Sequence[float] = [] + self, control: Control, targets: Sequence[int], op_type: OpType, params: Sequence[float] = ... ) -> None: ... @overload def __init__( - self, controls: AbstractSet[Control], target: int, op_type: OpType, params: Sequence[float] = [] + self, controls: AbstractSet[Control], target: int, op_type: OpType, params: Sequence[float] = ... ) -> None: ... @overload def __init__( - self, controls: AbstractSet[Control], targets: Sequence[int], op_type: OpType, params: Sequence[float] = [] + self, controls: AbstractSet[Control], targets: Sequence[int], op_type: OpType, params: Sequence[float] = ... ) -> None: ... @overload def __init__( - self, controls: AbstractSet[Control], target0: int, target1: int, op_type: OpType, params: Sequence[float] = [] + self, controls: AbstractSet[Control], target0: int, target1: int, op_type: OpType, params: Sequence[float] = ... ) -> None: ... class CompoundOperation(Operation, MutableSequence[Operation]): @@ -787,11 +787,11 @@ class SymbolicOperation(StandardOperation): def __init__(self) -> None: ... @overload def __init__( - self, target: int, op_type: OpType, params: Sequence[mqt.core.ir.symbolic.Expression | float] = [] + self, target: int, op_type: OpType, params: Sequence[mqt.core.ir.symbolic.Expression | float] = ... ) -> None: ... @overload def __init__( - self, targets: Sequence[int], op_type: OpType, params: Sequence[mqt.core.ir.symbolic.Expression | float] = [] + self, targets: Sequence[int], op_type: OpType, params: Sequence[mqt.core.ir.symbolic.Expression | float] = ... ) -> None: ... @overload def __init__( @@ -799,7 +799,7 @@ class SymbolicOperation(StandardOperation): control: Control, target: int, op_type: OpType, - params: Sequence[mqt.core.ir.symbolic.Expression | float] = [], + params: Sequence[mqt.core.ir.symbolic.Expression | float] = ..., ) -> None: ... @overload def __init__( @@ -807,7 +807,7 @@ class SymbolicOperation(StandardOperation): control: Control, targets: Sequence[int], op_type: OpType, - params: Sequence[mqt.core.ir.symbolic.Expression | float] = [], + params: Sequence[mqt.core.ir.symbolic.Expression | float] = ..., ) -> None: ... @overload def __init__( @@ -815,7 +815,7 @@ class SymbolicOperation(StandardOperation): controls: AbstractSet[Control], target: int, op_type: OpType, - params: Sequence[mqt.core.ir.symbolic.Expression | float] = [], + params: Sequence[mqt.core.ir.symbolic.Expression | float] = ..., ) -> None: ... @overload def __init__( @@ -823,7 +823,7 @@ class SymbolicOperation(StandardOperation): controls: AbstractSet[Control], targets: Sequence[int], op_type: OpType, - params: Sequence[mqt.core.ir.symbolic.Expression | float] = [], + params: Sequence[mqt.core.ir.symbolic.Expression | float] = ..., ) -> None: ... @overload def __init__( @@ -832,7 +832,7 @@ class SymbolicOperation(StandardOperation): target0: int, target1: int, op_type: OpType, - params: Sequence[mqt.core.ir.symbolic.Expression | float] = [], + params: Sequence[mqt.core.ir.symbolic.Expression | float] = ..., ) -> None: ... def get_parameter(self, index: int) -> mqt.core.ir.symbolic.Expression | float: """Get the parameter at the given index. From 214690fa5fd673650fff85d051b50e81271c3d5e Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 01:06:50 +0100 Subject: [PATCH 074/144] Fix argument name of Expression.__getitem__() --- bindings/ir/symbolic/register_expression.cpp | 2 +- python/mqt/core/ir/symbolic.pyi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/ir/symbolic/register_expression.cpp b/bindings/ir/symbolic/register_expression.cpp index 12f3fa0d78..16afaebc07 100644 --- a/bindings/ir/symbolic/register_expression.cpp +++ b/bindings/ir/symbolic/register_expression.cpp @@ -75,7 +75,7 @@ Alternatively, an expression can be created with a single term and a constant or } return expr.getTerms()[static_cast(idx)]; }, - "key"_a) + "index"_a) .def("is_zero", &sym::Expression::isZero, "Check if the expression is zero.") diff --git a/python/mqt/core/ir/symbolic.pyi b/python/mqt/core/ir/symbolic.pyi index 877d43427d..6c67a5a22e 100644 --- a/python/mqt/core/ir/symbolic.pyi +++ b/python/mqt/core/ir/symbolic.pyi @@ -99,7 +99,7 @@ class Expression: @constant.setter def constant(self, arg: float, /) -> None: ... def __iter__(self) -> Iterator[Term]: ... - def __getitem__(self, key: int) -> Term: ... + def __getitem__(self, index: int) -> Term: ... def is_zero(self) -> bool: """Check if the expression is zero.""" From ea40398a082d24787bd9e23e1ff5298fd1f15478 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 01:09:46 +0100 Subject: [PATCH 075/144] Do not export enum values --- bindings/fomac/fomac.cpp | 9 +- .../operations/register_if_else_operation.cpp | 3 +- bindings/ir/operations/register_optype.cpp | 4 +- python/mqt/core/fomac.pyi | 54 ----------- python/mqt/core/ir/operations.pyi | 92 ------------------- 5 files changed, 5 insertions(+), 157 deletions(-) diff --git a/bindings/fomac/fomac.cpp b/bindings/fomac/fomac.cpp index 621a5f8b0c..87d1cd1bf3 100644 --- a/bindings/fomac/fomac.cpp +++ b/bindings/fomac/fomac.cpp @@ -180,8 +180,7 @@ All authentication parameters are optional and can be provided as keyword argume .value("RUNNING", QDMI_JOB_STATUS_RUNNING) .value("DONE", QDMI_JOB_STATUS_DONE) .value("CANCELED", QDMI_JOB_STATUS_CANCELED) - .value("FAILED", QDMI_JOB_STATUS_FAILED) - .export_values(); + .value("FAILED", QDMI_JOB_STATUS_FAILED); // ProgramFormat enum nb::enum_(m, "ProgramFormat", @@ -199,8 +198,7 @@ All authentication parameters are optional and can be provided as keyword argume .value("CUSTOM2", QDMI_PROGRAM_FORMAT_CUSTOM2) .value("CUSTOM3", QDMI_PROGRAM_FORMAT_CUSTOM3) .value("CUSTOM4", QDMI_PROGRAM_FORMAT_CUSTOM4) - .value("CUSTOM5", QDMI_PROGRAM_FORMAT_CUSTOM5) - .export_values(); + .value("CUSTOM5", QDMI_PROGRAM_FORMAT_CUSTOM5); // Device class auto device = nb::class_( @@ -215,8 +213,7 @@ All authentication parameters are optional and can be provided as keyword argume .value("BUSY", QDMI_DEVICE_STATUS_BUSY) .value("ERROR", QDMI_DEVICE_STATUS_ERROR) .value("MAINTENANCE", QDMI_DEVICE_STATUS_MAINTENANCE) - .value("CALIBRATION", QDMI_DEVICE_STATUS_CALIBRATION) - .export_values(); + .value("CALIBRATION", QDMI_DEVICE_STATUS_CALIBRATION); device.def("name", &fomac::Session::Device::getName, "Returns the name of the device."); diff --git a/bindings/ir/operations/register_if_else_operation.cpp b/bindings/ir/operations/register_if_else_operation.cpp index a6177dbabd..928af900aa 100644 --- a/bindings/ir/operations/register_if_else_operation.cpp +++ b/bindings/ir/operations/register_if_else_operation.cpp @@ -37,8 +37,7 @@ void registerIfElseOperation(const nb::module_& m) { .value("leq", qc::ComparisonKind::Leq, "Less-than-or-equal comparison.") .value("gt", qc::ComparisonKind::Gt, "Greater-than comparison.") .value("geq", qc::ComparisonKind::Geq, - "Greater-than-or-equal comparison.") - .export_values(); + "Greater-than-or-equal comparison."); auto ifElse = nb::class_( m, "IfElseOperation", R"pb(If-else quantum operation. diff --git a/bindings/ir/operations/register_optype.cpp b/bindings/ir/operations/register_optype.cpp index 01f9c254d2..dad27e434d 100644 --- a/bindings/ir/operations/register_optype.cpp +++ b/bindings/ir/operations/register_optype.cpp @@ -226,9 +226,7 @@ See Also: It is used to control the execution of an operation based on the value of a classical register. See Also: - :meth:`mqt.core.ir.QuantumComputation.if_else`)pb") - - .export_values(); + :meth:`mqt.core.ir.QuantumComputation.if_else`)pb"); } } // namespace mqt diff --git a/python/mqt/core/fomac.pyi b/python/mqt/core/fomac.pyi index a89882a2b3..f22e2dab1f 100644 --- a/python/mqt/core/fomac.pyi +++ b/python/mqt/core/fomac.pyi @@ -150,20 +150,6 @@ class Job: FAILED = 6 - CREATED: Job.Status = ... - - SUBMITTED: Job.Status = ... - - QUEUED: Job.Status = ... - - RUNNING: Job.Status = ... - - DONE: Job.Status = ... - - CANCELED: Job.Status = ... - - FAILED: Job.Status = ... - class ProgramFormat(enum.Enum): """Enumeration of program formats.""" @@ -195,34 +181,6 @@ class ProgramFormat(enum.Enum): CUSTOM5 = 999999999 -QASM2: ProgramFormat = ... - -QASM3: ProgramFormat = ... - -QIR_BASE_STRING: ProgramFormat = ... - -QIR_BASE_MODULE: ProgramFormat = ... - -QIR_ADAPTIVE_STRING: ProgramFormat = ... - -QIR_ADAPTIVE_MODULE: ProgramFormat = ... - -CALIBRATION: ProgramFormat = ... - -QPY: ProgramFormat = ... - -IQM_JSON: ProgramFormat = ... - -CUSTOM1: ProgramFormat = ... - -CUSTOM2: ProgramFormat = ... - -CUSTOM3: ProgramFormat = ... - -CUSTOM4: ProgramFormat = ... - -CUSTOM5: ProgramFormat = ... - class Device: """A device represents a quantum device with its properties and capabilities.""" @@ -241,18 +199,6 @@ class Device: CALIBRATION = 5 - OFFLINE: Device.Status = ... - - IDLE: Device.Status = ... - - BUSY: Device.Status = ... - - ERROR: Device.Status = ... - - MAINTENANCE: Device.Status = ... - - CALIBRATION: Device.Status = ... - def name(self) -> str: """Returns the name of the device.""" diff --git a/python/mqt/core/ir/operations.pyi b/python/mqt/core/ir/operations.pyi index a182d887bd..0a018ab75b 100644 --- a/python/mqt/core/ir/operations.pyi +++ b/python/mqt/core/ir/operations.pyi @@ -341,86 +341,6 @@ class OpType(enum.Enum): :meth:`mqt.core.ir.QuantumComputation.if_else` """ -none: OpType = ... - -gphase: OpType = ... - -i: OpType = ... - -h: OpType = ... - -x: OpType = ... - -y: OpType = ... - -z: OpType = ... - -s: OpType = ... - -sdg: OpType = ... - -t: OpType = ... - -tdg: OpType = ... - -v: OpType = ... - -vdg: OpType = ... - -u: OpType = ... - -u2: OpType = ... - -p: OpType = ... - -sx: OpType = ... - -sxdg: OpType = ... - -rx: OpType = ... - -ry: OpType = ... - -rz: OpType = ... - -r: OpType = ... - -swap: OpType = ... - -iswap: OpType = ... - -iswapdg: OpType = ... - -peres: OpType = ... - -peresdg: OpType = ... - -dcx: OpType = ... - -ecr: OpType = ... - -rxx: OpType = ... - -ryy: OpType = ... - -rzz: OpType = ... - -rzx: OpType = ... - -xx_minus_yy: OpType = ... - -xx_plus_yy: OpType = ... - -compound: OpType = ... - -measure: OpType = ... - -reset: OpType = ... - -barrier: OpType = ... - -if_else: OpType = ... - class Control: """A control is a pair of a qubit and a type. The type can be either positive or negative. @@ -891,18 +811,6 @@ class ComparisonKind(enum.Enum): geq = 5 """Greater-than-or-equal comparison.""" -eq: ComparisonKind = ... - -neq: ComparisonKind = ... - -lt: ComparisonKind = ... - -leq: ComparisonKind = ... - -gt: ComparisonKind = ... - -geq: ComparisonKind = ... - class IfElseOperation(Operation): """If-else quantum operation. From e7aef985785a03feb0f734badc866be688860e41 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 14:27:03 +0100 Subject: [PATCH 076/144] Fix right division --- include/mqt-core/ir/operations/Expression.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/mqt-core/ir/operations/Expression.hpp b/include/mqt-core/ir/operations/Expression.hpp index 47a7ce230b..fabd6485a7 100644 --- a/include/mqt-core/ir/operations/Expression.hpp +++ b/include/mqt-core/ir/operations/Expression.hpp @@ -258,7 +258,7 @@ Term operator*(double lhs, const Term& rhs) { template >> Term operator/(double lhs, const Term& rhs) { - return rhs / lhs; + return lhs / rhs; } ///@} From dcd3a499cffc02d686d16192de2e864305eaa4e6 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 14:38:05 +0100 Subject: [PATCH 077/144] Document MemoryError --- bindings/dd/register_matrix_dds.cpp | 5 ++++- bindings/dd/register_vector_dds.cpp | 5 ++++- python/mqt/core/dd.pyi | 6 ++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/bindings/dd/register_matrix_dds.cpp b/bindings/dd/register_matrix_dds.cpp index 71d1e72067..8d26f36948 100644 --- a/bindings/dd/register_matrix_dds.cpp +++ b/bindings/dd/register_matrix_dds.cpp @@ -109,7 +109,10 @@ void registerMatrixDDs(const nb::module_& m) { threshold: The threshold for not including entries in the matrix. Defaults to 0.0. Returns: - The matrix.)pb"); + The matrix. + +Raises: + MemoryError: If the memory allocation fails.)pb"); mat.def( "to_dot", diff --git a/bindings/dd/register_vector_dds.cpp b/bindings/dd/register_vector_dds.cpp index d802d18e73..97fbba51b4 100644 --- a/bindings/dd/register_vector_dds.cpp +++ b/bindings/dd/register_vector_dds.cpp @@ -97,7 +97,10 @@ void registerVectorDDs(const nb::module_& m) { threshold: The threshold for not including amplitudes in the state vector. Defaults to 0.0. Returns: - The state vector.)pb"); + The state vector. + +Raises: + MemoryError: If the memory allocation fails.)pb"); vec.def( "to_dot", diff --git a/python/mqt/core/dd.pyi b/python/mqt/core/dd.pyi index 8cb302563c..2e8b8bd86c 100644 --- a/python/mqt/core/dd.pyi +++ b/python/mqt/core/dd.pyi @@ -52,6 +52,9 @@ class VectorDD: Returns: The state vector. + + Raises: + MemoryError: If the memory allocation fails. """ def to_dot( @@ -145,6 +148,9 @@ class MatrixDD: Returns: The matrix. + + Raises: + MemoryError: If the memory allocation fails. """ def to_dot( From 58a8f4571f020851723207c4cc4fe47560cc3131 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 14:45:44 +0100 Subject: [PATCH 078/144] Mark arithmetic operations --- bindings/ir/symbolic/register_expression.cpp | 17 +++++++++-------- bindings/ir/symbolic/register_term.cpp | 12 +++++++----- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/bindings/ir/symbolic/register_expression.cpp b/bindings/ir/symbolic/register_expression.cpp index 16afaebc07..fa19b52051 100644 --- a/bindings/ir/symbolic/register_expression.cpp +++ b/bindings/ir/symbolic/register_expression.cpp @@ -105,8 +105,8 @@ Alternatively, an expression can be created with a single term and a constant or The evaluated value of the expression.)pb") // addition operators - .def(nb::self + nb::self) - .def(nb::self + double()) + .def(nb::self + nb::self, nb::is_operator()) + .def(nb::self + double(), nb::is_operator()) .def("__add__", [](const sym::Expression& lhs, const sym::Term& rhs) { return lhs + rhs; }) .def("__radd__", [](const sym::Expression& rhs, @@ -114,18 +114,19 @@ Alternatively, an expression can be created with a single term and a constant or .def("__radd__", [](const sym::Expression& rhs, const double lhs) { return rhs + lhs; }) // subtraction operators - .def(nb::self - nb::self) // NOLINT(misc-redundant-expression) - .def(nb::self - double()) - .def(double() - nb::self) + // NOLINTNEXTLINE(misc-redundant-expression) + .def(nb::self - nb::self, nb::is_operator()) + .def(nb::self - double(), nb::is_operator()) + .def(double() - nb::self, nb::is_operator()) .def("__sub__", [](const sym::Expression& lhs, const sym::Term& rhs) { return lhs - rhs; }) .def("__rsub__", [](const sym::Expression& rhs, const sym::Term& lhs) { return lhs - rhs; }) // multiplication operators - .def(nb::self * double()) - .def(double() * nb::self) + .def(nb::self * double(), nb::is_operator()) + .def(double() * nb::self, nb::is_operator()) // division operators - .def(nb::self / double()) + .def(nb::self / double(), nb::is_operator()) // comparison operators .def(nb::self == nb::self, // NOLINT(misc-redundant-expression) nb::sig("def __eq__(self, arg: object, /) -> bool")) diff --git a/bindings/ir/symbolic/register_term.cpp b/bindings/ir/symbolic/register_term.cpp index eb2634cbf5..8a1c4520d7 100644 --- a/bindings/ir/symbolic/register_term.cpp +++ b/bindings/ir/symbolic/register_term.cpp @@ -58,11 +58,13 @@ void registerTerm(const nb::module_& m) { Returns: The evaluated value of the term.)pb") - .def(nb::self * double()) - .def(double() * nb::self) - .def(nb::self / double()) - .def("__rtruediv__", - [](const sym::Term& lhs, double rhs) { return rhs / lhs; }) + .def(nb::self * double(), nb::is_operator()) + .def(double() * nb::self, nb::is_operator()) + .def(nb::self / double(), nb::is_operator()) + .def( + "__rtruediv__", + [](const sym::Term& lhs, double rhs) { return rhs / lhs; }, + nb::is_operator()) .def(nb::self == nb::self, // NOLINT(misc-redundant-expression) nb::sig("def __eq__(self, arg: object, /) -> bool")) From f02304906567f205b9f48f92c59d0a9d1d4be35c Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 16:53:47 +0100 Subject: [PATCH 079/144] Validate register attributes --- bindings/dd/register_dd_package.cpp | 10 ++++- bindings/ir/register_permutation.cpp | 10 ++++- bindings/ir/register_quantum_computation.cpp | 10 ++++- bindings/ir/register_registers.cpp | 45 ++++++++++++++++---- 4 files changed, 61 insertions(+), 14 deletions(-) diff --git a/bindings/dd/register_dd_package.cpp b/bindings/dd/register_dd_package.cpp index 7d6dd379b9..10e1195deb 100644 --- a/bindings/dd/register_dd_package.cpp +++ b/bindings/dd/register_dd_package.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include // NOLINT(misc-include-cleaner) @@ -48,7 +49,7 @@ using namespace nb::literals; using Vector = nb::ndarray, nb::ndim<1>>; using Matrix = nb::ndarray, nb::ndim<2>>; -using Control = std::variant; +using Control = std::variant; using Controls = std::set; namespace { @@ -103,7 +104,12 @@ qc::Control getControl(const Control& control) { if (std::holds_alternative(control)) { return std::get(control); } - return static_cast(std::get(control)); + const auto controlInt = + static_cast(std::get(control)); + if (controlInt > std::numeric_limits::max()) { + throw nb::value_error("Qubit index exceeds maximum value"); + } + return static_cast(controlInt); } /// Helper function to convert Controls variant to qc::Controls diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index e940163245..949c166bb0 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -31,7 +32,7 @@ namespace mqt { namespace nb = nanobind; using namespace nb::literals; -using Control = std::variant; +using Control = std::variant; using Controls = std::set; namespace { @@ -41,7 +42,12 @@ qc::Control getControl(const Control& control) { if (std::holds_alternative(control)) { return std::get(control); } - return static_cast(std::get(control)); + const auto controlInt = + static_cast(std::get(control)); + if (controlInt > std::numeric_limits::max()) { + throw nb::value_error("Qubit index exceeds maximum value"); + } + return static_cast(controlInt); } /// Helper function to convert Controls variant to qc::Controls diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index 9ee91822e1..c5a9610cd3 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include // NOLINT(misc-include-cleaner) @@ -46,7 +47,7 @@ using namespace nb::literals; using DiffType = std::vector>::difference_type; using SizeType = std::vector>::size_type; -using Control = std::variant; +using Control = std::variant; using Controls = std::set; namespace { @@ -56,7 +57,12 @@ qc::Control getControl(const Control& control) { if (std::holds_alternative(control)) { return std::get(control); } - return static_cast(std::get(control)); + const auto controlInt = + static_cast(std::get(control)); + if (controlInt > std::numeric_limits::max()) { + throw nb::value_error("Qubit index exceeds maximum value"); + } + return static_cast(controlInt); } /// Helper function to convert Controls variant to qc::Controls diff --git a/bindings/ir/register_registers.cpp b/bindings/ir/register_registers.cpp index f0b90f09b2..3dee07b489 100644 --- a/bindings/ir/register_registers.cpp +++ b/bindings/ir/register_registers.cpp @@ -12,6 +12,7 @@ #include "ir/Register.hpp" #include +#include #include #include #include // NOLINT(misc-include-cleaner) @@ -42,14 +43,28 @@ void registerRegisters(const nb::module_& m) { .def_prop_rw( "start", [](const qc::QuantumRegister& reg) { return reg.getStartIndex(); }, - [](qc::QuantumRegister& reg, const qc::Qubit start) { - reg.getStartIndex() = start; + [](qc::QuantumRegister& reg, const nb::int_ start) { + const auto startInt = static_cast(start); + if (startInt < 0) { + throw nb::value_error("Start index cannot be negative"); + } + if (startInt > std::numeric_limits::max()) { + throw nb::value_error("Start index exceeds maximum value"); + } + reg.getStartIndex() = static_cast(startInt); }, "The index of the first qubit in the quantum register.") .def_prop_rw( "size", [](const qc::QuantumRegister& reg) { return reg.getSize(); }, - [](qc::QuantumRegister& reg, const std::size_t size) { - reg.getSize() = size; + [](qc::QuantumRegister& reg, const nb::int_ size) { + const auto sizeInt = static_cast(size); + if (sizeInt < 0) { + throw nb::value_error("Start index cannot be negative"); + } + if (sizeInt > std::numeric_limits::max()) { + throw nb::value_error("Start index exceeds maximum value"); + } + reg.getSize() = static_cast(sizeInt); }, "The number of qubits in the quantum register.") .def_prop_ro( @@ -106,16 +121,30 @@ void registerRegisters(const nb::module_& m) { .def_prop_rw( "start", [](const qc::ClassicalRegister& reg) { return reg.getStartIndex(); }, - [](qc::ClassicalRegister& reg, const qc::Bit start) { - reg.getStartIndex() = start; + [](qc::ClassicalRegister& reg, const nb::int_ start) { + const auto startInt = static_cast(start); + if (startInt < 0) { + throw nb::value_error("Start index cannot be negative"); + } + if (startInt > std::numeric_limits::max()) { + throw nb::value_error("Start index exceeds maximum value"); + } + reg.getStartIndex() = static_cast(startInt); }, "The index of the first bit in the classical register.") .def_prop_rw( "size", [](const qc::ClassicalRegister& reg) { return reg.getSize(); }, - [](qc::ClassicalRegister& reg, const std::size_t size) { - reg.getSize() = size; + [](qc::ClassicalRegister& reg, const nb::int_ size) { + const auto sizeInt = static_cast(size); + if (sizeInt < 0) { + throw nb::value_error("Size cannot be negative"); + } + if (sizeInt > std::numeric_limits::max()) { + throw nb::value_error("Size exceeds maximum value"); + } + reg.getSize() = static_cast(sizeInt); }, "The number of bits in the classical register.") From d3046a3da7e0d00a7157e4bd0ea6054f831716bd Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 17:03:35 +0100 Subject: [PATCH 080/144] Fix usage of std::ranges::sort --- bindings/ir/operations/register_compound_operation.cpp | 2 +- bindings/ir/register_quantum_computation.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/ir/operations/register_compound_operation.cpp b/bindings/ir/operations/register_compound_operation.cpp index 33074a3cd5..c149bcc5e9 100644 --- a/bindings/ir/operations/register_compound_operation.cpp +++ b/bindings/ir/operations/register_compound_operation.cpp @@ -181,7 +181,7 @@ A :class:`CompoundOperation` can contain any number of operations, including oth indices.emplace_back(static_cast(start) + (static_cast(i) * step)); } - std::ranges::sort(indices.begin(), indices.end(), std::greater<>()); + std::ranges::sort(indices, std::greater<>()); for (const auto idx : indices) { op.erase(op.begin() + idx); } diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index c5a9610cd3..c0feaa807c 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -305,7 +305,7 @@ When the quantum computation contains more than one operation, the resulting ope indices.emplace_back(static_cast(start) + (static_cast(i) * step)); } - std::ranges::sort(indices.begin(), indices.end(), std::greater<>()); + std::ranges::sort(indices, std::greater<>()); for (const auto idx : indices) { circ.erase(circ.begin() + idx); } From 0210d3a0dff98fb70687cd65544aa46d907612f9 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 17:03:58 +0100 Subject: [PATCH 081/144] Update lock file --- uv.lock | 414 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 215 insertions(+), 199 deletions(-) diff --git a/uv.lock b/uv.lock index d39bd57eed..b333ccd37e 100644 --- a/uv.lock +++ b/uv.lock @@ -635,31 +635,31 @@ wheels = [ [[package]] name = "debugpy" -version = "1.8.17" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/ad/71e708ff4ca377c4230530d6a7aa7992592648c122a2cd2b321cf8b35a76/debugpy-1.8.17.tar.gz", hash = "sha256:fd723b47a8c08892b1a16b2c6239a8b96637c62a59b94bb5dab4bac592a58a8e", size = 1644129, upload-time = "2025-09-17T16:33:20.633Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/36/b57c6e818d909f6e59c0182252921cf435e0951126a97e11de37e72ab5e1/debugpy-1.8.17-cp310-cp310-macosx_15_0_x86_64.whl", hash = "sha256:c41d2ce8bbaddcc0009cc73f65318eedfa3dbc88a8298081deb05389f1ab5542", size = 2098021, upload-time = "2025-09-17T16:33:22.556Z" }, - { url = "https://files.pythonhosted.org/packages/be/01/0363c7efdd1e9febd090bb13cee4fb1057215b157b2979a4ca5ccb678217/debugpy-1.8.17-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:1440fd514e1b815edd5861ca394786f90eb24960eb26d6f7200994333b1d79e3", size = 3087399, upload-time = "2025-09-17T16:33:24.292Z" }, - { url = "https://files.pythonhosted.org/packages/79/bc/4a984729674aa9a84856650438b9665f9a1d5a748804ac6f37932ce0d4aa/debugpy-1.8.17-cp310-cp310-win32.whl", hash = "sha256:3a32c0af575749083d7492dc79f6ab69f21b2d2ad4cd977a958a07d5865316e4", size = 5230292, upload-time = "2025-09-17T16:33:26.137Z" }, - { url = "https://files.pythonhosted.org/packages/5d/19/2b9b3092d0cf81a5aa10c86271999453030af354d1a5a7d6e34c574515d7/debugpy-1.8.17-cp310-cp310-win_amd64.whl", hash = "sha256:a3aad0537cf4d9c1996434be68c6c9a6d233ac6f76c2a482c7803295b4e4f99a", size = 5261885, upload-time = "2025-09-17T16:33:27.592Z" }, - { url = "https://files.pythonhosted.org/packages/d8/53/3af72b5c159278c4a0cf4cffa518675a0e73bdb7d1cac0239b815502d2ce/debugpy-1.8.17-cp311-cp311-macosx_15_0_universal2.whl", hash = "sha256:d3fce3f0e3de262a3b67e69916d001f3e767661c6e1ee42553009d445d1cd840", size = 2207154, upload-time = "2025-09-17T16:33:29.457Z" }, - { url = "https://files.pythonhosted.org/packages/8f/6d/204f407df45600e2245b4a39860ed4ba32552330a0b3f5f160ae4cc30072/debugpy-1.8.17-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:c6bdf134457ae0cac6fb68205776be635d31174eeac9541e1d0c062165c6461f", size = 3170322, upload-time = "2025-09-17T16:33:30.837Z" }, - { url = "https://files.pythonhosted.org/packages/f2/13/1b8f87d39cf83c6b713de2620c31205299e6065622e7dd37aff4808dd410/debugpy-1.8.17-cp311-cp311-win32.whl", hash = "sha256:e79a195f9e059edfe5d8bf6f3749b2599452d3e9380484cd261f6b7cd2c7c4da", size = 5155078, upload-time = "2025-09-17T16:33:33.331Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c5/c012c60a2922cc91caa9675d0ddfbb14ba59e1e36228355f41cab6483469/debugpy-1.8.17-cp311-cp311-win_amd64.whl", hash = "sha256:b532282ad4eca958b1b2d7dbcb2b7218e02cb934165859b918e3b6ba7772d3f4", size = 5179011, upload-time = "2025-09-17T16:33:35.711Z" }, - { url = "https://files.pythonhosted.org/packages/08/2b/9d8e65beb2751876c82e1aceb32f328c43ec872711fa80257c7674f45650/debugpy-1.8.17-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:f14467edef672195c6f6b8e27ce5005313cb5d03c9239059bc7182b60c176e2d", size = 2549522, upload-time = "2025-09-17T16:33:38.466Z" }, - { url = "https://files.pythonhosted.org/packages/b4/78/eb0d77f02971c05fca0eb7465b18058ba84bd957062f5eec82f941ac792a/debugpy-1.8.17-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:24693179ef9dfa20dca8605905a42b392be56d410c333af82f1c5dff807a64cc", size = 4309417, upload-time = "2025-09-17T16:33:41.299Z" }, - { url = "https://files.pythonhosted.org/packages/37/42/c40f1d8cc1fed1e75ea54298a382395b8b937d923fcf41ab0797a554f555/debugpy-1.8.17-cp312-cp312-win32.whl", hash = "sha256:6a4e9dacf2cbb60d2514ff7b04b4534b0139facbf2abdffe0639ddb6088e59cf", size = 5277130, upload-time = "2025-09-17T16:33:43.554Z" }, - { url = "https://files.pythonhosted.org/packages/72/22/84263b205baad32b81b36eac076de0cdbe09fe2d0637f5b32243dc7c925b/debugpy-1.8.17-cp312-cp312-win_amd64.whl", hash = "sha256:e8f8f61c518952fb15f74a302e068b48d9c4691768ade433e4adeea961993464", size = 5319053, upload-time = "2025-09-17T16:33:53.033Z" }, - { url = "https://files.pythonhosted.org/packages/50/76/597e5cb97d026274ba297af8d89138dfd9e695767ba0e0895edb20963f40/debugpy-1.8.17-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:857c1dd5d70042502aef1c6d1c2801211f3ea7e56f75e9c335f434afb403e464", size = 2538386, upload-time = "2025-09-17T16:33:54.594Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/ce5c34fcdfec493701f9d1532dba95b21b2f6394147234dce21160bd923f/debugpy-1.8.17-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:3bea3b0b12f3946e098cce9b43c3c46e317b567f79570c3f43f0b96d00788088", size = 4292100, upload-time = "2025-09-17T16:33:56.353Z" }, - { url = "https://files.pythonhosted.org/packages/e8/95/7873cf2146577ef71d2a20bf553f12df865922a6f87b9e8ee1df04f01785/debugpy-1.8.17-cp313-cp313-win32.whl", hash = "sha256:e34ee844c2f17b18556b5bbe59e1e2ff4e86a00282d2a46edab73fd7f18f4a83", size = 5277002, upload-time = "2025-09-17T16:33:58.231Z" }, - { url = "https://files.pythonhosted.org/packages/46/11/18c79a1cee5ff539a94ec4aa290c1c069a5580fd5cfd2fb2e282f8e905da/debugpy-1.8.17-cp313-cp313-win_amd64.whl", hash = "sha256:6c5cd6f009ad4fca8e33e5238210dc1e5f42db07d4b6ab21ac7ffa904a196420", size = 5319047, upload-time = "2025-09-17T16:34:00.586Z" }, - { url = "https://files.pythonhosted.org/packages/de/45/115d55b2a9da6de812696064ceb505c31e952c5d89c4ed1d9bb983deec34/debugpy-1.8.17-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:045290c010bcd2d82bc97aa2daf6837443cd52f6328592698809b4549babcee1", size = 2536899, upload-time = "2025-09-17T16:34:02.657Z" }, - { url = "https://files.pythonhosted.org/packages/5a/73/2aa00c7f1f06e997ef57dc9b23d61a92120bec1437a012afb6d176585197/debugpy-1.8.17-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:b69b6bd9dba6a03632534cdf67c760625760a215ae289f7489a452af1031fe1f", size = 4268254, upload-time = "2025-09-17T16:34:04.486Z" }, - { url = "https://files.pythonhosted.org/packages/86/b5/ed3e65c63c68a6634e3ba04bd10255c8e46ec16ebed7d1c79e4816d8a760/debugpy-1.8.17-cp314-cp314-win32.whl", hash = "sha256:5c59b74aa5630f3a5194467100c3b3d1c77898f9ab27e3f7dc5d40fc2f122670", size = 5277203, upload-time = "2025-09-17T16:34:06.65Z" }, - { url = "https://files.pythonhosted.org/packages/b0/26/394276b71c7538445f29e792f589ab7379ae70fd26ff5577dfde71158e96/debugpy-1.8.17-cp314-cp314-win_amd64.whl", hash = "sha256:893cba7bb0f55161de4365584b025f7064e1f88913551bcd23be3260b231429c", size = 5318493, upload-time = "2025-09-17T16:34:08.483Z" }, - { url = "https://files.pythonhosted.org/packages/b0/d0/89247ec250369fc76db477720a26b2fce7ba079ff1380e4ab4529d2fe233/debugpy-1.8.17-py2.py3-none-any.whl", hash = "sha256:60c7dca6571efe660ccb7a9508d73ca14b8796c4ed484c2002abba714226cfef", size = 5283210, upload-time = "2025-09-17T16:34:25.835Z" }, +version = "1.8.19" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/73/75/9e12d4d42349b817cd545b89247696c67917aab907012ae5b64bbfea3199/debugpy-1.8.19.tar.gz", hash = "sha256:eea7e5987445ab0b5ed258093722d5ecb8bb72217c5c9b1e21f64efe23ddebdb", size = 1644590, upload-time = "2025-12-15T21:53:28.044Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/98/d57054371887f37d3c959a7a8dc3c76b763acb65f5e78d849d7db7cadc5b/debugpy-1.8.19-cp310-cp310-macosx_15_0_x86_64.whl", hash = "sha256:fce6da15d73be5935b4438435c53adb512326a3e11e4f90793ea87cd9f018254", size = 2098493, upload-time = "2025-12-15T21:53:30.149Z" }, + { url = "https://files.pythonhosted.org/packages/ee/dd/c517b9aa3500157a30e4f4c4f5149f880026bd039d2b940acd2383a85d8e/debugpy-1.8.19-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:e24b1652a1df1ab04d81e7ead446a91c226de704ff5dde6bd0a0dbaab07aa3f2", size = 3087875, upload-time = "2025-12-15T21:53:31.511Z" }, + { url = "https://files.pythonhosted.org/packages/d8/57/3d5a5b0da9b63445253107ead151eff29190c6ad7440c68d1a59d56613aa/debugpy-1.8.19-cp310-cp310-win32.whl", hash = "sha256:327cb28c3ad9e17bc925efc7f7018195fd4787c2fe4b7af1eec11f1d19bdec62", size = 5239378, upload-time = "2025-12-15T21:53:32.979Z" }, + { url = "https://files.pythonhosted.org/packages/a6/36/7f9053c4c549160c87ae7e43800138f2695578c8b65947114c97250983b6/debugpy-1.8.19-cp310-cp310-win_amd64.whl", hash = "sha256:b7dd275cf2c99e53adb9654f5ae015f70415bbe2bacbe24cfee30d54b6aa03c5", size = 5271129, upload-time = "2025-12-15T21:53:35.085Z" }, + { url = "https://files.pythonhosted.org/packages/80/e2/48531a609b5a2aa94c6b6853afdfec8da05630ab9aaa96f1349e772119e9/debugpy-1.8.19-cp311-cp311-macosx_15_0_universal2.whl", hash = "sha256:c5dcfa21de1f735a4f7ced4556339a109aa0f618d366ede9da0a3600f2516d8b", size = 2207620, upload-time = "2025-12-15T21:53:37.1Z" }, + { url = "https://files.pythonhosted.org/packages/1b/d4/97775c01d56071969f57d93928899e5616a4cfbbf4c8cc75390d3a51c4a4/debugpy-1.8.19-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:806d6800246244004625d5222d7765874ab2d22f3ba5f615416cf1342d61c488", size = 3170796, upload-time = "2025-12-15T21:53:38.513Z" }, + { url = "https://files.pythonhosted.org/packages/8d/7e/8c7681bdb05be9ec972bbb1245eb7c4c7b0679bb6a9e6408d808bc876d3d/debugpy-1.8.19-cp311-cp311-win32.whl", hash = "sha256:783a519e6dfb1f3cd773a9bda592f4887a65040cb0c7bd38dde410f4e53c40d4", size = 5164287, upload-time = "2025-12-15T21:53:40.857Z" }, + { url = "https://files.pythonhosted.org/packages/f2/a8/aaac7ff12ddf5d68a39e13a423a8490426f5f661384f5ad8d9062761bd8e/debugpy-1.8.19-cp311-cp311-win_amd64.whl", hash = "sha256:14035cbdbb1fe4b642babcdcb5935c2da3b1067ac211c5c5a8fdc0bb31adbcaa", size = 5188269, upload-time = "2025-12-15T21:53:42.359Z" }, + { url = "https://files.pythonhosted.org/packages/4a/15/d762e5263d9e25b763b78be72dc084c7a32113a0bac119e2f7acae7700ed/debugpy-1.8.19-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:bccb1540a49cde77edc7ce7d9d075c1dbeb2414751bc0048c7a11e1b597a4c2e", size = 2549995, upload-time = "2025-12-15T21:53:43.773Z" }, + { url = "https://files.pythonhosted.org/packages/a7/88/f7d25c68b18873b7c53d7c156ca7a7ffd8e77073aa0eac170a9b679cf786/debugpy-1.8.19-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:e9c68d9a382ec754dc05ed1d1b4ed5bd824b9f7c1a8cd1083adb84b3c93501de", size = 4309891, upload-time = "2025-12-15T21:53:45.26Z" }, + { url = "https://files.pythonhosted.org/packages/c5/4f/a65e973aba3865794da65f71971dca01ae66666132c7b2647182d5be0c5f/debugpy-1.8.19-cp312-cp312-win32.whl", hash = "sha256:6599cab8a783d1496ae9984c52cb13b7c4a3bd06a8e6c33446832a5d97ce0bee", size = 5286355, upload-time = "2025-12-15T21:53:46.763Z" }, + { url = "https://files.pythonhosted.org/packages/d8/3a/d3d8b48fec96e3d824e404bf428276fb8419dfa766f78f10b08da1cb2986/debugpy-1.8.19-cp312-cp312-win_amd64.whl", hash = "sha256:66e3d2fd8f2035a8f111eb127fa508469dfa40928a89b460b41fd988684dc83d", size = 5328239, upload-time = "2025-12-15T21:53:48.868Z" }, + { url = "https://files.pythonhosted.org/packages/71/3d/388035a31a59c26f1ecc8d86af607d0c42e20ef80074147cd07b180c4349/debugpy-1.8.19-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:91e35db2672a0abaf325f4868fcac9c1674a0d9ad9bb8a8c849c03a5ebba3e6d", size = 2538859, upload-time = "2025-12-15T21:53:50.478Z" }, + { url = "https://files.pythonhosted.org/packages/4a/19/c93a0772d0962294f083dbdb113af1a7427bb632d36e5314297068f55db7/debugpy-1.8.19-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:85016a73ab84dea1c1f1dcd88ec692993bcbe4532d1b49ecb5f3c688ae50c606", size = 4292575, upload-time = "2025-12-15T21:53:51.821Z" }, + { url = "https://files.pythonhosted.org/packages/5c/56/09e48ab796b0a77e3d7dc250f95251832b8bf6838c9632f6100c98bdf426/debugpy-1.8.19-cp313-cp313-win32.whl", hash = "sha256:b605f17e89ba0ecee994391194285fada89cee111cfcd29d6f2ee11cbdc40976", size = 5286209, upload-time = "2025-12-15T21:53:53.602Z" }, + { url = "https://files.pythonhosted.org/packages/fb/4e/931480b9552c7d0feebe40c73725dd7703dcc578ba9efc14fe0e6d31cfd1/debugpy-1.8.19-cp313-cp313-win_amd64.whl", hash = "sha256:c30639998a9f9cd9699b4b621942c0179a6527f083c72351f95c6ab1728d5b73", size = 5328206, upload-time = "2025-12-15T21:53:55.433Z" }, + { url = "https://files.pythonhosted.org/packages/f6/b9/cbec520c3a00508327476c7fce26fbafef98f412707e511eb9d19a2ef467/debugpy-1.8.19-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:1e8c4d1bd230067bf1bbcdbd6032e5a57068638eb28b9153d008ecde288152af", size = 2537372, upload-time = "2025-12-15T21:53:57.318Z" }, + { url = "https://files.pythonhosted.org/packages/88/5e/cf4e4dc712a141e10d58405c58c8268554aec3c35c09cdcda7535ff13f76/debugpy-1.8.19-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:d40c016c1f538dbf1762936e3aeb43a89b965069d9f60f9e39d35d9d25e6b809", size = 4268729, upload-time = "2025-12-15T21:53:58.712Z" }, + { url = "https://files.pythonhosted.org/packages/82/a3/c91a087ab21f1047db328c1d3eb5d1ff0e52de9e74f9f6f6fa14cdd93d58/debugpy-1.8.19-cp314-cp314-win32.whl", hash = "sha256:0601708223fe1cd0e27c6cce67a899d92c7d68e73690211e6788a4b0e1903f5b", size = 5286388, upload-time = "2025-12-15T21:54:00.687Z" }, + { url = "https://files.pythonhosted.org/packages/17/b8/bfdc30b6e94f1eff09f2dc9cc1f9cd1c6cde3d996bcbd36ce2d9a4956e99/debugpy-1.8.19-cp314-cp314-win_amd64.whl", hash = "sha256:8e19a725f5d486f20e53a1dde2ab8bb2c9607c40c00a42ab646def962b41125f", size = 5327741, upload-time = "2025-12-15T21:54:02.148Z" }, + { url = "https://files.pythonhosted.org/packages/25/3e/e27078370414ef35fafad2c06d182110073daaeb5d3bf734b0b1eeefe452/debugpy-1.8.19-py2.py3-none-any.whl", hash = "sha256:360ffd231a780abbc414ba0f005dad409e71c78637efe8f2bd75837132a41d38", size = 5292321, upload-time = "2025-12-15T21:54:16.024Z" }, ] [[package]] @@ -752,68 +752,68 @@ wheels = [ [[package]] name = "filelock" -version = "3.20.0" +version = "3.20.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922, upload-time = "2025-10-08T18:03:50.056Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/23/ce7a1126827cedeb958fc043d61745754464eb56c5937c35bbf2b8e26f34/filelock-3.20.1.tar.gz", hash = "sha256:b8360948b351b80f420878d8516519a2204b07aefcdcfd24912a5d33127f188c", size = 19476, upload-time = "2025-12-15T23:54:28.027Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054, upload-time = "2025-10-08T18:03:48.35Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7f/a1a97644e39e7316d850784c642093c99df1290a460df4ede27659056834/filelock-3.20.1-py3-none-any.whl", hash = "sha256:15d9e9a67306188a44baa72f569d2bfd803076269365fdea0934385da4dc361a", size = 16666, upload-time = "2025-12-15T23:54:26.874Z" }, ] [[package]] name = "fonttools" -version = "4.61.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/33/f9/0e84d593c0e12244150280a630999835a64f2852276161b62a0f98318de0/fonttools-4.61.0.tar.gz", hash = "sha256:ec520a1f0c7758d7a858a00f090c1745f6cde6a7c5e76fb70ea4044a15f712e7", size = 3561884, upload-time = "2025-11-28T17:05:49.491Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/29/f3/91bba2721fb173fc68e09d15b6ccf3ad4f83d127fbff579be7e5984888a6/fonttools-4.61.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dc25a4a9c1225653e4431a9413d0381b1c62317b0f543bdcec24e1991f612f33", size = 2850151, upload-time = "2025-11-28T17:04:14.214Z" }, - { url = "https://files.pythonhosted.org/packages/f5/8c/a1691dec01038ac7e7bb3ab83300dcc5087b11d8f48640928c02a873eb92/fonttools-4.61.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b493c32d2555e9944ec1b911ea649ff8f01a649ad9cba6c118d6798e932b3f0", size = 2389769, upload-time = "2025-11-28T17:04:16.443Z" }, - { url = "https://files.pythonhosted.org/packages/2d/dd/5bb369a44319d92ba25612511eb8ed2a6fa75239979e0388907525626902/fonttools-4.61.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad751319dc532a79bdf628b8439af167181b4210a0cd28a8935ca615d9fdd727", size = 4893189, upload-time = "2025-11-28T17:04:18.398Z" }, - { url = "https://files.pythonhosted.org/packages/5e/02/51373fa8846bd22bb54e5efb30a824b417b058083f775a194a432f21a45f/fonttools-4.61.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2de14557d113faa5fb519f7f29c3abe4d69c17fe6a5a2595cc8cda7338029219", size = 4854415, upload-time = "2025-11-28T17:04:20.421Z" }, - { url = "https://files.pythonhosted.org/packages/8b/64/9cdbbb804577a7e6191448851c57e6a36eb02aa4bf6a9668b528c968e44e/fonttools-4.61.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:59587bbe455dbdf75354a9dbca1697a35a8903e01fab4248d6b98a17032cee52", size = 4870927, upload-time = "2025-11-28T17:04:22.625Z" }, - { url = "https://files.pythonhosted.org/packages/92/68/e40b22919dc96dc30a70b58fec609ab85112de950bdecfadf8dd478c5a88/fonttools-4.61.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:46cb3d9279f758ac0cf671dc3482da877104b65682679f01b246515db03dbb72", size = 4988674, upload-time = "2025-11-28T17:04:24.675Z" }, - { url = "https://files.pythonhosted.org/packages/9b/5c/e857349ce8aedb2451b9448282e86544b2b7f1c8b10ea0fe49b7cb369b72/fonttools-4.61.0-cp310-cp310-win32.whl", hash = "sha256:58b4f1b78dfbfe855bb8a6801b31b8cdcca0e2847ec769ad8e0b0b692832dd3b", size = 1497663, upload-time = "2025-11-28T17:04:26.598Z" }, - { url = "https://files.pythonhosted.org/packages/f9/0c/62961d5fe6f764d6cbc387ef2c001f5f610808c7aded837409836c0b3e7c/fonttools-4.61.0-cp310-cp310-win_amd64.whl", hash = "sha256:68704a8bbe0b61976262b255e90cde593dc0fe3676542d9b4d846bad2a890a76", size = 1546143, upload-time = "2025-11-28T17:04:28.432Z" }, - { url = "https://files.pythonhosted.org/packages/fd/be/5aa89cdddf2863d8afbdc19eb8ec5d8d35d40eeeb8e6cf52c5ff1c2dbd33/fonttools-4.61.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a32a16951cbf113d38f1dd8551b277b6e06e0f6f776fece0f99f746d739e1be3", size = 2847553, upload-time = "2025-11-28T17:04:30.539Z" }, - { url = "https://files.pythonhosted.org/packages/0d/3e/6ff643b07cead1236a534f51291ae2981721cf419135af5b740c002a66dd/fonttools-4.61.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:328a9c227984bebaf69f3ac9062265f8f6acc7ddf2e4e344c63358579af0aa3d", size = 2388298, upload-time = "2025-11-28T17:04:32.161Z" }, - { url = "https://files.pythonhosted.org/packages/c3/15/fca8dfbe7b482e6f240b1aad0ed7c6e2e75e7a28efa3d3a03b570617b5e5/fonttools-4.61.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2f0bafc8a3b3749c69cc610e5aa3da832d39c2a37a68f03d18ec9a02ecaac04a", size = 5054133, upload-time = "2025-11-28T17:04:34.035Z" }, - { url = "https://files.pythonhosted.org/packages/6a/a2/821c61c691b21fd09e07528a9a499cc2b075ac83ddb644aa16c9875a64bc/fonttools-4.61.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b5ca59b7417d149cf24e4c1933c9f44b2957424fc03536f132346d5242e0ebe5", size = 5031410, upload-time = "2025-11-28T17:04:36.141Z" }, - { url = "https://files.pythonhosted.org/packages/e8/f6/8b16339e93d03c732c8a23edefe3061b17a5f9107ddc47a3215ecd054cac/fonttools-4.61.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:df8cbce85cf482eb01f4551edca978c719f099c623277bda8332e5dbe7dba09d", size = 5030005, upload-time = "2025-11-28T17:04:38.314Z" }, - { url = "https://files.pythonhosted.org/packages/ac/eb/d4e150427bdaa147755239c931bbce829a88149ade5bfd8a327afe565567/fonttools-4.61.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7fb5b84f48a6a733ca3d7f41aa9551908ccabe8669ffe79586560abcc00a9cfd", size = 5154026, upload-time = "2025-11-28T17:04:40.34Z" }, - { url = "https://files.pythonhosted.org/packages/7f/5f/3dd00ce0dba6759943c707b1830af8c0bcf6f8f1a9fe46cb82e7ac2aaa74/fonttools-4.61.0-cp311-cp311-win32.whl", hash = "sha256:787ef9dfd1ea9fe49573c272412ae5f479d78e671981819538143bec65863865", size = 2276035, upload-time = "2025-11-28T17:04:42.59Z" }, - { url = "https://files.pythonhosted.org/packages/4e/44/798c472f096ddf12955eddb98f4f7c906e7497695d04ce073ddf7161d134/fonttools-4.61.0-cp311-cp311-win_amd64.whl", hash = "sha256:14fafda386377b6131d9e448af42d0926bad47e038de0e5ba1d58c25d621f028", size = 2327290, upload-time = "2025-11-28T17:04:44.57Z" }, - { url = "https://files.pythonhosted.org/packages/00/5d/19e5939f773c7cb05480fe2e881d63870b63ee2b4bdb9a77d55b1d36c7b9/fonttools-4.61.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e24a1565c4e57111ec7f4915f8981ecbb61adf66a55f378fdc00e206059fcfef", size = 2846930, upload-time = "2025-11-28T17:04:46.639Z" }, - { url = "https://files.pythonhosted.org/packages/25/b2/0658faf66f705293bd7e739a4f038302d188d424926be9c59bdad945664b/fonttools-4.61.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e2bfacb5351303cae9f072ccf3fc6ecb437a6f359c0606bae4b1ab6715201d87", size = 2383016, upload-time = "2025-11-28T17:04:48.525Z" }, - { url = "https://files.pythonhosted.org/packages/29/a3/1fa90b95b690f0d7541f48850adc40e9019374d896c1b8148d15012b2458/fonttools-4.61.0-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0bdcf2e29d65c26299cc3d502f4612365e8b90a939f46cd92d037b6cb7bb544a", size = 4949425, upload-time = "2025-11-28T17:04:50.482Z" }, - { url = "https://files.pythonhosted.org/packages/af/00/acf18c00f6c501bd6e05ee930f926186f8a8e268265407065688820f1c94/fonttools-4.61.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e6cd0d9051b8ddaf7385f99dd82ec2a058e2b46cf1f1961e68e1ff20fcbb61af", size = 4999632, upload-time = "2025-11-28T17:04:52.508Z" }, - { url = "https://files.pythonhosted.org/packages/5f/e0/19a2b86e54109b1d2ee8743c96a1d297238ae03243897bc5345c0365f34d/fonttools-4.61.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e074bc07c31406f45c418e17c1722e83560f181d122c412fa9e815df0ff74810", size = 4939438, upload-time = "2025-11-28T17:04:54.437Z" }, - { url = "https://files.pythonhosted.org/packages/04/35/7b57a5f57d46286360355eff8d6b88c64ab6331107f37a273a71c803798d/fonttools-4.61.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5a9b78da5d5faa17e63b2404b77feeae105c1b7e75f26020ab7a27b76e02039f", size = 5088960, upload-time = "2025-11-28T17:04:56.348Z" }, - { url = "https://files.pythonhosted.org/packages/3e/0e/6c5023eb2e0fe5d1ababc7e221e44acd3ff668781489cc1937a6f83d620a/fonttools-4.61.0-cp312-cp312-win32.whl", hash = "sha256:9821ed77bb676736b88fa87a737c97b6af06e8109667e625a4f00158540ce044", size = 2264404, upload-time = "2025-11-28T17:04:58.149Z" }, - { url = "https://files.pythonhosted.org/packages/36/0b/63273128c7c5df19b1e4cd92e0a1e6ea5bb74a400c4905054c96ad60a675/fonttools-4.61.0-cp312-cp312-win_amd64.whl", hash = "sha256:0011d640afa61053bc6590f9a3394bd222de7cfde19346588beabac374e9d8ac", size = 2314427, upload-time = "2025-11-28T17:04:59.812Z" }, - { url = "https://files.pythonhosted.org/packages/17/45/334f0d7f181e5473cfb757e1b60f4e60e7fc64f28d406e5d364a952718c0/fonttools-4.61.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba774b8cbd8754f54b8eb58124e8bd45f736b2743325ab1a5229698942b9b433", size = 2841801, upload-time = "2025-11-28T17:05:01.621Z" }, - { url = "https://files.pythonhosted.org/packages/cc/63/97b9c78e1f79bc741d4efe6e51f13872d8edb2b36e1b9fb2bab0d4491bb7/fonttools-4.61.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c84b430616ed73ce46e9cafd0bf0800e366a3e02fb7e1ad7c1e214dbe3862b1f", size = 2379024, upload-time = "2025-11-28T17:05:03.668Z" }, - { url = "https://files.pythonhosted.org/packages/4e/80/c87bc524a90dbeb2a390eea23eae448286983da59b7e02c67fa0ca96a8c5/fonttools-4.61.0-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b2b734d8391afe3c682320840c8191de9bd24e7eb85768dd4dc06ed1b63dbb1b", size = 4923706, upload-time = "2025-11-28T17:05:05.494Z" }, - { url = "https://files.pythonhosted.org/packages/6d/f6/a3b0374811a1de8c3f9207ec88f61ad1bb96f938ed89babae26c065c2e46/fonttools-4.61.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a5c5fff72bf31b0e558ed085e4fd7ed96eb85881404ecc39ed2a779e7cf724eb", size = 4979751, upload-time = "2025-11-28T17:05:07.665Z" }, - { url = "https://files.pythonhosted.org/packages/a5/3b/30f63b4308b449091573285f9d27619563a84f399946bca3eadc9554afbe/fonttools-4.61.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:14a290c5c93fcab76b7f451e6a4b7721b712d90b3b5ed6908f1abcf794e90d6d", size = 4921113, upload-time = "2025-11-28T17:05:09.551Z" }, - { url = "https://files.pythonhosted.org/packages/41/6c/58e6e9b7d9d8bf2d7010bd7bb493060b39b02a12d1cda64a8bfb116ce760/fonttools-4.61.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:13e3e20a5463bfeb77b3557d04b30bd6a96a6bb5c15c7b2e7908903e69d437a0", size = 5063183, upload-time = "2025-11-28T17:05:11.677Z" }, - { url = "https://files.pythonhosted.org/packages/3f/e3/52c790ab2b07492df059947a1fd7778e105aac5848c0473029a4d20481a2/fonttools-4.61.0-cp313-cp313-win32.whl", hash = "sha256:6781e7a4bb010be1cd69a29927b0305c86b843395f2613bdabe115f7d6ea7f34", size = 2263159, upload-time = "2025-11-28T17:05:13.292Z" }, - { url = "https://files.pythonhosted.org/packages/e9/1f/116013b200fbeba871046554d5d2a45fefa69a05c40e9cdfd0d4fff53edc/fonttools-4.61.0-cp313-cp313-win_amd64.whl", hash = "sha256:c53b47834ae41e8e4829171cc44fec0fdf125545a15f6da41776b926b9645a9a", size = 2313530, upload-time = "2025-11-28T17:05:14.848Z" }, - { url = "https://files.pythonhosted.org/packages/d3/99/59b1e25987787cb714aa9457cee4c9301b7c2153f0b673e2b8679d37669d/fonttools-4.61.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:96dfc9bc1f2302224e48e6ee37e656eddbab810b724b52e9d9c13a57a6abad01", size = 2841429, upload-time = "2025-11-28T17:05:16.671Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b2/4c1911d4332c8a144bb3b44416e274ccca0e297157c971ea1b3fbb855590/fonttools-4.61.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3b2065d94e5d63aafc2591c8b6ccbdb511001d9619f1bca8ad39b745ebeb5efa", size = 2378987, upload-time = "2025-11-28T17:05:18.69Z" }, - { url = "https://files.pythonhosted.org/packages/24/b0/f442e90fde5d2af2ae0cb54008ab6411edc557ee33b824e13e1d04925ac9/fonttools-4.61.0-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e0d87e81e4d869549585ba0beb3f033718501c1095004f5e6aef598d13ebc216", size = 4873270, upload-time = "2025-11-28T17:05:20.625Z" }, - { url = "https://files.pythonhosted.org/packages/bb/04/f5d5990e33053c8a59b90b1d7e10ad9b97a73f42c745304da0e709635fab/fonttools-4.61.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1cfa2eb9bae650e58f0e8ad53c49d19a844d6034d6b259f30f197238abc1ccee", size = 4968270, upload-time = "2025-11-28T17:05:22.515Z" }, - { url = "https://files.pythonhosted.org/packages/94/9f/2091402e0d27c9c8c4bab5de0e5cd146d9609a2d7d1c666bbb75c0011c1a/fonttools-4.61.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4238120002e68296d55e091411c09eab94e111c8ce64716d17df53fd0eb3bb3d", size = 4919799, upload-time = "2025-11-28T17:05:24.437Z" }, - { url = "https://files.pythonhosted.org/packages/a8/72/86adab22fde710b829f8ffbc8f264df01928e5b7a8f6177fa29979ebf256/fonttools-4.61.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b6ceac262cc62bec01b3bb59abccf41b24ef6580869e306a4e88b7e56bb4bdda", size = 5030966, upload-time = "2025-11-28T17:05:26.115Z" }, - { url = "https://files.pythonhosted.org/packages/e8/a7/7c8e31b003349e845b853f5e0a67b95ff6b052fa4f5224f8b72624f5ac69/fonttools-4.61.0-cp314-cp314-win32.whl", hash = "sha256:adbb4ecee1a779469a77377bbe490565effe8fce6fb2e6f95f064de58f8bac85", size = 2267243, upload-time = "2025-11-28T17:05:27.807Z" }, - { url = "https://files.pythonhosted.org/packages/20/ee/f434fe7749360497c52b7dcbcfdbccdaab0a71c59f19d572576066717122/fonttools-4.61.0-cp314-cp314-win_amd64.whl", hash = "sha256:02bdf8e04d1a70476564b8640380f04bb4ac74edc1fc71f1bacb840b3e398ee9", size = 2318822, upload-time = "2025-11-28T17:05:29.882Z" }, - { url = "https://files.pythonhosted.org/packages/33/b3/c16255320255e5c1863ca2b2599bb61a46e2f566db0bbb9948615a8fe692/fonttools-4.61.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:627216062d90ab0d98215176d8b9562c4dd5b61271d35f130bcd30f6a8aaa33a", size = 2924917, upload-time = "2025-11-28T17:05:31.46Z" }, - { url = "https://files.pythonhosted.org/packages/e2/b8/08067ae21de705a817777c02ef36ab0b953cbe91d8adf134f9c2da75ed6d/fonttools-4.61.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7b446623c9cd5f14a59493818eaa80255eec2468c27d2c01b56e05357c263195", size = 2413576, upload-time = "2025-11-28T17:05:33.343Z" }, - { url = "https://files.pythonhosted.org/packages/42/f1/96ff43f92addce2356780fdc203f2966206f3d22ea20e242c27826fd7442/fonttools-4.61.0-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:70e2a0c0182ee75e493ef33061bfebf140ea57e035481d2f95aa03b66c7a0e05", size = 4877447, upload-time = "2025-11-28T17:05:35.278Z" }, - { url = "https://files.pythonhosted.org/packages/d0/1e/a3d8e51ed9ccfd7385e239ae374b78d258a0fb82d82cab99160a014a45d1/fonttools-4.61.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9064b0f55b947e929ac669af5311ab1f26f750214db6dd9a0c97e091e918f486", size = 5095681, upload-time = "2025-11-28T17:05:37.142Z" }, - { url = "https://files.pythonhosted.org/packages/eb/f6/d256bd6c1065c146a0bdddf1c62f542e08ae5b3405dbf3fcc52be272f674/fonttools-4.61.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2cb5e45a824ce14b90510024d0d39dae51bd4fbb54c42a9334ea8c8cf4d95cbe", size = 4974140, upload-time = "2025-11-28T17:05:39.5Z" }, - { url = "https://files.pythonhosted.org/packages/5d/0c/96633eb4b26f138cc48561c6e0c44b4ea48acea56b20b507d6b14f8e80ce/fonttools-4.61.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6e5ca8c62efdec7972dfdfd454415c4db49b89aeaefaaacada432f3b7eea9866", size = 5001741, upload-time = "2025-11-28T17:05:41.424Z" }, - { url = "https://files.pythonhosted.org/packages/6f/9a/3b536bad3be4f26186f296e749ff17bad3e6d57232c104d752d24b2e265b/fonttools-4.61.0-cp314-cp314t-win32.whl", hash = "sha256:63c7125d31abe3e61d7bb917329b5543c5b3448db95f24081a13aaf064360fc8", size = 2330707, upload-time = "2025-11-28T17:05:43.548Z" }, - { url = "https://files.pythonhosted.org/packages/18/ea/e6b9ac610451ee9f04477c311ad126de971f6112cb579fa391d2a8edb00b/fonttools-4.61.0-cp314-cp314t-win_amd64.whl", hash = "sha256:67d841aa272be5500de7f447c40d1d8452783af33b4c3599899319f6ef9ad3c1", size = 2395950, upload-time = "2025-11-28T17:05:45.638Z" }, - { url = "https://files.pythonhosted.org/packages/0c/14/634f7daea5ffe6a5f7a0322ba8e1a0e23c9257b80aa91458107896d1dfc7/fonttools-4.61.0-py3-none-any.whl", hash = "sha256:276f14c560e6f98d24ef7f5f44438e55ff5a67f78fa85236b218462c9f5d0635", size = 1144485, upload-time = "2025-11-28T17:05:47.573Z" }, +version = "4.61.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/ca/cf17b88a8df95691275a3d77dc0a5ad9907f328ae53acbe6795da1b2f5ed/fonttools-4.61.1.tar.gz", hash = "sha256:6675329885c44657f826ef01d9e4fb33b9158e9d93c537d84ad8399539bc6f69", size = 3565756, upload-time = "2025-12-12T17:31:24.246Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/94/8a28707adb00bed1bf22dac16ccafe60faf2ade353dcb32c3617ee917307/fonttools-4.61.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c7db70d57e5e1089a274cbb2b1fd635c9a24de809a231b154965d415d6c6d24", size = 2854799, upload-time = "2025-12-12T17:29:27.5Z" }, + { url = "https://files.pythonhosted.org/packages/94/93/c2e682faaa5ee92034818d8f8a8145ae73eb83619600495dcf8503fa7771/fonttools-4.61.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5fe9fd43882620017add5eabb781ebfbc6998ee49b35bd7f8f79af1f9f99a958", size = 2403032, upload-time = "2025-12-12T17:29:30.115Z" }, + { url = "https://files.pythonhosted.org/packages/f1/62/1748f7e7e1ee41aa52279fd2e3a6d0733dc42a673b16932bad8e5d0c8b28/fonttools-4.61.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8db08051fc9e7d8bc622f2112511b8107d8f27cd89e2f64ec45e9825e8288da", size = 4897863, upload-time = "2025-12-12T17:29:32.535Z" }, + { url = "https://files.pythonhosted.org/packages/69/69/4ca02ee367d2c98edcaeb83fc278d20972502ee071214ad9d8ca85e06080/fonttools-4.61.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a76d4cb80f41ba94a6691264be76435e5f72f2cb3cab0b092a6212855f71c2f6", size = 4859076, upload-time = "2025-12-12T17:29:34.907Z" }, + { url = "https://files.pythonhosted.org/packages/8c/f5/660f9e3cefa078861a7f099107c6d203b568a6227eef163dd173bfc56bdc/fonttools-4.61.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a13fc8aeb24bad755eea8f7f9d409438eb94e82cf86b08fe77a03fbc8f6a96b1", size = 4875623, upload-time = "2025-12-12T17:29:37.33Z" }, + { url = "https://files.pythonhosted.org/packages/63/d1/9d7c5091d2276ed47795c131c1bf9316c3c1ab2789c22e2f59e0572ccd38/fonttools-4.61.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b846a1fcf8beadeb9ea4f44ec5bdde393e2f1569e17d700bfc49cd69bde75881", size = 4993327, upload-time = "2025-12-12T17:29:39.781Z" }, + { url = "https://files.pythonhosted.org/packages/6f/2d/28def73837885ae32260d07660a052b99f0aa00454867d33745dfe49dbf0/fonttools-4.61.1-cp310-cp310-win32.whl", hash = "sha256:78a7d3ab09dc47ac1a363a493e6112d8cabed7ba7caad5f54dbe2f08676d1b47", size = 1502180, upload-time = "2025-12-12T17:29:42.217Z" }, + { url = "https://files.pythonhosted.org/packages/63/fa/bfdc98abb4dd2bd491033e85e3ba69a2313c850e759a6daa014bc9433b0f/fonttools-4.61.1-cp310-cp310-win_amd64.whl", hash = "sha256:eff1ac3cc66c2ac7cda1e64b4e2f3ffef474b7335f92fc3833fc632d595fcee6", size = 1550654, upload-time = "2025-12-12T17:29:44.564Z" }, + { url = "https://files.pythonhosted.org/packages/69/12/bf9f4eaa2fad039356cc627587e30ed008c03f1cebd3034376b5ee8d1d44/fonttools-4.61.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c6604b735bb12fef8e0efd5578c9fb5d3d8532d5001ea13a19cddf295673ee09", size = 2852213, upload-time = "2025-12-12T17:29:46.675Z" }, + { url = "https://files.pythonhosted.org/packages/ac/49/4138d1acb6261499bedde1c07f8c2605d1d8f9d77a151e5507fd3ef084b6/fonttools-4.61.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5ce02f38a754f207f2f06557523cd39a06438ba3aafc0639c477ac409fc64e37", size = 2401689, upload-time = "2025-12-12T17:29:48.769Z" }, + { url = "https://files.pythonhosted.org/packages/e5/fe/e6ce0fe20a40e03aef906af60aa87668696f9e4802fa283627d0b5ed777f/fonttools-4.61.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77efb033d8d7ff233385f30c62c7c79271c8885d5c9657d967ede124671bbdfb", size = 5058809, upload-time = "2025-12-12T17:29:51.701Z" }, + { url = "https://files.pythonhosted.org/packages/79/61/1ca198af22f7dd22c17ab86e9024ed3c06299cfdb08170640e9996d501a0/fonttools-4.61.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:75c1a6dfac6abd407634420c93864a1e274ebc1c7531346d9254c0d8f6ca00f9", size = 5036039, upload-time = "2025-12-12T17:29:53.659Z" }, + { url = "https://files.pythonhosted.org/packages/99/cc/fa1801e408586b5fce4da9f5455af8d770f4fc57391cd5da7256bb364d38/fonttools-4.61.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0de30bfe7745c0d1ffa2b0b7048fb7123ad0d71107e10ee090fa0b16b9452e87", size = 5034714, upload-time = "2025-12-12T17:29:55.592Z" }, + { url = "https://files.pythonhosted.org/packages/bf/aa/b7aeafe65adb1b0a925f8f25725e09f078c635bc22754f3fecb7456955b0/fonttools-4.61.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:58b0ee0ab5b1fc9921eccfe11d1435added19d6494dde14e323f25ad2bc30c56", size = 5158648, upload-time = "2025-12-12T17:29:57.861Z" }, + { url = "https://files.pythonhosted.org/packages/99/f9/08ea7a38663328881384c6e7777bbefc46fd7d282adfd87a7d2b84ec9d50/fonttools-4.61.1-cp311-cp311-win32.whl", hash = "sha256:f79b168428351d11e10c5aeb61a74e1851ec221081299f4cf56036a95431c43a", size = 2280681, upload-time = "2025-12-12T17:29:59.943Z" }, + { url = "https://files.pythonhosted.org/packages/07/ad/37dd1ae5fa6e01612a1fbb954f0927681f282925a86e86198ccd7b15d515/fonttools-4.61.1-cp311-cp311-win_amd64.whl", hash = "sha256:fe2efccb324948a11dd09d22136fe2ac8a97d6c1347cf0b58a911dcd529f66b7", size = 2331951, upload-time = "2025-12-12T17:30:02.254Z" }, + { url = "https://files.pythonhosted.org/packages/6f/16/7decaa24a1bd3a70c607b2e29f0adc6159f36a7e40eaba59846414765fd4/fonttools-4.61.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f3cb4a569029b9f291f88aafc927dd53683757e640081ca8c412781ea144565e", size = 2851593, upload-time = "2025-12-12T17:30:04.225Z" }, + { url = "https://files.pythonhosted.org/packages/94/98/3c4cb97c64713a8cf499b3245c3bf9a2b8fd16a3e375feff2aed78f96259/fonttools-4.61.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41a7170d042e8c0024703ed13b71893519a1a6d6e18e933e3ec7507a2c26a4b2", size = 2400231, upload-time = "2025-12-12T17:30:06.47Z" }, + { url = "https://files.pythonhosted.org/packages/b7/37/82dbef0f6342eb01f54bca073ac1498433d6ce71e50c3c3282b655733b31/fonttools-4.61.1-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10d88e55330e092940584774ee5e8a6971b01fc2f4d3466a1d6c158230880796", size = 4954103, upload-time = "2025-12-12T17:30:08.432Z" }, + { url = "https://files.pythonhosted.org/packages/6c/44/f3aeac0fa98e7ad527f479e161aca6c3a1e47bb6996b053d45226fe37bf2/fonttools-4.61.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:15acc09befd16a0fb8a8f62bc147e1a82817542d72184acca9ce6e0aeda9fa6d", size = 5004295, upload-time = "2025-12-12T17:30:10.56Z" }, + { url = "https://files.pythonhosted.org/packages/14/e8/7424ced75473983b964d09f6747fa09f054a6d656f60e9ac9324cf40c743/fonttools-4.61.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e6bcdf33aec38d16508ce61fd81838f24c83c90a1d1b8c68982857038673d6b8", size = 4944109, upload-time = "2025-12-12T17:30:12.874Z" }, + { url = "https://files.pythonhosted.org/packages/c8/8b/6391b257fa3d0b553d73e778f953a2f0154292a7a7a085e2374b111e5410/fonttools-4.61.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5fade934607a523614726119164ff621e8c30e8fa1ffffbbd358662056ba69f0", size = 5093598, upload-time = "2025-12-12T17:30:15.79Z" }, + { url = "https://files.pythonhosted.org/packages/d9/71/fd2ea96cdc512d92da5678a1c98c267ddd4d8c5130b76d0f7a80f9a9fde8/fonttools-4.61.1-cp312-cp312-win32.whl", hash = "sha256:75da8f28eff26defba42c52986de97b22106cb8f26515b7c22443ebc9c2d3261", size = 2269060, upload-time = "2025-12-12T17:30:18.058Z" }, + { url = "https://files.pythonhosted.org/packages/80/3b/a3e81b71aed5a688e89dfe0e2694b26b78c7d7f39a5ffd8a7d75f54a12a8/fonttools-4.61.1-cp312-cp312-win_amd64.whl", hash = "sha256:497c31ce314219888c0e2fce5ad9178ca83fe5230b01a5006726cdf3ac9f24d9", size = 2319078, upload-time = "2025-12-12T17:30:22.862Z" }, + { url = "https://files.pythonhosted.org/packages/4b/cf/00ba28b0990982530addb8dc3e9e6f2fa9cb5c20df2abdda7baa755e8fe1/fonttools-4.61.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c56c488ab471628ff3bfa80964372fc13504ece601e0d97a78ee74126b2045c", size = 2846454, upload-time = "2025-12-12T17:30:24.938Z" }, + { url = "https://files.pythonhosted.org/packages/5a/ca/468c9a8446a2103ae645d14fee3f610567b7042aba85031c1c65e3ef7471/fonttools-4.61.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dc492779501fa723b04d0ab1f5be046797fee17d27700476edc7ee9ae535a61e", size = 2398191, upload-time = "2025-12-12T17:30:27.343Z" }, + { url = "https://files.pythonhosted.org/packages/a3/4b/d67eedaed19def5967fade3297fed8161b25ba94699efc124b14fb68cdbc/fonttools-4.61.1-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:64102ca87e84261419c3747a0d20f396eb024bdbeb04c2bfb37e2891f5fadcb5", size = 4928410, upload-time = "2025-12-12T17:30:29.771Z" }, + { url = "https://files.pythonhosted.org/packages/b0/8d/6fb3494dfe61a46258cd93d979cf4725ded4eb46c2a4ca35e4490d84daea/fonttools-4.61.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c1b526c8d3f615a7b1867f38a9410849c8f4aef078535742198e942fba0e9bd", size = 4984460, upload-time = "2025-12-12T17:30:32.073Z" }, + { url = "https://files.pythonhosted.org/packages/f7/f1/a47f1d30b3dc00d75e7af762652d4cbc3dff5c2697a0dbd5203c81afd9c3/fonttools-4.61.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:41ed4b5ec103bd306bb68f81dc166e77409e5209443e5773cb4ed837bcc9b0d3", size = 4925800, upload-time = "2025-12-12T17:30:34.339Z" }, + { url = "https://files.pythonhosted.org/packages/a7/01/e6ae64a0981076e8a66906fab01539799546181e32a37a0257b77e4aa88b/fonttools-4.61.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b501c862d4901792adaec7c25b1ecc749e2662543f68bb194c42ba18d6eec98d", size = 5067859, upload-time = "2025-12-12T17:30:36.593Z" }, + { url = "https://files.pythonhosted.org/packages/73/aa/28e40b8d6809a9b5075350a86779163f074d2b617c15d22343fce81918db/fonttools-4.61.1-cp313-cp313-win32.whl", hash = "sha256:4d7092bb38c53bbc78e9255a59158b150bcdc115a1e3b3ce0b5f267dc35dd63c", size = 2267821, upload-time = "2025-12-12T17:30:38.478Z" }, + { url = "https://files.pythonhosted.org/packages/1a/59/453c06d1d83dc0951b69ef692d6b9f1846680342927df54e9a1ca91c6f90/fonttools-4.61.1-cp313-cp313-win_amd64.whl", hash = "sha256:21e7c8d76f62ab13c9472ccf74515ca5b9a761d1bde3265152a6dc58700d895b", size = 2318169, upload-time = "2025-12-12T17:30:40.951Z" }, + { url = "https://files.pythonhosted.org/packages/32/8f/4e7bf82c0cbb738d3c2206c920ca34ca74ef9dabde779030145d28665104/fonttools-4.61.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fff4f534200a04b4a36e7ae3cb74493afe807b517a09e99cb4faa89a34ed6ecd", size = 2846094, upload-time = "2025-12-12T17:30:43.511Z" }, + { url = "https://files.pythonhosted.org/packages/71/09/d44e45d0a4f3a651f23a1e9d42de43bc643cce2971b19e784cc67d823676/fonttools-4.61.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d9203500f7c63545b4ce3799319fe4d9feb1a1b89b28d3cb5abd11b9dd64147e", size = 2396589, upload-time = "2025-12-12T17:30:45.681Z" }, + { url = "https://files.pythonhosted.org/packages/89/18/58c64cafcf8eb677a99ef593121f719e6dcbdb7d1c594ae5a10d4997ca8a/fonttools-4.61.1-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fa646ecec9528bef693415c79a86e733c70a4965dd938e9a226b0fc64c9d2e6c", size = 4877892, upload-time = "2025-12-12T17:30:47.709Z" }, + { url = "https://files.pythonhosted.org/packages/8a/ec/9e6b38c7ba1e09eb51db849d5450f4c05b7e78481f662c3b79dbde6f3d04/fonttools-4.61.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11f35ad7805edba3aac1a3710d104592df59f4b957e30108ae0ba6c10b11dd75", size = 4972884, upload-time = "2025-12-12T17:30:49.656Z" }, + { url = "https://files.pythonhosted.org/packages/5e/87/b5339da8e0256734ba0dbbf5b6cdebb1dd79b01dc8c270989b7bcd465541/fonttools-4.61.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b931ae8f62db78861b0ff1ac017851764602288575d65b8e8ff1963fed419063", size = 4924405, upload-time = "2025-12-12T17:30:51.735Z" }, + { url = "https://files.pythonhosted.org/packages/0b/47/e3409f1e1e69c073a3a6fd8cb886eb18c0bae0ee13db2c8d5e7f8495e8b7/fonttools-4.61.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b148b56f5de675ee16d45e769e69f87623a4944f7443850bf9a9376e628a89d2", size = 5035553, upload-time = "2025-12-12T17:30:54.823Z" }, + { url = "https://files.pythonhosted.org/packages/bf/b6/1f6600161b1073a984294c6c031e1a56ebf95b6164249eecf30012bb2e38/fonttools-4.61.1-cp314-cp314-win32.whl", hash = "sha256:9b666a475a65f4e839d3d10473fad6d47e0a9db14a2f4a224029c5bfde58ad2c", size = 2271915, upload-time = "2025-12-12T17:30:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/52/7b/91e7b01e37cc8eb0e1f770d08305b3655e4f002fc160fb82b3390eabacf5/fonttools-4.61.1-cp314-cp314-win_amd64.whl", hash = "sha256:4f5686e1fe5fce75d82d93c47a438a25bf0d1319d2843a926f741140b2b16e0c", size = 2323487, upload-time = "2025-12-12T17:30:59.804Z" }, + { url = "https://files.pythonhosted.org/packages/39/5c/908ad78e46c61c3e3ed70c3b58ff82ab48437faf84ec84f109592cabbd9f/fonttools-4.61.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:e76ce097e3c57c4bcb67c5aa24a0ecdbd9f74ea9219997a707a4061fbe2707aa", size = 2929571, upload-time = "2025-12-12T17:31:02.574Z" }, + { url = "https://files.pythonhosted.org/packages/bd/41/975804132c6dea64cdbfbaa59f3518a21c137a10cccf962805b301ac6ab2/fonttools-4.61.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9cfef3ab326780c04d6646f68d4b4742aae222e8b8ea1d627c74e38afcbc9d91", size = 2435317, upload-time = "2025-12-12T17:31:04.974Z" }, + { url = "https://files.pythonhosted.org/packages/b0/5a/aef2a0a8daf1ebaae4cfd83f84186d4a72ee08fd6a8451289fcd03ffa8a4/fonttools-4.61.1-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a75c301f96db737e1c5ed5fd7d77d9c34466de16095a266509e13da09751bd19", size = 4882124, upload-time = "2025-12-12T17:31:07.456Z" }, + { url = "https://files.pythonhosted.org/packages/80/33/d6db3485b645b81cea538c9d1c9219d5805f0877fda18777add4671c5240/fonttools-4.61.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:91669ccac46bbc1d09e9273546181919064e8df73488ea087dcac3e2968df9ba", size = 5100391, upload-time = "2025-12-12T17:31:09.732Z" }, + { url = "https://files.pythonhosted.org/packages/6c/d6/675ba631454043c75fcf76f0ca5463eac8eb0666ea1d7badae5fea001155/fonttools-4.61.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c33ab3ca9d3ccd581d58e989d67554e42d8d4ded94ab3ade3508455fe70e65f7", size = 4978800, upload-time = "2025-12-12T17:31:11.681Z" }, + { url = "https://files.pythonhosted.org/packages/7f/33/d3ec753d547a8d2bdaedd390d4a814e8d5b45a093d558f025c6b990b554c/fonttools-4.61.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:664c5a68ec406f6b1547946683008576ef8b38275608e1cee6c061828171c118", size = 5006426, upload-time = "2025-12-12T17:31:13.764Z" }, + { url = "https://files.pythonhosted.org/packages/b4/40/cc11f378b561a67bea850ab50063366a0d1dd3f6d0a30ce0f874b0ad5664/fonttools-4.61.1-cp314-cp314t-win32.whl", hash = "sha256:aed04cabe26f30c1647ef0e8fbb207516fd40fe9472e9439695f5c6998e60ac5", size = 2335377, upload-time = "2025-12-12T17:31:16.49Z" }, + { url = "https://files.pythonhosted.org/packages/e4/ff/c9a2b66b39f8628531ea58b320d66d951267c98c6a38684daa8f50fb02f8/fonttools-4.61.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2180f14c141d2f0f3da43f3a81bc8aa4684860f6b0e6f9e165a4831f24e6a23b", size = 2400613, upload-time = "2025-12-12T17:31:18.769Z" }, + { url = "https://files.pythonhosted.org/packages/c7/4e/ce75a57ff3aebf6fc1f4e9d508b8e5810618a33d900ad6c19eb30b290b97/fonttools-4.61.1-py3-none-any.whl", hash = "sha256:17d2bf5d541add43822bcf0c43d7d847b160c9bb01d15d5007d84e2217aaa371", size = 1148996, upload-time = "2025-12-12T17:31:21.03Z" }, ] [[package]] @@ -1359,7 +1359,7 @@ wheels = [ [[package]] name = "matplotlib" -version = "3.10.7" +version = "3.10.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "contourpy", version = "1.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, @@ -1374,62 +1374,62 @@ dependencies = [ { name = "pyparsing" }, { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ae/e2/d2d5295be2f44c678ebaf3544ba32d20c1f9ef08c49fe47f496180e1db15/matplotlib-3.10.7.tar.gz", hash = "sha256:a06ba7e2a2ef9131c79c49e63dad355d2d878413a0376c1727c8b9335ff731c7", size = 34804865, upload-time = "2025-10-09T00:28:00.669Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/87/3932d5778ab4c025db22710b61f49ccaed3956c5cf46ffb2ffa7492b06d9/matplotlib-3.10.7-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7ac81eee3b7c266dd92cee1cd658407b16c57eed08c7421fa354ed68234de380", size = 8247141, upload-time = "2025-10-09T00:26:06.023Z" }, - { url = "https://files.pythonhosted.org/packages/45/a8/bfed45339160102bce21a44e38a358a1134a5f84c26166de03fb4a53208f/matplotlib-3.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:667ecd5d8d37813a845053d8f5bf110b534c3c9f30e69ebd25d4701385935a6d", size = 8107995, upload-time = "2025-10-09T00:26:08.669Z" }, - { url = "https://files.pythonhosted.org/packages/e2/3c/5692a2d9a5ba848fda3f48d2b607037df96460b941a59ef236404b39776b/matplotlib-3.10.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc1c51b846aca49a5a8b44fbba6a92d583a35c64590ad9e1e950dc88940a4297", size = 8680503, upload-time = "2025-10-09T00:26:10.607Z" }, - { url = "https://files.pythonhosted.org/packages/ab/a0/86ace53c48b05d0e6e9c127b2ace097434901f3e7b93f050791c8243201a/matplotlib-3.10.7-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a11c2e9e72e7de09b7b72e62f3df23317c888299c875e2b778abf1eda8c0a42", size = 9514982, upload-time = "2025-10-09T00:26:12.594Z" }, - { url = "https://files.pythonhosted.org/packages/a6/81/ead71e2824da8f72640a64166d10e62300df4ae4db01a0bac56c5b39fa51/matplotlib-3.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f19410b486fdd139885ace124e57f938c1e6a3210ea13dd29cab58f5d4bc12c7", size = 9566429, upload-time = "2025-10-09T00:26:14.758Z" }, - { url = "https://files.pythonhosted.org/packages/65/7d/954b3067120456f472cce8fdcacaf4a5fcd522478db0c37bb243c7cb59dd/matplotlib-3.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:b498e9e4022f93de2d5a37615200ca01297ceebbb56fe4c833f46862a490f9e3", size = 8108174, upload-time = "2025-10-09T00:26:17.015Z" }, - { url = "https://files.pythonhosted.org/packages/fc/bc/0fb489005669127ec13f51be0c6adc074d7cf191075dab1da9fe3b7a3cfc/matplotlib-3.10.7-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:53b492410a6cd66c7a471de6c924f6ede976e963c0f3097a3b7abfadddc67d0a", size = 8257507, upload-time = "2025-10-09T00:26:19.073Z" }, - { url = "https://files.pythonhosted.org/packages/e2/6a/d42588ad895279ff6708924645b5d2ed54a7fb2dc045c8a804e955aeace1/matplotlib-3.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d9749313deb729f08207718d29c86246beb2ea3fdba753595b55901dee5d2fd6", size = 8119565, upload-time = "2025-10-09T00:26:21.023Z" }, - { url = "https://files.pythonhosted.org/packages/10/b7/4aa196155b4d846bd749cf82aa5a4c300cf55a8b5e0dfa5b722a63c0f8a0/matplotlib-3.10.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2222c7ba2cbde7fe63032769f6eb7e83ab3227f47d997a8453377709b7fe3a5a", size = 8692668, upload-time = "2025-10-09T00:26:22.967Z" }, - { url = "https://files.pythonhosted.org/packages/e6/e7/664d2b97016f46683a02d854d730cfcf54ff92c1dafa424beebef50f831d/matplotlib-3.10.7-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e91f61a064c92c307c5a9dc8c05dc9f8a68f0a3be199d9a002a0622e13f874a1", size = 9521051, upload-time = "2025-10-09T00:26:25.041Z" }, - { url = "https://files.pythonhosted.org/packages/a8/a3/37aef1404efa615f49b5758a5e0261c16dd88f389bc1861e722620e4a754/matplotlib-3.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6f1851eab59ca082c95df5a500106bad73672645625e04538b3ad0f69471ffcc", size = 9576878, upload-time = "2025-10-09T00:26:27.478Z" }, - { url = "https://files.pythonhosted.org/packages/33/cd/b145f9797126f3f809d177ca378de57c45413c5099c5990de2658760594a/matplotlib-3.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:6516ce375109c60ceec579e699524e9d504cd7578506f01150f7a6bc174a775e", size = 8115142, upload-time = "2025-10-09T00:26:29.774Z" }, - { url = "https://files.pythonhosted.org/packages/2e/39/63bca9d2b78455ed497fcf51a9c71df200a11048f48249038f06447fa947/matplotlib-3.10.7-cp311-cp311-win_arm64.whl", hash = "sha256:b172db79759f5f9bc13ef1c3ef8b9ee7b37b0247f987fbbbdaa15e4f87fd46a9", size = 7992439, upload-time = "2025-10-09T00:26:40.32Z" }, - { url = "https://files.pythonhosted.org/packages/be/b3/09eb0f7796932826ec20c25b517d568627754f6c6462fca19e12c02f2e12/matplotlib-3.10.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7a0edb7209e21840e8361e91ea84ea676658aa93edd5f8762793dec77a4a6748", size = 8272389, upload-time = "2025-10-09T00:26:42.474Z" }, - { url = "https://files.pythonhosted.org/packages/11/0b/1ae80ddafb8652fd8046cb5c8460ecc8d4afccb89e2c6d6bec61e04e1eaf/matplotlib-3.10.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c380371d3c23e0eadf8ebff114445b9f970aff2010198d498d4ab4c3b41eea4f", size = 8128247, upload-time = "2025-10-09T00:26:44.77Z" }, - { url = "https://files.pythonhosted.org/packages/7d/18/95ae2e242d4a5c98bd6e90e36e128d71cf1c7e39b0874feaed3ef782e789/matplotlib-3.10.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d5f256d49fea31f40f166a5e3131235a5d2f4b7f44520b1cf0baf1ce568ccff0", size = 8696996, upload-time = "2025-10-09T00:26:46.792Z" }, - { url = "https://files.pythonhosted.org/packages/7e/3d/5b559efc800bd05cb2033aa85f7e13af51958136a48327f7c261801ff90a/matplotlib-3.10.7-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11ae579ac83cdf3fb72573bb89f70e0534de05266728740d478f0f818983c695", size = 9530153, upload-time = "2025-10-09T00:26:49.07Z" }, - { url = "https://files.pythonhosted.org/packages/88/57/eab4a719fd110312d3c220595d63a3c85ec2a39723f0f4e7fa7e6e3f74ba/matplotlib-3.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4c14b6acd16cddc3569a2d515cfdd81c7a68ac5639b76548cfc1a9e48b20eb65", size = 9593093, upload-time = "2025-10-09T00:26:51.067Z" }, - { url = "https://files.pythonhosted.org/packages/31/3c/80816f027b3a4a28cd2a0a6ef7f89a2db22310e945cd886ec25bfb399221/matplotlib-3.10.7-cp312-cp312-win_amd64.whl", hash = "sha256:0d8c32b7ea6fb80b1aeff5a2ceb3fb9778e2759e899d9beff75584714afcc5ee", size = 8122771, upload-time = "2025-10-09T00:26:53.296Z" }, - { url = "https://files.pythonhosted.org/packages/de/77/ef1fc78bfe99999b2675435cc52120887191c566b25017d78beaabef7f2d/matplotlib-3.10.7-cp312-cp312-win_arm64.whl", hash = "sha256:5f3f6d315dcc176ba7ca6e74c7768fb7e4cf566c49cb143f6bc257b62e634ed8", size = 7992812, upload-time = "2025-10-09T00:26:54.882Z" }, - { url = "https://files.pythonhosted.org/packages/02/9c/207547916a02c78f6bdd83448d9b21afbc42f6379ed887ecf610984f3b4e/matplotlib-3.10.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1d9d3713a237970569156cfb4de7533b7c4eacdd61789726f444f96a0d28f57f", size = 8273212, upload-time = "2025-10-09T00:26:56.752Z" }, - { url = "https://files.pythonhosted.org/packages/bc/d0/b3d3338d467d3fc937f0bb7f256711395cae6f78e22cef0656159950adf0/matplotlib-3.10.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:37a1fea41153dd6ee061d21ab69c9cf2cf543160b1b85d89cd3d2e2a7902ca4c", size = 8128713, upload-time = "2025-10-09T00:26:59.001Z" }, - { url = "https://files.pythonhosted.org/packages/22/ff/6425bf5c20d79aa5b959d1ce9e65f599632345391381c9a104133fe0b171/matplotlib-3.10.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b3c4ea4948d93c9c29dc01c0c23eef66f2101bf75158c291b88de6525c55c3d1", size = 8698527, upload-time = "2025-10-09T00:27:00.69Z" }, - { url = "https://files.pythonhosted.org/packages/d0/7f/ccdca06f4c2e6c7989270ed7829b8679466682f4cfc0f8c9986241c023b6/matplotlib-3.10.7-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22df30ffaa89f6643206cf13877191c63a50e8f800b038bc39bee9d2d4957632", size = 9529690, upload-time = "2025-10-09T00:27:02.664Z" }, - { url = "https://files.pythonhosted.org/packages/b8/95/b80fc2c1f269f21ff3d193ca697358e24408c33ce2b106a7438a45407b63/matplotlib-3.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b69676845a0a66f9da30e87f48be36734d6748024b525ec4710be40194282c84", size = 9593732, upload-time = "2025-10-09T00:27:04.653Z" }, - { url = "https://files.pythonhosted.org/packages/e1/b6/23064a96308b9aeceeffa65e96bcde459a2ea4934d311dee20afde7407a0/matplotlib-3.10.7-cp313-cp313-win_amd64.whl", hash = "sha256:744991e0cc863dd669c8dc9136ca4e6e0082be2070b9d793cbd64bec872a6815", size = 8122727, upload-time = "2025-10-09T00:27:06.814Z" }, - { url = "https://files.pythonhosted.org/packages/b3/a6/2faaf48133b82cf3607759027f82b5c702aa99cdfcefb7f93d6ccf26a424/matplotlib-3.10.7-cp313-cp313-win_arm64.whl", hash = "sha256:fba2974df0bf8ce3c995fa84b79cde38326e0f7b5409e7a3a481c1141340bcf7", size = 7992958, upload-time = "2025-10-09T00:27:08.567Z" }, - { url = "https://files.pythonhosted.org/packages/4a/f0/b018fed0b599bd48d84c08794cb242227fe3341952da102ee9d9682db574/matplotlib-3.10.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:932c55d1fa7af4423422cb6a492a31cbcbdbe68fd1a9a3f545aa5e7a143b5355", size = 8316849, upload-time = "2025-10-09T00:27:10.254Z" }, - { url = "https://files.pythonhosted.org/packages/b0/b7/bb4f23856197659f275e11a2a164e36e65e9b48ea3e93c4ec25b4f163198/matplotlib-3.10.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e38c2d581d62ee729a6e144c47a71b3f42fb4187508dbbf4fe71d5612c3433b", size = 8178225, upload-time = "2025-10-09T00:27:12.241Z" }, - { url = "https://files.pythonhosted.org/packages/62/56/0600609893ff277e6f3ab3c0cef4eafa6e61006c058e84286c467223d4d5/matplotlib-3.10.7-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:786656bb13c237bbcebcd402f65f44dd61ead60ee3deb045af429d889c8dbc67", size = 8711708, upload-time = "2025-10-09T00:27:13.879Z" }, - { url = "https://files.pythonhosted.org/packages/d8/1a/6bfecb0cafe94d6658f2f1af22c43b76cf7a1c2f0dc34ef84cbb6809617e/matplotlib-3.10.7-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09d7945a70ea43bf9248f4b6582734c2fe726723204a76eca233f24cffc7ef67", size = 9541409, upload-time = "2025-10-09T00:27:15.684Z" }, - { url = "https://files.pythonhosted.org/packages/08/50/95122a407d7f2e446fd865e2388a232a23f2b81934960ea802f3171518e4/matplotlib-3.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d0b181e9fa8daf1d9f2d4c547527b167cb8838fc587deabca7b5c01f97199e84", size = 9594054, upload-time = "2025-10-09T00:27:17.547Z" }, - { url = "https://files.pythonhosted.org/packages/13/76/75b194a43b81583478a81e78a07da8d9ca6ddf50dd0a2ccabf258059481d/matplotlib-3.10.7-cp313-cp313t-win_amd64.whl", hash = "sha256:31963603041634ce1a96053047b40961f7a29eb8f9a62e80cc2c0427aa1d22a2", size = 8200100, upload-time = "2025-10-09T00:27:20.039Z" }, - { url = "https://files.pythonhosted.org/packages/f5/9e/6aefebdc9f8235c12bdeeda44cc0383d89c1e41da2c400caf3ee2073a3ce/matplotlib-3.10.7-cp313-cp313t-win_arm64.whl", hash = "sha256:aebed7b50aa6ac698c90f60f854b47e48cd2252b30510e7a1feddaf5a3f72cbf", size = 8042131, upload-time = "2025-10-09T00:27:21.608Z" }, - { url = "https://files.pythonhosted.org/packages/0d/4b/e5bc2c321b6a7e3a75638d937d19ea267c34bd5a90e12bee76c4d7c7a0d9/matplotlib-3.10.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d883460c43e8c6b173fef244a2341f7f7c0e9725c7fe68306e8e44ed9c8fb100", size = 8273787, upload-time = "2025-10-09T00:27:23.27Z" }, - { url = "https://files.pythonhosted.org/packages/86/ad/6efae459c56c2fbc404da154e13e3a6039129f3c942b0152624f1c621f05/matplotlib-3.10.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:07124afcf7a6504eafcb8ce94091c5898bbdd351519a1beb5c45f7a38c67e77f", size = 8131348, upload-time = "2025-10-09T00:27:24.926Z" }, - { url = "https://files.pythonhosted.org/packages/a6/5a/a4284d2958dee4116359cc05d7e19c057e64ece1b4ac986ab0f2f4d52d5a/matplotlib-3.10.7-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c17398b709a6cce3d9fdb1595c33e356d91c098cd9486cb2cc21ea2ea418e715", size = 9533949, upload-time = "2025-10-09T00:27:26.704Z" }, - { url = "https://files.pythonhosted.org/packages/de/ff/f3781b5057fa3786623ad8976fc9f7b0d02b2f28534751fd5a44240de4cf/matplotlib-3.10.7-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7146d64f561498764561e9cd0ed64fcf582e570fc519e6f521e2d0cfd43365e1", size = 9804247, upload-time = "2025-10-09T00:27:28.514Z" }, - { url = "https://files.pythonhosted.org/packages/47/5a/993a59facb8444efb0e197bf55f545ee449902dcee86a4dfc580c3b61314/matplotlib-3.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:90ad854c0a435da3104c01e2c6f0028d7e719b690998a2333d7218db80950722", size = 9595497, upload-time = "2025-10-09T00:27:30.418Z" }, - { url = "https://files.pythonhosted.org/packages/0d/a5/77c95aaa9bb32c345cbb49626ad8eb15550cba2e6d4c88081a6c2ac7b08d/matplotlib-3.10.7-cp314-cp314-win_amd64.whl", hash = "sha256:4645fc5d9d20ffa3a39361fcdbcec731382763b623b72627806bf251b6388866", size = 8252732, upload-time = "2025-10-09T00:27:32.332Z" }, - { url = "https://files.pythonhosted.org/packages/74/04/45d269b4268d222390d7817dae77b159651909669a34ee9fdee336db5883/matplotlib-3.10.7-cp314-cp314-win_arm64.whl", hash = "sha256:9257be2f2a03415f9105c486d304a321168e61ad450f6153d77c69504ad764bb", size = 8124240, upload-time = "2025-10-09T00:27:33.94Z" }, - { url = "https://files.pythonhosted.org/packages/4b/c7/ca01c607bb827158b439208c153d6f14ddb9fb640768f06f7ca3488ae67b/matplotlib-3.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1e4bbad66c177a8fdfa53972e5ef8be72a5f27e6a607cec0d8579abd0f3102b1", size = 8316938, upload-time = "2025-10-09T00:27:35.534Z" }, - { url = "https://files.pythonhosted.org/packages/84/d2/5539e66e9f56d2fdec94bb8436f5e449683b4e199bcc897c44fbe3c99e28/matplotlib-3.10.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d8eb7194b084b12feb19142262165832fc6ee879b945491d1c3d4660748020c4", size = 8178245, upload-time = "2025-10-09T00:27:37.334Z" }, - { url = "https://files.pythonhosted.org/packages/77/b5/e6ca22901fd3e4fe433a82e583436dd872f6c966fca7e63cf806b40356f8/matplotlib-3.10.7-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4d41379b05528091f00e1728004f9a8d7191260f3862178b88e8fd770206318", size = 9541411, upload-time = "2025-10-09T00:27:39.387Z" }, - { url = "https://files.pythonhosted.org/packages/9e/99/a4524db57cad8fee54b7237239a8f8360bfcfa3170d37c9e71c090c0f409/matplotlib-3.10.7-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4a74f79fafb2e177f240579bc83f0b60f82cc47d2f1d260f422a0627207008ca", size = 9803664, upload-time = "2025-10-09T00:27:41.492Z" }, - { url = "https://files.pythonhosted.org/packages/e6/a5/85e2edf76ea0ad4288d174926d9454ea85f3ce5390cc4e6fab196cbf250b/matplotlib-3.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:702590829c30aada1e8cef0568ddbffa77ca747b4d6e36c6d173f66e301f89cc", size = 9594066, upload-time = "2025-10-09T00:27:43.694Z" }, - { url = "https://files.pythonhosted.org/packages/39/69/9684368a314f6d83fe5c5ad2a4121a3a8e03723d2e5c8ea17b66c1bad0e7/matplotlib-3.10.7-cp314-cp314t-win_amd64.whl", hash = "sha256:f79d5de970fc90cd5591f60053aecfce1fcd736e0303d9f0bf86be649fa68fb8", size = 8342832, upload-time = "2025-10-09T00:27:45.543Z" }, - { url = "https://files.pythonhosted.org/packages/04/5f/e22e08da14bc1a0894184640d47819d2338b792732e20d292bf86e5ab785/matplotlib-3.10.7-cp314-cp314t-win_arm64.whl", hash = "sha256:cb783436e47fcf82064baca52ce748af71725d0352e1d31564cbe9c95df92b9c", size = 8172585, upload-time = "2025-10-09T00:27:47.185Z" }, - { url = "https://files.pythonhosted.org/packages/1e/6c/a9bcf03e9afb2a873e0a5855f79bce476d1023f26f8212969f2b7504756c/matplotlib-3.10.7-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5c09cf8f2793f81368f49f118b6f9f937456362bee282eac575cca7f84cda537", size = 8241204, upload-time = "2025-10-09T00:27:48.806Z" }, - { url = "https://files.pythonhosted.org/packages/5b/fd/0e6f5aa762ed689d9fa8750b08f1932628ffa7ed30e76423c399d19407d2/matplotlib-3.10.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:de66744b2bb88d5cd27e80dfc2ec9f0517d0a46d204ff98fe9e5f2864eb67657", size = 8104607, upload-time = "2025-10-09T00:27:50.876Z" }, - { url = "https://files.pythonhosted.org/packages/b9/a9/21c9439d698fac5f0de8fc68b2405b738ed1f00e1279c76f2d9aa5521ead/matplotlib-3.10.7-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:53cc80662dd197ece414dd5b66e07370201515a3eaf52e7c518c68c16814773b", size = 8682257, upload-time = "2025-10-09T00:27:52.597Z" }, - { url = "https://files.pythonhosted.org/packages/58/8f/76d5dc21ac64a49e5498d7f0472c0781dae442dd266a67458baec38288ec/matplotlib-3.10.7-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:15112bcbaef211bd663fa935ec33313b948e214454d949b723998a43357b17b0", size = 8252283, upload-time = "2025-10-09T00:27:54.739Z" }, - { url = "https://files.pythonhosted.org/packages/27/0d/9c5d4c2317feb31d819e38c9f947c942f42ebd4eb935fc6fd3518a11eaa7/matplotlib-3.10.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d2a959c640cdeecdd2ec3136e8ea0441da59bcaf58d67e9c590740addba2cb68", size = 8116733, upload-time = "2025-10-09T00:27:56.406Z" }, - { url = "https://files.pythonhosted.org/packages/9a/cc/3fe688ff1355010937713164caacf9ed443675ac48a997bab6ed23b3f7c0/matplotlib-3.10.7-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3886e47f64611046bc1db523a09dd0a0a6bed6081e6f90e13806dd1d1d1b5e91", size = 8693919, upload-time = "2025-10-09T00:27:58.41Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/8a/76/d3c6e3a13fe484ebe7718d14e269c9569c4eb0020a968a327acb3b9a8fe6/matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3", size = 34806269, upload-time = "2025-12-10T22:56:51.155Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/be/a30bd917018ad220c400169fba298f2bb7003c8ccbc0c3e24ae2aacad1e8/matplotlib-3.10.8-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:00270d217d6b20d14b584c521f810d60c5c78406dc289859776550df837dcda7", size = 8239828, upload-time = "2025-12-10T22:55:02.313Z" }, + { url = "https://files.pythonhosted.org/packages/58/27/ca01e043c4841078e82cf6e80a6993dfecd315c3d79f5f3153afbb8e1ec6/matplotlib-3.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b3c1cc42aa184b3f738cfa18c1c1d72fd496d85467a6cf7b807936d39aa656", size = 8128050, upload-time = "2025-12-10T22:55:04.997Z" }, + { url = "https://files.pythonhosted.org/packages/cb/aa/7ab67f2b729ae6a91bcf9dcac0affb95fb8c56f7fd2b2af894ae0b0cf6fa/matplotlib-3.10.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee40c27c795bda6a5292e9cff9890189d32f7e3a0bf04e0e3c9430c4a00c37df", size = 8700452, upload-time = "2025-12-10T22:55:07.47Z" }, + { url = "https://files.pythonhosted.org/packages/73/ae/2d5817b0acee3c49b7e7ccfbf5b273f284957cc8e270adf36375db353190/matplotlib-3.10.8-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a48f2b74020919552ea25d222d5cc6af9ca3f4eb43a93e14d068457f545c2a17", size = 9534928, upload-time = "2025-12-10T22:55:10.566Z" }, + { url = "https://files.pythonhosted.org/packages/c9/5b/8e66653e9f7c39cb2e5cab25fce4810daffa2bff02cbf5f3077cea9e942c/matplotlib-3.10.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f254d118d14a7f99d616271d6c3c27922c092dac11112670b157798b89bf4933", size = 9586377, upload-time = "2025-12-10T22:55:12.362Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e2/fd0bbadf837f81edb0d208ba8f8cb552874c3b16e27cb91a31977d90875d/matplotlib-3.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:f9b587c9c7274c1613a30afabf65a272114cd6cdbe67b3406f818c79d7ab2e2a", size = 8128127, upload-time = "2025-12-10T22:55:14.436Z" }, + { url = "https://files.pythonhosted.org/packages/f8/86/de7e3a1cdcfc941483af70609edc06b83e7c8a0e0dc9ac325200a3f4d220/matplotlib-3.10.8-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6be43b667360fef5c754dda5d25a32e6307a03c204f3c0fc5468b78fa87b4160", size = 8251215, upload-time = "2025-12-10T22:55:16.175Z" }, + { url = "https://files.pythonhosted.org/packages/fd/14/baad3222f424b19ce6ad243c71de1ad9ec6b2e4eb1e458a48fdc6d120401/matplotlib-3.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2b336e2d91a3d7006864e0990c83b216fcdca64b5a6484912902cef87313d78", size = 8139625, upload-time = "2025-12-10T22:55:17.712Z" }, + { url = "https://files.pythonhosted.org/packages/8f/a0/7024215e95d456de5883e6732e708d8187d9753a21d32f8ddb3befc0c445/matplotlib-3.10.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:efb30e3baaea72ce5928e32bab719ab4770099079d66726a62b11b1ef7273be4", size = 8712614, upload-time = "2025-12-10T22:55:20.8Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f4/b8347351da9a5b3f41e26cf547252d861f685c6867d179a7c9d60ad50189/matplotlib-3.10.8-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d56a1efd5bfd61486c8bc968fa18734464556f0fb8e51690f4ac25d85cbbbbc2", size = 9540997, upload-time = "2025-12-10T22:55:23.258Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c0/c7b914e297efe0bc36917bf216b2acb91044b91e930e878ae12981e461e5/matplotlib-3.10.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:238b7ce5717600615c895050239ec955d91f321c209dd110db988500558e70d6", size = 9596825, upload-time = "2025-12-10T22:55:25.217Z" }, + { url = "https://files.pythonhosted.org/packages/6f/d3/a4bbc01c237ab710a1f22b4da72f4ff6d77eb4c7735ea9811a94ae239067/matplotlib-3.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:18821ace09c763ec93aef5eeff087ee493a24051936d7b9ebcad9662f66501f9", size = 8135090, upload-time = "2025-12-10T22:55:27.162Z" }, + { url = "https://files.pythonhosted.org/packages/89/dd/a0b6588f102beab33ca6f5218b31725216577b2a24172f327eaf6417d5c9/matplotlib-3.10.8-cp311-cp311-win_arm64.whl", hash = "sha256:bab485bcf8b1c7d2060b4fcb6fc368a9e6f4cd754c9c2fea281f4be21df394a2", size = 8012377, upload-time = "2025-12-10T22:55:29.185Z" }, + { url = "https://files.pythonhosted.org/packages/9e/67/f997cdcbb514012eb0d10cd2b4b332667997fb5ebe26b8d41d04962fa0e6/matplotlib-3.10.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:64fcc24778ca0404ce0cb7b6b77ae1f4c7231cdd60e6778f999ee05cbd581b9a", size = 8260453, upload-time = "2025-12-10T22:55:30.709Z" }, + { url = "https://files.pythonhosted.org/packages/7e/65/07d5f5c7f7c994f12c768708bd2e17a4f01a2b0f44a1c9eccad872433e2e/matplotlib-3.10.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b9a5ca4ac220a0cdd1ba6bcba3608547117d30468fefce49bb26f55c1a3d5c58", size = 8148321, upload-time = "2025-12-10T22:55:33.265Z" }, + { url = "https://files.pythonhosted.org/packages/3e/f3/c5195b1ae57ef85339fd7285dfb603b22c8b4e79114bae5f4f0fcf688677/matplotlib-3.10.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3ab4aabc72de4ff77b3ec33a6d78a68227bf1123465887f9905ba79184a1cc04", size = 8716944, upload-time = "2025-12-10T22:55:34.922Z" }, + { url = "https://files.pythonhosted.org/packages/00/f9/7638f5cc82ec8a7aa005de48622eecc3ed7c9854b96ba15bd76b7fd27574/matplotlib-3.10.8-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24d50994d8c5816ddc35411e50a86ab05f575e2530c02752e02538122613371f", size = 9550099, upload-time = "2025-12-10T22:55:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/57/61/78cd5920d35b29fd2a0fe894de8adf672ff52939d2e9b43cb83cd5ce1bc7/matplotlib-3.10.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:99eefd13c0dc3b3c1b4d561c1169e65fe47aab7b8158754d7c084088e2329466", size = 9613040, upload-time = "2025-12-10T22:55:38.715Z" }, + { url = "https://files.pythonhosted.org/packages/30/4e/c10f171b6e2f44d9e3a2b96efa38b1677439d79c99357600a62cc1e9594e/matplotlib-3.10.8-cp312-cp312-win_amd64.whl", hash = "sha256:dd80ecb295460a5d9d260df63c43f4afbdd832d725a531f008dad1664f458adf", size = 8142717, upload-time = "2025-12-10T22:55:41.103Z" }, + { url = "https://files.pythonhosted.org/packages/f1/76/934db220026b5fef85f45d51a738b91dea7d70207581063cd9bd8fafcf74/matplotlib-3.10.8-cp312-cp312-win_arm64.whl", hash = "sha256:3c624e43ed56313651bc18a47f838b60d7b8032ed348911c54906b130b20071b", size = 8012751, upload-time = "2025-12-10T22:55:42.684Z" }, + { url = "https://files.pythonhosted.org/packages/3d/b9/15fd5541ef4f5b9a17eefd379356cf12175fe577424e7b1d80676516031a/matplotlib-3.10.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3f2e409836d7f5ac2f1c013110a4d50b9f7edc26328c108915f9075d7d7a91b6", size = 8261076, upload-time = "2025-12-10T22:55:44.648Z" }, + { url = "https://files.pythonhosted.org/packages/8d/a0/2ba3473c1b66b9c74dc7107c67e9008cb1782edbe896d4c899d39ae9cf78/matplotlib-3.10.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56271f3dac49a88d7fca5060f004d9d22b865f743a12a23b1e937a0be4818ee1", size = 8148794, upload-time = "2025-12-10T22:55:46.252Z" }, + { url = "https://files.pythonhosted.org/packages/75/97/a471f1c3eb1fd6f6c24a31a5858f443891d5127e63a7788678d14e249aea/matplotlib-3.10.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a0a7f52498f72f13d4a25ea70f35f4cb60642b466cbb0a9be951b5bc3f45a486", size = 8718474, upload-time = "2025-12-10T22:55:47.864Z" }, + { url = "https://files.pythonhosted.org/packages/01/be/cd478f4b66f48256f42927d0acbcd63a26a893136456cd079c0cc24fbabf/matplotlib-3.10.8-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:646d95230efb9ca614a7a594d4fcacde0ac61d25e37dd51710b36477594963ce", size = 9549637, upload-time = "2025-12-10T22:55:50.048Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7c/8dc289776eae5109e268c4fb92baf870678dc048a25d4ac903683b86d5bf/matplotlib-3.10.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f89c151aab2e2e23cb3fe0acad1e8b82841fd265379c4cecd0f3fcb34c15e0f6", size = 9613678, upload-time = "2025-12-10T22:55:52.21Z" }, + { url = "https://files.pythonhosted.org/packages/64/40/37612487cc8a437d4dd261b32ca21fe2d79510fe74af74e1f42becb1bdb8/matplotlib-3.10.8-cp313-cp313-win_amd64.whl", hash = "sha256:e8ea3e2d4066083e264e75c829078f9e149fa119d27e19acd503de65e0b13149", size = 8142686, upload-time = "2025-12-10T22:55:54.253Z" }, + { url = "https://files.pythonhosted.org/packages/66/52/8d8a8730e968185514680c2a6625943f70269509c3dcfc0dcf7d75928cb8/matplotlib-3.10.8-cp313-cp313-win_arm64.whl", hash = "sha256:c108a1d6fa78a50646029cb6d49808ff0fc1330fda87fa6f6250c6b5369b6645", size = 8012917, upload-time = "2025-12-10T22:55:56.268Z" }, + { url = "https://files.pythonhosted.org/packages/b5/27/51fe26e1062f298af5ef66343d8ef460e090a27fea73036c76c35821df04/matplotlib-3.10.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ad3d9833a64cf48cc4300f2b406c3d0f4f4724a91c0bd5640678a6ba7c102077", size = 8305679, upload-time = "2025-12-10T22:55:57.856Z" }, + { url = "https://files.pythonhosted.org/packages/2c/1e/4de865bc591ac8e3062e835f42dd7fe7a93168d519557837f0e37513f629/matplotlib-3.10.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:eb3823f11823deade26ce3b9f40dcb4a213da7a670013929f31d5f5ed1055b22", size = 8198336, upload-time = "2025-12-10T22:55:59.371Z" }, + { url = "https://files.pythonhosted.org/packages/c6/cb/2f7b6e75fb4dce87ef91f60cac4f6e34f4c145ab036a22318ec837971300/matplotlib-3.10.8-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d9050fee89a89ed57b4fb2c1bfac9a3d0c57a0d55aed95949eedbc42070fea39", size = 8731653, upload-time = "2025-12-10T22:56:01.032Z" }, + { url = "https://files.pythonhosted.org/packages/46/b3/bd9c57d6ba670a37ab31fb87ec3e8691b947134b201f881665b28cc039ff/matplotlib-3.10.8-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b44d07310e404ba95f8c25aa5536f154c0a8ec473303535949e52eb71d0a1565", size = 9561356, upload-time = "2025-12-10T22:56:02.95Z" }, + { url = "https://files.pythonhosted.org/packages/c0/3d/8b94a481456dfc9dfe6e39e93b5ab376e50998cddfd23f4ae3b431708f16/matplotlib-3.10.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0a33deb84c15ede243aead39f77e990469fff93ad1521163305095b77b72ce4a", size = 9614000, upload-time = "2025-12-10T22:56:05.411Z" }, + { url = "https://files.pythonhosted.org/packages/bd/cd/bc06149fe5585ba800b189a6a654a75f1f127e8aab02fd2be10df7fa500c/matplotlib-3.10.8-cp313-cp313t-win_amd64.whl", hash = "sha256:3a48a78d2786784cc2413e57397981fb45c79e968d99656706018d6e62e57958", size = 8220043, upload-time = "2025-12-10T22:56:07.551Z" }, + { url = "https://files.pythonhosted.org/packages/e3/de/b22cf255abec916562cc04eef457c13e58a1990048de0c0c3604d082355e/matplotlib-3.10.8-cp313-cp313t-win_arm64.whl", hash = "sha256:15d30132718972c2c074cd14638c7f4592bd98719e2308bccea40e0538bc0cb5", size = 8062075, upload-time = "2025-12-10T22:56:09.178Z" }, + { url = "https://files.pythonhosted.org/packages/3c/43/9c0ff7a2f11615e516c3b058e1e6e8f9614ddeca53faca06da267c48345d/matplotlib-3.10.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b53285e65d4fa4c86399979e956235deb900be5baa7fc1218ea67fbfaeaadd6f", size = 8262481, upload-time = "2025-12-10T22:56:10.885Z" }, + { url = "https://files.pythonhosted.org/packages/6f/ca/e8ae28649fcdf039fda5ef554b40a95f50592a3c47e6f7270c9561c12b07/matplotlib-3.10.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32f8dce744be5569bebe789e46727946041199030db8aeb2954d26013a0eb26b", size = 8151473, upload-time = "2025-12-10T22:56:12.377Z" }, + { url = "https://files.pythonhosted.org/packages/f1/6f/009d129ae70b75e88cbe7e503a12a4c0670e08ed748a902c2568909e9eb5/matplotlib-3.10.8-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4cf267add95b1c88300d96ca837833d4112756045364f5c734a2276038dae27d", size = 9553896, upload-time = "2025-12-10T22:56:14.432Z" }, + { url = "https://files.pythonhosted.org/packages/f5/26/4221a741eb97967bc1fd5e4c52b9aa5a91b2f4ec05b59f6def4d820f9df9/matplotlib-3.10.8-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2cf5bd12cecf46908f286d7838b2abc6c91cda506c0445b8223a7c19a00df008", size = 9824193, upload-time = "2025-12-10T22:56:16.29Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f3/3abf75f38605772cf48a9daf5821cd4f563472f38b4b828c6fba6fa6d06e/matplotlib-3.10.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:41703cc95688f2516b480f7f339d8851a6035f18e100ee6a32bc0b8536a12a9c", size = 9615444, upload-time = "2025-12-10T22:56:18.155Z" }, + { url = "https://files.pythonhosted.org/packages/93/a5/de89ac80f10b8dc615807ee1133cd99ac74082581196d4d9590bea10690d/matplotlib-3.10.8-cp314-cp314-win_amd64.whl", hash = "sha256:83d282364ea9f3e52363da262ce32a09dfe241e4080dcedda3c0db059d3c1f11", size = 8272719, upload-time = "2025-12-10T22:56:20.366Z" }, + { url = "https://files.pythonhosted.org/packages/69/ce/b006495c19ccc0a137b48083168a37bd056392dee02f87dba0472f2797fe/matplotlib-3.10.8-cp314-cp314-win_arm64.whl", hash = "sha256:2c1998e92cd5999e295a731bcb2911c75f597d937341f3030cc24ef2733d78a8", size = 8144205, upload-time = "2025-12-10T22:56:22.239Z" }, + { url = "https://files.pythonhosted.org/packages/68/d9/b31116a3a855bd313c6fcdb7226926d59b041f26061c6c5b1be66a08c826/matplotlib-3.10.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b5a2b97dbdc7d4f353ebf343744f1d1f1cca8aa8bfddb4262fcf4306c3761d50", size = 8305785, upload-time = "2025-12-10T22:56:24.218Z" }, + { url = "https://files.pythonhosted.org/packages/1e/90/6effe8103f0272685767ba5f094f453784057072f49b393e3ea178fe70a5/matplotlib-3.10.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3f5c3e4da343bba819f0234186b9004faba952cc420fbc522dc4e103c1985908", size = 8198361, upload-time = "2025-12-10T22:56:26.787Z" }, + { url = "https://files.pythonhosted.org/packages/d7/65/a73188711bea603615fc0baecca1061429ac16940e2385433cc778a9d8e7/matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f62550b9a30afde8c1c3ae450e5eb547d579dd69b25c2fc7a1c67f934c1717a", size = 9561357, upload-time = "2025-12-10T22:56:28.953Z" }, + { url = "https://files.pythonhosted.org/packages/f4/3d/b5c5d5d5be8ce63292567f0e2c43dde9953d3ed86ac2de0a72e93c8f07a1/matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:495672de149445ec1b772ff2c9ede9b769e3cb4f0d0aa7fa730d7f59e2d4e1c1", size = 9823610, upload-time = "2025-12-10T22:56:31.455Z" }, + { url = "https://files.pythonhosted.org/packages/4d/4b/e7beb6bbd49f6bae727a12b270a2654d13c397576d25bd6786e47033300f/matplotlib-3.10.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:595ba4d8fe983b88f0eec8c26a241e16d6376fe1979086232f481f8f3f67494c", size = 9614011, upload-time = "2025-12-10T22:56:33.85Z" }, + { url = "https://files.pythonhosted.org/packages/7c/e6/76f2813d31f032e65f6f797e3f2f6e4aab95b65015924b1c51370395c28a/matplotlib-3.10.8-cp314-cp314t-win_amd64.whl", hash = "sha256:25d380fe8b1dc32cf8f0b1b448470a77afb195438bafdf1d858bfb876f3edf7b", size = 8362801, upload-time = "2025-12-10T22:56:36.107Z" }, + { url = "https://files.pythonhosted.org/packages/5d/49/d651878698a0b67f23aa28e17f45a6d6dd3d3f933fa29087fa4ce5947b5a/matplotlib-3.10.8-cp314-cp314t-win_arm64.whl", hash = "sha256:113bb52413ea508ce954a02c10ffd0d565f9c3bc7f2eddc27dfe1731e71c7b5f", size = 8192560, upload-time = "2025-12-10T22:56:38.008Z" }, + { url = "https://files.pythonhosted.org/packages/f5/43/31d59500bb950b0d188e149a2e552040528c13d6e3d6e84d0cccac593dcd/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f97aeb209c3d2511443f8797e3e5a569aebb040d4f8bc79aa3ee78a8fb9e3dd8", size = 8237252, upload-time = "2025-12-10T22:56:39.529Z" }, + { url = "https://files.pythonhosted.org/packages/0c/2c/615c09984f3c5f907f51c886538ad785cf72e0e11a3225de2c0f9442aecc/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fb061f596dad3a0f52b60dc6a5dec4a0c300dec41e058a7efe09256188d170b7", size = 8124693, upload-time = "2025-12-10T22:56:41.758Z" }, + { url = "https://files.pythonhosted.org/packages/91/e1/2757277a1c56041e1fc104b51a0f7b9a4afc8eb737865d63cababe30bc61/matplotlib-3.10.8-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12d90df9183093fcd479f4172ac26b322b1248b15729cb57f42f71f24c7e37a3", size = 8702205, upload-time = "2025-12-10T22:56:43.415Z" }, + { url = "https://files.pythonhosted.org/packages/04/30/3afaa31c757f34b7725ab9d2ba8b48b5e89c2019c003e7d0ead143aabc5a/matplotlib-3.10.8-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6da7c2ce169267d0d066adcf63758f0604aa6c3eebf67458930f9d9b79ad1db1", size = 8249198, upload-time = "2025-12-10T22:56:45.584Z" }, + { url = "https://files.pythonhosted.org/packages/48/2f/6334aec331f57485a642a7c8be03cb286f29111ae71c46c38b363230063c/matplotlib-3.10.8-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9153c3292705be9f9c64498a8872118540c3f4123d1a1c840172edf262c8be4a", size = 8136817, upload-time = "2025-12-10T22:56:47.339Z" }, + { url = "https://files.pythonhosted.org/packages/73/e4/6d6f14b2a759c622f191b2d67e9075a3f56aaccb3be4bb9bb6890030d0a0/matplotlib-3.10.8-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ae029229a57cd1e8fe542485f27e7ca7b23aa9e8944ddb4985d0bc444f1eca2", size = 8713867, upload-time = "2025-12-10T22:56:48.954Z" }, ] [[package]] @@ -1642,11 +1642,11 @@ wheels = [ [[package]] name = "nanobind" -version = "2.10.1" +version = "2.10.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/f6/91c4a25793a7861011b5f2eb9f6075703a514851adf106756c88877cd2f3/nanobind-2.10.1.tar.gz", hash = "sha256:66d2c6fea9541401551b0ca6df674758bb769cf4939b11c1bcd73774c1dcc760", size = 993567, upload-time = "2025-12-08T19:14:19.322Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/7b/818fe4f6d1fdd516a14386ba86f2cbbac1b7304930da0f029724e9001658/nanobind-2.10.2.tar.gz", hash = "sha256:08509910ce6d1fadeed69cb0880d4d4fcb77739c6af9bd8fb4419391a3ca4c6b", size = 993651, upload-time = "2025-12-10T10:55:32.335Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/ff/819dd252a0262b69d6f659fb09b41ed281ac58467b159a9d3e5ef7c9b539/nanobind-2.10.1-py3-none-any.whl", hash = "sha256:ffd52007bcdf806773d7b44efd1c839657922e0b396e595a8c9514ac88cc42c3", size = 246506, upload-time = "2025-12-08T19:14:17.197Z" }, + { url = "https://files.pythonhosted.org/packages/14/06/cb08965f985a5e1b9cb55ed96337c1f6daaa6b9cbdaeabe6bb3f7a1a11df/nanobind-2.10.2-py3-none-any.whl", hash = "sha256:6976c1b04b90481d2612b346485a3063818c6faa5077fe9d8bbc9b5fbe29c380", size = 246514, upload-time = "2025-12-10T10:55:30.741Z" }, ] [[package]] @@ -2537,13 +2537,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, ] +[[package]] +name = "roman-numerals" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/20/a6b20239f54814de5c34bf3f504e553b11780c2aad3677ad2daf989f1fb3/roman_numerals-4.0.0.tar.gz", hash = "sha256:231287018a8788bf8c0718482a08c15b90458523ea1d840a18a791a86d4583b3", size = 9027, upload-time = "2025-12-16T01:53:36.309Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/9d/ad950fd3b65cf0974c633862320829f3d461aef125b981504277c8409a93/roman_numerals-4.0.0-py3-none-any.whl", hash = "sha256:4131feb23ba1a542494873e4cee7844ec8d226a750134efc65ceb20939ed33c9", size = 7668, upload-time = "2025-12-16T01:53:34.922Z" }, +] + [[package]] name = "roman-numerals-py" -version = "3.1.0" +version = "4.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/76/48fd56d17c5bdbdf65609abbc67288728a98ed4c02919428d4f52d23b24b/roman_numerals_py-3.1.0.tar.gz", hash = "sha256:be4bf804f083a4ce001b5eb7e3c0862479d10f94c936f6c4e5f250aa5ff5bd2d", size = 9017, upload-time = "2025-02-22T07:34:54.333Z" } +dependencies = [ + { name = "roman-numerals", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7e/02/6cb667fc1872c5fe814971be2973317f0747d86f1c0aac37d0e1a1df1ecc/roman_numerals_py-4.0.0.tar.gz", hash = "sha256:f7fa8dff5b7b7251d3a7586b97c57a0698e2e28898fa42c23bcc0cf51b02aee9", size = 1119, upload-time = "2025-12-16T01:53:43.097Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/53/97/d2cbbaa10c9b826af0e10fdf836e1bf344d9f0abb873ebc34d1f49642d3f/roman_numerals_py-3.1.0-py3-none-any.whl", hash = "sha256:9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c", size = 7742, upload-time = "2025-02-22T07:34:52.422Z" }, + { url = "https://files.pythonhosted.org/packages/d2/b7/c0aa3b0154e022faa8e8b4eadda0c49f53b09b6b8d55ed3b7cd311abfd92/roman_numerals_py-4.0.0-py3-none-any.whl", hash = "sha256:dfcecf6e0cddbf2ee1112e7e2ebf58ba771984f075cb57a30e1811cee4f06332", size = 1244, upload-time = "2025-12-16T01:53:42.369Z" }, ] [[package]] @@ -3126,47 +3138,51 @@ wheels = [ [[package]] name = "sqlalchemy" -version = "2.0.44" +version = "2.0.45" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f0/f2/840d7b9496825333f532d2e3976b8eadbf52034178aac53630d09fe6e1ef/sqlalchemy-2.0.44.tar.gz", hash = "sha256:0ae7454e1ab1d780aee69fd2aae7d6b8670a581d8847f2d1e0f7ddfbf47e5a22", size = 9819830, upload-time = "2025-10-10T14:39:12.935Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/a7/e9ccfa7eecaf34c6f57d8cb0bb7cbdeeff27017cc0f5d0ca90fdde7a7c0d/sqlalchemy-2.0.44-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c77f3080674fc529b1bd99489378c7f63fcb4ba7f8322b79732e0258f0ea3ce", size = 2137282, upload-time = "2025-10-10T15:36:10.965Z" }, - { url = "https://files.pythonhosted.org/packages/b1/e1/50bc121885bdf10833a4f65ecbe9fe229a3215f4d65a58da8a181734cae3/sqlalchemy-2.0.44-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4c26ef74ba842d61635b0152763d057c8d48215d5be9bb8b7604116a059e9985", size = 2127322, upload-time = "2025-10-10T15:36:12.428Z" }, - { url = "https://files.pythonhosted.org/packages/46/f2/a8573b7230a3ce5ee4b961a2d510d71b43872513647398e595b744344664/sqlalchemy-2.0.44-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4a172b31785e2f00780eccab00bc240ccdbfdb8345f1e6063175b3ff12ad1b0", size = 3214772, upload-time = "2025-10-10T15:34:15.09Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d8/c63d8adb6a7edaf8dcb6f75a2b1e9f8577960a1e489606859c4d73e7d32b/sqlalchemy-2.0.44-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9480c0740aabd8cb29c329b422fb65358049840b34aba0adf63162371d2a96e", size = 3214434, upload-time = "2025-10-10T15:47:00.473Z" }, - { url = "https://files.pythonhosted.org/packages/ee/a6/243d277a4b54fae74d4797957a7320a5c210c293487f931cbe036debb697/sqlalchemy-2.0.44-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:17835885016b9e4d0135720160db3095dc78c583e7b902b6be799fb21035e749", size = 3155365, upload-time = "2025-10-10T15:34:17.932Z" }, - { url = "https://files.pythonhosted.org/packages/5f/f8/6a39516ddd75429fd4ee5a0d72e4c80639fab329b2467c75f363c2ed9751/sqlalchemy-2.0.44-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cbe4f85f50c656d753890f39468fcd8190c5f08282caf19219f684225bfd5fd2", size = 3178910, upload-time = "2025-10-10T15:47:02.346Z" }, - { url = "https://files.pythonhosted.org/packages/43/f0/118355d4ad3c39d9a2f5ee4c7304a9665b3571482777357fa9920cd7a6b4/sqlalchemy-2.0.44-cp310-cp310-win32.whl", hash = "sha256:2fcc4901a86ed81dc76703f3b93ff881e08761c63263c46991081fd7f034b165", size = 2105624, upload-time = "2025-10-10T15:38:15.552Z" }, - { url = "https://files.pythonhosted.org/packages/61/83/6ae5f9466f8aa5d0dcebfff8c9c33b98b27ce23292df3b990454b3d434fd/sqlalchemy-2.0.44-cp310-cp310-win_amd64.whl", hash = "sha256:9919e77403a483ab81e3423151e8ffc9dd992c20d2603bf17e4a8161111e55f5", size = 2129240, upload-time = "2025-10-10T15:38:17.175Z" }, - { url = "https://files.pythonhosted.org/packages/e3/81/15d7c161c9ddf0900b076b55345872ed04ff1ed6a0666e5e94ab44b0163c/sqlalchemy-2.0.44-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fe3917059c7ab2ee3f35e77757062b1bea10a0b6ca633c58391e3f3c6c488dd", size = 2140517, upload-time = "2025-10-10T15:36:15.64Z" }, - { url = "https://files.pythonhosted.org/packages/d4/d5/4abd13b245c7d91bdf131d4916fd9e96a584dac74215f8b5bc945206a974/sqlalchemy-2.0.44-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:de4387a354ff230bc979b46b2207af841dc8bf29847b6c7dbe60af186d97aefa", size = 2130738, upload-time = "2025-10-10T15:36:16.91Z" }, - { url = "https://files.pythonhosted.org/packages/cb/3c/8418969879c26522019c1025171cefbb2a8586b6789ea13254ac602986c0/sqlalchemy-2.0.44-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3678a0fb72c8a6a29422b2732fe423db3ce119c34421b5f9955873eb9b62c1e", size = 3304145, upload-time = "2025-10-10T15:34:19.569Z" }, - { url = "https://files.pythonhosted.org/packages/94/2d/fdb9246d9d32518bda5d90f4b65030b9bf403a935cfe4c36a474846517cb/sqlalchemy-2.0.44-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cf6872a23601672d61a68f390e44703442639a12ee9dd5a88bbce52a695e46e", size = 3304511, upload-time = "2025-10-10T15:47:05.088Z" }, - { url = "https://files.pythonhosted.org/packages/7d/fb/40f2ad1da97d5c83f6c1269664678293d3fe28e90ad17a1093b735420549/sqlalchemy-2.0.44-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:329aa42d1be9929603f406186630135be1e7a42569540577ba2c69952b7cf399", size = 3235161, upload-time = "2025-10-10T15:34:21.193Z" }, - { url = "https://files.pythonhosted.org/packages/95/cb/7cf4078b46752dca917d18cf31910d4eff6076e5b513c2d66100c4293d83/sqlalchemy-2.0.44-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:70e03833faca7166e6a9927fbee7c27e6ecde436774cd0b24bbcc96353bce06b", size = 3261426, upload-time = "2025-10-10T15:47:07.196Z" }, - { url = "https://files.pythonhosted.org/packages/f8/3b/55c09b285cb2d55bdfa711e778bdffdd0dc3ffa052b0af41f1c5d6e582fa/sqlalchemy-2.0.44-cp311-cp311-win32.whl", hash = "sha256:253e2f29843fb303eca6b2fc645aca91fa7aa0aa70b38b6950da92d44ff267f3", size = 2105392, upload-time = "2025-10-10T15:38:20.051Z" }, - { url = "https://files.pythonhosted.org/packages/c7/23/907193c2f4d680aedbfbdf7bf24c13925e3c7c292e813326c1b84a0b878e/sqlalchemy-2.0.44-cp311-cp311-win_amd64.whl", hash = "sha256:7a8694107eb4308a13b425ca8c0e67112f8134c846b6e1f722698708741215d5", size = 2130293, upload-time = "2025-10-10T15:38:21.601Z" }, - { url = "https://files.pythonhosted.org/packages/62/c4/59c7c9b068e6813c898b771204aad36683c96318ed12d4233e1b18762164/sqlalchemy-2.0.44-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72fea91746b5890f9e5e0997f16cbf3d53550580d76355ba2d998311b17b2250", size = 2139675, upload-time = "2025-10-10T16:03:31.064Z" }, - { url = "https://files.pythonhosted.org/packages/d6/ae/eeb0920537a6f9c5a3708e4a5fc55af25900216bdb4847ec29cfddf3bf3a/sqlalchemy-2.0.44-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:585c0c852a891450edbb1eaca8648408a3cc125f18cf433941fa6babcc359e29", size = 2127726, upload-time = "2025-10-10T16:03:35.934Z" }, - { url = "https://files.pythonhosted.org/packages/d8/d5/2ebbabe0379418eda8041c06b0b551f213576bfe4c2f09d77c06c07c8cc5/sqlalchemy-2.0.44-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b94843a102efa9ac68a7a30cd46df3ff1ed9c658100d30a725d10d9c60a2f44", size = 3327603, upload-time = "2025-10-10T15:35:28.322Z" }, - { url = "https://files.pythonhosted.org/packages/45/e5/5aa65852dadc24b7d8ae75b7efb8d19303ed6ac93482e60c44a585930ea5/sqlalchemy-2.0.44-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:119dc41e7a7defcefc57189cfa0e61b1bf9c228211aba432b53fb71ef367fda1", size = 3337842, upload-time = "2025-10-10T15:43:45.431Z" }, - { url = "https://files.pythonhosted.org/packages/41/92/648f1afd3f20b71e880ca797a960f638d39d243e233a7082c93093c22378/sqlalchemy-2.0.44-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0765e318ee9179b3718c4fd7ba35c434f4dd20332fbc6857a5e8df17719c24d7", size = 3264558, upload-time = "2025-10-10T15:35:29.93Z" }, - { url = "https://files.pythonhosted.org/packages/40/cf/e27d7ee61a10f74b17740918e23cbc5bc62011b48282170dc4c66da8ec0f/sqlalchemy-2.0.44-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2e7b5b079055e02d06a4308d0481658e4f06bc7ef211567edc8f7d5dce52018d", size = 3301570, upload-time = "2025-10-10T15:43:48.407Z" }, - { url = "https://files.pythonhosted.org/packages/3b/3d/3116a9a7b63e780fb402799b6da227435be878b6846b192f076d2f838654/sqlalchemy-2.0.44-cp312-cp312-win32.whl", hash = "sha256:846541e58b9a81cce7dee8329f352c318de25aa2f2bbe1e31587eb1f057448b4", size = 2103447, upload-time = "2025-10-10T15:03:21.678Z" }, - { url = "https://files.pythonhosted.org/packages/25/83/24690e9dfc241e6ab062df82cc0df7f4231c79ba98b273fa496fb3dd78ed/sqlalchemy-2.0.44-cp312-cp312-win_amd64.whl", hash = "sha256:7cbcb47fd66ab294703e1644f78971f6f2f1126424d2b300678f419aa73c7b6e", size = 2130912, upload-time = "2025-10-10T15:03:24.656Z" }, - { url = "https://files.pythonhosted.org/packages/45/d3/c67077a2249fdb455246e6853166360054c331db4613cda3e31ab1cadbef/sqlalchemy-2.0.44-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ff486e183d151e51b1d694c7aa1695747599bb00b9f5f604092b54b74c64a8e1", size = 2135479, upload-time = "2025-10-10T16:03:37.671Z" }, - { url = "https://files.pythonhosted.org/packages/2b/91/eabd0688330d6fd114f5f12c4f89b0d02929f525e6bf7ff80aa17ca802af/sqlalchemy-2.0.44-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b1af8392eb27b372ddb783b317dea0f650241cea5bd29199b22235299ca2e45", size = 2123212, upload-time = "2025-10-10T16:03:41.755Z" }, - { url = "https://files.pythonhosted.org/packages/b0/bb/43e246cfe0e81c018076a16036d9b548c4cc649de241fa27d8d9ca6f85ab/sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b61188657e3a2b9ac4e8f04d6cf8e51046e28175f79464c67f2fd35bceb0976", size = 3255353, upload-time = "2025-10-10T15:35:31.221Z" }, - { url = "https://files.pythonhosted.org/packages/b9/96/c6105ed9a880abe346b64d3b6ddef269ddfcab04f7f3d90a0bf3c5a88e82/sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b87e7b91a5d5973dda5f00cd61ef72ad75a1db73a386b62877d4875a8840959c", size = 3260222, upload-time = "2025-10-10T15:43:50.124Z" }, - { url = "https://files.pythonhosted.org/packages/44/16/1857e35a47155b5ad927272fee81ae49d398959cb749edca6eaa399b582f/sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:15f3326f7f0b2bfe406ee562e17f43f36e16167af99c4c0df61db668de20002d", size = 3189614, upload-time = "2025-10-10T15:35:32.578Z" }, - { url = "https://files.pythonhosted.org/packages/88/ee/4afb39a8ee4fc786e2d716c20ab87b5b1fb33d4ac4129a1aaa574ae8a585/sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e77faf6ff919aa8cd63f1c4e561cac1d9a454a191bb864d5dd5e545935e5a40", size = 3226248, upload-time = "2025-10-10T15:43:51.862Z" }, - { url = "https://files.pythonhosted.org/packages/32/d5/0e66097fc64fa266f29a7963296b40a80d6a997b7ac13806183700676f86/sqlalchemy-2.0.44-cp313-cp313-win32.whl", hash = "sha256:ee51625c2d51f8baadf2829fae817ad0b66b140573939dd69284d2ba3553ae73", size = 2101275, upload-time = "2025-10-10T15:03:26.096Z" }, - { url = "https://files.pythonhosted.org/packages/03/51/665617fe4f8c6450f42a6d8d69243f9420f5677395572c2fe9d21b493b7b/sqlalchemy-2.0.44-cp313-cp313-win_amd64.whl", hash = "sha256:c1c80faaee1a6c3428cecf40d16a2365bcf56c424c92c2b6f0f9ad204b899e9e", size = 2127901, upload-time = "2025-10-10T15:03:27.548Z" }, - { url = "https://files.pythonhosted.org/packages/9c/5e/6a29fa884d9fb7ddadf6b69490a9d45fded3b38541713010dad16b77d015/sqlalchemy-2.0.44-py3-none-any.whl", hash = "sha256:19de7ca1246fbef9f9d1bff8f1ab25641569df226364a0e40457dc5457c54b05", size = 1928718, upload-time = "2025-10-10T15:29:45.32Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/be/f9/5e4491e5ccf42f5d9cfc663741d261b3e6e1683ae7812114e7636409fcc6/sqlalchemy-2.0.45.tar.gz", hash = "sha256:1632a4bda8d2d25703fdad6363058d882541bdaaee0e5e3ddfa0cd3229efce88", size = 9869912, upload-time = "2025-12-09T21:05:16.737Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/70/75b1387d72e2847220441166c5eb4e9846dd753895208c13e6d66523b2d9/sqlalchemy-2.0.45-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c64772786d9eee72d4d3784c28f0a636af5b0a29f3fe26ff11f55efe90c0bd85", size = 2154148, upload-time = "2025-12-10T20:03:21.023Z" }, + { url = "https://files.pythonhosted.org/packages/d8/a4/7805e02323c49cb9d1ae5cd4913b28c97103079765f520043f914fca4cb3/sqlalchemy-2.0.45-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7ae64ebf7657395824a19bca98ab10eb9a3ecb026bf09524014f1bb81cb598d4", size = 3233051, upload-time = "2025-12-09T22:06:04.768Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ec/32ae09139f61bef3de3142e85c47abdee8db9a55af2bb438da54a4549263/sqlalchemy-2.0.45-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f02325709d1b1a1489f23a39b318e175a171497374149eae74d612634b234c0", size = 3232781, upload-time = "2025-12-09T22:09:54.435Z" }, + { url = "https://files.pythonhosted.org/packages/ad/bd/bf7b869b6f5585eac34222e1cf4405f4ba8c3b85dd6b1af5d4ce8bca695f/sqlalchemy-2.0.45-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d2c3684fca8a05f0ac1d9a21c1f4a266983a7ea9180efb80ffeb03861ecd01a0", size = 3182096, upload-time = "2025-12-09T22:06:06.169Z" }, + { url = "https://files.pythonhosted.org/packages/21/6a/c219720a241bb8f35c88815ccc27761f5af7fdef04b987b0e8a2c1a6dcaa/sqlalchemy-2.0.45-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040f6f0545b3b7da6b9317fc3e922c9a98fc7243b2a1b39f78390fc0942f7826", size = 3205109, upload-time = "2025-12-09T22:09:55.969Z" }, + { url = "https://files.pythonhosted.org/packages/bd/c4/6ccf31b2bc925d5d95fab403ffd50d20d7c82b858cf1a4855664ca054dce/sqlalchemy-2.0.45-cp310-cp310-win32.whl", hash = "sha256:830d434d609fe7bfa47c425c445a8b37929f140a7a44cdaf77f6d34df3a7296a", size = 2114240, upload-time = "2025-12-09T21:29:54.007Z" }, + { url = "https://files.pythonhosted.org/packages/de/29/a27a31fca07316def418db6f7c70ab14010506616a2decef1906050a0587/sqlalchemy-2.0.45-cp310-cp310-win_amd64.whl", hash = "sha256:0209d9753671b0da74da2cfbb9ecf9c02f72a759e4b018b3ab35f244c91842c7", size = 2137615, upload-time = "2025-12-09T21:29:55.85Z" }, + { url = "https://files.pythonhosted.org/packages/a2/1c/769552a9d840065137272ebe86ffbb0bc92b0f1e0a68ee5266a225f8cd7b/sqlalchemy-2.0.45-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e90a344c644a4fa871eb01809c32096487928bd2038bf10f3e4515cb688cc56", size = 2153860, upload-time = "2025-12-10T20:03:23.843Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f8/9be54ff620e5b796ca7b44670ef58bc678095d51b0e89d6e3102ea468216/sqlalchemy-2.0.45-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8c8b41b97fba5f62349aa285654230296829672fc9939cd7f35aab246d1c08b", size = 3309379, upload-time = "2025-12-09T22:06:07.461Z" }, + { url = "https://files.pythonhosted.org/packages/f6/2b/60ce3ee7a5ae172bfcd419ce23259bb874d2cddd44f67c5df3760a1e22f9/sqlalchemy-2.0.45-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:12c694ed6468333a090d2f60950e4250b928f457e4962389553d6ba5fe9951ac", size = 3309948, upload-time = "2025-12-09T22:09:57.643Z" }, + { url = "https://files.pythonhosted.org/packages/a3/42/bac8d393f5db550e4e466d03d16daaafd2bad1f74e48c12673fb499a7fc1/sqlalchemy-2.0.45-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f7d27a1d977a1cfef38a0e2e1ca86f09c4212666ce34e6ae542f3ed0a33bc606", size = 3261239, upload-time = "2025-12-09T22:06:08.879Z" }, + { url = "https://files.pythonhosted.org/packages/6f/12/43dc70a0528c59842b04ea1c1ed176f072a9b383190eb015384dd102fb19/sqlalchemy-2.0.45-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d62e47f5d8a50099b17e2bfc1b0c7d7ecd8ba6b46b1507b58cc4f05eefc3bb1c", size = 3284065, upload-time = "2025-12-09T22:09:59.454Z" }, + { url = "https://files.pythonhosted.org/packages/cf/9c/563049cf761d9a2ec7bc489f7879e9d94e7b590496bea5bbee9ed7b4cc32/sqlalchemy-2.0.45-cp311-cp311-win32.whl", hash = "sha256:3c5f76216e7b85770d5bb5130ddd11ee89f4d52b11783674a662c7dd57018177", size = 2113480, upload-time = "2025-12-09T21:29:57.03Z" }, + { url = "https://files.pythonhosted.org/packages/bc/fa/09d0a11fe9f15c7fa5c7f0dd26be3d235b0c0cbf2f9544f43bc42efc8a24/sqlalchemy-2.0.45-cp311-cp311-win_amd64.whl", hash = "sha256:a15b98adb7f277316f2c276c090259129ee4afca783495e212048daf846654b2", size = 2138407, upload-time = "2025-12-09T21:29:58.556Z" }, + { url = "https://files.pythonhosted.org/packages/2d/c7/1900b56ce19bff1c26f39a4ce427faec7716c81ac792bfac8b6a9f3dca93/sqlalchemy-2.0.45-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3ee2aac15169fb0d45822983631466d60b762085bc4535cd39e66bea362df5f", size = 3333760, upload-time = "2025-12-09T22:11:02.66Z" }, + { url = "https://files.pythonhosted.org/packages/0a/93/3be94d96bb442d0d9a60e55a6bb6e0958dd3457751c6f8502e56ef95fed0/sqlalchemy-2.0.45-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba547ac0b361ab4f1608afbc8432db669bd0819b3e12e29fb5fa9529a8bba81d", size = 3348268, upload-time = "2025-12-09T22:13:49.054Z" }, + { url = "https://files.pythonhosted.org/packages/48/4b/f88ded696e61513595e4a9778f9d3f2bf7332cce4eb0c7cedaabddd6687b/sqlalchemy-2.0.45-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:215f0528b914e5c75ef2559f69dca86878a3beeb0c1be7279d77f18e8d180ed4", size = 3278144, upload-time = "2025-12-09T22:11:04.14Z" }, + { url = "https://files.pythonhosted.org/packages/ed/6a/310ecb5657221f3e1bd5288ed83aa554923fb5da48d760a9f7622afeb065/sqlalchemy-2.0.45-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:107029bf4f43d076d4011f1afb74f7c3e2ea029ec82eb23d8527d5e909e97aa6", size = 3313907, upload-time = "2025-12-09T22:13:50.598Z" }, + { url = "https://files.pythonhosted.org/packages/5c/39/69c0b4051079addd57c84a5bfb34920d87456dd4c90cf7ee0df6efafc8ff/sqlalchemy-2.0.45-cp312-cp312-win32.whl", hash = "sha256:0c9f6ada57b58420a2c0277ff853abe40b9e9449f8d7d231763c6bc30f5c4953", size = 2112182, upload-time = "2025-12-09T21:39:30.824Z" }, + { url = "https://files.pythonhosted.org/packages/f7/4e/510db49dd89fc3a6e994bee51848c94c48c4a00dc905e8d0133c251f41a7/sqlalchemy-2.0.45-cp312-cp312-win_amd64.whl", hash = "sha256:8defe5737c6d2179c7997242d6473587c3beb52e557f5ef0187277009f73e5e1", size = 2139200, upload-time = "2025-12-09T21:39:32.321Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c8/7cc5221b47a54edc72a0140a1efa56e0a2730eefa4058d7ed0b4c4357ff8/sqlalchemy-2.0.45-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe187fc31a54d7fd90352f34e8c008cf3ad5d064d08fedd3de2e8df83eb4a1cf", size = 3277082, upload-time = "2025-12-09T22:11:06.167Z" }, + { url = "https://files.pythonhosted.org/packages/0e/50/80a8d080ac7d3d321e5e5d420c9a522b0aa770ec7013ea91f9a8b7d36e4a/sqlalchemy-2.0.45-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:672c45cae53ba88e0dad74b9027dddd09ef6f441e927786b05bec75d949fbb2e", size = 3293131, upload-time = "2025-12-09T22:13:52.626Z" }, + { url = "https://files.pythonhosted.org/packages/da/4c/13dab31266fc9904f7609a5dc308a2432a066141d65b857760c3bef97e69/sqlalchemy-2.0.45-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:470daea2c1ce73910f08caf10575676a37159a6d16c4da33d0033546bddebc9b", size = 3225389, upload-time = "2025-12-09T22:11:08.093Z" }, + { url = "https://files.pythonhosted.org/packages/74/04/891b5c2e9f83589de202e7abaf24cd4e4fa59e1837d64d528829ad6cc107/sqlalchemy-2.0.45-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9c6378449e0940476577047150fd09e242529b761dc887c9808a9a937fe990c8", size = 3266054, upload-time = "2025-12-09T22:13:54.262Z" }, + { url = "https://files.pythonhosted.org/packages/f1/24/fc59e7f71b0948cdd4cff7a286210e86b0443ef1d18a23b0d83b87e4b1f7/sqlalchemy-2.0.45-cp313-cp313-win32.whl", hash = "sha256:4b6bec67ca45bc166c8729910bd2a87f1c0407ee955df110d78948f5b5827e8a", size = 2110299, upload-time = "2025-12-09T21:39:33.486Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c5/d17113020b2d43073412aeca09b60d2009442420372123b8d49cc253f8b8/sqlalchemy-2.0.45-cp313-cp313-win_amd64.whl", hash = "sha256:afbf47dc4de31fa38fd491f3705cac5307d21d4bb828a4f020ee59af412744ee", size = 2136264, upload-time = "2025-12-09T21:39:36.801Z" }, + { url = "https://files.pythonhosted.org/packages/3d/8d/bb40a5d10e7a5f2195f235c0b2f2c79b0bf6e8f00c0c223130a4fbd2db09/sqlalchemy-2.0.45-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:83d7009f40ce619d483d26ac1b757dfe3167b39921379a8bd1b596cf02dab4a6", size = 3521998, upload-time = "2025-12-09T22:13:28.622Z" }, + { url = "https://files.pythonhosted.org/packages/75/a5/346128b0464886f036c039ea287b7332a410aa2d3fb0bb5d404cb8861635/sqlalchemy-2.0.45-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d8a2ca754e5415cde2b656c27900b19d50ba076aa05ce66e2207623d3fe41f5a", size = 3473434, upload-time = "2025-12-09T22:13:30.188Z" }, + { url = "https://files.pythonhosted.org/packages/cc/64/4e1913772646b060b025d3fc52ce91a58967fe58957df32b455de5a12b4f/sqlalchemy-2.0.45-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f46ec744e7f51275582e6a24326e10c49fbdd3fc99103e01376841213028774", size = 3272404, upload-time = "2025-12-09T22:11:09.662Z" }, + { url = "https://files.pythonhosted.org/packages/b3/27/caf606ee924282fe4747ee4fd454b335a72a6e018f97eab5ff7f28199e16/sqlalchemy-2.0.45-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:883c600c345123c033c2f6caca18def08f1f7f4c3ebeb591a63b6fceffc95cce", size = 3277057, upload-time = "2025-12-09T22:13:56.213Z" }, + { url = "https://files.pythonhosted.org/packages/85/d0/3d64218c9724e91f3d1574d12eb7ff8f19f937643815d8daf792046d88ab/sqlalchemy-2.0.45-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2c0b74aa79e2deade948fe8593654c8ef4228c44ba862bb7c9585c8e0db90f33", size = 3222279, upload-time = "2025-12-09T22:11:11.1Z" }, + { url = "https://files.pythonhosted.org/packages/24/10/dd7688a81c5bc7690c2a3764d55a238c524cd1a5a19487928844cb247695/sqlalchemy-2.0.45-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8a420169cef179d4c9064365f42d779f1e5895ad26ca0c8b4c0233920973db74", size = 3244508, upload-time = "2025-12-09T22:13:57.932Z" }, + { url = "https://files.pythonhosted.org/packages/aa/41/db75756ca49f777e029968d9c9fee338c7907c563267740c6d310a8e3f60/sqlalchemy-2.0.45-cp314-cp314-win32.whl", hash = "sha256:e50dcb81a5dfe4b7b4a4aa8f338116d127cb209559124f3694c70d6cd072b68f", size = 2113204, upload-time = "2025-12-09T21:39:38.365Z" }, + { url = "https://files.pythonhosted.org/packages/89/a2/0e1590e9adb292b1d576dbcf67ff7df8cf55e56e78d2c927686d01080f4b/sqlalchemy-2.0.45-cp314-cp314-win_amd64.whl", hash = "sha256:4748601c8ea959e37e03d13dcda4a44837afcd1b21338e637f7c935b8da06177", size = 2138785, upload-time = "2025-12-09T21:39:39.503Z" }, + { url = "https://files.pythonhosted.org/packages/42/39/f05f0ed54d451156bbed0e23eb0516bcad7cbb9f18b3bf219c786371b3f0/sqlalchemy-2.0.45-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd337d3526ec5298f67d6a30bbbe4ed7e5e68862f0bf6dd21d289f8d37b7d60b", size = 3522029, upload-time = "2025-12-09T22:13:32.09Z" }, + { url = "https://files.pythonhosted.org/packages/54/0f/d15398b98b65c2bce288d5ee3f7d0a81f77ab89d9456994d5c7cc8b2a9db/sqlalchemy-2.0.45-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9a62b446b7d86a3909abbcd1cd3cc550a832f99c2bc37c5b22e1925438b9367b", size = 3475142, upload-time = "2025-12-09T22:13:33.739Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e1/3ccb13c643399d22289c6a9786c1a91e3dcbb68bce4beb44926ac2c557bf/sqlalchemy-2.0.45-py3-none-any.whl", hash = "sha256:5225a288e4c8cc2308dbdd874edad6e7d0fd38eac1e9e5f23503425c8eee20d0", size = 1936672, upload-time = "2025-12-09T21:54:52.608Z" }, ] [[package]] @@ -3273,21 +3289,21 @@ wheels = [ [[package]] name = "tornado" -version = "6.5.2" +version = "6.5.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/ce/1eb500eae19f4648281bb2186927bb062d2438c2e5093d1360391afd2f90/tornado-6.5.2.tar.gz", hash = "sha256:ab53c8f9a0fa351e2c0741284e06c7a45da86afb544133201c5cc8578eb076a0", size = 510821, upload-time = "2025-08-08T18:27:00.78Z" } +sdist = { url = "https://files.pythonhosted.org/packages/37/1d/0a336abf618272d53f62ebe274f712e213f5a03c0b2339575430b8362ef2/tornado-6.5.4.tar.gz", hash = "sha256:a22fa9047405d03260b483980635f0b041989d8bcc9a313f8fe18b411d84b1d7", size = 513632, upload-time = "2025-12-15T19:21:03.836Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/48/6a7529df2c9cc12efd2e8f5dd219516184d703b34c06786809670df5b3bd/tornado-6.5.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:2436822940d37cde62771cff8774f4f00b3c8024fe482e16ca8387b8a2724db6", size = 442563, upload-time = "2025-08-08T18:26:42.945Z" }, - { url = "https://files.pythonhosted.org/packages/f2/b5/9b575a0ed3e50b00c40b08cbce82eb618229091d09f6d14bce80fc01cb0b/tornado-6.5.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:583a52c7aa94ee046854ba81d9ebb6c81ec0fd30386d96f7640c96dad45a03ef", size = 440729, upload-time = "2025-08-08T18:26:44.473Z" }, - { url = "https://files.pythonhosted.org/packages/1b/4e/619174f52b120efcf23633c817fd3fed867c30bff785e2cd5a53a70e483c/tornado-6.5.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0fe179f28d597deab2842b86ed4060deec7388f1fd9c1b4a41adf8af058907e", size = 444295, upload-time = "2025-08-08T18:26:46.021Z" }, - { url = "https://files.pythonhosted.org/packages/95/fa/87b41709552bbd393c85dd18e4e3499dcd8983f66e7972926db8d96aa065/tornado-6.5.2-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b186e85d1e3536d69583d2298423744740986018e393d0321df7340e71898882", size = 443644, upload-time = "2025-08-08T18:26:47.625Z" }, - { url = "https://files.pythonhosted.org/packages/f9/41/fb15f06e33d7430ca89420283a8762a4e6b8025b800ea51796ab5e6d9559/tornado-6.5.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e792706668c87709709c18b353da1f7662317b563ff69f00bab83595940c7108", size = 443878, upload-time = "2025-08-08T18:26:50.599Z" }, - { url = "https://files.pythonhosted.org/packages/11/92/fe6d57da897776ad2e01e279170ea8ae726755b045fe5ac73b75357a5a3f/tornado-6.5.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:06ceb1300fd70cb20e43b1ad8aaee0266e69e7ced38fa910ad2e03285009ce7c", size = 444549, upload-time = "2025-08-08T18:26:51.864Z" }, - { url = "https://files.pythonhosted.org/packages/9b/02/c8f4f6c9204526daf3d760f4aa555a7a33ad0e60843eac025ccfd6ff4a93/tornado-6.5.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:74db443e0f5251be86cbf37929f84d8c20c27a355dd452a5cfa2aada0d001ec4", size = 443973, upload-time = "2025-08-08T18:26:53.625Z" }, - { url = "https://files.pythonhosted.org/packages/ae/2d/f5f5707b655ce2317190183868cd0f6822a1121b4baeae509ceb9590d0bd/tornado-6.5.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b5e735ab2889d7ed33b32a459cac490eda71a1ba6857b0118de476ab6c366c04", size = 443954, upload-time = "2025-08-08T18:26:55.072Z" }, - { url = "https://files.pythonhosted.org/packages/e8/59/593bd0f40f7355806bf6573b47b8c22f8e1374c9b6fd03114bd6b7a3dcfd/tornado-6.5.2-cp39-abi3-win32.whl", hash = "sha256:c6f29e94d9b37a95013bb669616352ddb82e3bfe8326fccee50583caebc8a5f0", size = 445023, upload-time = "2025-08-08T18:26:56.677Z" }, - { url = "https://files.pythonhosted.org/packages/c7/2a/f609b420c2f564a748a2d80ebfb2ee02a73ca80223af712fca591386cafb/tornado-6.5.2-cp39-abi3-win_amd64.whl", hash = "sha256:e56a5af51cc30dd2cae649429af65ca2f6571da29504a07995175df14c18f35f", size = 445427, upload-time = "2025-08-08T18:26:57.91Z" }, - { url = "https://files.pythonhosted.org/packages/5e/4f/e1f65e8f8c76d73658b33d33b81eed4322fb5085350e4328d5c956f0c8f9/tornado-6.5.2-cp39-abi3-win_arm64.whl", hash = "sha256:d6c33dc3672e3a1f3618eb63b7ef4683a7688e7b9e6e8f0d9aa5726360a004af", size = 444456, upload-time = "2025-08-08T18:26:59.207Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a9/e94a9d5224107d7ce3cc1fab8d5dc97f5ea351ccc6322ee4fb661da94e35/tornado-6.5.4-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d6241c1a16b1c9e4cc28148b1cda97dd1c6cb4fb7068ac1bedc610768dff0ba9", size = 443909, upload-time = "2025-12-15T19:20:48.382Z" }, + { url = "https://files.pythonhosted.org/packages/db/7e/f7b8d8c4453f305a51f80dbb49014257bb7d28ccb4bbb8dd328ea995ecad/tornado-6.5.4-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2d50f63dda1d2cac3ae1fa23d254e16b5e38153758470e9956cbc3d813d40843", size = 442163, upload-time = "2025-12-15T19:20:49.791Z" }, + { url = "https://files.pythonhosted.org/packages/ba/b5/206f82d51e1bfa940ba366a8d2f83904b15942c45a78dd978b599870ab44/tornado-6.5.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1cf66105dc6acb5af613c054955b8137e34a03698aa53272dbda4afe252be17", size = 445746, upload-time = "2025-12-15T19:20:51.491Z" }, + { url = "https://files.pythonhosted.org/packages/8e/9d/1a3338e0bd30ada6ad4356c13a0a6c35fbc859063fa7eddb309183364ac1/tornado-6.5.4-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50ff0a58b0dc97939d29da29cd624da010e7f804746621c78d14b80238669335", size = 445083, upload-time = "2025-12-15T19:20:52.778Z" }, + { url = "https://files.pythonhosted.org/packages/50/d4/e51d52047e7eb9a582da59f32125d17c0482d065afd5d3bc435ff2120dc5/tornado-6.5.4-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fb5e04efa54cf0baabdd10061eb4148e0be137166146fff835745f59ab9f7f", size = 445315, upload-time = "2025-12-15T19:20:53.996Z" }, + { url = "https://files.pythonhosted.org/packages/27/07/2273972f69ca63dbc139694a3fc4684edec3ea3f9efabf77ed32483b875c/tornado-6.5.4-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9c86b1643b33a4cd415f8d0fe53045f913bf07b4a3ef646b735a6a86047dda84", size = 446003, upload-time = "2025-12-15T19:20:56.101Z" }, + { url = "https://files.pythonhosted.org/packages/d1/83/41c52e47502bf7260044413b6770d1a48dda2f0246f95ee1384a3cd9c44a/tornado-6.5.4-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:6eb82872335a53dd063a4f10917b3efd28270b56a33db69009606a0312660a6f", size = 445412, upload-time = "2025-12-15T19:20:57.398Z" }, + { url = "https://files.pythonhosted.org/packages/10/c7/bc96917f06cbee182d44735d4ecde9c432e25b84f4c2086143013e7b9e52/tornado-6.5.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6076d5dda368c9328ff41ab5d9dd3608e695e8225d1cd0fd1e006f05da3635a8", size = 445392, upload-time = "2025-12-15T19:20:58.692Z" }, + { url = "https://files.pythonhosted.org/packages/0c/1a/d7592328d037d36f2d2462f4bc1fbb383eec9278bc786c1b111cbbd44cfa/tornado-6.5.4-cp39-abi3-win32.whl", hash = "sha256:1768110f2411d5cd281bac0a090f707223ce77fd110424361092859e089b38d1", size = 446481, upload-time = "2025-12-15T19:21:00.008Z" }, + { url = "https://files.pythonhosted.org/packages/d6/6d/c69be695a0a64fd37a97db12355a035a6d90f79067a3cf936ec2b1dc38cd/tornado-6.5.4-cp39-abi3-win_amd64.whl", hash = "sha256:fa07d31e0cd85c60713f2b995da613588aa03e1303d75705dca6af8babc18ddc", size = 446886, upload-time = "2025-12-15T19:21:01.287Z" }, + { url = "https://files.pythonhosted.org/packages/50/49/8dc3fd90902f70084bd2cd059d576ddb4f8bb44c2c7c0e33a11422acb17e/tornado-6.5.4-cp39-abi3-win_arm64.whl", hash = "sha256:053e6e16701eb6cbe641f308f4c1a9541f91b6261991160391bfc342e8a551a1", size = 445910, upload-time = "2025-12-15T19:21:02.571Z" }, ] [[package]] @@ -3335,20 +3351,20 @@ wheels = [ [[package]] name = "tzdata" -version = "2025.2" +version = "2025.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, + { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" }, ] [[package]] name = "urllib3" -version = "2.6.1" +version = "2.6.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/1d/0f3a93cca1ac5e8287842ed4eebbd0f7a991315089b1a0b01c7788aa7b63/urllib3-2.6.1.tar.gz", hash = "sha256:5379eb6e1aba4088bae84f8242960017ec8d8e3decf30480b3a1abdaa9671a3f", size = 432678, upload-time = "2025-12-08T15:25:26.773Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/24/a2a2ed9addd907787d7aa0355ba36a6cadf1768b934c652ea78acbd59dcd/urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797", size = 432930, upload-time = "2025-12-11T15:56:40.252Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/56/190ceb8cb10511b730b564fb1e0293fa468363dbad26145c34928a60cb0c/urllib3-2.6.1-py3-none-any.whl", hash = "sha256:e67d06fe947c36a7ca39f4994b08d73922d40e6cca949907be05efa6fd75110b", size = 131138, upload-time = "2025-12-08T15:25:25.51Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd", size = 131182, upload-time = "2025-12-11T15:56:38.584Z" }, ] [[package]] From b6f40b7b3da48da4bcb94df5b032b6cd6a64c2ee Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 17:35:12 +0100 Subject: [PATCH 082/144] Fully remove right division --- bindings/ir/symbolic/register_term.cpp | 4 ---- include/mqt-core/ir/operations/Expression.hpp | 5 ----- python/mqt/core/ir/symbolic.pyi | 1 - 3 files changed, 10 deletions(-) diff --git a/bindings/ir/symbolic/register_term.cpp b/bindings/ir/symbolic/register_term.cpp index 8a1c4520d7..2e7b5ba823 100644 --- a/bindings/ir/symbolic/register_term.cpp +++ b/bindings/ir/symbolic/register_term.cpp @@ -61,10 +61,6 @@ void registerTerm(const nb::module_& m) { .def(nb::self * double(), nb::is_operator()) .def(double() * nb::self, nb::is_operator()) .def(nb::self / double(), nb::is_operator()) - .def( - "__rtruediv__", - [](const sym::Term& lhs, double rhs) { return rhs / lhs; }, - nb::is_operator()) .def(nb::self == nb::self, // NOLINT(misc-redundant-expression) nb::sig("def __eq__(self, arg: object, /) -> bool")) diff --git a/include/mqt-core/ir/operations/Expression.hpp b/include/mqt-core/ir/operations/Expression.hpp index fabd6485a7..b8aa30f828 100644 --- a/include/mqt-core/ir/operations/Expression.hpp +++ b/include/mqt-core/ir/operations/Expression.hpp @@ -255,11 +255,6 @@ template operator*(double lhs, const Term& rhs) { return rhs * lhs; } -template >> -Term operator/(double lhs, const Term& rhs) { - return lhs / rhs; -} ///@} /** diff --git a/python/mqt/core/ir/symbolic.pyi b/python/mqt/core/ir/symbolic.pyi index 6c67a5a22e..e58b4b8f09 100644 --- a/python/mqt/core/ir/symbolic.pyi +++ b/python/mqt/core/ir/symbolic.pyi @@ -70,7 +70,6 @@ class Term: def __mul__(self, arg: float, /) -> Term: ... def __rmul__(self, arg: float, /) -> Term: ... def __truediv__(self, arg: float, /) -> Term: ... - def __rtruediv__(self, arg: float, /) -> Term: ... def __eq__(self, arg: object, /) -> bool: ... def __ne__(self, arg: object, /) -> bool: ... def __hash__(self) -> int: ... From 395a676fe7bf4ecb85c68af65c0cbd0c634252b1 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 17:37:37 +0100 Subject: [PATCH 083/144] Throw an error if control qubit index is negative --- bindings/dd/register_dd_package.cpp | 5 ++++- bindings/ir/register_permutation.cpp | 5 ++++- bindings/ir/register_quantum_computation.cpp | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/bindings/dd/register_dd_package.cpp b/bindings/dd/register_dd_package.cpp index 10e1195deb..cddf161bfe 100644 --- a/bindings/dd/register_dd_package.cpp +++ b/bindings/dd/register_dd_package.cpp @@ -106,8 +106,11 @@ qc::Control getControl(const Control& control) { } const auto controlInt = static_cast(std::get(control)); + if (controlInt < 0) { + throw nb::value_error("Control qubit index cannot be negative"); + } if (controlInt > std::numeric_limits::max()) { - throw nb::value_error("Qubit index exceeds maximum value"); + throw nb::value_error("Control qubit index exceeds maximum value"); } return static_cast(controlInt); } diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index 949c166bb0..d7dcac12e4 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -44,8 +44,11 @@ qc::Control getControl(const Control& control) { } const auto controlInt = static_cast(std::get(control)); + if (controlInt < 0) { + throw nb::value_error("Control qubit index cannot be negative"); + } if (controlInt > std::numeric_limits::max()) { - throw nb::value_error("Qubit index exceeds maximum value"); + throw nb::value_error("Control qubit index exceeds maximum value"); } return static_cast(controlInt); } diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index c0feaa807c..af87341075 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -59,8 +59,11 @@ qc::Control getControl(const Control& control) { } const auto controlInt = static_cast(std::get(control)); + if (controlInt < 0) { + throw nb::value_error("Control qubit index cannot be negative"); + } if (controlInt > std::numeric_limits::max()) { - throw nb::value_error("Qubit index exceeds maximum value"); + throw nb::value_error("Control qubit index exceeds maximum value"); } return static_cast(controlInt); } From 903bf81e01e165f0b7800b97a6f0122417cd37b3 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 17:38:50 +0100 Subject: [PATCH 084/144] Fix error messages --- bindings/ir/register_registers.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/ir/register_registers.cpp b/bindings/ir/register_registers.cpp index 3dee07b489..276096e20b 100644 --- a/bindings/ir/register_registers.cpp +++ b/bindings/ir/register_registers.cpp @@ -59,10 +59,10 @@ void registerRegisters(const nb::module_& m) { [](qc::QuantumRegister& reg, const nb::int_ size) { const auto sizeInt = static_cast(size); if (sizeInt < 0) { - throw nb::value_error("Start index cannot be negative"); + throw nb::value_error("Size cannot be negative"); } if (sizeInt > std::numeric_limits::max()) { - throw nb::value_error("Start index exceeds maximum value"); + throw nb::value_error("Size exceeds maximum value"); } reg.getSize() = static_cast(sizeInt); }, From 39443b350882630c209e670b3bc564ec99e2fe22 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 17:50:37 +0100 Subject: [PATCH 085/144] Fix linter errors --- bindings/dd/register_dd_package.cpp | 6 +++-- bindings/ir/register_permutation.cpp | 8 +++--- bindings/ir/register_quantum_computation.cpp | 5 ++-- bindings/ir/register_registers.cpp | 27 +++++++++----------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/bindings/dd/register_dd_package.cpp b/bindings/dd/register_dd_package.cpp index cddf161bfe..8a99fd737f 100644 --- a/bindings/dd/register_dd_package.cpp +++ b/bindings/dd/register_dd_package.cpp @@ -14,6 +14,7 @@ #include "dd/Operations.hpp" #include "dd/Package.hpp" #include "dd/StateGeneration.hpp" +#include "ir/Definitions.hpp" #include "ir/Permutation.hpp" #include "ir/operations/Control.hpp" #include "ir/operations/IfElseOperation.hpp" @@ -109,10 +110,11 @@ qc::Control getControl(const Control& control) { if (controlInt < 0) { throw nb::value_error("Control qubit index cannot be negative"); } - if (controlInt > std::numeric_limits::max()) { + const auto controlUint = static_cast(controlInt); + if (controlUint > std::numeric_limits::max()) { throw nb::value_error("Control qubit index exceeds maximum value"); } - return static_cast(controlInt); + return static_cast(controlUint); } /// Helper function to convert Controls variant to qc::Controls diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index d7dcac12e4..82db5593b4 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -42,15 +42,15 @@ qc::Control getControl(const Control& control) { if (std::holds_alternative(control)) { return std::get(control); } - const auto controlInt = - static_cast(std::get(control)); + auto controlInt = static_cast(std::get(control)); if (controlInt < 0) { throw nb::value_error("Control qubit index cannot be negative"); } - if (controlInt > std::numeric_limits::max()) { + const auto controlUint = static_cast(controlInt); + if (controlUint > std::numeric_limits::max()) { throw nb::value_error("Control qubit index exceeds maximum value"); } - return static_cast(controlInt); + return static_cast(controlUint); } /// Helper function to convert Controls variant to qc::Controls diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index af87341075..8fa007ec66 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -62,10 +62,11 @@ qc::Control getControl(const Control& control) { if (controlInt < 0) { throw nb::value_error("Control qubit index cannot be negative"); } - if (controlInt > std::numeric_limits::max()) { + const auto controlUint = static_cast(controlInt); + if (controlUint > std::numeric_limits::max()) { throw nb::value_error("Control qubit index exceeds maximum value"); } - return static_cast(controlInt); + return static_cast(controlUint); } /// Helper function to convert Controls variant to qc::Controls diff --git a/bindings/ir/register_registers.cpp b/bindings/ir/register_registers.cpp index 276096e20b..cb0aac5abc 100644 --- a/bindings/ir/register_registers.cpp +++ b/bindings/ir/register_registers.cpp @@ -12,6 +12,7 @@ #include "ir/Register.hpp" #include +#include #include #include #include @@ -43,27 +44,25 @@ void registerRegisters(const nb::module_& m) { .def_prop_rw( "start", [](const qc::QuantumRegister& reg) { return reg.getStartIndex(); }, - [](qc::QuantumRegister& reg, const nb::int_ start) { - const auto startInt = static_cast(start); + [](qc::QuantumRegister& reg, const nb::int_& start) { + auto startInt = static_cast(start); if (startInt < 0) { throw nb::value_error("Start index cannot be negative"); } - if (startInt > std::numeric_limits::max()) { + const auto startUint = static_cast(startInt); + if (startUint > std::numeric_limits::max()) { throw nb::value_error("Start index exceeds maximum value"); } - reg.getStartIndex() = static_cast(startInt); + reg.getStartIndex() = static_cast(startUint); }, "The index of the first qubit in the quantum register.") .def_prop_rw( "size", [](const qc::QuantumRegister& reg) { return reg.getSize(); }, - [](qc::QuantumRegister& reg, const nb::int_ size) { + [](qc::QuantumRegister& reg, const nb::int_& size) { const auto sizeInt = static_cast(size); if (sizeInt < 0) { throw nb::value_error("Size cannot be negative"); } - if (sizeInt > std::numeric_limits::max()) { - throw nb::value_error("Size exceeds maximum value"); - } reg.getSize() = static_cast(sizeInt); }, "The number of qubits in the quantum register.") @@ -121,29 +120,27 @@ void registerRegisters(const nb::module_& m) { .def_prop_rw( "start", [](const qc::ClassicalRegister& reg) { return reg.getStartIndex(); }, - [](qc::ClassicalRegister& reg, const nb::int_ start) { + [](qc::ClassicalRegister& reg, const nb::int_& start) { const auto startInt = static_cast(start); if (startInt < 0) { throw nb::value_error("Start index cannot be negative"); } - if (startInt > std::numeric_limits::max()) { + const auto startUint = static_cast(startInt); + if (startUint > std::numeric_limits::max()) { throw nb::value_error("Start index exceeds maximum value"); } - reg.getStartIndex() = static_cast(startInt); + reg.getStartIndex() = static_cast(startUint); }, "The index of the first bit in the classical register.") .def_prop_rw( "size", [](const qc::ClassicalRegister& reg) { return reg.getSize(); }, - [](qc::ClassicalRegister& reg, const nb::int_ size) { + [](qc::ClassicalRegister& reg, const nb::int_& size) { const auto sizeInt = static_cast(size); if (sizeInt < 0) { throw nb::value_error("Size cannot be negative"); } - if (sizeInt > std::numeric_limits::max()) { - throw nb::value_error("Size exceeds maximum value"); - } reg.getSize() = static_cast(sizeInt); }, "The number of bits in the classical register.") From 5fb1c5adb0edfafc734a925120ccc68c588b87a0 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 19:31:14 +0100 Subject: [PATCH 086/144] Fix docstring --- bindings/ir/register_quantum_computation.cpp | 2 +- python/mqt/core/ir/__init__.pyi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index 8fa007ec66..c0f86b6c41 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -1239,7 +1239,7 @@ See Also: R"pb(Apply a phase gate. .. math:: - P(\theta) = \begin{pmatrix} 1 & 0 \\ 0 & e^{i \ theta} \end{pmatrix} + P(\theta) = \begin{pmatrix} 1 & 0 \\ 0 & e^{i \theta} \end{pmatrix} Args: theta: The rotation angle diff --git a/python/mqt/core/ir/__init__.pyi b/python/mqt/core/ir/__init__.pyi index e25221af25..bf810aa506 100644 --- a/python/mqt/core/ir/__init__.pyi +++ b/python/mqt/core/ir/__init__.pyi @@ -1057,7 +1057,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): r"""Apply a phase gate. .. math:: - P(\theta) = \begin{pmatrix} 1 & 0 \\ 0 & e^{i \ theta} \end{pmatrix} + P(\theta) = \begin{pmatrix} 1 & 0 \\ 0 & e^{i \theta} \end{pmatrix} Args: theta: The rotation angle From cc81d6a8ae854e1628393bd629a46d2a83da478c Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 19:31:34 +0100 Subject: [PATCH 087/144] Improve consistency --- bindings/ir/register_registers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/ir/register_registers.cpp b/bindings/ir/register_registers.cpp index cb0aac5abc..6aba774378 100644 --- a/bindings/ir/register_registers.cpp +++ b/bindings/ir/register_registers.cpp @@ -161,7 +161,7 @@ void registerRegisters(const nb::module_& m) { .def( "__getitem__", [](const qc::ClassicalRegister& reg, nb::ssize_t idx) { - auto n = static_cast(reg.getSize()); + const auto n = static_cast(reg.getSize()); if (idx < 0) { idx += n; } From 682c784124668bbf413cd1476e05162cb0ba9f7b Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 19:34:05 +0100 Subject: [PATCH 088/144] Improve size validation --- bindings/ir/register_registers.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/bindings/ir/register_registers.cpp b/bindings/ir/register_registers.cpp index 6aba774378..df87c35be1 100644 --- a/bindings/ir/register_registers.cpp +++ b/bindings/ir/register_registers.cpp @@ -63,7 +63,11 @@ void registerRegisters(const nb::module_& m) { if (sizeInt < 0) { throw nb::value_error("Size cannot be negative"); } - reg.getSize() = static_cast(sizeInt); + const auto sizeUint = static_cast(sizeInt); + if (sizeUint > std::numeric_limits::max()) { + throw nb::value_error("Size exceeds maximum value"); + } + reg.getSize() = static_cast(sizeUint); }, "The number of qubits in the quantum register.") .def_prop_ro( @@ -141,7 +145,11 @@ void registerRegisters(const nb::module_& m) { if (sizeInt < 0) { throw nb::value_error("Size cannot be negative"); } - reg.getSize() = static_cast(sizeInt); + const auto sizeUint = static_cast(sizeInt); + if (sizeUint > std::numeric_limits::max()) { + throw nb::value_error("Size exceeds maximum value"); + } + reg.getSize() = static_cast(sizeUint); }, "The number of bits in the classical register.") From 18369f2438c1eba7bd49b124a0dd0afcf65146bc Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 19:41:25 +0100 Subject: [PATCH 089/144] Validate keys and values --- bindings/ir/register_permutation.cpp | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index 82db5593b4..73d484a0dd 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -37,20 +37,24 @@ using Controls = std::set; namespace { +qc::Qubit nbIntToQubit(const nb::int_& value) { + const auto valueInt = static_cast(value); + if (valueInt < 0) { + throw nb::value_error("Qubit index cannot be negative"); + } + const auto valueUint = static_cast(valueInt); + if (valueUint > std::numeric_limits::max()) { + throw nb::value_error("Qubit index exceeds maximum value"); + } + return static_cast(valueUint); +} + /// Helper function to convert Control variant to qc::Control qc::Control getControl(const Control& control) { if (std::holds_alternative(control)) { return std::get(control); } - auto controlInt = static_cast(std::get(control)); - if (controlInt < 0) { - throw nb::value_error("Control qubit index cannot be negative"); - } - const auto controlUint = static_cast(controlInt); - if (controlUint > std::numeric_limits::max()) { - throw nb::value_error("Control qubit index exceeds maximum value"); - } - return static_cast(controlUint); + return nbIntToQubit(std::get(control)); } /// Helper function to convert Controls variant to qc::Controls @@ -82,7 +86,10 @@ void registerPermutation(const nb::module_& m) { const nb::typed& p) { qc::Permutation perm; for (const auto& [key, value] : p) { - perm[nb::cast(key)] = nb::cast(value); + const auto keyQubit = nbIntToQubit(static_cast(key)); + const auto valueQubit = + nbIntToQubit(static_cast(value)); + perm[keyQubit] = valueQubit; } new (self) qc::Permutation(std::move(perm)); }, From 4080b1931d445c2306fd83deb83f4351198e015a Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 16 Dec 2025 20:24:31 +0100 Subject: [PATCH 090/144] Fix dangling references --- bindings/ir/operations/register_compound_operation.cpp | 8 ++++---- bindings/ir/register_quantum_computation.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/bindings/ir/operations/register_compound_operation.cpp b/bindings/ir/operations/register_compound_operation.cpp index c149bcc5e9..cfaaa5aa34 100644 --- a/bindings/ir/operations/register_compound_operation.cpp +++ b/bindings/ir/operations/register_compound_operation.cpp @@ -80,7 +80,7 @@ A :class:`CompoundOperation` can contain any number of operations, including oth .def( "__getitem__", - [&wrap](const qc::CompoundOperation& op, DiffType i) { + [wrap](const qc::CompoundOperation& op, DiffType i) { i = wrap(i, op.size()); return op.at(static_cast(i)).get(); }, @@ -123,8 +123,8 @@ A :class:`CompoundOperation` can contain any number of operations, including oth .def( "__setitem__", - [&wrap](qc::CompoundOperation& compOp, DiffType i, - const qc::Operation& op) { + [wrap](qc::CompoundOperation& compOp, DiffType i, + const qc::Operation& op) { i = wrap(i, compOp.size()); compOp[static_cast(i)] = op.clone(); }, @@ -161,7 +161,7 @@ A :class:`CompoundOperation` can contain any number of operations, including oth .def( "__delitem__", - [&wrap](qc::CompoundOperation& op, DiffType i) { + [wrap](qc::CompoundOperation& op, DiffType i) { i = wrap(i, op.size()); op.erase(op.begin() + i); }, diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index c0f86b6c41..13486c045a 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -209,7 +209,7 @@ When the quantum computation contains more than one operation, the resulting ope qc.def( "__getitem__", - [&wrap](const qc::QuantumComputation& circ, DiffType i) { + [wrap](const qc::QuantumComputation& circ, DiffType i) { i = wrap(i, circ.getNops()); return circ.at(static_cast(i)).get(); }, @@ -252,8 +252,8 @@ When the quantum computation contains more than one operation, the resulting ope qc.def( "__setitem__", - [&wrap](qc::QuantumComputation& circ, DiffType i, - const qc::Operation& op) { + [wrap](qc::QuantumComputation& circ, DiffType i, + const qc::Operation& op) { i = wrap(i, circ.getNops()); circ.at(static_cast(i)) = op.clone(); }, @@ -289,7 +289,7 @@ When the quantum computation contains more than one operation, the resulting ope qc.def( "__delitem__", - [&wrap](qc::QuantumComputation& circ, DiffType i) { + [wrap](qc::QuantumComputation& circ, DiffType i) { i = wrap(i, circ.getNops()); circ.erase(circ.begin() + i); }, From c9ecdf858ed1dadcde03d72263732ca00b4112cf Mon Sep 17 00:00:00 2001 From: burgholzer Date: Wed, 17 Dec 2025 01:23:51 +0100 Subject: [PATCH 091/144] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20upgraded=20nanobin?= =?UTF-8?q?d=20to=20version=202.10.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- .github/workflows/ci.yml | 2 +- pyproject.toml | 4 ++-- uv.lock | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e662ed902b..d773d7fea1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -149,7 +149,7 @@ jobs: build-project: true files-changed-only: true setup-python: true - install-pkgs: "nanobind==2.10.1" + install-pkgs: "nanobind==2.10.2" cpp-linter-extra-args: "-std=c++20" python-tests: diff --git a/pyproject.toml b/pyproject.toml index 2ba2614d12..900e9466fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ [build-system] requires = [ - "nanobind>=2.10.1", + "nanobind>=2.10.2", "scikit-build-core>=0.11.6", "setuptools-scm>=9.2.2", ] @@ -315,7 +315,7 @@ exclude = [ [dependency-groups] build = [ - "nanobind>=2.10.1", + "nanobind>=2.10.2", "scikit-build-core>=0.11.6", "setuptools-scm>=9.2.2", ] diff --git a/uv.lock b/uv.lock index d2432f5d86..28f4e1321a 100644 --- a/uv.lock +++ b/uv.lock @@ -1550,13 +1550,13 @@ provides-extras = ["qiskit"] [package.metadata.requires-dev] build = [ - { name = "nanobind", specifier = ">=2.10.1" }, + { name = "nanobind", specifier = ">=2.10.2" }, { name = "scikit-build-core", specifier = ">=0.11.6" }, { name = "setuptools-scm", specifier = ">=9.2.2" }, ] dev = [ { name = "lit", specifier = ">=18.1.8" }, - { name = "nanobind", specifier = ">=2.10.1" }, + { name = "nanobind", specifier = ">=2.10.2" }, { name = "nox", specifier = ">=2025.11.12" }, { name = "numpy", marker = "python_full_version >= '3.13'", specifier = ">=2.1" }, { name = "numpy", marker = "python_full_version >= '3.14'", specifier = ">=2.3.2" }, From 69a748472022d1263dc57237abc698bf28edd42f Mon Sep 17 00:00:00 2001 From: burgholzer Date: Wed, 17 Dec 2025 01:27:43 +0100 Subject: [PATCH 092/144] =?UTF-8?q?=E2=8F=AA=20restore=20`na/=5F=5Finit=5F?= =?UTF-8?q?=5F.py`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- python/mqt/core/na/__init__.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 python/mqt/core/na/__init__.py diff --git a/python/mqt/core/na/__init__.py b/python/mqt/core/na/__init__.py new file mode 100644 index 0000000000..4f663baa34 --- /dev/null +++ b/python/mqt/core/na/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +"""MQT Core NA - The MQT Core neutral atom module. + +This module contains all neutral atom related functionality of MQT Core. +""" From 36d9266c29315c6b95e1b5ef68a28fc56e5812b5 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Wed, 17 Dec 2025 01:30:44 +0100 Subject: [PATCH 093/144] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20upgrade=20maximum?= =?UTF-8?q?=20tested=20CMake=20version=20to=204.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0cd8e95518..0f11f175bc 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ # Licensed under the MIT License # set required cmake version -cmake_minimum_required(VERSION 3.24...4.0) +cmake_minimum_required(VERSION 3.24...4.2) project( mqt-core From 5ed4344f5d4266e1d9f733016479070990d31020 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Wed, 17 Dec 2025 01:40:06 +0100 Subject: [PATCH 094/144] =?UTF-8?q?=F0=9F=94=A7=20Explicitly=20search=20fo?= =?UTF-8?q?r=20the=20StableABI=20component=20if=20scikit-build-core=20is?= =?UTF-8?q?=20configured=20to=20build=20a=20StableABI=20wheel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f11f175bc..86f93ccc8e 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,7 +47,8 @@ if(BUILD_MQT_CORE_BINDINGS) endif() # top-level call to find Python - find_package(Python 3.10 REQUIRED COMPONENTS Interpreter Development.Module) + find_package(Python 3.10 REQUIRED COMPONENTS Interpreter Development.Module + ${SKBUILD_SABI_COMPONENT}) endif() # check if this is the master project or used via add_subdirectory From 46a778421325ab3ee0cf4a2e33697ca08d171221 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Wed, 17 Dec 2025 01:41:17 +0100 Subject: [PATCH 095/144] =?UTF-8?q?=F0=9F=94=A7Enable=20Stable=20ABI=20bui?= =?UTF-8?q?lds=20for=20CPython=203.12+?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 900e9466fe..9b0b3849ff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,6 +73,9 @@ wheel.install-dir = "mqt/core" # Explicitly set the package directory wheel.packages = ["python/mqt"] +# Enable Stable ABI builds for CPython 3.12+ +wheel.py-api = "cp312" + # Set required Ninja version ninja.version = ">=1.10" From 6ada5ad30e906ec36e2176d89e1db6c141437246 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Wed, 17 Dec 2025 01:51:15 +0100 Subject: [PATCH 096/144] =?UTF-8?q?=F0=9F=94=A7=20add=20ABI3=20checks=20to?= =?UTF-8?q?=20cibuildwheel=20repair=20wheel=20step?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- pyproject.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 9b0b3849ff..de170e6568 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -295,6 +295,11 @@ environment = { MACOSX_DEPLOYMENT_TARGET = "11.0" } before-build = "uv pip install delvewheel>=1.11.2" repair-wheel-command = "delvewheel repair -w {dest_dir} {wheel} --namespace-pkg mqt --ignore-existing" +[[tool.cibuildwheel.overrides]] +select = "cp312-*" +inherit.repair-wheel-command = "append" +repair-wheel-command = "uvx abi3audit --strict --report {wheel}" + [tool.uv] required-version = ">=0.5.20" From 98566c37ac83f28b488239d7bdc70f92105ee046 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Wed, 17 Dec 2025 02:01:15 +0100 Subject: [PATCH 097/144] =?UTF-8?q?=F0=9F=94=A7=20the=20explicit=20marker?= =?UTF-8?q?=20file=20we=20already=20have=20is=20fine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- bindings/CMakeLists.txt | 8 -------- 1 file changed, 8 deletions(-) diff --git a/bindings/CMakeLists.txt b/bindings/CMakeLists.txt index 2de4c5690d..280d1012e9 100644 --- a/bindings/CMakeLists.txt +++ b/bindings/CMakeLists.txt @@ -26,8 +26,6 @@ if(SKBUILD_STATE STREQUAL "editable") PYTHON_PATH ${CMAKE_INSTALL_PREFIX}/../.. INCLUDE_PRIVATE - MARKER_FILE - ir/py.typed COMPONENT ${MQT_CORE_TARGET_NAME}_Python) @@ -41,8 +39,6 @@ if(SKBUILD_STATE STREQUAL "editable") PYTHON_PATH ${CMAKE_INSTALL_PREFIX}/../.. INCLUDE_PRIVATE - MARKER_FILE - py.typed COMPONENT ${MQT_CORE_TARGET_NAME}_Python) @@ -56,8 +52,6 @@ if(SKBUILD_STATE STREQUAL "editable") PYTHON_PATH ${CMAKE_INSTALL_PREFIX}/../.. INCLUDE_PRIVATE - MARKER_FILE - py.typed COMPONENT ${MQT_CORE_TARGET_NAME}_Python) @@ -71,8 +65,6 @@ if(SKBUILD_STATE STREQUAL "editable") PYTHON_PATH ${CMAKE_INSTALL_PREFIX}/../.. INCLUDE_PRIVATE - MARKER_FILE - na/py.typed COMPONENT ${MQT_CORE_TARGET_NAME}_Python) endif() From 2a236d7b1179c325a093fd60e2dabf709dc123bc Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Thu, 18 Dec 2025 01:59:19 +0100 Subject: [PATCH 098/144] Remove CMake stub generation --- bindings/CMakeLists.txt | 58 ----------------------------------------- 1 file changed, 58 deletions(-) diff --git a/bindings/CMakeLists.txt b/bindings/CMakeLists.txt index 280d1012e9..848b5ad841 100644 --- a/bindings/CMakeLists.txt +++ b/bindings/CMakeLists.txt @@ -10,61 +10,3 @@ add_subdirectory(ir) add_subdirectory(dd) add_subdirectory(fomac) add_subdirectory(na) - -if(SKBUILD_STATE STREQUAL "editable") - nanobind_add_stub( - ir_stub - INSTALL_TIME - RECURSIVE - MODULE - mqt.core.ir - OUTPUT - ir/__init__.pyi - ir/operations.pyi - ir/registers.pyi - ir/symbolic.pyi - PYTHON_PATH - ${CMAKE_INSTALL_PREFIX}/../.. - INCLUDE_PRIVATE - COMPONENT - ${MQT_CORE_TARGET_NAME}_Python) - - nanobind_add_stub( - dd_stub - INSTALL_TIME - MODULE - mqt.core.dd - OUTPUT - dd.pyi - PYTHON_PATH - ${CMAKE_INSTALL_PREFIX}/../.. - INCLUDE_PRIVATE - COMPONENT - ${MQT_CORE_TARGET_NAME}_Python) - - nanobind_add_stub( - fomac_stub - INSTALL_TIME - MODULE - mqt.core.fomac - OUTPUT - fomac.pyi - PYTHON_PATH - ${CMAKE_INSTALL_PREFIX}/../.. - INCLUDE_PRIVATE - COMPONENT - ${MQT_CORE_TARGET_NAME}_Python) - - nanobind_add_stub( - na_fomac_stub - INSTALL_TIME - MODULE - mqt.core.na.fomac - OUTPUT - na/fomac.pyi - PYTHON_PATH - ${CMAKE_INSTALL_PREFIX}/../.. - INCLUDE_PRIVATE - COMPONENT - ${MQT_CORE_TARGET_NAME}_Python) -endif() From f2cd1caf0c31e3d4eea1e1f4a7379f644c86ec8b Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Thu, 18 Dec 2025 01:58:40 +0100 Subject: [PATCH 099/144] Install stub files for better IDE support --- bindings/dd/CMakeLists.txt | 8 ++++++++ bindings/fomac/CMakeLists.txt | 8 ++++++++ bindings/ir/CMakeLists.txt | 9 +++++++++ bindings/na/fomac/CMakeLists.txt | 8 ++++++++ 4 files changed, 33 insertions(+) diff --git a/bindings/dd/CMakeLists.txt b/bindings/dd/CMakeLists.txt index 050729e332..aa149adc3c 100644 --- a/bindings/dd/CMakeLists.txt +++ b/bindings/dd/CMakeLists.txt @@ -21,4 +21,12 @@ if(NOT TARGET ${MQT_CORE_TARGET_NAME}-dd-bindings) . LINK_LIBS MQT::CoreDD) + + # install the Python stub file in editable mode for better IDE support + if(SKBUILD_STATE STREQUAL "editable") + install( + FILES ${PROJECT_SOURCE_DIR}/python/mqt/core/dd.pyi + DESTINATION . + COMPONENT ${MQT_CORE_TARGET_NAME}_Python) + endif() endif() diff --git a/bindings/fomac/CMakeLists.txt b/bindings/fomac/CMakeLists.txt index 179c3c02fa..8c5eab1464 100644 --- a/bindings/fomac/CMakeLists.txt +++ b/bindings/fomac/CMakeLists.txt @@ -23,4 +23,12 @@ if(NOT TARGET ${TARGET_NAME}) . LINK_LIBS MQT::CoreFoMaC) + + # install the Python stub file in editable mode for better IDE support + if(SKBUILD_STATE STREQUAL "editable") + install( + FILES ${PROJECT_SOURCE_DIR}/python/mqt/core/fomac.pyi + DESTINATION . + COMPONENT ${MQT_CORE_TARGET_NAME}_Python) + endif() endif() diff --git a/bindings/ir/CMakeLists.txt b/bindings/ir/CMakeLists.txt index 23a1329279..0711646a6c 100644 --- a/bindings/ir/CMakeLists.txt +++ b/bindings/ir/CMakeLists.txt @@ -22,4 +22,13 @@ if(NOT TARGET ${MQT_CORE_TARGET_NAME}-ir-bindings) LINK_LIBS MQT::CoreIR MQT::CoreQASM) + + # install the Python stub file in editable mode for better IDE support + if(SKBUILD_STATE STREQUAL "editable") + file(GLOB_RECURSE IR_PYI_FILES ${PROJECT_SOURCE_DIR}/python/mqt/core/ir/*.pyi) + install( + FILES ${IR_PYI_FILES} + DESTINATION . + COMPONENT ${MQT_CORE_TARGET_NAME}_Python) + endif() endif() diff --git a/bindings/na/fomac/CMakeLists.txt b/bindings/na/fomac/CMakeLists.txt index 8ee95e3500..776805d16d 100644 --- a/bindings/na/fomac/CMakeLists.txt +++ b/bindings/na/fomac/CMakeLists.txt @@ -23,4 +23,12 @@ if(NOT TARGET ${TARGET_NAME}) ./na LINK_LIBS MQT::CoreNAFoMaC) + + # install the Python stub file in editable mode for better IDE support + if(SKBUILD_STATE STREQUAL "editable") + install( + FILES ${PROJECT_SOURCE_DIR}/python/mqt/core/na/fomac.pyi + DESTINATION . + COMPONENT ${MQT_CORE_TARGET_NAME}_Python) + endif() endif() From c0daa5ef84ca9a51bde6c4eb79fb6f491507e3dc Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Thu, 18 Dec 2025 02:15:57 +0100 Subject: [PATCH 100/144] Add pattern file and improve stub-generation script --- .license-tools-config.json | 3 ++- bindings/core_patterns.txt | 3 +++ bindings/generate-stubs.sh | 37 +++++++++++++------------------------ 3 files changed, 18 insertions(+), 25 deletions(-) create mode 100644 bindings/core_patterns.txt diff --git a/.license-tools-config.json b/.license-tools-config.json index 55387c4477..1172b134ef 100644 --- a/.license-tools-config.json +++ b/.license-tools-config.json @@ -33,6 +33,7 @@ ".*\\.tex", "uv\\.lock", "py\\.typed", - ".*build.*" + ".*build.*", + "core_patterns.txt" ] } diff --git a/bindings/core_patterns.txt b/bindings/core_patterns.txt new file mode 100644 index 0000000000..55bcfafb27 --- /dev/null +++ b/bindings/core_patterns.txt @@ -0,0 +1,3 @@ +_hashable_values_: + +_unhashable_values_map_: diff --git a/bindings/generate-stubs.sh b/bindings/generate-stubs.sh index 6b16fdeecb..92609bfc1b 100755 --- a/bindings/generate-stubs.sh +++ b/bindings/generate-stubs.sh @@ -35,33 +35,22 @@ for file in "${files[@]}"; do rm -f "$file" done -# Generate new stub files -uv run --no-sync -m nanobind.stubgen -m mqt.core.ir -o python/mqt/core/ir/__init__.pyi -P -uv run --no-sync -m nanobind.stubgen -m mqt.core.ir.operations -o python/mqt/core/ir/operations.pyi -P -uv run --no-sync -m nanobind.stubgen -m mqt.core.ir.registers -o python/mqt/core/ir/registers.pyi -P -uv run --no-sync -m nanobind.stubgen -m mqt.core.ir.symbolic -o python/mqt/core/ir/symbolic.pyi -P -uv run --no-sync -m nanobind.stubgen -m mqt.core.dd -o python/mqt/core/dd.pyi -P -uv run --no-sync -m nanobind.stubgen -m mqt.core.fomac -o python/mqt/core/fomac.pyi -P -uv run --no-sync -m nanobind.stubgen -m mqt.core.na.fomac -o python/mqt/core/na/fomac.pyi -P +# Define +core_dir=./python/mqt/core +core_patterns=./bindings/core_patterns.txt -# Remove private Enum members from the stub files -for file in "${files[@]}"; do - if [[ "$OSTYPE" == "darwin"* ]]; then - sed -i '' '/_hashable_values_:/d' "$file" - sed -i '' '/_unhashable_values_map_:/d' "$file" - else - sed -i '/_hashable_values_:/d' "$file" - sed -i '/_unhashable_values_map_:/d' "$file" - fi -done +# Generate new stub files +uv run --no-sync -m nanobind.stubgen -m mqt.core.ir -o $core_dir/ir/__init__.pyi -p $core_patterns -P +uv run --no-sync -m nanobind.stubgen -m mqt.core.ir.operations -o $core_dir/ir/operations.pyi -p $core_patterns -P +uv run --no-sync -m nanobind.stubgen -m mqt.core.ir.registers -o $core_dir/ir/registers.pyi -p $core_patterns -P +uv run --no-sync -m nanobind.stubgen -m mqt.core.ir.symbolic -o $core_dir/ir/symbolic.pyi -p $core_patterns -P +uv run --no-sync -m nanobind.stubgen -m mqt.core.dd -o $core_dir/dd.pyi -p $core_patterns -P +uv run --no-sync -m nanobind.stubgen -m mqt.core.fomac -o $core_dir/fomac.pyi -p $core_patterns -P +uv run --no-sync -m nanobind.stubgen -m mqt.core.na.fomac -o $core_dir/na/fomac.pyi -p $core_patterns -P set +e -# Add license headers to the generated stub files +# Run prek on generated stub files for file in "${files[@]}"; do - prek license-tools --files "$file" + uvx prek --files "$file" done - -# Run ruff -uvx ruff check -uvx ruff format From 2d7fe9f610caebdb7b38eca2a36b3c5effa0d646 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Thu, 18 Dec 2025 02:32:21 +0100 Subject: [PATCH 101/144] Update changelog and upgrade guide --- CHANGELOG.md | 3 +++ UPGRADING.md | 18 +++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2614c6bd0..c9deba254e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,8 @@ This project adheres to [Semantic Versioning], with the exception that minor rel ### Changed +- ♻️ Migrate Python bindings from `pybind11` to `nanobind` ([#1383]) ([**@denialhaag**]) +- 📦️ Provide Stable ABI wheels for Python 3.12+ ([#1383]) ([**@burgholzer**], [**@denialhaag**]) - ✨ Add common definitions and utilities for QDMI ([#1355]) ([**@burgholzer**]) - 🚚 Move `NA` QDMI device in its right place next to other QDMI devices ([#1355]) ([**@burgholzer**]) - ♻️ Allow repeated loading of QDMI device library with potentially different session configurations ([#1355]) ([**@burgholzer**]) @@ -285,6 +287,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool [#1385]: https://github.com/munich-quantum-toolkit/core/pull/1385 [#1384]: https://github.com/munich-quantum-toolkit/core/pull/1384 +[#1383]: https://github.com/munich-quantum-toolkit/core/pull/1383 [#1382]: https://github.com/munich-quantum-toolkit/core/pull/1382 [#1381]: https://github.com/munich-quantum-toolkit/core/pull/1381 [#1378]: https://github.com/munich-quantum-toolkit/core/pull/1378 diff --git a/UPGRADING.md b/UPGRADING.md index 9b56d35c1d..7b136656ae 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -52,12 +52,24 @@ In the process, the `mqt-core-dd-compare` entry point as well as the `evaluation The `eval/dd_evaluation.py` script acts as a drop-in replacement for the previous CLI entry point. Since the `eval` directory is not part of the Python package, this functionality is only available via source installations or by cloning the repository. -### Removal of Python 3.13t wheels +### Python wheels +This release contains two changes to the distributed wheels. + +First, we have removed all wheels for Python 3.13t. Free-threading Python was introduced as an experimental feature in Python 3.13. It became stable in Python 3.14. -To conserve space on PyPI and to reduce the CD build times, we have removed all wheels for Python 3.13t from our CI. -We continue to provide wheels for the regular Python versions 3.10 to 3.14, as well as 3.14t. + +Second, for Python 3.12+, we are now providing Stable ABI wheels instead of separate version-specific wheels. +This was enabled by migrating our Python bindings from `pybind11` to `nanobind`. + +Both of these changes were made in the interest of conserving PyPI space and reducing CD build times. +The full list of Python now reads: + +- 3.10 +- 3.11 +- 3.12+ Stable ABI +- 3.14t ## [3.3.0] From d4da0ab0b271fddaa4dbc760f11efd027d699c66 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Thu, 18 Dec 2025 02:34:05 +0100 Subject: [PATCH 102/144] Fix installation locations --- bindings/ir/CMakeLists.txt | 2 +- bindings/na/fomac/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/ir/CMakeLists.txt b/bindings/ir/CMakeLists.txt index 0711646a6c..a04353a156 100644 --- a/bindings/ir/CMakeLists.txt +++ b/bindings/ir/CMakeLists.txt @@ -28,7 +28,7 @@ if(NOT TARGET ${MQT_CORE_TARGET_NAME}-ir-bindings) file(GLOB_RECURSE IR_PYI_FILES ${PROJECT_SOURCE_DIR}/python/mqt/core/ir/*.pyi) install( FILES ${IR_PYI_FILES} - DESTINATION . + DESTINATION ./ir COMPONENT ${MQT_CORE_TARGET_NAME}_Python) endif() endif() diff --git a/bindings/na/fomac/CMakeLists.txt b/bindings/na/fomac/CMakeLists.txt index 776805d16d..b1bb6e983f 100644 --- a/bindings/na/fomac/CMakeLists.txt +++ b/bindings/na/fomac/CMakeLists.txt @@ -28,7 +28,7 @@ if(NOT TARGET ${TARGET_NAME}) if(SKBUILD_STATE STREQUAL "editable") install( FILES ${PROJECT_SOURCE_DIR}/python/mqt/core/na/fomac.pyi - DESTINATION . + DESTINATION ./na COMPONENT ${MQT_CORE_TARGET_NAME}_Python) endif() endif() From 1270a0570554cacf336e24c4bbf7e6ce5f49be80 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Thu, 18 Dec 2025 02:35:32 +0100 Subject: [PATCH 103/144] Add quotes to stub-generation script --- bindings/generate-stubs.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/bindings/generate-stubs.sh b/bindings/generate-stubs.sh index 92609bfc1b..4a74aec089 100755 --- a/bindings/generate-stubs.sh +++ b/bindings/generate-stubs.sh @@ -35,18 +35,18 @@ for file in "${files[@]}"; do rm -f "$file" done -# Define +# Define common paths core_dir=./python/mqt/core core_patterns=./bindings/core_patterns.txt # Generate new stub files -uv run --no-sync -m nanobind.stubgen -m mqt.core.ir -o $core_dir/ir/__init__.pyi -p $core_patterns -P -uv run --no-sync -m nanobind.stubgen -m mqt.core.ir.operations -o $core_dir/ir/operations.pyi -p $core_patterns -P -uv run --no-sync -m nanobind.stubgen -m mqt.core.ir.registers -o $core_dir/ir/registers.pyi -p $core_patterns -P -uv run --no-sync -m nanobind.stubgen -m mqt.core.ir.symbolic -o $core_dir/ir/symbolic.pyi -p $core_patterns -P -uv run --no-sync -m nanobind.stubgen -m mqt.core.dd -o $core_dir/dd.pyi -p $core_patterns -P -uv run --no-sync -m nanobind.stubgen -m mqt.core.fomac -o $core_dir/fomac.pyi -p $core_patterns -P -uv run --no-sync -m nanobind.stubgen -m mqt.core.na.fomac -o $core_dir/na/fomac.pyi -p $core_patterns -P +uv run --no-sync -m nanobind.stubgen -m mqt.core.ir -o "$core_dir/ir/__init__.pyi" -p "$core_patterns" -P +uv run --no-sync -m nanobind.stubgen -m mqt.core.ir.operations -o "$core_dir/ir/operations.pyi" -p "$core_patterns" -P +uv run --no-sync -m nanobind.stubgen -m mqt.core.ir.registers -o "$core_dir/ir/registers.pyi" -p "$core_patterns" -P +uv run --no-sync -m nanobind.stubgen -m mqt.core.ir.symbolic -o "$core_dir/ir/symbolic.pyi" -p "$core_patterns" -P +uv run --no-sync -m nanobind.stubgen -m mqt.core.dd -o "$core_dir/dd.pyi" -p "$core_patterns" -P +uv run --no-sync -m nanobind.stubgen -m mqt.core.fomac -o "$core_dir/fomac.pyi" -p "$core_patterns" -P +uv run --no-sync -m nanobind.stubgen -m mqt.core.na.fomac -o "$core_dir/na/fomac.pyi" -p "$core_patterns" -P set +e From 4d940c7fbfb2f83862a1398a482f75142a73f00b Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Thu, 18 Dec 2025 18:11:43 +0100 Subject: [PATCH 104/144] Test new workflows version --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d773d7fea1..885febc8fe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -197,8 +197,9 @@ jobs: name: 🐍 Lint needs: change-detection if: fromJSON(needs.change-detection.outputs.run-python-tests) - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-linter.yml@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-linter.yml@check-stubs # v1.17.4 with: + check-stubs: true enable-ty: true build-sdist: From b43988bc5726ab506513abc6fcd84ed83e0bfdb5 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Thu, 18 Dec 2025 18:12:37 +0100 Subject: [PATCH 105/144] Dummy change to test if check-stubs works --- bindings/dd/register_vector_dds.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/dd/register_vector_dds.cpp b/bindings/dd/register_vector_dds.cpp index 97fbba51b4..6f203e8e29 100644 --- a/bindings/dd/register_vector_dds.cpp +++ b/bindings/dd/register_vector_dds.cpp @@ -97,7 +97,7 @@ void registerVectorDDs(const nb::module_& m) { threshold: The threshold for not including amplitudes in the state vector. Defaults to 0.0. Returns: - The state vector. + The statevector. Raises: MemoryError: If the memory allocation fails.)pb"); From 7e10e9e5a3a1fe0c3793c45b4ecf63310751c07b Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Thu, 18 Dec 2025 18:13:26 +0100 Subject: [PATCH 106/144] Fix comment --- bindings/ir/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/ir/CMakeLists.txt b/bindings/ir/CMakeLists.txt index a04353a156..b560e19675 100644 --- a/bindings/ir/CMakeLists.txt +++ b/bindings/ir/CMakeLists.txt @@ -23,7 +23,7 @@ if(NOT TARGET ${MQT_CORE_TARGET_NAME}-ir-bindings) MQT::CoreIR MQT::CoreQASM) - # install the Python stub file in editable mode for better IDE support + # install the Python stub files in editable mode for better IDE support if(SKBUILD_STATE STREQUAL "editable") file(GLOB_RECURSE IR_PYI_FILES ${PROJECT_SOURCE_DIR}/python/mqt/core/ir/*.pyi) install( From 0206fd4fae23f56e74a761accc0073f9ee617bba Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Thu, 18 Dec 2025 18:18:54 +0100 Subject: [PATCH 107/144] Further improve stub-generation script --- bindings/generate-stubs.sh | 44 +++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/bindings/generate-stubs.sh b/bindings/generate-stubs.sh index 4a74aec089..07b6bd1f73 100755 --- a/bindings/generate-stubs.sh +++ b/bindings/generate-stubs.sh @@ -9,36 +9,40 @@ set -euo pipefail -files=( - "./python/mqt/core/ir/__init__.pyi" - "./python/mqt/core/ir/operations.pyi" - "./python/mqt/core/ir/registers.pyi" - "./python/mqt/core/ir/symbolic.pyi" - "./python/mqt/core/dd.pyi" - "./python/mqt/core/fomac.pyi" - "./python/mqt/core/na/fomac.pyi" +core_dir=./python/mqt/core +stub_files=( + "$core_dir/ir/__init__.pyi" + "$core_dir/ir/operations.pyi" + "$core_dir/ir/registers.pyi" + "$core_dir/ir/symbolic.pyi" + "$core_dir/dd.pyi" + "$core_dir/fomac.pyi" + "$core_dir/na/fomac.pyi" ) -# Check if all files exist -for file in "${files[@]}"; do - if [ ! -f "$file" ]; then - echo "Error: $file does not exist. Are you running this script from the root directory?" +core_patterns=./bindings/core_patterns.txt + +# Check if all stub files exist +for stub_file in "${stub_files[@]}"; do + if [ ! -f "$stub_file" ]; then + echo "Error: $stub_file does not exist. Are you running this script from the root directory?" exit 1 fi done +if [ ! -f "$core_patterns" ]; then + echo "Error: $core_patterns does not exist. Are you running this script from the root directory?" + exit 1 +fi + # Ensure that the most recent version of mqt.core is installed uv sync # Remove the existing stub files -for file in "${files[@]}"; do - rm -f "$file" +for stub_file in "${stub_files[@]}"; do + rm -f "$stub_file" done -# Define common paths -core_dir=./python/mqt/core -core_patterns=./bindings/core_patterns.txt - # Generate new stub files uv run --no-sync -m nanobind.stubgen -m mqt.core.ir -o "$core_dir/ir/__init__.pyi" -p "$core_patterns" -P uv run --no-sync -m nanobind.stubgen -m mqt.core.ir.operations -o "$core_dir/ir/operations.pyi" -p "$core_patterns" -P @@ -51,6 +55,6 @@ uv run --no-sync -m nanobind.stubgen -m mqt.core.na.fomac -o "$core_dir/na/fomac set +e # Run prek on generated stub files -for file in "${files[@]}"; do - uvx prek --files "$file" +for stub_file in "${stub_files[@]}"; do + uvx prek --files "$stub_file" done From dd1f962ffdca488009219d5d97d45b4d3d217fcc Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Thu, 18 Dec 2025 18:22:46 +0100 Subject: [PATCH 108/144] Add bindings-specific clang-tidy config --- bindings/.clang-tidy | 6 ++++++ bindings/fomac/fomac.cpp | 16 +++++++-------- bindings/ir/operations/register_control.cpp | 4 ++-- bindings/ir/operations/register_operation.cpp | 4 ++-- bindings/ir/register_permutation.cpp | 4 ++-- bindings/ir/register_registers.cpp | 4 ++-- bindings/ir/symbolic/register_expression.cpp | 4 ++-- bindings/ir/symbolic/register_term.cpp | 4 ++-- bindings/ir/symbolic/register_variable.cpp | 8 ++++---- bindings/na/fomac/fomac.cpp | 20 +++++++++---------- 10 files changed, 40 insertions(+), 34 deletions(-) create mode 100644 bindings/.clang-tidy diff --git a/bindings/.clang-tidy b/bindings/.clang-tidy new file mode 100644 index 0000000000..5b963db34d --- /dev/null +++ b/bindings/.clang-tidy @@ -0,0 +1,6 @@ +InheritParentConfig: true +Checks: | + -cppcoreguidelines-avoid-c-arrays, + -cppcoreguidelines-owning-memory, + -misc-redundant-expression, + -modernize-avoid-c-arrays diff --git a/bindings/fomac/fomac.cpp b/bindings/fomac/fomac.cpp index 87d1cd1bf3..90de05e92e 100644 --- a/bindings/fomac/fomac.cpp +++ b/bindings/fomac/fomac.cpp @@ -167,9 +167,9 @@ All authentication parameters are optional and can be provided as keyword argume job.def_prop_ro("num_shots", &fomac::Session::Job::getNumShots, "Returns the number of shots for the job."); - job.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + job.def(nb::self == nb::self, nb::sig("def __eq__(self, arg: object, /) -> bool")); - job.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + job.def(nb::self != nb::self, nb::sig("def __ne__(self, arg: object, /) -> bool")); // JobStatus enum @@ -280,9 +280,9 @@ All authentication parameters are optional and can be provided as keyword argume return ""; }); - device.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + device.def(nb::self == nb::self, nb::sig("def __eq__(self, arg: object, /) -> bool")); - device.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + device.def(nb::self != nb::self, nb::sig("def __ne__(self, arg: object, /) -> bool")); // Site class @@ -333,9 +333,9 @@ All authentication parameters are optional and can be provided as keyword argume return ""; }); - site.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + site.def(nb::self == nb::self, nb::sig("def __eq__(self, arg: object, /) -> bool")); - site.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + site.def(nb::self != nb::self, nb::sig("def __ne__(self, arg: object, /) -> bool")); // Operation class @@ -414,9 +414,9 @@ All authentication parameters are optional and can be provided as keyword argume return ""; }); - operation.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + operation.def(nb::self == nb::self, nb::sig("def __eq__(self, arg: object, /) -> bool")); - operation.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + operation.def(nb::self != nb::self, nb::sig("def __ne__(self, arg: object, /) -> bool")); #ifndef _WIN32 diff --git a/bindings/ir/operations/register_control.cpp b/bindings/ir/operations/register_control.cpp index 9c3f868f93..36ce5196e3 100644 --- a/bindings/ir/operations/register_control.cpp +++ b/bindings/ir/operations/register_control.cpp @@ -46,9 +46,9 @@ void registerControl(const nb::module_& m) { control.def("__str__", [](const qc::Control& c) { return c.toString(); }); control.def("__repr__", [](const qc::Control& c) { return c.toString(); }); - control.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + control.def(nb::self == nb::self, nb::sig("def __eq__(self, arg: object, /) -> bool")); - control.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + control.def(nb::self != nb::self, nb::sig("def __ne__(self, arg: object, /) -> bool")); control.def(nb::hash(nb::self)); diff --git a/bindings/ir/operations/register_operation.cpp b/bindings/ir/operations/register_operation.cpp index 312609856f..466de6134a 100644 --- a/bindings/ir/operations/register_operation.cpp +++ b/bindings/ir/operations/register_operation.cpp @@ -155,9 +155,9 @@ void registerOperation(const nb::module_& m) { .def("invert", &qc::Operation::invert, "Invert the operation (in-place).") - .def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + .def(nb::self == nb::self, nb::sig("def __eq__(self, arg: object, /) -> bool")) - .def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + .def(nb::self != nb::self, nb::sig("def __ne__(self, arg: object, /) -> bool")) .def(nb::hash(nb::self)) diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index 73d484a0dd..77609cd0a9 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -177,9 +177,9 @@ void registerPermutation(const nb::module_& m) { nb::sig("def items(self) -> collections.abc.ItemsView[int, int]"), nb::keep_alive<0, 1>()) - .def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + .def(nb::self == nb::self, nb::sig("def __eq__(self, arg: object, /) -> bool")) - .def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + .def(nb::self != nb::self, nb::sig("def __ne__(self, arg: object, /) -> bool")) .def("__str__", diff --git a/bindings/ir/register_registers.cpp b/bindings/ir/register_registers.cpp index df87c35be1..5d6e09ef96 100644 --- a/bindings/ir/register_registers.cpp +++ b/bindings/ir/register_registers.cpp @@ -75,9 +75,9 @@ void registerRegisters(const nb::module_& m) { [](const qc::QuantumRegister& reg) { return reg.getEndIndex(); }, "Index of the last qubit in the quantum register.") - .def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + .def(nb::self == nb::self, nb::sig("def __eq__(self, arg: object, /) -> bool")) - .def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + .def(nb::self != nb::self, nb::sig("def __ne__(self, arg: object, /) -> bool")) .def(nb::hash(nb::self)) diff --git a/bindings/ir/symbolic/register_expression.cpp b/bindings/ir/symbolic/register_expression.cpp index fa19b52051..f6d1960511 100644 --- a/bindings/ir/symbolic/register_expression.cpp +++ b/bindings/ir/symbolic/register_expression.cpp @@ -128,9 +128,9 @@ Alternatively, an expression can be created with a single term and a constant or // division operators .def(nb::self / double(), nb::is_operator()) // comparison operators - .def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + .def(nb::self == nb::self, nb::sig("def __eq__(self, arg: object, /) -> bool")) - .def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + .def(nb::self != nb::self, nb::sig("def __ne__(self, arg: object, /) -> bool")) .def(nb::hash(nb::self)) diff --git a/bindings/ir/symbolic/register_term.cpp b/bindings/ir/symbolic/register_term.cpp index 2e7b5ba823..25b157dff9 100644 --- a/bindings/ir/symbolic/register_term.cpp +++ b/bindings/ir/symbolic/register_term.cpp @@ -62,9 +62,9 @@ void registerTerm(const nb::module_& m) { .def(double() * nb::self, nb::is_operator()) .def(nb::self / double(), nb::is_operator()) - .def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + .def(nb::self == nb::self, nb::sig("def __eq__(self, arg: object, /) -> bool")) - .def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + .def(nb::self != nb::self, nb::sig("def __ne__(self, arg: object, /) -> bool")) .def(nb::hash(nb::self)) diff --git a/bindings/ir/symbolic/register_variable.cpp b/bindings/ir/symbolic/register_variable.cpp index 9c95e8503f..93bcc243c2 100644 --- a/bindings/ir/symbolic/register_variable.cpp +++ b/bindings/ir/symbolic/register_variable.cpp @@ -36,12 +36,12 @@ void registerVariable(const nb::module_& m) { .def("__str__", &sym::Variable::getName) .def("__repr__", &sym::Variable::getName) - .def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + .def(nb::self == nb::self, nb::sig("def __eq__(self, arg: object, /) -> bool")) - .def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + .def(nb::self != nb::self, nb::sig("def __ne__(self, arg: object, /) -> bool")) .def(nb::hash(nb::self)) - .def(nb::self < nb::self) // NOLINT(misc-redundant-expression) - .def(nb::self > nb::self); // NOLINT(misc-redundant-expression) + .def(nb::self < nb::self) + .def(nb::self > nb::self); } } // namespace mqt diff --git a/bindings/na/fomac/fomac.cpp b/bindings/na/fomac/fomac.cpp index 54e4f7daf7..5f3c140e4b 100644 --- a/bindings/na/fomac/fomac.cpp +++ b/bindings/na/fomac/fomac.cpp @@ -52,9 +52,9 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { return ""; }); - vector.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + vector.def(nb::self == nb::self, nb::sig("def __eq__(self, arg: object, /) -> bool")); - vector.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + vector.def(nb::self != nb::self, nb::sig("def __ne__(self, arg: object, /) -> bool")); auto region = nb::class_( @@ -70,9 +70,9 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { return ""; }); - size.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + size.def(nb::self == nb::self, nb::sig("def __eq__(self, arg: object, /) -> bool")); - size.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + size.def(nb::self != nb::self, nb::sig("def __ne__(self, arg: object, /) -> bool")); region.def_ro("origin", &na::Device::Region::origin, @@ -81,9 +81,9 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { region.def("__repr__", [](const na::Device::Region& r) { return ""; }); - region.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + region.def(nb::self == nb::self, nb::sig("def __eq__(self, arg: object, /) -> bool")); - region.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + region.def(nb::self != nb::self, nb::sig("def __ne__(self, arg: object, /) -> bool")); lattice.def_ro("lattice_origin", &na::Device::Lattice::latticeOrigin, @@ -99,9 +99,9 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { lattice.def("__repr__", [](const na::Device::Lattice& l) { return ""; }); - lattice.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + lattice.def(nb::self == nb::self, nb::sig("def __eq__(self, arg: object, /) -> bool")); - lattice.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + lattice.def(nb::self != nb::self, nb::sig("def __ne__(self, arg: object, /) -> bool")); device.def_prop_ro("traps", &na::Session::Device::getTraps, @@ -130,9 +130,9 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) { Returns: The converted NA FoMaC Device or None if the conversion is not possible.)pb"); - device.def(nb::self == nb::self, // NOLINT(misc-redundant-expression) + device.def(nb::self == nb::self, nb::sig("def __eq__(self, arg: object, /) -> bool")); - device.def(nb::self != nb::self, // NOLINT(misc-redundant-expression) + device.def(nb::self != nb::self, nb::sig("def __ne__(self, arg: object, /) -> bool")); m.def("devices", &na::Session::getDevices, From 571acd551b958563b69c26222092a7d6b96bc9e2 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Fri, 19 Dec 2025 00:02:35 +0100 Subject: [PATCH 109/144] Move up license-tools hook --- .pre-commit-config.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 61af566ff7..d3f9fb34df 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -58,6 +58,12 @@ repos: - id: rst-directive-colons - id: rst-inline-touching-normal + # Check for license headers + - repo: https://github.com/emzeat/mz-lictools + rev: v2.9.0 + hooks: + - id: license-tools + # Ensure uv lock file is up-to-date - repo: https://github.com/astral-sh/uv-pre-commit rev: 0.9.17 @@ -101,12 +107,6 @@ repos: - id: blacken-docs additional_dependencies: [black==25.*] - # Check for license headers - - repo: https://github.com/emzeat/mz-lictools - rev: v2.9.0 - hooks: - - id: license-tools - # Clang-format the C++ part of the code base automatically - repo: https://github.com/pre-commit/mirrors-clang-format rev: v21.1.7 From 0f780b049cf06d4b9bd221327e71df61cb3dfb24 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Fri, 19 Dec 2025 00:08:43 +0100 Subject: [PATCH 110/144] Further improve stub-generation script --- bindings/generate-stubs.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bindings/generate-stubs.sh b/bindings/generate-stubs.sh index 07b6bd1f73..4d72b1887b 100755 --- a/bindings/generate-stubs.sh +++ b/bindings/generate-stubs.sh @@ -56,5 +56,7 @@ set +e # Run prek on generated stub files for stub_file in "${stub_files[@]}"; do - uvx prek --files "$stub_file" + uvx prek license-tools --quiet --files "$stub_file" + uvx prek ruff-check --quiet --files "$stub_file" + uvx prek ruff-format --quiet --files "$stub_file" done From ac2ec2040d24061315b1ed323cfd8cb96b778818 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Fri, 19 Dec 2025 00:31:37 +0100 Subject: [PATCH 111/144] Improvements to make the rabbit happy --- UPGRADING.md | 2 +- bindings/.clang-tidy | 3 +- bindings/ir/register_registers.cpp | 2 +- bindings/ir/symbolic/register_expression.cpp | 36 ++++++++++++++------ 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/UPGRADING.md b/UPGRADING.md index 7b136656ae..182fb0db70 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -63,7 +63,7 @@ It became stable in Python 3.14. Second, for Python 3.12+, we are now providing Stable ABI wheels instead of separate version-specific wheels. This was enabled by migrating our Python bindings from `pybind11` to `nanobind`. -Both of these changes were made in the interest of conserving PyPI space and reducing CD build times. +Both of these changes were made in the interest of conserving PyPI space and reducing CI/CD build times. The full list of Python now reads: - 3.10 diff --git a/bindings/.clang-tidy b/bindings/.clang-tidy index 5b963db34d..08f5528c9e 100644 --- a/bindings/.clang-tidy +++ b/bindings/.clang-tidy @@ -1,6 +1,5 @@ InheritParentConfig: true Checks: | - -cppcoreguidelines-avoid-c-arrays, + -*-avoid-c-arrays, -cppcoreguidelines-owning-memory, -misc-redundant-expression, - -modernize-avoid-c-arrays diff --git a/bindings/ir/register_registers.cpp b/bindings/ir/register_registers.cpp index 5d6e09ef96..6920a28e0c 100644 --- a/bindings/ir/register_registers.cpp +++ b/bindings/ir/register_registers.cpp @@ -45,7 +45,7 @@ void registerRegisters(const nb::module_& m) { "start", [](const qc::QuantumRegister& reg) { return reg.getStartIndex(); }, [](qc::QuantumRegister& reg, const nb::int_& start) { - auto startInt = static_cast(start); + const auto startInt = static_cast(start); if (startInt < 0) { throw nb::value_error("Start index cannot be negative"); } diff --git a/bindings/ir/symbolic/register_expression.cpp b/bindings/ir/symbolic/register_expression.cpp index f6d1960511..580d249e0d 100644 --- a/bindings/ir/symbolic/register_expression.cpp +++ b/bindings/ir/symbolic/register_expression.cpp @@ -107,21 +107,37 @@ Alternatively, an expression can be created with a single term and a constant or // addition operators .def(nb::self + nb::self, nb::is_operator()) .def(nb::self + double(), nb::is_operator()) - .def("__add__", [](const sym::Expression& lhs, - const sym::Term& rhs) { return lhs + rhs; }) - .def("__radd__", [](const sym::Expression& rhs, - const sym::Term& lhs) { return lhs + rhs; }) - .def("__radd__", [](const sym::Expression& rhs, - const double lhs) { return rhs + lhs; }) + .def( + "__add__", + [](const sym::Expression& lhs, + const sym::Term& rhs) { return lhs + rhs; }, + nb::is_operator()) + .def( + "__radd__", + [](const sym::Expression& rhs, + const sym::Term& lhs) { return lhs + rhs; }, + nb::is_operator()) + .def( + "__radd__", + [](const sym::Expression& rhs, const double lhs) { + return rhs + lhs; + }, + nb::is_operator()) // subtraction operators // NOLINTNEXTLINE(misc-redundant-expression) .def(nb::self - nb::self, nb::is_operator()) .def(nb::self - double(), nb::is_operator()) .def(double() - nb::self, nb::is_operator()) - .def("__sub__", [](const sym::Expression& lhs, - const sym::Term& rhs) { return lhs - rhs; }) - .def("__rsub__", [](const sym::Expression& rhs, - const sym::Term& lhs) { return lhs - rhs; }) + .def( + "__sub__", + [](const sym::Expression& lhs, + const sym::Term& rhs) { return lhs - rhs; }, + nb::is_operator()) + .def( + "__rsub__", + [](const sym::Expression& rhs, + const sym::Term& lhs) { return lhs - rhs; }, + nb::is_operator()) // multiplication operators .def(nb::self * double(), nb::is_operator()) .def(double() * nb::self, nb::is_operator()) From 08e10a0ba3f952db28bf91fc427d9dca4ba96238 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Fri, 19 Dec 2025 00:52:37 +0100 Subject: [PATCH 112/144] Specify rv_policy in FoMaC bindings --- bindings/fomac/fomac.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bindings/fomac/fomac.cpp b/bindings/fomac/fomac.cpp index 90de05e92e..b3d46c74dc 100644 --- a/bindings/fomac/fomac.cpp +++ b/bindings/fomac/fomac.cpp @@ -109,6 +109,7 @@ All authentication parameters are optional and can be provided as keyword argume >>> devices = session.get_devices())pb"); session.def("get_devices", &fomac::Session::getDevices, + nb::rv_policy::reference_internal, R"pb(Get available devices from this session. Returns: @@ -273,8 +274,8 @@ All authentication parameters are optional and can be provided as keyword argume "Returns the list of program formats supported by the device."); device.def("submit_job", &fomac::Session::Device::submitJob, "program"_a, - "program_format"_a, "num_shots"_a, nb::keep_alive<0, 1>(), - "Submits a job to the device."); + "program_format"_a, "num_shots"_a, + nb::rv_policy::reference_internal, "Submits a job to the device."); device.def("__repr__", [](const fomac::Session::Device& dev) { return ""; From 63e42c080aab2f47576d9441a3f725d49b4939af Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Fri, 19 Dec 2025 00:52:58 +0100 Subject: [PATCH 113/144] Fix upgrade guide --- UPGRADING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UPGRADING.md b/UPGRADING.md index 182fb0db70..803baa54e2 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -64,7 +64,7 @@ Second, for Python 3.12+, we are now providing Stable ABI wheels instead of sepa This was enabled by migrating our Python bindings from `pybind11` to `nanobind`. Both of these changes were made in the interest of conserving PyPI space and reducing CI/CD build times. -The full list of Python now reads: +The full list of wheels now reads: - 3.10 - 3.11 From 57d39af5b2f04b0894a234fa5cb19bb9ecbbb326 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Fri, 19 Dec 2025 00:53:38 +0100 Subject: [PATCH 114/144] Revert "Dummy change to test if check-stubs works" This reverts commit b43988bc5726ab506513abc6fcd84ed83e0bfdb5. --- bindings/dd/register_vector_dds.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/dd/register_vector_dds.cpp b/bindings/dd/register_vector_dds.cpp index 6f203e8e29..97fbba51b4 100644 --- a/bindings/dd/register_vector_dds.cpp +++ b/bindings/dd/register_vector_dds.cpp @@ -97,7 +97,7 @@ void registerVectorDDs(const nb::module_& m) { threshold: The threshold for not including amplitudes in the state vector. Defaults to 0.0. Returns: - The statevector. + The state vector. Raises: MemoryError: If the memory allocation fails.)pb"); From f248f2b73c2fa403a14f2f4d035cc972fe86185d Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Fri, 19 Dec 2025 01:56:46 +0100 Subject: [PATCH 115/144] Update to new workflows version --- .github/workflows/cd.yml | 4 ++-- .github/workflows/ci.yml | 30 +++++++++++++++--------------- .github/workflows/upstream.yml | 4 ++-- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index fcbbc1af13..1b36d4b9cd 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -12,7 +12,7 @@ permissions: jobs: build-sdist: name: 🐍 Packaging - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-sdist.yml@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-sdist.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 # Builds wheels on all supported platforms using cibuildwheel. # The wheels are uploaded as GitHub artifacts `dev-cibw-*` or `cibw-*`, depending on whether @@ -31,7 +31,7 @@ jobs: windows-2022, windows-11-arm, ] - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-wheel-cibuildwheel.yml@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-wheel-cibuildwheel.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 with: runs-on: ${{ matrix.runs-on }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 885febc8fe..c26e115123 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ concurrency: jobs: change-detection: name: 🔍 Change - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-change-detection.yml@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-change-detection.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 cpp-tests-ubuntu: name: 🇨‌ Test 🐧 @@ -30,7 +30,7 @@ jobs: - runs-on: ubuntu-24.04 compiler: gcc config: Debug - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-ubuntu.yml@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-ubuntu.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 with: runs-on: ${{ matrix.runs-on }} compiler: ${{ matrix.compiler }} @@ -50,7 +50,7 @@ jobs: - runs-on: macos-14 compiler: clang config: Debug - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-macos.yml@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-macos.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 with: runs-on: ${{ matrix.runs-on }} compiler: ${{ matrix.compiler }} @@ -71,7 +71,7 @@ jobs: - runs-on: windows-2022 compiler: msvc config: Debug - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-windows.yml@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-windows.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 with: runs-on: ${{ matrix.runs-on }} compiler: ${{ matrix.compiler }} @@ -88,7 +88,7 @@ jobs: runs-on: [ubuntu-24.04, ubuntu-24.04-arm] compiler: [gcc, clang, clang-20, clang-21] config: [Release, Debug] - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-ubuntu.yml@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-ubuntu.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 with: runs-on: ${{ matrix.runs-on }} compiler: ${{ matrix.compiler }} @@ -105,7 +105,7 @@ jobs: runs-on: [macos-14, macos-15, macos-15-intel] compiler: [clang, clang-20, clang-21, gcc-14, gcc-15] config: [Release, Debug] - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-macos.yml@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-macos.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 with: runs-on: ${{ matrix.runs-on }} compiler: ${{ matrix.compiler }} @@ -123,7 +123,7 @@ jobs: runs-on: [windows-2022, windows-2025, windows-11-arm] compiler: [msvc, clang] config: [Release] - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-windows.yml@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-windows.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 with: runs-on: ${{ matrix.runs-on }} compiler: ${{ matrix.compiler }} @@ -133,7 +133,7 @@ jobs: name: 🇨‌ Coverage needs: change-detection if: fromJSON(needs.change-detection.outputs.run-cpp-tests) - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-coverage.yml@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-coverage.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 permissions: contents: read id-token: write @@ -142,7 +142,7 @@ jobs: name: 🇨‌ Lint needs: change-detection if: fromJSON(needs.change-detection.outputs.run-cpp-linter) - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-linter.yml@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-linter.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 with: cmake-args: -DBUILD_MQT_CORE_BENCHMARKS=ON -DBUILD_MQT_CORE_MLIR=ON -DBUILD_MQT_CORE_BINDINGS=ON clang-version: 21 @@ -167,7 +167,7 @@ jobs: macos-14, windows-2022, ] - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-tests.yml@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-tests.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 with: runs-on: ${{ matrix.runs-on }} @@ -175,7 +175,7 @@ jobs: name: 🐍 Coverage needs: [change-detection, python-tests] if: fromJSON(needs.change-detection.outputs.run-python-tests) - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-coverage.yml@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-coverage.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 permissions: contents: read id-token: write @@ -189,7 +189,7 @@ jobs: fail-fast: false matrix: runs-on: [macos-15, windows-2025] - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-tests.yml@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-tests.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 with: runs-on: ${{ matrix.runs-on }} @@ -197,7 +197,7 @@ jobs: name: 🐍 Lint needs: change-detection if: fromJSON(needs.change-detection.outputs.run-python-tests) - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-linter.yml@check-stubs # v1.17.4 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-linter.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 with: check-stubs: true enable-ty: true @@ -206,7 +206,7 @@ jobs: name: 🚀 CD needs: change-detection if: fromJSON(needs.change-detection.outputs.run-cd) - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-sdist.yml@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-sdist.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 build-wheel: name: 🚀 CD @@ -224,7 +224,7 @@ jobs: windows-2022, windows-11-arm, ] - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-wheel-cibuildwheel.yml@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-wheel-cibuildwheel.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 with: runs-on: ${{ matrix.runs-on }} diff --git a/.github/workflows/upstream.yml b/.github/workflows/upstream.yml index 8f20e574c2..08f3e3798b 100644 --- a/.github/workflows/upstream.yml +++ b/.github/workflows/upstream.yml @@ -19,7 +19,7 @@ jobs: fail-fast: false matrix: runs-on: [ubuntu-24.04, macos-14, windows-2022] - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-qiskit-upstream-tests.yml@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-qiskit-upstream-tests.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 with: runs-on: ${{ matrix.runs-on }} setup-z3: true @@ -28,7 +28,7 @@ jobs: name: Create issue on failure needs: qiskit-upstream-tests if: ${{ always() }} - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-qiskit-upstream-issue.yml@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-qiskit-upstream-issue.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 with: tests-result: ${{ needs.qiskit-upstream-tests.result }} permissions: From 350cad3f906ca74a265419c18d73c25c3c4894c7 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Fri, 19 Dec 2025 02:05:35 +0100 Subject: [PATCH 116/144] Set up MLIR for the C++ linter --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c26e115123..42d7fd6537 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -151,6 +151,7 @@ jobs: setup-python: true install-pkgs: "nanobind==2.10.2" cpp-linter-extra-args: "-std=c++20" + setup-mlir: true python-tests: name: 🐍 Test From 8768a1a1f9d1f79279a303020401ae42848599eb Mon Sep 17 00:00:00 2001 From: burgholzer Date: Fri, 5 Dec 2025 00:52:15 +0100 Subject: [PATCH 117/144] =?UTF-8?q?=F0=9F=90=9B=20fix=20empty=20link=20lib?= =?UTF-8?q?s=20for=20QIR=20Runner=20target?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- src/qir/runner/CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/qir/runner/CMakeLists.txt b/src/qir/runner/CMakeLists.txt index 3134001ca8..bc2155e40e 100644 --- a/src/qir/runner/CMakeLists.txt +++ b/src/qir/runner/CMakeLists.txt @@ -15,8 +15,13 @@ if(NOT TARGET ${TARGET_NAME}) add_llvm_tool(${TARGET_NAME} Runner.cpp DEPENDS intrinsics_gen) export_executable_symbols(${TARGET_NAME}) endif() + + # Get the native target libraries + llvm_map_components_to_libnames(llvm_native_libs native) + # Add link libraries - target_link_libraries(${TARGET_NAME} PRIVATE MQT::CoreQIRRuntime ${llvm_libs}) + target_link_libraries(${TARGET_NAME} PRIVATE MQT::CoreQIRRuntime LLVMOrcDebugging + ${llvm_native_libs}) # Set versioning information set_target_properties(${TARGET_NAME} PROPERTIES VERSION ${PROJECT_VERSION} EXPORT_NAME From bab4c569e8eab939ea1b8d0ce8f82d1d3455586e Mon Sep 17 00:00:00 2001 From: burgholzer Date: Sun, 21 Dec 2025 21:05:09 +0100 Subject: [PATCH 118/144] =?UTF-8?q?=F0=9F=9A=9A=20create=20dedicated=20`mq?= =?UTF-8?q?t.core.na`=20submodule=20to=20closely=20follow=20the=20structur?= =?UTF-8?q?e=20of=20other=20submodules?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- CHANGELOG.md | 1 + bindings/na/CMakeLists.txt | 28 ++++++++++++++- bindings/na/fomac/CMakeLists.txt | 34 ------------------- .../{fomac/fomac.cpp => register_fomac.cpp} | 3 +- bindings/na/register_na.cpp | 29 ++++++++++++++++ pyproject.toml | 3 +- .../mqt/core/na/{__init__.py => __init__.pyi} | 5 +-- 7 files changed, 60 insertions(+), 43 deletions(-) delete mode 100644 bindings/na/fomac/CMakeLists.txt rename bindings/na/{fomac/fomac.cpp => register_fomac.cpp} (99%) create mode 100644 bindings/na/register_na.cpp rename python/mqt/core/na/{__init__.py => __init__.pyi} (62%) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9deba254e..1bd5546f9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel - ♻️ Migrate Python bindings from `pybind11` to `nanobind` ([#1383]) ([**@denialhaag**]) - 📦️ Provide Stable ABI wheels for Python 3.12+ ([#1383]) ([**@burgholzer**], [**@denialhaag**]) +- 🚚 create dedicated `mqt.core.na` submodule to closely follow the structure of other submodules ([#1383]) ([**@burgholzer**]) - ✨ Add common definitions and utilities for QDMI ([#1355]) ([**@burgholzer**]) - 🚚 Move `NA` QDMI device in its right place next to other QDMI devices ([#1355]) ([**@burgholzer**]) - ♻️ Allow repeated loading of QDMI device library with potentially different session configurations ([#1355]) ([**@burgholzer**]) diff --git a/bindings/na/CMakeLists.txt b/bindings/na/CMakeLists.txt index 609fbf66e4..d81bedb2ea 100644 --- a/bindings/na/CMakeLists.txt +++ b/bindings/na/CMakeLists.txt @@ -6,4 +6,30 @@ # # Licensed under the MIT License -add_subdirectory(fomac) +set(TARGET_NAME "${MQT_CORE_TARGET_NAME}-na-bindings") + +if(NOT TARGET ${TARGET_NAME}) + # collect source files + file(GLOB_RECURSE SOURCES **.cpp) + + # declare the Python module + add_mqt_python_binding_nanobind( + CORE + ${TARGET_NAME} + ${SOURCES} + MODULE_NAME + na + INSTALL_DIR + . + LINK_LIBS + MQT::CoreNAFoMaC) + + # install the Python stub file in editable mode for better IDE support + if(SKBUILD_STATE STREQUAL "editable") + file(GLOB_RECURSE NA_PYI_FILES ${PROJECT_SOURCE_DIR}/python/mqt/core/na/*.pyi) + install( + FILES ${NA_PYI_FILES} + DESTINATION ./na + COMPONENT ${MQT_CORE_TARGET_NAME}_Python) + endif() +endif() diff --git a/bindings/na/fomac/CMakeLists.txt b/bindings/na/fomac/CMakeLists.txt deleted file mode 100644 index b1bb6e983f..0000000000 --- a/bindings/na/fomac/CMakeLists.txt +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM -# Copyright (c) 2025 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -set(TARGET_NAME "${MQT_CORE_TARGET_NAME}-na-fomac-bindings") - -if(NOT TARGET ${TARGET_NAME}) - # collect source files - file(GLOB_RECURSE SOURCES **.cpp) - - # declare the Python module - add_mqt_python_binding_nanobind( - CORE - ${TARGET_NAME} - ${SOURCES} - MODULE_NAME - fomac - INSTALL_DIR - ./na - LINK_LIBS - MQT::CoreNAFoMaC) - - # install the Python stub file in editable mode for better IDE support - if(SKBUILD_STATE STREQUAL "editable") - install( - FILES ${PROJECT_SOURCE_DIR}/python/mqt/core/na/fomac.pyi - DESTINATION ./na - COMPONENT ${MQT_CORE_TARGET_NAME}_Python) - endif() -endif() diff --git a/bindings/na/fomac/fomac.cpp b/bindings/na/register_fomac.cpp similarity index 99% rename from bindings/na/fomac/fomac.cpp rename to bindings/na/register_fomac.cpp index 5f3c140e4b..7587945872 100644 --- a/bindings/na/fomac/fomac.cpp +++ b/bindings/na/register_fomac.cpp @@ -9,7 +9,6 @@ */ #include "fomac/FoMaC.hpp" - #include "na/fomac/Device.hpp" #include "qdmi/na/Generator.hpp" @@ -35,7 +34,7 @@ template [[nodiscard]] auto repr(T c) -> std::string { } // namespace -NB_MODULE(MQT_CORE_MODULE_NAME, m) { +void registerFomac(nb::module_& m) { nb::module_::import_("mqt.core.fomac"); auto device = nb::class_( diff --git a/bindings/na/register_na.cpp b/bindings/na/register_na.cpp new file mode 100644 index 0000000000..0a8a7a145f --- /dev/null +++ b/bindings/na/register_na.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM + * Copyright (c) 2025 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include + +namespace mqt { + +namespace nb = nanobind; + +// forward declarations +void registerFomac(nb::module_& m); + +NB_MODULE(MQT_CORE_MODULE_NAME, m) { + m.doc() = R"pb(MQT Core NA - The MQT Core neutral atom module. + +This module contains all neutral atom related functionality of MQT Core.)pb"; + + nb::module_ fomac = m.def_submodule("fomac"); + registerFomac(fomac); +} + +} // namespace mqt diff --git a/pyproject.toml b/pyproject.toml index de170e6568..66b6d5310e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -96,8 +96,7 @@ build.targets = [ "mqt-core-qdmi-na-device", "mqt-core-qdmi-ddsim-device", "mqt-core-fomac-bindings", - "mqt-core-na-fomac", - "mqt-core-na-fomac-bindings", + "mqt-core-na-bindings", ] metadata.version.provider = "scikit_build_core.metadata.setuptools_scm" diff --git a/python/mqt/core/na/__init__.py b/python/mqt/core/na/__init__.pyi similarity index 62% rename from python/mqt/core/na/__init__.py rename to python/mqt/core/na/__init__.pyi index 4f663baa34..e89adef973 100644 --- a/python/mqt/core/na/__init__.py +++ b/python/mqt/core/na/__init__.pyi @@ -6,7 +6,4 @@ # # Licensed under the MIT License -"""MQT Core NA - The MQT Core neutral atom module. - -This module contains all neutral atom related functionality of MQT Core. -""" +from . import fomac as fomac From 65cc21a2bead34fbd46a5c31e3897e091ea79e39 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Sun, 21 Dec 2025 21:05:41 +0100 Subject: [PATCH 119/144] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20upgrade=20`ty`=20d?= =?UTF-8?q?ependency=20to=20version=200.0.5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- pyproject.toml | 2 +- uv.lock | 44 ++++++++++++++++++++++---------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 66b6d5310e..70fb0da577 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -359,5 +359,5 @@ dev = [ {include-group = "test"}, "lit>=18.1.8", "nox>=2025.11.12", - "ty==0.0.1a34", + "ty==0.0.5", ] diff --git a/uv.lock b/uv.lock index 28f4e1321a..ea3aad228b 100644 --- a/uv.lock +++ b/uv.lock @@ -1568,7 +1568,7 @@ dev = [ { name = "qiskit", extras = ["qasm3-import"], specifier = ">=1.0.0" }, { name = "scikit-build-core", specifier = ">=0.11.6" }, { name = "setuptools-scm", specifier = ">=9.2.2" }, - { name = "ty", specifier = "==0.0.1a34" }, + { name = "ty", specifier = "==0.0.5" }, ] docs = [ { name = "breathe", specifier = ">=4.36.0" }, @@ -3317,27 +3317,27 @@ wheels = [ [[package]] name = "ty" -version = "0.0.1a34" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f5/f9/f467d2fbf02a37af5d779eb21c59c7d5c9ce8c48f620d590d361f5220208/ty-0.0.1a34.tar.gz", hash = "sha256:659e409cc3b5c9fb99a453d256402a4e3bd95b1dbcc477b55c039697c807ab79", size = 4735988, upload-time = "2025-12-12T18:29:23.204Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/b7/d5a5c611baaa20e85971a7c9a527aaf3e8fb47e15de88d1db39c64ee3638/ty-0.0.1a34-py3-none-linux_armv6l.whl", hash = "sha256:00c138e28b12a80577ee3e15fc638eb1e35cf5aa75f5967bf2d1893916ce571c", size = 9708675, upload-time = "2025-12-12T18:29:06.571Z" }, - { url = "https://files.pythonhosted.org/packages/cb/62/0b78976c8da58b90a86d1a1b8816ff4a6e8437f6e52bb6800c4483242e7f/ty-0.0.1a34-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cbb9c187164675647143ecb56e684d6766f7d5ba7f6874a369fe7c3d380a6c92", size = 9515760, upload-time = "2025-12-12T18:28:56.901Z" }, - { url = "https://files.pythonhosted.org/packages/39/1f/4e3d286b37aab3428a30b8f5db5533b8ce6e23b1bd84f77a137bd782b418/ty-0.0.1a34-py3-none-macosx_11_0_arm64.whl", hash = "sha256:68b2375b366ee799a896594cde393a1b60414efdfd31399c326bfc136bfc41f3", size = 9064633, upload-time = "2025-12-12T18:29:10.211Z" }, - { url = "https://files.pythonhosted.org/packages/5d/31/e17049b868f5cac7590c000f31ff9453e4360125416da4e8195e82b5409a/ty-0.0.1a34-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f6b68d9673e43bdd5bdcaa6b5db50e873431fc44dde5e25e253e8226ec93ac1", size = 9310295, upload-time = "2025-12-12T18:29:21.635Z" }, - { url = "https://files.pythonhosted.org/packages/77/1d/7a89b3032e84a01223d0c33e47f33eef436ca36949b28600554a2a4da1f8/ty-0.0.1a34-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:832b360fd397c076e294c252db52581b9ecb38d8063d6262ac927610540702be", size = 9498451, upload-time = "2025-12-12T18:29:24.955Z" }, - { url = "https://files.pythonhosted.org/packages/fa/5e/e782c4367d14b965b1ee9bddc3f3102982ff1cc2dae699c201ecd655e389/ty-0.0.1a34-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0cb6fc497f1feb67e299fd3507ed30498c7e15b31099b3dcdbeca6b7ac2d3129", size = 9912522, upload-time = "2025-12-12T18:29:00.252Z" }, - { url = "https://files.pythonhosted.org/packages/9c/25/4d72d7174b60adeb9df6e4c5d8552161da2b84ddcebed8ab37d0f7f266ab/ty-0.0.1a34-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:284c8cfd64f255d942ef21953e3d40d087c74dec27e16495bd656decdd208f59", size = 10518743, upload-time = "2025-12-12T18:28:54.944Z" }, - { url = "https://files.pythonhosted.org/packages/05/c5/30a6e377bcab7d5b65d5c78740635b23ecee647bf268c9dc82a91d41c9ba/ty-0.0.1a34-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c34b028305642fd3a9076d4b07d651a819c61a65371ef38cde60f0b54dce6180", size = 10285473, upload-time = "2025-12-12T18:29:08.432Z" }, - { url = "https://files.pythonhosted.org/packages/97/aa/d2cd564ee37a587c8311383a5687584c9aed241a9e67301ee0280301eef3/ty-0.0.1a34-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad997a21648dc64017f11a96b7bb44f088ab0fd589decadc2d686fc97b102f4e", size = 10298873, upload-time = "2025-12-12T18:29:12.38Z" }, - { url = "https://files.pythonhosted.org/packages/2e/80/c427dabd51b5d8b50fc375e18674c098877a9d6545af810ccff4e40ff74a/ty-0.0.1a34-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1afe9798f94c0fbb9e42ff003dfcb4df982f97763d93e5b1d53f9da865a53af", size = 9851399, upload-time = "2025-12-12T18:29:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/cc/d8/7240c0e13bc3405b190b4437fbc67c86aa70e349b282e5fa79282181532b/ty-0.0.1a34-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:bd335010aa211fbf8149d3507d6331bdb947d5328ca31388cecdbd2eb49275c3", size = 9261475, upload-time = "2025-12-12T18:29:04.638Z" }, - { url = "https://files.pythonhosted.org/packages/6b/a1/6538f8fe7a5b1a71b20461d905969b7f62574cf9c8c6af580b765a647289/ty-0.0.1a34-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:29ebcc56aabaf6aa85c3baf788e211455ffc9935b807ddc9693954b6990e9a3c", size = 9554878, upload-time = "2025-12-12T18:29:16.349Z" }, - { url = "https://files.pythonhosted.org/packages/3d/f2/b8ab163b928de329d88a5f04a5c399a40c1c099b827c70e569e539f9a755/ty-0.0.1a34-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0cbb5a68fddec83c39db6b5f0a5c5da5a3f7d7620e4bcb4ad5bf3a0c7f89ab45", size = 9651340, upload-time = "2025-12-12T18:29:19.92Z" }, - { url = "https://files.pythonhosted.org/packages/dc/1b/1e4e24b684ee5f22dda18d86846430b123fb2e985f0c0eb986e6eccec1b9/ty-0.0.1a34-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f9b3fd934982a9497237bf39fa472f6d201260ac95b3dc75ba9444d05ec01654", size = 9944488, upload-time = "2025-12-12T18:28:58.544Z" }, - { url = "https://files.pythonhosted.org/packages/80/b0/6435f1795f76c57598933624af58bf67385c96b8fa3252f5f9087173e21a/ty-0.0.1a34-py3-none-win32.whl", hash = "sha256:bdabc3f1a048bc2891d4184b818a7ee855c681dd011d00ee672a05bfe6451156", size = 9151401, upload-time = "2025-12-12T18:28:53.028Z" }, - { url = "https://files.pythonhosted.org/packages/73/2e/adce0d7c07f6de30c7f3c125744ec818c7f04b14622a739fe17d4d0bdb93/ty-0.0.1a34-py3-none-win_amd64.whl", hash = "sha256:a4caa2e58685d6801719becbd0504fe61e3ab94f2509e84759f755a0ca480ada", size = 10031079, upload-time = "2025-12-12T18:29:14.556Z" }, - { url = "https://files.pythonhosted.org/packages/23/0d/1f123c69ce121dcabf5449a456a9a37c3bbad396e9e7484514f1fe568f96/ty-0.0.1a34-py3-none-win_arm64.whl", hash = "sha256:dd02c22b538657b042d154fe2d5e250dfb20c862b32e6036a6ffce2fd1ebca9d", size = 9534879, upload-time = "2025-12-12T18:29:18.187Z" }, +version = "0.0.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/db/6299d478000f4f1c6f9bf2af749359381610ffc4cbe6713b66e436ecf6e7/ty-0.0.5.tar.gz", hash = "sha256:983da6330773ff71e2b249810a19c689f9a0372f6e21bbf7cde37839d05b4346", size = 4806218, upload-time = "2025-12-20T21:19:17.24Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/98/c1f61ba378b4191e641bb36c07b7fcc70ff844d61be7a4bf2fea7472b4a9/ty-0.0.5-py3-none-linux_armv6l.whl", hash = "sha256:1594cd9bb68015eb2f5a3c68a040860f3c9306dc6667d7a0e5f4df9967b460e2", size = 9785554, upload-time = "2025-12-20T21:19:05.024Z" }, + { url = "https://files.pythonhosted.org/packages/ab/f9/b37b77c03396bd779c1397dae4279b7ad79315e005b3412feed8812a4256/ty-0.0.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7c0140ba980233d28699d9ddfe8f43d0b3535d6a3bbff9935df625a78332a3cf", size = 9603995, upload-time = "2025-12-20T21:19:15.256Z" }, + { url = "https://files.pythonhosted.org/packages/7d/70/4e75c11903b0e986c0203040472627cb61d6a709e1797fb08cdf9d565743/ty-0.0.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:15de414712cde92048ae4b1a77c4dc22920bd23653fe42acaf73028bad88f6b9", size = 9145815, upload-time = "2025-12-20T21:19:36.481Z" }, + { url = "https://files.pythonhosted.org/packages/89/05/93983dfcf871a41dfe58e5511d28e6aa332a1f826cc67333f77ae41a2f8a/ty-0.0.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:438aa51ad6c5fae64191f8d58876266e26f9250cf09f6624b6af47a22fa88618", size = 9619849, upload-time = "2025-12-20T21:19:19.084Z" }, + { url = "https://files.pythonhosted.org/packages/82/b6/896ab3aad59f846823f202e94be6016fb3f72434d999d2ae9bd0f28b3af9/ty-0.0.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1b3d373fd96af1564380caf153600481c676f5002ee76ba8a7c3508cdff82ee0", size = 9606611, upload-time = "2025-12-20T21:19:24.583Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ae/098e33fc92330285ed843e2750127e896140c4ebd2d73df7732ea496f588/ty-0.0.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8453692503212ad316cf8b99efbe85a91e5f63769c43be5345e435a1b16cba5a", size = 10029523, upload-time = "2025-12-20T21:19:07.055Z" }, + { url = "https://files.pythonhosted.org/packages/04/5a/f4b4c33758b9295e9aca0de9645deca0f4addd21d38847228723a6e780fc/ty-0.0.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2e4c454139473abbd529767b0df7a795ed828f780aef8d0d4b144558c0dc4446", size = 10870892, upload-time = "2025-12-20T21:19:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/c3/c5/4e3e7e88389365aa1e631c99378711cf0c9d35a67478cb4720584314cf44/ty-0.0.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:426d4f3b82475b1ec75f3cc9ee5a667c8a4ae8441a09fcd8e823a53b706d00c7", size = 10599291, upload-time = "2025-12-20T21:19:26.557Z" }, + { url = "https://files.pythonhosted.org/packages/c1/5d/138f859ea87bd95e17b9818e386ae25a910e46521c41d516bf230ed83ffc/ty-0.0.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5710817b67c6b2e4c0224e4f319b7decdff550886e9020f6d46aa1ce8f89a609", size = 10413515, upload-time = "2025-12-20T21:19:11.094Z" }, + { url = "https://files.pythonhosted.org/packages/27/21/1cbcd0d3b1182172f099e88218137943e0970603492fb10c7c9342369d9a/ty-0.0.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23c55ef08882c7c5ced1ccb90b4eeefa97f690aea254f58ac0987896c590f76", size = 10144992, upload-time = "2025-12-20T21:19:13.225Z" }, + { url = "https://files.pythonhosted.org/packages/ad/30/fdac06a5470c09ad2659a0806497b71f338b395d59e92611f71b623d05a0/ty-0.0.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b9e4c1a28a23b14cf8f4f793f4da396939f16c30bfa7323477c8cc234e352ac4", size = 9606408, upload-time = "2025-12-20T21:19:09.212Z" }, + { url = "https://files.pythonhosted.org/packages/09/93/e99dcd7f53295192d03efd9cbcec089a916f49cad4935c0160ea9adbd53d/ty-0.0.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4e9ebb61529b9745af662e37c37a01ad743cdd2c95f0d1421705672874d806cd", size = 9630040, upload-time = "2025-12-20T21:19:38.165Z" }, + { url = "https://files.pythonhosted.org/packages/d7/f8/6d1e87186e4c35eb64f28000c1df8fd5f73167ce126c5e3dd21fd1204a23/ty-0.0.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5eb191a8e332f50f56dfe45391bdd7d43dd4ef6e60884710fd7ce84c5d8c1eb5", size = 9754016, upload-time = "2025-12-20T21:19:32.79Z" }, + { url = "https://files.pythonhosted.org/packages/28/e6/20f989342cb3115852dda404f1d89a10a3ce93f14f42b23f095a3d1a00c9/ty-0.0.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:92ed7451a1e82ee134a2c24ca43b74dd31e946dff2b08e5c34473e6b051de542", size = 10252877, upload-time = "2025-12-20T21:19:20.787Z" }, + { url = "https://files.pythonhosted.org/packages/57/9d/fc66fa557443233dfad9ae197ff3deb70ae0efcfb71d11b30ef62f5cdcc3/ty-0.0.5-py3-none-win32.whl", hash = "sha256:71f6707e4c1c010c158029a688a498220f28bb22fdb6707e5c20e09f11a5e4f2", size = 9212640, upload-time = "2025-12-20T21:19:30.817Z" }, + { url = "https://files.pythonhosted.org/packages/68/b6/05c35f6dea29122e54af0e9f8dfedd0a100c721affc8cc801ebe2bc2ed13/ty-0.0.5-py3-none-win_amd64.whl", hash = "sha256:2b8b754a0d7191e94acdf0c322747fec34371a4d0669f5b4e89549aef28814ae", size = 10034701, upload-time = "2025-12-20T21:19:28.311Z" }, + { url = "https://files.pythonhosted.org/packages/df/ca/4201ed5cb2af73912663d0c6ded927c28c28b3c921c9348aa8d2cfef4853/ty-0.0.5-py3-none-win_arm64.whl", hash = "sha256:83bea5a5296caac20d52b790ded2b830a7ff91c4ed9f36730fe1f393ceed6654", size = 9566474, upload-time = "2025-12-20T21:19:22.518Z" }, ] [[package]] From f094707943148e1c576730fd4d2038234b08cce7 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Sun, 21 Dec 2025 21:06:34 +0100 Subject: [PATCH 120/144] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20replace=20`generat?= =?UTF-8?q?e-stubs`=20script=20with=20new=20`nox`=20session?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- bindings/generate-stubs.sh | 62 -------------------------------------- noxfile.py | 46 ++++++++++++++++++++++++++++ pyproject.toml | 2 +- 3 files changed, 47 insertions(+), 63 deletions(-) delete mode 100755 bindings/generate-stubs.sh diff --git a/bindings/generate-stubs.sh b/bindings/generate-stubs.sh deleted file mode 100755 index 4d72b1887b..0000000000 --- a/bindings/generate-stubs.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/bin/bash -# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM -# Copyright (c) 2025 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -set -euo pipefail - -core_dir=./python/mqt/core -stub_files=( - "$core_dir/ir/__init__.pyi" - "$core_dir/ir/operations.pyi" - "$core_dir/ir/registers.pyi" - "$core_dir/ir/symbolic.pyi" - "$core_dir/dd.pyi" - "$core_dir/fomac.pyi" - "$core_dir/na/fomac.pyi" -) - -core_patterns=./bindings/core_patterns.txt - -# Check if all stub files exist -for stub_file in "${stub_files[@]}"; do - if [ ! -f "$stub_file" ]; then - echo "Error: $stub_file does not exist. Are you running this script from the root directory?" - exit 1 - fi -done - -if [ ! -f "$core_patterns" ]; then - echo "Error: $core_patterns does not exist. Are you running this script from the root directory?" - exit 1 -fi - -# Ensure that the most recent version of mqt.core is installed -uv sync - -# Remove the existing stub files -for stub_file in "${stub_files[@]}"; do - rm -f "$stub_file" -done - -# Generate new stub files -uv run --no-sync -m nanobind.stubgen -m mqt.core.ir -o "$core_dir/ir/__init__.pyi" -p "$core_patterns" -P -uv run --no-sync -m nanobind.stubgen -m mqt.core.ir.operations -o "$core_dir/ir/operations.pyi" -p "$core_patterns" -P -uv run --no-sync -m nanobind.stubgen -m mqt.core.ir.registers -o "$core_dir/ir/registers.pyi" -p "$core_patterns" -P -uv run --no-sync -m nanobind.stubgen -m mqt.core.ir.symbolic -o "$core_dir/ir/symbolic.pyi" -p "$core_patterns" -P -uv run --no-sync -m nanobind.stubgen -m mqt.core.dd -o "$core_dir/dd.pyi" -p "$core_patterns" -P -uv run --no-sync -m nanobind.stubgen -m mqt.core.fomac -o "$core_dir/fomac.pyi" -p "$core_patterns" -P -uv run --no-sync -m nanobind.stubgen -m mqt.core.na.fomac -o "$core_dir/na/fomac.pyi" -p "$core_patterns" -P - -set +e - -# Run prek on generated stub files -for stub_file in "${stub_files[@]}"; do - uvx prek license-tools --quiet --files "$stub_file" - uvx prek ruff-check --quiet --files "$stub_file" - uvx prek ruff-format --quiet --files "$stub_file" -done diff --git a/noxfile.py b/noxfile.py index 946ad48252..b254789353 100755 --- a/noxfile.py +++ b/noxfile.py @@ -207,5 +207,51 @@ def docs(session: nox.Session) -> None: ) +@nox.session(reuse_venv=True, venv_backend="uv") +def stubs(session: nox.Session) -> None: + """Generate type stubs for Python bindings using nanobind.""" + env = {"UV_PROJECT_ENVIRONMENT": session.virtualenv.location} + session.run( + "uv", + "sync", + env=env, + ) + + from nanobind.stubgen import main as nanobind_main # type: ignore[import-not-found] + + package_root = Path(__file__).parent / "python" / "mqt" / "core" + pattern_file = Path(__file__).parent / "bindings" / "core_patterns.txt" + + args = [ + "--recursive", + "--include-private", + "--output-dir", + str(package_root), + "--pattern-file", + str(pattern_file), + "--module", + "mqt.core.ir", + "--module", + "mqt.core.dd", + "--module", + "mqt.core.fomac", + "--module", + "mqt.core.na", + ] + + nanobind_main(args) + + pyi_files = list(package_root.glob("**/*.pyi")) + + if shutil.which("prek") is None: + session.install("prek") + + success_codes = [0, 1] + session.run("prek", "run", "license-tools", "--files", *pyi_files, external=True, success_codes=success_codes) + session.run("prek", "run", "ruff-check", "--files", *pyi_files, external=True, success_codes=success_codes) + session.run("prek", "run", "ruff-format", "--files", *pyi_files, external=True, success_codes=success_codes) + session.run("prek", "run", "ruff-check", "--files", *pyi_files, external=True) + + if __name__ == "__main__": nox.main() diff --git a/pyproject.toml b/pyproject.toml index 70fb0da577..13403e789a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -231,7 +231,7 @@ known-first-party = ["mqt.core"] [tool.ruff.lint.per-file-ignores] "test/python/**" = ["T20", "INP001"] "docs/**" = ["T20", "INP001"] -"noxfile.py" = ["T20", "TID251"] +"noxfile.py" = ["T20", "TID251", "PLC0415"] "*.pyi" = ["D418", "DOC202", "E501", "PYI011", "PYI021"] "*.ipynb" = [ "D", # pydocstyle From cf279ddde37ccccb702448f61afa13b0ed72953d Mon Sep 17 00:00:00 2001 From: burgholzer Date: Sun, 21 Dec 2025 22:09:55 +0100 Subject: [PATCH 121/144] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20upgrade=20reusable?= =?UTF-8?q?=20workflows=20to=20version=20v1.17.6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- .github/workflows/cd.yml | 4 ++-- .github/workflows/ci.yml | 30 +++++++++++++++--------------- .github/workflows/upstream.yml | 4 ++-- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index ebff833461..6a7cfbfa7b 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -12,7 +12,7 @@ permissions: jobs: build-sdist: name: 🐍 Packaging - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-sdist.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-sdist.yml@2acb39781fa6ca7baa10c2a31a508cb0e2292d9e # v1.17.6 # Builds wheels on all supported platforms using cibuildwheel. # The wheels are uploaded as GitHub artifacts `dev-cibw-*` or `cibw-*`, depending on whether @@ -31,7 +31,7 @@ jobs: windows-2022, windows-11-arm, ] - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-wheel-cibuildwheel.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-wheel-cibuildwheel.yml@2acb39781fa6ca7baa10c2a31a508cb0e2292d9e # v1.17.6 with: runs-on: ${{ matrix.runs-on }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 42d7fd6537..2dbc269405 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ concurrency: jobs: change-detection: name: 🔍 Change - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-change-detection.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-change-detection.yml@2acb39781fa6ca7baa10c2a31a508cb0e2292d9e # v1.17.6 cpp-tests-ubuntu: name: 🇨‌ Test 🐧 @@ -30,7 +30,7 @@ jobs: - runs-on: ubuntu-24.04 compiler: gcc config: Debug - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-ubuntu.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-ubuntu.yml@2acb39781fa6ca7baa10c2a31a508cb0e2292d9e # v1.17.6 with: runs-on: ${{ matrix.runs-on }} compiler: ${{ matrix.compiler }} @@ -50,7 +50,7 @@ jobs: - runs-on: macos-14 compiler: clang config: Debug - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-macos.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-macos.yml@2acb39781fa6ca7baa10c2a31a508cb0e2292d9e # v1.17.6 with: runs-on: ${{ matrix.runs-on }} compiler: ${{ matrix.compiler }} @@ -71,7 +71,7 @@ jobs: - runs-on: windows-2022 compiler: msvc config: Debug - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-windows.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-windows.yml@2acb39781fa6ca7baa10c2a31a508cb0e2292d9e # v1.17.6 with: runs-on: ${{ matrix.runs-on }} compiler: ${{ matrix.compiler }} @@ -88,7 +88,7 @@ jobs: runs-on: [ubuntu-24.04, ubuntu-24.04-arm] compiler: [gcc, clang, clang-20, clang-21] config: [Release, Debug] - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-ubuntu.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-ubuntu.yml@2acb39781fa6ca7baa10c2a31a508cb0e2292d9e # v1.17.6 with: runs-on: ${{ matrix.runs-on }} compiler: ${{ matrix.compiler }} @@ -105,7 +105,7 @@ jobs: runs-on: [macos-14, macos-15, macos-15-intel] compiler: [clang, clang-20, clang-21, gcc-14, gcc-15] config: [Release, Debug] - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-macos.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-macos.yml@2acb39781fa6ca7baa10c2a31a508cb0e2292d9e # v1.17.6 with: runs-on: ${{ matrix.runs-on }} compiler: ${{ matrix.compiler }} @@ -123,7 +123,7 @@ jobs: runs-on: [windows-2022, windows-2025, windows-11-arm] compiler: [msvc, clang] config: [Release] - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-windows.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-windows.yml@2acb39781fa6ca7baa10c2a31a508cb0e2292d9e # v1.17.6 with: runs-on: ${{ matrix.runs-on }} compiler: ${{ matrix.compiler }} @@ -133,7 +133,7 @@ jobs: name: 🇨‌ Coverage needs: change-detection if: fromJSON(needs.change-detection.outputs.run-cpp-tests) - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-coverage.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-coverage.yml@2acb39781fa6ca7baa10c2a31a508cb0e2292d9e # v1.17.6 permissions: contents: read id-token: write @@ -142,7 +142,7 @@ jobs: name: 🇨‌ Lint needs: change-detection if: fromJSON(needs.change-detection.outputs.run-cpp-linter) - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-linter.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-linter.yml@2acb39781fa6ca7baa10c2a31a508cb0e2292d9e # v1.17.6 with: cmake-args: -DBUILD_MQT_CORE_BENCHMARKS=ON -DBUILD_MQT_CORE_MLIR=ON -DBUILD_MQT_CORE_BINDINGS=ON clang-version: 21 @@ -168,7 +168,7 @@ jobs: macos-14, windows-2022, ] - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-tests.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-tests.yml@2acb39781fa6ca7baa10c2a31a508cb0e2292d9e # v1.17.6 with: runs-on: ${{ matrix.runs-on }} @@ -176,7 +176,7 @@ jobs: name: 🐍 Coverage needs: [change-detection, python-tests] if: fromJSON(needs.change-detection.outputs.run-python-tests) - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-coverage.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-coverage.yml@2acb39781fa6ca7baa10c2a31a508cb0e2292d9e # v1.17.6 permissions: contents: read id-token: write @@ -190,7 +190,7 @@ jobs: fail-fast: false matrix: runs-on: [macos-15, windows-2025] - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-tests.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-tests.yml@2acb39781fa6ca7baa10c2a31a508cb0e2292d9e # v1.17.6 with: runs-on: ${{ matrix.runs-on }} @@ -198,7 +198,7 @@ jobs: name: 🐍 Lint needs: change-detection if: fromJSON(needs.change-detection.outputs.run-python-tests) - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-linter.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-linter.yml@2acb39781fa6ca7baa10c2a31a508cb0e2292d9e # v1.17.6 with: check-stubs: true enable-ty: true @@ -207,7 +207,7 @@ jobs: name: 🚀 CD needs: change-detection if: fromJSON(needs.change-detection.outputs.run-cd) - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-sdist.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-sdist.yml@2acb39781fa6ca7baa10c2a31a508cb0e2292d9e # v1.17.6 build-wheel: name: 🚀 CD @@ -225,7 +225,7 @@ jobs: windows-2022, windows-11-arm, ] - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-wheel-cibuildwheel.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-wheel-cibuildwheel.yml@2acb39781fa6ca7baa10c2a31a508cb0e2292d9e # v1.17.6 with: runs-on: ${{ matrix.runs-on }} diff --git a/.github/workflows/upstream.yml b/.github/workflows/upstream.yml index 08f3e3798b..c4d3a6badc 100644 --- a/.github/workflows/upstream.yml +++ b/.github/workflows/upstream.yml @@ -19,7 +19,7 @@ jobs: fail-fast: false matrix: runs-on: [ubuntu-24.04, macos-14, windows-2022] - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-qiskit-upstream-tests.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-qiskit-upstream-tests.yml@2acb39781fa6ca7baa10c2a31a508cb0e2292d9e # v1.17.6 with: runs-on: ${{ matrix.runs-on }} setup-z3: true @@ -28,7 +28,7 @@ jobs: name: Create issue on failure needs: qiskit-upstream-tests if: ${{ always() }} - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-qiskit-upstream-issue.yml@654680ab77b3af8226a9f5cd9acfd157bd106f17 # v1.17.5 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-qiskit-upstream-issue.yml@2acb39781fa6ca7baa10c2a31a508cb0e2292d9e # v1.17.6 with: tests-result: ${{ needs.qiskit-upstream-tests.result }} permissions: From fca6ec23d6f4be8f43f3da41e4d4730bcc9fd134 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Sun, 21 Dec 2025 22:18:55 +0100 Subject: [PATCH 122/144] =?UTF-8?q?=F0=9F=9A=A8=20fix=20clang-tidy=20warni?= =?UTF-8?q?ng?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- bindings/na/register_fomac.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/bindings/na/register_fomac.cpp b/bindings/na/register_fomac.cpp index 7587945872..d6eb1d667e 100644 --- a/bindings/na/register_fomac.cpp +++ b/bindings/na/register_fomac.cpp @@ -34,6 +34,7 @@ template [[nodiscard]] auto repr(T c) -> std::string { } // namespace +// NOLINTNEXTLINE(misc-use-internal-linkage) void registerFomac(nb::module_& m) { nb::module_::import_("mqt.core.fomac"); From 24f40d2f0d6a45b7a73a79234a3915b5c074875b Mon Sep 17 00:00:00 2001 From: burgholzer Date: Sun, 21 Dec 2025 22:28:47 +0100 Subject: [PATCH 123/144] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refine=20stubs=20s?= =?UTF-8?q?ession?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- noxfile.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/noxfile.py b/noxfile.py index b254789353..694ffd3826 100755 --- a/noxfile.py +++ b/noxfile.py @@ -214,15 +214,19 @@ def stubs(session: nox.Session) -> None: session.run( "uv", "sync", + "--no-dev", + "--group", + "build", env=env, ) - from nanobind.stubgen import main as nanobind_main # type: ignore[import-not-found] - package_root = Path(__file__).parent / "python" / "mqt" / "core" pattern_file = Path(__file__).parent / "bindings" / "core_patterns.txt" - args = [ + session.run( + "python", + "-m", + "nanobind.stubgen", "--recursive", "--include-private", "--output-dir", @@ -237,19 +241,20 @@ def stubs(session: nox.Session) -> None: "mqt.core.fomac", "--module", "mqt.core.na", - ] - - nanobind_main(args) + ) pyi_files = list(package_root.glob("**/*.pyi")) if shutil.which("prek") is None: session.install("prek") + # Allow both 0 (no issues) and 1 as success codes for fixing up stubs. success_codes = [0, 1] session.run("prek", "run", "license-tools", "--files", *pyi_files, external=True, success_codes=success_codes) session.run("prek", "run", "ruff-check", "--files", *pyi_files, external=True, success_codes=success_codes) session.run("prek", "run", "ruff-format", "--files", *pyi_files, external=True, success_codes=success_codes) + + # Finally, run ruff-check again to ensure everything is clean. session.run("prek", "run", "ruff-check", "--files", *pyi_files, external=True) From 026ceaf72edbc0cf1a8a061b17d67590b88c5406 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Sun, 21 Dec 2025 22:37:17 +0100 Subject: [PATCH 124/144] =?UTF-8?q?=F0=9F=91=8C=20address=20code=20rabbit?= =?UTF-8?q?=20comments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- bindings/na/CMakeLists.txt | 2 +- bindings/na/register_fomac.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/bindings/na/CMakeLists.txt b/bindings/na/CMakeLists.txt index d81bedb2ea..a7b165059e 100644 --- a/bindings/na/CMakeLists.txt +++ b/bindings/na/CMakeLists.txt @@ -10,7 +10,7 @@ set(TARGET_NAME "${MQT_CORE_TARGET_NAME}-na-bindings") if(NOT TARGET ${TARGET_NAME}) # collect source files - file(GLOB_RECURSE SOURCES **.cpp) + file(GLOB_RECURSE SOURCES *.cpp) # declare the Python module add_mqt_python_binding_nanobind( diff --git a/bindings/na/register_fomac.cpp b/bindings/na/register_fomac.cpp index d6eb1d667e..864ad9a120 100644 --- a/bindings/na/register_fomac.cpp +++ b/bindings/na/register_fomac.cpp @@ -105,6 +105,7 @@ void registerFomac(nb::module_& m) { nb::sig("def __ne__(self, arg: object, /) -> bool")); device.def_prop_ro("traps", &na::Session::Device::getTraps, + nb::rv_policy::reference_internal, "The list of trap positions in the device."); device.def_prop_ro( "t1", @@ -135,7 +136,7 @@ void registerFomac(nb::module_& m) { device.def(nb::self != nb::self, nb::sig("def __ne__(self, arg: object, /) -> bool")); - m.def("devices", &na::Session::getDevices, + m.def("devices", &na::Session::getDevices, nb::rv_policy::reference_internal, "Returns a list of available devices."); } From 493e505cda3b81abee171858a9b5b87a88d825cb Mon Sep 17 00:00:00 2001 From: burgholzer Date: Sun, 21 Dec 2025 22:45:11 +0100 Subject: [PATCH 125/144] =?UTF-8?q?=F0=9F=9A=A8=20one=20less=20ruff=20rule?= =?UTF-8?q?=20exclusion=20for=20stubs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 13403e789a..c2dd48bf48 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -232,7 +232,7 @@ known-first-party = ["mqt.core"] "test/python/**" = ["T20", "INP001"] "docs/**" = ["T20", "INP001"] "noxfile.py" = ["T20", "TID251", "PLC0415"] -"*.pyi" = ["D418", "DOC202", "E501", "PYI011", "PYI021"] +"*.pyi" = ["D418", "E501", "PYI011", "PYI021"] "*.ipynb" = [ "D", # pydocstyle "E402", # Allow imports to appear anywhere in Jupyter notebooks From 5e3c0c348c08e343bafd5cd5f0dbaf7eb513f4e0 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Sun, 21 Dec 2025 22:46:31 +0100 Subject: [PATCH 126/144] =?UTF-8?q?=F0=9F=9A=A8=20one=20less=20ruff=20rule?= =?UTF-8?q?=20exclusion=20for=20stubs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- pyproject.toml | 2 +- python/mqt/core/ir/__init__.pyi | 16 ++++++++-------- python/mqt/core/ir/operations.pyi | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c2dd48bf48..77eb7f466e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -232,7 +232,7 @@ known-first-party = ["mqt.core"] "test/python/**" = ["T20", "INP001"] "docs/**" = ["T20", "INP001"] "noxfile.py" = ["T20", "TID251", "PLC0415"] -"*.pyi" = ["D418", "E501", "PYI011", "PYI021"] +"*.pyi" = ["D418", "E501", "PYI021"] "*.ipynb" = [ "D", # pydocstyle "E402", # Allow imports to appear anywhere in Jupyter notebooks diff --git a/python/mqt/core/ir/__init__.pyi b/python/mqt/core/ir/__init__.pyi index bf810aa506..06c326ffba 100644 --- a/python/mqt/core/ir/__init__.pyi +++ b/python/mqt/core/ir/__init__.pyi @@ -1903,7 +1903,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): else_operation: operations.Operation, control_register: registers.ClassicalRegister, expected_value: int = 1, - comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, + comparison_kind: operations.ComparisonKind = ..., ) -> None: """Add an if-else operation to the circuit. @@ -1922,7 +1922,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): else_operation: operations.Operation, control_bit: int, expected_value: int = 1, - comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, + comparison_kind: operations.ComparisonKind = ..., ) -> None: """Add an if-else operation to the circuit. @@ -1941,7 +1941,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): target: int, control_register: registers.ClassicalRegister, expected_value: int = 1, - comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, + comparison_kind: operations.ComparisonKind = ..., params: Sequence[float] = ..., ) -> None: """Add an if operation to the circuit. @@ -1963,7 +1963,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): control: operations.Control, control_register: registers.ClassicalRegister, expected_value: int = 1, - comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, + comparison_kind: operations.ComparisonKind = ..., params: Sequence[float] = ..., ) -> None: """Add an if operation to the circuit. @@ -1986,7 +1986,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): controls: AbstractSet[operations.Control], control_register: registers.ClassicalRegister, expected_value: int = 1, - comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, + comparison_kind: operations.ComparisonKind = ..., params: Sequence[float] = ..., ) -> None: """Add an if operation to the circuit. @@ -2008,7 +2008,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): target: int, control_bit: int, expected_value: bool = True, - comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, + comparison_kind: operations.ComparisonKind = ..., params: Sequence[float] = ..., ) -> None: """Add an if operation to the circuit. @@ -2030,7 +2030,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): control: operations.Control, control_bit: int, expected_value: bool = True, - comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, + comparison_kind: operations.ComparisonKind = ..., params: Sequence[float] = ..., ) -> None: """Add an if operation to the circuit. @@ -2053,7 +2053,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): controls: AbstractSet[operations.Control], control_bit: int, expected_value: bool = True, - comparison_kind: operations.ComparisonKind = operations.ComparisonKind.eq, + comparison_kind: operations.ComparisonKind = ..., params: Sequence[float] = ..., ) -> None: """Add an if operation to the circuit. diff --git a/python/mqt/core/ir/operations.pyi b/python/mqt/core/ir/operations.pyi index 0a018ab75b..8701363a7d 100644 --- a/python/mqt/core/ir/operations.pyi +++ b/python/mqt/core/ir/operations.pyi @@ -683,7 +683,7 @@ class NonUnitaryOperation(Operation): @overload def __init__(self, target: int, classic: int) -> None: ... @overload - def __init__(self, targets: Sequence[int], op_type: OpType = OpType.reset) -> None: ... + def __init__(self, targets: Sequence[int], op_type: OpType = ...) -> None: ... @property def classics(self) -> list[int]: """The classical bits that are associated with the operation.""" @@ -833,7 +833,7 @@ class IfElseOperation(Operation): else_operation: Operation | None, control_register: mqt.core.ir.registers.ClassicalRegister, expected_value: int = 1, - comparison_kind: ComparisonKind = ComparisonKind.eq, + comparison_kind: ComparisonKind = ..., ) -> None: ... @overload def __init__( @@ -842,7 +842,7 @@ class IfElseOperation(Operation): else_operation: Operation | None, control_bit: int, expected_value: bool = True, - comparison_kind: ComparisonKind = ComparisonKind.eq, + comparison_kind: ComparisonKind = ..., ) -> None: ... @property def then_operation(self) -> Operation: From 10b3a941b827a0576ac70376a2ba5ebc169bab8d Mon Sep 17 00:00:00 2001 From: burgholzer Date: Sun, 21 Dec 2025 23:16:11 +0100 Subject: [PATCH 127/144] =?UTF-8?q?=F0=9F=9A=A8=20disable=20`*-prefer-stat?= =?UTF-8?q?ic-over-anonymous-namespace`=20rule?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- .clang-tidy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.clang-tidy b/.clang-tidy index 5e602655a6..8683b18a06 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -30,7 +30,8 @@ Checks: | readability-*, -readability-identifier-length, -readability-magic-numbers, - -readability-function-cognitive-complexity + -readability-function-cognitive-complexity, + -*-prefer-static-over-anonymous-namespace CheckOptions: - key: readability-identifier-naming.ClassCase From acb32256d4f8ca23430bdf69857d8c8fd45c7810 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Sun, 21 Dec 2025 23:16:26 +0100 Subject: [PATCH 128/144] =?UTF-8?q?=F0=9F=9A=A8=20fix=20compiler=20warning?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- test/qir/runtime/test_qir_runtime.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/qir/runtime/test_qir_runtime.cpp b/test/qir/runtime/test_qir_runtime.cpp index 34153deca3..ace8a2203f 100644 --- a/test/qir/runtime/test_qir_runtime.cpp +++ b/test/qir/runtime/test_qir_runtime.cpp @@ -508,9 +508,9 @@ INSTANTIATE_TEST_SUITE_P( QIRFilesTest, //< Test suite name // Parameters to test with ::testing::Values(TEST_EXECUTABLES), - [](const testing::TestParamInfo& info) { + [](const testing::TestParamInfo& inf) { // Extract the last part of the file path - auto filename = info.param.stem().string(); + auto filename = inf.param.stem().string(); // replace all '-' with '_' std::ranges::replace(filename, '-', '_'); return filename; From c9e22b5c012ca2acc690e504e02ac9d0cbea3637 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Sun, 21 Dec 2025 23:17:01 +0100 Subject: [PATCH 129/144] =?UTF-8?q?=F0=9F=9A=A8=20fix=20compiler=20warning?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- .../ir/operations/IfElseOperation.hpp | 46 +++--- src/ir/operations/IfElseOperation.cpp | 150 +++++++++--------- src/qdmi/dd/Device.cpp | 4 +- src/qir/runtime/Runtime.cpp | 2 +- 4 files changed, 100 insertions(+), 102 deletions(-) diff --git a/include/mqt-core/ir/operations/IfElseOperation.hpp b/include/mqt-core/ir/operations/IfElseOperation.hpp index 225db1e2b0..761b2150ae 100644 --- a/include/mqt-core/ir/operations/IfElseOperation.hpp +++ b/include/mqt-core/ir/operations/IfElseOperation.hpp @@ -66,28 +66,28 @@ class IfElseOperation final : public Operation { [[nodiscard]] bool isControlled() const override { return false; } - [[nodiscard]] auto getThenOp() const { return thenOp.get(); } + [[nodiscard]] auto getThenOp() const { return thenOp_.get(); } - [[nodiscard]] auto getElseOp() const { return elseOp.get(); } + [[nodiscard]] auto getElseOp() const { return elseOp_.get(); } [[nodiscard]] const auto& getControlRegister() const noexcept { - return controlRegister; + return controlRegister_; } [[nodiscard]] const auto& getControlBit() const noexcept { - return controlBit; + return controlBit_; } [[nodiscard]] auto getExpectedValueRegister() const noexcept { - return expectedValueRegister; + return expectedValueRegister_; } [[nodiscard]] bool getExpectedValueBit() const noexcept { - return expectedValueBit; + return expectedValueBit_; } [[nodiscard]] auto getComparisonKind() const noexcept { - return comparisonKind; + return comparisonKind_; } [[nodiscard]] bool equals(const Operation& op, const Permutation& perm1, @@ -109,27 +109,27 @@ class IfElseOperation final : public Operation { } // Override invalid Operation setters - void setTargets(const Targets&) override { + void setTargets(const Targets& /*t*/) override { throw std::runtime_error("An IfElseOperation does not have a target."); } - void setControls(const Controls&) override { + void setControls(const Controls& /*c*/) override { throw std::runtime_error("An IfElseOperation cannot be controlled."); } - void addControl(Control) override { + void addControl(Control /*c*/) override { throw std::runtime_error("An IfElseOperation cannot be controlled."); } void clearControls() override { throw std::runtime_error("An IfElseOperation cannot be controlled."); } - void removeControl(Control) override { + void removeControl(Control /*c*/) override { throw std::runtime_error("An IfElseOperation cannot be controlled."); } - Controls::iterator removeControl(Controls::iterator) override { + Controls::iterator removeControl(Controls::iterator /*it*/) override { throw std::runtime_error("An IfElseOperation cannot be controlled."); } - void setGate(const OpType) override { + void setGate(const OpType /*g*/) override { throw std::runtime_error( "Cannot set operation type of an IfElseOperation."); } @@ -139,13 +139,13 @@ class IfElseOperation final : public Operation { } private: - std::unique_ptr thenOp; - std::unique_ptr elseOp; - std::optional controlRegister; - std::optional controlBit; - std::uint64_t expectedValueRegister = 1U; - bool expectedValueBit = true; - ComparisonKind comparisonKind = Eq; + std::unique_ptr thenOp_; + std::unique_ptr elseOp_; + std::optional controlRegister_; + std::optional controlBit_; + std::uint64_t expectedValueRegister_ = 1U; + bool expectedValueBit_ = true; + ComparisonKind comparisonKind_ = Eq; /** * @brief Canonicalizes the IfElseOperation @@ -162,8 +162,6 @@ class IfElseOperation final : public Operation { }; } // namespace qc -namespace std { -template <> struct hash { +template <> struct std::hash { std::size_t operator()(qc::IfElseOperation const& op) const noexcept; -}; -} // namespace std +}; // namespace std diff --git a/src/ir/operations/IfElseOperation.cpp b/src/ir/operations/IfElseOperation.cpp index 4bd26c9019..539ca0181a 100644 --- a/src/ir/operations/IfElseOperation.cpp +++ b/src/ir/operations/IfElseOperation.cpp @@ -75,9 +75,9 @@ IfElseOperation::IfElseOperation(std::unique_ptr&& thenOp, const ClassicalRegister& controlRegister, const std::uint64_t expectedValue, const ComparisonKind kind) - : thenOp(std::move(thenOp)), elseOp(std::move(elseOp)), - controlRegister(controlRegister), expectedValueRegister(expectedValue), - comparisonKind(kind) { + : thenOp_(std::move(thenOp)), elseOp_(std::move(elseOp)), + controlRegister_(controlRegister), expectedValueRegister_(expectedValue), + comparisonKind_(kind) { name = "if_else"; type = IfElse; canonicalize(); @@ -87,42 +87,42 @@ IfElseOperation::IfElseOperation(std::unique_ptr&& thenOp, std::unique_ptr&& elseOp, const Bit controlBit, const bool expectedValue, const ComparisonKind kind) - : thenOp(std::move(thenOp)), elseOp(std::move(elseOp)), - controlBit(controlBit), expectedValueBit(expectedValue), - comparisonKind(kind) { + : thenOp_(std::move(thenOp)), elseOp_(std::move(elseOp)), + controlBit_(controlBit), expectedValueBit_(expectedValue), + comparisonKind_(kind) { name = "if_else"; type = IfElse; canonicalize(); } IfElseOperation::IfElseOperation(const IfElseOperation& op) - : Operation(op), thenOp(op.thenOp ? op.thenOp->clone() : nullptr), - elseOp(op.elseOp ? op.elseOp->clone() : nullptr), - controlRegister(op.controlRegister), controlBit(op.controlBit), - expectedValueRegister(op.expectedValueRegister), - expectedValueBit(op.expectedValueBit), comparisonKind(op.comparisonKind) { -} + : Operation(op), thenOp_(op.thenOp_ ? op.thenOp_->clone() : nullptr), + elseOp_(op.elseOp_ ? op.elseOp_->clone() : nullptr), + controlRegister_(op.controlRegister_), controlBit_(op.controlBit_), + expectedValueRegister_(op.expectedValueRegister_), + expectedValueBit_(op.expectedValueBit_), + comparisonKind_(op.comparisonKind_) {} IfElseOperation& IfElseOperation::operator=(const IfElseOperation& op) { if (this != &op) { Operation::operator=(op); - thenOp = op.thenOp ? op.thenOp->clone() : nullptr; - elseOp = op.elseOp ? op.elseOp->clone() : nullptr; - controlRegister = op.controlRegister; - controlBit = op.controlBit; - expectedValueRegister = op.expectedValueRegister; - expectedValueBit = op.expectedValueBit; - comparisonKind = op.comparisonKind; + thenOp_ = op.thenOp_ ? op.thenOp_->clone() : nullptr; + elseOp_ = op.elseOp_ ? op.elseOp_->clone() : nullptr; + controlRegister_ = op.controlRegister_; + controlBit_ = op.controlBit_; + expectedValueRegister_ = op.expectedValueRegister_; + expectedValueBit_ = op.expectedValueBit_; + comparisonKind_ = op.comparisonKind_; } return *this; } void IfElseOperation::apply(const Permutation& permutation) { - if (thenOp) { - thenOp->apply(permutation); + if (thenOp_) { + thenOp_->apply(permutation); } - if (elseOp) { - elseOp->apply(permutation); + if (elseOp_) { + elseOp_->apply(permutation); } } @@ -130,33 +130,33 @@ bool IfElseOperation::equals(const Operation& operation, const Permutation& perm1, const Permutation& perm2) const { if (const auto* other = dynamic_cast(&operation)) { - if (controlRegister != other->controlRegister) { + if (controlRegister_ != other->controlRegister_) { return false; } - if (controlBit != other->controlBit) { + if (controlBit_ != other->controlBit_) { return false; } - if (expectedValueRegister != other->expectedValueRegister) { + if (expectedValueRegister_ != other->expectedValueRegister_) { return false; } - if (expectedValueBit != other->expectedValueBit) { + if (expectedValueBit_ != other->expectedValueBit_) { return false; } - if (comparisonKind != other->comparisonKind) { + if (comparisonKind_ != other->comparisonKind_) { return false; } - if (thenOp && other->thenOp) { - if (!thenOp->equals(*other->thenOp, perm1, perm2)) { + if (thenOp_ && other->thenOp_) { + if (!thenOp_->equals(*other->thenOp_, perm1, perm2)) { return false; } - } else if (thenOp || other->thenOp) { + } else if (thenOp_ || other->thenOp_) { return false; } - if (elseOp && other->elseOp) { - if (!elseOp->equals(*other->elseOp, perm1, perm2)) { + if (elseOp_ && other->elseOp_) { + if (!elseOp_->equals(*other->elseOp_, perm1, perm2)) { return false; } - } else if (elseOp || other->elseOp) { + } else if (elseOp_ || other->elseOp_) { return false; } return true; @@ -172,27 +172,27 @@ IfElseOperation::print(std::ostream& os, const Permutation& permutation, // print condition header line os << indent << "\033[1m\033[35m" << "if ("; - if (controlRegister.has_value()) { - assert(!controlBit.has_value()); - os << controlRegister->getName() << ' ' << comparisonKind << ' ' - << expectedValueRegister; - } else if (controlBit.has_value()) { - assert(!controlRegister.has_value()); - os << (!expectedValueBit ? "!" : "") << "c[" << controlBit.value() << "]"; + if (controlRegister_.has_value()) { + assert(!controlBit_.has_value()); + os << controlRegister_->getName() << ' ' << comparisonKind_ << ' ' + << expectedValueRegister_; + } else if (controlBit_.has_value()) { + assert(!controlRegister_.has_value()); + os << (!expectedValueBit_ ? "!" : "") << "c[" << controlBit_.value() << "]"; } os << ") {\033[0m" << '\n'; // cyan brace // then-block - if (thenOp) { + if (thenOp_) { os << indent; - thenOp->print(os, permutation, prefixWidth, nqubits); + thenOp_->print(os, permutation, prefixWidth, nqubits); } os << '\n'; // else-block (only if present) - if (elseOp) { + if (elseOp_) { os << indent << " \033[1m\033[35m} else {\033[0m" << '\n' << indent; - elseOp->print(os, permutation, prefixWidth, nqubits); + elseOp_->print(os, permutation, prefixWidth, nqubits); os << '\n'; } @@ -209,41 +209,41 @@ void IfElseOperation::dumpOpenQASM(std::ostream& of, const bool openQASM3) const { of << std::string(indent * OUTPUT_INDENT_SIZE, ' '); of << "if ("; - if (controlRegister.has_value()) { - assert(!controlBit.has_value()); - of << controlRegister->getName() << ' ' << comparisonKind << ' ' - << expectedValueRegister; - } else if (controlBit.has_value()) { - of << (!expectedValueBit ? "!" : "") << bitMap.at(*controlBit).second; + if (controlRegister_.has_value()) { + assert(!controlBit_.has_value()); + of << controlRegister_->getName() << ' ' << comparisonKind_ << ' ' + << expectedValueRegister_; + } else if (controlBit_.has_value()) { + of << (!expectedValueBit_ ? "!" : "") << bitMap.at(*controlBit_).second; } of << ") "; of << "{\n"; - if (thenOp) { - thenOp->dumpOpenQASM(of, qubitMap, bitMap, indent + 1, openQASM3); + if (thenOp_) { + thenOp_->dumpOpenQASM(of, qubitMap, bitMap, indent + 1, openQASM3); } - if (!elseOp) { + if (!elseOp_) { of << "}\n"; return; } of << "}"; if (openQASM3) { of << " else {\n"; - elseOp->dumpOpenQASM(of, qubitMap, bitMap, indent + 1, openQASM3); + elseOp_->dumpOpenQASM(of, qubitMap, bitMap, indent + 1, openQASM3); } else { of << '\n' << "if ("; - if (controlRegister.has_value()) { - assert(!controlBit.has_value()); - of << controlRegister->getName() << ' ' - << getInvertedComparisonKind(comparisonKind) << ' ' - << expectedValueRegister; + if (controlRegister_.has_value()) { + assert(!controlBit_.has_value()); + of << controlRegister_->getName() << ' ' + << getInvertedComparisonKind(comparisonKind_) << ' ' + << expectedValueRegister_; } - if (controlBit.has_value()) { - assert(!controlRegister.has_value()); - of << (expectedValueBit ? "!" : "") << bitMap.at(*controlBit).second; + if (controlBit_.has_value()) { + assert(!controlRegister_.has_value()); + of << (expectedValueBit_ ? "!" : "") << bitMap.at(*controlBit_).second; } of << ") "; of << "{\n"; - elseOp->dumpOpenQASM(of, qubitMap, bitMap, indent + 1, openQASM3); + elseOp_->dumpOpenQASM(of, qubitMap, bitMap, indent + 1, openQASM3); } of << "}\n"; } @@ -266,27 +266,27 @@ void IfElseOperation::dumpOpenQASM(std::ostream& of, */ void IfElseOperation::canonicalize() { // If thenOp is null, swap thenOp and elseOp, and invert the comparison kind. - if (thenOp == nullptr) { - std::swap(thenOp, elseOp); - comparisonKind = getInvertedComparisonKind(comparisonKind); + if (thenOp_ == nullptr) { + std::swap(thenOp_, elseOp_); + comparisonKind_ = getInvertedComparisonKind(comparisonKind_); } // If control is a single bit, only equality comparisons are supported. - if (controlBit.has_value()) { + if (controlBit_.has_value()) { // Convert Neq to Eq by inverting expectedValueBit. - if (comparisonKind == Neq) { - comparisonKind = Eq; - expectedValueBit = !expectedValueBit; + if (comparisonKind_ == Neq) { + comparisonKind_ = Eq; + expectedValueBit_ = !expectedValueBit_; } // Throw if comparison is not Eq (after possible conversion above). - if (comparisonKind != Eq) { + if (comparisonKind_ != Eq) { throw std::invalid_argument( "Inequality comparisons on a single bit are not supported."); } // If expectedValueBit is false and elseOp exists, swap thenOp and elseOp, // and set expectedValueBit to true. - if (!expectedValueBit && elseOp != nullptr) { - std::swap(thenOp, elseOp); - expectedValueBit = true; + if (!expectedValueBit_ && elseOp_ != nullptr) { + std::swap(thenOp_, elseOp_); + expectedValueBit_ = true; } } } diff --git a/src/qdmi/dd/Device.cpp b/src/qdmi/dd/Device.cpp index 09a165d75e..e771db7477 100644 --- a/src/qdmi/dd/Device.cpp +++ b/src/qdmi/dd/Device.cpp @@ -299,8 +299,8 @@ auto MQT_DDSIM_QDMI_Device_Session_impl_d::querySiteProperty( IS_INVALID_ARGUMENT(prop, QDMI_SITE_PROPERTY)) { return QDMI_ERROR_INVALIDARGUMENT; } - const auto id = - static_cast(reinterpret_cast(site) - OFFSET); + const auto id = reinterpret_cast(site) - OFFSET; + static_assert(sizeof(uintptr_t) == sizeof(size_t)); ADD_SINGLE_VALUE_PROPERTY(QDMI_SITE_PROPERTY_INDEX, size_t, id, prop, size, value, sizeRet) return QDMI_ERROR_NOTSUPPORTED; diff --git a/src/qir/runtime/Runtime.cpp b/src/qir/runtime/Runtime.cpp index de27e86d05..ea9eebabb8 100644 --- a/src/qir/runtime/Runtime.cpp +++ b/src/qir/runtime/Runtime.cpp @@ -90,7 +90,7 @@ auto Runtime::enlargeState(const std::uint64_t maxQubit) -> void { static_cast::difference_type>( numQubitsInQState), qubitPermutation.end(), numQubitsInQState); - numQubitsInQState += d; + numQubitsInQState += static_cast(d); // resize the DD package only if necessary if (dd->qubits() < numQubitsInQState) { From 74dfb25e394193a28c0382c1197a83cb1277027c Mon Sep 17 00:00:00 2001 From: burgholzer Date: Sun, 21 Dec 2025 23:17:43 +0100 Subject: [PATCH 130/144] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20simplify=20functio?= =?UTF-8?q?n=20calls=20and=20enforce=20matrix=20shape=20annotations=20in?= =?UTF-8?q?=20DD=20bindings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- bindings/dd/register_dd.cpp | 16 ++--- bindings/dd/register_dd_package.cpp | 101 ++++++++++++---------------- bindings/dd/register_matrix_dds.cpp | 6 +- bindings/dd/register_vector_dds.cpp | 6 +- python/mqt/core/dd.pyi | 14 ++-- 5 files changed, 64 insertions(+), 79 deletions(-) diff --git a/bindings/dd/register_dd.cpp b/bindings/dd/register_dd.cpp index f9ce5bd43c..ca24d6312d 100644 --- a/bindings/dd/register_dd.cpp +++ b/bindings/dd/register_dd.cpp @@ -83,9 +83,9 @@ It supports mid-circuit measurements, resets, and classical control. m.def( "simulate_statevector", [](const qc::QuantumComputation& qc) { - auto dd = std::make_unique(qc.getNqubits()); - auto in = makeZeroState(qc.getNqubits(), *dd); - const auto sim = dd::simulate(qc, in, *dd); + const auto dd = std::make_unique(qc.getNqubits()); + const auto in = makeZeroState(qc.getNqubits(), *dd); + const auto sim = simulate(qc, in, *dd); return getVector(sim); }, "qc"_a, @@ -111,9 +111,9 @@ Consider using the :func:`~mqt.core.dd.simulate` or the :func:`~mqt.core.dd.samp m.def( "build_unitary", [](const qc::QuantumComputation& qc, const bool recursive = false) { - auto dd = std::make_unique(qc.getNqubits()); - auto u = recursive ? dd::buildFunctionalityRecursive(qc, *dd) - : dd::buildFunctionality(qc, *dd); + const auto dd = std::make_unique(qc.getNqubits()); + const auto u = recursive ? buildFunctionalityRecursive(qc, *dd) + : buildFunctionality(qc, *dd); return getMatrix(u, qc.getNqubits()); }, "qc"_a, "recursive"_a = false, @@ -163,9 +163,9 @@ The simulation is effectively computed by sequentially applying the operations o [](const qc::QuantumComputation& qc, dd::Package& p, const bool recursive = false) { if (recursive) { - return dd::buildFunctionalityRecursive(qc, p); + return buildFunctionalityRecursive(qc, p); } - return dd::buildFunctionality(qc, p); + return buildFunctionality(qc, p); }, "qc"_a, "dd_package"_a, "recursive"_a = false, R"pb(Build a functional representation of a quantum computation. diff --git a/bindings/dd/register_dd_package.cpp b/bindings/dd/register_dd_package.cpp index 8a99fd737f..c6ec926510 100644 --- a/bindings/dd/register_dd_package.cpp +++ b/bindings/dd/register_dd_package.cpp @@ -49,6 +49,10 @@ using namespace nb::literals; using Vector = nb::ndarray, nb::ndim<1>>; using Matrix = nb::ndarray, nb::ndim<2>>; +using SingleQubitMatrix = + nb::ndarray, nb::shape<2, 2>>; +using TwoQubitMatrix = + nb::ndarray, nb::shape<4, 4>>; using Control = std::variant; using Controls = std::set; @@ -177,7 +181,7 @@ Specifically, it dd.def( "zero_state", [](dd::Package& p, const size_t numQubits) { - return dd::makeZeroState(numQubits, p); + return makeZeroState(numQubits, p); }, "num_qubits"_a, // keep the DD package alive while the returned vector DD is alive. @@ -196,7 +200,7 @@ Specifically, it "computational_basis_state", [](dd::Package& p, const size_t numQubits, const std::vector& state) { - return dd::makeBasisState(numQubits, state, p); + return makeBasisState(numQubits, state, p); }, "num_qubits"_a, "state"_a, // keep the DD package alive while the returned vector DD is alive. @@ -235,7 +239,7 @@ Specifically, it "basis_state", [](dd::Package& p, const size_t numQubits, const std::vector& state) { - return dd::makeBasisState(numQubits, state, p); + return makeBasisState(numQubits, state, p); }, "num_qubits"_a, "state"_a, // keep the DD package alive while the returned vector DD is alive. @@ -255,7 +259,7 @@ Specifically, it dd.def( "ghz_state", [](dd::Package& p, const size_t numQubits) { - return dd::makeGHZState(numQubits, p); + return makeGHZState(numQubits, p); }, "num_qubits"_a, // keep the DD package alive while the returned vector DD is alive. @@ -273,7 +277,7 @@ Specifically, it dd.def( "w_state", [](dd::Package& p, const size_t numQubits) { - return dd::makeWState(numQubits, p); + return makeWState(numQubits, p); }, "num_qubits"_a, // keep the DD package alive while the returned vector DD is alive. @@ -309,7 +313,7 @@ Specifically, it } const auto level = static_cast(std::log2(length) - 1); const auto state = makeDDFromVector(p, v, 0, length, level); - const dd::vEdge e{state.p, p.cn.lookup(state.w)}; + const dd::vEdge e{.p = state.p, .w = p.cn.lookup(state.w)}; p.incRef(e); return e; }, @@ -330,7 +334,7 @@ Specifically, it "apply_unitary_operation", [](dd::Package& p, const dd::vEdge& v, const qc::Operation& op, const qc::Permutation& perm = {}) { - return dd::applyUnitaryOperation(op, v, p, perm); + return applyUnitaryOperation(op, v, p, perm); }, "vec"_a, "operation"_a, "permutation"_a = qc::Permutation{}, // keep the DD package alive while the returned vector DD is alive. @@ -356,7 +360,7 @@ Specifically, it static std::mt19937_64 rng(std::random_device{}()); auto measurementsCopy = measurements; return std::pair{ - dd::applyMeasurement(op, v, p, rng, measurementsCopy, perm), + applyMeasurement(op, v, p, rng, measurementsCopy, perm), measurementsCopy}; }, "vec"_a, "operation"_a, "measurements"_a, @@ -382,7 +386,7 @@ Specifically, it [](dd::Package& p, const dd::vEdge& v, const qc::NonUnitaryOperation& op, const qc::Permutation& perm = {}) { static std::mt19937_64 rng(std::random_device{}()); - return dd::applyReset(op, v, p, rng, perm); + return applyReset(op, v, p, rng, perm); }, "vec"_a, "operation"_a, "permutation"_a = qc::Permutation{}, // keep the DD package alive while the returned vector DD is alive. @@ -405,7 +409,7 @@ Specifically, it [](dd::Package& p, const dd::vEdge& v, const qc::IfElseOperation& op, const std::vector& measurements, const qc::Permutation& perm = {}) { - return dd::applyIfElseOperation(op, v, p, measurements, perm); + return applyIfElseOperation(op, v, p, measurements, perm); }, "vec"_a, "operation"_a, "measurements"_a, "permutation"_a = qc::Permutation{}, @@ -472,11 +476,9 @@ Specifically, it dd.def( "single_qubit_gate", - [](dd::Package& p, const Matrix& m, const dd::Qubit target) { - if (m.ndim() != 2 || m.shape(0) != 2 || m.shape(1) != 2) { - throw std::invalid_argument("Matrix must be 2x2."); - } - return p.makeGateDD({m(0, 0), m(0, 1), m(1, 0), m(1, 1)}, target); + [](dd::Package& p, const SingleQubitMatrix& mat, const dd::Qubit target) { + return p.makeGateDD({mat(0, 0), mat(0, 1), mat(1, 0), mat(1, 1)}, + target); }, "matrix"_a, "target"_a, // keep the DD package alive while the returned matrix DD is alive. @@ -491,12 +493,9 @@ Specifically, it dd.def( "controlled_single_qubit_gate", - [](dd::Package& p, const Matrix& m, const Control& control, + [](dd::Package& p, const SingleQubitMatrix& mat, const Control& control, const dd::Qubit target) { - if (m.ndim() != 2 || m.shape(0) != 2 || m.shape(1) != 2) { - throw std::invalid_argument("Matrix must be 2x2."); - } - return p.makeGateDD({m(0, 0), m(0, 1), m(1, 0), m(1, 1)}, + return p.makeGateDD({mat(0, 0), mat(0, 1), mat(1, 0), mat(1, 1)}, getControl(control), target); }, "matrix"_a, "control"_a, "target"_a, @@ -514,12 +513,9 @@ Specifically, it dd.def( "multi_controlled_single_qubit_gate", - [](dd::Package& p, const Matrix& m, const Controls& controls, + [](dd::Package& p, const SingleQubitMatrix& mat, const Controls& controls, const dd::Qubit target) { - if (m.ndim() != 2 || m.shape(0) != 2 || m.shape(1) != 2) { - throw std::invalid_argument("Matrix must be 2x2."); - } - return p.makeGateDD({m(0, 0), m(0, 1), m(1, 0), m(1, 1)}, + return p.makeGateDD({mat(0, 0), mat(0, 1), mat(1, 0), mat(1, 1)}, getControls(controls), target); }, "matrix"_a, "controls"_a, "target"_a, @@ -537,16 +533,13 @@ Specifically, it dd.def( "two_qubit_gate", - [](dd::Package& p, const Matrix& m, const dd::Qubit target0, + [](dd::Package& p, const TwoQubitMatrix& mat, const dd::Qubit target0, const dd::Qubit target1) { - if (m.ndim() != 2 || m.shape(0) != 4 || m.shape(1) != 4) { - throw std::invalid_argument("Matrix must be 4x4."); - } return p.makeTwoQubitGateDD( - {std::array{m(0, 0), m(0, 1), m(0, 2), m(0, 3)}, - {m(1, 0), m(1, 1), m(1, 2), m(1, 3)}, - {m(2, 0), m(2, 1), m(2, 2), m(2, 3)}, - {m(3, 0), m(3, 1), m(3, 2), m(3, 3)}}, + {std::array{mat(0, 0), mat(0, 1), mat(0, 2), mat(0, 3)}, + {mat(1, 0), mat(1, 1), mat(1, 2), mat(1, 3)}, + {mat(2, 0), mat(2, 1), mat(2, 2), mat(2, 3)}, + {mat(3, 0), mat(3, 1), mat(3, 2), mat(3, 3)}}, target0, target1); }, "matrix"_a, "target0"_a, "target1"_a, @@ -563,16 +556,13 @@ Specifically, it dd.def( "controlled_two_qubit_gate", - [](dd::Package& p, const Matrix& m, const Control& control, + [](dd::Package& p, const TwoQubitMatrix& mat, const Control& control, const dd::Qubit target0, const dd::Qubit target1) { - if (m.ndim() != 2 || m.shape(0) != 4 || m.shape(1) != 4) { - throw std::invalid_argument("Matrix must be 4x4."); - } return p.makeTwoQubitGateDD( - {std::array{m(0, 0), m(0, 1), m(0, 2), m(0, 3)}, - {m(1, 0), m(1, 1), m(1, 2), m(1, 3)}, - {m(2, 0), m(2, 1), m(2, 2), m(2, 3)}, - {m(3, 0), m(3, 1), m(3, 2), m(3, 3)}}, + {std::array{mat(0, 0), mat(0, 1), mat(0, 2), mat(0, 3)}, + {mat(1, 0), mat(1, 1), mat(1, 2), mat(1, 3)}, + {mat(2, 0), mat(2, 1), mat(2, 2), mat(2, 3)}, + {mat(3, 0), mat(3, 1), mat(3, 2), mat(3, 3)}}, getControl(control), target0, target1); }, "matrix"_a, "control"_a, "target0"_a, "target1"_a, @@ -591,16 +581,13 @@ Specifically, it dd.def( "multi_controlled_two_qubit_gate", - [](dd::Package& p, const Matrix& m, const Controls& controls, + [](dd::Package& p, const TwoQubitMatrix& mat, const Controls& controls, const dd::Qubit target0, const dd::Qubit target1) { - if (m.ndim() != 2 || m.shape(0) != 4 || m.shape(1) != 4) { - throw std::invalid_argument("Matrix must be 4x4."); - } return p.makeTwoQubitGateDD( - {std::array{m(0, 0), m(0, 1), m(0, 2), m(0, 3)}, - {m(1, 0), m(1, 1), m(1, 2), m(1, 3)}, - {m(2, 0), m(2, 1), m(2, 2), m(2, 3)}, - {m(3, 0), m(3, 1), m(3, 2), m(3, 3)}}, + {std::array{mat(0, 0), mat(0, 1), mat(0, 2), mat(0, 3)}, + {mat(1, 0), mat(1, 1), mat(1, 2), mat(1, 3)}, + {mat(2, 0), mat(2, 1), mat(2, 2), mat(2, 3)}, + {mat(3, 0), mat(3, 1), mat(3, 2), mat(3, 3)}}, getControls(controls), target0, target1); }, "matrix"_a, "controls"_a, "target0"_a, "target1"_a, @@ -619,9 +606,9 @@ Specifically, it dd.def( "from_matrix", - [](dd::Package& p, const Matrix& m) { - const auto rows = m.shape(0); - const auto cols = m.shape(1); + [](dd::Package& p, const Matrix& mat) { + const auto rows = mat.shape(0); + const auto cols = mat.shape(1); if (rows != cols) { throw std::invalid_argument("Matrix must be square."); } @@ -633,11 +620,11 @@ Specifically, it "Matrix must have a size of a power of two."); } if (rows == 1) { - return dd::mEdge::terminal(p.cn.lookup(m(0, 0))); + return dd::mEdge::terminal(p.cn.lookup(mat(0, 0))); } const auto level = static_cast(std::log2(rows) - 1); - const auto matrixDD = makeDDFromMatrix(p, m, 0, rows, 0, cols, level); - return dd::mEdge{matrixDD.p, p.cn.lookup(matrixDD.w)}; + const auto matrixDD = makeDDFromMatrix(p, mat, 0, rows, 0, cols, level); + return dd::mEdge{.p = matrixDD.p, .w = p.cn.lookup(matrixDD.w)}; }, "matrix"_a, // keep the DD package alive while the returned matrix DD is alive. @@ -653,9 +640,9 @@ Specifically, it "from_operation", [](dd::Package& p, const qc::Operation& op, const bool invert = false) { if (invert) { - return dd::getInverseDD(op, p); + return getInverseDD(op, p); } - return dd::getDD(op, p); + return getDD(op, p); }, "operation"_a, "invert"_a = false, // keep the DD package alive while the returned matrix DD is alive. diff --git a/bindings/dd/register_matrix_dds.cpp b/bindings/dd/register_matrix_dds.cpp index 8d26f36948..d52d188542 100644 --- a/bindings/dd/register_matrix_dds.cpp +++ b/bindings/dd/register_matrix_dds.cpp @@ -120,7 +120,7 @@ void registerMatrixDDs(const nb::module_& m) { const bool edgeLabels = false, const bool classic = false, const bool memory = false, const bool formatAsPolar = true) { std::ostringstream os; - dd::toDot(e, os, colored, edgeLabels, classic, memory, formatAsPolar); + toDot(e, os, colored, edgeLabels, classic, memory, formatAsPolar); return os.str(); }, "colored"_a = true, "edge_labels"_a = false, "classic"_a = false, @@ -146,8 +146,8 @@ void registerMatrixDDs(const nb::module_& m) { // replace the filename extension with .dot const auto dotFilename = filename.substr(0, filename.find_last_of('.')) + ".dot"; - dd::export2Dot(e, dotFilename, colored, edgeLabels, classic, memory, - true, formatAsPolar); + export2Dot(e, dotFilename, colored, edgeLabels, classic, memory, true, + formatAsPolar); }, "filename"_a, "colored"_a = true, "edge_labels"_a = false, "classic"_a = false, "memory"_a = false, "format_as_polar"_a = true, diff --git a/bindings/dd/register_vector_dds.cpp b/bindings/dd/register_vector_dds.cpp index 97fbba51b4..065a682571 100644 --- a/bindings/dd/register_vector_dds.cpp +++ b/bindings/dd/register_vector_dds.cpp @@ -108,7 +108,7 @@ void registerVectorDDs(const nb::module_& m) { const bool edgeLabels = false, const bool classic = false, const bool memory = false, const bool formatAsPolar = true) { std::ostringstream os; - dd::toDot(e, os, colored, edgeLabels, classic, memory, formatAsPolar); + toDot(e, os, colored, edgeLabels, classic, memory, formatAsPolar); return os.str(); }, "colored"_a = true, "edge_labels"_a = false, "classic"_a = false, @@ -134,8 +134,8 @@ void registerVectorDDs(const nb::module_& m) { // replace the filename extension with .dot const auto dotFilename = filename.substr(0, filename.find_last_of('.')) + ".dot"; - dd::export2Dot(e, dotFilename, colored, edgeLabels, classic, memory, - true, formatAsPolar); + export2Dot(e, dotFilename, colored, edgeLabels, classic, memory, true, + formatAsPolar); }, "filename"_a, "colored"_a = true, "edge_labels"_a = false, "classic"_a = false, "memory"_a = false, "format_as_polar"_a = true, diff --git a/python/mqt/core/dd.pyi b/python/mqt/core/dd.pyi index 2e8b8bd86c..85333df8f6 100644 --- a/python/mqt/core/dd.pyi +++ b/python/mqt/core/dd.pyi @@ -437,9 +437,7 @@ class DDPackage: The DD for the identity matrix. """ - def single_qubit_gate( - self, matrix: Annotated[NDArray[np.complex128], {"shape": (None, None)}], target: int - ) -> MatrixDD: + def single_qubit_gate(self, matrix: Annotated[NDArray[np.complex128], {"shape": (2, 2)}], target: int) -> MatrixDD: r"""Create the DD for a single-qubit gate. Args: @@ -452,7 +450,7 @@ class DDPackage: def controlled_single_qubit_gate( self, - matrix: Annotated[NDArray[np.complex128], {"shape": (None, None)}], + matrix: Annotated[NDArray[np.complex128], {"shape": (2, 2)}], control: mqt.core.ir.operations.Control | int, target: int, ) -> MatrixDD: @@ -469,7 +467,7 @@ class DDPackage: def multi_controlled_single_qubit_gate( self, - matrix: Annotated[NDArray[np.complex128], {"shape": (None, None)}], + matrix: Annotated[NDArray[np.complex128], {"shape": (2, 2)}], controls: AbstractSet[mqt.core.ir.operations.Control | int], target: int, ) -> MatrixDD: @@ -485,7 +483,7 @@ class DDPackage: """ def two_qubit_gate( - self, matrix: Annotated[NDArray[np.complex128], {"shape": (None, None)}], target0: int, target1: int + self, matrix: Annotated[NDArray[np.complex128], {"shape": (4, 4)}], target0: int, target1: int ) -> MatrixDD: r"""Create the DD for a two-qubit gate. @@ -500,7 +498,7 @@ class DDPackage: def controlled_two_qubit_gate( self, - matrix: Annotated[NDArray[np.complex128], {"shape": (None, None)}], + matrix: Annotated[NDArray[np.complex128], {"shape": (4, 4)}], control: mqt.core.ir.operations.Control | int, target0: int, target1: int, @@ -519,7 +517,7 @@ class DDPackage: def multi_controlled_two_qubit_gate( self, - matrix: Annotated[NDArray[np.complex128], {"shape": (None, None)}], + matrix: Annotated[NDArray[np.complex128], {"shape": (4, 4)}], controls: AbstractSet[mqt.core.ir.operations.Control | int], target0: int, target1: int, From bb1cff805244e787320c62d938656acfe8904d9e Mon Sep 17 00:00:00 2001 From: burgholzer Date: Sun, 21 Dec 2025 23:28:18 +0100 Subject: [PATCH 131/144] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20enforce=20keyword-?= =?UTF-8?q?only=20arguments=20in=20session=20and=20library=20functions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- bindings/fomac/fomac.cpp | 4 ++-- python/mqt/core/fomac.pyi | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/bindings/fomac/fomac.cpp b/bindings/fomac/fomac.cpp index b3d46c74dc..3f1b00d1cc 100644 --- a/bindings/fomac/fomac.cpp +++ b/bindings/fomac/fomac.cpp @@ -63,7 +63,7 @@ All authentication parameters are optional and can be provided as keyword argume .custom5 = std::move(custom5)}; new (self) fomac::Session(config); }, - "token"_a = std::nullopt, "auth_file"_a = std::nullopt, + nb::kw_only(), "token"_a = std::nullopt, "auth_file"_a = std::nullopt, "auth_url"_a = std::nullopt, "username"_a = std::nullopt, "password"_a = std::nullopt, "project_id"_a = std::nullopt, "custom1"_a = std::nullopt, "custom2"_a = std::nullopt, @@ -453,7 +453,7 @@ All authentication parameters are optional and can be provided as keyword argume libraryPath, prefix, config); return fomac::Session::Device::fromQDMIDevice(qdmiDevice); }, - "library_path"_a, "prefix"_a, "base_url"_a = std::nullopt, + "library_path"_a, "prefix"_a, nb::kw_only(), "base_url"_a = std::nullopt, "token"_a = std::nullopt, "auth_file"_a = std::nullopt, "auth_url"_a = std::nullopt, "username"_a = std::nullopt, "password"_a = std::nullopt, "custom1"_a = std::nullopt, diff --git a/python/mqt/core/fomac.pyi b/python/mqt/core/fomac.pyi index f22e2dab1f..60d00b5514 100644 --- a/python/mqt/core/fomac.pyi +++ b/python/mqt/core/fomac.pyi @@ -18,6 +18,7 @@ class Session: def __init__( self, + *, token: str | None = None, auth_file: str | None = None, auth_url: str | None = None, @@ -346,6 +347,7 @@ class Device: def add_dynamic_device_library( library_path: str, prefix: str, + *, base_url: str | None = None, token: str | None = None, auth_file: str | None = None, From e0fa893c5beda58b2d6cff27fed40b52156d7411 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Sun, 21 Dec 2025 23:53:09 +0100 Subject: [PATCH 132/144] =?UTF-8?q?=F0=9F=9A=A8=20ignore=20`*-pro-bounds-a?= =?UTF-8?q?void-unchecked-container-access`=20in=20bindings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- bindings/.clang-tidy | 1 + 1 file changed, 1 insertion(+) diff --git a/bindings/.clang-tidy b/bindings/.clang-tidy index 08f5528c9e..72daa60469 100644 --- a/bindings/.clang-tidy +++ b/bindings/.clang-tidy @@ -3,3 +3,4 @@ Checks: | -*-avoid-c-arrays, -cppcoreguidelines-owning-memory, -misc-redundant-expression, + -*-pro-bounds-avoid-unchecked-container-access From 1a3857669ed79946507fdc2d14357b6e2d80c0fc Mon Sep 17 00:00:00 2001 From: burgholzer Date: Sun, 21 Dec 2025 23:53:55 +0100 Subject: [PATCH 133/144] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20simplify=20permuta?= =?UTF-8?q?tion=20bindings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- bindings/ir/register_permutation.cpp | 34 +++++----------------------- python/mqt/core/ir/__init__.pyi | 2 +- 2 files changed, 7 insertions(+), 29 deletions(-) diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index 77609cd0a9..759a922b6d 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -32,9 +32,6 @@ namespace mqt { namespace nb = nanobind; using namespace nb::literals; -using Control = std::variant; -using Controls = std::set; - namespace { qc::Qubit nbIntToQubit(const nb::int_& value) { @@ -49,23 +46,6 @@ qc::Qubit nbIntToQubit(const nb::int_& value) { return static_cast(valueUint); } -/// Helper function to convert Control variant to qc::Control -qc::Control getControl(const Control& control) { - if (std::holds_alternative(control)) { - return std::get(control); - } - return nbIntToQubit(std::get(control)); -} - -/// Helper function to convert Controls variant to qc::Controls -qc::Controls getControls(const Controls& controls) { - qc::Controls result; - for (const auto& control : controls) { - result.insert(getControl(control)); - } - return result; -} - } // namespace // NOLINTNEXTLINE(misc-use-internal-linkage) @@ -95,12 +75,10 @@ void registerPermutation(const nb::module_& m) { }, "permutation"_a, "Create a permutation from a dictionary.") - .def( - "apply", - [](const qc::Permutation& p, const Controls& controls) { - return p.apply(getControls(controls)); - }, - "controls"_a, R"pb(Apply the permutation to a set of controls. + .def("apply", + nb::overload_cast(&qc::Permutation::apply, + nb::const_), + "controls"_a, R"pb(Apply the permutation to a set of controls. Args: controls: The set of controls to apply the permutation to. @@ -160,7 +138,7 @@ void registerPermutation(const nb::module_& m) { .def( "__iter__", [](const qc::Permutation& p) { - return nb::make_key_iterator( + return make_key_iterator( nb::type(), "key_iterator", p.begin(), p.end(), "Return an iterator over the indices of the permutation."); }, @@ -169,7 +147,7 @@ void registerPermutation(const nb::module_& m) { .def( "items", [](const qc::Permutation& p) { - return nb::make_iterator( + return make_iterator( nb::type(), "item_iterator", p.begin(), p.end(), "Return an iterable over the items of the permutation."); diff --git a/python/mqt/core/ir/__init__.pyi b/python/mqt/core/ir/__init__.pyi index 06c326ffba..a4c40594ed 100644 --- a/python/mqt/core/ir/__init__.pyi +++ b/python/mqt/core/ir/__init__.pyi @@ -28,7 +28,7 @@ class Permutation(MutableMapping[int, int]): """Create a permutation from a dictionary.""" @overload - def apply(self, controls: AbstractSet[operations.Control | int]) -> set[operations.Control]: + def apply(self, controls: AbstractSet[operations.Control]) -> set[operations.Control]: """Apply the permutation to a set of controls. Args: From a961cda53f78c6b729a7491d50047446a7af817e Mon Sep 17 00:00:00 2001 From: burgholzer Date: Sun, 21 Dec 2025 23:54:11 +0100 Subject: [PATCH 134/144] =?UTF-8?q?=F0=9F=9A=A8=20fix=20some=20linter=20wa?= =?UTF-8?q?rnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- bindings/ir/symbolic/register_expression.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/bindings/ir/symbolic/register_expression.cpp b/bindings/ir/symbolic/register_expression.cpp index 580d249e0d..a45a0a5367 100644 --- a/bindings/ir/symbolic/register_expression.cpp +++ b/bindings/ir/symbolic/register_expression.cpp @@ -57,9 +57,8 @@ Alternatively, an expression can be created with a single term and a constant or .def( "__iter__", [](const sym::Expression& expr) { - return nb::make_iterator( - nb::type>(), "iterator", - expr.begin(), expr.end()); + return make_iterator(nb::type>(), + "iterator", expr.begin(), expr.end()); }, nb::keep_alive<0, 1>()) @@ -73,6 +72,7 @@ Alternatively, an expression can be created with a single term and a constant or if (idx < 0 || idx >= n) { throw nb::index_error(); } + // NOLINTNEXTLINE(*-pro-bounds-avoid-unchecked-container-access) return expr.getTerms()[static_cast(idx)]; }, "index"_a) @@ -149,14 +149,12 @@ Alternatively, an expression can be created with a single term and a constant or .def(nb::self != nb::self, nb::sig("def __ne__(self, arg: object, /) -> bool")) .def(nb::hash(nb::self)) - .def("__str__", [](const sym::Expression& expr) { std::stringstream ss; ss << expr; return ss.str(); }) - .def("__repr__", [](const sym::Expression& expr) { std::stringstream ss; ss << expr; From d4b57158324f5aa47baaec040da8822c7a2977c6 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Mon, 22 Dec 2025 01:37:01 +0100 Subject: [PATCH 135/144] =?UTF-8?q?=F0=9F=90=9B=20fix=20wrong=20method=20b?= =?UTF-8?q?eing=20bound=20for=20`p`=20gate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- bindings/ir/register_quantum_computation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index 13486c045a..f1c48ad711 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -1235,7 +1235,7 @@ See Also: // P - qc.def("p", &qc::QuantumComputation::rz, "theta"_a, "q"_a, + qc.def("p", &qc::QuantumComputation::p, "theta"_a, "q"_a, R"pb(Apply a phase gate. .. math:: From 4a48766fd492596ebaf8ba56d0c2dae2943aa51f Mon Sep 17 00:00:00 2001 From: burgholzer Date: Mon, 22 Dec 2025 01:37:25 +0100 Subject: [PATCH 136/144] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20streamline=20docst?= =?UTF-8?q?ring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- bindings/ir/register_quantum_computation.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index f1c48ad711..350de1a191 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -430,9 +430,7 @@ Any operations in the quantum circuit are expected to be expressed in terms of t This is a permutation of the qubits in the quantum computation. It is mainly used to track where individual qubits end up at the end of the quantum computation, for example after a circuit has been compiled to a specific device and SWAP gates have been inserted, which permute the qubits. -The keys are the qubits in the circuit and the values are the actual qubits being measured. - -Similar to the initial layout, the keys in the output permutation are the qubits actually present in the circuit and the values are the qubits in the "original" circuit. +Similar to the initial layout, the keys are the qubits in the circuit and the values are the qubits in the "original" circuit. Examples: - If no output permutation is explicitly specified and the circuit does not contain measurements at the end, the output permutation is assumed to be the identity permutation. From 91608d448acbd04084f17fd3c907b56d8fac73d5 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Mon, 22 Dec 2025 01:38:12 +0100 Subject: [PATCH 137/144] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20include=20`Path?= =?UTF-8?q?Like[str]`=20in=20`qasm2`=20and=20`qasm3`=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- bindings/ir/register_quantum_computation.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index 350de1a191..6099f400dd 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -581,6 +581,7 @@ If the circuit contains measurements at the end, these measurements are used to circ.dump(filename, qc::Format::OpenQASM2); }, "filename"_a, + nb::sig("def qasm2(self, filename: os.PathLike[str] | str) -> None"), R"pb(Write the OpenQASM2 representation of the quantum computation to a file. See Also: @@ -603,6 +604,7 @@ See Also: circ.dump(filename, qc::Format::OpenQASM3); }, "filename"_a, + nb::sig("def qasm3(self, filename: os.PathLike[str] | str) -> None"), R"pb(Write the OpenQASM3 representation of the quantum computation to a file. See Also: From 5ff3329896205696a6e55ccc5d8c8733fa70e92c Mon Sep 17 00:00:00 2001 From: burgholzer Date: Mon, 22 Dec 2025 01:39:07 +0100 Subject: [PATCH 138/144] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20solve=20`Contro?= =?UTF-8?q?l=20|=20int`=20type=20hint=20without=20performance=20hit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- bindings/ir/register_quantum_computation.cpp | 955 +++++++++---------- python/mqt/core/ir/__init__.pyi | 29 +- 2 files changed, 467 insertions(+), 517 deletions(-) diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index 6099f400dd..92d89cbee3 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -47,39 +47,6 @@ using namespace nb::literals; using DiffType = std::vector>::difference_type; using SizeType = std::vector>::size_type; -using Control = std::variant; -using Controls = std::set; - -namespace { - -/// Helper function to convert Control variant to qc::Control -qc::Control getControl(const Control& control) { - if (std::holds_alternative(control)) { - return std::get(control); - } - const auto controlInt = - static_cast(std::get(control)); - if (controlInt < 0) { - throw nb::value_error("Control qubit index cannot be negative"); - } - const auto controlUint = static_cast(controlInt); - if (controlUint > std::numeric_limits::max()) { - throw nb::value_error("Control qubit index exceeds maximum value"); - } - return static_cast(controlUint); -} - -/// Helper function to convert Controls variant to qc::Controls -qc::Controls getControls(const Controls& controls) { - qc::Controls result; - for (const auto& control : controls) { - result.insert(getControl(control)); - } - return result; -} - -} // namespace - // NOLINTNEXTLINE(misc-use-internal-linkage) void registerQuantumComputation(const nb::module_& m) { auto wrap = [](DiffType i, const SizeType size) { @@ -643,6 +610,9 @@ See Also: Args: q: The target qubit)pb"); qc.def("ci", &qc::QuantumComputation::ci, "control"_a, "target"_a, + nb::sig("def ci(self, control: mqt.core.ir.operations.Control | int, " + "target: int) " + "-> None"), R"pb(Apply a controlled identity operation. See Also: @@ -652,6 +622,9 @@ See Also: control: The control qubit target: The target qubit)pb"); qc.def("mci", &qc::QuantumComputation::mci, "controls"_a, "target"_a, + nb::sig("def mci(self, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | " + "int], target: int) -> None"), R"pb(Apply a multi-controlled identity operation. See Also: @@ -671,12 +644,11 @@ See Also: Args: q: The target qubit)pb"); - qc.def( - "cx", - [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { circ.cx(getControl(control), target); }, - "control"_a, "target"_a, - R"pb(Apply a controlled Pauli-X (i.e., CNOT or CX) gate. + qc.def("cx", &qc::QuantumComputation::cx, "control"_a, "target"_a, + nb::sig("def cx(self, control: mqt.core.ir.operations.Control | int, " + "target: int) " + "-> None"), + R"pb(Apply a controlled Pauli-X (i.e., CNOT or CX) gate. See Also: :meth:`x` @@ -684,12 +656,11 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def( - "mcx", - [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { circ.mcx(getControls(controls), target); }, - "controls"_a, "target"_a, - R"pb(Apply a multi-controlled Pauli-X (i.e., Toffoli or MCX) gate. + qc.def("mcx", &qc::QuantumComputation::mcx, "controls"_a, "target"_a, + nb::sig("def mcx(self, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | " + "int], target: int) -> None"), + R"pb(Apply a multi-controlled Pauli-X (i.e., Toffoli or MCX) gate. See Also: :meth:`x` @@ -708,12 +679,11 @@ See Also: Args: q: The target qubit)pb"); - qc.def( - "cy", - [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { circ.cy(getControl(control), target); }, - "control"_a, "target"_a, - R"pb(Apply a controlled Pauli-Y gate. + qc.def("cy", &qc::QuantumComputation::cy, "control"_a, "target"_a, + nb::sig("def cy(self, control: mqt.core.ir.operations.Control | int, " + "target: int) " + "-> None"), + R"pb(Apply a controlled Pauli-Y gate. See Also: :meth:`y` @@ -721,12 +691,11 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def( - "mcy", - [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { circ.mcy(getControls(controls), target); }, - "controls"_a, "target"_a, - R"pb(Apply a multi-controlled Pauli-Y gate. + qc.def("mcy", &qc::QuantumComputation::mcy, "controls"_a, "target"_a, + nb::sig("def mcy(self, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | " + "int], target: int) -> None"), + R"pb(Apply a multi-controlled Pauli-Y gate. See Also: :meth:`y` @@ -745,12 +714,11 @@ See Also: Args: q: The target qubit)pb"); - qc.def( - "cz", - [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { circ.cz(getControl(control), target); }, - "control"_a, "target"_a, - R"pb(Apply a controlled Pauli-Z gate. + qc.def("cz", &qc::QuantumComputation::cz, "control"_a, "target"_a, + nb::sig("def cz(self, control: mqt.core.ir.operations.Control | int, " + "target: int) " + "-> None"), + R"pb(Apply a controlled Pauli-Z gate. See Also: :meth:`z` @@ -758,12 +726,11 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def( - "mcz", - [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { circ.mcz(getControls(controls), target); }, - "controls"_a, "target"_a, - R"pb(Apply a multi-controlled Pauli-Z gate. + qc.def("mcz", &qc::QuantumComputation::mcz, "controls"_a, "target"_a, + nb::sig("def mcz(self, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | " + "int], target: int) -> None"), + R"pb(Apply a multi-controlled Pauli-Z gate. See Also: :meth:`z` @@ -782,12 +749,11 @@ See Also: Args: q: The target qubit)pb"); - qc.def( - "ch", - [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { circ.ch(getControl(control), target); }, - "control"_a, "target"_a, - R"pb(Apply a controlled Hadamard gate. + qc.def("ch", &qc::QuantumComputation::ch, "control"_a, "target"_a, + nb::sig("def ch(self, control: mqt.core.ir.operations.Control | int, " + "target: int) " + "-> None"), + R"pb(Apply a controlled Hadamard gate. See Also: :meth:`h` @@ -795,12 +761,11 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def( - "mch", - [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { circ.mch(getControls(controls), target); }, - "controls"_a, "target"_a, - R"pb(Apply a multi-controlled Hadamard gate. + qc.def("mch", &qc::QuantumComputation::mch, "controls"_a, "target"_a, + nb::sig("def mch(self, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | " + "int], target: int) -> None"), + R"pb(Apply a multi-controlled Hadamard gate. See Also: :meth:`h` @@ -819,12 +784,11 @@ See Also: Args: q: The target qubit)pb"); - qc.def( - "cs", - [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { circ.cs(getControl(control), target); }, - "control"_a, "target"_a, - R"pb(Apply a controlled S gate. + qc.def("cs", &qc::QuantumComputation::cs, "control"_a, "target"_a, + nb::sig("def cs(self, control: mqt.core.ir.operations.Control | int, " + "target: int) " + "-> None"), + R"pb(Apply a controlled S gate. See Also: :meth:`s` @@ -832,12 +796,11 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def( - "mcs", - [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { circ.mcs(getControls(controls), target); }, - "controls"_a, "target"_a, - R"pb(Apply a multi-controlled S gate. + qc.def("mcs", &qc::QuantumComputation::mcs, "controls"_a, "target"_a, + nb::sig("def mcs(self, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | " + "int], target: int) -> None"), + R"pb(Apply a multi-controlled S gate. See Also: :meth:`s` @@ -856,12 +819,11 @@ See Also: Args: q: The target qubit)pb"); - qc.def( - "csdg", - [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { circ.csdg(getControl(control), target); }, - "control"_a, "target"_a, - R"pb(Apply a controlled :math:`S^\dagger` gate. + qc.def("csdg", &qc::QuantumComputation::csdg, "control"_a, "target"_a, + nb::sig("def csdg(self, control: mqt.core.ir.operations.Control | " + "int, target: " + "int) -> None"), + R"pb(Apply a controlled :math:`S^\dagger` gate. See Also: :meth:`sdg` @@ -869,12 +831,11 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def( - "mcsdg", - [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { circ.mcsdg(getControls(controls), target); }, - "controls"_a, "target"_a, - R"pb(Apply a multi-controlled :math:`S^\dagger` gate. + qc.def("mcsdg", &qc::QuantumComputation::mcsdg, "controls"_a, "target"_a, + nb::sig("def mcsdg(self, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | " + "int], target: int) -> None"), + R"pb(Apply a multi-controlled :math:`S^\dagger` gate. See Also: :meth:`sdg` @@ -893,12 +854,11 @@ See Also: Args: q: The target qubit)pb"); - qc.def( - "ct", - [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { circ.ct(getControl(control), target); }, - "control"_a, "target"_a, - R"pb(Apply a controlled T gate. + qc.def("ct", &qc::QuantumComputation::ct, "control"_a, "target"_a, + nb::sig("def ct(self, control: mqt.core.ir.operations.Control | int, " + "target: int) " + "-> None"), + R"pb(Apply a controlled T gate. See Also: :meth:`t` @@ -906,12 +866,11 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def( - "mct", - [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { circ.mct(getControls(controls), target); }, - "controls"_a, "target"_a, - R"pb(Apply a multi-controlled T gate. + qc.def("mct", &qc::QuantumComputation::mct, "controls"_a, "target"_a, + nb::sig("def mct(self, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | " + "int], target: int) -> None"), + R"pb(Apply a multi-controlled T gate. See Also: :meth:`t` @@ -930,12 +889,11 @@ See Also: Args: q: The target qubit)pb"); - qc.def( - "ctdg", - [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { circ.ctdg(getControl(control), target); }, - "control"_a, "target"_a, - R"pb(Apply a controlled :math:`T^\dagger` gate. + qc.def("ctdg", &qc::QuantumComputation::ctdg, "control"_a, "target"_a, + nb::sig("def ctdg(self, control: mqt.core.ir.operations.Control | " + "int, target: " + "int) -> None"), + R"pb(Apply a controlled :math:`T^\dagger` gate. See Also: :meth:`tdg` @@ -943,12 +901,11 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def( - "mctdg", - [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { circ.mctdg(getControls(controls), target); }, - "controls"_a, "target"_a, - R"pb(Apply a multi-controlled :math:`T^\dagger` gate. + qc.def("mctdg", &qc::QuantumComputation::mctdg, "controls"_a, "target"_a, + nb::sig("def mctdg(self, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | " + "int], target: int) -> None"), + R"pb(Apply a multi-controlled :math:`T^\dagger` gate. See Also: :meth:`tdg` @@ -967,12 +924,11 @@ See Also: Args: q: The target qubit)pb"); - qc.def( - "cv", - [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { circ.cv(getControl(control), target); }, - "control"_a, "target"_a, - R"pb(Apply a controlled V gate. + qc.def("cv", &qc::QuantumComputation::cv, "control"_a, "target"_a, + nb::sig("def cv(self, control: mqt.core.ir.operations.Control | int, " + "target: int) " + "-> None"), + R"pb(Apply a controlled V gate. See Also: :meth:`v` @@ -980,12 +936,11 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def( - "mcv", - [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { circ.mcv(getControls(controls), target); }, - "controls"_a, "target"_a, - R"pb(Apply a multi-controlled V gate. + qc.def("mcv", &qc::QuantumComputation::mcv, "controls"_a, "target"_a, + nb::sig("def mcv(self, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | " + "int], target: int) -> None"), + R"pb(Apply a multi-controlled V gate. See Also: :meth:`v` @@ -1004,12 +959,11 @@ See Also: Args: q: The target qubit)pb"); - qc.def( - "cvdg", - [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { circ.cvdg(getControl(control), target); }, - "control"_a, "target"_a, - R"pb(Apply a controlled :math:`V^\dagger` gate. + qc.def("cvdg", &qc::QuantumComputation::cvdg, "control"_a, "target"_a, + nb::sig("def cvdg(self, control: mqt.core.ir.operations.Control | " + "int, target: " + "int) -> None"), + R"pb(Apply a controlled :math:`V^\dagger` gate. See Also: :meth:`vdg` @@ -1017,12 +971,11 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def( - "mcvdg", - [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { circ.mcvdg(getControls(controls), target); }, - "controls"_a, "target"_a, - R"pb(Apply a multi-controlled :math:`V^\dagger` gate. + qc.def("mcvdg", &qc::QuantumComputation::mcvdg, "controls"_a, "target"_a, + nb::sig("def mcvdg(self, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | " + "int], target: int) -> None"), + R"pb(Apply a multi-controlled :math:`V^\dagger` gate. See Also: :meth:`vdg` @@ -1041,12 +994,11 @@ See Also: Args: q: The target qubit)pb"); - qc.def( - "csx", - [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { circ.csx(getControl(control), target); }, - "control"_a, "target"_a, - R"pb(Apply a controlled :math:`\sqrt{X}` gate. + qc.def("csx", &qc::QuantumComputation::csx, "control"_a, "target"_a, + nb::sig("def csx(self, control: mqt.core.ir.operations.Control | int, " + "target: " + "int) -> None"), + R"pb(Apply a controlled :math:`\sqrt{X}` gate. See Also: :meth:`sx` @@ -1054,12 +1006,11 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def( - "mcsx", - [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { circ.mcsx(getControls(controls), target); }, - "controls"_a, "target"_a, - R"pb(Apply a multi-controlled :math:`\sqrt{X}` gate. + qc.def("mcsx", &qc::QuantumComputation::mcsx, "controls"_a, "target"_a, + nb::sig("def mcsx(self, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | " + "int], target: int) -> None"), + R"pb(Apply a multi-controlled :math:`\sqrt{X}` gate. See Also: :meth:`sx` @@ -1078,12 +1029,11 @@ See Also: Args: q: The target qubit)pb"); - qc.def( - "csxdg", - [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target) { circ.csxdg(getControl(control), target); }, - "control"_a, "target"_a, - R"pb(Apply a controlled :math:`\sqrt{X}^\dagger` gate. + qc.def("csxdg", &qc::QuantumComputation::csxdg, "control"_a, "target"_a, + nb::sig("def csxdg(self, control: mqt.core.ir.operations.Control | " + "int, target: " + "int) -> None"), + R"pb(Apply a controlled :math:`\sqrt{X}^\dagger` gate. See Also: :meth:`sxdg` @@ -1091,12 +1041,11 @@ See Also: Args: control: The control qubit target: The target qubit)pb"); - qc.def( - "mcsxdg", - [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target) { circ.mcsxdg(getControls(controls), target); }, - "controls"_a, "target"_a, - R"pb(Apply a multi-controlled :math:`\sqrt{X}^\dagger` gate. + qc.def("mcsxdg", &qc::QuantumComputation::mcsxdg, "controls"_a, "target"_a, + nb::sig("def mcsxdg(self, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control " + "| int], target: int) -> None"), + R"pb(Apply a multi-controlled :math:`\sqrt{X}^\dagger` gate. See Also: :meth:`sxdg` @@ -1117,13 +1066,12 @@ See Also: Args: theta: The rotation angle q: The target qubit)pb"); - qc.def( - "crx", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Control& control, - qc::Qubit target) { circ.crx(theta, getControl(control), target); }, - "theta"_a, "control"_a, "target"_a, - R"pb(Apply a controlled :math:`R_x(\theta)` gate. + qc.def("crx", &qc::QuantumComputation::crx, "theta"_a, "control"_a, + "target"_a, + nb::sig("def crx(self, theta: mqt.core.ir.symbolic.Expression | " + "float, control: " + "mqt.core.ir.operations.Control | int, target: int) -> None"), + R"pb(Apply a controlled :math:`R_x(\theta)` gate. See Also: :meth:`rx` @@ -1132,13 +1080,14 @@ See Also: theta: The rotation angle control: The control qubit target: The target qubit)pb"); - qc.def( - "mcrx", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Controls& controls, - qc::Qubit target) { circ.mcrx(theta, getControls(controls), target); }, - "theta"_a, "controls"_a, "target"_a, - R"pb(Apply a multi-controlled :math:`R_x(\theta)` gate. + qc.def("mcrx", &qc::QuantumComputation::mcrx, "theta"_a, "controls"_a, + "target"_a, + nb::sig("def mcrx(self, theta: mqt.core.ir.symbolic.Expression | " + "float, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | int], " + "target: int) " + "-> None"), + R"pb(Apply a multi-controlled :math:`R_x(\theta)` gate. See Also: :meth:`rx` @@ -1160,13 +1109,12 @@ See Also: Args: theta: The rotation angle q: The target qubit)pb"); - qc.def( - "cry", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Control& control, - qc::Qubit target) { circ.cry(theta, getControl(control), target); }, - "theta"_a, "control"_a, "target"_a, - R"pb(Apply a controlled :math:`R_y(\theta)` gate. + qc.def("cry", &qc::QuantumComputation::cry, "theta"_a, "control"_a, + "target"_a, + nb::sig("def cry(self, theta: mqt.core.ir.symbolic.Expression | " + "float, control: " + "mqt.core.ir.operations.Control | int, target: int) -> None"), + R"pb(Apply a controlled :math:`R_y(\theta)` gate. See Also: :meth:`ry` @@ -1175,13 +1123,14 @@ See Also: theta: The rotation angle control: The control qubit target: The target qubit)pb"); - qc.def( - "mcry", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Controls& controls, - qc::Qubit target) { circ.mcry(theta, getControls(controls), target); }, - "theta"_a, "controls"_a, "target"_a, - R"pb(Apply a multi-controlled :math:`R_y(\theta)` gate. + qc.def("mcry", &qc::QuantumComputation::mcry, "theta"_a, "controls"_a, + "target"_a, + nb::sig("def mcry(self, theta: mqt.core.ir.symbolic.Expression | " + "float, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | int], " + "target: int) " + "-> None"), + R"pb(Apply a multi-controlled :math:`R_y(\theta)` gate. See Also: :meth:`ry` @@ -1202,13 +1151,12 @@ See Also: Args: theta: The rotation angle q: The target qubit)pb"); - qc.def( - "crz", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Control& control, - qc::Qubit target) { circ.crz(theta, getControl(control), target); }, - "theta"_a, "control"_a, "target"_a, - R"pb(Apply a controlled :math:`R_z(\theta)` gate. + qc.def("crz", &qc::QuantumComputation::crz, "theta"_a, "control"_a, + "target"_a, + nb::sig("def crz(self, theta: mqt.core.ir.symbolic.Expression | " + "float, control: " + "mqt.core.ir.operations.Control | int, target: int) -> None"), + R"pb(Apply a controlled :math:`R_z(\theta)` gate. See Also: :meth:`rz` @@ -1217,13 +1165,14 @@ See Also: theta: The rotation angle control: The control qubit target: The target qubit)pb"); - qc.def( - "mcrz", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Controls& controls, - qc::Qubit target) { circ.mcrz(theta, getControls(controls), target); }, - "theta"_a, "controls"_a, "target"_a, - R"pb(Apply a multi-controlled :math:`R_z(\theta)` gate. + qc.def("mcrz", &qc::QuantumComputation::mcrz, "theta"_a, "controls"_a, + "target"_a, + nb::sig("def mcrz(self, theta: mqt.core.ir.symbolic.Expression | " + "float, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | int], " + "target: int) " + "-> None"), + R"pb(Apply a multi-controlled :math:`R_z(\theta)` gate. See Also: :meth:`rz` @@ -1244,13 +1193,11 @@ See Also: Args: theta: The rotation angle q: The target qubit)pb"); - qc.def( - "cp", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Control& control, - qc::Qubit target) { circ.cp(theta, getControl(control), target); }, - "theta"_a, "control"_a, "target"_a, - R"pb(Apply a controlled phase gate. + qc.def("cp", &qc::QuantumComputation::cp, "theta"_a, "control"_a, "target"_a, + nb::sig("def cp(self, theta: mqt.core.ir.symbolic.Expression | float, " + "control: " + "mqt.core.ir.operations.Control | int, target: int) -> None"), + R"pb(Apply a controlled phase gate. See Also: :meth:`p` @@ -1259,13 +1206,14 @@ See Also: theta: The rotation angle control: The control qubit target: The target qubit)pb"); - qc.def( - "mcp", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Controls& controls, - qc::Qubit target) { circ.mcp(theta, getControls(controls), target); }, - "theta"_a, "controls"_a, "target"_a, - R"pb(Apply a multi-controlled phase gate. + qc.def("mcp", &qc::QuantumComputation::mcp, "theta"_a, "controls"_a, + "target"_a, + nb::sig("def mcp(self, theta: mqt.core.ir.symbolic.Expression | " + "float, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | int], " + "target: int) " + "-> None"), + R"pb(Apply a multi-controlled phase gate. See Also: :meth:`p` @@ -1287,14 +1235,14 @@ See Also: phi: The rotation angle lambda_: The rotation angle q: The target qubit)pb"); - qc.def( - "cu2", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& phi, - qc::SymbolOrNumber& lambda, const Control& control, qc::Qubit target) { - circ.cu2(phi, lambda, getControl(control), target); - }, - "phi"_a, "lambda_"_a, "control"_a, "target"_a, - R"pb(Apply a controlled :math:`U_2(\phi, \lambda)` gate. + qc.def("cu2", &qc::QuantumComputation::cu2, "phi"_a, "lambda_"_a, "control"_a, + "target"_a, + nb::sig("def cu2(self, phi: mqt.core.ir.symbolic.Expression | float, " + "lambda_: " + "mqt.core.ir.symbolic.Expression | float, control: " + "mqt.core.ir.operations.Control | " + "int, target: int) -> None"), + R"pb(Apply a controlled :math:`U_2(\phi, \lambda)` gate. See Also: :meth:`u2` @@ -1304,15 +1252,15 @@ See Also: lambda_: The rotation angle control: The control qubit target: The target qubit)pb"); - qc.def( - "mcu2", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& phi, - qc::SymbolOrNumber& lambda, const Controls& controls, - qc::Qubit target) { - circ.mcu2(phi, lambda, getControls(controls), target); - }, - "phi"_a, "lambda_"_a, "controls"_a, "target"_a, - R"pb(Apply a multi-controlled :math:`U_2(\phi, \lambda)` gate. + qc.def("mcu2", &qc::QuantumComputation::mcu2, "phi"_a, "lambda_"_a, + "controls"_a, "target"_a, + nb::sig("def mcu2(self, phi: mqt.core.ir.symbolic.Expression | float, " + "lambda_: " + "mqt.core.ir.symbolic.Expression | float, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | int], " + "target: int) " + "-> None"), + R"pb(Apply a multi-controlled :math:`U_2(\phi, \lambda)` gate. See Also: :meth:`u2` @@ -1337,12 +1285,13 @@ See Also: phi: The rotation angle q: The target qubit)pb"); qc.def( - "cr", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - qc::SymbolOrNumber& phi, const Control& control, qc::Qubit target) { - circ.cr(theta, phi, getControl(control), target); - }, - "theta"_a, "phi"_a, "control"_a, "target"_a, + "cr", &qc::QuantumComputation::cr, "theta"_a, "phi"_a, "control"_a, + "target"_a, + nb::sig( + "def cr(self, theta: mqt.core.ir.symbolic.Expression | float, phi: " + "mqt.core.ir.symbolic.Expression " + "| float, control: mqt.core.ir.operations.Control | int, target: " + "int) -> None"), R"pb(Apply a controlled :math:`R(\theta, \phi)` gate. See Also: @@ -1354,12 +1303,14 @@ See Also: control: The control qubit target: The target qubit)pb"); qc.def( - "mcr", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - qc::SymbolOrNumber& phi, const Controls& controls, qc::Qubit target) { - circ.mcr(theta, phi, getControls(controls), target); - }, - "theta"_a, "phi"_a, "controls"_a, "target"_a, + "mcr", &qc::QuantumComputation::mcr, "theta"_a, "phi"_a, "controls"_a, + "target"_a, + nb::sig( + "def mcr(self, theta: mqt.core.ir.symbolic.Expression | float, phi: " + "mqt.core.ir.symbolic.Expression | float, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | int], target: " + "int) " + "-> None"), R"pb(Apply a multi-controlled :math:`R(\theta, \phi)` gate. See Also: @@ -1386,13 +1337,14 @@ See Also: lambda_: The rotation angle q: The target qubit)pb"); qc.def( - "cu", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - qc::SymbolOrNumber& phi, qc::SymbolOrNumber& lambda, - const Control& control, qc::Qubit target) { - circ.cu(theta, phi, lambda, getControl(control), target); - }, - "theta"_a, "phi"_a, "lambda_"_a, "control"_a, "target"_a, + "cu", &qc::QuantumComputation::cu, "theta"_a, "phi"_a, "lambda_"_a, + "control"_a, "target"_a, + nb::sig( + "def cu(self, theta: mqt.core.ir.symbolic.Expression | float, phi: " + "mqt.core.ir.symbolic.Expression | float, lambda_: " + "mqt.core.ir.symbolic.Expression | " + "float, control: mqt.core.ir.operations.Control | int, target: int) " + "-> None"), R"pb(Apply a controlled :math:`U(\theta, \phi, \lambda)` gate. See Also: @@ -1405,13 +1357,15 @@ See Also: control: The control qubit target: The target qubit)pb"); qc.def( - "mcu", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - qc::SymbolOrNumber& phi, qc::SymbolOrNumber& lambda, - const Controls& controls, qc::Qubit target) { - circ.mcu(theta, phi, lambda, getControls(controls), target); - }, - "theta"_a, "phi"_a, "lambda_"_a, "controls"_a, "target"_a, + "mcu", &qc::QuantumComputation::mcu, "theta"_a, "phi"_a, "lambda_"_a, + "controls"_a, "target"_a, + nb::sig( + "def mcu(self, theta: mqt.core.ir.symbolic.Expression | float, phi: " + "mqt.core.ir.symbolic.Expression | float, lambda_: " + "mqt.core.ir.symbolic.Expression | " + "float, controls: collections.abc.Set[mqt.core.ir.operations.Control " + "| " + "int], target: int) -> None"), R"pb(Apply a multi-controlled :math:`U(\theta, \phi, \lambda)` gate. See Also: @@ -1435,14 +1389,12 @@ See Also: Args: target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "cswap", - [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target0, qc::Qubit target1) { - circ.cswap(getControl(control), target0, target1); - }, - "control"_a, "target1"_a, "target2"_a, - R"pb(Apply a controlled SWAP gate. + qc.def("cswap", &qc::QuantumComputation::cswap, "control"_a, "target1"_a, + "target2"_a, + nb::sig("def cswap(self, control: mqt.core.ir.operations.Control | " + "int, target1: int, " + "target2: int) -> None"), + R"pb(Apply a controlled SWAP gate. See Also: :meth:`swap` @@ -1451,14 +1403,12 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "mcswap", - [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target0, qc::Qubit target1) { - circ.mcswap(getControls(controls), target0, target1); - }, - "controls"_a, "target1"_a, "target2"_a, - R"pb(Apply a multi-controlled SWAP gate. + qc.def("mcswap", &qc::QuantumComputation::mcswap, "controls"_a, "target1"_a, + "target2"_a, + nb::sig("def mcswap(self, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control " + "| int], target1: int, target2: int) -> None"), + R"pb(Apply a multi-controlled SWAP gate. See Also: :meth:`swap` @@ -1479,14 +1429,12 @@ See Also: Args: target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "cdcx", - [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target0, qc::Qubit target1) { - circ.cdcx(getControl(control), target0, target1); - }, - "control"_a, "target1"_a, "target2"_a, - R"pb(Apply a controlled DCX gate. + qc.def("cdcx", &qc::QuantumComputation::cdcx, "control"_a, "target1"_a, + "target2"_a, + nb::sig("def cdcx(self, control: mqt.core.ir.operations.Control | " + "int, target1: int, " + "target2: int) -> None"), + R"pb(Apply a controlled DCX gate. See Also: :meth:`dcx` @@ -1495,14 +1443,12 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "mcdcx", - [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target0, qc::Qubit target1) { - circ.mcdcx(getControls(controls), target0, target1); - }, - "controls"_a, "target1"_a, "target2"_a, - R"pb(Apply a multi-controlled DCX gate. + qc.def("mcdcx", &qc::QuantumComputation::mcdcx, "controls"_a, "target1"_a, + "target2"_a, + nb::sig("def mcdcx(self, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | " + "int], target1: int, target2: int) -> None"), + R"pb(Apply a multi-controlled DCX gate. See Also: :meth:`dcx` @@ -1523,14 +1469,12 @@ See Also: Args: target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "cecr", - [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target0, qc::Qubit target1) { - circ.cecr(getControl(control), target0, target1); - }, - "control"_a, "target1"_a, "target2"_a, - R"pb(Apply a controlled ECR gate. + qc.def("cecr", &qc::QuantumComputation::cecr, "control"_a, "target1"_a, + "target2"_a, + nb::sig("def cecr(self, control: mqt.core.ir.operations.Control | " + "int, target1: int, " + "target2: int) -> None"), + R"pb(Apply a controlled ECR gate. See Also: :meth:`ecr` @@ -1539,14 +1483,12 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "mcecr", - [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target0, qc::Qubit target1) { - circ.mcecr(getControls(controls), target0, target1); - }, - "controls"_a, "target1"_a, "target2"_a, - R"pb(Apply a multi-controlled ECR gate. + qc.def("mcecr", &qc::QuantumComputation::mcecr, "controls"_a, "target1"_a, + "target2"_a, + nb::sig("def mcecr(self, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | " + "int], target1: int, target2: int) -> None"), + R"pb(Apply a multi-controlled ECR gate. See Also: :meth:`ecr` @@ -1567,14 +1509,12 @@ See Also: Args: target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "ciswap", - [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target0, qc::Qubit target1) { - circ.ciswap(getControl(control), target0, target1); - }, - "control"_a, "target1"_a, "target2"_a, - R"pb(Apply a controlled :math:`i\text{SWAP}` gate. + qc.def("ciswap", &qc::QuantumComputation::ciswap, "control"_a, "target1"_a, + "target2"_a, + nb::sig("def ciswap(self, control: mqt.core.ir.operations.Control | " + "int, target1: int, " + "target2: int) -> None"), + R"pb(Apply a controlled :math:`i\text{SWAP}` gate. See Also: :meth:`iswap` @@ -1583,14 +1523,12 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "mciswap", - [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target0, qc::Qubit target1) { - circ.mciswap(getControls(controls), target0, target1); - }, - "controls"_a, "target1"_a, "target2"_a, - R"pb(Apply a multi-controlled :math:`i\text{SWAP}` gate. + qc.def("mciswap", &qc::QuantumComputation::mciswap, "controls"_a, "target1"_a, + "target2"_a, + nb::sig("def mciswap(self, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control " + "| int], target1: int, target2: int) -> None"), + R"pb(Apply a multi-controlled :math:`i\text{SWAP}` gate. See Also: :meth:`iswap` @@ -1611,14 +1549,12 @@ See Also: Args: target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "ciswapdg", - [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target0, qc::Qubit target1) { - circ.ciswapdg(getControl(control), target0, target1); - }, - "control"_a, "target1"_a, "target2"_a, - R"pb(Apply a controlled :math:`i\text{SWAP}^\dagger` gate. + qc.def("ciswapdg", &qc::QuantumComputation::ciswapdg, "control"_a, + "target1"_a, "target2"_a, + nb::sig("def ciswapdg(self, control: mqt.core.ir.operations.Control | " + "int, target1: " + "int, target2: int) -> None"), + R"pb(Apply a controlled :math:`i\text{SWAP}^\dagger` gate. See Also: :meth:`iswapdg` @@ -1627,14 +1563,12 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "mciswapdg", - [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target0, qc::Qubit target1) { - circ.mciswapdg(getControls(controls), target0, target1); - }, - "controls"_a, "target1"_a, "target2"_a, - R"pb(Apply a multi-controlled :math:`i\text{SWAP}^\dagger` gate. + qc.def("mciswapdg", &qc::QuantumComputation::mciswapdg, "controls"_a, + "target1"_a, "target2"_a, + nb::sig("def mciswapdg(self, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control " + "| int], target1: int, target2: int) -> None"), + R"pb(Apply a multi-controlled :math:`i\text{SWAP}^\dagger` gate. See Also: :meth:`iswapdg` @@ -1655,14 +1589,12 @@ See Also: Args: target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "cperes", - [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target0, qc::Qubit target1) { - circ.cperes(getControl(control), target0, target1); - }, - "control"_a, "target1"_a, "target2"_a, - R"pb(Apply a controlled Peres gate. + qc.def("cperes", &qc::QuantumComputation::cperes, "control"_a, "target1"_a, + "target2"_a, + nb::sig("def cperes(self, control: mqt.core.ir.operations.Control | " + "int, target1: int, " + "target2: int) -> None"), + R"pb(Apply a controlled Peres gate. See Also: :meth:`peres` @@ -1671,14 +1603,12 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "mcperes", - [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target0, qc::Qubit target1) { - circ.mcperes(getControls(controls), target0, target1); - }, - "controls"_a, "target1"_a, "target2"_a, - R"pb(Apply a multi-controlled Peres gate. + qc.def("mcperes", &qc::QuantumComputation::mcperes, "controls"_a, "target1"_a, + "target2"_a, + nb::sig("def mcperes(self, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control " + "| int], target1: int, target2: int) -> None"), + R"pb(Apply a multi-controlled Peres gate. See Also: :meth:`peres` @@ -1698,14 +1628,12 @@ See Also: Args: target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "cperesdg", - [](qc::QuantumComputation& circ, const Control& control, - qc::Qubit target0, qc::Qubit target1) { - circ.cperesdg(getControl(control), target0, target1); - }, - "control"_a, "target1"_a, "target2"_a, - R"pb(Apply a controlled :math:`\text{Peres}^\dagger` gate. + qc.def("cperesdg", &qc::QuantumComputation::cperesdg, "control"_a, + "target1"_a, "target2"_a, + nb::sig("def cperesdg(self, control: mqt.core.ir.operations.Control | " + "int, target1: " + "int, target2: int) -> None"), + R"pb(Apply a controlled :math:`\text{Peres}^\dagger` gate. See Also: :meth:`peresdg` @@ -1714,14 +1642,12 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "mcperesdg", - [](qc::QuantumComputation& circ, const Controls& controls, - qc::Qubit target0, qc::Qubit target1) { - circ.mcperesdg(getControls(controls), target0, target1); - }, - "controls"_a, "target1"_a, "target2"_a, - R"pb(Apply a multi-controlled :math:`\text{Peres}^\dagger` gate. + qc.def("mcperesdg", &qc::QuantumComputation::mcperesdg, "controls"_a, + "target1"_a, "target2"_a, + nb::sig("def mcperesdg(self, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control " + "| int], target1: int, target2: int) -> None"), + R"pb(Apply a multi-controlled :math:`\text{Peres}^\dagger` gate. See Also: :meth:`peresdg` @@ -1748,14 +1674,13 @@ See Also: theta: The rotation angle target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "crxx", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Control& control, qc::Qubit target0, qc::Qubit target1) { - circ.crxx(theta, getControl(control), target0, target1); - }, - "theta"_a, "control"_a, "target1"_a, "target2"_a, - R"pb(Apply a controlled :math:`R_{xx}(\theta)` gate. + qc.def("crxx", &qc::QuantumComputation::crxx, "theta"_a, "control"_a, + "target1"_a, "target2"_a, + nb::sig("def crxx(self, theta: mqt.core.ir.symbolic.Expression | " + "float, control: " + "mqt.core.ir.operations.Control | int, target1: int, target2: " + "int) -> None"), + R"pb(Apply a controlled :math:`R_{xx}(\theta)` gate. See Also: :meth:`rxx` @@ -1765,14 +1690,14 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "mcrxx", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Controls& controls, qc::Qubit target0, qc::Qubit target1) { - circ.mcrxx(theta, getControls(controls), target0, target1); - }, - "theta"_a, "controls"_a, "target1"_a, "target2"_a, - R"pb(Apply a multi-controlled :math:`R_{xx}(\theta)` gate. + qc.def("mcrxx", &qc::QuantumComputation::mcrxx, "theta"_a, "controls"_a, + "target1"_a, "target2"_a, + nb::sig("def mcrxx(self, theta: mqt.core.ir.symbolic.Expression | " + "float, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | int], " + "target1: int, " + "target2: int) -> None"), + R"pb(Apply a multi-controlled :math:`R_{xx}(\theta)` gate. See Also: :meth:`rxx` @@ -1800,14 +1725,13 @@ See Also: theta: The rotation angle target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "cryy", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Control& control, qc::Qubit target0, qc::Qubit target1) { - circ.cryy(theta, getControl(control), target0, target1); - }, - "theta"_a, "control"_a, "target1"_a, "target2"_a, - R"pb(Apply a controlled :math:`R_{yy}(\theta)` gate. + qc.def("cryy", &qc::QuantumComputation::cryy, "theta"_a, "control"_a, + "target1"_a, "target2"_a, + nb::sig("def cryy(self, theta: mqt.core.ir.symbolic.Expression | " + "float, control: " + "mqt.core.ir.operations.Control | int, target1: int, target2: " + "int) -> None"), + R"pb(Apply a controlled :math:`R_{yy}(\theta)` gate. See Also: :meth:`ryy` @@ -1817,14 +1741,14 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "mcryy", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Controls& controls, qc::Qubit target0, qc::Qubit target1) { - circ.mcryy(theta, getControls(controls), target0, target1); - }, - "theta"_a, "controls"_a, "target1"_a, "target2"_a, - R"pb(Apply a multi-controlled :math:`R_{yy}(\theta)` gate. + qc.def("mcryy", &qc::QuantumComputation::mcryy, "theta"_a, "controls"_a, + "target1"_a, "target2"_a, + nb::sig("def mcryy(self, theta: mqt.core.ir.symbolic.Expression | " + "float, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | int], " + "target1: int, " + "target2: int) -> None"), + R"pb(Apply a multi-controlled :math:`R_{yy}(\theta)` gate. See Also: :meth:`ryy` @@ -1852,14 +1776,13 @@ See Also: theta: The rotation angle target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "crzx", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Control& control, qc::Qubit target0, qc::Qubit target1) { - circ.crzx(theta, getControl(control), target0, target1); - }, - "theta"_a, "control"_a, "target1"_a, "target2"_a, - R"pb(Apply a controlled :math:`R_{zx}(\theta)` gate. + qc.def("crzx", &qc::QuantumComputation::crzx, "theta"_a, "control"_a, + "target1"_a, "target2"_a, + nb::sig("def crzx(self, theta: mqt.core.ir.symbolic.Expression | " + "float, control: " + "mqt.core.ir.operations.Control | int, target1: int, target2: " + "int) -> None"), + R"pb(Apply a controlled :math:`R_{zx}(\theta)` gate. See Also: :meth:`rzx` @@ -1869,14 +1792,14 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "mcrzx", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Controls& controls, qc::Qubit target0, qc::Qubit target1) { - circ.mcrzx(theta, getControls(controls), target0, target1); - }, - "theta"_a, "controls"_a, "target1"_a, "target2"_a, - R"pb(Apply a multi-controlled :math:`R_{zx}(\theta)` gate. + qc.def("mcrzx", &qc::QuantumComputation::mcrzx, "theta"_a, "controls"_a, + "target1"_a, "target2"_a, + nb::sig("def mcrzx(self, theta: mqt.core.ir.symbolic.Expression | " + "float, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | int], " + "target1: int, " + "target2: int) -> None"), + R"pb(Apply a multi-controlled :math:`R_{zx}(\theta)` gate. See Also: :meth:`rzx` @@ -1904,14 +1827,13 @@ See Also: theta: The rotation angle target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "crzz", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Control& control, qc::Qubit target0, qc::Qubit target1) { - circ.crzz(theta, getControl(control), target0, target1); - }, - "theta"_a, "control"_a, "target1"_a, "target2"_a, - R"pb(Apply a controlled :math:`R_{zz}(\theta)` gate. + qc.def("crzz", &qc::QuantumComputation::crzz, "theta"_a, "control"_a, + "target1"_a, "target2"_a, + nb::sig("def crzz(self, theta: mqt.core.ir.symbolic.Expression | " + "float, control: " + "mqt.core.ir.operations.Control | int, target1: int, target2: " + "int) -> None"), + R"pb(Apply a controlled :math:`R_{zz}(\theta)` gate. See Also: :meth:`rzz` @@ -1921,14 +1843,14 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "mcrzz", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - const Controls& controls, qc::Qubit target0, qc::Qubit target1) { - circ.mcrzz(theta, getControls(controls), target0, target1); - }, - "theta"_a, "controls"_a, "target1"_a, "target2"_a, - R"pb(Apply a multi-controlled :math:`R_{zz}(\theta)` gate. + qc.def("mcrzz", &qc::QuantumComputation::mcrzz, "theta"_a, "controls"_a, + "target1"_a, "target2"_a, + nb::sig("def mcrzz(self, theta: mqt.core.ir.symbolic.Expression | " + "float, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | int], " + "target1: int, " + "target2: int) -> None"), + R"pb(Apply a multi-controlled :math:`R_{zz}(\theta)` gate. See Also: :meth:`rzz` @@ -1957,15 +1879,14 @@ See Also: beta: The rotation angle target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "cxx_minus_yy", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - qc::SymbolOrNumber& beta, const Control& control, qc::Qubit target0, - qc::Qubit target1) { - circ.cxx_minus_yy(theta, beta, getControl(control), target0, target1); - }, - "theta"_a, "beta"_a, "control"_a, "target1"_a, "target2"_a, - R"pb(Apply a controlled :math:`R_{XX - YY}(\theta, \beta)` gate. + qc.def("cxx_minus_yy", &qc::QuantumComputation::cxx_minus_yy, "theta"_a, + "beta"_a, "control"_a, "target1"_a, "target2"_a, + nb::sig("def cxx_minus_yy(self, theta: " + "mqt.core.ir.symbolic.Expression | float, beta: " + "mqt.core.ir.symbolic.Expression | float, control: " + "mqt.core.ir.operations.Control | " + "int, target1: int, target2: int) -> None"), + R"pb(Apply a controlled :math:`R_{XX - YY}(\theta, \beta)` gate. See Also: :meth:`xx_minus_yy` @@ -1976,16 +1897,15 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "mcxx_minus_yy", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - qc::SymbolOrNumber& beta, const Controls& controls, qc::Qubit target0, - qc::Qubit target1) { - circ.mcxx_minus_yy(theta, beta, getControls(controls), target0, - target1); - }, - "theta"_a, "beta"_a, "controls"_a, "target1"_a, "target2"_a, - R"pb(Apply a multi-controlled :math:`R_{XX - YY}(\theta, \beta)` gate. + qc.def("mcxx_minus_yy", &qc::QuantumComputation::mcxx_minus_yy, "theta"_a, + "beta"_a, "controls"_a, "target1"_a, "target2"_a, + nb::sig("def mcxx_minus_yy(self, theta: " + "mqt.core.ir.symbolic.Expression | float, beta: " + "mqt.core.ir.symbolic.Expression | float, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | int], " + "target1: int, " + "target2: int) -> None"), + R"pb(Apply a multi-controlled :math:`R_{XX - YY}(\theta, \beta)` gate. See Also: :meth:`xx_minus_yy` @@ -2015,15 +1935,14 @@ See Also: beta: The rotation angle target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "cxx_plus_yy", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - qc::SymbolOrNumber& beta, const Control& control, qc::Qubit target0, - qc::Qubit target1) { - circ.cxx_plus_yy(theta, beta, getControl(control), target0, target1); - }, - "theta"_a, "beta"_a, "control"_a, "target1"_a, "target2"_a, - R"pb(Apply a controlled :math:`R_{XX + YY}(\theta, \beta)` gate. + qc.def("cxx_plus_yy", &qc::QuantumComputation::cxx_plus_yy, "theta"_a, + "beta"_a, "control"_a, "target1"_a, "target2"_a, + nb::sig("def cxx_plus_yy(self, theta: mqt.core.ir.symbolic.Expression " + "| float, beta: " + "mqt.core.ir.symbolic.Expression | float, control: " + "mqt.core.ir.operations.Control | " + "int, target1: int, target2: int) -> None"), + R"pb(Apply a controlled :math:`R_{XX + YY}(\theta, \beta)` gate. See Also: :meth:`xx_plus_yy` @@ -2034,15 +1953,15 @@ See Also: control: The control qubit target1: The first target qubit target2: The second target qubit)pb"); - qc.def( - "mcxx_plus_yy", - [](qc::QuantumComputation& circ, qc::SymbolOrNumber& theta, - qc::SymbolOrNumber& beta, const Controls& controls, qc::Qubit target0, - qc::Qubit target1) { - circ.mcxx_plus_yy(theta, beta, getControls(controls), target0, target1); - }, - "theta"_a, "beta"_a, "controls"_a, "target1"_a, "target2"_a, - R"pb(Apply a multi-controlled :math:`R_{XX + YY}(\theta, \beta)` gate. + qc.def("mcxx_plus_yy", &qc::QuantumComputation::mcxx_plus_yy, "theta"_a, + "beta"_a, "controls"_a, "target1"_a, "target2"_a, + nb::sig("def mcxx_plus_yy(self, theta: " + "mqt.core.ir.symbolic.Expression | float, beta: " + "mqt.core.ir.symbolic.Expression | float, controls: " + "collections.abc.Set[mqt.core.ir.operations.Control | int], " + "target1: int, " + "target2: int) -> None"), + R"pb(Apply a multi-controlled :math:`R_{XX + YY}(\theta, \beta)` gate. See Also: :meth:`xx_plus_yy` @@ -2203,7 +2122,15 @@ This method is equivalent to calling :meth:`measure` multiple times. &qc::QuantumComputation::if_), "op_type"_a, "target"_a, "control"_a, "control_register"_a, "expected_value"_a = 1U, "comparison_kind"_a = qc::ComparisonKind::Eq, - "params"_a.sig("...") = std::vector{}, + "params"_a = std::vector{}, + nb::sig( + "def if_(self, op_type: mqt.core.ir.operations.OpType, target: " + "int, control: mqt.core.ir.operations.Control | int, " + "control_register: mqt.core.ir.registers.ClassicalRegister, " + "expected_value: int = 1, comparison_kind: " + "mqt.core.ir.operations.ComparisonKind = ..., params: " + "collections.abc.Sequence[mqt.core.ir.symbolic.Expression | float] " + "= ...) -> None"), R"pb(Add an if operation to the circuit. Args: @@ -2223,7 +2150,16 @@ This method is equivalent to calling :meth:`measure` multiple times. &qc::QuantumComputation::if_), "op_type"_a, "target"_a, "controls"_a, "control_register"_a, "expected_value"_a = 1U, "comparison_kind"_a = qc::ComparisonKind::Eq, - "params"_a.sig("...") = std::vector{}, + "params"_a = std::vector{}, + nb::sig( + "def if_(self, op_type: mqt.core.ir.operations.OpType, target: " + "int, controls: collections.abc.Set[mqt.core.ir.operations.Control | " + "int], " + "control_register: mqt.core.ir.registers.ClassicalRegister, " + "expected_value: int = 1, comparison_kind: " + "mqt.core.ir.operations.ComparisonKind = ..., params: " + "collections.abc.Sequence[mqt.core.ir.symbolic.Expression | float] " + "= ...) -> None"), R"pb(Add an if operation to the circuit. Args: @@ -2253,16 +2189,23 @@ This method is equivalent to calling :meth:`measure` multiple times. comparison_kind: The kind of comparison to perform params: The parameters of the operation.)pb"); - qc.def("if_", - nb::overload_cast&>( - &qc::QuantumComputation::if_), - "op_type"_a, "target"_a, "control"_a, "control_bit"_a, - "expected_value"_a = true, - "comparison_kind"_a = qc::ComparisonKind::Eq, - "params"_a.sig("...") = std::vector{}, - R"pb(Add an if operation to the circuit. + qc.def( + "if_", + nb::overload_cast&>( + &qc::QuantumComputation::if_), + "op_type"_a, "target"_a, "control"_a, "control_bit"_a, + "expected_value"_a = true, "comparison_kind"_a = qc::ComparisonKind::Eq, + "params"_a = std::vector{}, + nb::sig("def if_(self, op_type: mqt.core.ir.operations.OpType, target: " + "int, control: mqt.core.ir.operations.Control | int, " + "control_bit: int, expected_value: bool = True, " + "comparison_kind: " + "mqt.core.ir.operations.ComparisonKind = ..., params: " + "collections.abc.Sequence[mqt.core.ir.symbolic.Expression | " + "float] = ...) -> None"), + R"pb(Add an if operation to the circuit. Args: op_type: The operation to apply @@ -2281,7 +2224,15 @@ This method is equivalent to calling :meth:`measure` multiple times. &qc::QuantumComputation::if_), "op_type"_a, "target"_a, "controls"_a, "control_bit"_a, "expected_value"_a = true, "comparison_kind"_a = qc::ComparisonKind::Eq, - "params"_a.sig("...") = std::vector{}, + "params"_a = std::vector{}, + nb::sig( + "def if_(self, op_type: mqt.core.ir.operations.OpType, target: " + "int, controls: collections.abc.Set[mqt.core.ir.operations.Control " + "| int], control_bit: int, expected_value: bool = True, " + "comparison_kind: " + "mqt.core.ir.operations.ComparisonKind = ..., params: " + "collections.abc.Sequence[mqt.core.ir.symbolic.Expression | " + "float] = ...) -> None"), R"pb(Add an if operation to the circuit. Args: diff --git a/python/mqt/core/ir/__init__.pyi b/python/mqt/core/ir/__init__.pyi index a4c40594ed..a27e6b8052 100644 --- a/python/mqt/core/ir/__init__.pyi +++ b/python/mqt/core/ir/__init__.pyi @@ -6,6 +6,7 @@ # # Licensed under the MIT License +import os from collections.abc import ItemsView, Iterable, Iterator, Mapping, MutableMapping, MutableSequence, Sequence from collections.abc import Set as AbstractSet from typing import overload @@ -364,9 +365,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): This is a permutation of the qubits in the quantum computation. It is mainly used to track where individual qubits end up at the end of the quantum computation, for example after a circuit has been compiled to a specific device and SWAP gates have been inserted, which permute the qubits. - The keys are the qubits in the circuit and the values are the actual qubits being measured. - - Similar to the initial layout, the keys in the output permutation are the qubits actually present in the circuit and the values are the qubits in the "original" circuit. + Similar to the initial layout, the keys are the qubits in the circuit and the values are the qubits in the "original" circuit. Examples: - If no output permutation is explicitly specified and the circuit does not contain measurements at the end, the output permutation is assumed to be the identity permutation. @@ -497,7 +496,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): The OpenQASM2 representation of the quantum computation as a string. """ - def qasm2(self, filename: str) -> None: + def qasm2(self, filename: os.PathLike[str] | str) -> None: """Write the OpenQASM2 representation of the quantum computation to a file. See Also: @@ -514,7 +513,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): The OpenQASM3 representation of the quantum computation as a string. """ - def qasm3(self, filename: str) -> None: + def qasm3(self, filename: os.PathLike[str] | str) -> None: """Write the OpenQASM3 representation of the quantum computation to a file. See Also: @@ -534,7 +533,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): q: The target qubit """ - def ci(self, control: operations.Control, target: int) -> None: + def ci(self, control: operations.Control | int, target: int) -> None: """Apply a controlled identity operation. See Also: @@ -545,7 +544,7 @@ class QuantumComputation(MutableSequence[operations.Operation]): target: The target qubit """ - def mci(self, controls: AbstractSet[operations.Control], target: int) -> None: + def mci(self, controls: AbstractSet[operations.Control | int], target: int) -> None: """Apply a multi-controlled identity operation. See Also: @@ -1960,11 +1959,11 @@ class QuantumComputation(MutableSequence[operations.Operation]): self, op_type: operations.OpType, target: int, - control: operations.Control, + control: operations.Control | int, control_register: registers.ClassicalRegister, expected_value: int = 1, comparison_kind: operations.ComparisonKind = ..., - params: Sequence[float] = ..., + params: Sequence[symbolic.Expression | float] = ..., ) -> None: """Add an if operation to the circuit. @@ -1983,11 +1982,11 @@ class QuantumComputation(MutableSequence[operations.Operation]): self, op_type: operations.OpType, target: int, - controls: AbstractSet[operations.Control], + controls: AbstractSet[operations.Control | int], control_register: registers.ClassicalRegister, expected_value: int = 1, comparison_kind: operations.ComparisonKind = ..., - params: Sequence[float] = ..., + params: Sequence[symbolic.Expression | float] = ..., ) -> None: """Add an if operation to the circuit. @@ -2027,11 +2026,11 @@ class QuantumComputation(MutableSequence[operations.Operation]): self, op_type: operations.OpType, target: int, - control: operations.Control, + control: operations.Control | int, control_bit: int, expected_value: bool = True, comparison_kind: operations.ComparisonKind = ..., - params: Sequence[float] = ..., + params: Sequence[symbolic.Expression | float] = ..., ) -> None: """Add an if operation to the circuit. @@ -2050,11 +2049,11 @@ class QuantumComputation(MutableSequence[operations.Operation]): self, op_type: operations.OpType, target: int, - controls: AbstractSet[operations.Control], + controls: AbstractSet[operations.Control | int], control_bit: int, expected_value: bool = True, comparison_kind: operations.ComparisonKind = ..., - params: Sequence[float] = ..., + params: Sequence[symbolic.Expression | float] = ..., ) -> None: """Add an if operation to the circuit. From 5efd34c7ff134f8a73d9591f39b4838ed4c9e38d Mon Sep 17 00:00:00 2001 From: burgholzer Date: Mon, 22 Dec 2025 01:54:54 +0100 Subject: [PATCH 139/144] =?UTF-8?q?=F0=9F=91=8C=20address=20CodeRabbit=20c?= =?UTF-8?q?omments=20and=20linter=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- bindings/dd/register_dd.cpp | 2 -- bindings/ir/register_permutation.cpp | 15 ++++++++++++--- bindings/ir/register_quantum_computation.cpp | 3 --- python/mqt/core/dd.pyi | 2 -- src/qir/runtime/Runtime.cpp | 3 ++- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/bindings/dd/register_dd.cpp b/bindings/dd/register_dd.cpp index ca24d6312d..64815fed6a 100644 --- a/bindings/dd/register_dd.cpp +++ b/bindings/dd/register_dd.cpp @@ -100,7 +100,6 @@ Consider using the :func:`~mqt.core.dd.simulate` or the :func:`~mqt.core.dd.samp Notes: This function internally constructs a :class:`~mqt.core.dd.DDPackage`, creates the zero state, and simulates the quantum computation via the :func:`simulate` function. The state vector is then extracted from the resulting DD via the :meth:`~mqt.core.dd.VectorDD.get_vector` method. - The resulting :class:`~mqt.core.dd.Vector` can be converted to a NumPy array without copying the data by calling :func:`numpy.array` with the `copy=False` argument. Args: qc: The quantum computation. Must only contain unitary operations. @@ -128,7 +127,6 @@ Consider using the :func:`~mqt.core.dd.build_functionality` function, which neve Notes: This function internally constructs a :class:`~mqt.core.dd.DDPackage`, creates the identity matrix, and builds the unitary matrix via the :func:`~mqt.core.dd.build_functionality` function. The unitary matrix is then extracted from the resulting DD via the :meth:`~mqt.core.dd.MatrixDD.get_matrix` method. - The resulting :class:`~mqt.core.dd.Matrix` can be converted to a NumPy array without copying the data by calling :func:`numpy.array` with the `copy=False` argument. Args: qc: The quantum computation. Must only contain unitary operations. diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index 759a922b6d..e14cb23576 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -22,10 +22,8 @@ #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) -#include #include #include -#include namespace mqt { @@ -126,7 +124,18 @@ void registerPermutation(const nb::module_& m) { .def( "__delitem__", - [](qc::Permutation& p, const qc::Qubit q) { p.erase(q); }, "index"_a, + [](qc::Permutation& p, const qc::Qubit q) { + const auto it = p.find(q); + if (it == p.end()) { + // Match Python's KeyError semantics for missing keys. + const auto msg = + std::string("Permutation does not contain index ") + + std::to_string(q); + throw nb::key_error(msg.c_str()); + } + p.erase(it); + }, + "index"_a, R"pb(Delete the value of the permutation at the given index. Args: diff --git a/bindings/ir/register_quantum_computation.cpp b/bindings/ir/register_quantum_computation.cpp index 92d89cbee3..e1a2b3f439 100644 --- a/bindings/ir/register_quantum_computation.cpp +++ b/bindings/ir/register_quantum_computation.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include // NOLINT(misc-include-cleaner) @@ -32,11 +31,9 @@ #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) -#include #include #include #include -#include #include namespace mqt { diff --git a/python/mqt/core/dd.pyi b/python/mqt/core/dd.pyi index 85333df8f6..05dc211576 100644 --- a/python/mqt/core/dd.pyi +++ b/python/mqt/core/dd.pyi @@ -835,7 +835,6 @@ def simulate_statevector(qc: mqt.core.ir.QuantumComputation) -> Annotated[NDArra Notes: This function internally constructs a :class:`~mqt.core.dd.DDPackage`, creates the zero state, and simulates the quantum computation via the :func:`simulate` function. The state vector is then extracted from the resulting DD via the :meth:`~mqt.core.dd.VectorDD.get_vector` method. - The resulting :class:`~mqt.core.dd.Vector` can be converted to a NumPy array without copying the data by calling :func:`numpy.array` with the `copy=False` argument. Args: qc: The quantum computation. Must only contain unitary operations. @@ -858,7 +857,6 @@ def build_unitary( Notes: This function internally constructs a :class:`~mqt.core.dd.DDPackage`, creates the identity matrix, and builds the unitary matrix via the :func:`~mqt.core.dd.build_functionality` function. The unitary matrix is then extracted from the resulting DD via the :meth:`~mqt.core.dd.MatrixDD.get_matrix` method. - The resulting :class:`~mqt.core.dd.Matrix` can be converted to a NumPy array without copying the data by calling :func:`numpy.array` with the `copy=False` argument. Args: qc: The quantum computation. Must only contain unitary operations. diff --git a/src/qir/runtime/Runtime.cpp b/src/qir/runtime/Runtime.cpp index ea9eebabb8..b9ddd51d86 100644 --- a/src/qir/runtime/Runtime.cpp +++ b/src/qir/runtime/Runtime.cpp @@ -10,6 +10,7 @@ #include "qir/runtime/Runtime.hpp" +#include "dd/DDDefinitions.hpp" #include "dd/Node.hpp" #include "dd/Package.hpp" #include "dd/StateGeneration.hpp" @@ -99,7 +100,7 @@ auto Runtime::enlargeState(const std::uint64_t maxQubit) -> void { // if the state is terminal, we need to create a new node if (qState.isTerminal()) { - qState = dd::makeZeroState(d, *dd); + qState = makeZeroState(d, *dd); return; } From cf79be359b8a604ef01927e94310ae0c3e8e860e Mon Sep 17 00:00:00 2001 From: burgholzer Date: Mon, 22 Dec 2025 01:58:33 +0100 Subject: [PATCH 140/144] =?UTF-8?q?=F0=9F=91=A5=20add=20to=20changelog=20e?= =?UTF-8?q?ntry=20for=20nanobind=20migration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bd5546f9a..ee03fb410f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel ### Changed -- ♻️ Migrate Python bindings from `pybind11` to `nanobind` ([#1383]) ([**@denialhaag**]) +- ♻️ Migrate Python bindings from `pybind11` to `nanobind` ([#1383]) ([**@denialhaag**], [**@burgholzer**]) - 📦️ Provide Stable ABI wheels for Python 3.12+ ([#1383]) ([**@burgholzer**], [**@denialhaag**]) - 🚚 create dedicated `mqt.core.na` submodule to closely follow the structure of other submodules ([#1383]) ([**@burgholzer**]) - ✨ Add common definitions and utilities for QDMI ([#1355]) ([**@burgholzer**]) From 31c84452e4a0257d8e247025178deffa85e5ccd3 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Mon, 22 Dec 2025 02:16:10 +0100 Subject: [PATCH 141/144] =?UTF-8?q?=F0=9F=91=8C=20address=20further=20Code?= =?UTF-8?q?Rabbit=20comments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- CHANGELOG.md | 2 +- bindings/ir/register_permutation.cpp | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee03fb410f..5519b06b6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel - ♻️ Migrate Python bindings from `pybind11` to `nanobind` ([#1383]) ([**@denialhaag**], [**@burgholzer**]) - 📦️ Provide Stable ABI wheels for Python 3.12+ ([#1383]) ([**@burgholzer**], [**@denialhaag**]) -- 🚚 create dedicated `mqt.core.na` submodule to closely follow the structure of other submodules ([#1383]) ([**@burgholzer**]) +- 🚚 Create dedicated `mqt.core.na` submodule to closely follow the structure of other submodules ([#1383]) ([**@burgholzer**]) - ✨ Add common definitions and utilities for QDMI ([#1355]) ([**@burgholzer**]) - 🚚 Move `NA` QDMI device in its right place next to other QDMI devices ([#1355]) ([**@burgholzer**]) - ♻️ Allow repeated loading of QDMI device library with potentially different session configurations ([#1355]) ([**@burgholzer**]) diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index e14cb23576..5990997ae6 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -101,7 +101,10 @@ void registerPermutation(const nb::module_& m) { .def( "__getitem__", - [](const qc::Permutation& p, const qc::Qubit q) { return p.at(q); }, + [](const qc::Permutation& p, const nb::int_& index) { + const auto q = nbIntToQubit(index); + return p.at(q); + }, "index"_a, R"pb(Get the value of the permutation at the given index. Args: @@ -112,7 +115,9 @@ void registerPermutation(const nb::module_& m) { .def( "__setitem__", - [](qc::Permutation& p, const qc::Qubit q, const qc::Qubit r) { + [](qc::Permutation& p, const nb::int_& index, const nb::int_& value) { + const auto q = nbIntToQubit(index); + const auto r = nbIntToQubit(value); p[q] = r; }, "index"_a, "value"_a, @@ -124,7 +129,8 @@ void registerPermutation(const nb::module_& m) { .def( "__delitem__", - [](qc::Permutation& p, const qc::Qubit q) { + [](qc::Permutation& p, const nb::int_& index) { + const auto q = nbIntToQubit(index); const auto it = p.find(q); if (it == p.end()) { // Match Python's KeyError semantics for missing keys. From 0e7be5d911103d963dfcb0b33e08728fced77fcc Mon Sep 17 00:00:00 2001 From: burgholzer Date: Mon, 22 Dec 2025 02:16:44 +0100 Subject: [PATCH 142/144] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor=20control?= =?UTF-8?q?=20type=20handling=20in=20bindings=20for=20improved=20simplicit?= =?UTF-8?q?y=20and=20clarity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- bindings/dd/register_dd_package.cpp | 76 ++++++++++++----------------- 1 file changed, 32 insertions(+), 44 deletions(-) diff --git a/bindings/dd/register_dd_package.cpp b/bindings/dd/register_dd_package.cpp index c6ec926510..4b43e4b997 100644 --- a/bindings/dd/register_dd_package.cpp +++ b/bindings/dd/register_dd_package.cpp @@ -33,13 +33,10 @@ #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) -#include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) #include -#include #include #include -#include #include namespace mqt { @@ -54,9 +51,6 @@ using SingleQubitMatrix = using TwoQubitMatrix = nb::ndarray, nb::shape<4, 4>>; -using Control = std::variant; -using Controls = std::set; - namespace { /// Recursive helper function to create a vector DD from a numpy array @@ -103,33 +97,6 @@ dd::mCachedEdge makeDDFromMatrix(dd::Package& p, const Matrix& m, makeDDFromMatrix(p, m, rowHalf, rowEnd, colStart, colHalf, level - 1), makeDDFromMatrix(p, m, rowHalf, rowEnd, colHalf, colEnd, level - 1)}); } - -/// Helper function to convert Control variant to qc::Control -qc::Control getControl(const Control& control) { - if (std::holds_alternative(control)) { - return std::get(control); - } - const auto controlInt = - static_cast(std::get(control)); - if (controlInt < 0) { - throw nb::value_error("Control qubit index cannot be negative"); - } - const auto controlUint = static_cast(controlInt); - if (controlUint > std::numeric_limits::max()) { - throw nb::value_error("Control qubit index exceeds maximum value"); - } - return static_cast(controlUint); -} - -/// Helper function to convert Controls variant to qc::Controls -qc::Controls getControls(const Controls& controls) { - qc::Controls result; - for (const auto& control : controls) { - result.insert(getControl(control)); - } - return result; -} - } // namespace // NOLINTNEXTLINE(misc-use-internal-linkage) @@ -493,14 +460,19 @@ Specifically, it dd.def( "controlled_single_qubit_gate", - [](dd::Package& p, const SingleQubitMatrix& mat, const Control& control, - const dd::Qubit target) { + [](dd::Package& p, const SingleQubitMatrix& mat, + const qc::Control& control, const dd::Qubit target) { return p.makeGateDD({mat(0, 0), mat(0, 1), mat(1, 0), mat(1, 1)}, - getControl(control), target); + control, target); }, "matrix"_a, "control"_a, "target"_a, // keep the DD package alive while the returned matrix DD is alive. nb::keep_alive<0, 1>(), + nb::sig( + "def controlled_single_qubit_gate(self, " + "matrix: Annotated[ANDArray[numpy.complex128], {\"shape\": (2, 2)}]," + "control: mqt.core.ir.operations.Control | int," + "target: int) -> mqt.core.dd.MatrixDD"), R"pb(Create the DD for a controlled single-qubit gate. Args: @@ -513,14 +485,19 @@ Specifically, it dd.def( "multi_controlled_single_qubit_gate", - [](dd::Package& p, const SingleQubitMatrix& mat, const Controls& controls, - const dd::Qubit target) { + [](dd::Package& p, const SingleQubitMatrix& mat, + const qc::Controls& controls, const dd::Qubit target) { return p.makeGateDD({mat(0, 0), mat(0, 1), mat(1, 0), mat(1, 1)}, - getControls(controls), target); + controls, target); }, "matrix"_a, "controls"_a, "target"_a, // keep the DD package alive while the returned matrix DD is alive. nb::keep_alive<0, 1>(), + nb::sig( + "def multi_controlled_single_qubit_gate(self, " + "matrix: Annotated[ANDArray[numpy.complex128], {\"shape\": (2, 2)}]," + "controls: collections.abc.Set[mqt.core.ir.operations.Control | int]," + "target: int) -> mqt.core.dd.MatrixDD"), R"pb(Create the DD for a multi-controlled single-qubit gate. Args: @@ -556,18 +533,23 @@ Specifically, it dd.def( "controlled_two_qubit_gate", - [](dd::Package& p, const TwoQubitMatrix& mat, const Control& control, + [](dd::Package& p, const TwoQubitMatrix& mat, const qc::Control& control, const dd::Qubit target0, const dd::Qubit target1) { return p.makeTwoQubitGateDD( {std::array{mat(0, 0), mat(0, 1), mat(0, 2), mat(0, 3)}, {mat(1, 0), mat(1, 1), mat(1, 2), mat(1, 3)}, {mat(2, 0), mat(2, 1), mat(2, 2), mat(2, 3)}, {mat(3, 0), mat(3, 1), mat(3, 2), mat(3, 3)}}, - getControl(control), target0, target1); + control, target0, target1); }, "matrix"_a, "control"_a, "target0"_a, "target1"_a, // keep the DD package alive while the returned matrix DD is alive. nb::keep_alive<0, 1>(), + nb::sig( + "def controlled_two_qubit_gate(self, " + "matrix: Annotated[ANDArray[numpy.complex128], {\"shape\": (4, 4)}]," + "control: mqt.core.ir.operations.Control | int," + "target0: int, target1: int) -> mqt.core.dd.MatrixDD"), R"pb(Create the DD for a controlled two-qubit gate. Args: @@ -581,18 +563,24 @@ Specifically, it dd.def( "multi_controlled_two_qubit_gate", - [](dd::Package& p, const TwoQubitMatrix& mat, const Controls& controls, - const dd::Qubit target0, const dd::Qubit target1) { + [](dd::Package& p, const TwoQubitMatrix& mat, + const qc::Controls& controls, const dd::Qubit target0, + const dd::Qubit target1) { return p.makeTwoQubitGateDD( {std::array{mat(0, 0), mat(0, 1), mat(0, 2), mat(0, 3)}, {mat(1, 0), mat(1, 1), mat(1, 2), mat(1, 3)}, {mat(2, 0), mat(2, 1), mat(2, 2), mat(2, 3)}, {mat(3, 0), mat(3, 1), mat(3, 2), mat(3, 3)}}, - getControls(controls), target0, target1); + controls, target0, target1); }, "matrix"_a, "controls"_a, "target0"_a, "target1"_a, // keep the DD package alive while the returned matrix DD is alive. nb::keep_alive<0, 1>(), + nb::sig( + "def multi_controlled_two_qubit_gate(self, " + "matrix: Annotated[ANDArray[numpy.complex128], {\"shape\": (4, 4)}]," + "controls: collections.abc.Set[mqt.core.ir.operations.Control | int]," + "target0: int, target1: int) -> mqt.core.dd.MatrixDD"), R"pb(Create the DD for a multi-controlled two-qubit gate. Args: From 07206d3461bcd8a5fd1c0321d93833d65abd4581 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Mon, 22 Dec 2025 02:38:51 +0100 Subject: [PATCH 143/144] =?UTF-8?q?=F0=9F=91=8C=20next=20batch=20of=20Code?= =?UTF-8?q?Rabbit=20comments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- bindings/dd/register_dd_package.cpp | 13 ++++++++----- bindings/ir/register_permutation.cpp | 9 ++++++++- pyproject.toml | 1 + python/mqt/core/dd.pyi | 4 ++++ 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/bindings/dd/register_dd_package.cpp b/bindings/dd/register_dd_package.cpp index 4b43e4b997..49a4c1aacb 100644 --- a/bindings/dd/register_dd_package.cpp +++ b/bindings/dd/register_dd_package.cpp @@ -14,7 +14,6 @@ #include "dd/Operations.hpp" #include "dd/Package.hpp" #include "dd/StateGeneration.hpp" -#include "ir/Definitions.hpp" #include "ir/Permutation.hpp" #include "ir/operations/Control.hpp" #include "ir/operations/IfElseOperation.hpp" @@ -117,6 +116,7 @@ Specifically, it Notes: It is undefined behavior to pass VectorDD or MatrixDD objects that were created with a different DDPackage to the methods of the DDPackage. + The only exception is the identity DD returned by identity(), which represents the global one-terminal and can be used with any DDPackage instance. Args: num_qubits: The maximum number of qubits that the DDPackage can handle. @@ -438,6 +438,9 @@ Specifically, it dd.def_static("identity", &dd::Package::makeIdent, R"pb(Create the DD for the identity matrix :math:`I`. +Notes: + Returns the global one-terminal (identity matrix), which is package-agnostic and safe to use across DDPackage instances. + Returns: The DD for the identity matrix.)pb"); @@ -470,7 +473,7 @@ Specifically, it nb::keep_alive<0, 1>(), nb::sig( "def controlled_single_qubit_gate(self, " - "matrix: Annotated[ANDArray[numpy.complex128], {\"shape\": (2, 2)}]," + "matrix: Annotated[NDArray[numpy.complex128], {\"shape\": (2, 2)}]," "control: mqt.core.ir.operations.Control | int," "target: int) -> mqt.core.dd.MatrixDD"), R"pb(Create the DD for a controlled single-qubit gate. @@ -495,7 +498,7 @@ Specifically, it nb::keep_alive<0, 1>(), nb::sig( "def multi_controlled_single_qubit_gate(self, " - "matrix: Annotated[ANDArray[numpy.complex128], {\"shape\": (2, 2)}]," + "matrix: Annotated[NDArray[numpy.complex128], {\"shape\": (2, 2)}]," "controls: collections.abc.Set[mqt.core.ir.operations.Control | int]," "target: int) -> mqt.core.dd.MatrixDD"), R"pb(Create the DD for a multi-controlled single-qubit gate. @@ -547,7 +550,7 @@ Specifically, it nb::keep_alive<0, 1>(), nb::sig( "def controlled_two_qubit_gate(self, " - "matrix: Annotated[ANDArray[numpy.complex128], {\"shape\": (4, 4)}]," + "matrix: Annotated[NDArray[numpy.complex128], {\"shape\": (4, 4)}]," "control: mqt.core.ir.operations.Control | int," "target0: int, target1: int) -> mqt.core.dd.MatrixDD"), R"pb(Create the DD for a controlled two-qubit gate. @@ -578,7 +581,7 @@ Specifically, it nb::keep_alive<0, 1>(), nb::sig( "def multi_controlled_two_qubit_gate(self, " - "matrix: Annotated[ANDArray[numpy.complex128], {\"shape\": (4, 4)}]," + "matrix: Annotated[NDArray[numpy.complex128], {\"shape\": (4, 4)}]," "controls: collections.abc.Set[mqt.core.ir.operations.Control | int]," "target0: int, target1: int) -> mqt.core.dd.MatrixDD"), R"pb(Create the DD for a multi-controlled two-qubit gate. diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index 5990997ae6..89240e298a 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -103,7 +103,14 @@ void registerPermutation(const nb::module_& m) { "__getitem__", [](const qc::Permutation& p, const nb::int_& index) { const auto q = nbIntToQubit(index); - return p.at(q); + const auto it = p.find(q); + if (it == p.end()) { + const auto msg = + std::string("Permutation does not contain index ") + + std::to_string(q); + throw nb::key_error(msg.c_str()); + } + return it->second; }, "index"_a, R"pb(Get the value of the permutation at the given index. diff --git a/pyproject.toml b/pyproject.toml index 77eb7f466e..f94599ace7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -262,6 +262,7 @@ anc = "anc" mch = "mch" ket = "ket" optin = "optin" +nd = "nd" [tool.repo-review] diff --git a/python/mqt/core/dd.pyi b/python/mqt/core/dd.pyi index 05dc211576..3a8f6b9ab9 100644 --- a/python/mqt/core/dd.pyi +++ b/python/mqt/core/dd.pyi @@ -211,6 +211,7 @@ class DDPackage: Notes: It is undefined behavior to pass VectorDD or MatrixDD objects that were created with a different DDPackage to the methods of the DDPackage. + The only exception is the identity DD returned by identity(), which represents the global one-terminal and can be used with any DDPackage instance. Args: num_qubits: The maximum number of qubits that the DDPackage can handle. @@ -433,6 +434,9 @@ class DDPackage: def identity() -> MatrixDD: """Create the DD for the identity matrix :math:`I`. + Notes: + Returns the global one-terminal (identity matrix), which is package-agnostic and safe to use across DDPackage instances. + Returns: The DD for the identity matrix. """ From 5f7c1f4c1f7fa8652c7dd0872418973cc771f929 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Mon, 22 Dec 2025 08:34:37 +0100 Subject: [PATCH 144/144] =?UTF-8?q?=F0=9F=9A=A8=20fix=20clang-tidy=20warni?= =?UTF-8?q?ngs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- bindings/dd/register_dd_package.cpp | 2 -- bindings/ir/register_permutation.cpp | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/bindings/dd/register_dd_package.cpp b/bindings/dd/register_dd_package.cpp index 49a4c1aacb..a25d942c6f 100644 --- a/bindings/dd/register_dd_package.cpp +++ b/bindings/dd/register_dd_package.cpp @@ -24,8 +24,6 @@ #include #include #include -#include -#include #include #include #include // NOLINT(misc-include-cleaner) diff --git a/bindings/ir/register_permutation.cpp b/bindings/ir/register_permutation.cpp index 89240e298a..a7d6760b69 100644 --- a/bindings/ir/register_permutation.cpp +++ b/bindings/ir/register_permutation.cpp @@ -23,6 +23,7 @@ #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) #include +#include #include namespace mqt {