From 2438e1b7872a0c8feea504e7975818e670996e31 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 11 Mar 2026 12:06:22 +0100 Subject: [PATCH 01/29] :sparkles: Add DLPack-compatible graph tensor export functionality --- python/aigverse/networks.pyi | 60 +++++ src/aigverse/networks/graph_tensors.hpp | 268 +++++++++++++++++++++++ src/aigverse/networks/logic_networks.cpp | 64 ++++++ 3 files changed, 392 insertions(+) create mode 100644 src/aigverse/networks/graph_tensors.hpp diff --git a/python/aigverse/networks.pyi b/python/aigverse/networks.pyi index a208ff19..473d40f2 100644 --- a/python/aigverse/networks.pyi +++ b/python/aigverse/networks.pyi @@ -4,9 +4,32 @@ This module includes network types, edge and index list utilities, and helper objects for structural manipulation. """ +import enum from collections.abc import Iterator, Sequence from typing import NoReturn, overload +class GraphTensorEncoding(enum.Enum): + """Encoding mode for exported graph tensors. + + This enum controls how categorical edge and node type features are represented + in the exported tensors. The types will be `float32` for all modes, but the encoding scheme differs: + - `ZERO_ONE`: Categorical features are represented as binary indicators (0.0 or 1.0). + - `ONE_MINUS_ONE`: Categorical features are represented as +1.0 for regular and -1.0 for inverted (edge-only). + - `ONE_HOT`: Categorical features are represented as one-hot encoded vectors, where the dimension corresponds + to the number of categories (e.g., node types or edge types). + """ + + ZERO_ONE = 0 + """Labels are encoded as 0.0 (regular) and 1.0 (inverted).""" + + ONE_MINUS_ONE = 1 + """Labels are encoded as +1.0 (regular) and -1.0 (inverted).""" + + ONE_HOT = 2 + """ + Labels are encoded as one-hot vectors, where the dimension corresponds to the number of categories. + """ + class AigSignal: """Represents a signal in an AIG. @@ -271,6 +294,43 @@ class Aig: The corresponding index-list representation. """ + def to_graph_tensors( + self, + node_encoding: GraphTensorEncoding = ..., + edge_encoding: GraphTensorEncoding = ..., + include_level: bool = True, + include_fanout: bool = False, + include_truth_table: bool = False, + ) -> dict: + """Exports graph tensors for machine-learning workflows. + + Returns sparse graph topology and features as DLPack-compatible arrays. + + Edge encoding mapping: + - ``ZERO_ONE``: regular=0.0, inverted=1.0 + - ``ONE_MINUS_ONE``: regular=+1.0, inverted=-1.0 + - ``ONE_HOT``: regular=[1.0, 0.0], inverted=[0.0, 1.0] + + Node encoding mapping: + - ``ZERO_ONE``: constant=0, pi=1, gate=2, po=3 + - ``ONE_HOT``: [constant, pi, gate, po] + + Note: + ``ONE_MINUS_ONE`` is edge-only and cannot be used for ``node_encoding``. + + Args: + node_encoding: Node encoding mode as :class:`~aigverse.networks.GraphTensorEncoding`. + edge_encoding: Edge encoding mode as :class:`~aigverse.networks.GraphTensorEncoding`. + include_level: Appends logic level as a node feature. + include_fanout: Appends fanout size as a node feature. + include_truth_table: Appends simulated node/output truth-table bits. + + Returns: + A dictionary with ``edge_index`` (shape ``(2, E)``, dtype ``int64``), + ``edge_attr`` (shape ``(E, D_edge)``, dtype ``float32``), and ``node_attr`` + (shape ``(N, D_node)``, dtype ``float32``). + """ + def __len__(self) -> int: """Returns the number of nodes.""" diff --git a/src/aigverse/networks/graph_tensors.hpp b/src/aigverse/networks/graph_tensors.hpp new file mode 100644 index 00000000..3bde94cc --- /dev/null +++ b/src/aigverse/networks/graph_tensors.hpp @@ -0,0 +1,268 @@ +#pragma once + +#include "edge_list.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace aigverse +{ + +/** + * @brief Encoding mode for exported graph tensors. + * + * This enum controls how categorical edge and node type features are represented + * in the exported tensors. The types will be `float32` for all modes, but the encoding scheme differs: + * - `ZERO_ONE`: Categorical features are represented as binary indicators (0.0 or 1.0). + * - `ONE_MINUS_ONE`: Categorical features are represented as +1.0 for regular and -1.0 for inverted (edge-only). + * - `ONE_HOT`: Categorical features are represented as one-hot encoded vectors, where the dimension corresponds to the + * number of categories (e.g., node types or edge types). + */ +enum class graph_tensor_encoding : uint8_t +{ + /// Labels are encoded as 0.0 (regular) and 1.0 (inverted). + ZERO_ONE, + /// Labels are encoded as +1.0 (regular) and -1.0 (inverted). + ONE_MINUS_ONE, + /// Labels are encoded as one-hot vectors, where the dimension corresponds to the number of categories (e.g., node + /// types or edge types). + ONE_HOT, +}; + +namespace detail +{ + +/** + * @brief Creates an owning NumPy-backed nanobind ndarray from a moved vector. + * + * The returned array keeps data alive via a capsule-owned shared pointer, making + * it safe to consume through DLPack in downstream frameworks. + * + * @tparam T Element type. + * @param data Moved data buffer. + * @param shape Target tensor shape. + * @return NumPy-backed ndarray that owns the provided data. + */ +template +nanobind::ndarray make_owned_ndarray(std::vector&& data, + const std::initializer_list& shape) +{ + namespace nb = nanobind; + + auto storage = std::make_shared>(std::move(data)); + auto holder = std::make_unique>>(storage); + nb::capsule owner(holder.get(), + [](void* ptr) noexcept + { + delete static_cast>*>(ptr); + }); // TODO does this really need to call delete? Why not simply let the smart pointer handle it? + holder.release(); + + return nb::ndarray(storage->data(), shape, owner); +} +/** + * @brief Exports an AIG-style network to sparse COO-like graph tensors. + * + * The result dictionary contains: + * - ``edge_index`` with shape ``(2, E)`` + * - ``edge_attr`` with shape ``(E, D_edge)`` + * - ``node_attr`` with shape ``(N, D_node)`` + * + * All returned tensors are NumPy-backed ndarrays and can be consumed by DLPack + * consumers (e.g., PyTorch via ``torch.from_dlpack``). + * + * @tparam Ntk Network type. + * @param ntk Input network. + * @param node_encoding Node-type encoding mode. + * @param edge_encoding Edge-type encoding mode. + * @param include_level Whether to append depth-based level features. + * @param include_fanout Whether to append fanout-size features. + * @param include_truth_table Whether to append truth-table bits. + * @return Dictionary of exported tensors. + */ +template +nanobind::dict to_graph_tensors(const Ntk& ntk, const graph_tensor_encoding node_encoding, + const graph_tensor_encoding edge_encoding, const bool include_level, + const bool include_fanout, const bool include_truth_table) +{ + namespace nb = nanobind; + + constexpr size_t node_type_one_hot_dim = 4; + + constexpr int64_t type_constant = 0; + constexpr int64_t type_pi = 1; + constexpr int64_t type_gate = 2; + constexpr int64_t type_po = 3; + + if (node_encoding == graph_tensor_encoding::ONE_MINUS_ONE) + { + throw std::invalid_argument("node_encoding only supports ZERO_ONE or ONE_HOT; ONE_MINUS_ONE is edge-only"); + } + + const auto edges = aigverse::to_edge_list(ntk).edges; + const auto edge_count = edges.size(); + + std::vector edge_index(2 * edge_count, 0); + for (size_t i = 0; i < edge_count; ++i) + { + edge_index[i] = static_cast(edges[i].source); + edge_index[edge_count + i] = static_cast(edges[i].target); + } + + const size_t edge_dim = edge_encoding == graph_tensor_encoding::ONE_HOT ? 2 : 1; + + std::vector edge_attr(edge_count * edge_dim, 0.0f); + for (size_t i = 0; i < edge_count; ++i) + { + const auto inverted = edges[i].weight != 0; + switch (edge_encoding) + { + case graph_tensor_encoding::ZERO_ONE: + { + edge_attr[i] = inverted ? 1.0f : 0.0f; + break; + } + case graph_tensor_encoding::ONE_MINUS_ONE: + { + edge_attr[i] = inverted ? -1.0f : 1.0f; + break; + } + case graph_tensor_encoding::ONE_HOT: + { + edge_attr[i * 2] = inverted ? 0.0f : 1.0f; + edge_attr[i * 2 + 1] = inverted ? 1.0f : 0.0f; + break; + } + } + } + + size_t tt_dim = 0; + + std::optional> node_tts{}; + std::vector output_tts{}; + if (include_truth_table) + { + node_tts = mockturtle::simulate_nodes( + ntk, mockturtle::default_simulator{static_cast(ntk.num_pis())}); + output_tts = mockturtle::simulate( + ntk, mockturtle::default_simulator{static_cast(ntk.num_pis())}); + + if (ntk.size() > 0) + { + tt_dim = node_tts.value()[ntk.get_constant(false)].num_bits(); + } + } + + const size_t base_dim = node_encoding == graph_tensor_encoding::ONE_HOT ? node_type_one_hot_dim : 1; + const size_t node_dim = + base_dim + (include_level ? 1 : 0) + (include_fanout ? 1 : 0) + (include_truth_table ? tt_dim : 0); + const size_t node_count = ntk.size() + ntk.num_pos(); + + std::vector node_attr(node_count * node_dim, 0.0f); + + std::optional> depth_ntk{}; + if (include_level) + { + depth_ntk.emplace(ntk); + } + const auto po_level = include_level ? static_cast(depth_ntk->depth() + 1) : 0.0f; + + const auto fill_base = [&](const size_t row, const int64_t type_index) -> size_t + { + const size_t offset = row * node_dim; + if (node_encoding == graph_tensor_encoding::ZERO_ONE) + { + node_attr[offset] = static_cast(type_index); + return offset + 1; + } + + node_attr[offset + static_cast(type_index)] = 1.0f; + return offset + node_type_one_hot_dim; + }; + + ntk.foreach_node( + [&](const auto& n) + { + const auto row = static_cast(ntk.node_to_index(n)); + int64_t type_index = type_gate; + if (ntk.is_constant(n)) + { + type_index = type_constant; + } + else if (ntk.is_pi(n)) + { + type_index = type_pi; + } + + auto feature_offset = fill_base(row, type_index); + + if (include_level) + { + node_attr[feature_offset++] = static_cast(depth_ntk->level(n)); + } + if (include_fanout) + { + node_attr[feature_offset++] = static_cast(ntk.fanout_size(n)); + } + if (include_truth_table) + { + const auto& tt = node_tts.value()[n]; + for (size_t i = 0; i < tt_dim; ++i) + { + node_attr[feature_offset + i] = kitty::get_bit(tt, static_cast(i)) ? 1.0f : 0.0f; + } + } + }); + + ntk.foreach_po( + [&](const auto& po) + { + const auto po_idx = static_cast(ntk.po_index(po)); + const auto row = static_cast(ntk.size()) + po_idx; + + auto feature_offset = fill_base(row, type_po); + + if (include_level) + { + node_attr[feature_offset++] = po_level; + } + if (include_fanout) + { + node_attr[feature_offset++] = 0.0f; + } + if (include_truth_table) + { + const auto& tt = output_tts[po_idx]; + for (size_t i = 0; i < tt_dim; ++i) + { + node_attr[feature_offset + i] = kitty::get_bit(tt, i) ? 1.0f : 0.0f; + } + } + }); + + auto result = nb::dict(); + + result["edge_index"] = make_owned_ndarray(std::move(edge_index), {2, edge_count}); + result["edge_attr"] = make_owned_ndarray(std::move(edge_attr), {edge_count, edge_dim}); + result["node_attr"] = make_owned_ndarray(std::move(node_attr), {node_count, node_dim}); + + return result; +} + +} // namespace detail + +} // namespace aigverse diff --git a/src/aigverse/networks/logic_networks.cpp b/src/aigverse/networks/logic_networks.cpp index 1b84ed94..264103f7 100644 --- a/src/aigverse/networks/logic_networks.cpp +++ b/src/aigverse/networks/logic_networks.cpp @@ -5,6 +5,7 @@ #include "aigverse/types.hpp" #include "edge_list.hpp" +#include "graph_tensors.hpp" #include "index_list.hpp" #include @@ -112,6 +113,28 @@ bool contains_node(const Ntk& ntk, const nanobind::object& value) } // namespace +void bind_graph_tensor_encoding(nanobind::module_& m) // NOLINT(misc-use-internal-linkage) +{ + namespace nb = nanobind; + + nb::enum_(m, "GraphTensorEncoding", + R"pb(Encoding mode for exported graph tensors. + +This enum controls how categorical edge and node type features are represented +in the exported tensors. The types will be `float32` for all modes, but the encoding scheme differs: +- `ZERO_ONE`: Categorical features are represented as binary indicators (0.0 or 1.0). +- `ONE_MINUS_ONE`: Categorical features are represented as +1.0 for regular and -1.0 for inverted (edge-only). +- `ONE_HOT`: Categorical features are represented as one-hot encoded vectors, where the dimension corresponds + to the number of categories (e.g., node types or edge types).)pb") + .value("ZERO_ONE", aigverse::graph_tensor_encoding::ZERO_ONE, + R"pb(Labels are encoded as 0.0 (regular) and 1.0 (inverted).)pb") + .value("ONE_MINUS_ONE", aigverse::graph_tensor_encoding::ONE_MINUS_ONE, + R"pb(Labels are encoded as +1.0 (regular) and -1.0 (inverted).)pb") + .value( + "ONE_HOT", aigverse::graph_tensor_encoding::ONE_HOT, + R"pb(Labels are encoded as one-hot vectors, where the dimension corresponds to the number of categories.)pb"); +} + template void bind_network(nanobind::module_& m, const std::string& network_name) // NOLINT(misc-use-internal-linkage) { @@ -355,6 +378,46 @@ void bind_network(nanobind::module_& m, const std::string& network_name) // NOL Returns: The corresponding index-list representation.)pb", nb::rv_policy::move) + .def( + "to_graph_tensors", + [](const Ntk& ntk, const aigverse::graph_tensor_encoding node_encoding, + const aigverse::graph_tensor_encoding edge_encoding, const bool include_level, const bool include_fanout, + const bool include_truth_table) + { + return aigverse::detail::to_graph_tensors(ntk, node_encoding, edge_encoding, include_level, + include_fanout, include_truth_table); + }, + nb::arg("node_encoding") = aigverse::graph_tensor_encoding::ONE_HOT, + nb::arg("edge_encoding") = aigverse::graph_tensor_encoding::ZERO_ONE, nb::arg("include_level") = true, + nb::arg("include_fanout") = false, nb::arg("include_truth_table") = false, + R"pb(Exports graph tensors for machine-learning workflows. + +Returns sparse graph topology and features as DLPack-compatible arrays. + +Edge encoding mapping: + - ``ZERO_ONE``: regular=0.0, inverted=1.0 + - ``ONE_MINUS_ONE``: regular=+1.0, inverted=-1.0 + - ``ONE_HOT``: regular=[1.0, 0.0], inverted=[0.0, 1.0] + +Node encoding mapping: + - ``ZERO_ONE``: constant=0, pi=1, gate=2, po=3 + - ``ONE_HOT``: [constant, pi, gate, po] + +Note: + ``ONE_MINUS_ONE`` is edge-only and cannot be used for ``node_encoding``. + +Args: + node_encoding: Node encoding mode as :class:`~aigverse.networks.GraphTensorEncoding`. + edge_encoding: Edge encoding mode as :class:`~aigverse.networks.GraphTensorEncoding`. + include_level: Appends logic level as a node feature. + include_fanout: Appends fanout size as a node feature. + include_truth_table: Appends simulated node/output truth-table bits. + +Returns: + A dictionary with ``edge_index`` (shape ``(2, E)``, dtype ``int64``), + ``edge_attr`` (shape ``(E, D_edge)``, dtype ``float32``), and ``node_attr`` + (shape ``(N, D_node)``, dtype ``float32``). +)pb") .def("__len__", &Ntk::size, R"pb(Returns the number of nodes.)pb") .def( "__repr__", @@ -710,6 +773,7 @@ template void bind_network(nanobind::module_&, const std::string& void bind_logic_networks(nanobind::module_& m) // NOLINT(misc-use-internal-linkage) { + detail::bind_graph_tensor_encoding(m); detail::bind_network(m, "Aig"); } From cda50b903da596319eace78ededd7972bfe25e02 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 11 Mar 2026 12:46:00 +0100 Subject: [PATCH 02/29] :recycle: Refactor tensor encoding enums for clarity and consistency --- python/aigverse/networks.pyi | 57 ++++++++++++---------- src/aigverse/networks/graph_tensors.hpp | 58 ++++++++++++---------- src/aigverse/networks/logic_networks.cpp | 61 +++++++++++++----------- 3 files changed, 100 insertions(+), 76 deletions(-) diff --git a/python/aigverse/networks.pyi b/python/aigverse/networks.pyi index 473d40f2..0af74bad 100644 --- a/python/aigverse/networks.pyi +++ b/python/aigverse/networks.pyi @@ -8,27 +8,39 @@ import enum from collections.abc import Iterator, Sequence from typing import NoReturn, overload -class GraphTensorEncoding(enum.Enum): - """Encoding mode for exported graph tensors. - - This enum controls how categorical edge and node type features are represented - in the exported tensors. The types will be `float32` for all modes, but the encoding scheme differs: - - `ZERO_ONE`: Categorical features are represented as binary indicators (0.0 or 1.0). - - `ONE_MINUS_ONE`: Categorical features are represented as +1.0 for regular and -1.0 for inverted (edge-only). - - `ONE_HOT`: Categorical features are represented as one-hot encoded vectors, where the dimension corresponds - to the number of categories (e.g., node types or edge types). +class NodeTensorEncoding(enum.Enum): + """Node encoding mode for exported graph tensors. + + All node features use `float32`; only the categorical encoding scheme changes. + - `INTEGER`: Node classes are scalar labels in the first feature column. + - `ONE_HOT`: Node classes are one-hot vectors in `[constant, pi, gate, po]` order. + """ + + INTEGER = 0 """ + Scalar node labels in the first feature column: 0=constant, 1=pi, 2=gate, 3=po. + """ + + ONE_HOT = 1 + """One-hot node labels in [constant, pi, gate, po] order.""" + +class EdgeTensorEncoding(enum.Enum): + """Edge encoding mode for exported graph tensors. - ZERO_ONE = 0 + All edge features use `float32`; only the categorical encoding scheme changes. + - `BINARY`: Edge polarity is binary (regular=0.0, inverted=1.0). + - `SIGNED`: Edge polarity is signed (regular=+1.0, inverted=-1.0). + - `ONE_HOT`: Edge polarity is one-hot in `[regular, inverted]` order. + """ + + BINARY = 0 """Labels are encoded as 0.0 (regular) and 1.0 (inverted).""" - ONE_MINUS_ONE = 1 + SIGNED = 1 """Labels are encoded as +1.0 (regular) and -1.0 (inverted).""" ONE_HOT = 2 - """ - Labels are encoded as one-hot vectors, where the dimension corresponds to the number of categories. - """ + """One-hot edge labels in [regular, inverted] order.""" class AigSignal: """Represents a signal in an AIG. @@ -296,8 +308,8 @@ class Aig: def to_graph_tensors( self, - node_encoding: GraphTensorEncoding = ..., - edge_encoding: GraphTensorEncoding = ..., + node_encoding: NodeTensorEncoding = ..., + edge_encoding: EdgeTensorEncoding = ..., include_level: bool = True, include_fanout: bool = False, include_truth_table: bool = False, @@ -307,20 +319,17 @@ class Aig: Returns sparse graph topology and features as DLPack-compatible arrays. Edge encoding mapping: - - ``ZERO_ONE``: regular=0.0, inverted=1.0 - - ``ONE_MINUS_ONE``: regular=+1.0, inverted=-1.0 + - ``BINARY``: regular=0.0, inverted=1.0 + - ``SIGNED``: regular=+1.0, inverted=-1.0 - ``ONE_HOT``: regular=[1.0, 0.0], inverted=[0.0, 1.0] Node encoding mapping: - - ``ZERO_ONE``: constant=0, pi=1, gate=2, po=3 + - ``INTEGER``: constant=0, pi=1, gate=2, po=3 - ``ONE_HOT``: [constant, pi, gate, po] - Note: - ``ONE_MINUS_ONE`` is edge-only and cannot be used for ``node_encoding``. - Args: - node_encoding: Node encoding mode as :class:`~aigverse.networks.GraphTensorEncoding`. - edge_encoding: Edge encoding mode as :class:`~aigverse.networks.GraphTensorEncoding`. + node_encoding: Node encoding mode as :class:`~aigverse.networks.NodeTensorEncoding`. + edge_encoding: Edge encoding mode as :class:`~aigverse.networks.EdgeTensorEncoding`. include_level: Appends logic level as a node feature. include_fanout: Appends fanout size as a node feature. include_truth_table: Appends simulated node/output truth-table bits. diff --git a/src/aigverse/networks/graph_tensors.hpp b/src/aigverse/networks/graph_tensors.hpp index 3bde94cc..45c0a0ef 100644 --- a/src/aigverse/networks/graph_tensors.hpp +++ b/src/aigverse/networks/graph_tensors.hpp @@ -23,23 +23,38 @@ namespace aigverse { /** - * @brief Encoding mode for exported graph tensors. + * @brief Node encoding mode for exported graph tensors. * - * This enum controls how categorical edge and node type features are represented + * This enum controls how categorical node type features are represented * in the exported tensors. The types will be `float32` for all modes, but the encoding scheme differs: - * - `ZERO_ONE`: Categorical features are represented as binary indicators (0.0 or 1.0). - * - `ONE_MINUS_ONE`: Categorical features are represented as +1.0 for regular and -1.0 for inverted (edge-only). - * - `ONE_HOT`: Categorical features are represented as one-hot encoded vectors, where the dimension corresponds to the - * number of categories (e.g., node types or edge types). + * - `INTEGER`: Node types are represented as integer class labels. + * - `ONE_HOT`: Node types are represented as one-hot encoded vectors, where the dimension corresponds to the number + * of node categories in the order `[constant, pi, gate, po]`. */ -enum class graph_tensor_encoding : uint8_t +enum class node_tensor_encoding : uint8_t +{ + /// Labels are encoded as 0.0 (constant), 1.0 (PI), 2.0 (gate), and 3.0 (PO). + INTEGER, + /// Labels are encoded as one-hot vectors in the order [constant, pi, gate, po]. + ONE_HOT, +}; + +/** + * @brief Edge encoding mode for exported graph tensors. + * + * This enum controls how categorical edge type features are represented + * in the exported tensors. The types will be `float32` for all modes, but the encoding scheme differs: + * - `BINARY`: Edge polarity is represented as binary indicators (0.0 or 1.0). + * - `SIGNED`: Edge polarity is represented as +1.0 for regular and -1.0 for inverted. + * - `ONE_HOT`: Edge polarity is represented as one-hot encoded vectors in the order `[regular, inverted]`. + */ +enum class edge_tensor_encoding : uint8_t { /// Labels are encoded as 0.0 (regular) and 1.0 (inverted). - ZERO_ONE, + BINARY, /// Labels are encoded as +1.0 (regular) and -1.0 (inverted). - ONE_MINUS_ONE, - /// Labels are encoded as one-hot vectors, where the dimension corresponds to the number of categories (e.g., node - /// types or edge types). + SIGNED, + /// Labels are encoded as one-hot vectors in the order [regular, inverted]. ONE_HOT, }; @@ -95,8 +110,8 @@ nanobind::ndarray make_owned_ndarray(std::vector&& * @return Dictionary of exported tensors. */ template -nanobind::dict to_graph_tensors(const Ntk& ntk, const graph_tensor_encoding node_encoding, - const graph_tensor_encoding edge_encoding, const bool include_level, +nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_encoding, + const edge_tensor_encoding edge_encoding, const bool include_level, const bool include_fanout, const bool include_truth_table) { namespace nb = nanobind; @@ -108,11 +123,6 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const graph_tensor_encoding node constexpr int64_t type_gate = 2; constexpr int64_t type_po = 3; - if (node_encoding == graph_tensor_encoding::ONE_MINUS_ONE) - { - throw std::invalid_argument("node_encoding only supports ZERO_ONE or ONE_HOT; ONE_MINUS_ONE is edge-only"); - } - const auto edges = aigverse::to_edge_list(ntk).edges; const auto edge_count = edges.size(); @@ -123,7 +133,7 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const graph_tensor_encoding node edge_index[edge_count + i] = static_cast(edges[i].target); } - const size_t edge_dim = edge_encoding == graph_tensor_encoding::ONE_HOT ? 2 : 1; + const size_t edge_dim = edge_encoding == edge_tensor_encoding::ONE_HOT ? 2 : 1; std::vector edge_attr(edge_count * edge_dim, 0.0f); for (size_t i = 0; i < edge_count; ++i) @@ -131,17 +141,17 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const graph_tensor_encoding node const auto inverted = edges[i].weight != 0; switch (edge_encoding) { - case graph_tensor_encoding::ZERO_ONE: + case edge_tensor_encoding::BINARY: { edge_attr[i] = inverted ? 1.0f : 0.0f; break; } - case graph_tensor_encoding::ONE_MINUS_ONE: + case edge_tensor_encoding::SIGNED: { edge_attr[i] = inverted ? -1.0f : 1.0f; break; } - case graph_tensor_encoding::ONE_HOT: + case edge_tensor_encoding::ONE_HOT: { edge_attr[i * 2] = inverted ? 0.0f : 1.0f; edge_attr[i * 2 + 1] = inverted ? 1.0f : 0.0f; @@ -167,7 +177,7 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const graph_tensor_encoding node } } - const size_t base_dim = node_encoding == graph_tensor_encoding::ONE_HOT ? node_type_one_hot_dim : 1; + const size_t base_dim = node_encoding == node_tensor_encoding::ONE_HOT ? node_type_one_hot_dim : 1; const size_t node_dim = base_dim + (include_level ? 1 : 0) + (include_fanout ? 1 : 0) + (include_truth_table ? tt_dim : 0); const size_t node_count = ntk.size() + ntk.num_pos(); @@ -184,7 +194,7 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const graph_tensor_encoding node const auto fill_base = [&](const size_t row, const int64_t type_index) -> size_t { const size_t offset = row * node_dim; - if (node_encoding == graph_tensor_encoding::ZERO_ONE) + if (node_encoding == node_tensor_encoding::INTEGER) { node_attr[offset] = static_cast(type_index); return offset + 1; diff --git a/src/aigverse/networks/logic_networks.cpp b/src/aigverse/networks/logic_networks.cpp index 264103f7..f096675f 100644 --- a/src/aigverse/networks/logic_networks.cpp +++ b/src/aigverse/networks/logic_networks.cpp @@ -113,26 +113,34 @@ bool contains_node(const Ntk& ntk, const nanobind::object& value) } // namespace -void bind_graph_tensor_encoding(nanobind::module_& m) // NOLINT(misc-use-internal-linkage) +void bind_tensor_encodings(nanobind::module_& m) // NOLINT(misc-use-internal-linkage) { namespace nb = nanobind; - nb::enum_(m, "GraphTensorEncoding", - R"pb(Encoding mode for exported graph tensors. - -This enum controls how categorical edge and node type features are represented -in the exported tensors. The types will be `float32` for all modes, but the encoding scheme differs: -- `ZERO_ONE`: Categorical features are represented as binary indicators (0.0 or 1.0). -- `ONE_MINUS_ONE`: Categorical features are represented as +1.0 for regular and -1.0 for inverted (edge-only). -- `ONE_HOT`: Categorical features are represented as one-hot encoded vectors, where the dimension corresponds - to the number of categories (e.g., node types or edge types).)pb") - .value("ZERO_ONE", aigverse::graph_tensor_encoding::ZERO_ONE, + nb::enum_(m, "NodeTensorEncoding", + R"pb(Node encoding mode for exported graph tensors. + + All node features use `float32`; only the categorical encoding scheme changes. + - `INTEGER`: Node classes are scalar labels in the first feature column. + - `ONE_HOT`: Node classes are one-hot vectors in `[constant, pi, gate, po]` order.)pb") + .value("INTEGER", aigverse::node_tensor_encoding::INTEGER, + R"pb(Scalar node labels in the first feature column: 0=constant, 1=pi, 2=gate, 3=po.)pb") + .value("ONE_HOT", aigverse::node_tensor_encoding::ONE_HOT, + R"pb(One-hot node labels in [constant, pi, gate, po] order.)pb"); + + nb::enum_(m, "EdgeTensorEncoding", + R"pb(Edge encoding mode for exported graph tensors. + + All edge features use `float32`; only the categorical encoding scheme changes. + - `BINARY`: Edge polarity is binary (regular=0.0, inverted=1.0). + - `SIGNED`: Edge polarity is signed (regular=+1.0, inverted=-1.0). + - `ONE_HOT`: Edge polarity is one-hot in `[regular, inverted]` order.)pb") + .value("BINARY", aigverse::edge_tensor_encoding::BINARY, R"pb(Labels are encoded as 0.0 (regular) and 1.0 (inverted).)pb") - .value("ONE_MINUS_ONE", aigverse::graph_tensor_encoding::ONE_MINUS_ONE, + .value("SIGNED", aigverse::edge_tensor_encoding::SIGNED, R"pb(Labels are encoded as +1.0 (regular) and -1.0 (inverted).)pb") - .value( - "ONE_HOT", aigverse::graph_tensor_encoding::ONE_HOT, - R"pb(Labels are encoded as one-hot vectors, where the dimension corresponds to the number of categories.)pb"); + .value("ONE_HOT", aigverse::edge_tensor_encoding::ONE_HOT, + R"pb(One-hot edge labels in [regular, inverted] order.)pb"); } template @@ -380,35 +388,32 @@ void bind_network(nanobind::module_& m, const std::string& network_name) // NOL nb::rv_policy::move) .def( "to_graph_tensors", - [](const Ntk& ntk, const aigverse::graph_tensor_encoding node_encoding, - const aigverse::graph_tensor_encoding edge_encoding, const bool include_level, const bool include_fanout, + [](const Ntk& ntk, const aigverse::node_tensor_encoding node_encoding, + const aigverse::edge_tensor_encoding edge_encoding, const bool include_level, const bool include_fanout, const bool include_truth_table) { return aigverse::detail::to_graph_tensors(ntk, node_encoding, edge_encoding, include_level, include_fanout, include_truth_table); }, - nb::arg("node_encoding") = aigverse::graph_tensor_encoding::ONE_HOT, - nb::arg("edge_encoding") = aigverse::graph_tensor_encoding::ZERO_ONE, nb::arg("include_level") = true, + nb::arg("node_encoding") = aigverse::node_tensor_encoding::INTEGER, + nb::arg("edge_encoding") = aigverse::edge_tensor_encoding::BINARY, nb::arg("include_level") = true, nb::arg("include_fanout") = false, nb::arg("include_truth_table") = false, R"pb(Exports graph tensors for machine-learning workflows. Returns sparse graph topology and features as DLPack-compatible arrays. Edge encoding mapping: - - ``ZERO_ONE``: regular=0.0, inverted=1.0 - - ``ONE_MINUS_ONE``: regular=+1.0, inverted=-1.0 + - ``BINARY``: regular=0.0, inverted=1.0 + - ``SIGNED``: regular=+1.0, inverted=-1.0 - ``ONE_HOT``: regular=[1.0, 0.0], inverted=[0.0, 1.0] Node encoding mapping: - - ``ZERO_ONE``: constant=0, pi=1, gate=2, po=3 + - ``INTEGER``: constant=0, pi=1, gate=2, po=3 - ``ONE_HOT``: [constant, pi, gate, po] -Note: - ``ONE_MINUS_ONE`` is edge-only and cannot be used for ``node_encoding``. - Args: - node_encoding: Node encoding mode as :class:`~aigverse.networks.GraphTensorEncoding`. - edge_encoding: Edge encoding mode as :class:`~aigverse.networks.GraphTensorEncoding`. + node_encoding: Node encoding mode as :class:`~aigverse.networks.NodeTensorEncoding`. + edge_encoding: Edge encoding mode as :class:`~aigverse.networks.EdgeTensorEncoding`. include_level: Appends logic level as a node feature. include_fanout: Appends fanout size as a node feature. include_truth_table: Appends simulated node/output truth-table bits. @@ -773,7 +778,7 @@ template void bind_network(nanobind::module_&, const std::string& void bind_logic_networks(nanobind::module_& m) // NOLINT(misc-use-internal-linkage) { - detail::bind_graph_tensor_encoding(m); + detail::bind_tensor_encodings(m); detail::bind_network(m, "Aig"); } From 13a3c627a0c296611f4e9c769de80fd5f83bcffe Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 11 Mar 2026 13:01:45 +0100 Subject: [PATCH 03/29] :white_check_mark: Add unit tests for graph tensor export functionality and encoding validations --- pyproject.toml | 3 +- test/networks/test_graph_tensors.py | 240 +++++++++++++++++++++++ uv.lock | 285 ++++++++++++++++++++++++++++ 3 files changed, 527 insertions(+), 1 deletion(-) create mode 100644 test/networks/test_graph_tensors.py diff --git a/pyproject.toml b/pyproject.toml index 4aeb2c1d..9988422b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,7 @@ requires-python = ">=3.10" dynamic = ["version"] [project.optional-dependencies] -adapters = ["networkx>=3.0.0", "numpy>=1.23.0"] +adapters = ["networkx>=3.0.0", "numpy>=1.23.0", "torch>=2.2.0"] [project.urls] Source = "https://github.com/marcelwa/aigverse" @@ -207,6 +207,7 @@ build = [ adapters = [ "networkx>=3.0.0", "numpy>=1.23.0", + "torch>=2.2.0", ] docs = [ { include-group = "adapters" }, diff --git a/test/networks/test_graph_tensors.py b/test/networks/test_graph_tensors.py new file mode 100644 index 00000000..e876b63e --- /dev/null +++ b/test/networks/test_graph_tensors.py @@ -0,0 +1,240 @@ +from __future__ import annotations + +import numpy as np +import pytest +import torch + +from aigverse.networks import Aig, EdgeTensorEncoding, NodeTensorEncoding + + +@pytest.fixture +def sample_aig() -> Aig: + """Create a tiny AIG for tensor export tests. + + Returns: + A small AIG with two PIs, one gate, and one PO. + """ + aig = Aig() + a = aig.create_pi() + b = aig.create_pi() + c = aig.create_and(a, b) + aig.create_po(c) + return aig + + +@pytest.fixture +def large_aig() -> Aig: + """Create a larger AIG with many gates and outputs. + + Returns: + A deterministic larger AIG for stress-style shape and dtype checks. + """ + aig = Aig() + pis = [aig.create_pi() for _ in range(12)] + + level = pis + for _ in range(6): + next_level = [] + for i in range(0, len(level), 2): + lhs = level[i] + rhs = level[(i + 1) % len(level)] + next_level.extend((aig.create_and(lhs, rhs), aig.create_and(~lhs, rhs))) + level = next_level + + for signal in level[:10]: + aig.create_po(signal) + + return aig + + +def test_graph_tensor_encoding_enums_exposed() -> None: + """Checks that node and edge tensor encoding enums are available in Python.""" + assert NodeTensorEncoding.INTEGER.value == 0 + assert NodeTensorEncoding.ONE_HOT.value == 1 + + assert EdgeTensorEncoding.BINARY.value == 0 + assert EdgeTensorEncoding.SIGNED.value == 1 + assert EdgeTensorEncoding.ONE_HOT.value == 2 + + +def test_to_graph_tensors_int_encoding_shapes(sample_aig: Aig) -> None: + """Checks default sparse COO tensor shapes and dimensions.""" + tensors = sample_aig.to_graph_tensors( + node_encoding=NodeTensorEncoding.INTEGER, + edge_encoding=EdgeTensorEncoding.BINARY, + ) + + edge_index = tensors["edge_index"] + edge_attr = tensors["edge_attr"] + node_attr = tensors["node_attr"] + + assert edge_index.ndim == 2 + assert edge_index.shape[0] == 2 + assert edge_index.shape[1] == len(list(sample_aig.to_edge_list())) + + assert edge_attr.ndim == 2 + assert edge_attr.shape[0] == edge_index.shape[1] + assert edge_attr.shape[1] == 1 + + assert node_attr.ndim == 2 + assert node_attr.shape[0] == sample_aig.size + sample_aig.num_pos + assert node_attr.shape[1] == 2 # base int type + level + + +def test_to_graph_tensors_one_hot_dimensions(sample_aig: Aig) -> None: + """Checks one-hot encoding dimensions for nodes and edges.""" + tensors = sample_aig.to_graph_tensors( + node_encoding=NodeTensorEncoding.ONE_HOT, + edge_encoding=EdgeTensorEncoding.ONE_HOT, + include_level=False, + ) + + edge_attr = tensors["edge_attr"] + node_attr = tensors["node_attr"] + + assert edge_attr.shape[1] == 2 + assert node_attr.shape[1] == 4 + + +def test_to_graph_tensors_signed_edge_encoding_values(sample_aig: Aig) -> None: + """Checks signed edge encoding maps to +1/-1 values.""" + tensors = sample_aig.to_graph_tensors( + node_encoding=NodeTensorEncoding.INTEGER, + edge_encoding=EdgeTensorEncoding.SIGNED, + include_level=False, + ) + edge_attr = np.from_dlpack(tensors["edge_attr"]) + + assert edge_attr.shape[1] == 1 + unique_values = {float(v) for v in edge_attr[:, 0]} + assert unique_values.issubset({-1.0, 1.0}) + + +def test_to_graph_tensors_truth_table_feature_dim(sample_aig: Aig) -> None: + """Checks truth-table feature expansion matches 2^num_pis.""" + tensors = sample_aig.to_graph_tensors( + node_encoding=NodeTensorEncoding.ONE_HOT, + edge_encoding=EdgeTensorEncoding.BINARY, + include_level=False, + include_fanout=True, + include_truth_table=True, + ) + + node_attr = tensors["node_attr"] + tt_dim = 2**sample_aig.num_pis + assert node_attr.shape[1] == 4 + 1 + tt_dim + + +def test_to_graph_tensors_torch_from_dlpack(sample_aig: Aig) -> None: + """Ensures PyTorch can consume all exported tensors via DLPack.""" + tensors = sample_aig.to_graph_tensors( + node_encoding=NodeTensorEncoding.ONE_HOT, + edge_encoding=EdgeTensorEncoding.BINARY, + ) + + edge_index = torch.from_dlpack(tensors["edge_index"]) + edge_attr = torch.from_dlpack(tensors["edge_attr"]) + node_attr = torch.from_dlpack(tensors["node_attr"]) + + assert edge_index.shape[0] == 2 + assert edge_attr.shape[0] == edge_index.shape[1] + assert node_attr.shape[0] == sample_aig.size + sample_aig.num_pos + assert edge_index.dtype == torch.int64 + assert edge_attr.dtype == torch.float32 + assert node_attr.dtype == torch.float32 + + +def test_to_graph_tensors_numpy_from_dlpack(sample_aig: Aig) -> None: + """Ensures NumPy can consume exported tensors via DLPack protocol.""" + tensors = sample_aig.to_graph_tensors( + node_encoding=NodeTensorEncoding.INTEGER, + edge_encoding=EdgeTensorEncoding.BINARY, + ) + + edge_index = np.from_dlpack(tensors["edge_index"]) + edge_attr = np.from_dlpack(tensors["edge_attr"]) + node_attr = np.from_dlpack(tensors["node_attr"]) + + assert edge_index.shape[0] == 2 + assert edge_attr.shape[0] == edge_index.shape[1] + assert node_attr.shape[0] == sample_aig.size + sample_aig.num_pos + assert edge_index.dtype == np.int64 + assert edge_attr.dtype == np.float32 + assert node_attr.dtype == np.float32 + + +def test_to_graph_tensors_dlpack_pointer_aliasing(sample_aig: Aig) -> None: + """Checks Torch and NumPy views share the same backing storage pointer.""" + tensors = sample_aig.to_graph_tensors( + node_encoding=NodeTensorEncoding.INTEGER, + edge_encoding=EdgeTensorEncoding.BINARY, + ) + + edge_attr_torch = torch.from_dlpack(tensors["edge_attr"]) + edge_attr_np = np.from_dlpack(tensors["edge_attr"]) + + assert edge_attr_torch.data_ptr() == edge_attr_np.__array_interface__["data"][0] + + +def test_to_graph_tensors_dlpack_mutation_propagation(sample_aig: Aig) -> None: + """Checks mutations in Torch are visible in NumPy for the same DLPack export.""" + tensors = sample_aig.to_graph_tensors( + node_encoding=NodeTensorEncoding.INTEGER, + edge_encoding=EdgeTensorEncoding.BINARY, + ) + + edge_attr_torch = torch.from_dlpack(tensors["edge_attr"]) + edge_attr_np = np.from_dlpack(tensors["edge_attr"]) + + original = float(edge_attr_torch[0, 0].item()) + edge_attr_torch[0, 0] = original + 7.0 + + assert float(edge_attr_np[0, 0]) == pytest.approx(original + 7.0) + + +def test_to_graph_tensors_large_aig_shapes_and_dtypes(large_aig: Aig) -> None: + """Checks export invariants on a larger AIG.""" + tensors = large_aig.to_graph_tensors( + node_encoding=NodeTensorEncoding.ONE_HOT, + edge_encoding=EdgeTensorEncoding.SIGNED, + include_level=True, + include_fanout=True, + include_truth_table=False, + ) + + edge_index = np.from_dlpack(tensors["edge_index"]) + edge_attr = np.from_dlpack(tensors["edge_attr"]) + node_attr = np.from_dlpack(tensors["node_attr"]) + + assert edge_index.shape == (2, len(list(large_aig.to_edge_list()))) + assert edge_attr.shape == (edge_index.shape[1], 1) + assert node_attr.shape[0] == large_aig.size + large_aig.num_pos + assert node_attr.shape[1] == 6 # one-hot(4) + level + fanout + + assert edge_index.dtype == np.int64 + assert edge_attr.dtype == np.float32 + assert node_attr.dtype == np.float32 + + +def test_to_graph_tensors_empty_aig_edge_case() -> None: + """Checks tensor export for an empty AIG (no PIs, no POs, no gates).""" + empty_aig = Aig() + tensors = empty_aig.to_graph_tensors( + node_encoding=NodeTensorEncoding.INTEGER, + edge_encoding=EdgeTensorEncoding.ONE_HOT, + include_level=False, + include_fanout=False, + include_truth_table=False, + ) + + edge_index = np.from_dlpack(tensors["edge_index"]) + edge_attr = np.from_dlpack(tensors["edge_attr"]) + node_attr = np.from_dlpack(tensors["node_attr"]) + + assert edge_index.shape == (2, 0) + assert edge_attr.shape == (0, 2) + assert node_attr.shape == (empty_aig.size + empty_aig.num_pos, 1) + + assert edge_index.dtype == np.int64 + assert edge_attr.dtype == np.float32 + assert node_attr.dtype == np.float32 diff --git a/uv.lock b/uv.lock index 8d0f6f66..f176b922 100644 --- a/uv.lock +++ b/uv.lock @@ -29,6 +29,7 @@ adapters = [ { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "torch" }, ] [package.dev-dependencies] @@ -37,6 +38,7 @@ adapters = [ { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "torch" }, ] build = [ { name = "nanobind" }, @@ -68,6 +70,7 @@ dev = [ { name = "sphinx-design", version = "0.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinxcontrib-svg2pdfconverter" }, { name = "sphinxext-opengraph" }, + { name = "torch" }, ] docs = [ { name = "furo" }, @@ -88,6 +91,7 @@ docs = [ { name = "sphinx-design", version = "0.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinxcontrib-svg2pdfconverter" }, { name = "sphinxext-opengraph" }, + { name = "torch" }, ] test = [ { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, @@ -97,12 +101,14 @@ test = [ { name = "pytest" }, { name = "pytest-sugar" }, { name = "pytest-xdist" }, + { name = "torch" }, ] [package.metadata] requires-dist = [ { name = "networkx", marker = "extra == 'adapters'", specifier = ">=3.0.0" }, { name = "numpy", marker = "extra == 'adapters'", specifier = ">=1.23.0" }, + { name = "torch", marker = "extra == 'adapters'", specifier = ">=2.2.0" }, ] provides-extras = ["adapters"] @@ -110,6 +116,7 @@ provides-extras = ["adapters"] adapters = [ { name = "networkx", specifier = ">=3.0.0" }, { name = "numpy", specifier = ">=1.23.0" }, + { name = "torch", specifier = ">=2.2.0" }, ] build = [ { name = "nanobind", specifier = "~=2.12.0" }, @@ -137,6 +144,7 @@ dev = [ { name = "sphinx-design", specifier = ">=0.6.1" }, { name = "sphinxcontrib-svg2pdfconverter", specifier = ">=1.2.3" }, { name = "sphinxext-opengraph", specifier = ">=0.9.1" }, + { name = "torch", specifier = ">=2.2.0" }, ] docs = [ { name = "furo", specifier = ">=2024.8.6" }, @@ -153,6 +161,7 @@ docs = [ { name = "sphinx-design", specifier = ">=0.6.1" }, { name = "sphinxcontrib-svg2pdfconverter", specifier = ">=1.2.3" }, { name = "sphinxext-opengraph", specifier = ">=0.9.1" }, + { name = "torch", specifier = ">=2.2.0" }, ] test = [ { name = "networkx", specifier = ">=3.0.0" }, @@ -160,6 +169,7 @@ test = [ { name = "pytest", specifier = ">=9.0.1" }, { name = "pytest-sugar", specifier = ">=1.1.1" }, { name = "pytest-xdist", specifier = ">=3.8.0" }, + { name = "torch", specifier = ">=2.2.0" }, ] [[package]] @@ -619,6 +629,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", size = 225315, upload-time = "2025-07-26T12:02:58.801Z" }, ] +[[package]] +name = "cuda-bindings" +version = "12.9.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-pathfinder" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/d8/b546104b8da3f562c1ff8ab36d130c8fe1dd6a045ced80b4f6ad74f7d4e1/cuda_bindings-12.9.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d3c842c2a4303b2a580fe955018e31aea30278be19795ae05226235268032e5", size = 12148218, upload-time = "2025-10-21T14:51:28.855Z" }, + { url = "https://files.pythonhosted.org/packages/45/e7/b47792cc2d01c7e1d37c32402182524774dadd2d26339bd224e0e913832e/cuda_bindings-12.9.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c912a3d9e6b6651853eed8eed96d6800d69c08e94052c292fec3f282c5a817c9", size = 12210593, upload-time = "2025-10-21T14:51:36.574Z" }, + { url = "https://files.pythonhosted.org/packages/a9/c1/dabe88f52c3e3760d861401bb994df08f672ec893b8f7592dc91626adcf3/cuda_bindings-12.9.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fda147a344e8eaeca0c6ff113d2851ffca8f7dfc0a6c932374ee5c47caa649c8", size = 12151019, upload-time = "2025-10-21T14:51:43.167Z" }, + { url = "https://files.pythonhosted.org/packages/63/56/e465c31dc9111be3441a9ba7df1941fe98f4aa6e71e8788a3fb4534ce24d/cuda_bindings-12.9.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:32bdc5a76906be4c61eb98f546a6786c5773a881f3b166486449b5d141e4a39f", size = 11906628, upload-time = "2025-10-21T14:51:49.905Z" }, + { url = "https://files.pythonhosted.org/packages/a3/84/1e6be415e37478070aeeee5884c2022713c1ecc735e6d82d744de0252eee/cuda_bindings-12.9.4-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56e0043c457a99ac473ddc926fe0dc4046694d99caef633e92601ab52cbe17eb", size = 11925991, upload-time = "2025-10-21T14:51:56.535Z" }, + { url = "https://files.pythonhosted.org/packages/d1/af/6dfd8f2ed90b1d4719bc053ff8940e494640fe4212dc3dd72f383e4992da/cuda_bindings-12.9.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8b72ee72a9cc1b531db31eebaaee5c69a8ec3500e32c6933f2d3b15297b53686", size = 11922703, upload-time = "2025-10-21T14:52:03.585Z" }, + { url = "https://files.pythonhosted.org/packages/6c/19/90ac264acc00f6df8a49378eedec9fd2db3061bf9263bf9f39fd3d8377c3/cuda_bindings-12.9.4-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d80bffc357df9988dca279734bc9674c3934a654cab10cadeed27ce17d8635ee", size = 11924658, upload-time = "2025-10-21T14:52:10.411Z" }, +] + +[[package]] +name = "cuda-pathfinder" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/02/59a5bc738a09def0b49aea0e460bdf97f65206d0d041246147cf6207e69c/cuda_pathfinder-1.4.1-py3-none-any.whl", hash = "sha256:40793006082de88e0950753655e55558a446bed9a7d9d0bcb48b2506d50ed82a", size = 43903, upload-time = "2026-03-06T21:05:24.372Z" }, +] + [[package]] name = "cycler" version = "0.12.1" @@ -818,6 +853,15 @@ wheels = [ { 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]] +name = "fsspec" +version = "2026.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/51/7c/f60c259dcbf4f0c47cc4ddb8f7720d2dcdc8888c8e5ad84c73ea4531cc5b/fsspec-2026.2.0.tar.gz", hash = "sha256:6544e34b16869f5aacd5b90bdf1a71acb37792ea3ddf6125ee69a22a53fb8bff", size = 313441, upload-time = "2026-02-05T21:50:53.743Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl", hash = "sha256:98de475b5cb3bd66bedd5c4679e87b4fdfe1a3bf4d707b151b3c07e58c9a2437", size = 202505, upload-time = "2026-02-05T21:50:51.819Z" }, +] + [[package]] name = "furo" version = "2025.12.19" @@ -845,6 +889,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/38/3f/9859f655d11901e7b2996c6e3d33e0caa9a1d4572c3bc61ed0faa64b2f4c/greenlet-3.3.2-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9bc885b89709d901859cf95179ec9f6bb67a3d2bb1f0e88456461bd4b7f8fd0d", size = 277747, upload-time = "2026-02-20T20:16:21.325Z" }, { url = "https://files.pythonhosted.org/packages/fb/07/cb284a8b5c6498dbd7cba35d31380bb123d7dceaa7907f606c8ff5993cbf/greenlet-3.3.2-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b568183cf65b94919be4438dc28416b234b678c608cafac8874dfeeb2a9bbe13", size = 579202, upload-time = "2026-02-20T20:47:28.955Z" }, { url = "https://files.pythonhosted.org/packages/ed/45/67922992b3a152f726163b19f890a85129a992f39607a2a53155de3448b8/greenlet-3.3.2-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:527fec58dc9f90efd594b9b700662ed3fb2493c2122067ac9c740d98080a620e", size = 590620, upload-time = "2026-02-20T20:55:55.581Z" }, + { url = "https://files.pythonhosted.org/packages/03/5f/6e2a7d80c353587751ef3d44bb947f0565ec008a2e0927821c007e96d3a7/greenlet-3.3.2-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:508c7f01f1791fbc8e011bd508f6794cb95397fdb198a46cb6635eb5b78d85a7", size = 602132, upload-time = "2026-02-20T21:02:43.261Z" }, { url = "https://files.pythonhosted.org/packages/ad/55/9f1ebb5a825215fadcc0f7d5073f6e79e3007e3282b14b22d6aba7ca6cb8/greenlet-3.3.2-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ad0c8917dd42a819fe77e6bdfcb84e3379c0de956469301d9fd36427a1ca501f", size = 591729, upload-time = "2026-02-20T20:20:58.395Z" }, { url = "https://files.pythonhosted.org/packages/24/b4/21f5455773d37f94b866eb3cf5caed88d6cea6dd2c6e1f9c34f463cba3ec/greenlet-3.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:97245cc10e5515dbc8c3104b2928f7f02b6813002770cfaffaf9a6e0fc2b94ef", size = 1551946, upload-time = "2026-02-20T20:49:31.102Z" }, { url = "https://files.pythonhosted.org/packages/00/68/91f061a926abead128fe1a87f0b453ccf07368666bd59ffa46016627a930/greenlet-3.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8c1fdd7d1b309ff0da81d60a9688a8bd044ac4e18b250320a96fc68d31c209ca", size = 1618494, upload-time = "2026-02-20T20:21:06.541Z" }, @@ -852,6 +897,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f3/47/16400cb42d18d7a6bb46f0626852c1718612e35dcb0dffa16bbaffdf5dd2/greenlet-3.3.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86", size = 278890, upload-time = "2026-02-20T20:19:39.263Z" }, { url = "https://files.pythonhosted.org/packages/a3/90/42762b77a5b6aa96cd8c0e80612663d39211e8ae8a6cd47c7f1249a66262/greenlet-3.3.2-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f", size = 581120, upload-time = "2026-02-20T20:47:30.161Z" }, { url = "https://files.pythonhosted.org/packages/bf/6f/f3d64f4fa0a9c7b5c5b3c810ff1df614540d5aa7d519261b53fba55d4df9/greenlet-3.3.2-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a443358b33c4ec7b05b79a7c8b466f5d275025e750298be7340f8fc63dff2a55", size = 594363, upload-time = "2026-02-20T20:55:56.965Z" }, + { url = "https://files.pythonhosted.org/packages/9c/8b/1430a04657735a3f23116c2e0d5eb10220928846e4537a938a41b350bed6/greenlet-3.3.2-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4375a58e49522698d3e70cc0b801c19433021b5c37686f7ce9c65b0d5c8677d2", size = 605046, upload-time = "2026-02-20T21:02:45.234Z" }, { url = "https://files.pythonhosted.org/packages/72/83/3e06a52aca8128bdd4dcd67e932b809e76a96ab8c232a8b025b2850264c5/greenlet-3.3.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e2cd90d413acbf5e77ae41e5d3c9b3ac1d011a756d7284d7f3f2b806bbd6358", size = 594156, upload-time = "2026-02-20T20:20:59.955Z" }, { url = "https://files.pythonhosted.org/packages/70/79/0de5e62b873e08fe3cef7dbe84e5c4bc0e8ed0c7ff131bccb8405cd107c8/greenlet-3.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:442b6057453c8cb29b4fb36a2ac689382fc71112273726e2423f7f17dc73bf99", size = 1554649, upload-time = "2026-02-20T20:49:32.293Z" }, { url = "https://files.pythonhosted.org/packages/5a/00/32d30dee8389dc36d42170a9c66217757289e2afb0de59a3565260f38373/greenlet-3.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45abe8eb6339518180d5a7fa47fa01945414d7cca5ecb745346fc6a87d2750be", size = 1619472, upload-time = "2026-02-20T20:21:07.966Z" }, @@ -860,6 +906,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ea/ab/1608e5a7578e62113506740b88066bf09888322a311cff602105e619bd87/greenlet-3.3.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:ac8d61d4343b799d1e526db579833d72f23759c71e07181c2d2944e429eb09cd", size = 280358, upload-time = "2026-02-20T20:17:43.971Z" }, { url = "https://files.pythonhosted.org/packages/a5/23/0eae412a4ade4e6623ff7626e38998cb9b11e9ff1ebacaa021e4e108ec15/greenlet-3.3.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ceec72030dae6ac0c8ed7591b96b70410a8be370b6a477b1dbc072856ad02bd", size = 601217, upload-time = "2026-02-20T20:47:31.462Z" }, { url = "https://files.pythonhosted.org/packages/f8/16/5b1678a9c07098ecb9ab2dd159fafaf12e963293e61ee8d10ecb55273e5e/greenlet-3.3.2-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a2a5be83a45ce6188c045bcc44b0ee037d6a518978de9a5d97438548b953a1ac", size = 611792, upload-time = "2026-02-20T20:55:58.423Z" }, + { url = "https://files.pythonhosted.org/packages/5c/c5/cc09412a29e43406eba18d61c70baa936e299bc27e074e2be3806ed29098/greenlet-3.3.2-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ae9e21c84035c490506c17002f5c8ab25f980205c3e61ddb3a2a2a2e6c411fcb", size = 626250, upload-time = "2026-02-20T21:02:46.596Z" }, { url = "https://files.pythonhosted.org/packages/50/1f/5155f55bd71cabd03765a4aac9ac446be129895271f73872c36ebd4b04b6/greenlet-3.3.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43e99d1749147ac21dde49b99c9abffcbc1e2d55c67501465ef0930d6e78e070", size = 613875, upload-time = "2026-02-20T20:21:01.102Z" }, { url = "https://files.pythonhosted.org/packages/fc/dd/845f249c3fcd69e32df80cdab059b4be8b766ef5830a3d0aa9d6cad55beb/greenlet-3.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c956a19350e2c37f2c48b336a3afb4bff120b36076d9d7fb68cb44e05d95b79", size = 1571467, upload-time = "2026-02-20T20:49:33.495Z" }, { url = "https://files.pythonhosted.org/packages/2a/50/2649fe21fcc2b56659a452868e695634722a6655ba245d9f77f5656010bf/greenlet-3.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6c6f8ba97d17a1e7d664151284cb3315fc5f8353e75221ed4324f84eb162b395", size = 1640001, upload-time = "2026-02-20T20:21:09.154Z" }, @@ -868,6 +915,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ac/48/f8b875fa7dea7dd9b33245e37f065af59df6a25af2f9561efa8d822fde51/greenlet-3.3.2-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:aa6ac98bdfd716a749b84d4034486863fd81c3abde9aa3cf8eff9127981a4ae4", size = 279120, upload-time = "2026-02-20T20:19:01.9Z" }, { url = "https://files.pythonhosted.org/packages/49/8d/9771d03e7a8b1ee456511961e1b97a6d77ae1dea4a34a5b98eee706689d3/greenlet-3.3.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab0c7e7901a00bc0a7284907273dc165b32e0d109a6713babd04471327ff7986", size = 603238, upload-time = "2026-02-20T20:47:32.873Z" }, { url = "https://files.pythonhosted.org/packages/59/0e/4223c2bbb63cd5c97f28ffb2a8aee71bdfb30b323c35d409450f51b91e3e/greenlet-3.3.2-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d248d8c23c67d2291ffd47af766e2a3aa9fa1c6703155c099feb11f526c63a92", size = 614219, upload-time = "2026-02-20T20:55:59.817Z" }, + { url = "https://files.pythonhosted.org/packages/94/2b/4d012a69759ac9d77210b8bfb128bc621125f5b20fc398bce3940d036b1c/greenlet-3.3.2-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ccd21bb86944ca9be6d967cf7691e658e43417782bce90b5d2faeda0ff78a7dd", size = 628268, upload-time = "2026-02-20T21:02:48.024Z" }, { url = "https://files.pythonhosted.org/packages/7a/34/259b28ea7a2a0c904b11cd36c79b8cef8019b26ee5dbe24e73b469dea347/greenlet-3.3.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6997d360a4e6a4e936c0f9625b1c20416b8a0ea18a8e19cabbefc712e7397ab", size = 616774, upload-time = "2026-02-20T20:21:02.454Z" }, { url = "https://files.pythonhosted.org/packages/0a/03/996c2d1689d486a6e199cb0f1cf9e4aa940c500e01bdf201299d7d61fa69/greenlet-3.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:64970c33a50551c7c50491671265d8954046cb6e8e2999aacdd60e439b70418a", size = 1571277, upload-time = "2026-02-20T20:49:34.795Z" }, { url = "https://files.pythonhosted.org/packages/d9/c4/2570fc07f34a39f2caf0bf9f24b0a1a0a47bc2e8e465b2c2424821389dfc/greenlet-3.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1a9172f5bf6bd88e6ba5a84e0a68afeac9dc7b6b412b245dd64f52d83c81e55b", size = 1640455, upload-time = "2026-02-20T20:21:10.261Z" }, @@ -876,6 +924,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3f/ae/8bffcbd373b57a5992cd077cbe8858fff39110480a9d50697091faea6f39/greenlet-3.3.2-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:8d1658d7291f9859beed69a776c10822a0a799bc4bfe1bd4272bb60e62507dab", size = 279650, upload-time = "2026-02-20T20:18:00.783Z" }, { url = "https://files.pythonhosted.org/packages/d1/c0/45f93f348fa49abf32ac8439938726c480bd96b2a3c6f4d949ec0124b69f/greenlet-3.3.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18cb1b7337bca281915b3c5d5ae19f4e76d35e1df80f4ad3c1a7be91fadf1082", size = 650295, upload-time = "2026-02-20T20:47:34.036Z" }, { url = "https://files.pythonhosted.org/packages/b3/de/dd7589b3f2b8372069ab3e4763ea5329940fc7ad9dcd3e272a37516d7c9b/greenlet-3.3.2-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2e47408e8ce1c6f1ceea0dffcdf6ebb85cc09e55c7af407c99f1112016e45e9", size = 662163, upload-time = "2026-02-20T20:56:01.295Z" }, + { url = "https://files.pythonhosted.org/packages/cd/ac/85804f74f1ccea31ba518dcc8ee6f14c79f73fe36fa1beba38930806df09/greenlet-3.3.2-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e3cb43ce200f59483eb82949bf1835a99cf43d7571e900d7c8d5c62cdf25d2f9", size = 675371, upload-time = "2026-02-20T21:02:49.664Z" }, { url = "https://files.pythonhosted.org/packages/d2/d8/09bfa816572a4d83bccd6750df1926f79158b1c36c5f73786e26dbe4ee38/greenlet-3.3.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63d10328839d1973e5ba35e98cccbca71b232b14051fd957b6f8b6e8e80d0506", size = 664160, upload-time = "2026-02-20T20:21:04.015Z" }, { url = "https://files.pythonhosted.org/packages/48/cf/56832f0c8255d27f6c35d41b5ec91168d74ec721d85f01a12131eec6b93c/greenlet-3.3.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8e4ab3cfb02993c8cc248ea73d7dae6cec0253e9afa311c9b37e603ca9fad2ce", size = 1619181, upload-time = "2026-02-20T20:49:36.052Z" }, { url = "https://files.pythonhosted.org/packages/0a/23/b90b60a4aabb4cec0796e55f25ffbfb579a907c3898cd2905c8918acaa16/greenlet-3.3.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94ad81f0fd3c0c0681a018a976e5c2bd2ca2d9d94895f23e7bb1af4e8af4e2d5", size = 1687713, upload-time = "2026-02-20T20:21:11.684Z" }, @@ -884,6 +933,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/98/6d/8f2ef704e614bcf58ed43cfb8d87afa1c285e98194ab2cfad351bf04f81e/greenlet-3.3.2-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:e26e72bec7ab387ac80caa7496e0f908ff954f31065b0ffc1f8ecb1338b11b54", size = 286617, upload-time = "2026-02-20T20:19:29.856Z" }, { url = "https://files.pythonhosted.org/packages/5e/0d/93894161d307c6ea237a43988f27eba0947b360b99ac5239ad3fe09f0b47/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b466dff7a4ffda6ca975979bab80bdadde979e29fc947ac3be4451428d8b0e4", size = 655189, upload-time = "2026-02-20T20:47:35.742Z" }, { url = "https://files.pythonhosted.org/packages/f5/2c/d2d506ebd8abcb57386ec4f7ba20f4030cbe56eae541bc6fd6ef399c0b41/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b8bddc5b73c9720bea487b3bffdb1840fe4e3656fba3bd40aa1489e9f37877ff", size = 658225, upload-time = "2026-02-20T20:56:02.527Z" }, + { url = "https://files.pythonhosted.org/packages/d1/67/8197b7e7e602150938049d8e7f30de1660cfb87e4c8ee349b42b67bdb2e1/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:59b3e2c40f6706b05a9cd299c836c6aa2378cabe25d021acd80f13abf81181cf", size = 666581, upload-time = "2026-02-20T21:02:51.526Z" }, { url = "https://files.pythonhosted.org/packages/8e/30/3a09155fbf728673a1dea713572d2d31159f824a37c22da82127056c44e4/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b26b0f4428b871a751968285a1ac9648944cea09807177ac639b030bddebcea4", size = 657907, upload-time = "2026-02-20T20:21:05.259Z" }, { url = "https://files.pythonhosted.org/packages/f3/fd/d05a4b7acd0154ed758797f0a43b4c0962a843bedfe980115e842c5b2d08/greenlet-3.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1fb39a11ee2e4d94be9a76671482be9398560955c9e568550de0224e41104727", size = 1618857, upload-time = "2026-02-20T20:49:37.309Z" }, { url = "https://files.pythonhosted.org/packages/6f/e1/50ee92a5db521de8f35075b5eff060dd43d39ebd46c2181a2042f7070385/greenlet-3.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:20154044d9085151bc309e7689d6f7ba10027f8f5a8c0676ad398b951913d89e", size = 1680010, upload-time = "2026-02-20T20:21:13.427Z" }, @@ -1482,6 +1532,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, +] + [[package]] name = "myst-nb" version = "1.4.0" @@ -1790,6 +1849,140 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/de/e5/b7d20451657664b07986c2f6e3be564433f5dcaf3482d68eaecd79afaf03/numpy-2.4.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:be71bf1edb48ebbbf7f6337b5bfd2f895d1902f6335a5830b20141fc126ffba0", size = 12502577, upload-time = "2026-01-31T23:13:07.08Z" }, ] +[[package]] +name = "nvidia-cublas-cu12" +version = "12.8.4.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" }, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" }, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.8.93" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" }, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" }, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.10.2.21" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, +] + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.3.3.83" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, +] + +[[package]] +name = "nvidia-cufile-cu12" +version = "1.13.1.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" }, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.9.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" }, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.7.3.90" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12" }, + { name = "nvidia-cusparse-cu12" }, + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, +] + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.5.8.93" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, +] + +[[package]] +name = "nvidia-cusparselt-cu12" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" }, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.27.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229, upload-time = "2025-06-26T04:11:28.385Z" }, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.8.93" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" }, +] + +[[package]] +name = "nvidia-nvshmem-cu12" +version = "3.4.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/09/6ea3ea725f82e1e76684f0708bbedd871fc96da89945adeba65c3835a64c/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd", size = 139103095, upload-time = "2025-09-06T00:32:31.266Z" }, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" }, +] + [[package]] name = "packaging" version = "26.0" @@ -2794,6 +2987,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, ] +[[package]] +name = "sympy" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, +] + [[package]] name = "tabulate" version = "0.10.0" @@ -2866,6 +3071,72 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" }, ] +[[package]] +name = "torch" +version = "2.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-bindings", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "setuptools", marker = "python_full_version >= '3.12'" }, + { name = "sympy" }, + { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/30/bfebdd8ec77db9a79775121789992d6b3b75ee5494971294d7b4b7c999bc/torch-2.10.0-2-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:2b980edd8d7c0a68c4e951ee1856334a43193f98730d97408fbd148c1a933313", size = 79411457, upload-time = "2026-02-10T21:44:59.189Z" }, + { url = "https://files.pythonhosted.org/packages/0f/8b/4b61d6e13f7108f36910df9ab4b58fd389cc2520d54d81b88660804aad99/torch-2.10.0-2-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:418997cb02d0a0f1497cf6a09f63166f9f5df9f3e16c8a716ab76a72127c714f", size = 79423467, upload-time = "2026-02-10T21:44:48.711Z" }, + { url = "https://files.pythonhosted.org/packages/d3/54/a2ba279afcca44bbd320d4e73675b282fcee3d81400ea1b53934efca6462/torch-2.10.0-2-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:13ec4add8c3faaed8d13e0574f5cd4a323c11655546f91fbe6afa77b57423574", size = 79498202, upload-time = "2026-02-10T21:44:52.603Z" }, + { url = "https://files.pythonhosted.org/packages/ec/23/2c9fe0c9c27f7f6cb865abcea8a4568f29f00acaeadfc6a37f6801f84cb4/torch-2.10.0-2-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:e521c9f030a3774ed770a9c011751fb47c4d12029a3d6522116e48431f2ff89e", size = 79498254, upload-time = "2026-02-10T21:44:44.095Z" }, + { url = "https://files.pythonhosted.org/packages/0c/1a/c61f36cfd446170ec27b3a4984f072fd06dab6b5d7ce27e11adb35d6c838/torch-2.10.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5276fa790a666ee8becaffff8acb711922252521b28fbce5db7db5cf9cb2026d", size = 145992962, upload-time = "2026-01-21T16:24:14.04Z" }, + { url = "https://files.pythonhosted.org/packages/b5/60/6662535354191e2d1555296045b63e4279e5a9dbad49acf55a5d38655a39/torch-2.10.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:aaf663927bcd490ae971469a624c322202a2a1e68936eb952535ca4cd3b90444", size = 915599237, upload-time = "2026-01-21T16:23:25.497Z" }, + { url = "https://files.pythonhosted.org/packages/40/b8/66bbe96f0d79be2b5c697b2e0b187ed792a15c6c4b8904613454651db848/torch-2.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:a4be6a2a190b32ff5c8002a0977a25ea60e64f7ba46b1be37093c141d9c49aeb", size = 113720931, upload-time = "2026-01-21T16:24:23.743Z" }, + { url = "https://files.pythonhosted.org/packages/76/bb/d820f90e69cda6c8169b32a0c6a3ab7b17bf7990b8f2c680077c24a3c14c/torch-2.10.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:35e407430795c8d3edb07a1d711c41cc1f9eaddc8b2f1cc0a165a6767a8fb73d", size = 79411450, upload-time = "2026-01-21T16:25:30.692Z" }, + { url = "https://files.pythonhosted.org/packages/78/89/f5554b13ebd71e05c0b002f95148033e730d3f7067f67423026cc9c69410/torch-2.10.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:3282d9febd1e4e476630a099692b44fdc214ee9bf8ee5377732d9d9dfe5712e4", size = 145992610, upload-time = "2026-01-21T16:25:26.327Z" }, + { url = "https://files.pythonhosted.org/packages/ae/30/a3a2120621bf9c17779b169fc17e3dc29b230c29d0f8222f499f5e159aa8/torch-2.10.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a2f9edd8dbc99f62bc4dfb78af7bf89499bca3d753423ac1b4e06592e467b763", size = 915607863, upload-time = "2026-01-21T16:25:06.696Z" }, + { url = "https://files.pythonhosted.org/packages/6f/3d/c87b33c5f260a2a8ad68da7147e105f05868c281c63d65ed85aa4da98c66/torch-2.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:29b7009dba4b7a1c960260fc8ac85022c784250af43af9fb0ebafc9883782ebd", size = 113723116, upload-time = "2026-01-21T16:25:21.916Z" }, + { url = "https://files.pythonhosted.org/packages/61/d8/15b9d9d3a6b0c01b883787bd056acbe5cc321090d4b216d3ea89a8fcfdf3/torch-2.10.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:b7bd80f3477b830dd166c707c5b0b82a898e7b16f59a7d9d42778dd058272e8b", size = 79423461, upload-time = "2026-01-21T16:24:50.266Z" }, + { url = "https://files.pythonhosted.org/packages/cc/af/758e242e9102e9988969b5e621d41f36b8f258bb4a099109b7a4b4b50ea4/torch-2.10.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:5fd4117d89ffd47e3dcc71e71a22efac24828ad781c7e46aaaf56bf7f2796acf", size = 145996088, upload-time = "2026-01-21T16:24:44.171Z" }, + { url = "https://files.pythonhosted.org/packages/23/8e/3c74db5e53bff7ed9e34c8123e6a8bfef718b2450c35eefab85bb4a7e270/torch-2.10.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:787124e7db3b379d4f1ed54dd12ae7c741c16a4d29b49c0226a89bea50923ffb", size = 915711952, upload-time = "2026-01-21T16:23:53.503Z" }, + { url = "https://files.pythonhosted.org/packages/6e/01/624c4324ca01f66ae4c7cd1b74eb16fb52596dce66dbe51eff95ef9e7a4c/torch-2.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:2c66c61f44c5f903046cc696d088e21062644cbe541c7f1c4eaae88b2ad23547", size = 113757972, upload-time = "2026-01-21T16:24:39.516Z" }, + { url = "https://files.pythonhosted.org/packages/c9/5c/dee910b87c4d5c0fcb41b50839ae04df87c1cfc663cf1b5fca7ea565eeaa/torch-2.10.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:6d3707a61863d1c4d6ebba7be4ca320f42b869ee657e9b2c21c736bf17000294", size = 79498198, upload-time = "2026-01-21T16:24:34.704Z" }, + { url = "https://files.pythonhosted.org/packages/c9/6f/f2e91e34e3fcba2e3fc8d8f74e7d6c22e74e480bbd1db7bc8900fdf3e95c/torch-2.10.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:5c4d217b14741e40776dd7074d9006fd28b8a97ef5654db959d8635b2fe5f29b", size = 146004247, upload-time = "2026-01-21T16:24:29.335Z" }, + { url = "https://files.pythonhosted.org/packages/98/fb/5160261aeb5e1ee12ee95fe599d0541f7c976c3701d607d8fc29e623229f/torch-2.10.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6b71486353fce0f9714ca0c9ef1c850a2ae766b409808acd58e9678a3edb7738", size = 915716445, upload-time = "2026-01-21T16:22:45.353Z" }, + { url = "https://files.pythonhosted.org/packages/6a/16/502fb1b41e6d868e8deb5b0e3ae926bbb36dab8ceb0d1b769b266ad7b0c3/torch-2.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:c2ee399c644dc92ef7bc0d4f7e74b5360c37cdbe7c5ba11318dda49ffac2bc57", size = 113757050, upload-time = "2026-01-21T16:24:19.204Z" }, + { url = "https://files.pythonhosted.org/packages/1a/0b/39929b148f4824bc3ad6f9f72a29d4ad865bcf7ebfc2fa67584773e083d2/torch-2.10.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:3202429f58309b9fa96a614885eace4b7995729f44beb54d3e4a47773649d382", size = 79851305, upload-time = "2026-01-21T16:24:09.209Z" }, + { url = "https://files.pythonhosted.org/packages/d8/14/21fbce63bc452381ba5f74a2c0a959fdf5ad5803ccc0c654e752e0dbe91a/torch-2.10.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:aae1b29cd68e50a9397f5ee897b9c24742e9e306f88a807a27d617f07adb3bd8", size = 146005472, upload-time = "2026-01-21T16:22:29.022Z" }, + { url = "https://files.pythonhosted.org/packages/54/fd/b207d1c525cb570ef47f3e9f836b154685011fce11a2f444ba8a4084d042/torch-2.10.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:6021db85958db2f07ec94e1bc77212721ba4920c12a18dc552d2ae36a3eb163f", size = 915612644, upload-time = "2026-01-21T16:21:47.019Z" }, + { url = "https://files.pythonhosted.org/packages/36/53/0197f868c75f1050b199fe58f9bf3bf3aecac9b4e85cc9c964383d745403/torch-2.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff43db38af76fda183156153983c9a096fc4c78d0cd1e07b14a2314c7f01c2c8", size = 113997015, upload-time = "2026-01-21T16:23:00.767Z" }, + { url = "https://files.pythonhosted.org/packages/0e/13/e76b4d9c160e89fff48bf16b449ea324bda84745d2ab30294c37c2434c0d/torch-2.10.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:cdf2a523d699b70d613243211ecaac14fe9c5df8a0b0a9c02add60fb2a413e0f", size = 79498248, upload-time = "2026-01-21T16:23:09.315Z" }, + { url = "https://files.pythonhosted.org/packages/4f/93/716b5ac0155f1be70ed81bacc21269c3ece8dba0c249b9994094110bfc51/torch-2.10.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:bf0d9ff448b0218e0433aeb198805192346c4fd659c852370d5cc245f602a06a", size = 79464992, upload-time = "2026-01-21T16:23:05.162Z" }, + { url = "https://files.pythonhosted.org/packages/69/2b/51e663ff190c9d16d4a8271203b71bc73a16aa7619b9f271a69b9d4a936b/torch-2.10.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:233aed0659a2503b831d8a67e9da66a62c996204c0bba4f4c442ccc0c68a3f60", size = 146018567, upload-time = "2026-01-21T16:22:23.393Z" }, + { url = "https://files.pythonhosted.org/packages/5e/cd/4b95ef7f293b927c283db0b136c42be91c8ec6845c44de0238c8c23bdc80/torch-2.10.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:682497e16bdfa6efeec8cde66531bc8d1fbbbb4d8788ec6173c089ed3cc2bfe5", size = 915721646, upload-time = "2026-01-21T16:21:16.983Z" }, + { url = "https://files.pythonhosted.org/packages/56/97/078a007208f8056d88ae43198833469e61a0a355abc0b070edd2c085eb9a/torch-2.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:6528f13d2a8593a1a412ea07a99812495bec07e9224c28b2a25c0a30c7da025c", size = 113752373, upload-time = "2026-01-21T16:22:13.471Z" }, + { url = "https://files.pythonhosted.org/packages/d8/94/71994e7d0d5238393df9732fdab607e37e2b56d26a746cb59fdb415f8966/torch-2.10.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:f5ab4ba32383061be0fb74bda772d470140a12c1c3b58a0cfbf3dae94d164c28", size = 79850324, upload-time = "2026-01-21T16:22:09.494Z" }, + { url = "https://files.pythonhosted.org/packages/e2/65/1a05346b418ea8ccd10360eef4b3e0ce688fba544e76edec26913a8d0ee0/torch-2.10.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:716b01a176c2a5659c98f6b01bf868244abdd896526f1c692712ab36dbaf9b63", size = 146006482, upload-time = "2026-01-21T16:22:18.42Z" }, + { url = "https://files.pythonhosted.org/packages/1d/b9/5f6f9d9e859fc3235f60578fa64f52c9c6e9b4327f0fe0defb6de5c0de31/torch-2.10.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:d8f5912ba938233f86361e891789595ff35ca4b4e2ac8fe3670895e5976731d6", size = 915613050, upload-time = "2026-01-21T16:20:49.035Z" }, + { url = "https://files.pythonhosted.org/packages/66/4d/35352043ee0eaffdeff154fad67cd4a31dbed7ff8e3be1cc4549717d6d51/torch-2.10.0-cp314-cp314t-win_amd64.whl", hash = "sha256:71283a373f0ee2c89e0f0d5f446039bdabe8dbc3c9ccf35f0f784908b0acd185", size = 113995816, upload-time = "2026-01-21T16:22:05.312Z" }, +] + [[package]] name = "tornado" version = "6.5.4" @@ -2894,6 +3165,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, ] +[[package]] +name = "triton" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/f7/f1c9d3424ab199ac53c2da567b859bcddbb9c9e7154805119f8bd95ec36f/triton-3.6.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6550fae429e0667e397e5de64b332d1e5695b73650ee75a6146e2e902770bea", size = 188105201, upload-time = "2026-01-20T16:00:29.272Z" }, + { url = "https://files.pythonhosted.org/packages/e0/12/b05ba554d2c623bffa59922b94b0775673de251f468a9609bc9e45de95e9/triton-3.6.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8e323d608e3a9bfcc2d9efcc90ceefb764a82b99dea12a86d643c72539ad5d3", size = 188214640, upload-time = "2026-01-20T16:00:35.869Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a8/cdf8b3e4c98132f965f88c2313a4b493266832ad47fb52f23d14d4f86bb5/triton-3.6.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74caf5e34b66d9f3a429af689c1c7128daba1d8208df60e81106b115c00d6fca", size = 188266850, upload-time = "2026-01-20T16:00:43.041Z" }, + { url = "https://files.pythonhosted.org/packages/f9/0b/37d991d8c130ce81a8728ae3c25b6e60935838e9be1b58791f5997b24a54/triton-3.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10c7f76c6e72d2ef08df639e3d0d30729112f47a56b0c81672edc05ee5116ac9", size = 188289450, upload-time = "2026-01-20T16:00:49.136Z" }, + { url = "https://files.pythonhosted.org/packages/35/f8/9c66bfc55361ec6d0e4040a0337fb5924ceb23de4648b8a81ae9d33b2b38/triton-3.6.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d002e07d7180fd65e622134fbd980c9a3d4211fb85224b56a0a0efbd422ab72f", size = 188400296, upload-time = "2026-01-20T16:00:56.042Z" }, + { url = "https://files.pythonhosted.org/packages/df/3d/9e7eee57b37c80cec63322c0231bb6da3cfe535a91d7a4d64896fcb89357/triton-3.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a17a5d5985f0ac494ed8a8e54568f092f7057ef60e1b0fa09d3fd1512064e803", size = 188273063, upload-time = "2026-01-20T16:01:07.278Z" }, + { url = "https://files.pythonhosted.org/packages/f6/56/6113c23ff46c00aae423333eb58b3e60bdfe9179d542781955a5e1514cb3/triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7", size = 188397994, upload-time = "2026-01-20T16:01:14.236Z" }, +] + [[package]] name = "typing-extensions" version = "4.15.0" From 66d2d80e5b2c46cab477324d32eaa7c1ae3a40df Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 11 Mar 2026 13:10:53 +0100 Subject: [PATCH 04/29] :memo: Add documentation for DLPack sparse tensor export functionality --- docs/machine_learning.md | 80 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/docs/machine_learning.md b/docs/machine_learning.md index f960b114..88e39ee2 100644 --- a/docs/machine_learning.md +++ b/docs/machine_learning.md @@ -128,6 +128,86 @@ plt.margins(x=0.2) plt.show() ``` +### DLPack Sparse Tensors + +For high-throughput ML pipelines, `aigverse` can export graph tensors directly in a sparse COO-style layout where each +tensor implements the [DLPack](https://dmlc.github.io/dlpack/latest/) protocol. This allows zero-copy hand-off to modern +tensor frameworks, such +as [PyTorch](https://docs.pytorch.org/docs/stable/dlpack.html), +[JAX](https://docs.jax.dev/en/latest/_autosummary/jax.dlpack.from_dlpack.html#jax.dlpack.from_dlpack), +[TensorFlow](https://www.tensorflow.org/api_docs/python/tf/experimental/dlpack/from_dlpack), etc., through `from_dlpack`. + +```{code-cell} ipython3 +import torch + +from aigverse.networks import Aig, EdgeTensorEncoding, NodeTensorEncoding + +aig = Aig() +a = aig.create_pi() +b = aig.create_pi() +g = aig.create_and(a, b) +aig.create_po(g) + +dlpack_data = aig.to_graph_tensors( + node_encoding=NodeTensorEncoding.INTEGER, + edge_encoding=EdgeTensorEncoding.BINARY, + include_level=True, + include_fanout=True, + include_truth_table=False, +) + +edge_index = torch.from_dlpack(dlpack_data["edge_index"]) +edge_attr = torch.from_dlpack(dlpack_data["edge_attr"]) +node_attr = torch.from_dlpack(dlpack_data["node_attr"]) + +print(edge_index.shape, edge_attr.shape, node_attr.shape) +``` + +You can immediately construct sparse tensors in Python: + +```{code-cell} ipython3 +num_nodes = node_attr.shape[0] + +sparse_adj = torch.sparse_coo_tensor( + indices=edge_index, + values=edge_attr, + size=(num_nodes, num_nodes, edge_attr.shape[1]), +) + +print(sparse_adj.shape) +``` + +The same export also works with [NumPy's DLPack consumer API](https://numpy.org/doc/stable/release/1.22.0-notes.html#add-nep-47-compatible-dlpack-support): + +```{code-cell} ipython3 +import numpy as np + +edge_index_np = np.from_dlpack(aig.to_graph_tensors()["edge_index"]) +print(edge_index_np.shape) +``` + +Encoding and dtype mapping: + +- Edge encoding (`edge_attr`): + - `EdgeTensorEncoding.BINARY`: regular `0.0`, inverted `1.0` + - `EdgeTensorEncoding.SIGNED`: regular `+1.0`, inverted `-1.0` + - `EdgeTensorEncoding.ONE_HOT`: regular `[1.0, 0.0]`, inverted `[0.0, 1.0]` +- Node encoding (`node_attr`): + - `NodeTensorEncoding.INTEGER`: `constant=0`, `pi=1`, `gate=2`, `po=3` + - `NodeTensorEncoding.ONE_HOT`: `[constant, pi, gate, po]` +- Tensor dtypes: + - `edge_index`: `int64` + - `edge_attr`: `float32` + - `node_attr`: `float32` + +The exported tensor shapes are: + +- `edge_index`: `(2, E)` +- `edge_attr`: `(E, D_edge)` +- `node_attr`: `(N, D_node)` + +where `D_edge` is `1` for `BINARY` and `SIGNED`, and `2` for `ONE_HOT`. + ## Truth Tables Truth tables can be easily converted to Python lists or [NumPy](https://numpy.org/) arrays, making them compatible with From b8e82e5773ee06ea7b0dfc380b2edd689a3246c1 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 11 Mar 2026 13:22:52 +0100 Subject: [PATCH 05/29] :recycle: Refactor capsule ownership management in NumPy-backed ndarray creation --- src/aigverse/networks/graph_tensors.hpp | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/aigverse/networks/graph_tensors.hpp b/src/aigverse/networks/graph_tensors.hpp index 45c0a0ef..42c385e5 100644 --- a/src/aigverse/networks/graph_tensors.hpp +++ b/src/aigverse/networks/graph_tensors.hpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -64,8 +63,8 @@ namespace detail /** * @brief Creates an owning NumPy-backed nanobind ndarray from a moved vector. * - * The returned array keeps data alive via a capsule-owned shared pointer, making - * it safe to consume through DLPack in downstream frameworks. + * The returned array keeps data alive via capsule-managed ownership of a heap + * vector, making it safe to consume through DLPack in downstream frameworks. * * @tparam T Element type. * @param data Moved data buffer. @@ -78,16 +77,17 @@ nanobind::ndarray make_owned_ndarray(std::vector&& { namespace nb = nanobind; - auto storage = std::make_shared>(std::move(data)); - auto holder = std::make_unique>>(storage); - nb::capsule owner(holder.get(), - [](void* ptr) noexcept - { - delete static_cast>*>(ptr); - }); // TODO does this really need to call delete? Why not simply let the smart pointer handle it? - holder.release(); - - return nb::ndarray(storage->data(), shape, owner); + // Use unique_ptr as an exception-safe staging owner while creating the capsule. + // Once the capsule is constructed, ownership is intentionally transferred to + // the capsule deleter via release(). + auto storage = std::make_unique>(std::move(data)); + auto* raw_storage = storage.get(); + // nanobind::capsule stores a raw pointer plus a C-style destructor callback. + // The callback is the final owner and performs the matching delete. + nb::capsule owner(raw_storage, [](void* ptr) noexcept { delete static_cast*>(ptr); }); + storage.release(); + + return nb::ndarray(raw_storage->data(), shape, owner); } /** * @brief Exports an AIG-style network to sparse COO-like graph tensors. From bc52b59e682f675b01a54d9a469d85cd24c3ca5e Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 11 Mar 2026 14:05:10 +0100 Subject: [PATCH 06/29] :rotating_light: Fix `clang-tidy` warnings --- src/aigverse/networks/graph_tensors.hpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/aigverse/networks/graph_tensors.hpp b/src/aigverse/networks/graph_tensors.hpp index 42c385e5..5cddca36 100644 --- a/src/aigverse/networks/graph_tensors.hpp +++ b/src/aigverse/networks/graph_tensors.hpp @@ -1,11 +1,12 @@ #pragma once +#include "aigverse/types.hpp" + #include "edge_list.hpp" #include -#include -#include -#include +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include #include #include @@ -84,7 +85,11 @@ nanobind::ndarray make_owned_ndarray(std::vector&& auto* raw_storage = storage.get(); // nanobind::capsule stores a raw pointer plus a C-style destructor callback. // The callback is the final owner and performs the matching delete. - nb::capsule owner(raw_storage, [](void* ptr) noexcept { delete static_cast*>(ptr); }); + nb::capsule owner(raw_storage, + [](void* ptr) noexcept + { + delete static_cast*>(ptr); // NOLINT(cppcoreguidelines-owning-memory) + }); storage.release(); return nb::ndarray(raw_storage->data(), shape, owner); @@ -153,8 +158,8 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ } case edge_tensor_encoding::ONE_HOT: { - edge_attr[i * 2] = inverted ? 0.0f : 1.0f; - edge_attr[i * 2 + 1] = inverted ? 1.0f : 0.0f; + edge_attr[i * 2] = inverted ? 0.0f : 1.0f; + edge_attr[(i * 2) + 1] = inverted ? 1.0f : 0.0f; break; } } From 5dddd59d4bb72cca01edbe16728653fc6e411210 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 11 Mar 2026 14:09:26 +0100 Subject: [PATCH 07/29] :memo: Update machine learning documentation with optimized truth table processing and tensor conversion --- docs/machine_learning.md | 58 +++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/docs/machine_learning.md b/docs/machine_learning.md index 88e39ee2..d090be83 100644 --- a/docs/machine_learning.md +++ b/docs/machine_learning.md @@ -210,48 +210,38 @@ where `D_edge` is `1` for `BINARY` and `SIGNED`, and `2` for `ONE_HOT`. ## Truth Tables -Truth tables can be easily converted to Python lists or [NumPy](https://numpy.org/) arrays, making them compatible with -standard ML libraries such as [scikit-learn](https://scikit-learn.org/), [PyTorch](https://pytorch.org/), or -[TensorFlow](https://www.tensorflow.org/). Since `TruthTable` objects are iterable, this conversion is direct and -intuitive. You can use these arrays as labels or features in supervised learning tasks, or as part of a dataset for -training and evaluating models. +Truth tables are iterable, but for ML pipelines it is best to keep data in contiguous array/tensor form from the +start. A practical pattern is: + +1. Materialize labels once as a NumPy array. +2. Build the input matrix with vectorized NumPy operations. +3. Convert to framework tensors (for example, [PyTorch](https://docs.pytorch.org/docs/stable/index.html)) without copying. + +This keeps preprocessing fast and avoids Python-level loops in hot paths. ```{code-cell} ipython3 -from aigverse.utils import TruthTable import numpy as np +import torch -# Create a simple truth table, e.g., a 3-input majority function +from aigverse.utils import TruthTable + +# Create a simple truth table (3-input majority function) tt = TruthTable(3) tt.create_from_hex_string("e8") -# Export to a list -tt_list = list(tt) -print(f"As list: {tt_list}") - -# Export to NumPy arrays of different types -tt_np_bool = np.array(tt) -print(f"As NumPy bool array: {tt_np_bool}") -tt_np_int = np.array(tt, dtype=np.int32) -print(f"As NumPy int array: {tt_np_int}") -tt_np_float = np.array(tt, dtype=np.float64) -print(f"As NumPy float array: {tt_np_float}") - - -# These arrays can now be used as labels for an ML model. -# For example, let's generate the corresponding feature matrix: -def generate_inputs(num_vars): - inputs = [] - for i in range(2**num_vars): - # Convert i to binary and pad with zeros - binary = bin(i)[2:].zfill(num_vars) - inputs.append([int(bit) for bit in binary]) - return np.array(inputs) +# Vectorized label extraction (shape: [2**num_vars]) +y_np = np.fromiter(tt, dtype=np.uint8) +# Vectorized feature matrix generation (shape: [2**num_vars, num_vars]) +n = tt.num_vars() +indices = np.array(range(2**n), dtype=np.uint32)[:, None] +bit_positions = np.array(range(n - 1, -1, -1), dtype=np.uint32) +X_np = ((indices >> bit_positions) & 1).astype(np.uint8) -feature_matrix = generate_inputs(tt.num_vars()) -labels = tt_np_int # Using the integer array as labels +# Zero-copy bridge NumPy -> PyTorch +X_torch = torch.from_numpy(X_np) +y_torch = torch.from_numpy(y_np) -print("\nFeature matrix (X) and labels (y) for ML:") -print("X:\n", feature_matrix) -print("y:\n", labels) +print("NumPy shapes:", X_np.shape, y_np.shape) +print("Torch shapes:", X_torch.shape, y_torch.shape) ``` From d12ad38275f87894cc49025e4b8d1e8ed68e2ee0 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 11 Mar 2026 14:09:59 +0100 Subject: [PATCH 08/29] Potential fix for code scanning alert no. 846: Poorly documented large function Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- src/aigverse/networks/graph_tensors.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/aigverse/networks/graph_tensors.hpp b/src/aigverse/networks/graph_tensors.hpp index 42c385e5..c1a59df5 100644 --- a/src/aigverse/networks/graph_tensors.hpp +++ b/src/aigverse/networks/graph_tensors.hpp @@ -116,16 +116,21 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ { namespace nb = nanobind; + // Number of node-type categories used for one-hot encodings: + // [constant, primary input, internal gate, primary output]. constexpr size_t node_type_one_hot_dim = 4; + // Canonical integer labels for node types; shared across all node encodings. constexpr int64_t type_constant = 0; constexpr int64_t type_pi = 1; constexpr int64_t type_gate = 2; constexpr int64_t type_po = 3; + // Flatten the network into an edge list to derive COO edge indices. const auto edges = aigverse::to_edge_list(ntk).edges; const auto edge_count = edges.size(); + // Preallocate the 2×E edge_index tensor in column-major (source/target) form. std::vector edge_index(2 * edge_count, 0); for (size_t i = 0; i < edge_count; ++i) { From 34e475c3c241d4920ae0d2d44e98495df8ec48f8 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 11 Mar 2026 14:25:10 +0100 Subject: [PATCH 09/29] :goal_net: Add `to_graph_tensors` method to Sequential networks with appropriate error handling --- python/aigverse/networks.pyi | 10 ++++++++++ src/aigverse/networks/logic_networks.cpp | 15 +++++++++++++++ test/networks/test_sequential_aig.py | 12 ++++++++++++ 3 files changed, 37 insertions(+) diff --git a/python/aigverse/networks.pyi b/python/aigverse/networks.pyi index 0af74bad..fcf7f261 100644 --- a/python/aigverse/networks.pyi +++ b/python/aigverse/networks.pyi @@ -633,6 +633,16 @@ class SequentialAig(Aig): def to_index_list(self) -> NoReturn: """Sequential networks cannot be encoded as combinational index lists.""" + def to_graph_tensors( + self, + node_encoding: NodeTensorEncoding = ..., + edge_encoding: EdgeTensorEncoding = ..., + include_level: bool = True, + include_fanout: bool = False, + include_truth_table: bool = False, + ) -> NoReturn: + """Sequential networks cannot be exported as combinational graph tensors.""" + def __getstate__(self) -> NoReturn: """Sequential networks are not pickleable via combinational index-list state.""" diff --git a/src/aigverse/networks/logic_networks.cpp b/src/aigverse/networks/logic_networks.cpp index f096675f..b525b070 100644 --- a/src/aigverse/networks/logic_networks.cpp +++ b/src/aigverse/networks/logic_networks.cpp @@ -661,6 +661,21 @@ Preserves only combinational structure and does not capture augmented view metad }, R"pb(Sequential networks cannot be encoded as combinational index lists.)pb", nb::sig("def to_index_list(self) -> NoReturn")) + .def( + "to_graph_tensors", + [network_name](const SequentialNtk&, const aigverse::node_tensor_encoding, + const aigverse::edge_tensor_encoding, const bool, const bool, const bool) -> nb::dict + { + const auto message = fmt::format("Sequential{} does not support to_graph_tensors() because graph " + "tensor export is combinational-only and would drop register " + "state.", + network_name); + throw nb::type_error(message.c_str()); + }, + nb::arg("node_encoding") = aigverse::node_tensor_encoding::INTEGER, + nb::arg("edge_encoding") = aigverse::edge_tensor_encoding::BINARY, nb::arg("include_level") = true, + nb::arg("include_fanout") = false, nb::arg("include_truth_table") = false, + R"pb(Sequential networks cannot be exported as combinational graph tensors.)pb") .def( "__getstate__", [network_name](const SequentialNtk&) -> nb::tuple diff --git a/test/networks/test_sequential_aig.py b/test/networks/test_sequential_aig.py index 96c6a54e..32e58c9d 100644 --- a/test/networks/test_sequential_aig.py +++ b/test/networks/test_sequential_aig.py @@ -144,6 +144,18 @@ def test_sequential_aig_to_index_list_raises() -> None: saig.to_index_list() +def test_sequential_aig_to_graph_tensors_raises() -> None: + saig = SequentialAig() + pi = saig.create_pi() + ro = saig.create_ro() + gate = saig.create_and(pi, ro) + saig.create_po(gate) + saig.create_ri(gate) + + with pytest.raises(TypeError, match="register state"): + saig.to_graph_tensors() + + def test_sequential_aig_clone_and_copy_preserve_wrapper_type() -> None: saig = SequentialAig() pi = saig.create_pi() From b77c9b83f18aac25594b7c4897513be19f647df1 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 11 Mar 2026 14:39:50 +0100 Subject: [PATCH 10/29] :memo: Update machine learning documentation for DLPack Tensors --- docs/machine_learning.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/docs/machine_learning.md b/docs/machine_learning.md index d090be83..4aadf9c9 100644 --- a/docs/machine_learning.md +++ b/docs/machine_learning.md @@ -51,7 +51,7 @@ dataset = [ print(len(dataset), dataset[0].num_pis, dataset[0].num_gates) ``` -### NetworkX +## NetworkX The [NetworkX](https://networkx.org/) adapter allows you to convert an AIG into a {py:class}`~networkx.DiGraph` object. This enables you to leverage the rich ecosystem of graph-based machine learning and data science tools that operate on @@ -128,14 +128,15 @@ plt.margins(x=0.2) plt.show() ``` -### DLPack Sparse Tensors +## DLPack Tensors -For high-throughput ML pipelines, `aigverse` can export graph tensors directly in a sparse COO-style layout where each -tensor implements the [DLPack](https://dmlc.github.io/dlpack/latest/) protocol. This allows zero-copy hand-off to modern +For high-throughput ML pipelines, `aigverse` can export AIG objects directly as graph tensors (node attributes, edge +indices, and edge attributes) utilizing the [DLPack](https://dmlc.github.io/dlpack/latest/) protocol. This allows zero-copy hand-off to modern tensor frameworks, such as [PyTorch](https://docs.pytorch.org/docs/stable/dlpack.html), [JAX](https://docs.jax.dev/en/latest/_autosummary/jax.dlpack.from_dlpack.html#jax.dlpack.from_dlpack), -[TensorFlow](https://www.tensorflow.org/api_docs/python/tf/experimental/dlpack/from_dlpack), etc., through `from_dlpack`. +[TensorFlow](https://www.tensorflow.org/api_docs/python/tf/experimental/dlpack/from_dlpack), etc., through +`from_dlpack`. ```{code-cell} ipython3 import torch @@ -177,7 +178,8 @@ sparse_adj = torch.sparse_coo_tensor( print(sparse_adj.shape) ``` -The same export also works with [NumPy's DLPack consumer API](https://numpy.org/doc/stable/release/1.22.0-notes.html#add-nep-47-compatible-dlpack-support): +The same export also works +with [NumPy's DLPack consumer API](https://numpy.org/doc/stable/release/1.22.0-notes.html#add-nep-47-compatible-dlpack-support): ```{code-cell} ipython3 import numpy as np @@ -215,7 +217,8 @@ start. A practical pattern is: 1. Materialize labels once as a NumPy array. 2. Build the input matrix with vectorized NumPy operations. -3. Convert to framework tensors (for example, [PyTorch](https://docs.pytorch.org/docs/stable/index.html)) without copying. +3. Convert to framework tensors (for example, [PyTorch](https://docs.pytorch.org/docs/stable/index.html)) without + copying. This keeps preprocessing fast and avoids Python-level loops in hot paths. From d160ea896b82efc96349801e016e09c990179322 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 11 Mar 2026 14:47:27 +0100 Subject: [PATCH 11/29] :memo: Enhance DLPack Tensors documentation on encoding, dtype mapping, and tensor shape conventions --- docs/machine_learning.md | 52 +++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/docs/machine_learning.md b/docs/machine_learning.md index 4aadf9c9..aebd7f1b 100644 --- a/docs/machine_learning.md +++ b/docs/machine_learning.md @@ -131,13 +131,39 @@ plt.show() ## DLPack Tensors For high-throughput ML pipelines, `aigverse` can export AIG objects directly as graph tensors (node attributes, edge -indices, and edge attributes) utilizing the [DLPack](https://dmlc.github.io/dlpack/latest/) protocol. This allows zero-copy hand-off to modern -tensor frameworks, such +indices, and edge attributes) utilizing the [DLPack](https://dmlc.github.io/dlpack/latest/) protocol. This allows +zero-copy hand-off to modern tensor frameworks, such as [PyTorch](https://docs.pytorch.org/docs/stable/dlpack.html), [JAX](https://docs.jax.dev/en/latest/_autosummary/jax.dlpack.from_dlpack.html#jax.dlpack.from_dlpack), [TensorFlow](https://www.tensorflow.org/api_docs/python/tf/experimental/dlpack/from_dlpack), etc., through `from_dlpack`. +Encoding and `dtype` mapping: + +- Edge encoding (`edge_attr`): + - `EdgeTensorEncoding.BINARY`: regular `0.0`, inverted `1.0` + - `EdgeTensorEncoding.SIGNED`: regular `+1.0`, inverted `-1.0` + - `EdgeTensorEncoding.ONE_HOT`: regular `[1.0, 0.0]`, inverted `[0.0, 1.0]` +- Node encoding (`node_attr`): + - `NodeTensorEncoding.INTEGER`: `constant=0`, `pi=1`, `gate=2`, `po=3` + - `NodeTensorEncoding.ONE_HOT`: `[constant, pi, gate, po]` +- Tensor `dtype`s: + - `edge_index`: `int64` + - `edge_attr`: `float32` + - `node_attr`: `float32` + +Tensor shapes follow the convention: + +$$ +\mathbf{edge\_index} \in \mathbb{Z}^{2 \times E}, \quad +\mathbf{edge\_attr} \in \mathbb{R}^{E \times D_{\text{edge}}}, \quad +\mathbf{node\_attr} \in \mathbb{R}^{N \times D_{\text{node}}} +$$ + +where $E$ is the number of edges and $N$ is the number of nodes. For edge features, +$D_{\text{edge}} = 1$ for `BINARY` and `SIGNED`, and $D_{\text{edge}} = 2$ for `ONE_HOT`. +The node feature width $D_{\text{node}}$ depends on the chosen node encoding and enabled optional features. + ```{code-cell} ipython3 import torch @@ -188,28 +214,6 @@ edge_index_np = np.from_dlpack(aig.to_graph_tensors()["edge_index"]) print(edge_index_np.shape) ``` -Encoding and dtype mapping: - -- Edge encoding (`edge_attr`): - - `EdgeTensorEncoding.BINARY`: regular `0.0`, inverted `1.0` - - `EdgeTensorEncoding.SIGNED`: regular `+1.0`, inverted `-1.0` - - `EdgeTensorEncoding.ONE_HOT`: regular `[1.0, 0.0]`, inverted `[0.0, 1.0]` -- Node encoding (`node_attr`): - - `NodeTensorEncoding.INTEGER`: `constant=0`, `pi=1`, `gate=2`, `po=3` - - `NodeTensorEncoding.ONE_HOT`: `[constant, pi, gate, po]` -- Tensor dtypes: - - `edge_index`: `int64` - - `edge_attr`: `float32` - - `node_attr`: `float32` - -The exported tensor shapes are: - -- `edge_index`: `(2, E)` -- `edge_attr`: `(E, D_edge)` -- `node_attr`: `(N, D_node)` - -where `D_edge` is `1` for `BINARY` and `SIGNED`, and `2` for `ONE_HOT`. - ## Truth Tables Truth tables are iterable, but for ML pipelines it is best to keep data in contiguous array/tensor form from the From 4b5158b5fd06a75c4b842d84ed8951f32d0b43c7 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 11 Mar 2026 15:07:33 +0100 Subject: [PATCH 12/29] :art: Address CodeRabbit's comments --- src/aigverse/networks/graph_tensors.hpp | 10 +++++-- test/networks/test_graph_tensors.py | 35 +++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/aigverse/networks/graph_tensors.hpp b/src/aigverse/networks/graph_tensors.hpp index c9bf4f4e..7769ae08 100644 --- a/src/aigverse/networks/graph_tensors.hpp +++ b/src/aigverse/networks/graph_tensors.hpp @@ -14,8 +14,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -176,6 +178,11 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ std::vector output_tts{}; if (include_truth_table) { + if (ntk.num_pis() > 16) + { + throw std::invalid_argument("truth-table export is only supported up to 16 primary inputs"); + } + node_tts = mockturtle::simulate_nodes( ntk, mockturtle::default_simulator{static_cast(ntk.num_pis())}); output_tts = mockturtle::simulate( @@ -199,7 +206,6 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ { depth_ntk.emplace(ntk); } - const auto po_level = include_level ? static_cast(depth_ntk->depth() + 1) : 0.0f; const auto fill_base = [&](const size_t row, const int64_t type_index) -> size_t { @@ -258,7 +264,7 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ if (include_level) { - node_attr[feature_offset++] = po_level; + node_attr[feature_offset++] = static_cast(depth_ntk->level(ntk.get_node(po)) + 1); } if (include_fanout) { diff --git a/test/networks/test_graph_tensors.py b/test/networks/test_graph_tensors.py index e876b63e..f73b8822 100644 --- a/test/networks/test_graph_tensors.py +++ b/test/networks/test_graph_tensors.py @@ -238,3 +238,38 @@ def test_to_graph_tensors_empty_aig_edge_case() -> None: assert edge_index.dtype == np.int64 assert edge_attr.dtype == np.float32 assert node_attr.dtype == np.float32 + + +def test_to_graph_tensors_truth_table_pi_limit_raises() -> None: + """Checks include_truth_table fails fast for too many primary inputs.""" + aig = Aig() + pis = [aig.create_pi() for _ in range(17)] + aig.create_po(pis[0]) + + with pytest.raises(ValueError, match="only supported up to 16 primary inputs"): + aig.to_graph_tensors(include_truth_table=True) + + +def test_to_graph_tensors_po_levels_follow_driver_depth() -> None: + """Checks each PO level is derived from its driving node depth.""" + aig = Aig() + a = aig.create_pi() + b = aig.create_pi() + gate = aig.create_and(a, b) + aig.create_po(a) + aig.create_po(gate) + + tensors = aig.to_graph_tensors( + node_encoding=NodeTensorEncoding.INTEGER, + edge_encoding=EdgeTensorEncoding.BINARY, + include_level=True, + include_fanout=False, + include_truth_table=False, + ) + + node_attr = np.from_dlpack(tensors["node_attr"]) + po_rows = node_attr[aig.size :, :] + + assert po_rows.shape[0] == 2 + assert po_rows[0, 1] == pytest.approx(1.0) + assert po_rows[1, 1] == pytest.approx(2.0) From e97d2eeeb7a5cf8d42e36d36fc83eec2a513bb3d Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 11 Mar 2026 15:08:23 +0100 Subject: [PATCH 13/29] :art: Address CodeRabbit's comments --- docs/machine_learning.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/machine_learning.md b/docs/machine_learning.md index aebd7f1b..05ff42f1 100644 --- a/docs/machine_learning.md +++ b/docs/machine_learning.md @@ -130,8 +130,8 @@ plt.show() ## DLPack Tensors -For high-throughput ML pipelines, `aigverse` can export AIG objects directly as graph tensors (node attributes, edge -indices, and edge attributes) utilizing the [DLPack](https://dmlc.github.io/dlpack/latest/) protocol. This allows +For high-throughput ML pipelines, `aigverse` can export combinational AIG objects directly as graph tensors (node +attributes, edge indices, and edge attributes) utilizing the [DLPack](https://dmlc.github.io/dlpack/latest/) protocol. This allows zero-copy hand-off to modern tensor frameworks, such as [PyTorch](https://docs.pytorch.org/docs/stable/dlpack.html), [JAX](https://docs.jax.dev/en/latest/_autosummary/jax.dlpack.from_dlpack.html#jax.dlpack.from_dlpack), From 553d81a7de52e8b0b55cdeb48dfd72a358e680fa Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 11 Mar 2026 16:57:13 +0100 Subject: [PATCH 14/29] :rotating_light: Fix `clang-tidy` warnings --- src/aigverse/networks/graph_tensors.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/aigverse/networks/graph_tensors.hpp b/src/aigverse/networks/graph_tensors.hpp index 7769ae08..1979efc2 100644 --- a/src/aigverse/networks/graph_tensors.hpp +++ b/src/aigverse/networks/graph_tensors.hpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include From 603534797ff910decf6351e2d84c4b3891e7d5fb Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 11 Mar 2026 16:57:27 +0100 Subject: [PATCH 15/29] :memo: Update machine learning documentation for DLPack Tensors with method details and current limitations --- docs/machine_learning.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/machine_learning.md b/docs/machine_learning.md index 05ff42f1..688bb8a9 100644 --- a/docs/machine_learning.md +++ b/docs/machine_learning.md @@ -130,8 +130,8 @@ plt.show() ## DLPack Tensors -For high-throughput ML pipelines, `aigverse` can export combinational AIG objects directly as graph tensors (node -attributes, edge indices, and edge attributes) utilizing the [DLPack](https://dmlc.github.io/dlpack/latest/) protocol. This allows +For high-throughput ML pipelines, `aigverse` can export AIG objects directly as graph tensors (node +attributes, edge indices, and edge attributes) utilizing the [DLPack](https://dmlc.github.io/dlpack/latest/) protocol via the {py:meth}`~aigverse.networks.Aig.to_graph_tensors` method. This allows zero-copy hand-off to modern tensor frameworks, such as [PyTorch](https://docs.pytorch.org/docs/stable/dlpack.html), [JAX](https://docs.jax.dev/en/latest/_autosummary/jax.dlpack.from_dlpack.html#jax.dlpack.from_dlpack), @@ -164,6 +164,15 @@ where $E$ is the number of edges and $N$ is the number of nodes. For edge featur $D_{\text{edge}} = 1$ for `BINARY` and `SIGNED`, and $D_{\text{edge}} = 2$ for `ONE_HOT`. The node feature width $D_{\text{node}}$ depends on the chosen node encoding and enabled optional features. +:::{note} +Current limitations of `to_graph_tensors`: + +- The export targets **combinational** networks. Sequential networks are not supported. +- Exported tensors are backed by **CPU host memory** (NumPy-backed DLPack producer). +- `torch.from_dlpack(...)` is zero-copy on CPU, but moving tensors to CUDA still allocates GPU memory and performs a host-to-device copy. +- When `include_truth_table=True`, the export is restricted to at most 16 primary inputs due to the exponential growth of truth table size. This is a practical limit for ML applications, but it is not a fundamental limitation of the API. + ::: + ```{code-cell} ipython3 import torch From f7cfbbae2320193bcd0362caa5cef41f3b0d6129 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 11 Mar 2026 17:04:48 +0100 Subject: [PATCH 16/29] :art: Streamline include paths --- src/aigverse/algorithms/refactoring.cpp | 3 +-- src/aigverse/algorithms/resubstitution.cpp | 3 +-- src/aigverse/networks/edge_list.cpp | 2 +- src/aigverse/networks/graph_tensors.hpp | 3 +-- src/aigverse/networks/index_list.cpp | 2 +- src/aigverse/networks/logic_networks.cpp | 7 +++---- 6 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/aigverse/algorithms/refactoring.cpp b/src/aigverse/algorithms/refactoring.cpp index f21c7e26..a8161e0a 100644 --- a/src/aigverse/algorithms/refactoring.cpp +++ b/src/aigverse/algorithms/refactoring.cpp @@ -2,10 +2,9 @@ // Created by marcel on 03.09.25. // +#include "aigverse/algorithms/transform_helpers.hpp" #include "aigverse/types.hpp" -#include "transform_helpers.hpp" - #include #include #include diff --git a/src/aigverse/algorithms/resubstitution.cpp b/src/aigverse/algorithms/resubstitution.cpp index 1c2832e0..68e0d283 100644 --- a/src/aigverse/algorithms/resubstitution.cpp +++ b/src/aigverse/algorithms/resubstitution.cpp @@ -2,10 +2,9 @@ // Created by marcel on 03.09.25. // +#include "aigverse/algorithms/transform_helpers.hpp" #include "aigverse/types.hpp" -#include "transform_helpers.hpp" - #include #include #include diff --git a/src/aigverse/networks/edge_list.cpp b/src/aigverse/networks/edge_list.cpp index d103fe17..677eafa9 100644 --- a/src/aigverse/networks/edge_list.cpp +++ b/src/aigverse/networks/edge_list.cpp @@ -2,7 +2,7 @@ // Created by marcel on 04.09.25. // -#include "edge_list.hpp" +#include "aigverse/networks/edge_list.hpp" #include "aigverse/types.hpp" diff --git a/src/aigverse/networks/graph_tensors.hpp b/src/aigverse/networks/graph_tensors.hpp index 1979efc2..1ca6970c 100644 --- a/src/aigverse/networks/graph_tensors.hpp +++ b/src/aigverse/networks/graph_tensors.hpp @@ -1,9 +1,8 @@ #pragma once +#include "aigverse/networks/edge_list.hpp" #include "aigverse/types.hpp" -#include "edge_list.hpp" - #include #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) diff --git a/src/aigverse/networks/index_list.cpp b/src/aigverse/networks/index_list.cpp index af142d66..f1740109 100644 --- a/src/aigverse/networks/index_list.cpp +++ b/src/aigverse/networks/index_list.cpp @@ -2,7 +2,7 @@ // Created by marcel on 04.09.25. // -#include "index_list.hpp" +#include "aigverse/networks/index_list.hpp" #include "aigverse/types.hpp" diff --git a/src/aigverse/networks/logic_networks.cpp b/src/aigverse/networks/logic_networks.cpp index b525b070..153adeac 100644 --- a/src/aigverse/networks/logic_networks.cpp +++ b/src/aigverse/networks/logic_networks.cpp @@ -2,12 +2,11 @@ // Created by marcel on 03.09.25. // +#include "aigverse/networks/edge_list.hpp" +#include "aigverse/networks/graph_tensors.hpp" +#include "aigverse/networks/index_list.hpp" #include "aigverse/types.hpp" -#include "edge_list.hpp" -#include "graph_tensors.hpp" -#include "index_list.hpp" - #include #include #include From 9782c4b55f77a9e084e0b8bf2ee58ffe61c698b9 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Sun, 15 Mar 2026 16:52:27 +0100 Subject: [PATCH 17/29] :lock: Update lockfile --- uv.lock | 527 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 275 insertions(+), 252 deletions(-) diff --git a/uv.lock b/uv.lock index 41d366df..d3aa67ee 100644 --- a/uv.lock +++ b/uv.lock @@ -28,7 +28,7 @@ adapters = [ { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "torch" }, ] @@ -37,7 +37,7 @@ adapters = [ { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "torch" }, ] build = [ @@ -54,7 +54,7 @@ dev = [ { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "nox" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pygraphviz" }, { name = "pytest" }, { name = "pytest-sugar" }, @@ -80,7 +80,7 @@ docs = [ { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pygraphviz" }, { name = "setuptools-scm" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, @@ -102,7 +102,7 @@ test = [ { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pytest" }, { name = "pytest-sugar" }, { name = "pytest-xdist" }, @@ -562,7 +562,7 @@ resolution-markers = [ "python_full_version == '3.11.*'", ] dependencies = [ - { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } wheels = [ @@ -658,10 +658,10 @@ wheels = [ [[package]] name = "cuda-pathfinder" -version = "1.4.1" +version = "1.4.2" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/02/59a5bc738a09def0b49aea0e460bdf97f65206d0d041246147cf6207e69c/cuda_pathfinder-1.4.1-py3-none-any.whl", hash = "sha256:40793006082de88e0950753655e55558a446bed9a7d9d0bcb48b2506d50ed82a", size = 43903, upload-time = "2026-03-06T21:05:24.372Z" }, + { url = "https://files.pythonhosted.org/packages/92/de/8ca2b613042550dcf9ef50c596c8b1f602afda92cf9032ac28a73f6ee410/cuda_pathfinder-1.4.2-py3-none-any.whl", hash = "sha256:eb354abc20278f8609dc5b666a24648655bef5613c6dfe78a238a6fd95566754", size = 44779, upload-time = "2026-03-10T21:57:30.974Z" }, ] [[package]] @@ -799,68 +799,68 @@ wheels = [ [[package]] name = "filelock" -version = "3.25.0" +version = "3.25.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/77/18/a1fd2231c679dcb9726204645721b12498aeac28e1ad0601038f94b42556/filelock-3.25.0.tar.gz", hash = "sha256:8f00faf3abf9dc730a1ffe9c354ae5c04e079ab7d3a683b7c32da5dd05f26af3", size = 40158, upload-time = "2026-03-01T15:08:45.916Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/b8/00651a0f559862f3bb7d6f7477b192afe3f583cc5e26403b44e59a55ab34/filelock-3.25.2.tar.gz", hash = "sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694", size = 40480, upload-time = "2026-03-11T20:45:38.487Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/0b/de6f54d4a8bedfe8645c41497f3c18d749f0bd3218170c667bf4b81d0cdd/filelock-3.25.0-py3-none-any.whl", hash = "sha256:5ccf8069f7948f494968fc0713c10e5c182a9c9d9eef3a636307a20c2490f047", size = 26427, upload-time = "2026-03-01T15:08:44.593Z" }, + { url = "https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl", hash = "sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70", size = 26759, upload-time = "2026-03-11T20:45:37.437Z" }, ] [[package]] name = "fonttools" -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" }, +version = "4.62.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/08/7012b00a9a5874311b639c3920270c36ee0c445b69d9989a85e5c92ebcb0/fonttools-4.62.1.tar.gz", hash = "sha256:e54c75fd6041f1122476776880f7c3c3295ffa31962dc6ebe2543c00dca58b5d", size = 3580737, upload-time = "2026-03-13T13:54:25.52Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/ff/532ed43808b469c807e8cb6b21358da3fe6fd51486b3a8c93db0bb5d957f/fonttools-4.62.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ad5cca75776cd453b1b035b530e943334957ae152a36a88a320e779d61fc980c", size = 2873740, upload-time = "2026-03-13T13:52:11.822Z" }, + { url = "https://files.pythonhosted.org/packages/85/e4/2318d2b430562da7227010fb2bb029d2fa54d7b46443ae8942bab224e2a0/fonttools-4.62.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0b3ae47e8636156a9accff64c02c0924cbebad62854c4a6dbdc110cd5b4b341a", size = 2417649, upload-time = "2026-03-13T13:52:14.605Z" }, + { url = "https://files.pythonhosted.org/packages/4c/28/40f15523b5188598018e7956899fed94eb7debec89e2dd70cb4a8df90492/fonttools-4.62.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9b9e288b4da2f64fd6180644221749de651703e8d0c16bd4b719533a3a7d6e3", size = 4935213, upload-time = "2026-03-13T13:52:17.399Z" }, + { url = "https://files.pythonhosted.org/packages/42/09/7dbe3d7023f57d9b580cfa832109d521988112fd59dddfda3fddda8218f9/fonttools-4.62.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7bca7a1c1faf235ffe25d4f2e555246b4750220b38de8261d94ebc5ce8a23c23", size = 4892374, upload-time = "2026-03-13T13:52:20.175Z" }, + { url = "https://files.pythonhosted.org/packages/d1/2d/84509a2e32cb925371560ef5431365d8da2183c11d98e5b4b8b4e42426a5/fonttools-4.62.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b4e0fcf265ad26e487c56cb12a42dffe7162de708762db951e1b3f755319507d", size = 4911856, upload-time = "2026-03-13T13:52:22.777Z" }, + { url = "https://files.pythonhosted.org/packages/a5/80/df28131379eed93d9e6e6fccd3bf6e3d077bebbfe98cc83f21bbcd83ed02/fonttools-4.62.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2d850f66830a27b0d498ee05adb13a3781637b1826982cd7e2b3789ef0cc71ae", size = 5031712, upload-time = "2026-03-13T13:52:25.14Z" }, + { url = "https://files.pythonhosted.org/packages/3d/03/3c8f09aad64230cd6d921ae7a19f9603c36f70930b00459f112706f6769a/fonttools-4.62.1-cp310-cp310-win32.whl", hash = "sha256:486f32c8047ccd05652aba17e4a8819a3a9d78570eb8a0e3b4503142947880ed", size = 1507878, upload-time = "2026-03-13T13:52:28.149Z" }, + { url = "https://files.pythonhosted.org/packages/dd/ec/f53f626f8f3e89f4cadd8fc08f3452c8fd182c951ad5caa35efac22b29ab/fonttools-4.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:5a648bde915fba9da05ae98856987ca91ba832949a9e2888b48c47ef8b96c5a9", size = 1556766, upload-time = "2026-03-13T13:52:30.814Z" }, + { url = "https://files.pythonhosted.org/packages/88/39/23ff32561ec8d45a4d48578b4d241369d9270dc50926c017570e60893701/fonttools-4.62.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:40975849bac44fb0b9253d77420c6d8b523ac4dcdcefeff6e4d706838a5b80f7", size = 2871039, upload-time = "2026-03-13T13:52:33.127Z" }, + { url = "https://files.pythonhosted.org/packages/24/7f/66d3f8a9338a9b67fe6e1739f47e1cd5cee78bd3bc1206ef9b0b982289a5/fonttools-4.62.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9dde91633f77fa576879a0c76b1d89de373cae751a98ddf0109d54e173b40f14", size = 2416346, upload-time = "2026-03-13T13:52:35.676Z" }, + { url = "https://files.pythonhosted.org/packages/aa/53/5276ceba7bff95da7793a07c5284e1da901cf00341ce5e2f3273056c0cca/fonttools-4.62.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6acb4109f8bee00fec985c8c7afb02299e35e9c94b57287f3ea542f28bd0b0a7", size = 5100897, upload-time = "2026-03-13T13:52:38.102Z" }, + { url = "https://files.pythonhosted.org/packages/cc/a1/40a5c4d8e28b0851d53a8eeeb46fbd73c325a2a9a165f290a5ed90e6c597/fonttools-4.62.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1c5c25671ce8805e0d080e2ffdeca7f1e86778c5cbfbeae86d7f866d8830517b", size = 5071078, upload-time = "2026-03-13T13:52:41.305Z" }, + { url = "https://files.pythonhosted.org/packages/e3/be/d378fca4c65ea1956fee6d90ace6e861776809cbbc5af22388a090c3c092/fonttools-4.62.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a5d8825e1140f04e6c99bb7d37a9e31c172f3bc208afbe02175339e699c710e1", size = 5076908, upload-time = "2026-03-13T13:52:44.122Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d9/ae6a1d0693a4185a84605679c8a1f719a55df87b9c6e8e817bfdd9ef5936/fonttools-4.62.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:268abb1cb221e66c014acc234e872b7870d8b5d4657a83a8f4205094c32d2416", size = 5202275, upload-time = "2026-03-13T13:52:46.591Z" }, + { url = "https://files.pythonhosted.org/packages/54/6c/af95d9c4efb15cabff22642b608342f2bd67137eea6107202d91b5b03184/fonttools-4.62.1-cp311-cp311-win32.whl", hash = "sha256:942b03094d7edbb99bdf1ae7e9090898cad7bf9030b3d21f33d7072dbcb51a53", size = 2293075, upload-time = "2026-03-13T13:52:48.711Z" }, + { url = "https://files.pythonhosted.org/packages/d3/97/bf54c5b3f2be34e1f143e6db838dfdc54f2ffa3e68c738934c82f3b2a08d/fonttools-4.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:e8514f4924375f77084e81467e63238b095abda5107620f49421c368a6017ed2", size = 2344593, upload-time = "2026-03-13T13:52:50.725Z" }, + { url = "https://files.pythonhosted.org/packages/47/d4/dbacced3953544b9a93088cc10ef2b596d348c983d5c67a404fa41ec51ba/fonttools-4.62.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:90365821debbd7db678809c7491ca4acd1e0779b9624cdc6ddaf1f31992bf974", size = 2870219, upload-time = "2026-03-13T13:52:53.664Z" }, + { url = "https://files.pythonhosted.org/packages/66/9e/a769c8e99b81e5a87ab7e5e7236684de4e96246aae17274e5347d11ebd78/fonttools-4.62.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12859ff0b47dd20f110804c3e0d0970f7b832f561630cd879969011541a464a9", size = 2414891, upload-time = "2026-03-13T13:52:56.493Z" }, + { url = "https://files.pythonhosted.org/packages/69/64/f19a9e3911968c37e1e620e14dfc5778299e1474f72f4e57c5ec771d9489/fonttools-4.62.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c125ffa00c3d9003cdaaf7f2c79e6e535628093e14b5de1dccb08859b680936", size = 5033197, upload-time = "2026-03-13T13:52:59.179Z" }, + { url = "https://files.pythonhosted.org/packages/9b/8a/99c8b3c3888c5c474c08dbfd7c8899786de9604b727fcefb055b42c84bba/fonttools-4.62.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:149f7d84afca659d1a97e39a4778794a2f83bf344c5ee5134e09995086cc2392", size = 4988768, upload-time = "2026-03-13T13:53:02.761Z" }, + { url = "https://files.pythonhosted.org/packages/d1/c6/0f904540d3e6ab463c1243a0d803504826a11604c72dd58c2949796a1762/fonttools-4.62.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0aa72c43a601cfa9273bb1ae0518f1acadc01ee181a6fc60cd758d7fdadffc04", size = 4971512, upload-time = "2026-03-13T13:53:05.678Z" }, + { url = "https://files.pythonhosted.org/packages/29/0b/5cbef6588dc9bd6b5c9ad6a4d5a8ca384d0cea089da31711bbeb4f9654a6/fonttools-4.62.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:19177c8d96c7c36359266e571c5173bcee9157b59cfc8cb0153c5673dc5a3a7d", size = 5122723, upload-time = "2026-03-13T13:53:08.662Z" }, + { url = "https://files.pythonhosted.org/packages/4a/47/b3a5342d381595ef439adec67848bed561ab7fdb1019fa522e82101b7d9c/fonttools-4.62.1-cp312-cp312-win32.whl", hash = "sha256:a24decd24d60744ee8b4679d38e88b8303d86772053afc29b19d23bb8207803c", size = 2281278, upload-time = "2026-03-13T13:53:10.998Z" }, + { url = "https://files.pythonhosted.org/packages/28/b1/0c2ab56a16f409c6c8a68816e6af707827ad5d629634691ff60a52879792/fonttools-4.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:9e7863e10b3de72376280b515d35b14f5eeed639d1aa7824f4cf06779ec65e42", size = 2331414, upload-time = "2026-03-13T13:53:13.992Z" }, + { url = "https://files.pythonhosted.org/packages/3b/56/6f389de21c49555553d6a5aeed5ac9767631497ac836c4f076273d15bd72/fonttools-4.62.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c22b1014017111c401469e3acc5433e6acf6ebcc6aa9efb538a533c800971c79", size = 2865155, upload-time = "2026-03-13T13:53:16.132Z" }, + { url = "https://files.pythonhosted.org/packages/03/c5/0e3966edd5ec668d41dfe418787726752bc07e2f5fd8c8f208615e61fa89/fonttools-4.62.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68959f5fc58ed4599b44aad161c2837477d7f35f5f79402d97439974faebfebe", size = 2412802, upload-time = "2026-03-13T13:53:18.878Z" }, + { url = "https://files.pythonhosted.org/packages/52/94/e6ac4b44026de7786fe46e3bfa0c87e51d5d70a841054065d49cd62bb909/fonttools-4.62.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef46db46c9447103b8f3ff91e8ba009d5fe181b1920a83757a5762551e32bb68", size = 5013926, upload-time = "2026-03-13T13:53:21.379Z" }, + { url = "https://files.pythonhosted.org/packages/e2/98/8b1e801939839d405f1f122e7d175cebe9aeb4e114f95bfc45e3152af9a7/fonttools-4.62.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6706d1cb1d5e6251a97ad3c1b9347505c5615c112e66047abbef0f8545fa30d1", size = 4964575, upload-time = "2026-03-13T13:53:23.857Z" }, + { url = "https://files.pythonhosted.org/packages/46/76/7d051671e938b1881670528fec69cc4044315edd71a229c7fd712eaa5119/fonttools-4.62.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e7abd2b1e11736f58c1de27819e1955a53267c21732e78243fa2fa2e5c1e069", size = 4953693, upload-time = "2026-03-13T13:53:26.569Z" }, + { url = "https://files.pythonhosted.org/packages/1f/ae/b41f8628ec0be3c1b934fc12b84f4576a5c646119db4d3bdd76a217c90b5/fonttools-4.62.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:403d28ce06ebfc547fbcb0cb8b7f7cc2f7a2d3e1a67ba9a34b14632df9e080f9", size = 5094920, upload-time = "2026-03-13T13:53:29.329Z" }, + { url = "https://files.pythonhosted.org/packages/f2/f6/53a1e9469331a23dcc400970a27a4caa3d9f6edbf5baab0260285238b884/fonttools-4.62.1-cp313-cp313-win32.whl", hash = "sha256:93c316e0f5301b2adbe6a5f658634307c096fd5aae60a5b3412e4f3e1728ab24", size = 2279928, upload-time = "2026-03-13T13:53:32.352Z" }, + { url = "https://files.pythonhosted.org/packages/38/60/35186529de1db3c01f5ad625bde07c1f576305eab6d86bbda4c58445f721/fonttools-4.62.1-cp313-cp313-win_amd64.whl", hash = "sha256:7aa21ff53e28a9c2157acbc44e5b401149d3c9178107130e82d74ceb500e5056", size = 2330514, upload-time = "2026-03-13T13:53:34.991Z" }, + { url = "https://files.pythonhosted.org/packages/36/f0/2888cdac391807d68d90dcb16ef858ddc1b5309bfc6966195a459dd326e2/fonttools-4.62.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fa1d16210b6b10a826d71bed68dd9ec24a9e218d5a5e2797f37c573e7ec215ca", size = 2864442, upload-time = "2026-03-13T13:53:37.509Z" }, + { url = "https://files.pythonhosted.org/packages/4b/b2/e521803081f8dc35990816b82da6360fa668a21b44da4b53fc9e77efcd62/fonttools-4.62.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:aa69d10ed420d8121118e628ad47d86e4caa79ba37f968597b958f6cceab7eca", size = 2410901, upload-time = "2026-03-13T13:53:40.55Z" }, + { url = "https://files.pythonhosted.org/packages/00/a4/8c3511ff06e53110039358dbbdc1a65d72157a054638387aa2ada300a8b8/fonttools-4.62.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd13b7999d59c5eb1c2b442eb2d0c427cb517a0b7a1f5798fc5c9e003f5ff782", size = 4999608, upload-time = "2026-03-13T13:53:42.798Z" }, + { url = "https://files.pythonhosted.org/packages/28/63/cd0c3b26afe60995a5295f37c246a93d454023726c3261cfbb3559969bb9/fonttools-4.62.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8d337fdd49a79b0d51c4da87bc38169d21c3abbf0c1aa9367eff5c6656fb6dae", size = 4912726, upload-time = "2026-03-13T13:53:45.405Z" }, + { url = "https://files.pythonhosted.org/packages/70/b9/ac677cb07c24c685cf34f64e140617d58789d67a3dd524164b63648c6114/fonttools-4.62.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d241cdc4a67b5431c6d7f115fdf63335222414995e3a1df1a41e1182acd4bcc7", size = 4951422, upload-time = "2026-03-13T13:53:48.326Z" }, + { url = "https://files.pythonhosted.org/packages/e6/10/11c08419a14b85b7ca9a9faca321accccc8842dd9e0b1c8a72908de05945/fonttools-4.62.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c05557a78f8fa514da0f869556eeda40887a8abc77c76ee3f74cf241778afd5a", size = 5060979, upload-time = "2026-03-13T13:53:51.366Z" }, + { url = "https://files.pythonhosted.org/packages/4e/3c/12eea4a4cf054e7ab058ed5ceada43b46809fce2bf319017c4d63ae55bb4/fonttools-4.62.1-cp314-cp314-win32.whl", hash = "sha256:49a445d2f544ce4a69338694cad575ba97b9a75fff02720da0882d1a73f12800", size = 2283733, upload-time = "2026-03-13T13:53:53.606Z" }, + { url = "https://files.pythonhosted.org/packages/6b/67/74b070029043186b5dd13462c958cb7c7f811be0d2e634309d9a1ffb1505/fonttools-4.62.1-cp314-cp314-win_amd64.whl", hash = "sha256:1eecc128c86c552fb963fe846ca4e011b1be053728f798185a1687502f6d398e", size = 2335663, upload-time = "2026-03-13T13:53:56.23Z" }, + { url = "https://files.pythonhosted.org/packages/42/c5/4d2ed3ca6e33617fc5624467da353337f06e7f637707478903c785bd8e20/fonttools-4.62.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:1596aeaddf7f78e21e68293c011316a25267b3effdaccaf4d59bc9159d681b82", size = 2947288, upload-time = "2026-03-13T13:53:59.397Z" }, + { url = "https://files.pythonhosted.org/packages/1f/e9/7ab11ddfda48ed0f89b13380e5595ba572619c27077be0b2c447a63ff351/fonttools-4.62.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:8f8fca95d3bb3208f59626a4b0ea6e526ee51f5a8ad5d91821c165903e8d9260", size = 2449023, upload-time = "2026-03-13T13:54:01.642Z" }, + { url = "https://files.pythonhosted.org/packages/b2/10/a800fa090b5e8819942e54e19b55fc7c21fe14a08757c3aa3ca8db358939/fonttools-4.62.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee91628c08e76f77b533d65feb3fbe6d9dad699f95be51cf0d022db94089cdc4", size = 5137599, upload-time = "2026-03-13T13:54:04.495Z" }, + { url = "https://files.pythonhosted.org/packages/37/dc/8ccd45033fffd74deb6912fa1ca524643f584b94c87a16036855b498a1ed/fonttools-4.62.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5f37df1cac61d906e7b836abe356bc2f34c99d4477467755c216b72aa3dc748b", size = 4920933, upload-time = "2026-03-13T13:54:07.557Z" }, + { url = "https://files.pythonhosted.org/packages/99/eb/e618adefb839598d25ac8136cd577925d6c513dc0d931d93b8af956210f0/fonttools-4.62.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:92bb00a947e666169c99b43753c4305fc95a890a60ef3aeb2a6963e07902cc87", size = 5016232, upload-time = "2026-03-13T13:54:10.611Z" }, + { url = "https://files.pythonhosted.org/packages/d9/5f/9b5c9bfaa8ec82def8d8168c4f13615990d6ce5996fe52bd49bfb5e05134/fonttools-4.62.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bdfe592802ef939a0e33106ea4a318eeb17822c7ee168c290273cbd5fabd746c", size = 5042987, upload-time = "2026-03-13T13:54:13.569Z" }, + { url = "https://files.pythonhosted.org/packages/90/aa/dfbbe24c6a6afc5c203d90cc0343e24bcbb09e76d67c4d6eef8c2558d7ba/fonttools-4.62.1-cp314-cp314t-win32.whl", hash = "sha256:b820fcb92d4655513d8402d5b219f94481c4443d825b4372c75a2072aa4b357a", size = 2348021, upload-time = "2026-03-13T13:54:16.98Z" }, + { url = "https://files.pythonhosted.org/packages/13/6f/ae9c4e4dd417948407b680855c2c7790efb52add6009aaecff1e3bc50e8e/fonttools-4.62.1-cp314-cp314t-win_amd64.whl", hash = "sha256:59b372b4f0e113d3746b88985f1c796e7bf830dd54b28374cd85c2b8acd7583e", size = 2414147, upload-time = "2026-03-13T13:54:19.416Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ba/56147c165442cc5ba7e82ecf301c9a68353cede498185869e6e02b4c264f/fonttools-4.62.1-py3-none-any.whl", hash = "sha256:7487782e2113861f4ddcc07c3436450659e3caa5e470b27dc2177cade2d8e7fd", size = 1152647, upload-time = "2026-03-13T13:54:22.735Z" }, ] [[package]] @@ -1211,110 +1211,126 @@ wheels = [ [[package]] name = "kiwisolver" -version = "1.4.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/3c/85844f1b0feb11ee581ac23fe5fce65cd049a200c1446708cc1b7f922875/kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d", size = 97564, upload-time = "2025-08-10T21:27:49.279Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/5d/8ce64e36d4e3aac5ca96996457dcf33e34e6051492399a3f1fec5657f30b/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b4b4d74bda2b8ebf4da5bd42af11d02d04428b2c32846e4c2c93219df8a7987b", size = 124159, upload-time = "2025-08-10T21:25:35.472Z" }, - { url = "https://files.pythonhosted.org/packages/96/1e/22f63ec454874378175a5f435d6ea1363dd33fb2af832c6643e4ccea0dc8/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fb3b8132019ea572f4611d770991000d7f58127560c4889729248eb5852a102f", size = 66578, upload-time = "2025-08-10T21:25:36.73Z" }, - { url = "https://files.pythonhosted.org/packages/41/4c/1925dcfff47a02d465121967b95151c82d11027d5ec5242771e580e731bd/kiwisolver-1.4.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84fd60810829c27ae375114cd379da1fa65e6918e1da405f356a775d49a62bcf", size = 65312, upload-time = "2025-08-10T21:25:37.658Z" }, - { url = "https://files.pythonhosted.org/packages/d4/42/0f333164e6307a0687d1eb9ad256215aae2f4bd5d28f4653d6cd319a3ba3/kiwisolver-1.4.9-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b78efa4c6e804ecdf727e580dbb9cba85624d2e1c6b5cb059c66290063bd99a9", size = 1628458, upload-time = "2025-08-10T21:25:39.067Z" }, - { url = "https://files.pythonhosted.org/packages/86/b6/2dccb977d651943995a90bfe3495c2ab2ba5cd77093d9f2318a20c9a6f59/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4efec7bcf21671db6a3294ff301d2fc861c31faa3c8740d1a94689234d1b415", size = 1225640, upload-time = "2025-08-10T21:25:40.489Z" }, - { url = "https://files.pythonhosted.org/packages/50/2b/362ebd3eec46c850ccf2bfe3e30f2fc4c008750011f38a850f088c56a1c6/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:90f47e70293fc3688b71271100a1a5453aa9944a81d27ff779c108372cf5567b", size = 1244074, upload-time = "2025-08-10T21:25:42.221Z" }, - { url = "https://files.pythonhosted.org/packages/6f/bb/f09a1e66dab8984773d13184a10a29fe67125337649d26bdef547024ed6b/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fdca1def57a2e88ef339de1737a1449d6dbf5fab184c54a1fca01d541317154", size = 1293036, upload-time = "2025-08-10T21:25:43.801Z" }, - { url = "https://files.pythonhosted.org/packages/ea/01/11ecf892f201cafda0f68fa59212edaea93e96c37884b747c181303fccd1/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9cf554f21be770f5111a1690d42313e140355e687e05cf82cb23d0a721a64a48", size = 2175310, upload-time = "2025-08-10T21:25:45.045Z" }, - { url = "https://files.pythonhosted.org/packages/7f/5f/bfe11d5b934f500cc004314819ea92427e6e5462706a498c1d4fc052e08f/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1795ac5cd0510207482c3d1d3ed781143383b8cfd36f5c645f3897ce066220", size = 2270943, upload-time = "2025-08-10T21:25:46.393Z" }, - { url = "https://files.pythonhosted.org/packages/3d/de/259f786bf71f1e03e73d87e2db1a9a3bcab64d7b4fd780167123161630ad/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ccd09f20ccdbbd341b21a67ab50a119b64a403b09288c27481575105283c1586", size = 2440488, upload-time = "2025-08-10T21:25:48.074Z" }, - { url = "https://files.pythonhosted.org/packages/1b/76/c989c278faf037c4d3421ec07a5c452cd3e09545d6dae7f87c15f54e4edf/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:540c7c72324d864406a009d72f5d6856f49693db95d1fbb46cf86febef873634", size = 2246787, upload-time = "2025-08-10T21:25:49.442Z" }, - { url = "https://files.pythonhosted.org/packages/a2/55/c2898d84ca440852e560ca9f2a0d28e6e931ac0849b896d77231929900e7/kiwisolver-1.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:ede8c6d533bc6601a47ad4046080d36b8fc99f81e6f1c17b0ac3c2dc91ac7611", size = 73730, upload-time = "2025-08-10T21:25:51.102Z" }, - { url = "https://files.pythonhosted.org/packages/e8/09/486d6ac523dd33b80b368247f238125d027964cfacb45c654841e88fb2ae/kiwisolver-1.4.9-cp310-cp310-win_arm64.whl", hash = "sha256:7b4da0d01ac866a57dd61ac258c5607b4cd677f63abaec7b148354d2b2cdd536", size = 65036, upload-time = "2025-08-10T21:25:52.063Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ab/c80b0d5a9d8a1a65f4f815f2afff9798b12c3b9f31f1d304dd233dd920e2/kiwisolver-1.4.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eb14a5da6dc7642b0f3a18f13654847cd8b7a2550e2645a5bda677862b03ba16", size = 124167, upload-time = "2025-08-10T21:25:53.403Z" }, - { url = "https://files.pythonhosted.org/packages/a0/c0/27fe1a68a39cf62472a300e2879ffc13c0538546c359b86f149cc19f6ac3/kiwisolver-1.4.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:39a219e1c81ae3b103643d2aedb90f1ef22650deb266ff12a19e7773f3e5f089", size = 66579, upload-time = "2025-08-10T21:25:54.79Z" }, - { url = "https://files.pythonhosted.org/packages/31/a2/a12a503ac1fd4943c50f9822678e8015a790a13b5490354c68afb8489814/kiwisolver-1.4.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2405a7d98604b87f3fc28b1716783534b1b4b8510d8142adca34ee0bc3c87543", size = 65309, upload-time = "2025-08-10T21:25:55.76Z" }, - { url = "https://files.pythonhosted.org/packages/66/e1/e533435c0be77c3f64040d68d7a657771194a63c279f55573188161e81ca/kiwisolver-1.4.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dc1ae486f9abcef254b5618dfb4113dd49f94c68e3e027d03cf0143f3f772b61", size = 1435596, upload-time = "2025-08-10T21:25:56.861Z" }, - { url = "https://files.pythonhosted.org/packages/67/1e/51b73c7347f9aabdc7215aa79e8b15299097dc2f8e67dee2b095faca9cb0/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a1f570ce4d62d718dce3f179ee78dac3b545ac16c0c04bb363b7607a949c0d1", size = 1246548, upload-time = "2025-08-10T21:25:58.246Z" }, - { url = "https://files.pythonhosted.org/packages/21/aa/72a1c5d1e430294f2d32adb9542719cfb441b5da368d09d268c7757af46c/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb27e7b78d716c591e88e0a09a2139c6577865d7f2e152488c2cc6257f460872", size = 1263618, upload-time = "2025-08-10T21:25:59.857Z" }, - { url = "https://files.pythonhosted.org/packages/a3/af/db1509a9e79dbf4c260ce0cfa3903ea8945f6240e9e59d1e4deb731b1a40/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:15163165efc2f627eb9687ea5f3a28137217d217ac4024893d753f46bce9de26", size = 1317437, upload-time = "2025-08-10T21:26:01.105Z" }, - { url = "https://files.pythonhosted.org/packages/e0/f2/3ea5ee5d52abacdd12013a94130436e19969fa183faa1e7c7fbc89e9a42f/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bdee92c56a71d2b24c33a7d4c2856bd6419d017e08caa7802d2963870e315028", size = 2195742, upload-time = "2025-08-10T21:26:02.675Z" }, - { url = "https://files.pythonhosted.org/packages/6f/9b/1efdd3013c2d9a2566aa6a337e9923a00590c516add9a1e89a768a3eb2fc/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:412f287c55a6f54b0650bd9b6dce5aceddb95864a1a90c87af16979d37c89771", size = 2290810, upload-time = "2025-08-10T21:26:04.009Z" }, - { url = "https://files.pythonhosted.org/packages/fb/e5/cfdc36109ae4e67361f9bc5b41323648cb24a01b9ade18784657e022e65f/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2c93f00dcba2eea70af2be5f11a830a742fe6b579a1d4e00f47760ef13be247a", size = 2461579, upload-time = "2025-08-10T21:26:05.317Z" }, - { url = "https://files.pythonhosted.org/packages/62/86/b589e5e86c7610842213994cdea5add00960076bef4ae290c5fa68589cac/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f117e1a089d9411663a3207ba874f31be9ac8eaa5b533787024dc07aeb74f464", size = 2268071, upload-time = "2025-08-10T21:26:06.686Z" }, - { url = "https://files.pythonhosted.org/packages/3b/c6/f8df8509fd1eee6c622febe54384a96cfaf4d43bf2ccec7a0cc17e4715c9/kiwisolver-1.4.9-cp311-cp311-win_amd64.whl", hash = "sha256:be6a04e6c79819c9a8c2373317d19a96048e5a3f90bec587787e86a1153883c2", size = 73840, upload-time = "2025-08-10T21:26:07.94Z" }, - { url = "https://files.pythonhosted.org/packages/e2/2d/16e0581daafd147bc11ac53f032a2b45eabac897f42a338d0a13c1e5c436/kiwisolver-1.4.9-cp311-cp311-win_arm64.whl", hash = "sha256:0ae37737256ba2de764ddc12aed4956460277f00c4996d51a197e72f62f5eec7", size = 65159, upload-time = "2025-08-10T21:26:09.048Z" }, - { url = "https://files.pythonhosted.org/packages/86/c9/13573a747838aeb1c76e3267620daa054f4152444d1f3d1a2324b78255b5/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ac5a486ac389dddcc5bef4f365b6ae3ffff2c433324fb38dd35e3fab7c957999", size = 123686, upload-time = "2025-08-10T21:26:10.034Z" }, - { url = "https://files.pythonhosted.org/packages/51/ea/2ecf727927f103ffd1739271ca19c424d0e65ea473fbaeea1c014aea93f6/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2ba92255faa7309d06fe44c3a4a97efe1c8d640c2a79a5ef728b685762a6fd2", size = 66460, upload-time = "2025-08-10T21:26:11.083Z" }, - { url = "https://files.pythonhosted.org/packages/5b/5a/51f5464373ce2aeb5194508298a508b6f21d3867f499556263c64c621914/kiwisolver-1.4.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a2899935e724dd1074cb568ce7ac0dce28b2cd6ab539c8e001a8578eb106d14", size = 64952, upload-time = "2025-08-10T21:26:12.058Z" }, - { url = "https://files.pythonhosted.org/packages/70/90/6d240beb0f24b74371762873e9b7f499f1e02166a2d9c5801f4dbf8fa12e/kiwisolver-1.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f6008a4919fdbc0b0097089f67a1eb55d950ed7e90ce2cc3e640abadd2757a04", size = 1474756, upload-time = "2025-08-10T21:26:13.096Z" }, - { url = "https://files.pythonhosted.org/packages/12/42/f36816eaf465220f683fb711efdd1bbf7a7005a2473d0e4ed421389bd26c/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:67bb8b474b4181770f926f7b7d2f8c0248cbcb78b660fdd41a47054b28d2a752", size = 1276404, upload-time = "2025-08-10T21:26:14.457Z" }, - { url = "https://files.pythonhosted.org/packages/2e/64/bc2de94800adc830c476dce44e9b40fd0809cddeef1fde9fcf0f73da301f/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2327a4a30d3ee07d2fbe2e7933e8a37c591663b96ce42a00bc67461a87d7df77", size = 1294410, upload-time = "2025-08-10T21:26:15.73Z" }, - { url = "https://files.pythonhosted.org/packages/5f/42/2dc82330a70aa8e55b6d395b11018045e58d0bb00834502bf11509f79091/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a08b491ec91b1d5053ac177afe5290adacf1f0f6307d771ccac5de30592d198", size = 1343631, upload-time = "2025-08-10T21:26:17.045Z" }, - { url = "https://files.pythonhosted.org/packages/22/fd/f4c67a6ed1aab149ec5a8a401c323cee7a1cbe364381bb6c9c0d564e0e20/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8fc5c867c22b828001b6a38d2eaeb88160bf5783c6cb4a5e440efc981ce286d", size = 2224963, upload-time = "2025-08-10T21:26:18.737Z" }, - { url = "https://files.pythonhosted.org/packages/45/aa/76720bd4cb3713314677d9ec94dcc21ced3f1baf4830adde5bb9b2430a5f/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3b3115b2581ea35bb6d1f24a4c90af37e5d9b49dcff267eeed14c3893c5b86ab", size = 2321295, upload-time = "2025-08-10T21:26:20.11Z" }, - { url = "https://files.pythonhosted.org/packages/80/19/d3ec0d9ab711242f56ae0dc2fc5d70e298bb4a1f9dfab44c027668c673a1/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858e4c22fb075920b96a291928cb7dea5644e94c0ee4fcd5af7e865655e4ccf2", size = 2487987, upload-time = "2025-08-10T21:26:21.49Z" }, - { url = "https://files.pythonhosted.org/packages/39/e9/61e4813b2c97e86b6fdbd4dd824bf72d28bcd8d4849b8084a357bc0dd64d/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ed0fecd28cc62c54b262e3736f8bb2512d8dcfdc2bcf08be5f47f96bf405b145", size = 2291817, upload-time = "2025-08-10T21:26:22.812Z" }, - { url = "https://files.pythonhosted.org/packages/a0/41/85d82b0291db7504da3c2defe35c9a8a5c9803a730f297bd823d11d5fb77/kiwisolver-1.4.9-cp312-cp312-win_amd64.whl", hash = "sha256:f68208a520c3d86ea51acf688a3e3002615a7f0238002cccc17affecc86a8a54", size = 73895, upload-time = "2025-08-10T21:26:24.37Z" }, - { url = "https://files.pythonhosted.org/packages/e2/92/5f3068cf15ee5cb624a0c7596e67e2a0bb2adee33f71c379054a491d07da/kiwisolver-1.4.9-cp312-cp312-win_arm64.whl", hash = "sha256:2c1a4f57df73965f3f14df20b80ee29e6a7930a57d2d9e8491a25f676e197c60", size = 64992, upload-time = "2025-08-10T21:26:25.732Z" }, - { url = "https://files.pythonhosted.org/packages/31/c1/c2686cda909742ab66c7388e9a1a8521a59eb89f8bcfbee28fc980d07e24/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5d0432ccf1c7ab14f9949eec60c5d1f924f17c037e9f8b33352fa05799359b8", size = 123681, upload-time = "2025-08-10T21:26:26.725Z" }, - { url = "https://files.pythonhosted.org/packages/ca/f0/f44f50c9f5b1a1860261092e3bc91ecdc9acda848a8b8c6abfda4a24dd5c/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efb3a45b35622bb6c16dbfab491a8f5a391fe0e9d45ef32f4df85658232ca0e2", size = 66464, upload-time = "2025-08-10T21:26:27.733Z" }, - { url = "https://files.pythonhosted.org/packages/2d/7a/9d90a151f558e29c3936b8a47ac770235f436f2120aca41a6d5f3d62ae8d/kiwisolver-1.4.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a12cf6398e8a0a001a059747a1cbf24705e18fe413bc22de7b3d15c67cffe3f", size = 64961, upload-time = "2025-08-10T21:26:28.729Z" }, - { url = "https://files.pythonhosted.org/packages/e9/e9/f218a2cb3a9ffbe324ca29a9e399fa2d2866d7f348ec3a88df87fc248fc5/kiwisolver-1.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b67e6efbf68e077dd71d1a6b37e43e1a99d0bff1a3d51867d45ee8908b931098", size = 1474607, upload-time = "2025-08-10T21:26:29.798Z" }, - { url = "https://files.pythonhosted.org/packages/d9/28/aac26d4c882f14de59041636292bc838db8961373825df23b8eeb807e198/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5656aa670507437af0207645273ccdfee4f14bacd7f7c67a4306d0dcaeaf6eed", size = 1276546, upload-time = "2025-08-10T21:26:31.401Z" }, - { url = "https://files.pythonhosted.org/packages/8b/ad/8bfc1c93d4cc565e5069162f610ba2f48ff39b7de4b5b8d93f69f30c4bed/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bfc08add558155345129c7803b3671cf195e6a56e7a12f3dde7c57d9b417f525", size = 1294482, upload-time = "2025-08-10T21:26:32.721Z" }, - { url = "https://files.pythonhosted.org/packages/da/f1/6aca55ff798901d8ce403206d00e033191f63d82dd708a186e0ed2067e9c/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:40092754720b174e6ccf9e845d0d8c7d8e12c3d71e7fc35f55f3813e96376f78", size = 1343720, upload-time = "2025-08-10T21:26:34.032Z" }, - { url = "https://files.pythonhosted.org/packages/d1/91/eed031876c595c81d90d0f6fc681ece250e14bf6998c3d7c419466b523b7/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:497d05f29a1300d14e02e6441cf0f5ee81c1ff5a304b0d9fb77423974684e08b", size = 2224907, upload-time = "2025-08-10T21:26:35.824Z" }, - { url = "https://files.pythonhosted.org/packages/e9/ec/4d1925f2e49617b9cca9c34bfa11adefad49d00db038e692a559454dfb2e/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdd1a81a1860476eb41ac4bc1e07b3f07259e6d55bbf739b79c8aaedcf512799", size = 2321334, upload-time = "2025-08-10T21:26:37.534Z" }, - { url = "https://files.pythonhosted.org/packages/43/cb/450cd4499356f68802750c6ddc18647b8ea01ffa28f50d20598e0befe6e9/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e6b93f13371d341afee3be9f7c5964e3fe61d5fa30f6a30eb49856935dfe4fc3", size = 2488313, upload-time = "2025-08-10T21:26:39.191Z" }, - { url = "https://files.pythonhosted.org/packages/71/67/fc76242bd99f885651128a5d4fa6083e5524694b7c88b489b1b55fdc491d/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d75aa530ccfaa593da12834b86a0724f58bff12706659baa9227c2ccaa06264c", size = 2291970, upload-time = "2025-08-10T21:26:40.828Z" }, - { url = "https://files.pythonhosted.org/packages/75/bd/f1a5d894000941739f2ae1b65a32892349423ad49c2e6d0771d0bad3fae4/kiwisolver-1.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:dd0a578400839256df88c16abddf9ba14813ec5f21362e1fe65022e00c883d4d", size = 73894, upload-time = "2025-08-10T21:26:42.33Z" }, - { url = "https://files.pythonhosted.org/packages/95/38/dce480814d25b99a391abbddadc78f7c117c6da34be68ca8b02d5848b424/kiwisolver-1.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:d4188e73af84ca82468f09cadc5ac4db578109e52acb4518d8154698d3a87ca2", size = 64995, upload-time = "2025-08-10T21:26:43.889Z" }, - { url = "https://files.pythonhosted.org/packages/e2/37/7d218ce5d92dadc5ebdd9070d903e0c7cf7edfe03f179433ac4d13ce659c/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:5a0f2724dfd4e3b3ac5a82436a8e6fd16baa7d507117e4279b660fe8ca38a3a1", size = 126510, upload-time = "2025-08-10T21:26:44.915Z" }, - { url = "https://files.pythonhosted.org/packages/23/b0/e85a2b48233daef4b648fb657ebbb6f8367696a2d9548a00b4ee0eb67803/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1b11d6a633e4ed84fc0ddafd4ebfd8ea49b3f25082c04ad12b8315c11d504dc1", size = 67903, upload-time = "2025-08-10T21:26:45.934Z" }, - { url = "https://files.pythonhosted.org/packages/44/98/f2425bc0113ad7de24da6bb4dae1343476e95e1d738be7c04d31a5d037fd/kiwisolver-1.4.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61874cdb0a36016354853593cffc38e56fc9ca5aa97d2c05d3dcf6922cd55a11", size = 66402, upload-time = "2025-08-10T21:26:47.101Z" }, - { url = "https://files.pythonhosted.org/packages/98/d8/594657886df9f34c4177cc353cc28ca7e6e5eb562d37ccc233bff43bbe2a/kiwisolver-1.4.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:60c439763a969a6af93b4881db0eed8fadf93ee98e18cbc35bc8da868d0c4f0c", size = 1582135, upload-time = "2025-08-10T21:26:48.665Z" }, - { url = "https://files.pythonhosted.org/packages/5c/c6/38a115b7170f8b306fc929e166340c24958347308ea3012c2b44e7e295db/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92a2f997387a1b79a75e7803aa7ded2cfbe2823852ccf1ba3bcf613b62ae3197", size = 1389409, upload-time = "2025-08-10T21:26:50.335Z" }, - { url = "https://files.pythonhosted.org/packages/bf/3b/e04883dace81f24a568bcee6eb3001da4ba05114afa622ec9b6fafdc1f5e/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31d512c812daea6d8b3be3b2bfcbeb091dbb09177706569bcfc6240dcf8b41c", size = 1401763, upload-time = "2025-08-10T21:26:51.867Z" }, - { url = "https://files.pythonhosted.org/packages/9f/80/20ace48e33408947af49d7d15c341eaee69e4e0304aab4b7660e234d6288/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:52a15b0f35dad39862d376df10c5230155243a2c1a436e39eb55623ccbd68185", size = 1453643, upload-time = "2025-08-10T21:26:53.592Z" }, - { url = "https://files.pythonhosted.org/packages/64/31/6ce4380a4cd1f515bdda976a1e90e547ccd47b67a1546d63884463c92ca9/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a30fd6fdef1430fd9e1ba7b3398b5ee4e2887783917a687d86ba69985fb08748", size = 2330818, upload-time = "2025-08-10T21:26:55.051Z" }, - { url = "https://files.pythonhosted.org/packages/fa/e9/3f3fcba3bcc7432c795b82646306e822f3fd74df0ee81f0fa067a1f95668/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cc9617b46837c6468197b5945e196ee9ca43057bb7d9d1ae688101e4e1dddf64", size = 2419963, upload-time = "2025-08-10T21:26:56.421Z" }, - { url = "https://files.pythonhosted.org/packages/99/43/7320c50e4133575c66e9f7dadead35ab22d7c012a3b09bb35647792b2a6d/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:0ab74e19f6a2b027ea4f845a78827969af45ce790e6cb3e1ebab71bdf9f215ff", size = 2594639, upload-time = "2025-08-10T21:26:57.882Z" }, - { url = "https://files.pythonhosted.org/packages/65/d6/17ae4a270d4a987ef8a385b906d2bdfc9fce502d6dc0d3aea865b47f548c/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dba5ee5d3981160c28d5490f0d1b7ed730c22470ff7f6cc26cfcfaacb9896a07", size = 2391741, upload-time = "2025-08-10T21:26:59.237Z" }, - { url = "https://files.pythonhosted.org/packages/2a/8f/8f6f491d595a9e5912971f3f863d81baddccc8a4d0c3749d6a0dd9ffc9df/kiwisolver-1.4.9-cp313-cp313t-win_arm64.whl", hash = "sha256:0749fd8f4218ad2e851e11cc4dc05c7cbc0cbc4267bdfdb31782e65aace4ee9c", size = 68646, upload-time = "2025-08-10T21:27:00.52Z" }, - { url = "https://files.pythonhosted.org/packages/6b/32/6cc0fbc9c54d06c2969faa9c1d29f5751a2e51809dd55c69055e62d9b426/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:9928fe1eb816d11ae170885a74d074f57af3a0d65777ca47e9aeb854a1fba386", size = 123806, upload-time = "2025-08-10T21:27:01.537Z" }, - { url = "https://files.pythonhosted.org/packages/b2/dd/2bfb1d4a4823d92e8cbb420fe024b8d2167f72079b3bb941207c42570bdf/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d0005b053977e7b43388ddec89fa567f43d4f6d5c2c0affe57de5ebf290dc552", size = 66605, upload-time = "2025-08-10T21:27:03.335Z" }, - { url = "https://files.pythonhosted.org/packages/f7/69/00aafdb4e4509c2ca6064646cba9cd4b37933898f426756adb2cb92ebbed/kiwisolver-1.4.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2635d352d67458b66fd0667c14cb1d4145e9560d503219034a18a87e971ce4f3", size = 64925, upload-time = "2025-08-10T21:27:04.339Z" }, - { url = "https://files.pythonhosted.org/packages/43/dc/51acc6791aa14e5cb6d8a2e28cefb0dc2886d8862795449d021334c0df20/kiwisolver-1.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:767c23ad1c58c9e827b649a9ab7809fd5fd9db266a9cf02b0e926ddc2c680d58", size = 1472414, upload-time = "2025-08-10T21:27:05.437Z" }, - { url = "https://files.pythonhosted.org/packages/3d/bb/93fa64a81db304ac8a246f834d5094fae4b13baf53c839d6bb6e81177129/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72d0eb9fba308b8311685c2268cf7d0a0639a6cd027d8128659f72bdd8a024b4", size = 1281272, upload-time = "2025-08-10T21:27:07.063Z" }, - { url = "https://files.pythonhosted.org/packages/70/e6/6df102916960fb8d05069d4bd92d6d9a8202d5a3e2444494e7cd50f65b7a/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f68e4f3eeca8fb22cc3d731f9715a13b652795ef657a13df1ad0c7dc0e9731df", size = 1298578, upload-time = "2025-08-10T21:27:08.452Z" }, - { url = "https://files.pythonhosted.org/packages/7c/47/e142aaa612f5343736b087864dbaebc53ea8831453fb47e7521fa8658f30/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d84cd4061ae292d8ac367b2c3fa3aad11cb8625a95d135fe93f286f914f3f5a6", size = 1345607, upload-time = "2025-08-10T21:27:10.125Z" }, - { url = "https://files.pythonhosted.org/packages/54/89/d641a746194a0f4d1a3670fb900d0dbaa786fb98341056814bc3f058fa52/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a60ea74330b91bd22a29638940d115df9dc00af5035a9a2a6ad9399ffb4ceca5", size = 2230150, upload-time = "2025-08-10T21:27:11.484Z" }, - { url = "https://files.pythonhosted.org/packages/aa/6b/5ee1207198febdf16ac11f78c5ae40861b809cbe0e6d2a8d5b0b3044b199/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ce6a3a4e106cf35c2d9c4fa17c05ce0b180db622736845d4315519397a77beaf", size = 2325979, upload-time = "2025-08-10T21:27:12.917Z" }, - { url = "https://files.pythonhosted.org/packages/fc/ff/b269eefd90f4ae14dcc74973d5a0f6d28d3b9bb1afd8c0340513afe6b39a/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:77937e5e2a38a7b48eef0585114fe7930346993a88060d0bf886086d2aa49ef5", size = 2491456, upload-time = "2025-08-10T21:27:14.353Z" }, - { url = "https://files.pythonhosted.org/packages/fc/d4/10303190bd4d30de547534601e259a4fbf014eed94aae3e5521129215086/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:24c175051354f4a28c5d6a31c93906dc653e2bf234e8a4bbfb964892078898ce", size = 2294621, upload-time = "2025-08-10T21:27:15.808Z" }, - { url = "https://files.pythonhosted.org/packages/28/e0/a9a90416fce5c0be25742729c2ea52105d62eda6c4be4d803c2a7be1fa50/kiwisolver-1.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:0763515d4df10edf6d06a3c19734e2566368980d21ebec439f33f9eb936c07b7", size = 75417, upload-time = "2025-08-10T21:27:17.436Z" }, - { url = "https://files.pythonhosted.org/packages/1f/10/6949958215b7a9a264299a7db195564e87900f709db9245e4ebdd3c70779/kiwisolver-1.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:0e4e2bf29574a6a7b7f6cb5fa69293b9f96c928949ac4a53ba3f525dffb87f9c", size = 66582, upload-time = "2025-08-10T21:27:18.436Z" }, - { url = "https://files.pythonhosted.org/packages/ec/79/60e53067903d3bc5469b369fe0dfc6b3482e2133e85dae9daa9527535991/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d976bbb382b202f71c67f77b0ac11244021cfa3f7dfd9e562eefcea2df711548", size = 126514, upload-time = "2025-08-10T21:27:19.465Z" }, - { url = "https://files.pythonhosted.org/packages/25/d1/4843d3e8d46b072c12a38c97c57fab4608d36e13fe47d47ee96b4d61ba6f/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2489e4e5d7ef9a1c300a5e0196e43d9c739f066ef23270607d45aba368b91f2d", size = 67905, upload-time = "2025-08-10T21:27:20.51Z" }, - { url = "https://files.pythonhosted.org/packages/8c/ae/29ffcbd239aea8b93108de1278271ae764dfc0d803a5693914975f200596/kiwisolver-1.4.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e2ea9f7ab7fbf18fffb1b5434ce7c69a07582f7acc7717720f1d69f3e806f90c", size = 66399, upload-time = "2025-08-10T21:27:21.496Z" }, - { url = "https://files.pythonhosted.org/packages/a1/ae/d7ba902aa604152c2ceba5d352d7b62106bedbccc8e95c3934d94472bfa3/kiwisolver-1.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b34e51affded8faee0dfdb705416153819d8ea9250bbbf7ea1b249bdeb5f1122", size = 1582197, upload-time = "2025-08-10T21:27:22.604Z" }, - { url = "https://files.pythonhosted.org/packages/f2/41/27c70d427eddb8bc7e4f16420a20fefc6f480312122a59a959fdfe0445ad/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8aacd3d4b33b772542b2e01beb50187536967b514b00003bdda7589722d2a64", size = 1390125, upload-time = "2025-08-10T21:27:24.036Z" }, - { url = "https://files.pythonhosted.org/packages/41/42/b3799a12bafc76d962ad69083f8b43b12bf4fe78b097b12e105d75c9b8f1/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7cf974dd4e35fa315563ac99d6287a1024e4dc2077b8a7d7cd3d2fb65d283134", size = 1402612, upload-time = "2025-08-10T21:27:25.773Z" }, - { url = "https://files.pythonhosted.org/packages/d2/b5/a210ea073ea1cfaca1bb5c55a62307d8252f531beb364e18aa1e0888b5a0/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:85bd218b5ecfbee8c8a82e121802dcb519a86044c9c3b2e4aef02fa05c6da370", size = 1453990, upload-time = "2025-08-10T21:27:27.089Z" }, - { url = "https://files.pythonhosted.org/packages/5f/ce/a829eb8c033e977d7ea03ed32fb3c1781b4fa0433fbadfff29e39c676f32/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0856e241c2d3df4efef7c04a1e46b1936b6120c9bcf36dd216e3acd84bc4fb21", size = 2331601, upload-time = "2025-08-10T21:27:29.343Z" }, - { url = "https://files.pythonhosted.org/packages/e0/4b/b5e97eb142eb9cd0072dacfcdcd31b1c66dc7352b0f7c7255d339c0edf00/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9af39d6551f97d31a4deebeac6f45b156f9755ddc59c07b402c148f5dbb6482a", size = 2422041, upload-time = "2025-08-10T21:27:30.754Z" }, - { url = "https://files.pythonhosted.org/packages/40/be/8eb4cd53e1b85ba4edc3a9321666f12b83113a178845593307a3e7891f44/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:bb4ae2b57fc1d8cbd1cf7b1d9913803681ffa903e7488012be5b76dedf49297f", size = 2594897, upload-time = "2025-08-10T21:27:32.803Z" }, - { url = "https://files.pythonhosted.org/packages/99/dd/841e9a66c4715477ea0abc78da039832fbb09dac5c35c58dc4c41a407b8a/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:aedff62918805fb62d43a4aa2ecd4482c380dc76cd31bd7c8878588a61bd0369", size = 2391835, upload-time = "2025-08-10T21:27:34.23Z" }, - { url = "https://files.pythonhosted.org/packages/0c/28/4b2e5c47a0da96896fdfdb006340ade064afa1e63675d01ea5ac222b6d52/kiwisolver-1.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:1fa333e8b2ce4d9660f2cda9c0e1b6bafcfb2457a9d259faa82289e73ec24891", size = 79988, upload-time = "2025-08-10T21:27:35.587Z" }, - { url = "https://files.pythonhosted.org/packages/80/be/3578e8afd18c88cdf9cb4cffde75a96d2be38c5a903f1ed0ceec061bd09e/kiwisolver-1.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:4a48a2ce79d65d363597ef7b567ce3d14d68783d2b2263d98db3d9477805ba32", size = 70260, upload-time = "2025-08-10T21:27:36.606Z" }, - { url = "https://files.pythonhosted.org/packages/a2/63/fde392691690f55b38d5dd7b3710f5353bf7a8e52de93a22968801ab8978/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4d1d9e582ad4d63062d34077a9a1e9f3c34088a2ec5135b1f7190c07cf366527", size = 60183, upload-time = "2025-08-10T21:27:37.669Z" }, - { url = "https://files.pythonhosted.org/packages/27/b1/6aad34edfdb7cced27f371866f211332bba215bfd918ad3322a58f480d8b/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:deed0c7258ceb4c44ad5ec7d9918f9f14fd05b2be86378d86cf50e63d1e7b771", size = 58675, upload-time = "2025-08-10T21:27:39.031Z" }, - { url = "https://files.pythonhosted.org/packages/9d/1a/23d855a702bb35a76faed5ae2ba3de57d323f48b1f6b17ee2176c4849463/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a590506f303f512dff6b7f75fd2fd18e16943efee932008fe7140e5fa91d80e", size = 80277, upload-time = "2025-08-10T21:27:40.129Z" }, - { url = "https://files.pythonhosted.org/packages/5a/5b/5239e3c2b8fb5afa1e8508f721bb77325f740ab6994d963e61b2b7abcc1e/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e09c2279a4d01f099f52d5c4b3d9e208e91edcbd1a175c9662a8b16e000fece9", size = 77994, upload-time = "2025-08-10T21:27:41.181Z" }, - { url = "https://files.pythonhosted.org/packages/f9/1c/5d4d468fb16f8410e596ed0eac02d2c68752aa7dc92997fe9d60a7147665/kiwisolver-1.4.9-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c9e7cdf45d594ee04d5be1b24dd9d49f3d1590959b2271fb30b5ca2b262c00fb", size = 73744, upload-time = "2025-08-10T21:27:42.254Z" }, - { url = "https://files.pythonhosted.org/packages/a3/0f/36d89194b5a32c054ce93e586d4049b6c2c22887b0eb229c61c68afd3078/kiwisolver-1.4.9-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:720e05574713db64c356e86732c0f3c5252818d05f9df320f0ad8380641acea5", size = 60104, upload-time = "2025-08-10T21:27:43.287Z" }, - { url = "https://files.pythonhosted.org/packages/52/ba/4ed75f59e4658fd21fe7dde1fee0ac397c678ec3befba3fe6482d987af87/kiwisolver-1.4.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:17680d737d5335b552994a2008fab4c851bcd7de33094a82067ef3a576ff02fa", size = 58592, upload-time = "2025-08-10T21:27:44.314Z" }, - { url = "https://files.pythonhosted.org/packages/33/01/a8ea7c5ea32a9b45ceeaee051a04c8ed4320f5add3c51bfa20879b765b70/kiwisolver-1.4.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:85b5352f94e490c028926ea567fc569c52ec79ce131dadb968d3853e809518c2", size = 80281, upload-time = "2025-08-10T21:27:45.369Z" }, - { url = "https://files.pythonhosted.org/packages/da/e3/dbd2ecdce306f1d07a1aaf324817ee993aab7aee9db47ceac757deabafbe/kiwisolver-1.4.9-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:464415881e4801295659462c49461a24fb107c140de781d55518c4b80cb6790f", size = 78009, upload-time = "2025-08-10T21:27:46.376Z" }, - { url = "https://files.pythonhosted.org/packages/da/e9/0d4add7873a73e462aeb45c036a2dead2562b825aa46ba326727b3f31016/kiwisolver-1.4.9-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:fb940820c63a9590d31d88b815e7a3aa5915cad3ce735ab45f0c730b39547de1", size = 73929, upload-time = "2025-08-10T21:27:48.236Z" }, +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/67/9c61eccb13f0bdca9307614e782fec49ffdde0f7a2314935d489fa93cd9c/kiwisolver-1.5.0.tar.gz", hash = "sha256:d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a", size = 103482, upload-time = "2026-03-09T13:15:53.382Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/f8/06549565caa026e540b7e7bab5c5a90eb7ca986015f4c48dace243cd24d9/kiwisolver-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:32cc0a5365239a6ea0c6ed461e8838d053b57e397443c0ca894dcc8e388d4374", size = 122802, upload-time = "2026-03-09T13:12:37.515Z" }, + { url = "https://files.pythonhosted.org/packages/84/eb/8476a0818850c563ff343ea7c9c05dcdcbd689a38e01aa31657df01f91fa/kiwisolver-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cc0b66c1eec9021353a4b4483afb12dfd50e3669ffbb9152d6842eb34c7e29fd", size = 66216, upload-time = "2026-03-09T13:12:38.812Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c4/f9c8a6b4c21aed4198566e45923512986d6cef530e7263b3a5f823546561/kiwisolver-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86e0287879f75621ae85197b0877ed2f8b7aa57b511c7331dce2eb6f4de7d476", size = 63917, upload-time = "2026-03-09T13:12:40.053Z" }, + { url = "https://files.pythonhosted.org/packages/f1/0e/ba4ae25d03722f64de8b2c13e80d82ab537a06b30fc7065183c6439357e3/kiwisolver-1.5.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:62f59da443c4f4849f73a51a193b1d9d258dcad0c41bc4d1b8fb2bcc04bfeb22", size = 1628776, upload-time = "2026-03-09T13:12:41.976Z" }, + { url = "https://files.pythonhosted.org/packages/8a/e4/3f43a011bc8a0860d1c96f84d32fa87439d3feedf66e672fef03bf5e8bac/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9190426b7aa26c5229501fa297b8d0653cfd3f5a36f7990c264e157cbf886b3b", size = 1228164, upload-time = "2026-03-09T13:12:44.002Z" }, + { url = "https://files.pythonhosted.org/packages/4b/34/3a901559a1e0c218404f9a61a93be82d45cb8f44453ba43088644980f033/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c8277104ded0a51e699c8c3aff63ce2c56d4ed5519a5f73e0fd7057f959a2b9e", size = 1246656, upload-time = "2026-03-09T13:12:45.557Z" }, + { url = "https://files.pythonhosted.org/packages/87/9e/f78c466ea20527822b95ad38f141f2de1dcd7f23fb8716b002b0d91bbe59/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8f9baf6f0a6e7571c45c8863010b45e837c3ee1c2c77fcd6ef423be91b21fedb", size = 1295562, upload-time = "2026-03-09T13:12:47.562Z" }, + { url = "https://files.pythonhosted.org/packages/0a/66/fd0e4a612e3a286c24e6d6f3a5428d11258ed1909bc530ba3b59807fd980/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cff8e5383db4989311f99e814feeb90c4723eb4edca425b9d5d9c3fefcdd9537", size = 2178473, upload-time = "2026-03-09T13:12:50.254Z" }, + { url = "https://files.pythonhosted.org/packages/dc/8e/6cac929e0049539e5ee25c1ee937556f379ba5204840d03008363ced662d/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ebae99ed6764f2b5771c522477b311be313e8841d2e0376db2b10922daebbba4", size = 2274035, upload-time = "2026-03-09T13:12:51.785Z" }, + { url = "https://files.pythonhosted.org/packages/ca/d3/9d0c18f1b52ea8074b792452cf17f1f5a56bd0302a85191f405cfbf9da16/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:d5cd5189fc2b6a538b75ae45433140c4823463918f7b1617c31e68b085c0022c", size = 2443217, upload-time = "2026-03-09T13:12:53.329Z" }, + { url = "https://files.pythonhosted.org/packages/45/2a/6e19368803a038b2a90857bf4ee9e3c7b667216d045866bf22d3439fd75e/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f42c23db5d1521218a3276bb08666dcb662896a0be7347cba864eca45ff64ede", size = 2249196, upload-time = "2026-03-09T13:12:55.057Z" }, + { url = "https://files.pythonhosted.org/packages/75/2b/3f641dfcbe72e222175d626bacf2f72c3b34312afec949dd1c50afa400f5/kiwisolver-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:94eff26096eb5395136634622515b234ecb6c9979824c1f5004c6e3c3c85ccd2", size = 73389, upload-time = "2026-03-09T13:12:56.496Z" }, + { url = "https://files.pythonhosted.org/packages/da/88/299b137b9e0025d8982e03d2d52c123b0a2b159e84b0ef1501ef446339cf/kiwisolver-1.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:dd952e03bfbb096cfe2dd35cd9e00f269969b67536cb4370994afc20ff2d0875", size = 64782, upload-time = "2026-03-09T13:12:57.609Z" }, + { url = "https://files.pythonhosted.org/packages/12/dd/a495a9c104be1c476f0386e714252caf2b7eca883915422a64c50b88c6f5/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9eed0f7edbb274413b6ee781cca50541c8c0facd3d6fd289779e494340a2b85c", size = 122798, upload-time = "2026-03-09T13:12:58.963Z" }, + { url = "https://files.pythonhosted.org/packages/11/60/37b4047a2af0cf5ef6d8b4b26e91829ae6fc6a2d1f74524bcb0e7cd28a32/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c4923e404d6bcd91b6779c009542e5647fef32e4a5d75e115e3bbac6f2335eb", size = 66216, upload-time = "2026-03-09T13:13:00.155Z" }, + { url = "https://files.pythonhosted.org/packages/0a/aa/510dc933d87767584abfe03efa445889996c70c2990f6f87c3ebaa0a18c5/kiwisolver-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0df54df7e686afa55e6f21fb86195224a6d9beb71d637e8d7920c95cf0f89aac", size = 63911, upload-time = "2026-03-09T13:13:01.671Z" }, + { url = "https://files.pythonhosted.org/packages/80/46/bddc13df6c2a40741e0cc7865bb1c9ed4796b6760bd04ce5fae3928ef917/kiwisolver-1.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2517e24d7315eb51c10664cdb865195df38ab74456c677df67bb47f12d088a27", size = 1438209, upload-time = "2026-03-09T13:13:03.385Z" }, + { url = "https://files.pythonhosted.org/packages/fd/d6/76621246f5165e5372f02f5e6f3f48ea336a8f9e96e43997d45b240ed8cd/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff710414307fefa903e0d9bdf300972f892c23477829f49504e59834f4195398", size = 1248888, upload-time = "2026-03-09T13:13:05.231Z" }, + { url = "https://files.pythonhosted.org/packages/b2/c1/31559ec6fb39a5b48035ce29bb63ade628f321785f38c384dee3e2c08bc1/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6176c1811d9d5a04fa391c490cc44f451e240697a16977f11c6f722efb9041db", size = 1266304, upload-time = "2026-03-09T13:13:06.743Z" }, + { url = "https://files.pythonhosted.org/packages/5e/ef/1cb8276f2d29cc6a41e0a042f27946ca347d3a4a75acf85d0a16aa6dcc82/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50847dca5d197fcbd389c805aa1a1cf32f25d2e7273dc47ab181a517666b68cc", size = 1319650, upload-time = "2026-03-09T13:13:08.607Z" }, + { url = "https://files.pythonhosted.org/packages/4c/e4/5ba3cecd7ce6236ae4a80f67e5d5531287337d0e1f076ca87a5abe4cd5d0/kiwisolver-1.5.0-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:01808c6d15f4c3e8559595d6d1fe6411c68e4a3822b4b9972b44473b24f4e679", size = 970949, upload-time = "2026-03-09T13:13:10.299Z" }, + { url = "https://files.pythonhosted.org/packages/5a/69/dc61f7ae9a2f071f26004ced87f078235b5507ab6e5acd78f40365655034/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f1f9f4121ec58628c96baa3de1a55a4e3a333c5102c8e94b64e23bf7b2083309", size = 2199125, upload-time = "2026-03-09T13:13:11.841Z" }, + { url = "https://files.pythonhosted.org/packages/e5/7b/abbe0f1b5afa85f8d084b73e90e5f801c0939eba16ac2e49af7c61a6c28d/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b7d335370ae48a780c6e6a6bbfa97342f563744c39c35562f3f367665f5c1de2", size = 2293783, upload-time = "2026-03-09T13:13:14.399Z" }, + { url = "https://files.pythonhosted.org/packages/8a/80/5908ae149d96d81580d604c7f8aefd0e98f4fd728cf172f477e9f2a81744/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:800ee55980c18545af444d93fdd60c56b580db5cc54867d8cbf8a1dc0829938c", size = 1960726, upload-time = "2026-03-09T13:13:16.047Z" }, + { url = "https://files.pythonhosted.org/packages/84/08/a78cb776f8c085b7143142ce479859cfec086bd09ee638a317040b6ef420/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c438f6ca858697c9ab67eb28246c92508af972e114cac34e57a6d4ba17a3ac08", size = 2464738, upload-time = "2026-03-09T13:13:17.897Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e1/65584da5356ed6cb12c63791a10b208860ac40a83de165cb6a6751a686e3/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8c63c91f95173f9c2a67c7c526b2cea976828a0e7fced9cdcead2802dc10f8a4", size = 2270718, upload-time = "2026-03-09T13:13:19.421Z" }, + { url = "https://files.pythonhosted.org/packages/be/6c/28f17390b62b8f2f520e2915095b3c94d88681ecf0041e75389d9667f202/kiwisolver-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:beb7f344487cdcb9e1efe4b7a29681b74d34c08f0043a327a74da852a6749e7b", size = 73480, upload-time = "2026-03-09T13:13:20.818Z" }, + { url = "https://files.pythonhosted.org/packages/d8/0e/2ee5debc4f77a625778fec5501ff3e8036fe361b7ee28ae402a485bb9694/kiwisolver-1.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:ad4ae4ffd1ee9cd11357b4c66b612da9888f4f4daf2f36995eda64bd45370cac", size = 64930, upload-time = "2026-03-09T13:13:21.997Z" }, + { url = "https://files.pythonhosted.org/packages/4d/b2/818b74ebea34dabe6d0c51cb1c572e046730e64844da6ed646d5298c40ce/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4e9750bc21b886308024f8a54ccb9a2cc38ac9fa813bf4348434e3d54f337ff9", size = 123158, upload-time = "2026-03-09T13:13:23.127Z" }, + { url = "https://files.pythonhosted.org/packages/bf/d9/405320f8077e8e1c5c4bd6adc45e1e6edf6d727b6da7f2e2533cf58bff71/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72ec46b7eba5b395e0a7b63025490d3214c11013f4aacb4f5e8d6c3041829588", size = 66388, upload-time = "2026-03-09T13:13:24.765Z" }, + { url = "https://files.pythonhosted.org/packages/99/9f/795fedf35634f746151ca8839d05681ceb6287fbed6cc1c9bf235f7887c2/kiwisolver-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ed3a984b31da7481b103f68776f7128a89ef26ed40f4dc41a2223cda7fb24819", size = 64068, upload-time = "2026-03-09T13:13:25.878Z" }, + { url = "https://files.pythonhosted.org/packages/c4/13/680c54afe3e65767bed7ec1a15571e1a2f1257128733851ade24abcefbcc/kiwisolver-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb5136fb5352d3f422df33f0c879a1b0c204004324150cc3b5e3c4f310c9049f", size = 1477934, upload-time = "2026-03-09T13:13:27.166Z" }, + { url = "https://files.pythonhosted.org/packages/c8/2f/cebfcdb60fd6a9b0f6b47a9337198bcbad6fbe15e68189b7011fd914911f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2af221f268f5af85e776a73d62b0845fc8baf8ef0abfae79d29c77d0e776aaf", size = 1278537, upload-time = "2026-03-09T13:13:28.707Z" }, + { url = "https://files.pythonhosted.org/packages/f2/0d/9b782923aada3fafb1d6b84e13121954515c669b18af0c26e7d21f579855/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b0f172dc8ffaccb8522d7c5d899de00133f2f1ca7b0a49b7da98e901de87bf2d", size = 1296685, upload-time = "2026-03-09T13:13:30.528Z" }, + { url = "https://files.pythonhosted.org/packages/27/70/83241b6634b04fe44e892688d5208332bde130f38e610c0418f9ede47ded/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6ab8ba9152203feec73758dad83af9a0bbe05001eb4639e547207c40cfb52083", size = 1346024, upload-time = "2026-03-09T13:13:32.818Z" }, + { url = "https://files.pythonhosted.org/packages/e4/db/30ed226fb271ae1a6431fc0fe0edffb2efe23cadb01e798caeb9f2ceae8f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:cdee07c4d7f6d72008d3f73b9bf027f4e11550224c7c50d8df1ae4a37c1402a6", size = 987241, upload-time = "2026-03-09T13:13:34.435Z" }, + { url = "https://files.pythonhosted.org/packages/ec/bd/c314595208e4c9587652d50959ead9e461995389664e490f4dce7ff0f782/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7c60d3c9b06fb23bd9c6139281ccbdc384297579ae037f08ae90c69f6845c0b1", size = 2227742, upload-time = "2026-03-09T13:13:36.4Z" }, + { url = "https://files.pythonhosted.org/packages/c1/43/0499cec932d935229b5543d073c2b87c9c22846aab48881e9d8d6e742a2d/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e315e5ec90d88e140f57696ff85b484ff68bb311e36f2c414aa4286293e6dee0", size = 2323966, upload-time = "2026-03-09T13:13:38.204Z" }, + { url = "https://files.pythonhosted.org/packages/3d/6f/79b0d760907965acfd9d61826a3d41f8f093c538f55cd2633d3f0db269f6/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:1465387ac63576c3e125e5337a6892b9e99e0627d52317f3ca79e6930d889d15", size = 1977417, upload-time = "2026-03-09T13:13:39.966Z" }, + { url = "https://files.pythonhosted.org/packages/ab/31/01d0537c41cb75a551a438c3c7a80d0c60d60b81f694dac83dd436aec0d0/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:530a3fd64c87cffa844d4b6b9768774763d9caa299e9b75d8eca6a4423b31314", size = 2491238, upload-time = "2026-03-09T13:13:41.698Z" }, + { url = "https://files.pythonhosted.org/packages/e4/34/8aefdd0be9cfd00a44509251ba864f5caf2991e36772e61c408007e7f417/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d9daea4ea6b9be74fe2f01f7fbade8d6ffab263e781274cffca0dba9be9eec9", size = 2294947, upload-time = "2026-03-09T13:13:43.343Z" }, + { url = "https://files.pythonhosted.org/packages/ad/cf/0348374369ca588f8fe9c338fae49fa4e16eeb10ffb3d012f23a54578a9e/kiwisolver-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:f18c2d9782259a6dc132fdc7a63c168cbc74b35284b6d75c673958982a378384", size = 73569, upload-time = "2026-03-09T13:13:45.792Z" }, + { url = "https://files.pythonhosted.org/packages/28/26/192b26196e2316e2bd29deef67e37cdf9870d9af8e085e521afff0fed526/kiwisolver-1.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:f7c7553b13f69c1b29a5bde08ddc6d9d0c8bfb84f9ed01c30db25944aeb852a7", size = 64997, upload-time = "2026-03-09T13:13:46.878Z" }, + { url = "https://files.pythonhosted.org/packages/9d/69/024d6711d5ba575aa65d5538042e99964104e97fa153a9f10bc369182bc2/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:fd40bb9cd0891c4c3cb1ddf83f8bbfa15731a248fdc8162669405451e2724b09", size = 123166, upload-time = "2026-03-09T13:13:48.032Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/adbb40df306f587054a348831220812b9b1d787aff714cfbc8556e38fccd/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c0e1403fd7c26d77c1f03e096dc58a5c726503fa0db0456678b8668f76f521e3", size = 66395, upload-time = "2026-03-09T13:13:49.365Z" }, + { url = "https://files.pythonhosted.org/packages/a8/3a/d0a972b34e1c63e2409413104216cd1caa02c5a37cb668d1687d466c1c45/kiwisolver-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dda366d548e89a90d88a86c692377d18d8bd64b39c1fb2b92cb31370e2896bbd", size = 64065, upload-time = "2026-03-09T13:13:50.562Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0a/7b98e1e119878a27ba8618ca1e18b14f992ff1eda40f47bccccf4de44121/kiwisolver-1.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:332b4f0145c30b5f5ad9374881133e5aa64320428a57c2c2b61e9d891a51c2f3", size = 1477903, upload-time = "2026-03-09T13:13:52.084Z" }, + { url = "https://files.pythonhosted.org/packages/18/d8/55638d89ffd27799d5cc3d8aa28e12f4ce7a64d67b285114dbedc8ea4136/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c50b89ffd3e1a911c69a1dd3de7173c0cd10b130f56222e57898683841e4f96", size = 1278751, upload-time = "2026-03-09T13:13:54.673Z" }, + { url = "https://files.pythonhosted.org/packages/b8/97/b4c8d0d18421ecceba20ad8701358453b88e32414e6f6950b5a4bad54e65/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4db576bb8c3ef9365f8b40fe0f671644de6736ae2c27a2c62d7d8a1b4329f099", size = 1296793, upload-time = "2026-03-09T13:13:56.287Z" }, + { url = "https://files.pythonhosted.org/packages/c4/10/f862f94b6389d8957448ec9df59450b81bec4abb318805375c401a1e6892/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0b85aad90cea8ac6797a53b5d5f2e967334fa4d1149f031c4537569972596cb8", size = 1346041, upload-time = "2026-03-09T13:13:58.269Z" }, + { url = "https://files.pythonhosted.org/packages/a3/6a/f1650af35821eaf09de398ec0bc2aefc8f211f0cda50204c9f1673741ba9/kiwisolver-1.5.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:d36ca54cb4c6c4686f7cbb7b817f66f5911c12ddb519450bbe86707155028f87", size = 987292, upload-time = "2026-03-09T13:13:59.871Z" }, + { url = "https://files.pythonhosted.org/packages/de/19/d7fb82984b9238115fe629c915007be608ebd23dc8629703d917dbfaffd4/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:38f4a703656f493b0ad185211ccfca7f0386120f022066b018eb5296d8613e23", size = 2227865, upload-time = "2026-03-09T13:14:01.401Z" }, + { url = "https://files.pythonhosted.org/packages/7f/b9/46b7f386589fd222dac9e9de9c956ce5bcefe2ee73b4e79891381dda8654/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ac2360e93cb41be81121755c6462cff3beaa9967188c866e5fce5cf13170859", size = 2324369, upload-time = "2026-03-09T13:14:02.972Z" }, + { url = "https://files.pythonhosted.org/packages/92/8b/95e237cf3d9c642960153c769ddcbe278f182c8affb20cecc1cc983e7cc5/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c95cab08d1965db3d84a121f1c7ce7479bdd4072c9b3dafd8fecce48a2e6b902", size = 1977989, upload-time = "2026-03-09T13:14:04.503Z" }, + { url = "https://files.pythonhosted.org/packages/1b/95/980c9df53501892784997820136c01f62bc1865e31b82b9560f980c0e649/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fc20894c3d21194d8041a28b65622d5b86db786da6e3cfe73f0c762951a61167", size = 2491645, upload-time = "2026-03-09T13:14:06.106Z" }, + { url = "https://files.pythonhosted.org/packages/cb/32/900647fd0840abebe1561792c6b31e6a7c0e278fc3973d30572a965ca14c/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7a32f72973f0f950c1920475d5c5ea3d971b81b6f0ec53b8d0a956cc965f22e0", size = 2295237, upload-time = "2026-03-09T13:14:08.891Z" }, + { url = "https://files.pythonhosted.org/packages/be/8a/be60e3bbcf513cc5a50f4a3e88e1dcecebb79c1ad607a7222877becaa101/kiwisolver-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bf3acf1419fa93064a4c2189ac0b58e3be7872bf6ee6177b0d4c63dc4cea276", size = 73573, upload-time = "2026-03-09T13:14:12.327Z" }, + { url = "https://files.pythonhosted.org/packages/4d/d2/64be2e429eb4fca7f7e1c52a91b12663aeaf25de3895e5cca0f47ef2a8d0/kiwisolver-1.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:fa8eb9ecdb7efb0b226acec134e0d709e87a909fa4971a54c0c4f6e88635484c", size = 64998, upload-time = "2026-03-09T13:14:13.469Z" }, + { url = "https://files.pythonhosted.org/packages/b0/69/ce68dd0c85755ae2de490bf015b62f2cea5f6b14ff00a463f9d0774449ff/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:db485b3847d182b908b483b2ed133c66d88d49cacf98fd278fadafe11b4478d1", size = 125700, upload-time = "2026-03-09T13:14:14.636Z" }, + { url = "https://files.pythonhosted.org/packages/74/aa/937aac021cf9d4349990d47eb319309a51355ed1dbdc9c077cdc9224cb11/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:be12f931839a3bdfe28b584db0e640a65a8bcbc24560ae3fdb025a449b3d754e", size = 67537, upload-time = "2026-03-09T13:14:15.808Z" }, + { url = "https://files.pythonhosted.org/packages/ee/20/3a87fbece2c40ad0f6f0aefa93542559159c5f99831d596050e8afae7a9f/kiwisolver-1.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:16b85d37c2cbb3253226d26e64663f755d88a03439a9c47df6246b35defbdfb7", size = 65514, upload-time = "2026-03-09T13:14:18.035Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7f/f943879cda9007c45e1f7dba216d705c3a18d6b35830e488b6c6a4e7cdf0/kiwisolver-1.5.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4432b835675f0ea7414aab3d37d119f7226d24869b7a829caeab49ebda407b0c", size = 1584848, upload-time = "2026-03-09T13:14:19.745Z" }, + { url = "https://files.pythonhosted.org/packages/37/f8/4d4f85cc1870c127c88d950913370dd76138482161cd07eabbc450deff01/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b0feb50971481a2cc44d94e88bdb02cdd497618252ae226b8eb1201b957e368", size = 1391542, upload-time = "2026-03-09T13:14:21.54Z" }, + { url = "https://files.pythonhosted.org/packages/04/0b/65dd2916c84d252b244bd405303220f729e7c17c9d7d33dca6feeff9ffc4/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56fa888f10d0f367155e76ce849fa1166fc9730d13bd2d65a2aa13b6f5424489", size = 1404447, upload-time = "2026-03-09T13:14:23.205Z" }, + { url = "https://files.pythonhosted.org/packages/39/5c/2606a373247babce9b1d056c03a04b65f3cf5290a8eac5d7bdead0a17e21/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:940dda65d5e764406b9fb92761cbf462e4e63f712ab60ed98f70552e496f3bf1", size = 1455918, upload-time = "2026-03-09T13:14:24.74Z" }, + { url = "https://files.pythonhosted.org/packages/d5/d1/c6078b5756670658e9192a2ef11e939c92918833d2745f85cd14a6004bdf/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_39_riscv64.whl", hash = "sha256:89fc958c702ee9a745e4700378f5d23fddbc46ff89e8fdbf5395c24d5c1452a3", size = 1072856, upload-time = "2026-03-09T13:14:26.597Z" }, + { url = "https://files.pythonhosted.org/packages/cb/c8/7def6ddf16eb2b3741d8b172bdaa9af882b03c78e9b0772975408801fa63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9027d773c4ff81487181a925945743413f6069634d0b122d0b37684ccf4f1e18", size = 2333580, upload-time = "2026-03-09T13:14:28.237Z" }, + { url = "https://files.pythonhosted.org/packages/9e/87/2ac1fce0eb1e616fcd3c35caa23e665e9b1948bb984f4764790924594128/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5b233ea3e165e43e35dba1d2b8ecc21cf070b45b65ae17dd2747d2713d942021", size = 2423018, upload-time = "2026-03-09T13:14:30.018Z" }, + { url = "https://files.pythonhosted.org/packages/67/13/c6700ccc6cc218716bfcda4935e4b2997039869b4ad8a94f364c5a3b8e63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ce9bf03dad3b46408c08649c6fbd6ca28a9fce0eb32fdfffa6775a13103b5310", size = 2062804, upload-time = "2026-03-09T13:14:32.888Z" }, + { url = "https://files.pythonhosted.org/packages/1b/bd/877056304626943ff0f1f44c08f584300c199b887cb3176cd7e34f1515f1/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:fc4d3f1fb9ca0ae9f97b095963bc6326f1dbfd3779d6679a1e016b9baaa153d3", size = 2597482, upload-time = "2026-03-09T13:14:34.971Z" }, + { url = "https://files.pythonhosted.org/packages/75/19/c60626c47bf0f8ac5dcf72c6c98e266d714f2fbbfd50cf6dab5ede3aaa50/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f443b4825c50a51ee68585522ab4a1d1257fac65896f282b4c6763337ac9f5d2", size = 2394328, upload-time = "2026-03-09T13:14:36.816Z" }, + { url = "https://files.pythonhosted.org/packages/47/84/6a6d5e5bb8273756c27b7d810d47f7ef2f1f9b9fd23c9ee9a3f8c75c9cef/kiwisolver-1.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:893ff3a711d1b515ba9da14ee090519bad4610ed1962fbe298a434e8c5f8db53", size = 68410, upload-time = "2026-03-09T13:14:38.695Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/060f45052f2a01ad5762c8fdecd6d7a752b43400dc29ff75cd47225a40fd/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8df31fe574b8b3993cc61764f40941111b25c2d9fea13d3ce24a49907cd2d615", size = 123231, upload-time = "2026-03-09T13:14:41.323Z" }, + { url = "https://files.pythonhosted.org/packages/c2/a7/78da680eadd06ff35edef6ef68a1ad273bad3e2a0936c9a885103230aece/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1d49a49ac4cbfb7c1375301cd1ec90169dfeae55ff84710d782260ce77a75a02", size = 66489, upload-time = "2026-03-09T13:14:42.534Z" }, + { url = "https://files.pythonhosted.org/packages/49/b2/97980f3ad4fae37dd7fe31626e2bf75fbf8bdf5d303950ec1fab39a12da8/kiwisolver-1.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0cbe94b69b819209a62cb27bdfa5dc2a8977d8de2f89dfd97ba4f53ed3af754e", size = 64063, upload-time = "2026-03-09T13:14:44.759Z" }, + { url = "https://files.pythonhosted.org/packages/e7/f9/b06c934a6aa8bc91f566bd2a214fd04c30506c2d9e2b6b171953216a65b6/kiwisolver-1.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80aa065ffd378ff784822a6d7c3212f2d5f5e9c3589614b5c228b311fd3063ac", size = 1475913, upload-time = "2026-03-09T13:14:46.247Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f0/f768ae564a710135630672981231320bc403cf9152b5596ec5289de0f106/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e7f886f47ab881692f278ae901039a234e4025a68e6dfab514263a0b1c4ae05", size = 1282782, upload-time = "2026-03-09T13:14:48.458Z" }, + { url = "https://files.pythonhosted.org/packages/e2/9f/1de7aad00697325f05238a5f2eafbd487fb637cc27a558b5367a5f37fb7f/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5060731cc3ed12ca3a8b57acd4aeca5bbc2f49216dd0bec1650a1acd89486bcd", size = 1300815, upload-time = "2026-03-09T13:14:50.721Z" }, + { url = "https://files.pythonhosted.org/packages/5a/c2/297f25141d2e468e0ce7f7a7b92e0cf8918143a0cbd3422c1ad627e85a06/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a4aa69609f40fce3cbc3f87b2061f042eee32f94b8f11db707b66a26461591a", size = 1347925, upload-time = "2026-03-09T13:14:52.304Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d3/f4c73a02eb41520c47610207b21afa8cdd18fdbf64ffd94674ae21c4812d/kiwisolver-1.5.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:d168fda2dbff7b9b5f38e693182d792a938c31db4dac3a80a4888de603c99554", size = 991322, upload-time = "2026-03-09T13:14:54.637Z" }, + { url = "https://files.pythonhosted.org/packages/7b/46/d3f2efef7732fcda98d22bf4ad5d3d71d545167a852ca710a494f4c15343/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:413b820229730d358efd838ecbab79902fe97094565fdc80ddb6b0a18c18a581", size = 2232857, upload-time = "2026-03-09T13:14:56.471Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ec/2d9756bf2b6d26ae4349b8d3662fb3993f16d80c1f971c179ce862b9dbae/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5124d1ea754509b09e53738ec185584cc609aae4a3b510aaf4ed6aa047ef9303", size = 2329376, upload-time = "2026-03-09T13:14:58.072Z" }, + { url = "https://files.pythonhosted.org/packages/8f/9f/876a0a0f2260f1bde92e002b3019a5fabc35e0939c7d945e0fa66185eb20/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e4415a8db000bf49a6dd1c478bf70062eaacff0f462b92b0ba68791a905861f9", size = 1982549, upload-time = "2026-03-09T13:14:59.668Z" }, + { url = "https://files.pythonhosted.org/packages/6c/4f/ba3624dfac23a64d54ac4179832860cb537c1b0af06024936e82ca4154a0/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d618fd27420381a4f6044faa71f46d8bfd911bd077c555f7138ed88729bfbe79", size = 2494680, upload-time = "2026-03-09T13:15:01.364Z" }, + { url = "https://files.pythonhosted.org/packages/39/b7/97716b190ab98911b20d10bf92eca469121ec483b8ce0edd314f51bc85af/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5092eb5b1172947f57d6ea7d89b2f29650414e4293c47707eb499ec07a0ac796", size = 2297905, upload-time = "2026-03-09T13:15:03.925Z" }, + { url = "https://files.pythonhosted.org/packages/a3/36/4e551e8aa55c9188bca9abb5096805edbf7431072b76e2298e34fd3a3008/kiwisolver-1.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:d76e2d8c75051d58177e762164d2e9ab92886534e3a12e795f103524f221dd8e", size = 75086, upload-time = "2026-03-09T13:15:07.775Z" }, + { url = "https://files.pythonhosted.org/packages/70/15/9b90f7df0e31a003c71649cf66ef61c3c1b862f48c81007fa2383c8bd8d7/kiwisolver-1.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:fa6248cd194edff41d7ea9425ced8ca3a6f838bfb295f6f1d6e6bb694a8518df", size = 66577, upload-time = "2026-03-09T13:15:09.139Z" }, + { url = "https://files.pythonhosted.org/packages/17/01/7dc8c5443ff42b38e72731643ed7cf1ed9bf01691ae5cdca98501999ed83/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d1ffeb80b5676463d7a7d56acbe8e37a20ce725570e09549fe738e02ca6b7e1e", size = 125794, upload-time = "2026-03-09T13:15:10.525Z" }, + { url = "https://files.pythonhosted.org/packages/46/8a/b4ebe46ebaac6a303417fab10c2e165c557ddaff558f9699d302b256bc53/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc4d8e252f532ab46a1de9349e2d27b91fce46736a9eedaa37beaca66f574ed4", size = 67646, upload-time = "2026-03-09T13:15:12.016Z" }, + { url = "https://files.pythonhosted.org/packages/60/35/10a844afc5f19d6f567359bf4789e26661755a2f36200d5d1ed8ad0126e5/kiwisolver-1.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6783e069732715ad0c3ce96dbf21dbc2235ab0593f2baf6338101f70371f4028", size = 65511, upload-time = "2026-03-09T13:15:13.311Z" }, + { url = "https://files.pythonhosted.org/packages/f8/8a/685b297052dd041dcebce8e8787b58923b6e78acc6115a0dc9189011c44b/kiwisolver-1.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e7c4c09a490dc4d4a7f8cbee56c606a320f9dc28cf92a7157a39d1ce7676a657", size = 1584858, upload-time = "2026-03-09T13:15:15.103Z" }, + { url = "https://files.pythonhosted.org/packages/9e/80/04865e3d4638ac5bddec28908916df4a3075b8c6cc101786a96803188b96/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a075bd7bd19c70cf67c8badfa36cf7c5d8de3c9ddb8420c51e10d9c50e94920", size = 1392539, upload-time = "2026-03-09T13:15:16.661Z" }, + { url = "https://files.pythonhosted.org/packages/ba/01/77a19cacc0893fa13fafa46d1bba06fb4dc2360b3292baf4b56d8e067b24/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bdd3e53429ff02aa319ba59dfe4ceeec345bf46cf180ec2cf6fd5b942e7975e9", size = 1405310, upload-time = "2026-03-09T13:15:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/53/39/bcaf5d0cca50e604cfa9b4e3ae1d64b50ca1ae5b754122396084599ef903/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cdcb35dc9d807259c981a85531048ede628eabcffb3239adf3d17463518992d", size = 1456244, upload-time = "2026-03-09T13:15:20.444Z" }, + { url = "https://files.pythonhosted.org/packages/d0/7a/72c187abc6975f6978c3e39b7cf67aeb8b3c0a8f9790aa7fd412855e9e1f/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:70d593af6a6ca332d1df73d519fddb5148edb15cd90d5f0155e3746a6d4fcc65", size = 1073154, upload-time = "2026-03-09T13:15:22.039Z" }, + { url = "https://files.pythonhosted.org/packages/c7/ca/cf5b25783ebbd59143b4371ed0c8428a278abe68d6d0104b01865b1bbd0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:377815a8616074cabbf3f53354e1d040c35815a134e01d7614b7692e4bf8acfa", size = 2334377, upload-time = "2026-03-09T13:15:23.741Z" }, + { url = "https://files.pythonhosted.org/packages/4a/e5/b1f492adc516796e88751282276745340e2a72dcd0d36cf7173e0daf3210/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0255a027391d52944eae1dbb5d4cc5903f57092f3674e8e544cdd2622826b3f0", size = 2425288, upload-time = "2026-03-09T13:15:25.789Z" }, + { url = "https://files.pythonhosted.org/packages/e6/e5/9b21fbe91a61b8f409d74a26498706e97a48008bfcd1864373d32a6ba31c/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:012b1eb16e28718fa782b5e61dc6f2da1f0792ca73bd05d54de6cb9561665fc9", size = 2063158, upload-time = "2026-03-09T13:15:27.63Z" }, + { url = "https://files.pythonhosted.org/packages/b1/02/83f47986138310f95ea95531f851b2a62227c11cbc3e690ae1374fe49f0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e3aafb33aed7479377e5e9a82e9d4bf87063741fc99fc7ae48b0f16e32bdd6f", size = 2597260, upload-time = "2026-03-09T13:15:29.421Z" }, + { url = "https://files.pythonhosted.org/packages/07/18/43a5f24608d8c313dd189cf838c8e68d75b115567c6279de7796197cfb6a/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7a116ae737f0000343218c4edf5bd45893bfeaff0993c0b215d7124c9f77646", size = 2394403, upload-time = "2026-03-09T13:15:31.517Z" }, + { url = "https://files.pythonhosted.org/packages/3b/b5/98222136d839b8afabcaa943b09bd05888c2d36355b7e448550211d1fca4/kiwisolver-1.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1dd9b0b119a350976a6d781e7278ec7aca0b201e1a9e2d23d9804afecb6ca681", size = 79687, upload-time = "2026-03-09T13:15:33.204Z" }, + { url = "https://files.pythonhosted.org/packages/99/a2/ca7dc962848040befed12732dff6acae7fb3c4f6fc4272b3f6c9a30b8713/kiwisolver-1.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:58f812017cd2985c21fbffb4864d59174d4903dd66fa23815e74bbc7a0e2dd57", size = 70032, upload-time = "2026-03-09T13:15:34.411Z" }, + { url = "https://files.pythonhosted.org/packages/1c/fa/2910df836372d8761bb6eff7d8bdcb1613b5c2e03f260efe7abe34d388a7/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:5ae8e62c147495b01a0f4765c878e9bfdf843412446a247e28df59936e99e797", size = 130262, upload-time = "2026-03-09T13:15:35.629Z" }, + { url = "https://files.pythonhosted.org/packages/0f/41/c5f71f9f00aabcc71fee8b7475e3f64747282580c2fe748961ba29b18385/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f6764a4ccab3078db14a632420930f6186058750df066b8ea2a7106df91d3203", size = 138036, upload-time = "2026-03-09T13:15:36.894Z" }, + { url = "https://files.pythonhosted.org/packages/fa/06/7399a607f434119c6e1fdc8ec89a8d51ccccadf3341dee4ead6bd14caaf5/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c31c13da98624f957b0fb1b5bae5383b2333c2c3f6793d9825dd5ce79b525cb7", size = 194295, upload-time = "2026-03-09T13:15:38.22Z" }, + { url = "https://files.pythonhosted.org/packages/b5/91/53255615acd2a1eaca307ede3c90eb550bae9c94581f8c00081b6b1c8f44/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:1f1489f769582498610e015a8ef2d36f28f505ab3096d0e16b4858a9ec214f57", size = 75987, upload-time = "2026-03-09T13:15:39.65Z" }, + { url = "https://files.pythonhosted.org/packages/17/6f/6fd4f690a40c2582fa34b97d2678f718acf3706b91d270c65ecb455d0a06/kiwisolver-1.5.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:295d9ffe712caa9f8a3081de8d32fc60191b4b51c76f02f951fd8407253528f4", size = 59606, upload-time = "2026-03-09T13:15:40.81Z" }, + { url = "https://files.pythonhosted.org/packages/82/a0/2355d5e3b338f13ce63f361abb181e3b6ea5fffdb73f739b3e80efa76159/kiwisolver-1.5.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:51e8c4084897de9f05898c2c2a39af6318044ae969d46ff7a34ed3f96274adca", size = 57537, upload-time = "2026-03-09T13:15:42.071Z" }, + { url = "https://files.pythonhosted.org/packages/c8/b9/1d50e610ecadebe205b71d6728fd224ce0e0ca6aba7b9cbe1da049203ac5/kiwisolver-1.5.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b83af57bdddef03c01a9138034c6ff03181a3028d9a1003b301eb1a55e161a3f", size = 79888, upload-time = "2026-03-09T13:15:43.317Z" }, + { url = "https://files.pythonhosted.org/packages/cd/ee/b85ffcd75afed0357d74f0e6fc02a4507da441165de1ca4760b9f496390d/kiwisolver-1.5.0-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf4679a3d71012a7c2bf360e5cd878fbd5e4fcac0896b56393dec239d81529ed", size = 77584, upload-time = "2026-03-09T13:15:44.605Z" }, + { url = "https://files.pythonhosted.org/packages/6b/dd/644d0dde6010a8583b4cd66dd41c5f83f5325464d15c4f490b3340ab73b4/kiwisolver-1.5.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:41024ed50e44ab1a60d3fe0a9d15a4ccc9f5f2b1d814ff283c8d01134d5b81bc", size = 73390, upload-time = "2026-03-09T13:15:45.832Z" }, + { url = "https://files.pythonhosted.org/packages/e9/eb/5fcbbbf9a0e2c3a35effb88831a483345326bbc3a030a3b5b69aee647f84/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ec4c85dc4b687c7f7f15f553ff26a98bfe8c58f5f7f0ac8905f0ba4c7be60232", size = 59532, upload-time = "2026-03-09T13:15:47.047Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9b/e17104555bb4db148fd52327feea1e96be4b88e8e008b029002c281a21ab/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:12e91c215a96e39f57989c8912ae761286ac5a9584d04030ceb3368a357f017a", size = 57420, upload-time = "2026-03-09T13:15:48.199Z" }, + { url = "https://files.pythonhosted.org/packages/48/44/2b5b95b7aa39fb2d8d9d956e0f3d5d45aef2ae1d942d4c3ffac2f9cfed1a/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be4a51a55833dc29ab5d7503e7bcb3b3af3402d266018137127450005cdfe737", size = 79892, upload-time = "2026-03-09T13:15:49.694Z" }, + { url = "https://files.pythonhosted.org/packages/52/7d/7157f9bba6b455cfb4632ed411e199fc8b8977642c2b12082e1bd9e6d173/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:daae526907e262de627d8f70058a0f64acc9e2641c164c99c8f594b34a799a16", size = 77603, upload-time = "2026-03-09T13:15:50.945Z" }, + { url = "https://files.pythonhosted.org/packages/0a/dd/8050c947d435c8d4bc94e3252f4d8bb8a76cfb424f043a8680be637a57f1/kiwisolver-1.5.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:59cd8683f575d96df5bb48f6add94afc055012c29e28124fcae2b63661b9efb1", size = 73558, upload-time = "2026-03-09T13:15:52.112Z" }, ] [[package]] @@ -1444,7 +1460,7 @@ dependencies = [ { name = "fonttools" }, { name = "kiwisolver" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "packaging" }, { name = "pillow" }, { name = "pyparsing" }, @@ -1778,85 +1794,85 @@ wheels = [ [[package]] name = "numpy" -version = "2.4.2" +version = "2.4.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.12'", "python_full_version == '3.11.*'", ] -sdist = { url = "https://files.pythonhosted.org/packages/57/fd/0005efbd0af48e55eb3c7208af93f2862d4b1a56cd78e84309a2d959208d/numpy-2.4.2.tar.gz", hash = "sha256:659a6107e31a83c4e33f763942275fd278b21d095094044eb35569e86a21ddae", size = 20723651, upload-time = "2026-01-31T23:13:10.135Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/44/71852273146957899753e69986246d6a176061ea183407e95418c2aa4d9a/numpy-2.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7e88598032542bd49af7c4747541422884219056c268823ef6e5e89851c8825", size = 16955478, upload-time = "2026-01-31T23:10:25.623Z" }, - { url = "https://files.pythonhosted.org/packages/74/41/5d17d4058bd0cd96bcbd4d9ff0fb2e21f52702aab9a72e4a594efa18692f/numpy-2.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7edc794af8b36ca37ef5fcb5e0d128c7e0595c7b96a2318d1badb6fcd8ee86b1", size = 14965467, upload-time = "2026-01-31T23:10:28.186Z" }, - { url = "https://files.pythonhosted.org/packages/49/48/fb1ce8136c19452ed15f033f8aee91d5defe515094e330ce368a0647846f/numpy-2.4.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:6e9f61981ace1360e42737e2bae58b27bf28a1b27e781721047d84bd754d32e7", size = 5475172, upload-time = "2026-01-31T23:10:30.848Z" }, - { url = "https://files.pythonhosted.org/packages/40/a9/3feb49f17bbd1300dd2570432961f5c8a4ffeff1db6f02c7273bd020a4c9/numpy-2.4.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:cb7bbb88aa74908950d979eeaa24dbdf1a865e3c7e45ff0121d8f70387b55f73", size = 6805145, upload-time = "2026-01-31T23:10:32.352Z" }, - { url = "https://files.pythonhosted.org/packages/3f/39/fdf35cbd6d6e2fcad42fcf85ac04a85a0d0fbfbf34b30721c98d602fd70a/numpy-2.4.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f069069931240b3fc703f1e23df63443dbd6390614c8c44a87d96cd0ec81eb1", size = 15966084, upload-time = "2026-01-31T23:10:34.502Z" }, - { url = "https://files.pythonhosted.org/packages/1b/46/6fa4ea94f1ddf969b2ee941290cca6f1bfac92b53c76ae5f44afe17ceb69/numpy-2.4.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c02ef4401a506fb60b411467ad501e1429a3487abca4664871d9ae0b46c8ba32", size = 16899477, upload-time = "2026-01-31T23:10:37.075Z" }, - { url = "https://files.pythonhosted.org/packages/09/a1/2a424e162b1a14a5bd860a464ab4e07513916a64ab1683fae262f735ccd2/numpy-2.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2653de5c24910e49c2b106499803124dde62a5a1fe0eedeaecf4309a5f639390", size = 17323429, upload-time = "2026-01-31T23:10:39.704Z" }, - { url = "https://files.pythonhosted.org/packages/ce/a2/73014149ff250628df72c58204822ac01d768697913881aacf839ff78680/numpy-2.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1ae241bbfc6ae276f94a170b14785e561cb5e7f626b6688cf076af4110887413", size = 18635109, upload-time = "2026-01-31T23:10:41.924Z" }, - { url = "https://files.pythonhosted.org/packages/6c/0c/73e8be2f1accd56df74abc1c5e18527822067dced5ec0861b5bb882c2ce0/numpy-2.4.2-cp311-cp311-win32.whl", hash = "sha256:df1b10187212b198dd45fa943d8985a3c8cf854aed4923796e0e019e113a1bda", size = 6237915, upload-time = "2026-01-31T23:10:45.26Z" }, - { url = "https://files.pythonhosted.org/packages/76/ae/e0265e0163cf127c24c3969d29f1c4c64551a1e375d95a13d32eab25d364/numpy-2.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:b9c618d56a29c9cb1c4da979e9899be7578d2e0b3c24d52079c166324c9e8695", size = 12607972, upload-time = "2026-01-31T23:10:47.021Z" }, - { url = "https://files.pythonhosted.org/packages/29/a5/c43029af9b8014d6ea157f192652c50042e8911f4300f8f6ed3336bf437f/numpy-2.4.2-cp311-cp311-win_arm64.whl", hash = "sha256:47c5a6ed21d9452b10227e5e8a0e1c22979811cad7dcc19d8e3e2fb8fa03f1a3", size = 10485763, upload-time = "2026-01-31T23:10:50.087Z" }, - { url = "https://files.pythonhosted.org/packages/51/6e/6f394c9c77668153e14d4da83bcc247beb5952f6ead7699a1a2992613bea/numpy-2.4.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:21982668592194c609de53ba4933a7471880ccbaadcc52352694a59ecc860b3a", size = 16667963, upload-time = "2026-01-31T23:10:52.147Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f8/55483431f2b2fd015ae6ed4fe62288823ce908437ed49db5a03d15151678/numpy-2.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40397bda92382fcec844066efb11f13e1c9a3e2a8e8f318fb72ed8b6db9f60f1", size = 14693571, upload-time = "2026-01-31T23:10:54.789Z" }, - { url = "https://files.pythonhosted.org/packages/2f/20/18026832b1845cdc82248208dd929ca14c9d8f2bac391f67440707fff27c/numpy-2.4.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:b3a24467af63c67829bfaa61eecf18d5432d4f11992688537be59ecd6ad32f5e", size = 5203469, upload-time = "2026-01-31T23:10:57.343Z" }, - { url = "https://files.pythonhosted.org/packages/7d/33/2eb97c8a77daaba34eaa3fa7241a14ac5f51c46a6bd5911361b644c4a1e2/numpy-2.4.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:805cc8de9fd6e7a22da5aed858e0ab16be5a4db6c873dde1d7451c541553aa27", size = 6550820, upload-time = "2026-01-31T23:10:59.429Z" }, - { url = "https://files.pythonhosted.org/packages/b1/91/b97fdfd12dc75b02c44e26c6638241cc004d4079a0321a69c62f51470c4c/numpy-2.4.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d82351358ffbcdcd7b686b90742a9b86632d6c1c051016484fa0b326a0a1548", size = 15663067, upload-time = "2026-01-31T23:11:01.291Z" }, - { url = "https://files.pythonhosted.org/packages/f5/c6/a18e59f3f0b8071cc85cbc8d80cd02d68aa9710170b2553a117203d46936/numpy-2.4.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e35d3e0144137d9fdae62912e869136164534d64a169f86438bc9561b6ad49f", size = 16619782, upload-time = "2026-01-31T23:11:03.669Z" }, - { url = "https://files.pythonhosted.org/packages/b7/83/9751502164601a79e18847309f5ceec0b1446d7b6aa12305759b72cf98b2/numpy-2.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adb6ed2ad29b9e15321d167d152ee909ec73395901b70936f029c3bc6d7f4460", size = 17013128, upload-time = "2026-01-31T23:11:05.913Z" }, - { url = "https://files.pythonhosted.org/packages/61/c4/c4066322256ec740acc1c8923a10047818691d2f8aec254798f3dd90f5f2/numpy-2.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8906e71fd8afcb76580404e2a950caef2685df3d2a57fe82a86ac8d33cc007ba", size = 18345324, upload-time = "2026-01-31T23:11:08.248Z" }, - { url = "https://files.pythonhosted.org/packages/ab/af/6157aa6da728fa4525a755bfad486ae7e3f76d4c1864138003eb84328497/numpy-2.4.2-cp312-cp312-win32.whl", hash = "sha256:ec055f6dae239a6299cace477b479cca2fc125c5675482daf1dd886933a1076f", size = 5960282, upload-time = "2026-01-31T23:11:10.497Z" }, - { url = "https://files.pythonhosted.org/packages/92/0f/7ceaaeaacb40567071e94dbf2c9480c0ae453d5bb4f52bea3892c39dc83c/numpy-2.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:209fae046e62d0ce6435fcfe3b1a10537e858249b3d9b05829e2a05218296a85", size = 12314210, upload-time = "2026-01-31T23:11:12.176Z" }, - { url = "https://files.pythonhosted.org/packages/2f/a3/56c5c604fae6dd40fa2ed3040d005fca97e91bd320d232ac9931d77ba13c/numpy-2.4.2-cp312-cp312-win_arm64.whl", hash = "sha256:fbde1b0c6e81d56f5dccd95dd4a711d9b95df1ae4009a60887e56b27e8d903fa", size = 10220171, upload-time = "2026-01-31T23:11:14.684Z" }, - { url = "https://files.pythonhosted.org/packages/a1/22/815b9fe25d1d7ae7d492152adbc7226d3eff731dffc38fe970589fcaaa38/numpy-2.4.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25f2059807faea4b077a2b6837391b5d830864b3543627f381821c646f31a63c", size = 16663696, upload-time = "2026-01-31T23:11:17.516Z" }, - { url = "https://files.pythonhosted.org/packages/09/f0/817d03a03f93ba9c6c8993de509277d84e69f9453601915e4a69554102a1/numpy-2.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bd3a7a9f5847d2fb8c2c6d1c862fa109c31a9abeca1a3c2bd5a64572955b2979", size = 14688322, upload-time = "2026-01-31T23:11:19.883Z" }, - { url = "https://files.pythonhosted.org/packages/da/b4/f805ab79293c728b9a99438775ce51885fd4f31b76178767cfc718701a39/numpy-2.4.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8e4549f8a3c6d13d55041925e912bfd834285ef1dd64d6bc7d542583355e2e98", size = 5198157, upload-time = "2026-01-31T23:11:22.375Z" }, - { url = "https://files.pythonhosted.org/packages/74/09/826e4289844eccdcd64aac27d13b0fd3f32039915dd5b9ba01baae1f436c/numpy-2.4.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:aea4f66ff44dfddf8c2cffd66ba6538c5ec67d389285292fe428cb2c738c8aef", size = 6546330, upload-time = "2026-01-31T23:11:23.958Z" }, - { url = "https://files.pythonhosted.org/packages/19/fb/cbfdbfa3057a10aea5422c558ac57538e6acc87ec1669e666d32ac198da7/numpy-2.4.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3cd545784805de05aafe1dde61752ea49a359ccba9760c1e5d1c88a93bbf2b7", size = 15660968, upload-time = "2026-01-31T23:11:25.713Z" }, - { url = "https://files.pythonhosted.org/packages/04/dc/46066ce18d01645541f0186877377b9371b8fa8017fa8262002b4ef22612/numpy-2.4.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0d9b7c93578baafcbc5f0b83eaf17b79d345c6f36917ba0c67f45226911d499", size = 16607311, upload-time = "2026-01-31T23:11:28.117Z" }, - { url = "https://files.pythonhosted.org/packages/14/d9/4b5adfc39a43fa6bf918c6d544bc60c05236cc2f6339847fc5b35e6cb5b0/numpy-2.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f74f0f7779cc7ae07d1810aab8ac6b1464c3eafb9e283a40da7309d5e6e48fbb", size = 17012850, upload-time = "2026-01-31T23:11:30.888Z" }, - { url = "https://files.pythonhosted.org/packages/b7/20/adb6e6adde6d0130046e6fdfb7675cc62bc2f6b7b02239a09eb58435753d/numpy-2.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c7ac672d699bf36275c035e16b65539931347d68b70667d28984c9fb34e07fa7", size = 18334210, upload-time = "2026-01-31T23:11:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/78/0e/0a73b3dff26803a8c02baa76398015ea2a5434d9b8265a7898a6028c1591/numpy-2.4.2-cp313-cp313-win32.whl", hash = "sha256:8e9afaeb0beff068b4d9cd20d322ba0ee1cecfb0b08db145e4ab4dd44a6b5110", size = 5958199, upload-time = "2026-01-31T23:11:35.385Z" }, - { url = "https://files.pythonhosted.org/packages/43/bc/6352f343522fcb2c04dbaf94cb30cca6fd32c1a750c06ad6231b4293708c/numpy-2.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:7df2de1e4fba69a51c06c28f5a3de36731eb9639feb8e1cf7e4a7b0daf4cf622", size = 12310848, upload-time = "2026-01-31T23:11:38.001Z" }, - { url = "https://files.pythonhosted.org/packages/6e/8d/6da186483e308da5da1cc6918ce913dcfe14ffde98e710bfeff2a6158d4e/numpy-2.4.2-cp313-cp313-win_arm64.whl", hash = "sha256:0fece1d1f0a89c16b03442eae5c56dc0be0c7883b5d388e0c03f53019a4bfd71", size = 10221082, upload-time = "2026-01-31T23:11:40.392Z" }, - { url = "https://files.pythonhosted.org/packages/25/a1/9510aa43555b44781968935c7548a8926274f815de42ad3997e9e83680dd/numpy-2.4.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5633c0da313330fd20c484c78cdd3f9b175b55e1a766c4a174230c6b70ad8262", size = 14815866, upload-time = "2026-01-31T23:11:42.495Z" }, - { url = "https://files.pythonhosted.org/packages/36/30/6bbb5e76631a5ae46e7923dd16ca9d3f1c93cfa8d4ed79a129814a9d8db3/numpy-2.4.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d9f64d786b3b1dd742c946c42d15b07497ed14af1a1f3ce840cce27daa0ce913", size = 5325631, upload-time = "2026-01-31T23:11:44.7Z" }, - { url = "https://files.pythonhosted.org/packages/46/00/3a490938800c1923b567b3a15cd17896e68052e2145d8662aaf3e1ffc58f/numpy-2.4.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:b21041e8cb6a1eb5312dd1d2f80a94d91efffb7a06b70597d44f1bd2dfc315ab", size = 6646254, upload-time = "2026-01-31T23:11:46.341Z" }, - { url = "https://files.pythonhosted.org/packages/d3/e9/fac0890149898a9b609caa5af7455a948b544746e4b8fe7c212c8edd71f8/numpy-2.4.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:00ab83c56211a1d7c07c25e3217ea6695e50a3e2f255053686b081dc0b091a82", size = 15720138, upload-time = "2026-01-31T23:11:48.082Z" }, - { url = "https://files.pythonhosted.org/packages/ea/5c/08887c54e68e1e28df53709f1893ce92932cc6f01f7c3d4dc952f61ffd4e/numpy-2.4.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fb882da679409066b4603579619341c6d6898fc83a8995199d5249f986e8e8f", size = 16655398, upload-time = "2026-01-31T23:11:50.293Z" }, - { url = "https://files.pythonhosted.org/packages/4d/89/253db0fa0e66e9129c745e4ef25631dc37d5f1314dad2b53e907b8538e6d/numpy-2.4.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:66cb9422236317f9d44b67b4d18f44efe6e9c7f8794ac0462978513359461554", size = 17079064, upload-time = "2026-01-31T23:11:52.927Z" }, - { url = "https://files.pythonhosted.org/packages/2a/d5/cbade46ce97c59c6c3da525e8d95b7abe8a42974a1dc5c1d489c10433e88/numpy-2.4.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0f01dcf33e73d80bd8dc0f20a71303abbafa26a19e23f6b68d1aa9990af90257", size = 18379680, upload-time = "2026-01-31T23:11:55.22Z" }, - { url = "https://files.pythonhosted.org/packages/40/62/48f99ae172a4b63d981babe683685030e8a3df4f246c893ea5c6ef99f018/numpy-2.4.2-cp313-cp313t-win32.whl", hash = "sha256:52b913ec40ff7ae845687b0b34d8d93b60cb66dcee06996dd5c99f2fc9328657", size = 6082433, upload-time = "2026-01-31T23:11:58.096Z" }, - { url = "https://files.pythonhosted.org/packages/07/38/e054a61cfe48ad9f1ed0d188e78b7e26859d0b60ef21cd9de4897cdb5326/numpy-2.4.2-cp313-cp313t-win_amd64.whl", hash = "sha256:5eea80d908b2c1f91486eb95b3fb6fab187e569ec9752ab7d9333d2e66bf2d6b", size = 12451181, upload-time = "2026-01-31T23:11:59.782Z" }, - { url = "https://files.pythonhosted.org/packages/6e/a4/a05c3a6418575e185dd84d0b9680b6bb2e2dc3e4202f036b7b4e22d6e9dc/numpy-2.4.2-cp313-cp313t-win_arm64.whl", hash = "sha256:fd49860271d52127d61197bb50b64f58454e9f578cb4b2c001a6de8b1f50b0b1", size = 10290756, upload-time = "2026-01-31T23:12:02.438Z" }, - { url = "https://files.pythonhosted.org/packages/18/88/b7df6050bf18fdcfb7046286c6535cabbdd2064a3440fca3f069d319c16e/numpy-2.4.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:444be170853f1f9d528428eceb55f12918e4fda5d8805480f36a002f1415e09b", size = 16663092, upload-time = "2026-01-31T23:12:04.521Z" }, - { url = "https://files.pythonhosted.org/packages/25/7a/1fee4329abc705a469a4afe6e69b1ef7e915117747886327104a8493a955/numpy-2.4.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d1240d50adff70c2a88217698ca844723068533f3f5c5fa6ee2e3220e3bdb000", size = 14698770, upload-time = "2026-01-31T23:12:06.96Z" }, - { url = "https://files.pythonhosted.org/packages/fb/0b/f9e49ba6c923678ad5bc38181c08ac5e53b7a5754dbca8e581aa1a56b1ff/numpy-2.4.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:7cdde6de52fb6664b00b056341265441192d1291c130e99183ec0d4b110ff8b1", size = 5208562, upload-time = "2026-01-31T23:12:09.632Z" }, - { url = "https://files.pythonhosted.org/packages/7d/12/d7de8f6f53f9bb76997e5e4c069eda2051e3fe134e9181671c4391677bb2/numpy-2.4.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:cda077c2e5b780200b6b3e09d0b42205a3d1c68f30c6dceb90401c13bff8fe74", size = 6543710, upload-time = "2026-01-31T23:12:11.969Z" }, - { url = "https://files.pythonhosted.org/packages/09/63/c66418c2e0268a31a4cf8a8b512685748200f8e8e8ec6c507ce14e773529/numpy-2.4.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d30291931c915b2ab5717c2974bb95ee891a1cf22ebc16a8006bd59cd210d40a", size = 15677205, upload-time = "2026-01-31T23:12:14.33Z" }, - { url = "https://files.pythonhosted.org/packages/5d/6c/7f237821c9642fb2a04d2f1e88b4295677144ca93285fd76eff3bcba858d/numpy-2.4.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bba37bc29d4d85761deed3954a1bc62be7cf462b9510b51d367b769a8c8df325", size = 16611738, upload-time = "2026-01-31T23:12:16.525Z" }, - { url = "https://files.pythonhosted.org/packages/c2/a7/39c4cdda9f019b609b5c473899d87abff092fc908cfe4d1ecb2fcff453b0/numpy-2.4.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b2f0073ed0868db1dcd86e052d37279eef185b9c8db5bf61f30f46adac63c909", size = 17028888, upload-time = "2026-01-31T23:12:19.306Z" }, - { url = "https://files.pythonhosted.org/packages/da/b3/e84bb64bdfea967cc10950d71090ec2d84b49bc691df0025dddb7c26e8e3/numpy-2.4.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7f54844851cdb630ceb623dcec4db3240d1ac13d4990532446761baede94996a", size = 18339556, upload-time = "2026-01-31T23:12:21.816Z" }, - { url = "https://files.pythonhosted.org/packages/88/f5/954a291bc1192a27081706862ac62bb5920fbecfbaa302f64682aa90beed/numpy-2.4.2-cp314-cp314-win32.whl", hash = "sha256:12e26134a0331d8dbd9351620f037ec470b7c75929cb8a1537f6bfe411152a1a", size = 6006899, upload-time = "2026-01-31T23:12:24.14Z" }, - { url = "https://files.pythonhosted.org/packages/05/cb/eff72a91b2efdd1bc98b3b8759f6a1654aa87612fc86e3d87d6fe4f948c4/numpy-2.4.2-cp314-cp314-win_amd64.whl", hash = "sha256:068cdb2d0d644cdb45670810894f6a0600797a69c05f1ac478e8d31670b8ee75", size = 12443072, upload-time = "2026-01-31T23:12:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/37/75/62726948db36a56428fce4ba80a115716dc4fad6a3a4352487f8bb950966/numpy-2.4.2-cp314-cp314-win_arm64.whl", hash = "sha256:6ed0be1ee58eef41231a5c943d7d1375f093142702d5723ca2eb07db9b934b05", size = 10494886, upload-time = "2026-01-31T23:12:28.488Z" }, - { url = "https://files.pythonhosted.org/packages/36/2f/ee93744f1e0661dc267e4b21940870cabfae187c092e1433b77b09b50ac4/numpy-2.4.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:98f16a80e917003a12c0580f97b5f875853ebc33e2eaa4bccfc8201ac6869308", size = 14818567, upload-time = "2026-01-31T23:12:30.709Z" }, - { url = "https://files.pythonhosted.org/packages/a7/24/6535212add7d76ff938d8bdc654f53f88d35cddedf807a599e180dcb8e66/numpy-2.4.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:20abd069b9cda45874498b245c8015b18ace6de8546bf50dfa8cea1696ed06ef", size = 5328372, upload-time = "2026-01-31T23:12:32.962Z" }, - { url = "https://files.pythonhosted.org/packages/5e/9d/c48f0a035725f925634bf6b8994253b43f2047f6778a54147d7e213bc5a7/numpy-2.4.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:e98c97502435b53741540a5717a6749ac2ada901056c7db951d33e11c885cc7d", size = 6649306, upload-time = "2026-01-31T23:12:34.797Z" }, - { url = "https://files.pythonhosted.org/packages/81/05/7c73a9574cd4a53a25907bad38b59ac83919c0ddc8234ec157f344d57d9a/numpy-2.4.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da6cad4e82cb893db4b69105c604d805e0c3ce11501a55b5e9f9083b47d2ffe8", size = 15722394, upload-time = "2026-01-31T23:12:36.565Z" }, - { url = "https://files.pythonhosted.org/packages/35/fa/4de10089f21fc7d18442c4a767ab156b25c2a6eaf187c0db6d9ecdaeb43f/numpy-2.4.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e4424677ce4b47fe73c8b5556d876571f7c6945d264201180db2dc34f676ab5", size = 16653343, upload-time = "2026-01-31T23:12:39.188Z" }, - { url = "https://files.pythonhosted.org/packages/b8/f9/d33e4ffc857f3763a57aa85650f2e82486832d7492280ac21ba9efda80da/numpy-2.4.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2b8f157c8a6f20eb657e240f8985cc135598b2b46985c5bccbde7616dc9c6b1e", size = 17078045, upload-time = "2026-01-31T23:12:42.041Z" }, - { url = "https://files.pythonhosted.org/packages/c8/b8/54bdb43b6225badbea6389fa038c4ef868c44f5890f95dd530a218706da3/numpy-2.4.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5daf6f3914a733336dab21a05cdec343144600e964d2fcdabaac0c0269874b2a", size = 18380024, upload-time = "2026-01-31T23:12:44.331Z" }, - { url = "https://files.pythonhosted.org/packages/a5/55/6e1a61ded7af8df04016d81b5b02daa59f2ea9252ee0397cb9f631efe9e5/numpy-2.4.2-cp314-cp314t-win32.whl", hash = "sha256:8c50dd1fc8826f5b26a5ee4d77ca55d88a895f4e4819c7ecc2a9f5905047a443", size = 6153937, upload-time = "2026-01-31T23:12:47.229Z" }, - { url = "https://files.pythonhosted.org/packages/45/aa/fa6118d1ed6d776b0983f3ceac9b1a5558e80df9365b1c3aa6d42bf9eee4/numpy-2.4.2-cp314-cp314t-win_amd64.whl", hash = "sha256:fcf92bee92742edd401ba41135185866f7026c502617f422eb432cfeca4fe236", size = 12631844, upload-time = "2026-01-31T23:12:48.997Z" }, - { url = "https://files.pythonhosted.org/packages/32/0a/2ec5deea6dcd158f254a7b372fb09cfba5719419c8d66343bab35237b3fb/numpy-2.4.2-cp314-cp314t-win_arm64.whl", hash = "sha256:1f92f53998a17265194018d1cc321b2e96e900ca52d54c7c77837b71b9465181", size = 10565379, upload-time = "2026-01-31T23:12:51.345Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f8/50e14d36d915ef64d8f8bc4a087fc8264d82c785eda6711f80ab7e620335/numpy-2.4.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:89f7268c009bc492f506abd6f5265defa7cb3f7487dc21d357c3d290add45082", size = 16833179, upload-time = "2026-01-31T23:12:53.5Z" }, - { url = "https://files.pythonhosted.org/packages/17/17/809b5cad63812058a8189e91a1e2d55a5a18fd04611dbad244e8aeae465c/numpy-2.4.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6dee3bb76aa4009d5a912180bf5b2de012532998d094acee25d9cb8dee3e44a", size = 14889755, upload-time = "2026-01-31T23:12:55.933Z" }, - { url = "https://files.pythonhosted.org/packages/3e/ea/181b9bcf7627fc8371720316c24db888dcb9829b1c0270abf3d288b2e29b/numpy-2.4.2-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:cd2bd2bbed13e213d6b55dc1d035a4f91748a7d3edc9480c13898b0353708920", size = 5399500, upload-time = "2026-01-31T23:12:58.671Z" }, - { url = "https://files.pythonhosted.org/packages/33/9f/413adf3fc955541ff5536b78fcf0754680b3c6d95103230252a2c9408d23/numpy-2.4.2-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:cf28c0c1d4c4bf00f509fa7eb02c58d7caf221b50b467bcb0d9bbf1584d5c821", size = 6714252, upload-time = "2026-01-31T23:13:00.518Z" }, - { url = "https://files.pythonhosted.org/packages/91/da/643aad274e29ccbdf42ecd94dafe524b81c87bcb56b83872d54827f10543/numpy-2.4.2-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e04ae107ac591763a47398bb45b568fc38f02dbc4aa44c063f67a131f99346cb", size = 15797142, upload-time = "2026-01-31T23:13:02.219Z" }, - { url = "https://files.pythonhosted.org/packages/66/27/965b8525e9cb5dc16481b30a1b3c21e50c7ebf6e9dbd48d0c4d0d5089c7e/numpy-2.4.2-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:602f65afdef699cda27ec0b9224ae5dc43e328f4c24c689deaf77133dbee74d0", size = 16727979, upload-time = "2026-01-31T23:13:04.62Z" }, - { url = "https://files.pythonhosted.org/packages/de/e5/b7d20451657664b07986c2f6e3be564433f5dcaf3482d68eaecd79afaf03/numpy-2.4.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:be71bf1edb48ebbbf7f6337b5bfd2f895d1902f6335a5830b20141fc126ffba0", size = 12502577, upload-time = "2026-01-31T23:13:07.08Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/10/8b/c265f4823726ab832de836cdd184d0986dcf94480f81e8739692a7ac7af2/numpy-2.4.3.tar.gz", hash = "sha256:483a201202b73495f00dbc83796c6ae63137a9bdade074f7648b3e32613412dd", size = 20727743, upload-time = "2026-03-09T07:58:53.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/51/5093a2df15c4dc19da3f79d1021e891f5dcf1d9d1db6ba38891d5590f3fe/numpy-2.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:33b3bf58ee84b172c067f56aeadc7ee9ab6de69c5e800ab5b10295d54c581adb", size = 16957183, upload-time = "2026-03-09T07:55:57.774Z" }, + { url = "https://files.pythonhosted.org/packages/b5/7c/c061f3de0630941073d2598dc271ac2f6cbcf5c83c74a5870fea07488333/numpy-2.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8ba7b51e71c05aa1f9bc3641463cd82308eab40ce0d5c7e1fd4038cbf9938147", size = 14968734, upload-time = "2026-03-09T07:56:00.494Z" }, + { url = "https://files.pythonhosted.org/packages/ef/27/d26c85cbcd86b26e4f125b0668e7a7c0542d19dd7d23ee12e87b550e95b5/numpy-2.4.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a1988292870c7cb9d0ebb4cc96b4d447513a9644801de54606dc7aabf2b7d920", size = 5475288, upload-time = "2026-03-09T07:56:02.857Z" }, + { url = "https://files.pythonhosted.org/packages/2b/09/3c4abbc1dcd8010bf1a611d174c7aa689fc505585ec806111b4406f6f1b1/numpy-2.4.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:23b46bb6d8ecb68b58c09944483c135ae5f0e9b8d8858ece5e4ead783771d2a9", size = 6805253, upload-time = "2026-03-09T07:56:04.53Z" }, + { url = "https://files.pythonhosted.org/packages/21/bc/e7aa3f6817e40c3f517d407742337cbb8e6fc4b83ce0b55ab780c829243b/numpy-2.4.3-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a016db5c5dba78fa8fe9f5d80d6708f9c42ab087a739803c0ac83a43d686a470", size = 15969479, upload-time = "2026-03-09T07:56:06.638Z" }, + { url = "https://files.pythonhosted.org/packages/78/51/9f5d7a41f0b51649ddf2f2320595e15e122a40610b233d51928dd6c92353/numpy-2.4.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:715de7f82e192e8cae5a507a347d97ad17598f8e026152ca97233e3666daaa71", size = 16901035, upload-time = "2026-03-09T07:56:09.405Z" }, + { url = "https://files.pythonhosted.org/packages/64/6e/b221dd847d7181bc5ee4857bfb026182ef69499f9305eb1371cbb1aea626/numpy-2.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2ddb7919366ee468342b91dea2352824c25b55814a987847b6c52003a7c97f15", size = 17325657, upload-time = "2026-03-09T07:56:12.067Z" }, + { url = "https://files.pythonhosted.org/packages/eb/b8/8f3fd2da596e1063964b758b5e3c970aed1949a05200d7e3d46a9d46d643/numpy-2.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a315e5234d88067f2d97e1f2ef670a7569df445d55400f1e33d117418d008d52", size = 18635512, upload-time = "2026-03-09T07:56:14.629Z" }, + { url = "https://files.pythonhosted.org/packages/5c/24/2993b775c37e39d2f8ab4125b44337ab0b2ba106c100980b7c274a22bee7/numpy-2.4.3-cp311-cp311-win32.whl", hash = "sha256:2b3f8d2c4589b1a2028d2a770b0fc4d1f332fb5e01521f4de3199a896d158ddd", size = 6238100, upload-time = "2026-03-09T07:56:17.243Z" }, + { url = "https://files.pythonhosted.org/packages/76/1d/edccf27adedb754db7c4511d5eac8b83f004ae948fe2d3509e8b78097d4c/numpy-2.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:77e76d932c49a75617c6d13464e41203cd410956614d0a0e999b25e9e8d27eec", size = 12609816, upload-time = "2026-03-09T07:56:19.089Z" }, + { url = "https://files.pythonhosted.org/packages/92/82/190b99153480076c8dce85f4cfe7d53ea84444145ffa54cb58dcd460d66b/numpy-2.4.3-cp311-cp311-win_arm64.whl", hash = "sha256:eb610595dd91560905c132c709412b512135a60f1851ccbd2c959e136431ff67", size = 10485757, upload-time = "2026-03-09T07:56:21.753Z" }, + { url = "https://files.pythonhosted.org/packages/a9/ed/6388632536f9788cea23a3a1b629f25b43eaacd7d7377e5d6bc7b9deb69b/numpy-2.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:61b0cbabbb6126c8df63b9a3a0c4b1f44ebca5e12ff6997b80fcf267fb3150ef", size = 16669628, upload-time = "2026-03-09T07:56:24.252Z" }, + { url = "https://files.pythonhosted.org/packages/74/1b/ee2abfc68e1ce728b2958b6ba831d65c62e1b13ce3017c13943f8f9b5b2e/numpy-2.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7395e69ff32526710748f92cd8c9849b361830968ea3e24a676f272653e8983e", size = 14696872, upload-time = "2026-03-09T07:56:26.991Z" }, + { url = "https://files.pythonhosted.org/packages/ba/d1/780400e915ff5638166f11ca9dc2c5815189f3d7cf6f8759a1685e586413/numpy-2.4.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:abdce0f71dcb4a00e4e77f3faf05e4616ceccfe72ccaa07f47ee79cda3b7b0f4", size = 5203489, upload-time = "2026-03-09T07:56:29.414Z" }, + { url = "https://files.pythonhosted.org/packages/0b/bb/baffa907e9da4cc34a6e556d6d90e032f6d7a75ea47968ea92b4858826c4/numpy-2.4.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:48da3a4ee1336454b07497ff7ec83903efa5505792c4e6d9bf83d99dc07a1e18", size = 6550814, upload-time = "2026-03-09T07:56:32.225Z" }, + { url = "https://files.pythonhosted.org/packages/7b/12/8c9f0c6c95f76aeb20fc4a699c33e9f827fa0d0f857747c73bb7b17af945/numpy-2.4.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:32e3bef222ad6b052280311d1d60db8e259e4947052c3ae7dd6817451fc8a4c5", size = 15666601, upload-time = "2026-03-09T07:56:34.461Z" }, + { url = "https://files.pythonhosted.org/packages/bd/79/cc665495e4d57d0aa6fbcc0aa57aa82671dfc78fbf95fe733ed86d98f52a/numpy-2.4.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e7dd01a46700b1967487141a66ac1a3cf0dd8ebf1f08db37d46389401512ca97", size = 16621358, upload-time = "2026-03-09T07:56:36.852Z" }, + { url = "https://files.pythonhosted.org/packages/a8/40/b4ecb7224af1065c3539f5ecfff879d090de09608ad1008f02c05c770cb3/numpy-2.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:76f0f283506c28b12bba319c0fab98217e9f9b54e6160e9c79e9f7348ba32e9c", size = 17016135, upload-time = "2026-03-09T07:56:39.337Z" }, + { url = "https://files.pythonhosted.org/packages/f7/b1/6a88e888052eed951afed7a142dcdf3b149a030ca59b4c71eef085858e43/numpy-2.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:737f630a337364665aba3b5a77e56a68cc42d350edd010c345d65a3efa3addcc", size = 18345816, upload-time = "2026-03-09T07:56:42.31Z" }, + { url = "https://files.pythonhosted.org/packages/f3/8f/103a60c5f8c3d7fc678c19cd7b2476110da689ccb80bc18050efbaeae183/numpy-2.4.3-cp312-cp312-win32.whl", hash = "sha256:26952e18d82a1dbbc2f008d402021baa8d6fc8e84347a2072a25e08b46d698b9", size = 5960132, upload-time = "2026-03-09T07:56:44.851Z" }, + { url = "https://files.pythonhosted.org/packages/d7/7c/f5ee1bf6ed888494978046a809df2882aad35d414b622893322df7286879/numpy-2.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:65f3c2455188f09678355f5cae1f959a06b778bc66d535da07bf2ef20cd319d5", size = 12316144, upload-time = "2026-03-09T07:56:47.057Z" }, + { url = "https://files.pythonhosted.org/packages/71/46/8d1cb3f7a00f2fb6394140e7e6623696e54c6318a9d9691bb4904672cf42/numpy-2.4.3-cp312-cp312-win_arm64.whl", hash = "sha256:2abad5c7fef172b3377502bde47892439bae394a71bc329f31df0fd829b41a9e", size = 10220364, upload-time = "2026-03-09T07:56:49.849Z" }, + { url = "https://files.pythonhosted.org/packages/b6/d0/1fe47a98ce0df229238b77611340aff92d52691bcbc10583303181abf7fc/numpy-2.4.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b346845443716c8e542d54112966383b448f4a3ba5c66409771b8c0889485dd3", size = 16665297, upload-time = "2026-03-09T07:56:52.296Z" }, + { url = "https://files.pythonhosted.org/packages/27/d9/4e7c3f0e68dfa91f21c6fb6cf839bc829ec920688b1ce7ec722b1a6202fb/numpy-2.4.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2629289168f4897a3c4e23dc98d6f1731f0fc0fe52fb9db19f974041e4cc12b9", size = 14691853, upload-time = "2026-03-09T07:56:54.992Z" }, + { url = "https://files.pythonhosted.org/packages/3a/66/bd096b13a87549683812b53ab211e6d413497f84e794fb3c39191948da97/numpy-2.4.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:bb2e3cf95854233799013779216c57e153c1ee67a0bf92138acca0e429aefaee", size = 5198435, upload-time = "2026-03-09T07:56:57.184Z" }, + { url = "https://files.pythonhosted.org/packages/a2/2f/687722910b5a5601de2135c891108f51dfc873d8e43c8ed9f4ebb440b4a2/numpy-2.4.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:7f3408ff897f8ab07a07fbe2823d7aee6ff644c097cc1f90382511fe982f647f", size = 6546347, upload-time = "2026-03-09T07:56:59.531Z" }, + { url = "https://files.pythonhosted.org/packages/bf/ec/7971c4e98d86c564750393fab8d7d83d0a9432a9d78bb8a163a6dc59967a/numpy-2.4.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:decb0eb8a53c3b009b0962378065589685d66b23467ef5dac16cbe818afde27f", size = 15664626, upload-time = "2026-03-09T07:57:01.385Z" }, + { url = "https://files.pythonhosted.org/packages/7e/eb/7daecbea84ec935b7fc732e18f532073064a3816f0932a40a17f3349185f/numpy-2.4.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5f51900414fc9204a0e0da158ba2ac52b75656e7dce7e77fb9f84bfa343b4cc", size = 16608916, upload-time = "2026-03-09T07:57:04.008Z" }, + { url = "https://files.pythonhosted.org/packages/df/58/2a2b4a817ffd7472dca4421d9f0776898b364154e30c95f42195041dc03b/numpy-2.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6bd06731541f89cdc01b261ba2c9e037f1543df7472517836b78dfb15bd6e476", size = 17015824, upload-time = "2026-03-09T07:57:06.347Z" }, + { url = "https://files.pythonhosted.org/packages/4a/ca/627a828d44e78a418c55f82dd4caea8ea4a8ef24e5144d9e71016e52fb40/numpy-2.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:22654fe6be0e5206f553a9250762c653d3698e46686eee53b399ab90da59bd92", size = 18334581, upload-time = "2026-03-09T07:57:09.114Z" }, + { url = "https://files.pythonhosted.org/packages/cd/c0/76f93962fc79955fcba30a429b62304332345f22d4daec1cb33653425643/numpy-2.4.3-cp313-cp313-win32.whl", hash = "sha256:d71e379452a2f670ccb689ec801b1218cd3983e253105d6e83780967e899d687", size = 5958618, upload-time = "2026-03-09T07:57:11.432Z" }, + { url = "https://files.pythonhosted.org/packages/b1/3c/88af0040119209b9b5cb59485fa48b76f372c73068dbf9254784b975ac53/numpy-2.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:0a60e17a14d640f49146cb38e3f105f571318db7826d9b6fef7e4dce758faecd", size = 12312824, upload-time = "2026-03-09T07:57:13.586Z" }, + { url = "https://files.pythonhosted.org/packages/58/ce/3d07743aced3d173f877c3ef6a454c2174ba42b584ab0b7e6d99374f51ed/numpy-2.4.3-cp313-cp313-win_arm64.whl", hash = "sha256:c9619741e9da2059cd9c3f206110b97583c7152c1dc9f8aafd4beb450ac1c89d", size = 10221218, upload-time = "2026-03-09T07:57:16.183Z" }, + { url = "https://files.pythonhosted.org/packages/62/09/d96b02a91d09e9d97862f4fc8bfebf5400f567d8eb1fe4b0cc4795679c15/numpy-2.4.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7aa4e54f6469300ebca1d9eb80acd5253cdfa36f2c03d79a35883687da430875", size = 14819570, upload-time = "2026-03-09T07:57:18.564Z" }, + { url = "https://files.pythonhosted.org/packages/b5/ca/0b1aba3905fdfa3373d523b2b15b19029f4f3031c87f4066bd9d20ef6c6b/numpy-2.4.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d1b90d840b25874cf5cd20c219af10bac3667db3876d9a495609273ebe679070", size = 5326113, upload-time = "2026-03-09T07:57:21.052Z" }, + { url = "https://files.pythonhosted.org/packages/c0/63/406e0fd32fcaeb94180fd6a4c41e55736d676c54346b7efbce548b94a914/numpy-2.4.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a749547700de0a20a6718293396ec237bb38218049cfce788e08fcb716e8cf73", size = 6646370, upload-time = "2026-03-09T07:57:22.804Z" }, + { url = "https://files.pythonhosted.org/packages/b6/d0/10f7dc157d4b37af92720a196be6f54f889e90dcd30dce9dc657ed92c257/numpy-2.4.3-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94f3c4a151a2e529adf49c1d54f0f57ff8f9b233ee4d44af623a81553ab86368", size = 15723499, upload-time = "2026-03-09T07:57:24.693Z" }, + { url = "https://files.pythonhosted.org/packages/66/f1/d1c2bf1161396629701bc284d958dc1efa3a5a542aab83cf11ee6eb4cba5/numpy-2.4.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22c31dc07025123aedf7f2db9e91783df13f1776dc52c6b22c620870dc0fab22", size = 16657164, upload-time = "2026-03-09T07:57:27.676Z" }, + { url = "https://files.pythonhosted.org/packages/1a/be/cca19230b740af199ac47331a21c71e7a3d0ba59661350483c1600d28c37/numpy-2.4.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:148d59127ac95979d6f07e4d460f934ebdd6eed641db9c0db6c73026f2b2101a", size = 17081544, upload-time = "2026-03-09T07:57:30.664Z" }, + { url = "https://files.pythonhosted.org/packages/b9/c5/9602b0cbb703a0936fb40f8a95407e8171935b15846de2f0776e08af04c7/numpy-2.4.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a97cbf7e905c435865c2d939af3d93f99d18eaaa3cabe4256f4304fb51604349", size = 18380290, upload-time = "2026-03-09T07:57:33.763Z" }, + { url = "https://files.pythonhosted.org/packages/ed/81/9f24708953cd30be9ee36ec4778f4b112b45165812f2ada4cc5ea1c1f254/numpy-2.4.3-cp313-cp313t-win32.whl", hash = "sha256:be3b8487d725a77acccc9924f65fd8bce9af7fac8c9820df1049424a2115af6c", size = 6082814, upload-time = "2026-03-09T07:57:36.491Z" }, + { url = "https://files.pythonhosted.org/packages/e2/9e/52f6eaa13e1a799f0ab79066c17f7016a4a8ae0c1aefa58c82b4dab690b4/numpy-2.4.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1ec84fd7c8e652b0f4aaaf2e6e9cc8eaa9b1b80a537e06b2e3a2fb176eedcb26", size = 12452673, upload-time = "2026-03-09T07:57:38.281Z" }, + { url = "https://files.pythonhosted.org/packages/c4/04/b8cece6ead0b30c9fbd99bb835ad7ea0112ac5f39f069788c5558e3b1ab2/numpy-2.4.3-cp313-cp313t-win_arm64.whl", hash = "sha256:120df8c0a81ebbf5b9020c91439fccd85f5e018a927a39f624845be194a2be02", size = 10290907, upload-time = "2026-03-09T07:57:40.747Z" }, + { url = "https://files.pythonhosted.org/packages/70/ae/3936f79adebf8caf81bd7a599b90a561334a658be4dcc7b6329ebf4ee8de/numpy-2.4.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:5884ce5c7acfae1e4e1b6fde43797d10aa506074d25b531b4f54bde33c0c31d4", size = 16664563, upload-time = "2026-03-09T07:57:43.817Z" }, + { url = "https://files.pythonhosted.org/packages/9b/62/760f2b55866b496bb1fa7da2a6db076bef908110e568b02fcfc1422e2a3a/numpy-2.4.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:297837823f5bc572c5f9379b0c9f3a3365f08492cbdc33bcc3af174372ebb168", size = 14702161, upload-time = "2026-03-09T07:57:46.169Z" }, + { url = "https://files.pythonhosted.org/packages/32/af/a7a39464e2c0a21526fb4fb76e346fb172ebc92f6d1c7a07c2c139cc17b1/numpy-2.4.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:a111698b4a3f8dcbe54c64a7708f049355abd603e619013c346553c1fd4ca90b", size = 5208738, upload-time = "2026-03-09T07:57:48.506Z" }, + { url = "https://files.pythonhosted.org/packages/29/8c/2a0cf86a59558fa078d83805589c2de490f29ed4fb336c14313a161d358a/numpy-2.4.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:4bd4741a6a676770e0e97fe9ab2e51de01183df3dcbcec591d26d331a40de950", size = 6543618, upload-time = "2026-03-09T07:57:50.591Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b8/612ce010c0728b1c363fa4ea3aa4c22fe1c5da1de008486f8c2f5cb92fae/numpy-2.4.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:54f29b877279d51e210e0c80709ee14ccbbad647810e8f3d375561c45ef613dd", size = 15680676, upload-time = "2026-03-09T07:57:52.34Z" }, + { url = "https://files.pythonhosted.org/packages/a9/7e/4f120ecc54ba26ddf3dc348eeb9eb063f421de65c05fc961941798feea18/numpy-2.4.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:679f2a834bae9020f81534671c56fd0cc76dd7e5182f57131478e23d0dc59e24", size = 16613492, upload-time = "2026-03-09T07:57:54.91Z" }, + { url = "https://files.pythonhosted.org/packages/2c/86/1b6020db73be330c4b45d5c6ee4295d59cfeef0e3ea323959d053e5a6909/numpy-2.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d84f0f881cb2225c2dfd7f78a10a5645d487a496c6668d6cc39f0f114164f3d0", size = 17031789, upload-time = "2026-03-09T07:57:57.641Z" }, + { url = "https://files.pythonhosted.org/packages/07/3a/3b90463bf41ebc21d1b7e06079f03070334374208c0f9a1f05e4ae8455e7/numpy-2.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d213c7e6e8d211888cc359bab7199670a00f5b82c0978b9d1c75baf1eddbeac0", size = 18339941, upload-time = "2026-03-09T07:58:00.577Z" }, + { url = "https://files.pythonhosted.org/packages/a8/74/6d736c4cd962259fd8bae9be27363eb4883a2f9069763747347544c2a487/numpy-2.4.3-cp314-cp314-win32.whl", hash = "sha256:52077feedeff7c76ed7c9f1a0428558e50825347b7545bbb8523da2cd55c547a", size = 6007503, upload-time = "2026-03-09T07:58:03.331Z" }, + { url = "https://files.pythonhosted.org/packages/48/39/c56ef87af669364356bb011922ef0734fc49dad51964568634c72a009488/numpy-2.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:0448e7f9caefb34b4b7dd2b77f21e8906e5d6f0365ad525f9f4f530b13df2afc", size = 12444915, upload-time = "2026-03-09T07:58:06.353Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1f/ab8528e38d295fd349310807496fabb7cf9fe2e1f70b97bc20a483ea9d4a/numpy-2.4.3-cp314-cp314-win_arm64.whl", hash = "sha256:b44fd60341c4d9783039598efadd03617fa28d041fc37d22b62d08f2027fa0e7", size = 10494875, upload-time = "2026-03-09T07:58:08.734Z" }, + { url = "https://files.pythonhosted.org/packages/e6/ef/b7c35e4d5ef141b836658ab21a66d1a573e15b335b1d111d31f26c8ef80f/numpy-2.4.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0a195f4216be9305a73c0e91c9b026a35f2161237cf1c6de9b681637772ea657", size = 14822225, upload-time = "2026-03-09T07:58:11.034Z" }, + { url = "https://files.pythonhosted.org/packages/cd/8d/7730fa9278cf6648639946cc816e7cc89f0d891602584697923375f801ed/numpy-2.4.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:cd32fbacb9fd1bf041bf8e89e4576b6f00b895f06d00914820ae06a616bdfef7", size = 5328769, upload-time = "2026-03-09T07:58:13.67Z" }, + { url = "https://files.pythonhosted.org/packages/47/01/d2a137317c958b074d338807c1b6a383406cdf8b8e53b075d804cc3d211d/numpy-2.4.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:2e03c05abaee1f672e9d67bc858f300b5ccba1c21397211e8d77d98350972093", size = 6649461, upload-time = "2026-03-09T07:58:15.912Z" }, + { url = "https://files.pythonhosted.org/packages/5c/34/812ce12bc0f00272a4b0ec0d713cd237cb390666eb6206323d1cc9cedbb2/numpy-2.4.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d1ce23cce91fcea443320a9d0ece9b9305d4368875bab09538f7a5b4131938a", size = 15725809, upload-time = "2026-03-09T07:58:17.787Z" }, + { url = "https://files.pythonhosted.org/packages/25/c0/2aed473a4823e905e765fee3dc2cbf504bd3e68ccb1150fbdabd5c39f527/numpy-2.4.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c59020932feb24ed49ffd03704fbab89f22aa9c0d4b180ff45542fe8918f5611", size = 16655242, upload-time = "2026-03-09T07:58:20.476Z" }, + { url = "https://files.pythonhosted.org/packages/f2/c8/7e052b2fc87aa0e86de23f20e2c42bd261c624748aa8efd2c78f7bb8d8c6/numpy-2.4.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9684823a78a6cd6ad7511fc5e25b07947d1d5b5e2812c93fe99d7d4195130720", size = 17080660, upload-time = "2026-03-09T07:58:23.067Z" }, + { url = "https://files.pythonhosted.org/packages/f3/3d/0876746044db2adcb11549f214d104f2e1be00f07a67edbb4e2812094847/numpy-2.4.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0200b25c687033316fb39f0ff4e3e690e8957a2c3c8d22499891ec58c37a3eb5", size = 18380384, upload-time = "2026-03-09T07:58:25.839Z" }, + { url = "https://files.pythonhosted.org/packages/07/12/8160bea39da3335737b10308df4f484235fd297f556745f13092aa039d3b/numpy-2.4.3-cp314-cp314t-win32.whl", hash = "sha256:5e10da9e93247e554bb1d22f8edc51847ddd7dde52d85ce31024c1b4312bfba0", size = 6154547, upload-time = "2026-03-09T07:58:28.289Z" }, + { url = "https://files.pythonhosted.org/packages/42/f3/76534f61f80d74cc9cdf2e570d3d4eeb92c2280a27c39b0aaf471eda7b48/numpy-2.4.3-cp314-cp314t-win_amd64.whl", hash = "sha256:45f003dbdffb997a03da2d1d0cb41fbd24a87507fb41605c0420a3db5bd4667b", size = 12633645, upload-time = "2026-03-09T07:58:30.384Z" }, + { url = "https://files.pythonhosted.org/packages/1f/b6/7c0d4334c15983cec7f92a69e8ce9b1e6f31857e5ee3a413ac424e6bd63d/numpy-2.4.3-cp314-cp314t-win_arm64.whl", hash = "sha256:4d382735cecd7bcf090172489a525cd7d4087bc331f7df9f60ddc9a296cf208e", size = 10565454, upload-time = "2026-03-09T07:58:33.031Z" }, + { url = "https://files.pythonhosted.org/packages/64/e4/4dab9fb43c83719c29241c535d9e07be73bea4bc0c6686c5816d8e1b6689/numpy-2.4.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c6b124bfcafb9e8d3ed09130dbee44848c20b3e758b6bbf006e641778927c028", size = 16834892, upload-time = "2026-03-09T07:58:35.334Z" }, + { url = "https://files.pythonhosted.org/packages/c9/29/f8b6d4af90fed3dfda84ebc0df06c9833d38880c79ce954e5b661758aa31/numpy-2.4.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:76dbb9d4e43c16cf9aa711fcd8de1e2eeb27539dcefb60a1d5e9f12fae1d1ed8", size = 14893070, upload-time = "2026-03-09T07:58:37.7Z" }, + { url = "https://files.pythonhosted.org/packages/9a/04/a19b3c91dbec0a49269407f15d5753673a09832daed40c45e8150e6fa558/numpy-2.4.3-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:29363fbfa6f8ee855d7569c96ce524845e3d726d6c19b29eceec7dd555dab152", size = 5399609, upload-time = "2026-03-09T07:58:39.853Z" }, + { url = "https://files.pythonhosted.org/packages/79/34/4d73603f5420eab89ea8a67097b31364bf7c30f811d4dd84b1659c7476d9/numpy-2.4.3-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:bc71942c789ef415a37f0d4eab90341425a00d538cd0642445d30b41023d3395", size = 6714355, upload-time = "2026-03-09T07:58:42.365Z" }, + { url = "https://files.pythonhosted.org/packages/58/ad/1100d7229bb248394939a12a8074d485b655e8ed44207d328fdd7fcebc7b/numpy-2.4.3-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e58765ad74dcebd3ef0208a5078fba32dc8ec3578fe84a604432950cd043d79", size = 15800434, upload-time = "2026-03-09T07:58:44.837Z" }, + { url = "https://files.pythonhosted.org/packages/0c/fd/16d710c085d28ba4feaf29ac60c936c9d662e390344f94a6beaa2ac9899b/numpy-2.4.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e236dbda4e1d319d681afcbb136c0c4a8e0f1a5c58ceec2adebb547357fe857", size = 16729409, upload-time = "2026-03-09T07:58:47.972Z" }, + { url = "https://files.pythonhosted.org/packages/57/a7/b35835e278c18b85206834b3aa3abe68e77a98769c59233d1f6300284781/numpy-2.4.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:4b42639cdde6d24e732ff823a3fa5b701d8acad89c4142bc1d0bd6dc85200ba5", size = 12504685, upload-time = "2026-03-09T07:58:50.525Z" }, ] [[package]] @@ -2297,15 +2313,15 @@ wheels = [ [[package]] name = "python-discovery" -version = "1.1.1" +version = "1.1.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ec/67/09765eacf4e44413c4f8943ba5a317fcb9c7b447c3b8b0b7fce7e3090b0b/python_discovery-1.1.1.tar.gz", hash = "sha256:584c08b141c5b7029f206b4e8b78b1a1764b22121e21519b89dec56936e95b0a", size = 56016, upload-time = "2026-03-07T00:00:56.354Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/7e/9f3b0dd3a074a6c3e1e79f35e465b1f2ee4b262d619de00cfce523cc9b24/python_discovery-1.1.3.tar.gz", hash = "sha256:7acca36e818cd88e9b2ba03e045ad7e93e1713e29c6bbfba5d90202310b7baa5", size = 56945, upload-time = "2026-03-10T15:08:15.038Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/75/0f/2bf7e3b5a4a65f623cb820feb5793e243fad58ae561015ee15a6152f67a2/python_discovery-1.1.1-py3-none-any.whl", hash = "sha256:69f11073fa2392251e405d4e847d60ffffd25fd762a0dc4d1a7d6b9c3f79f1a3", size = 30732, upload-time = "2026-03-07T00:00:55.143Z" }, + { url = "https://files.pythonhosted.org/packages/e7/80/73211fc5bfbfc562369b4aa61dc1e4bf07dc7b34df7b317e4539316b809c/python_discovery-1.1.3-py3-none-any.whl", hash = "sha256:90e795f0121bc84572e737c9aa9966311b9fde44ffb88a5953b3ec9b31c6945e", size = 31485, upload-time = "2026-03-10T15:08:13.06Z" }, ] [[package]] @@ -2623,11 +2639,11 @@ wheels = [ [[package]] name = "setuptools" -version = "82.0.0" +version = "82.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/f3/748f4d6f65d1756b9ae577f329c951cda23fb900e4de9f70900ced962085/setuptools-82.0.0.tar.gz", hash = "sha256:22e0a2d69474c6ae4feb01951cb69d515ed23728cf96d05513d36e42b62b37cb", size = 1144893, upload-time = "2026-02-08T15:08:40.206Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/c6/76dc613121b793286a3f91621d7b75a2b493e0390ddca50f11993eadf192/setuptools-82.0.0-py3-none-any.whl", hash = "sha256:70b18734b607bd1da571d097d236cfcfacaf01de45717d59e6e04b96877532e0", size = 1003468, upload-time = "2026-02-08T15:08:38.723Z" }, + { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" }, ] [[package]] @@ -3117,6 +3133,13 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0f/8b/4b61d6e13f7108f36910df9ab4b58fd389cc2520d54d81b88660804aad99/torch-2.10.0-2-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:418997cb02d0a0f1497cf6a09f63166f9f5df9f3e16c8a716ab76a72127c714f", size = 79423467, upload-time = "2026-02-10T21:44:48.711Z" }, { url = "https://files.pythonhosted.org/packages/d3/54/a2ba279afcca44bbd320d4e73675b282fcee3d81400ea1b53934efca6462/torch-2.10.0-2-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:13ec4add8c3faaed8d13e0574f5cd4a323c11655546f91fbe6afa77b57423574", size = 79498202, upload-time = "2026-02-10T21:44:52.603Z" }, { url = "https://files.pythonhosted.org/packages/ec/23/2c9fe0c9c27f7f6cb865abcea8a4568f29f00acaeadfc6a37f6801f84cb4/torch-2.10.0-2-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:e521c9f030a3774ed770a9c011751fb47c4d12029a3d6522116e48431f2ff89e", size = 79498254, upload-time = "2026-02-10T21:44:44.095Z" }, + { url = "https://files.pythonhosted.org/packages/16/ee/efbd56687be60ef9af0c9c0ebe106964c07400eade5b0af8902a1d8cd58c/torch-2.10.0-3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a1ff626b884f8c4e897c4c33782bdacdff842a165fee79817b1dd549fdda1321", size = 915510070, upload-time = "2026-03-11T14:16:39.386Z" }, + { url = "https://files.pythonhosted.org/packages/36/ab/7b562f1808d3f65414cd80a4f7d4bb00979d9355616c034c171249e1a303/torch-2.10.0-3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ac5bdcbb074384c66fa160c15b1ead77839e3fe7ed117d667249afce0acabfac", size = 915518691, upload-time = "2026-03-11T14:15:43.147Z" }, + { url = "https://files.pythonhosted.org/packages/b3/7a/abada41517ce0011775f0f4eacc79659bc9bc6c361e6bfe6f7052a6b9363/torch-2.10.0-3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:98c01b8bb5e3240426dcde1446eed6f40c778091c8544767ef1168fc663a05a6", size = 915622781, upload-time = "2026-03-11T14:17:11.354Z" }, + { url = "https://files.pythonhosted.org/packages/ab/c6/4dfe238342ffdcec5aef1c96c457548762d33c40b45a1ab7033bb26d2ff2/torch-2.10.0-3-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:80b1b5bfe38eb0e9f5ff09f206dcac0a87aadd084230d4a36eea5ec5232c115b", size = 915627275, upload-time = "2026-03-11T14:16:11.325Z" }, + { url = "https://files.pythonhosted.org/packages/d8/f0/72bf18847f58f877a6a8acf60614b14935e2f156d942483af1ffc081aea0/torch-2.10.0-3-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:46b3574d93a2a8134b3f5475cfb98e2eb46771794c57015f6ad1fb795ec25e49", size = 915523474, upload-time = "2026-03-11T14:17:44.422Z" }, + { url = "https://files.pythonhosted.org/packages/f4/39/590742415c3030551944edc2ddc273ea1fdfe8ffb2780992e824f1ebee98/torch-2.10.0-3-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:b1d5e2aba4eb7f8e87fbe04f86442887f9167a35f092afe4c237dfcaaef6e328", size = 915632474, upload-time = "2026-03-11T14:15:13.666Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8e/34949484f764dde5b222b7fe3fede43e4a6f0da9d7f8c370bb617d629ee2/torch-2.10.0-3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:0228d20b06701c05a8f978357f657817a4a63984b0c90745def81c18aedfa591", size = 915523882, upload-time = "2026-03-11T14:14:46.311Z" }, { url = "https://files.pythonhosted.org/packages/0c/1a/c61f36cfd446170ec27b3a4984f072fd06dab6b5d7ce27e11adb35d6c838/torch-2.10.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5276fa790a666ee8becaffff8acb711922252521b28fbce5db7db5cf9cb2026d", size = 145992962, upload-time = "2026-01-21T16:24:14.04Z" }, { url = "https://files.pythonhosted.org/packages/b5/60/6662535354191e2d1555296045b63e4279e5a9dbad49acf55a5d38655a39/torch-2.10.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:aaf663927bcd490ae971469a624c322202a2a1e68936eb952535ca4cd3b90444", size = 915599237, upload-time = "2026-01-21T16:23:25.497Z" }, { url = "https://files.pythonhosted.org/packages/40/b8/66bbe96f0d79be2b5c697b2e0b187ed792a15c6c4b8904613454651db848/torch-2.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:a4be6a2a190b32ff5c8002a0977a25ea60e64f7ba46b1be37093c141d9c49aeb", size = 113720931, upload-time = "2026-01-21T16:24:23.743Z" }, @@ -3231,7 +3254,7 @@ wheels = [ [[package]] name = "virtualenv" -version = "21.1.0" +version = "21.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, @@ -3240,9 +3263,9 @@ dependencies = [ { name = "python-discovery" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2f/c9/18d4b36606d6091844daa3bd93cf7dc78e6f5da21d9f21d06c221104b684/virtualenv-21.1.0.tar.gz", hash = "sha256:1990a0188c8f16b6b9cf65c9183049007375b26aad415514d377ccacf1e4fb44", size = 5840471, upload-time = "2026-02-27T08:49:29.702Z" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/92/58199fe10049f9703c2666e809c4f686c54ef0a68b0f6afccf518c0b1eb9/virtualenv-21.2.0.tar.gz", hash = "sha256:1720dc3a62ef5b443092e3f499228599045d7fea4c79199770499df8becf9098", size = 5840618, upload-time = "2026-03-09T17:24:38.013Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/55/896b06bf93a49bec0f4ae2a6f1ed12bd05c8860744ac3a70eda041064e4d/virtualenv-21.1.0-py3-none-any.whl", hash = "sha256:164f5e14c5587d170cf98e60378eb91ea35bf037be313811905d3a24ea33cc07", size = 5825072, upload-time = "2026-02-27T08:49:27.516Z" }, + { url = "https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl", hash = "sha256:1bd755b504931164a5a496d217c014d098426cddc79363ad66ac78125f9d908f", size = 5825084, upload-time = "2026-03-09T17:24:35.378Z" }, ] [[package]] From db8d402175f56fbca782e5ba07a7401631140721 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Sun, 15 Mar 2026 18:39:11 +0100 Subject: [PATCH 18/29] :zap: Fix potential performance issue in `to_edge_list` for large networks --- src/aigverse/networks/edge_list.hpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/aigverse/networks/edge_list.hpp b/src/aigverse/networks/edge_list.hpp index 9f8e967c..fc33e397 100644 --- a/src/aigverse/networks/edge_list.hpp +++ b/src/aigverse/networks/edge_list.hpp @@ -129,12 +129,33 @@ struct edge_list */ std::vector> edges{}; }; +/** + * @brief Converts a network to an edge list. + * + * @tparam Ntk Network type. + * @param ntk Network. + * @param regular_weight Weight assigned to non-inverted edges. + * @param inverted_weight Weight assigned to inverted edges. + * @return Edge list representation of the network. + */ template [[nodiscard]] edge_list to_edge_list(const Ntk& ntk, const int64_t regular_weight = 0, const int64_t inverted_weight = 1) noexcept { auto el = edge_list(ntk); + // estimate edge count for initial vector reservation + std::size_t edge_count = + (static_cast(Ntk::max_fanin_size) * static_cast(ntk.num_gates())) + + static_cast(ntk.num_pos()); + + if constexpr (mockturtle::has_foreach_ri_v && mockturtle::has_ri_to_ro_v) + { + edge_count += static_cast(ntk.num_registers()); + } + + el.edges.reserve(edge_count); + // constants, primary inputs, and regular nodes ntk.foreach_node( [&ntk, regular_weight, inverted_weight, &el](const auto& n) From 7e392e0efd376ac23e7f974488618f2cb85c8856 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Sun, 15 Mar 2026 18:51:16 +0100 Subject: [PATCH 19/29] :zap: Fix potential performance issue in `to_graph_tensor` for large networks --- src/aigverse/networks/graph_tensors.hpp | 108 ++++++++++++++++-------- 1 file changed, 75 insertions(+), 33 deletions(-) diff --git a/src/aigverse/networks/graph_tensors.hpp b/src/aigverse/networks/graph_tensors.hpp index 1ca6970c..c2651828 100644 --- a/src/aigverse/networks/graph_tensors.hpp +++ b/src/aigverse/networks/graph_tensors.hpp @@ -1,6 +1,5 @@ #pragma once -#include "aigverse/networks/edge_list.hpp" #include "aigverse/types.hpp" #include @@ -73,8 +72,8 @@ namespace detail * @return NumPy-backed ndarray that owns the provided data. */ template -nanobind::ndarray make_owned_ndarray(std::vector&& data, - const std::initializer_list& shape) +nanobind::ndarray make_owned_ndarray(std::vector&& data, + const std::initializer_list& shape) { namespace nb = nanobind; @@ -116,14 +115,14 @@ nanobind::ndarray make_owned_ndarray(std::vector&& */ template nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_encoding, - const edge_tensor_encoding edge_encoding, const bool include_level, - const bool include_fanout, const bool include_truth_table) + const edge_tensor_encoding edge_encoding, const bool include_level = false, + const bool include_fanout = false, const bool include_truth_table = false) { namespace nb = nanobind; // Number of node-type categories used for one-hot encodings: // [constant, primary input, internal gate, primary output]. - constexpr size_t node_type_one_hot_dim = 4; + constexpr std::size_t node_type_one_hot_dim = 4; // Canonical integer labels for node types; shared across all node encodings. constexpr int64_t type_constant = 0; @@ -131,46 +130,89 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ constexpr int64_t type_gate = 2; constexpr int64_t type_po = 3; - // Flatten the network into an edge list to derive COO edge indices. - const auto edges = aigverse::to_edge_list(ntk).edges; - const auto edge_count = edges.size(); + // Calculate the number of edges in the network to preallocate the output buffer + // NOTE: this will break for mixed-fanin nodes but is perfectly fine for AIGs + std::size_t edge_count = + (static_cast(Ntk::max_fanin_size) * static_cast(ntk.num_gates())) + + static_cast(ntk.num_pos()); + if constexpr (mockturtle::has_foreach_ri_v && mockturtle::has_ri_to_ro_v) + { + edge_count += static_cast(ntk.num_registers()); + } // Preallocate the 2×E edge_index tensor in column-major (source/target) form. std::vector edge_index(2 * edge_count, 0); - for (size_t i = 0; i < edge_count; ++i) - { - edge_index[i] = static_cast(edges[i].source); - edge_index[edge_count + i] = static_cast(edges[i].target); - } - const size_t edge_dim = edge_encoding == edge_tensor_encoding::ONE_HOT ? 2 : 1; + const std::size_t edge_dim = edge_encoding == edge_tensor_encoding::ONE_HOT ? 2 : 1; std::vector edge_attr(edge_count * edge_dim, 0.0f); - for (size_t i = 0; i < edge_count; ++i) + + std::size_t edge_cursor = 0; + const auto append_edge = [&](const int64_t source, const int64_t target, const bool inverted) { - const auto inverted = edges[i].weight != 0; + edge_index[edge_cursor] = source; + edge_index[edge_count + edge_cursor] = target; + switch (edge_encoding) { case edge_tensor_encoding::BINARY: { - edge_attr[i] = inverted ? 1.0f : 0.0f; + edge_attr[edge_cursor] = inverted ? 1.0f : 0.0f; break; } case edge_tensor_encoding::SIGNED: { - edge_attr[i] = inverted ? -1.0f : 1.0f; + edge_attr[edge_cursor] = inverted ? -1.0f : 1.0f; break; } case edge_tensor_encoding::ONE_HOT: { - edge_attr[i * 2] = inverted ? 0.0f : 1.0f; - edge_attr[(i * 2) + 1] = inverted ? 1.0f : 0.0f; + edge_attr[edge_cursor * 2] = inverted ? 0.0f : 1.0f; + edge_attr[(edge_cursor * 2) + 1] = inverted ? 1.0f : 0.0f; break; } } + + ++edge_cursor; + }; + + ntk.foreach_node( + [&](const auto& n) + { + const auto target = static_cast(ntk.node_to_index(n)); + ntk.foreach_fanin(n, + [&](const auto& f) + { + const auto source = static_cast(ntk.node_to_index(ntk.get_node(f))); + append_edge(source, target, ntk.is_complemented(f)); + }); + }); + + ntk.foreach_po( + [&](const auto& po) + { + const auto source = static_cast(ntk.node_to_index(ntk.get_node(po))); + const auto target = static_cast(ntk.size() + ntk.po_index(po)); + append_edge(source, target, ntk.is_complemented(po)); + }); + + if constexpr (mockturtle::has_foreach_ri_v && mockturtle::has_ri_to_ro_v) + { + ntk.foreach_ri( + [&](const auto& ri) + { + const auto source = static_cast(ntk.node_to_index(ntk.get_node(ri))); + const auto target = static_cast(ntk.node_to_index(ntk.ri_to_ro(ri))); + append_edge(source, target, ntk.is_complemented(ri)); + }); + } + + if (edge_cursor != edge_count) + { + throw std::runtime_error("inconsistent edge count during graph tensor export"); } - size_t tt_dim = 0; + std::size_t tt_dim = 0; std::optional> node_tts{}; std::vector output_tts{}; @@ -192,10 +234,10 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ } } - const size_t base_dim = node_encoding == node_tensor_encoding::ONE_HOT ? node_type_one_hot_dim : 1; - const size_t node_dim = + const std::size_t base_dim = node_encoding == node_tensor_encoding::ONE_HOT ? node_type_one_hot_dim : 1; + const std::size_t node_dim = base_dim + (include_level ? 1 : 0) + (include_fanout ? 1 : 0) + (include_truth_table ? tt_dim : 0); - const size_t node_count = ntk.size() + ntk.num_pos(); + const std::size_t node_count = ntk.size() + ntk.num_pos(); std::vector node_attr(node_count * node_dim, 0.0f); @@ -205,23 +247,23 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ depth_ntk.emplace(ntk); } - const auto fill_base = [&](const size_t row, const int64_t type_index) -> size_t + const auto fill_base = [&](const std::size_t row, const int64_t type_index) -> std::size_t { - const size_t offset = row * node_dim; + const std::size_t offset = row * node_dim; if (node_encoding == node_tensor_encoding::INTEGER) { node_attr[offset] = static_cast(type_index); return offset + 1; } - node_attr[offset + static_cast(type_index)] = 1.0f; + node_attr[offset + static_cast(type_index)] = 1.0f; return offset + node_type_one_hot_dim; }; ntk.foreach_node( [&](const auto& n) { - const auto row = static_cast(ntk.node_to_index(n)); + const auto row = static_cast(ntk.node_to_index(n)); int64_t type_index = type_gate; if (ntk.is_constant(n)) { @@ -245,7 +287,7 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ if (include_truth_table) { const auto& tt = node_tts.value()[n]; - for (size_t i = 0; i < tt_dim; ++i) + for (std::size_t i = 0; i < tt_dim; ++i) { node_attr[feature_offset + i] = kitty::get_bit(tt, static_cast(i)) ? 1.0f : 0.0f; } @@ -255,8 +297,8 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ ntk.foreach_po( [&](const auto& po) { - const auto po_idx = static_cast(ntk.po_index(po)); - const auto row = static_cast(ntk.size()) + po_idx; + const auto po_idx = static_cast(ntk.po_index(po)); + const auto row = static_cast(ntk.size()) + po_idx; auto feature_offset = fill_base(row, type_po); @@ -271,7 +313,7 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ if (include_truth_table) { const auto& tt = output_tts[po_idx]; - for (size_t i = 0; i < tt_dim; ++i) + for (std::size_t i = 0; i < tt_dim; ++i) { node_attr[feature_offset + i] = kitty::get_bit(tt, i) ? 1.0f : 0.0f; } From 6d2c372937cef1c0e2b8f7de9ffeaee1ac97a100 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Thu, 2 Apr 2026 14:03:02 +0200 Subject: [PATCH 20/29] :art: Streamline function parameter names in accordance with the `to_networkx` method --- python/aigverse/networks.pyi | 18 +++++----- src/aigverse/networks/graph_tensors.hpp | 45 ++++++++++++------------ src/aigverse/networks/logic_networks.cpp | 21 ++++++----- test/networks/test_graph_tensors.py | 32 ++++++++--------- 4 files changed, 57 insertions(+), 59 deletions(-) diff --git a/python/aigverse/networks.pyi b/python/aigverse/networks.pyi index e08f31f6..c4fd3d50 100644 --- a/python/aigverse/networks.pyi +++ b/python/aigverse/networks.pyi @@ -314,9 +314,9 @@ class Aig: self, node_encoding: NodeTensorEncoding = ..., edge_encoding: EdgeTensorEncoding = ..., - include_level: bool = True, - include_fanout: bool = False, - include_truth_table: bool = False, + levels: bool = True, + fanouts: bool = False, + node_tts: bool = False, ) -> dict: """Exports graph tensors for machine-learning workflows. @@ -334,9 +334,9 @@ class Aig: Args: node_encoding: Node encoding mode as :class:`~aigverse.networks.NodeTensorEncoding`. edge_encoding: Edge encoding mode as :class:`~aigverse.networks.EdgeTensorEncoding`. - include_level: Appends logic level as a node feature. - include_fanout: Appends fanout size as a node feature. - include_truth_table: Appends simulated node/output truth-table bits. + levels: Appends logic level as a node feature. + fanouts: Appends fanout size as a node feature. + node_tts: Appends simulated node/output truth-table bits. Returns: A dictionary with ``edge_index`` (shape ``(2, E)``, dtype ``int64``), @@ -706,9 +706,9 @@ class SequentialAig(Aig): self, node_encoding: NodeTensorEncoding = ..., edge_encoding: EdgeTensorEncoding = ..., - include_level: bool = True, - include_fanout: bool = False, - include_truth_table: bool = False, + levels: bool = True, + fanouts: bool = False, + node_tts: bool = False, ) -> NoReturn: """Sequential networks cannot be exported as combinational graph tensors.""" diff --git a/src/aigverse/networks/graph_tensors.hpp b/src/aigverse/networks/graph_tensors.hpp index c2651828..b6d62505 100644 --- a/src/aigverse/networks/graph_tensors.hpp +++ b/src/aigverse/networks/graph_tensors.hpp @@ -108,15 +108,15 @@ nanobind::ndarray make_owned_ndarray(std::vector&& * @param ntk Input network. * @param node_encoding Node-type encoding mode. * @param edge_encoding Edge-type encoding mode. - * @param include_level Whether to append depth-based level features. - * @param include_fanout Whether to append fanout-size features. - * @param include_truth_table Whether to append truth-table bits. + * @param levels Whether to append depth-based level features. + * @param fanouts Whether to append fanout-size features. + * @param node_tts Whether to append node truth-table bits. * @return Dictionary of exported tensors. */ template nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_encoding, - const edge_tensor_encoding edge_encoding, const bool include_level = false, - const bool include_fanout = false, const bool include_truth_table = false) + const edge_tensor_encoding edge_encoding, const bool levels = false, + const bool fanouts = false, const bool node_tts = false) { namespace nb = nanobind; @@ -214,35 +214,34 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ std::size_t tt_dim = 0; - std::optional> node_tts{}; - std::vector output_tts{}; - if (include_truth_table) + std::optional> node_truth_tables{}; + std::vector output_truth_tables{}; + if (node_tts) { if (ntk.num_pis() > 16) { throw std::invalid_argument("truth-table export is only supported up to 16 primary inputs"); } - node_tts = mockturtle::simulate_nodes( + node_truth_tables = mockturtle::simulate_nodes( ntk, mockturtle::default_simulator{static_cast(ntk.num_pis())}); - output_tts = mockturtle::simulate( + output_truth_tables = mockturtle::simulate( ntk, mockturtle::default_simulator{static_cast(ntk.num_pis())}); if (ntk.size() > 0) { - tt_dim = node_tts.value()[ntk.get_constant(false)].num_bits(); + tt_dim = node_truth_tables.value()[ntk.get_constant(false)].num_bits(); } } - const std::size_t base_dim = node_encoding == node_tensor_encoding::ONE_HOT ? node_type_one_hot_dim : 1; - const std::size_t node_dim = - base_dim + (include_level ? 1 : 0) + (include_fanout ? 1 : 0) + (include_truth_table ? tt_dim : 0); + const std::size_t base_dim = node_encoding == node_tensor_encoding::ONE_HOT ? node_type_one_hot_dim : 1; + const std::size_t node_dim = base_dim + (levels ? 1 : 0) + (fanouts ? 1 : 0) + (node_tts ? tt_dim : 0); const std::size_t node_count = ntk.size() + ntk.num_pos(); std::vector node_attr(node_count * node_dim, 0.0f); std::optional> depth_ntk{}; - if (include_level) + if (levels) { depth_ntk.emplace(ntk); } @@ -276,17 +275,17 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ auto feature_offset = fill_base(row, type_index); - if (include_level) + if (levels) { node_attr[feature_offset++] = static_cast(depth_ntk->level(n)); } - if (include_fanout) + if (fanouts) { node_attr[feature_offset++] = static_cast(ntk.fanout_size(n)); } - if (include_truth_table) + if (node_tts) { - const auto& tt = node_tts.value()[n]; + const auto& tt = node_truth_tables.value()[n]; for (std::size_t i = 0; i < tt_dim; ++i) { node_attr[feature_offset + i] = kitty::get_bit(tt, static_cast(i)) ? 1.0f : 0.0f; @@ -302,17 +301,17 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ auto feature_offset = fill_base(row, type_po); - if (include_level) + if (levels) { node_attr[feature_offset++] = static_cast(depth_ntk->level(ntk.get_node(po)) + 1); } - if (include_fanout) + if (fanouts) { node_attr[feature_offset++] = 0.0f; } - if (include_truth_table) + if (node_tts) { - const auto& tt = output_tts[po_idx]; + const auto& tt = output_truth_tables[po_idx]; for (std::size_t i = 0; i < tt_dim; ++i) { node_attr[feature_offset + i] = kitty::get_bit(tt, i) ? 1.0f : 0.0f; diff --git a/src/aigverse/networks/logic_networks.cpp b/src/aigverse/networks/logic_networks.cpp index 153adeac..dacf7cd7 100644 --- a/src/aigverse/networks/logic_networks.cpp +++ b/src/aigverse/networks/logic_networks.cpp @@ -388,15 +388,14 @@ void bind_network(nanobind::module_& m, const std::string& network_name) // NOL .def( "to_graph_tensors", [](const Ntk& ntk, const aigverse::node_tensor_encoding node_encoding, - const aigverse::edge_tensor_encoding edge_encoding, const bool include_level, const bool include_fanout, - const bool include_truth_table) + const aigverse::edge_tensor_encoding edge_encoding, const bool levels, const bool fanouts, + const bool node_tts) { - return aigverse::detail::to_graph_tensors(ntk, node_encoding, edge_encoding, include_level, - include_fanout, include_truth_table); + return aigverse::detail::to_graph_tensors(ntk, node_encoding, edge_encoding, levels, fanouts, node_tts); }, nb::arg("node_encoding") = aigverse::node_tensor_encoding::INTEGER, - nb::arg("edge_encoding") = aigverse::edge_tensor_encoding::BINARY, nb::arg("include_level") = true, - nb::arg("include_fanout") = false, nb::arg("include_truth_table") = false, + nb::arg("edge_encoding") = aigverse::edge_tensor_encoding::BINARY, nb::arg("levels") = true, + nb::arg("fanouts") = false, nb::arg("node_tts") = false, R"pb(Exports graph tensors for machine-learning workflows. Returns sparse graph topology and features as DLPack-compatible arrays. @@ -413,9 +412,9 @@ Node encoding mapping: Args: node_encoding: Node encoding mode as :class:`~aigverse.networks.NodeTensorEncoding`. edge_encoding: Edge encoding mode as :class:`~aigverse.networks.EdgeTensorEncoding`. - include_level: Appends logic level as a node feature. - include_fanout: Appends fanout size as a node feature. - include_truth_table: Appends simulated node/output truth-table bits. + levels: Appends logic level as a node feature. + fanouts: Appends fanout size as a node feature. + node_tts: Appends simulated node/output truth-table bits. Returns: A dictionary with ``edge_index`` (shape ``(2, E)``, dtype ``int64``), @@ -672,8 +671,8 @@ Preserves only combinational structure and does not capture augmented view metad throw nb::type_error(message.c_str()); }, nb::arg("node_encoding") = aigverse::node_tensor_encoding::INTEGER, - nb::arg("edge_encoding") = aigverse::edge_tensor_encoding::BINARY, nb::arg("include_level") = true, - nb::arg("include_fanout") = false, nb::arg("include_truth_table") = false, + nb::arg("edge_encoding") = aigverse::edge_tensor_encoding::BINARY, nb::arg("levels") = true, + nb::arg("fanouts") = false, nb::arg("node_tts") = false, R"pb(Sequential networks cannot be exported as combinational graph tensors.)pb") .def( "__getstate__", diff --git a/test/networks/test_graph_tensors.py b/test/networks/test_graph_tensors.py index f73b8822..c261a0c0 100644 --- a/test/networks/test_graph_tensors.py +++ b/test/networks/test_graph_tensors.py @@ -86,7 +86,7 @@ def test_to_graph_tensors_one_hot_dimensions(sample_aig: Aig) -> None: tensors = sample_aig.to_graph_tensors( node_encoding=NodeTensorEncoding.ONE_HOT, edge_encoding=EdgeTensorEncoding.ONE_HOT, - include_level=False, + levels=False, ) edge_attr = tensors["edge_attr"] @@ -101,7 +101,7 @@ def test_to_graph_tensors_signed_edge_encoding_values(sample_aig: Aig) -> None: tensors = sample_aig.to_graph_tensors( node_encoding=NodeTensorEncoding.INTEGER, edge_encoding=EdgeTensorEncoding.SIGNED, - include_level=False, + levels=False, ) edge_attr = np.from_dlpack(tensors["edge_attr"]) @@ -115,9 +115,9 @@ def test_to_graph_tensors_truth_table_feature_dim(sample_aig: Aig) -> None: tensors = sample_aig.to_graph_tensors( node_encoding=NodeTensorEncoding.ONE_HOT, edge_encoding=EdgeTensorEncoding.BINARY, - include_level=False, - include_fanout=True, - include_truth_table=True, + levels=False, + fanouts=True, + node_tts=True, ) node_attr = tensors["node_attr"] @@ -197,9 +197,9 @@ def test_to_graph_tensors_large_aig_shapes_and_dtypes(large_aig: Aig) -> None: tensors = large_aig.to_graph_tensors( node_encoding=NodeTensorEncoding.ONE_HOT, edge_encoding=EdgeTensorEncoding.SIGNED, - include_level=True, - include_fanout=True, - include_truth_table=False, + levels=True, + fanouts=True, + node_tts=False, ) edge_index = np.from_dlpack(tensors["edge_index"]) @@ -222,9 +222,9 @@ def test_to_graph_tensors_empty_aig_edge_case() -> None: tensors = empty_aig.to_graph_tensors( node_encoding=NodeTensorEncoding.INTEGER, edge_encoding=EdgeTensorEncoding.ONE_HOT, - include_level=False, - include_fanout=False, - include_truth_table=False, + levels=False, + fanouts=False, + node_tts=False, ) edge_index = np.from_dlpack(tensors["edge_index"]) @@ -241,13 +241,13 @@ def test_to_graph_tensors_empty_aig_edge_case() -> None: def test_to_graph_tensors_truth_table_pi_limit_raises() -> None: - """Checks include_truth_table fails fast for too many primary inputs.""" + """Checks node_tts fails fast for too many primary inputs.""" aig = Aig() pis = [aig.create_pi() for _ in range(17)] aig.create_po(pis[0]) with pytest.raises(ValueError, match="only supported up to 16 primary inputs"): - aig.to_graph_tensors(include_truth_table=True) + aig.to_graph_tensors(node_tts=True) def test_to_graph_tensors_po_levels_follow_driver_depth() -> None: @@ -262,9 +262,9 @@ def test_to_graph_tensors_po_levels_follow_driver_depth() -> None: tensors = aig.to_graph_tensors( node_encoding=NodeTensorEncoding.INTEGER, edge_encoding=EdgeTensorEncoding.BINARY, - include_level=True, - include_fanout=False, - include_truth_table=False, + levels=True, + fanouts=False, + node_tts=False, ) node_attr = np.from_dlpack(tensors["node_attr"]) From f1ac0f4f3520cfb0740ec1b4b3e274fc73336219 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Thu, 2 Apr 2026 15:34:28 +0200 Subject: [PATCH 21/29] :art: Streamline function parameter names in accordance with the `to_networkx` method --- python/aigverse/networks.pyi | 2 ++ src/aigverse/networks/logic_networks.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/python/aigverse/networks.pyi b/python/aigverse/networks.pyi index c4fd3d50..fd601f62 100644 --- a/python/aigverse/networks.pyi +++ b/python/aigverse/networks.pyi @@ -314,6 +314,7 @@ class Aig: self, node_encoding: NodeTensorEncoding = ..., edge_encoding: EdgeTensorEncoding = ..., + *, levels: bool = True, fanouts: bool = False, node_tts: bool = False, @@ -706,6 +707,7 @@ class SequentialAig(Aig): self, node_encoding: NodeTensorEncoding = ..., edge_encoding: EdgeTensorEncoding = ..., + *, levels: bool = True, fanouts: bool = False, node_tts: bool = False, diff --git a/src/aigverse/networks/logic_networks.cpp b/src/aigverse/networks/logic_networks.cpp index dacf7cd7..05acbb54 100644 --- a/src/aigverse/networks/logic_networks.cpp +++ b/src/aigverse/networks/logic_networks.cpp @@ -394,7 +394,7 @@ void bind_network(nanobind::module_& m, const std::string& network_name) // NOL return aigverse::detail::to_graph_tensors(ntk, node_encoding, edge_encoding, levels, fanouts, node_tts); }, nb::arg("node_encoding") = aigverse::node_tensor_encoding::INTEGER, - nb::arg("edge_encoding") = aigverse::edge_tensor_encoding::BINARY, nb::arg("levels") = true, + nb::arg("edge_encoding") = aigverse::edge_tensor_encoding::BINARY, nb::kw_only(), nb::arg("levels") = true, nb::arg("fanouts") = false, nb::arg("node_tts") = false, R"pb(Exports graph tensors for machine-learning workflows. @@ -671,7 +671,7 @@ Preserves only combinational structure and does not capture augmented view metad throw nb::type_error(message.c_str()); }, nb::arg("node_encoding") = aigverse::node_tensor_encoding::INTEGER, - nb::arg("edge_encoding") = aigverse::edge_tensor_encoding::BINARY, nb::arg("levels") = true, + nb::arg("edge_encoding") = aigverse::edge_tensor_encoding::BINARY, nb::kw_only(), nb::arg("levels") = true, nb::arg("fanouts") = false, nb::arg("node_tts") = false, R"pb(Sequential networks cannot be exported as combinational graph tensors.)pb") .def( From 671aa2480166b8ca9ed73a8044161203d23ffc4c Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 13 May 2026 11:54:15 +0200 Subject: [PATCH 22/29] :zap: avoid repeated graph tensor index lookups in export loops --- src/aigverse/networks/graph_tensors.hpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/aigverse/networks/graph_tensors.hpp b/src/aigverse/networks/graph_tensors.hpp index b6d62505..188f3913 100644 --- a/src/aigverse/networks/graph_tensors.hpp +++ b/src/aigverse/networks/graph_tensors.hpp @@ -176,10 +176,11 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ ++edge_cursor; }; + int64_t edge_target = 0; ntk.foreach_node( [&](const auto& n) { - const auto target = static_cast(ntk.node_to_index(n)); + const auto target = edge_target++; ntk.foreach_fanin(n, [&](const auto& f) { @@ -259,10 +260,11 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ return offset + node_type_one_hot_dim; }; + std::size_t node_row = 0; ntk.foreach_node( [&](const auto& n) { - const auto row = static_cast(ntk.node_to_index(n)); + const auto row = node_row++; int64_t type_index = type_gate; if (ntk.is_constant(n)) { @@ -293,10 +295,11 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ } }); + std::size_t po_row = 0; ntk.foreach_po( [&](const auto& po) { - const auto po_idx = static_cast(ntk.po_index(po)); + const auto po_idx = po_row++; const auto row = static_cast(ntk.size()) + po_idx; auto feature_offset = fill_base(row, type_po); From 0bc17445823594c86e482b936ed83a6e668d36ec Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 13 May 2026 12:02:54 +0200 Subject: [PATCH 23/29] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Avoid=20PO=20target?= =?UTF-8?q?=20lookup=20in=20graph=20tensor=20exports?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/aigverse/networks/graph_tensors.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/aigverse/networks/graph_tensors.hpp b/src/aigverse/networks/graph_tensors.hpp index 188f3913..df8d184a 100644 --- a/src/aigverse/networks/graph_tensors.hpp +++ b/src/aigverse/networks/graph_tensors.hpp @@ -189,11 +189,12 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ }); }); + int64_t po_target = static_cast(ntk.size()); ntk.foreach_po( [&](const auto& po) { const auto source = static_cast(ntk.node_to_index(ntk.get_node(po))); - const auto target = static_cast(ntk.size() + ntk.po_index(po)); + const auto target = po_target++; append_edge(source, target, ntk.is_complemented(po)); }); From c6e6a30ddf297210445eacca8a06042bea6fd7dd Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 13 May 2026 12:18:04 +0200 Subject: [PATCH 24/29] :zap: avoid zero-initializing fully overwritten export buffers --- src/aigverse/networks/graph_tensors.hpp | 74 +++++++++++++++++-------- 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/src/aigverse/networks/graph_tensors.hpp b/src/aigverse/networks/graph_tensors.hpp index df8d184a..8bf1d3f2 100644 --- a/src/aigverse/networks/graph_tensors.hpp +++ b/src/aigverse/networks/graph_tensors.hpp @@ -93,6 +93,22 @@ nanobind::ndarray make_owned_ndarray(std::vector&& return nb::ndarray(raw_storage->data(), shape, owner); } + +template +nanobind::ndarray make_owned_ndarray(std::unique_ptr&& data, + const std::initializer_list& shape) +{ + namespace nb = nanobind; + + auto* raw_storage = data.release(); + nb::capsule owner(raw_storage, + [](void* ptr) noexcept + { + delete[] static_cast(ptr); // NOLINT(cppcoreguidelines-owning-memory) + }); + + return nb::ndarray(raw_storage, shape, owner); +} /** * @brief Exports an AIG-style network to sparse COO-like graph tensors. * @@ -140,35 +156,40 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ edge_count += static_cast(ntk.num_registers()); } - // Preallocate the 2×E edge_index tensor in column-major (source/target) form. - std::vector edge_index(2 * edge_count, 0); + // edge_index and edge_attr are fully overwritten during export, so avoid + // paying for zero-initialization up front. + std::unique_ptr edge_index{new int64_t[2 * edge_count]}; const std::size_t edge_dim = edge_encoding == edge_tensor_encoding::ONE_HOT ? 2 : 1; - std::vector edge_attr(edge_count * edge_dim, 0.0f); + std::unique_ptr edge_attr{new float[edge_count * edge_dim]}; + + auto* edge_sources = edge_index.get(); + auto* edge_targets = edge_sources + edge_count; + auto* edge_values = edge_attr.get(); std::size_t edge_cursor = 0; const auto append_edge = [&](const int64_t source, const int64_t target, const bool inverted) { - edge_index[edge_cursor] = source; - edge_index[edge_count + edge_cursor] = target; + edge_sources[edge_cursor] = source; + edge_targets[edge_cursor] = target; switch (edge_encoding) { case edge_tensor_encoding::BINARY: { - edge_attr[edge_cursor] = inverted ? 1.0f : 0.0f; + edge_values[edge_cursor] = inverted ? 1.0f : 0.0f; break; } case edge_tensor_encoding::SIGNED: { - edge_attr[edge_cursor] = inverted ? -1.0f : 1.0f; + edge_values[edge_cursor] = inverted ? -1.0f : 1.0f; break; } case edge_tensor_encoding::ONE_HOT: { - edge_attr[edge_cursor * 2] = inverted ? 0.0f : 1.0f; - edge_attr[(edge_cursor * 2) + 1] = inverted ? 1.0f : 0.0f; + edge_values[edge_cursor * 2] = inverted ? 0.0f : 1.0f; + edge_values[(edge_cursor * 2) + 1] = inverted ? 1.0f : 0.0f; break; } } @@ -240,7 +261,8 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ const std::size_t node_dim = base_dim + (levels ? 1 : 0) + (fanouts ? 1 : 0) + (node_tts ? tt_dim : 0); const std::size_t node_count = ntk.size() + ntk.num_pos(); - std::vector node_attr(node_count * node_dim, 0.0f); + std::unique_ptr node_attr{new float[node_count * node_dim]}; + auto* node_values = node_attr.get(); std::optional> depth_ntk{}; if (levels) @@ -248,17 +270,21 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ depth_ntk.emplace(ntk); } - const auto fill_base = [&](const std::size_t row, const int64_t type_index) -> std::size_t + const auto fill_base = [&](const std::size_t row, const int64_t type_index) -> float* { - const std::size_t offset = row * node_dim; + auto* row_data = node_values + (row * node_dim); if (node_encoding == node_tensor_encoding::INTEGER) { - node_attr[offset] = static_cast(type_index); - return offset + 1; + row_data[0] = static_cast(type_index); + return row_data + 1; } - node_attr[offset + static_cast(type_index)] = 1.0f; - return offset + node_type_one_hot_dim; + row_data[0] = 0.0f; + row_data[1] = 0.0f; + row_data[2] = 0.0f; + row_data[3] = 0.0f; + row_data[static_cast(type_index)] = 1.0f; + return row_data + node_type_one_hot_dim; }; std::size_t node_row = 0; @@ -276,22 +302,22 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ type_index = type_pi; } - auto feature_offset = fill_base(row, type_index); + auto* feature_offset = fill_base(row, type_index); if (levels) { - node_attr[feature_offset++] = static_cast(depth_ntk->level(n)); + *feature_offset++ = static_cast(depth_ntk->level(n)); } if (fanouts) { - node_attr[feature_offset++] = static_cast(ntk.fanout_size(n)); + *feature_offset++ = static_cast(ntk.fanout_size(n)); } if (node_tts) { const auto& tt = node_truth_tables.value()[n]; for (std::size_t i = 0; i < tt_dim; ++i) { - node_attr[feature_offset + i] = kitty::get_bit(tt, static_cast(i)) ? 1.0f : 0.0f; + feature_offset[i] = kitty::get_bit(tt, static_cast(i)) ? 1.0f : 0.0f; } } }); @@ -303,22 +329,22 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ const auto po_idx = po_row++; const auto row = static_cast(ntk.size()) + po_idx; - auto feature_offset = fill_base(row, type_po); + auto* feature_offset = fill_base(row, type_po); if (levels) { - node_attr[feature_offset++] = static_cast(depth_ntk->level(ntk.get_node(po)) + 1); + *feature_offset++ = static_cast(depth_ntk->level(ntk.get_node(po)) + 1); } if (fanouts) { - node_attr[feature_offset++] = 0.0f; + *feature_offset++ = 0.0f; } if (node_tts) { const auto& tt = output_truth_tables[po_idx]; for (std::size_t i = 0; i < tt_dim; ++i) { - node_attr[feature_offset + i] = kitty::get_bit(tt, i) ? 1.0f : 0.0f; + feature_offset[i] = kitty::get_bit(tt, i) ? 1.0f : 0.0f; } } }); From d55911ffc785521ae93fae388e20516ce218d5f8 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 13 May 2026 12:21:40 +0200 Subject: [PATCH 25/29] :memo: document graph tensor export layout and hot-path decisions --- src/aigverse/networks/graph_tensors.hpp | 93 +++++++++++++++++++++---- 1 file changed, 78 insertions(+), 15 deletions(-) diff --git a/src/aigverse/networks/graph_tensors.hpp b/src/aigverse/networks/graph_tensors.hpp index 8bf1d3f2..f1ba4231 100644 --- a/src/aigverse/networks/graph_tensors.hpp +++ b/src/aigverse/networks/graph_tensors.hpp @@ -24,11 +24,14 @@ namespace aigverse /** * @brief Node encoding mode for exported graph tensors. * - * This enum controls how categorical node type features are represented - * in the exported tensors. The types will be `float32` for all modes, but the encoding scheme differs: - * - `INTEGER`: Node types are represented as integer class labels. - * - `ONE_HOT`: Node types are represented as one-hot encoded vectors, where the dimension corresponds to the number - * of node categories in the order `[constant, pi, gate, po]`. + * This enum controls how the categorical node-type prefix is laid out inside + * ``node_attr``. All exported node features use ``float32`` storage so the + * result can be consumed directly by NumPy/DLPack users without dtype juggling. + * + * The node-type categories always appear in the canonical order + * ``[constant, pi, gate, po]``: + * - `INTEGER`: Store that category as a single scalar label. + * - `ONE_HOT`: Store that category as a one-hot prefix of length four. */ enum class node_tensor_encoding : uint8_t { @@ -41,11 +44,14 @@ enum class node_tensor_encoding : uint8_t /** * @brief Edge encoding mode for exported graph tensors. * - * This enum controls how categorical edge type features are represented - * in the exported tensors. The types will be `float32` for all modes, but the encoding scheme differs: - * - `BINARY`: Edge polarity is represented as binary indicators (0.0 or 1.0). - * - `SIGNED`: Edge polarity is represented as +1.0 for regular and -1.0 for inverted. - * - `ONE_HOT`: Edge polarity is represented as one-hot encoded vectors in the order `[regular, inverted]`. + * This enum controls how edge polarity is laid out inside ``edge_attr``. + * As with node features, all edge features use ``float32`` storage. + * + * The polarity categories always appear in the canonical order + * ``[regular, inverted]``: + * - `BINARY`: Store polarity as ``0.0`` or ``1.0``. + * - `SIGNED`: Store polarity as ``+1.0`` or ``-1.0``. + * - `ONE_HOT`: Store polarity as a length-two one-hot vector. */ enum class edge_tensor_encoding : uint8_t { @@ -65,6 +71,8 @@ namespace detail * * The returned array keeps data alive via capsule-managed ownership of a heap * vector, making it safe to consume through DLPack in downstream frameworks. + * This overload is convenient when the exporter naturally materializes a + * ``std::vector`` first. * * @tparam T Element type. * @param data Moved data buffer. @@ -109,6 +117,7 @@ nanobind::ndarray make_owned_ndarray(std::unique_ptr&& return nb::ndarray(raw_storage, shape, owner); } + /** * @brief Exports an AIG-style network to sparse COO-like graph tensors. * @@ -117,8 +126,21 @@ nanobind::ndarray make_owned_ndarray(std::unique_ptr&& * - ``edge_attr`` with shape ``(E, D_edge)`` * - ``node_attr`` with shape ``(N, D_node)`` * + * Export order is stable and intentionally simple: + * - rows ``[0, ntk.size())`` in ``node_attr`` correspond to ``foreach_node`` + * order + * - rows ``[ntk.size(), ntk.size() + ntk.num_pos())`` correspond to synthetic + * PO rows in ``foreach_po`` order + * - columns in ``edge_index`` are emitted in the same order as the exporter + * traverses fanins, then POs, then optional register inputs + * * All returned tensors are NumPy-backed ndarrays and can be consumed by DLPack - * consumers (e.g., PyTorch via ``torch.from_dlpack``). + * consumers such as PyTorch via ``torch.from_dlpack``. + * + * The implementation is written around the export hot path: it precomputes the + * exact output sizes, fills the buffers linearly, avoids repeated PO index + * lookups by using row counters, and uses raw arrays for buffers that are fully + * overwritten to avoid paying an unnecessary zero-fill cost. * * @tparam Ntk Network type. * @param ntk Input network. @@ -131,6 +153,19 @@ nanobind::ndarray make_owned_ndarray(std::unique_ptr&& */ template nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_encoding, + + /** + * @brief Creates an owning NumPy-backed nanobind ndarray from a raw array. + * + * This overload exists for the hot export path, where the tensor storage is + * fully overwritten and a raw array avoids the default zero-initialization that + * ``std::vector(count, value)`` would perform. + * + * @tparam T Element type. + * @param data Moved raw array buffer. + * @param shape Target tensor shape. + * @return NumPy-backed ndarray that owns the provided data. + */ const edge_tensor_encoding edge_encoding, const bool levels = false, const bool fanouts = false, const bool node_tts = false) { @@ -146,8 +181,10 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ constexpr int64_t type_gate = 2; constexpr int64_t type_po = 3; - // Calculate the number of edges in the network to preallocate the output buffer - // NOTE: this will break for mixed-fanin nodes but is perfectly fine for AIGs + // Precompute the exact number of emitted edges so the export loops can fill + // the destination buffers linearly without growth checks or reallocations. + // This formula relies on AIG-style fixed fanin counts; the shared exporter + // is intentionally optimized for that dominant case. std::size_t edge_count = (static_cast(Ntk::max_fanin_size) * static_cast(ntk.num_gates())) + static_cast(ntk.num_pos()); @@ -156,8 +193,9 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ edge_count += static_cast(ntk.num_registers()); } - // edge_index and edge_attr are fully overwritten during export, so avoid - // paying for zero-initialization up front. + // edge_index and edge_attr are fully overwritten during export, so raw + // storage avoids paying for a zero-initialization pass that would be thrown + // away immediately. std::unique_ptr edge_index{new int64_t[2 * edge_count]}; const std::size_t edge_dim = edge_encoding == edge_tensor_encoding::ONE_HOT ? 2 : 1; @@ -171,9 +209,14 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ std::size_t edge_cursor = 0; const auto append_edge = [&](const int64_t source, const int64_t target, const bool inverted) { + // edge_index is stored in the conventional COO layout with one row for + // sources and one row for targets. edge_sources[edge_cursor] = source; edge_targets[edge_cursor] = target; + // The encoding branch remains here because it is shared by all export + // modes, but each branch writes directly into the final contiguous edge + // buffer without temporary objects. switch (edge_encoding) { case edge_tensor_encoding::BINARY: @@ -201,6 +244,8 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ ntk.foreach_node( [&](const auto& n) { + // Node rows are emitted in foreach_node order, so a monotonic + // counter is enough and avoids repeated index remapping work. const auto target = edge_target++; ntk.foreach_fanin(n, [&](const auto& f) @@ -214,6 +259,10 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ ntk.foreach_po( [&](const auto& po) { + // Synthetic PO rows are appended after the real network nodes. + // Using a running target counter avoids the old repeated PO index + // lookup, which was expensive on mockturtle AIGs because it scanned + // the outputs linearly. const auto source = static_cast(ntk.node_to_index(ntk.get_node(po))); const auto target = po_target++; append_edge(source, target, ntk.is_complemented(po)); @@ -237,6 +286,8 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ std::size_t tt_dim = 0; + // Truth-table simulation is optional because it is by far the most + // expensive feature family when enabled. std::optional> node_truth_tables{}; std::vector output_truth_tables{}; if (node_tts) @@ -261,12 +312,15 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ const std::size_t node_dim = base_dim + (levels ? 1 : 0) + (fanouts ? 1 : 0) + (node_tts ? tt_dim : 0); const std::size_t node_count = ntk.size() + ntk.num_pos(); + // node_attr is also fully overwritten row-by-row, so it gets the same raw + // storage treatment as the edge buffers. std::unique_ptr node_attr{new float[node_count * node_dim]}; auto* node_values = node_attr.get(); std::optional> depth_ntk{}; if (levels) { + // Construct the depth view only when level features are requested. depth_ntk.emplace(ntk); } @@ -279,6 +333,9 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ return row_data + 1; } + // Only the one-hot prefix needs clearing here. The trailing optional + // features are written unconditionally by the code paths that enabled + // them, so clearing the full row would just add extra memory traffic. row_data[0] = 0.0f; row_data[1] = 0.0f; row_data[2] = 0.0f; @@ -291,6 +348,8 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ ntk.foreach_node( [&](const auto& n) { + // Node rows follow foreach_node order, so a linear cursor is enough + // and matches the row numbers used by the edge exporter above. const auto row = node_row++; int64_t type_index = type_gate; if (ntk.is_constant(n)) @@ -326,6 +385,8 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ ntk.foreach_po( [&](const auto& po) { + // Synthetic PO feature rows are appended after real nodes in the + // same order used for PO edges. const auto po_idx = po_row++; const auto row = static_cast(ntk.size()) + po_idx; @@ -351,6 +412,8 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ auto result = nb::dict(); + // Hand off ownership to nanobind capsules so downstream DLPack consumers + // can borrow the buffers without an extra copy. result["edge_index"] = make_owned_ndarray(std::move(edge_index), {2, edge_count}); result["edge_attr"] = make_owned_ndarray(std::move(edge_attr), {edge_count, edge_dim}); result["node_attr"] = make_owned_ndarray(std::move(node_attr), {node_count, node_dim}); From a79d2d92a9340a7e224501c7f11029a1843dc0bb Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 13 May 2026 12:32:38 +0200 Subject: [PATCH 26/29] :zap: speed up full-feature graph tensor truth-table export --- src/aigverse/networks/graph_tensors.hpp | 52 +++++++++++++++------- test/networks/test_graph_tensors.py | 59 +++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 15 deletions(-) diff --git a/src/aigverse/networks/graph_tensors.hpp b/src/aigverse/networks/graph_tensors.hpp index f1ba4231..9528329c 100644 --- a/src/aigverse/networks/graph_tensors.hpp +++ b/src/aigverse/networks/graph_tensors.hpp @@ -2,7 +2,6 @@ #include "aigverse/types.hpp" -#include #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) #include @@ -118,6 +117,37 @@ nanobind::ndarray make_owned_ndarray(std::unique_ptr&& return nb::ndarray(raw_storage, shape, owner); } +/** + * @brief Expands one dynamic truth table into a contiguous float feature slice. + * + * ``kitty::dynamic_truth_table`` stores bits in 64-bit blocks. Iterating those + * blocks directly avoids paying the helper-call and index-arithmetic overhead of + * ``kitty::get_bit`` for every output feature. + * + * @param destination Start of the destination feature slice. + * @param tt Source truth table. + * @param invert Whether to invert each exported bit. + */ +inline void write_truth_table_bits(float* destination, const aigverse::truth_table& tt, const bool invert = false) +{ + std::size_t bit_offset = 0; + const auto tt_dim = static_cast(tt.num_bits()); + + for (const auto block : tt) + { + const auto remaining_bits = tt_dim - bit_offset; + const auto block_bits = remaining_bits < 64 ? remaining_bits : 64; + const auto bits = invert ? ~block : block; + + for (std::size_t bit = 0; bit < block_bits; ++bit) + { + destination[bit_offset + bit] = static_cast((bits >> bit) & 0x1ULL); + } + + bit_offset += block_bits; + } +} + /** * @brief Exports an AIG-style network to sparse COO-like graph tensors. * @@ -289,7 +319,6 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ // Truth-table simulation is optional because it is by far the most // expensive feature family when enabled. std::optional> node_truth_tables{}; - std::vector output_truth_tables{}; if (node_tts) { if (ntk.num_pis() > 16) @@ -299,8 +328,6 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ node_truth_tables = mockturtle::simulate_nodes( ntk, mockturtle::default_simulator{static_cast(ntk.num_pis())}); - output_truth_tables = mockturtle::simulate( - ntk, mockturtle::default_simulator{static_cast(ntk.num_pis())}); if (ntk.size() > 0) { @@ -324,6 +351,8 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ depth_ntk.emplace(ntk); } + const auto* simulated_nodes = node_tts ? &node_truth_tables.value() : nullptr; + const auto fill_base = [&](const std::size_t row, const int64_t type_index) -> float* { auto* row_data = node_values + (row * node_dim); @@ -373,11 +402,7 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ } if (node_tts) { - const auto& tt = node_truth_tables.value()[n]; - for (std::size_t i = 0; i < tt_dim; ++i) - { - feature_offset[i] = kitty::get_bit(tt, static_cast(i)) ? 1.0f : 0.0f; - } + write_truth_table_bits(feature_offset, (*simulated_nodes)[n]); } }); @@ -389,12 +414,13 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ // same order used for PO edges. const auto po_idx = po_row++; const auto row = static_cast(ntk.size()) + po_idx; + const auto driver = ntk.get_node(po); auto* feature_offset = fill_base(row, type_po); if (levels) { - *feature_offset++ = static_cast(depth_ntk->level(ntk.get_node(po)) + 1); + *feature_offset++ = static_cast(depth_ntk->level(driver) + 1); } if (fanouts) { @@ -402,11 +428,7 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ } if (node_tts) { - const auto& tt = output_truth_tables[po_idx]; - for (std::size_t i = 0; i < tt_dim; ++i) - { - feature_offset[i] = kitty::get_bit(tt, i) ? 1.0f : 0.0f; - } + write_truth_table_bits(feature_offset, (*simulated_nodes)[driver], ntk.is_complemented(po)); } }); diff --git a/test/networks/test_graph_tensors.py b/test/networks/test_graph_tensors.py index c261a0c0..ec214af9 100644 --- a/test/networks/test_graph_tensors.py +++ b/test/networks/test_graph_tensors.py @@ -125,6 +125,29 @@ def test_to_graph_tensors_truth_table_feature_dim(sample_aig: Aig) -> None: assert node_attr.shape[1] == 4 + 1 + tt_dim +def test_to_graph_tensors_complemented_po_truth_table_bits() -> None: + """Checks complemented POs export the inverted driver truth table.""" + aig = Aig() + a = aig.create_pi() + aig.create_po(~a) + + tensors = aig.to_graph_tensors( + node_encoding=NodeTensorEncoding.INTEGER, + edge_encoding=EdgeTensorEncoding.BINARY, + levels=False, + fanouts=False, + node_tts=True, + ) + + node_attr = np.from_dlpack(tensors["node_attr"]) + driver_row = node_attr[aig.get_node(a)] + po_row = node_attr[aig.size] + + assert driver_row[0] == pytest.approx(1.0) + assert po_row[0] == pytest.approx(3.0) + assert np.allclose(po_row[1:], 1.0 - driver_row[1:]) + + def test_to_graph_tensors_torch_from_dlpack(sample_aig: Aig) -> None: """Ensures PyTorch can consume all exported tensors via DLPack.""" tensors = sample_aig.to_graph_tensors( @@ -273,3 +296,39 @@ def test_to_graph_tensors_po_levels_follow_driver_depth() -> None: assert po_rows.shape[0] == 2 assert po_rows[0, 1] == pytest.approx(1.0) assert po_rows[1, 1] == pytest.approx(2.0) + + +def test_to_graph_tensors_fanout_feature_values() -> None: + """Checks fanout features reflect the network fanout counts.""" + aig = Aig() + a = aig.create_pi() + b = aig.create_pi() + shared = aig.create_and(a, b) + left = aig.create_and(shared, a) + right = aig.create_and(shared, b) + aig.create_po(left) + aig.create_po(right) + + tensors = aig.to_graph_tensors( + node_encoding=NodeTensorEncoding.INTEGER, + edge_encoding=EdgeTensorEncoding.BINARY, + levels=False, + fanouts=True, + node_tts=False, + ) + + node_attr = np.from_dlpack(tensors["node_attr"]) + po_rows = node_attr[aig.size :, :] + + a_node = aig.get_node(a) + b_node = aig.get_node(b) + shared_node = aig.get_node(shared) + left_node = aig.get_node(left) + right_node = aig.get_node(right) + + assert node_attr[a_node, 1] == pytest.approx(2.0) + assert node_attr[b_node, 1] == pytest.approx(2.0) + assert node_attr[shared_node, 1] == pytest.approx(2.0) + assert node_attr[left_node, 1] == pytest.approx(1.0) + assert node_attr[right_node, 1] == pytest.approx(1.0) + assert np.allclose(po_rows[:, 1], 0.0) From cc91ae2313aa2b16078b8925d79164e5118cb0bf Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Fri, 15 May 2026 12:42:35 +0200 Subject: [PATCH 27/29] =?UTF-8?q?=F0=9F=93=9D=20Fix=20parameter=20names=20?= =?UTF-8?q?in=20documentation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/machine_learning.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/machine_learning.md b/docs/machine_learning.md index 688bb8a9..724fbbf4 100644 --- a/docs/machine_learning.md +++ b/docs/machine_learning.md @@ -170,7 +170,7 @@ Current limitations of `to_graph_tensors`: - The export targets **combinational** networks. Sequential networks are not supported. - Exported tensors are backed by **CPU host memory** (NumPy-backed DLPack producer). - `torch.from_dlpack(...)` is zero-copy on CPU, but moving tensors to CUDA still allocates GPU memory and performs a host-to-device copy. -- When `include_truth_table=True`, the export is restricted to at most 16 primary inputs due to the exponential growth of truth table size. This is a practical limit for ML applications, but it is not a fundamental limitation of the API. +- When `node_tts=True`, the export is restricted to at most 16 primary inputs due to the exponential growth of truth table size. This is a practical limit for ML applications, but it is not a fundamental limitation of the API. ::: ```{code-cell} ipython3 @@ -187,9 +187,9 @@ aig.create_po(g) dlpack_data = aig.to_graph_tensors( node_encoding=NodeTensorEncoding.INTEGER, edge_encoding=EdgeTensorEncoding.BINARY, - include_level=True, - include_fanout=True, - include_truth_table=False, + levels=True, + fanouts=True, + node_tts=False, ) edge_index = torch.from_dlpack(dlpack_data["edge_index"]) From be80e0b81f10bc7cff6ca82110a3aafea5408f83 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Fri, 15 May 2026 18:07:38 +0200 Subject: [PATCH 28/29] =?UTF-8?q?=F0=9F=9A=A8=20Address=20`clang-tidy`=20w?= =?UTF-8?q?arnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/aigverse/networks/edge_list.hpp | 1 + src/aigverse/networks/graph_tensors.hpp | 23 +++++++++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/aigverse/networks/edge_list.hpp b/src/aigverse/networks/edge_list.hpp index fc33e397..6a2655fc 100644 --- a/src/aigverse/networks/edge_list.hpp +++ b/src/aigverse/networks/edge_list.hpp @@ -8,6 +8,7 @@ #include // NOLINT(misc-include-cleaner) #include +#include #include #include #include diff --git a/src/aigverse/networks/graph_tensors.hpp b/src/aigverse/networks/graph_tensors.hpp index 9528329c..dd4e46b8 100644 --- a/src/aigverse/networks/graph_tensors.hpp +++ b/src/aigverse/networks/graph_tensors.hpp @@ -102,12 +102,16 @@ nanobind::ndarray make_owned_ndarray(std::vector&& } template +// Raw contiguous storage is intentional here because the export hot path +// fully overwrites the buffer before handing it off to nanobind. +// NOLINTNEXTLINE(*-avoid-c-arrays) nanobind::ndarray make_owned_ndarray(std::unique_ptr&& data, const std::initializer_list& shape) { namespace nb = nanobind; - auto* raw_storage = data.release(); + auto storage = std::move(data); + auto* raw_storage = storage.release(); nb::capsule owner(raw_storage, [](void* ptr) noexcept { @@ -128,6 +132,8 @@ nanobind::ndarray make_owned_ndarray(std::unique_ptr&& * @param tt Source truth table. * @param invert Whether to invert each exported bit. */ +// Direct writes into the caller-provided slice are a performance optimization. +// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) inline void write_truth_table_bits(float* destination, const aigverse::truth_table& tt, const bool invert = false) { std::size_t bit_offset = 0; @@ -147,6 +153,7 @@ inline void write_truth_table_bits(float* destination, const aigverse::truth_tab bit_offset += block_bits; } } +// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) /** * @brief Exports an AIG-style network to sparse COO-like graph tensors. @@ -226,12 +233,17 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ // edge_index and edge_attr are fully overwritten during export, so raw // storage avoids paying for a zero-initialization pass that would be thrown // away immediately. + // NOLINTBEGIN(*-avoid-c-arrays) std::unique_ptr edge_index{new int64_t[2 * edge_count]}; const std::size_t edge_dim = edge_encoding == edge_tensor_encoding::ONE_HOT ? 2 : 1; std::unique_ptr edge_attr{new float[edge_count * edge_dim]}; + // NOLINTEND(*-avoid-c-arrays) + // Direct pointer-based writes benchmarked better than zero-filled vector + // materialization for this exporter. + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) auto* edge_sources = edge_index.get(); auto* edge_targets = edge_sources + edge_count; auto* edge_values = edge_attr.get(); @@ -269,6 +281,7 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ ++edge_cursor; }; + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) int64_t edge_target = 0; ntk.foreach_node( @@ -285,7 +298,7 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ }); }); - int64_t po_target = static_cast(ntk.size()); + auto po_target = static_cast(ntk.size()); ntk.foreach_po( [&](const auto& po) { @@ -341,6 +354,7 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ // node_attr is also fully overwritten row-by-row, so it gets the same raw // storage treatment as the edge buffers. + // NOLINTNEXTLINE(*-avoid-c-arrays) std::unique_ptr node_attr{new float[node_count * node_dim]}; auto* node_values = node_attr.get(); @@ -353,6 +367,8 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ const auto* simulated_nodes = node_tts ? &node_truth_tables.value() : nullptr; + // Direct feature-slice writes are intentional runtime optimizations. + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) const auto fill_base = [&](const std::size_t row, const int64_t type_index) -> float* { auto* row_data = node_values + (row * node_dim); @@ -431,14 +447,17 @@ nanobind::dict to_graph_tensors(const Ntk& ntk, const node_tensor_encoding node_ write_truth_table_bits(feature_offset, (*simulated_nodes)[driver], ntk.is_complemented(po)); } }); + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) auto result = nb::dict(); // Hand off ownership to nanobind capsules so downstream DLPack consumers // can borrow the buffers without an extra copy. + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-avoid-unchecked-container-access) result["edge_index"] = make_owned_ndarray(std::move(edge_index), {2, edge_count}); result["edge_attr"] = make_owned_ndarray(std::move(edge_attr), {edge_count, edge_dim}); result["node_attr"] = make_owned_ndarray(std::move(node_attr), {node_count, node_dim}); + // NOLINTEND(cppcoreguidelines-pro-bounds-avoid-unchecked-container-access) return result; } From 4ab06e004bd3f5d4adb923bc0fbbdb1de399c0f5 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Fri, 15 May 2026 18:22:27 +0200 Subject: [PATCH 29/29] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20Update=20stubs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- python/aigverse/networks.pyi | 72 ++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/python/aigverse/networks.pyi b/python/aigverse/networks.pyi index 38c14d1c..f6241305 100644 --- a/python/aigverse/networks.pyi +++ b/python/aigverse/networks.pyi @@ -310,41 +310,6 @@ class Aig: The corresponding index-list representation. """ - def to_graph_tensors( - self, - node_encoding: NodeTensorEncoding = ..., - edge_encoding: EdgeTensorEncoding = ..., - *, - levels: bool = True, - fanouts: bool = False, - node_tts: bool = False, - ) -> dict: - """Exports graph tensors for machine-learning workflows. - - Returns sparse graph topology and features as DLPack-compatible arrays. - - Edge encoding mapping: - - ``BINARY``: regular=0.0, inverted=1.0 - - ``SIGNED``: regular=+1.0, inverted=-1.0 - - ``ONE_HOT``: regular=[1.0, 0.0], inverted=[0.0, 1.0] - - Node encoding mapping: - - ``INTEGER``: constant=0, pi=1, gate=2, po=3 - - ``ONE_HOT``: [constant, pi, gate, po] - - Args: - node_encoding: Node encoding mode as :class:`~aigverse.networks.NodeTensorEncoding`. - edge_encoding: Edge encoding mode as :class:`~aigverse.networks.EdgeTensorEncoding`. - levels: Appends logic level as a node feature. - fanouts: Appends fanout size as a node feature. - node_tts: Appends simulated node/output truth-table bits. - - Returns: - A dictionary with ``edge_index`` (shape ``(2, E)``, dtype ``int64``), - ``edge_attr`` (shape ``(E, D_edge)``, dtype ``float32``), and ``node_attr`` - (shape ``(N, D_node)``, dtype ``float32``). - """ - if TYPE_CHECKING: def to_networkx( self, @@ -413,6 +378,41 @@ class Aig: PO nodes (only for :class:`~aigverse.NamedAig`). """ + def to_graph_tensors( + self, + node_encoding: NodeTensorEncoding = ..., + edge_encoding: EdgeTensorEncoding = ..., + *, + levels: bool = True, + fanouts: bool = False, + node_tts: bool = False, + ) -> dict: + """Exports graph tensors for machine-learning workflows. + + Returns sparse graph topology and features as DLPack-compatible arrays. + + Edge encoding mapping: + - ``BINARY``: regular=0.0, inverted=1.0 + - ``SIGNED``: regular=+1.0, inverted=-1.0 + - ``ONE_HOT``: regular=[1.0, 0.0], inverted=[0.0, 1.0] + + Node encoding mapping: + - ``INTEGER``: constant=0, pi=1, gate=2, po=3 + - ``ONE_HOT``: [constant, pi, gate, po] + + Args: + node_encoding: Node encoding mode as :class:`~aigverse.networks.NodeTensorEncoding`. + edge_encoding: Edge encoding mode as :class:`~aigverse.networks.EdgeTensorEncoding`. + levels: Appends logic level as a node feature. + fanouts: Appends fanout size as a node feature. + node_tts: Appends simulated node/output truth-table bits. + + Returns: + A dictionary with ``edge_index`` (shape ``(2, E)``, dtype ``int64``), + ``edge_attr`` (shape ``(E, D_edge)``, dtype ``float32``), and ``node_attr`` + (shape ``(N, D_node)``, dtype ``float32``). + """ + def __len__(self) -> int: """Returns the number of nodes.""" @@ -714,7 +714,7 @@ class SequentialAig(Aig): levels: bool = True, fanouts: bool = False, node_tts: bool = False, - ) -> NoReturn: + ) -> dict: """Sequential networks cannot be exported as combinational graph tensors.""" def __getstate__(self) -> NoReturn: