diff --git a/docs/machine_learning.md b/docs/machine_learning.md index 8da3458a..dad26f2a 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 use of graph-analysis and graph-learning tooling that operates on NetworkX graphs. Once converted, you can @@ -128,50 +128,136 @@ plt.margins(x=0.2) plt.show() ``` -## Truth Tables +## 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 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), +[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. + +:::{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 `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 +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, + levels=True, + fanouts=True, + node_tts=False, +) -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. +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 -from aigverse.utils import TruthTable import numpy as np -# Create a simple truth table, e.g., a 3-input majority function -tt = TruthTable(3) -tt.create_from_hex_string("e8") +edge_index_np = np.from_dlpack(aig.to_graph_tensors()["edge_index"]) +print(edge_index_np.shape) +``` + +## Truth Tables -# Export to a list -tt_list = list(tt) -print(f"As list: {tt_list}") +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: -# 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}") +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 +import numpy as np +import torch + +from aigverse.utils import TruthTable + +# Create a simple truth table (3-input majority function) +tt = TruthTable(3) +tt.create_from_hex_string("e8") -# 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) ``` diff --git a/pyproject.toml b/pyproject.toml index ac65522d..b353ff32 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,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" @@ -224,6 +224,7 @@ build = [ adapters = [ "networkx>=3.0.0", "numpy>=1.23.0", + "torch>=2.2.0", ] docs = [ { include-group = "adapters" }, diff --git a/python/aigverse/networks.pyi b/python/aigverse/networks.pyi index 76b82da2..f6241305 100644 --- a/python/aigverse/networks.pyi +++ b/python/aigverse/networks.pyi @@ -4,6 +4,7 @@ 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 TYPE_CHECKING, NoReturn, overload @@ -11,6 +12,40 @@ if TYPE_CHECKING: import networkx as nx import numpy as np +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. + + 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).""" + + SIGNED = 1 + """Labels are encoded as +1.0 (regular) and -1.0 (inverted).""" + + ONE_HOT = 2 + """One-hot edge labels in [regular, inverted] order.""" + class AigSignal: """Represents a signal in an AIG. @@ -343,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.""" @@ -636,6 +706,17 @@ 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 = ..., + *, + levels: bool = True, + fanouts: bool = False, + node_tts: bool = False, + ) -> dict: + """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/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.hpp b/src/aigverse/networks/edge_list.hpp index 9f8e967c..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 @@ -129,12 +130,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) diff --git a/src/aigverse/networks/graph_tensors.hpp b/src/aigverse/networks/graph_tensors.hpp new file mode 100644 index 00000000..dd4e46b8 --- /dev/null +++ b/src/aigverse/networks/graph_tensors.hpp @@ -0,0 +1,467 @@ +#pragma once + +#include "aigverse/types.hpp" + +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace aigverse +{ + +/** + * @brief Node encoding mode for exported graph tensors. + * + * 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 +{ + /// 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 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 +{ + /// Labels are encoded as 0.0 (regular) and 1.0 (inverted). + BINARY, + /// Labels are encoded as +1.0 (regular) and -1.0 (inverted). + SIGNED, + /// Labels are encoded as one-hot vectors in the order [regular, inverted]. + ONE_HOT, +}; + +namespace detail +{ + +/** + * @brief Creates an owning NumPy-backed nanobind ndarray from a moved vector. + * + * 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. + * @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; + + // 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); // NOLINT(cppcoreguidelines-owning-memory) + }); + storage.release(); + + return nb::ndarray(raw_storage->data(), shape, owner); +} + +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 storage = std::move(data); + auto* raw_storage = storage.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 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. + */ +// 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; + 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; + } +} +// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + +/** + * @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)`` + * + * 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 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. + * @param node_encoding Node-type encoding mode. + * @param edge_encoding Edge-type encoding mode. + * @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, + + /** + * @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) +{ + namespace nb = nanobind; + + // Number of node-type categories used for one-hot encodings: + // [constant, primary input, internal gate, primary output]. + 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; + constexpr int64_t type_pi = 1; + constexpr int64_t type_gate = 2; + constexpr int64_t type_po = 3; + + // 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()); + if constexpr (mockturtle::has_foreach_ri_v && mockturtle::has_ri_to_ro_v) + { + edge_count += static_cast(ntk.num_registers()); + } + + // 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(); + + 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: + { + edge_values[edge_cursor] = inverted ? 1.0f : 0.0f; + break; + } + case edge_tensor_encoding::SIGNED: + { + edge_values[edge_cursor] = inverted ? -1.0f : 1.0f; + break; + } + case edge_tensor_encoding::ONE_HOT: + { + edge_values[edge_cursor * 2] = inverted ? 0.0f : 1.0f; + edge_values[(edge_cursor * 2) + 1] = inverted ? 1.0f : 0.0f; + break; + } + } + + ++edge_cursor; + }; + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + + int64_t edge_target = 0; + 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) + { + const auto source = static_cast(ntk.node_to_index(ntk.get_node(f))); + append_edge(source, target, ntk.is_complemented(f)); + }); + }); + + auto po_target = static_cast(ntk.size()); + 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)); + }); + + 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"); + } + + 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{}; + if (node_tts) + { + if (ntk.num_pis() > 16) + { + throw std::invalid_argument("truth-table export is only supported up to 16 primary inputs"); + } + + node_truth_tables = mockturtle::simulate_nodes( + ntk, mockturtle::default_simulator{static_cast(ntk.num_pis())}); + + if (ntk.size() > 0) + { + 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 + (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. + // NOLINTNEXTLINE(*-avoid-c-arrays) + 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); + } + + 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); + if (node_encoding == node_tensor_encoding::INTEGER) + { + row_data[0] = static_cast(type_index); + 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; + 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; + 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)) + { + type_index = type_constant; + } + else if (ntk.is_pi(n)) + { + type_index = type_pi; + } + + auto* feature_offset = fill_base(row, type_index); + + if (levels) + { + *feature_offset++ = static_cast(depth_ntk->level(n)); + } + if (fanouts) + { + *feature_offset++ = static_cast(ntk.fanout_size(n)); + } + if (node_tts) + { + write_truth_table_bits(feature_offset, (*simulated_nodes)[n]); + } + }); + + std::size_t po_row = 0; + 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; + const auto driver = ntk.get_node(po); + + auto* feature_offset = fill_base(row, type_po); + + if (levels) + { + *feature_offset++ = static_cast(depth_ntk->level(driver) + 1); + } + if (fanouts) + { + *feature_offset++ = 0.0f; + } + if (node_tts) + { + 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; +} + +} // namespace detail + +} // namespace aigverse 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 1b84ed94..05acbb54 100644 --- a/src/aigverse/networks/logic_networks.cpp +++ b/src/aigverse/networks/logic_networks.cpp @@ -2,11 +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 "index_list.hpp" - #include #include #include @@ -112,6 +112,36 @@ bool contains_node(const Ntk& ntk, const nanobind::object& value) } // namespace +void bind_tensor_encodings(nanobind::module_& m) // NOLINT(misc-use-internal-linkage) +{ + namespace nb = nanobind; + + 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("SIGNED", aigverse::edge_tensor_encoding::SIGNED, + R"pb(Labels are encoded as +1.0 (regular) and -1.0 (inverted).)pb") + .value("ONE_HOT", aigverse::edge_tensor_encoding::ONE_HOT, + R"pb(One-hot edge labels in [regular, inverted] order.)pb"); +} + template void bind_network(nanobind::module_& m, const std::string& network_name) // NOLINT(misc-use-internal-linkage) { @@ -355,6 +385,42 @@ 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::node_tensor_encoding node_encoding, + 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, levels, fanouts, node_tts); + }, + nb::arg("node_encoding") = aigverse::node_tensor_encoding::INTEGER, + 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. + +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``). +)pb") .def("__len__", &Ntk::size, R"pb(Returns the number of nodes.)pb") .def( "__repr__", @@ -593,6 +659,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::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( "__getstate__", [network_name](const SequentialNtk&) -> nb::tuple @@ -710,6 +791,7 @@ template void bind_network(nanobind::module_&, const std::string& void bind_logic_networks(nanobind::module_& m) // NOLINT(misc-use-internal-linkage) { + detail::bind_tensor_encodings(m); detail::bind_network(m, "Aig"); } diff --git a/test/networks/test_graph_tensors.py b/test/networks/test_graph_tensors.py new file mode 100644 index 00000000..ec214af9 --- /dev/null +++ b/test/networks/test_graph_tensors.py @@ -0,0 +1,334 @@ +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, + levels=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, + levels=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, + levels=False, + fanouts=True, + node_tts=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_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( + 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, + levels=True, + fanouts=True, + node_tts=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, + levels=False, + fanouts=False, + node_tts=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 + + +def test_to_graph_tensors_truth_table_pi_limit_raises() -> None: + """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(node_tts=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, + levels=True, + fanouts=False, + node_tts=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) + + +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) diff --git a/test/networks/test_sequential_aig.py b/test/networks/test_sequential_aig.py index a88364a6..4dc38363 100644 --- a/test/networks/test_sequential_aig.py +++ b/test/networks/test_sequential_aig.py @@ -134,6 +134,18 @@ def test_sequential_aig_to_index_list_raises( 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( sequential_aig_single_register: tuple[SequentialAig, AigSignal, AigSignal, AigSignal], ) -> None: diff --git a/uv.lock b/uv.lock index 5da8f26c..3fd209e2 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.4", 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.4", 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" }, { name = "ty" }, ] docs = [ @@ -89,6 +92,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" }, ] lint = [ { name = "nox" }, @@ -102,12 +106,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"] @@ -115,6 +121,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" }, @@ -142,6 +149,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" }, { name = "ty", specifier = "==0.0.24" }, ] docs = [ @@ -159,6 +167,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" }, ] lint = [ { name = "nox", specifier = ">=2025.10.16" }, @@ -170,6 +179,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]] @@ -645,6 +655,76 @@ 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 = "13.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-pathfinder" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/fe/7351d7e586a8b4c9f89731bfe4cf0148223e8f9903ff09571f78b3fb0682/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08b395f79cb89ce0cd8effff07c4a1e20101b873c256a1aeb286e8fd7bd0f556", size = 5744254, upload-time = "2026-03-11T00:12:29.798Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ef/184aa775e970fc089942cd9ec6302e6e44679d4c14549c6a7ea45bf7f798/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6f3682ec3c4769326aafc67c2ba669d97d688d0b7e63e659d36d2f8b72f32d6", size = 6329075, upload-time = "2026-03-11T00:12:32.319Z" }, + { url = "https://files.pythonhosted.org/packages/e0/a9/3a8241c6e19483ac1f1dcf5c10238205dcb8a6e9d0d4d4709240dff28ff4/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:721104c603f059780d287969be3d194a18d0cc3b713ed9049065a1107706759d", size = 5730273, upload-time = "2026-03-11T00:12:37.18Z" }, + { url = "https://files.pythonhosted.org/packages/e9/94/2748597f47bb1600cd466b20cab4159f1530a3a33fe7f70fee199b3abb9e/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1eba9504ac70667dd48313395fe05157518fd6371b532790e96fbb31bbb5a5e1", size = 6313924, upload-time = "2026-03-11T00:12:39.462Z" }, + { url = "https://files.pythonhosted.org/packages/52/c8/b2589d68acf7e3d63e2be330b84bc25712e97ed799affbca7edd7eae25d6/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e865447abfb83d6a98ad5130ed3c70b1fc295ae3eeee39fd07b4ddb0671b6788", size = 5722404, upload-time = "2026-03-11T00:12:44.041Z" }, + { url = "https://files.pythonhosted.org/packages/1f/92/f899f7bbb5617bb65ec52a6eac1e9a1447a86b916c4194f8a5001b8cde0c/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46d8776a55d6d5da9dd6e9858fba2efcda2abe6743871dee47dd06eb8cb6d955", size = 6320619, upload-time = "2026-03-11T00:12:45.939Z" }, + { url = "https://files.pythonhosted.org/packages/df/93/eef988860a3ca985f82c4f3174fc0cdd94e07331ba9a92e8e064c260337f/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6629ca2df6f795b784752409bcaedbd22a7a651b74b56a165ebc0c9dcbd504d0", size = 5614610, upload-time = "2026-03-11T00:12:50.337Z" }, + { url = "https://files.pythonhosted.org/packages/18/23/6db3aba46864aee357ab2415135b3fe3da7e9f1fa0221fa2a86a5968099c/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dca0da053d3b4cc4869eff49c61c03f3c5dbaa0bcd712317a358d5b8f3f385d", size = 6149914, upload-time = "2026-03-11T00:12:52.374Z" }, + { url = "https://files.pythonhosted.org/packages/c0/87/87a014f045b77c6de5c8527b0757fe644417b184e5367db977236a141602/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6464b30f46692d6c7f65d4a0e0450d81dd29de3afc1bb515653973d01c2cd6e", size = 5685673, upload-time = "2026-03-11T00:12:56.371Z" }, + { url = "https://files.pythonhosted.org/packages/ee/5e/c0fe77a73aaefd3fff25ffaccaac69c5a63eafdf8b9a4c476626ef0ac703/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4af9f3e1be603fa12d5ad6cfca7844c9d230befa9792b5abdf7dd79979c3626", size = 6191386, upload-time = "2026-03-11T00:12:58.965Z" }, + { url = "https://files.pythonhosted.org/packages/5f/58/ed2c3b39c8dd5f96aa7a4abef0d47a73932c7a988e30f5fa428f00ed0da1/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df850a1ff8ce1b3385257b08e47b70e959932f5f432d0a4e46a355962b4e4771", size = 5507469, upload-time = "2026-03-11T00:13:04.063Z" }, + { url = "https://files.pythonhosted.org/packages/1f/01/0c941b112ceeb21439b05895eace78ca1aa2eaaf695c8521a068fd9b4c00/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8a16384c6494e5485f39314b0b4afb04bee48d49edb16d5d8593fd35bbd231b", size = 6059693, upload-time = "2026-03-11T00:13:06.003Z" }, +] + +[[package]] +name = "cuda-pathfinder" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/d0/c177e29701cf1d3008d7d2b16b5fc626592ce13bd535f8795c5f57187e0e/cuda_pathfinder-1.5.4-py3-none-any.whl", hash = "sha256:9563d3175ce1828531acf4b94e1c1c7d67208c347ca002493e2654878b26f4b7", size = 51657, upload-time = "2026-04-27T22:42:07.712Z" }, +] + +[[package]] +name = "cuda-toolkit" +version = "13.0.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/b2/453099f5f3b698d7d0eab38916aac44c7f76229f451709e2eb9db6615dcd/cuda_toolkit-13.0.2-py2.py3-none-any.whl", hash = "sha256:b198824cf2f54003f50d64ada3a0f184b42ca0846c1c94192fa269ecd97a66eb", size = 2364, upload-time = "2025-12-19T23:24:07.328Z" }, +] + +[package.optional-dependencies] +cudart = [ + { name = "nvidia-cuda-runtime", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +cufft = [ + { name = "nvidia-cufft", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +cufile = [ + { name = "nvidia-cufile", marker = "sys_platform == 'linux'" }, +] +cupti = [ + { name = "nvidia-cuda-cupti", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +curand = [ + { name = "nvidia-curand", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +cusolver = [ + { name = "nvidia-cusolver", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +cusparse = [ + { name = "nvidia-cusparse", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +nvjitlink = [ + { name = "nvidia-nvjitlink", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +nvrtc = [ + { name = "nvidia-cuda-nvrtc", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +nvtx = [ + { name = "nvidia-nvtx", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] + [[package]] name = "cycler" version = "0.12.1" @@ -844,6 +924,15 @@ wheels = [ { 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]] +name = "fsspec" +version = "2026.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d5/8d/1c51c094345df128ca4a990d633fe1a0ff28726c9e6b3c41ba65087bba1d/fsspec-2026.4.0.tar.gz", hash = "sha256:301d8ac70ae90ef3ad05dcf94d6c3754a097f9b5fe4667d2787aa359ec7df7e4", size = 312760, upload-time = "2026-04-29T20:42:38.635Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/0c/043d5e551459da400957a1395e0febbf771446ff34291afcbe3d8be2a279/fsspec-2026.4.0-py3-none-any.whl", hash = "sha256:11ef7bb35dab8a394fde6e608221d5cf3e8499401c249bebaeaad760a1a8dec2", size = 203402, upload-time = "2026-04-29T20:42:36.842Z" }, +] + [[package]] name = "furo" version = "2025.12.19" @@ -1501,6 +1590,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" @@ -1808,6 +1906,158 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/04/74/f4c001f4714c3ad9ce037e18cf2b9c64871a84951eaa0baf683a9ca9301c/numpy-2.4.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f2cf083b324a467e1ab358c105f6cad5ea950f50524668a80c486ff1db24e119", size = 12509075, upload-time = "2026-03-29T13:21:57.644Z" }, ] +[[package]] +name = "nvidia-cublas" +version = "13.1.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cuda-nvrtc" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/a1/0bd24ee8c8d03adac032fd2909426a00c88f8c57961b1277ded97f91119f/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b7a210458267ac818974c53038fbec2e969d5c99f305ab15c72522fa9f001dd5", size = 542848918, upload-time = "2026-04-08T18:46:22.985Z" }, + { url = "https://files.pythonhosted.org/packages/3b/cd/154ca20c38269e05eff77c1464e6c1da89f50a6390b565e9d82e06bc11e1/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:37936a16db8fe4ac1f065c2139360608a543a09275cb1a1af612e08cfa065436", size = 423138758, upload-time = "2026-04-08T18:46:58.655Z" }, +] + +[[package]] +name = "nvidia-cuda-cupti" +version = "13.0.85" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/2a/80353b103fc20ce05ef51e928daed4b6015db4aaa9162ed0997090fe2250/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_aarch64.whl", hash = "sha256:796bd679890ee55fb14a94629b698b6db54bcfd833d391d5e94017dd9d7d3151", size = 10310827, upload-time = "2025-09-04T08:26:42.012Z" }, + { url = "https://files.pythonhosted.org/packages/33/6d/737d164b4837a9bbd202f5ae3078975f0525a55730fe871d8ed4e3b952b0/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_x86_64.whl", hash = "sha256:4eb01c08e859bf924d222250d2e8f8b8ff6d3db4721288cf35d14252a4d933c8", size = 10715597, upload-time = "2025-09-04T08:26:51.312Z" }, +] + +[[package]] +name = "nvidia-cuda-nvrtc" +version = "13.0.88" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/68/483a78f5e8f31b08fb1bb671559968c0ca3a065ac7acabfc7cee55214fd6/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:ad9b6d2ead2435f11cbb6868809d2adeeee302e9bb94bcf0539c7a40d80e8575", size = 90215200, upload-time = "2025-09-04T08:28:44.204Z" }, + { url = "https://files.pythonhosted.org/packages/b7/dc/6bb80850e0b7edd6588d560758f17e0550893a1feaf436807d64d2da040f/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b", size = 43015449, upload-time = "2025-09-04T08:28:20.239Z" }, +] + +[[package]] +name = "nvidia-cuda-runtime" +version = "13.0.96" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/4f/17d7b9b8e285199c58ce28e31b5c5bbaa4d8271af06a89b6405258245de2/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef9bcbe90493a2b9d810e43d249adb3d02e98dd30200d86607d8d02687c43f55", size = 2261060, upload-time = "2025-10-09T08:55:15.78Z" }, + { url = "https://files.pythonhosted.org/packages/2e/24/d1558f3b68b1d26e706813b1d10aa1d785e4698c425af8db8edc3dced472/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548", size = 2243632, upload-time = "2025-10-09T08:55:36.117Z" }, +] + +[[package]] +name = "nvidia-cudnn-cu13" +version = "9.20.0.48" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/c5/83384d846b2fd17c44bd499b36c75a45ed4f095fbbb2252294e89cea5c5c/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:e31454ae00094b0c55319d9d15b6fa2fc50a9e1c0f5c8c80fb75258234e731e1", size = 444574296, upload-time = "2026-03-09T19:28:27.751Z" }, + { url = "https://files.pythonhosted.org/packages/6e/5e/edb9c0ae051602c3ccaffe424256463636d639e27d7f302dde9975ef9e7a/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0c45dd8eeb50b603f07995b1b300c62ffe6a1980482b82b3bcf94a4ca9d49304", size = 366173588, upload-time = "2026-03-09T19:29:34.474Z" }, +] + +[[package]] +name = "nvidia-cufft" +version = "12.0.0.61" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2f/7b57e29836ea8714f81e9898409196f47d772d5ddedddf1592eadb8ab743/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c44f692dce8fd5ffd3e3df134b6cdb9c2f72d99cf40b62c32dde45eea9ddad3", size = 214085489, upload-time = "2025-09-04T08:31:56.044Z" }, +] + +[[package]] +name = "nvidia-cufile" +version = "1.15.1.6" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/70/4f193de89a48b71714e74602ee14d04e4019ad36a5a9f20c425776e72cd6/nvidia_cufile-1.15.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08a3ecefae5a01c7f5117351c64f17c7c62efa5fffdbe24fc7d298da19cd0b44", size = 1223672, upload-time = "2025-09-04T08:32:22.779Z" }, + { url = "https://files.pythonhosted.org/packages/ab/73/cc4a14c9813a8a0d509417cf5f4bdaba76e924d58beb9864f5a7baceefbf/nvidia_cufile-1.15.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:bdc0deedc61f548bddf7733bdc216456c2fdb101d020e1ab4b88d232d5e2f6d1", size = 1136992, upload-time = "2025-09-04T08:32:14.119Z" }, +] + +[[package]] +name = "nvidia-curand" +version = "10.4.0.35" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/72/7c2ae24fb6b63a32e6ae5d241cc65263ea18d08802aaae087d9f013335a2/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:133df5a7509c3e292aaa2b477afd0194f06ce4ea24d714d616ff36439cee349a", size = 61962106, upload-time = "2025-08-04T10:21:41.128Z" }, + { url = "https://files.pythonhosted.org/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc", size = 59544258, upload-time = "2025-08-04T10:22:03.992Z" }, +] + +[[package]] +name = "nvidia-cusolver" +version = "12.0.4.66" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas" }, + { name = "nvidia-cusparse" }, + { name = "nvidia-nvjitlink" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" }, + { url = "https://files.pythonhosted.org/packages/5f/67/cba3777620cdacb99102da4042883709c41c709f4b6323c10781a9c3aa34/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0a759da5dea5c0ea10fd307de75cdeb59e7ea4fcb8add0924859b944babf1112", size = 200941980, upload-time = "2025-09-04T08:33:22.767Z" }, +] + +[[package]] +name = "nvidia-cusparse" +version = "12.6.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" }, + { url = "https://files.pythonhosted.org/packages/fa/18/623c77619c31d62efd55302939756966f3ecc8d724a14dab2b75f1508850/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b3c89c88d01ee0e477cb7f82ef60a11a4bcd57b6b87c33f789350b59759360b", size = 145942937, upload-time = "2025-09-04T08:33:58.029Z" }, +] + +[[package]] +name = "nvidia-cusparselt-cu13" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/e1/cdc1797eadf82d3a9a575a19b33fdc871a97edbec42c00b5b5e914f4aff4/nvidia_cusparselt_cu13-0.8.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4dca476c50bf4780d46cd0bfbd82e2bc10a08e4fef7950917ce8d7578d22a23f", size = 221051344, upload-time = "2025-09-05T18:49:51.289Z" }, + { url = "https://files.pythonhosted.org/packages/34/7d/2661f2fb3ac4302f3a246f5fc030213ac60c1fe0bce84f9783dbd831dbb7/nvidia_cusparselt_cu13-0.8.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:786ce87568c303fadb5afcc7102d454cd3040d75f6f8626f5db460d1871f4dd0", size = 170148586, upload-time = "2025-09-05T18:50:50.248Z" }, +] + +[[package]] +name = "nvidia-nccl-cu13" +version = "2.29.7" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/0d/daf50d44177ee0cbc7ff0a0c91eb5ff676c82be42f9a970bc7597f440c3a/nvidia_nccl_cu13-2.29.7-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:674a12383e3c38a1bcccae7d4f3633b37852230b6047883cb2f4c2d1b36d9bf5", size = 206014712, upload-time = "2026-03-03T05:34:20.843Z" }, + { url = "https://files.pythonhosted.org/packages/67/f4/58e4e91b6919367c7aafb8e36fce9aad1a3047e536bf7e2fd560927d3a4c/nvidia_nccl_cu13-2.29.7-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:edd81538446786ec3b73972543e53bb43bcaf0bfc8ef76cb679fcc390ffe136d", size = 205976000, upload-time = "2026-03-03T05:36:24.472Z" }, +] + +[[package]] +name = "nvidia-nvjitlink" +version = "13.0.88" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/7a/123e033aaff487c77107195fa5a2b8686795ca537935a24efae476c41f05/nvidia_nvjitlink-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:13a74f429e23b921c1109976abefacc69835f2f433ebd323d3946e11d804e47b", size = 40713933, upload-time = "2025-09-04T08:35:43.553Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2c/93c5250e64df4f894f1cbb397c6fd71f79813f9fd79d7cd61de3f97b3c2d/nvidia_nvjitlink-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e931536ccc7d467a98ba1d8b89ff7fa7f1fa3b13f2b0069118cd7f47bff07d0c", size = 38768748, upload-time = "2025-09-04T08:35:20.008Z" }, +] + +[[package]] +name = "nvidia-nvshmem-cu13" +version = "3.4.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/0f/05cc9c720236dcd2db9c1ab97fff629e96821be2e63103569da0c9b72f19/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dc2a197f38e5d0376ad52cd1a2a3617d3cdc150fd5966f4aee9bcebb1d68fe9", size = 60215947, upload-time = "2025-09-06T00:32:20.022Z" }, + { url = "https://files.pythonhosted.org/packages/3c/35/a9bf80a609e74e3b000fef598933235c908fcefcef9026042b8e6dfde2a9/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:290f0a2ee94c9f3687a02502f3b9299a9f9fe826e6d0287ee18482e78d495b80", size = 60412546, upload-time = "2025-09-06T00:32:41.564Z" }, +] + +[[package]] +name = "nvidia-nvtx" +version = "13.0.85" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/f3/d86c845465a2723ad7e1e5c36dcd75ddb82898b3f53be47ebd429fb2fa5d/nvidia_nvtx-13.0.85-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4936d1d6780fbe68db454f5e72a42ff64d1fd6397df9f363ae786930fd5c1cd4", size = 148047, upload-time = "2025-09-04T08:29:01.761Z" }, + { url = "https://files.pythonhosted.org/packages/a8/64/3708a90d1ebe202ffdeb7185f878a3c84d15c2b2c31858da2ce0583e2def/nvidia_nvtx-13.0.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6", size = 148878, upload-time = "2025-09-04T08:28:53.627Z" }, +] + [[package]] name = "packaging" version = "26.2" @@ -2438,11 +2688,11 @@ wheels = [ [[package]] name = "setuptools" -version = "82.0.1" +version = "81.0.0" source = { registry = "https://pypi.org/simple" } -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" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/1c/73e719955c59b8e424d015ab450f51c0af856ae46ea2da83eba51cc88de1/setuptools-81.0.0.tar.gz", hash = "sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a", size = 1198299, upload-time = "2026-02-06T21:10:39.601Z" } wheels = [ - { 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" }, + { url = "https://files.pythonhosted.org/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6", size = 1062021, upload-time = "2026-02-06T21:10:37.175Z" }, ] [[package]] @@ -2814,6 +3064,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" @@ -2886,6 +3148,59 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, ] +[[package]] +name = "torch" +version = "2.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-bindings", marker = "sys_platform == 'linux'" }, + { name = "cuda-toolkit", extra = ["cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "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", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" }, + { name = "setuptools" }, + { name = "sympy" }, + { name = "triton", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/b7/53fe0436586716ab7aecff41e26b9302d57c85ded481fd83a2cd741e6b4e/torch-2.12.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:1834bd984f8a2f4f16bdfbeecca9146184b220aa46276bf5756735b5dae12812", size = 87981887, upload-time = "2026-05-13T14:55:53.234Z" }, + { url = "https://files.pythonhosted.org/packages/34/60/d930eac44c30de06ed16f6d1ba4e785e1632532b50d8f0bf9bf699a4d0c7/torch-2.12.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:d4d029801cb7b6df858804a2a21b00cc2aa0bf0ee5d2ab18d343c9e9e5681f35", size = 426355000, upload-time = "2026-05-13T14:54:31.944Z" }, + { url = "https://files.pythonhosted.org/packages/8e/0c/c76b6a087820bab55705b94dfc074e520de9ae91f5ef90da2ecbf2a3ef12/torch-2.12.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:d47e7dee68ac4cd7a068b26bcd6b989935427709fae1c8f7bd0019978f829e15", size = 532144998, upload-time = "2026-05-13T14:56:05.523Z" }, + { url = "https://files.pythonhosted.org/packages/4a/64/8a0d036e166a6aa85ee09bef072f3655d1ba5d5486a68d1b03b6813c01b3/torch-2.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:cf9839790285dd472e7a16aafcb4a4e6bf58ec1b494045044b0eefb0eb4bd1f2", size = 122949877, upload-time = "2026-05-13T14:55:46.841Z" }, + { url = "https://files.pythonhosted.org/packages/18/62/131124fb95df03811b8260d1d43dcc5ee85ea1a344b964613d7efe77fb08/torch-2.12.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:10802fd383bbfed646212e765a72c37d2185205d4f26eb197a254e8ac7ddcb25", size = 87990344, upload-time = "2026-05-13T14:55:42.154Z" }, + { url = "https://files.pythonhosted.org/packages/12/9c/dda0dbd547dc549839824135f223792fd0e725f28ed0715dda366b7acaa2/torch-2.12.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:c12592630aef72feaf18bd3f197ef587bbfa21131b31c38b23ab2e55fce92e36", size = 426362932, upload-time = "2026-05-13T14:54:15.295Z" }, + { url = "https://files.pythonhosted.org/packages/e2/d2/a7dd5a3f9bdaa7842124e8e2359202b317c48d47d2fc5816fafdf2049adb/torch-2.12.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:415c1b8d0412f67551c8e89a2daca0fb3e56694af0281ba155eaa9da481f58b4", size = 532170085, upload-time = "2026-05-13T14:55:20.788Z" }, + { url = "https://files.pythonhosted.org/packages/12/1b/a61ce2004f9ab0ea8964a6e6168133a127795667639e2ff4f8f2bdb16a65/torch-2.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:dd37188ea325042cb1f6cafa56822b11ada2520c04791a52629b0af25bdfbfd9", size = 122953128, upload-time = "2026-05-13T14:54:52.744Z" }, + { url = "https://files.pythonhosted.org/packages/ef/bb/285d643f254731294c9b595a007eac39db4600a98682d7bca688f42ca164/torch-2.12.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:b41339df93d491435e790ff8bcbae1c0ce777175889bfd1281d119862793e6a2", size = 88010197, upload-time = "2026-05-13T14:55:35.414Z" }, + { url = "https://files.pythonhosted.org/packages/79/81/76debf1db1343bd929bbb5d74c89fb437c2ed88eb144712557e7bd3eea45/torch-2.12.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:8fbef9f108a863e7722a73740998967e3b074742a834fc5be3a535a2befa7057", size = 426376751, upload-time = "2026-05-13T14:55:03.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/f0/80026028b603c4650ff270fc3785bdef4bd6738765a9cc5a0f5a637d65a2/torch-2.12.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4b4f64c2c2b11f7510d93dd6412b87025ff6eddd6bb61c3b5a3d892ea20c4756", size = 532261691, upload-time = "2026-05-13T14:52:54.453Z" }, + { url = "https://files.pythonhosted.org/packages/b9/c2/64b06cbb7830fb3cd9be13e1158b31a3f36b68e6a209105ee3c9d9480be0/torch-2.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:8b958caff4a14d3a3b0b2dfc6a378f64dda9728a9dad28c08a0db9ce4dafb549", size = 122988114, upload-time = "2026-05-13T14:54:42.153Z" }, + { url = "https://files.pythonhosted.org/packages/86/ca/01896c80ba921676aa45886b2c5b8d774912de2a1f719de48169c6f755cd/torch-2.12.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:90dd587a5f61bfe1307148b581e2084fc5bc4a06e2b90a20e9a36b81087ff16b", size = 88009511, upload-time = "2026-05-13T14:54:47.411Z" }, + { url = "https://files.pythonhosted.org/packages/a5/04/52bdaf4787eab6ac7d7f5851dff934e4def0bc8ead9c8fd2b69b3e529699/torch-2.12.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:864392c73b7654f4d2b3ae712f607937d0dbb1101c4555fbb41848106b297f39", size = 426383231, upload-time = "2026-05-13T14:53:32.129Z" }, + { url = "https://files.pythonhosted.org/packages/49/8a/94bdecd13f5aaa90d45920b89789d9fe7c6f4af8c3cdd7ce01fcb59908fc/torch-2.12.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5d6b560dfa7d56291c07d615c3bb73e8d9943d9b6d87f76cd0d9d570c4797fa6", size = 532269288, upload-time = "2026-05-13T14:53:49.423Z" }, + { url = "https://files.pythonhosted.org/packages/3e/2f/bdbaaa267de519ef1b73054bf590d8c93c37a266c9a4e24a01bd38b6918f/torch-2.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:3fee918902090ade827643e758e98363278815de583c75d111fdd665ebffde9f", size = 122987706, upload-time = "2026-05-13T14:54:00.335Z" }, + { url = "https://files.pythonhosted.org/packages/9b/ad/e95e822f3538171e22640a7fbe839a1fdb666600bf6487025de2ff03b11a/torch-2.12.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:10ee1448a9f304d3b987eb4656f664ba6e4d7b410ca7a5a7c642199777a2cf88", size = 88319556, upload-time = "2026-05-13T14:54:05.574Z" }, + { url = "https://files.pythonhosted.org/packages/b7/07/055d06d985b445d67422d25b033c11cf55bbb81785d4c4e68e28bca5820e/torch-2.12.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:af68dbf403439cae9ceaeaaf92f8352b460787dcd27b92aa05c40dd4a19c0f1e", size = 426397656, upload-time = "2026-05-13T14:52:38.84Z" }, + { url = "https://files.pythonhosted.org/packages/43/94/b0b4fdc3014122e0a7302fb90086d352aa48f2576f0b252561ebb38c01a8/torch-2.12.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:a6a2eebb237d3b1d9ad3b378e86d9b9e0782afdea8b1e0eba6a13646b9b49c07", size = 532183124, upload-time = "2026-05-13T14:53:16.178Z" }, + { url = "https://files.pythonhosted.org/packages/d8/c8/052405e6ad05d3237bfe5a4df78f917773956f8e17813a2d44c059068b74/torch-2.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2140e373e9a51a3e22ef62e8d14366d0b470d18f0adf19fdc757368077133a34", size = 123232462, upload-time = "2026-05-13T14:52:27.26Z" }, + { url = "https://files.pythonhosted.org/packages/67/dc/ac069f8d6e8be701535921141055293b0d4819d3d7f224a4612cf157c7f9/torch-2.12.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:f7dfae4a519197dfa050e98d8e36378a0fb5899625a875c2b54445005a2e404e", size = 88027282, upload-time = "2026-05-13T14:53:05.258Z" }, + { url = "https://files.pythonhosted.org/packages/33/c3/1c1eb00e34555b536dddf792676026a988d710ed36981aa00499b36b0620/torch-2.12.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:891c769072637c74e9a5a77a3bc782894696d8ffec83b938df8536dee7f0ba78", size = 426386961, upload-time = "2026-05-13T14:51:28.406Z" }, + { url = "https://files.pythonhosted.org/packages/cd/d4/7e730dba0c7032a4154dc9056b76cf9625515e030e269cfbf8098fcfee7d/torch-2.12.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:e2ad3eb85d39c3cab62dfa93ed5a73516e6a53c6713cb97d004004fe089f0f1f", size = 532272265, upload-time = "2026-05-13T14:51:59.308Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b4/92c80d1bbfee1c0036c06d1d2155a3065bd2423134c83bf8a47e65cd6b9b/torch-2.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:c66696857e987efb8bc1777a37357ec4f60ab5e8af6250b83d6034437fa2d8f3", size = 122987138, upload-time = "2026-05-13T14:51:45.942Z" }, + { url = "https://files.pythonhosted.org/packages/7b/78/2e12b37ce50a19a037d7bc62d652a5a8f27385a7b05859d6bc9204f20cfe/torch-2.12.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:b4556715c8572758625d62b6e0ae3b1f76c440221913a6fb5e100f321fb4fb02", size = 88320100, upload-time = "2026-05-13T14:51:39.955Z" }, + { url = "https://files.pythonhosted.org/packages/56/5e/83c450ec7b0bb40a7b74611c1b5440f9260e33c54c90d556fd4a1f0fd955/torch-2.12.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a43ac605a5e13116c72b64c359644cce0229f213dde48d2ae0ae5eb5becf7feb", size = 426391871, upload-time = "2026-05-13T14:52:14.989Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e9/1a0b575d98d0afedd8f157d23fa3d2759421483660448e60d0a4b10b6daa/torch-2.12.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6a7512adfdd7f6732e40de1c620831e3c75b39b98cef60b11d0c5f0a76473ec5", size = 532192241, upload-time = "2026-05-13T14:51:07.795Z" }, + { url = "https://files.pythonhosted.org/packages/88/21/afadd25ecd81b3cea1e11c73cf1ab41a983a50271548c3ec7ec3b9efc3e9/torch-2.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:5f96b63f8287f66a005dd1b5a6abba2920f11156c5e5c4d815f3e2050fd1aa16", size = 123231092, upload-time = "2026-05-13T14:51:18.854Z" }, +] + [[package]] name = "tornado" version = "6.5.5" @@ -2912,6 +3227,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/da/98/a9937a969d018a23badfea0b381f66783649d48e0ea6c41923265c3cbeb3/traitlets-5.15.0-py3-none-any.whl", hash = "sha256:fb36a18867a6803deab09f3c5e0fa81bb7b26a5c9e82501c9933f759166eff40", size = 85877, upload-time = "2026-05-06T08:05:55.853Z" }, ] +[[package]] +name = "triton" +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/97/dcd1f2a0f8336691bff74abc59b2ed9c69a0c0f8f65cd77109c49e05f068/triton-3.7.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223ac302091491436c248a34ee1e6c47a1026486579103c906ffd805be50cb89", size = 188367104, upload-time = "2026-05-07T19:04:56.68Z" }, + { url = "https://files.pythonhosted.org/packages/b2/c0/c2ac4fd2d8809b7579d4a820a0f9e5de62a9bc8a757ed4b3abf4f7ee964a/triton-3.7.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c631b65668d4951213b948a413c0564184305b77bb45cc9d686d3e1ecc4701a3", size = 201313191, upload-time = "2026-05-07T18:45:58.444Z" }, + { url = "https://files.pythonhosted.org/packages/b8/c1/5d842314bb6c78442cc60437928781701c6050b8d479bc2a1aed691d37ca/triton-3.7.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a9e71fc392675fac364e0ecf4ef3f76f85b7f5433a16f4c3c5fe5f05a52c85fe", size = 188480277, upload-time = "2026-05-07T19:05:03.231Z" }, + { url = "https://files.pythonhosted.org/packages/13/31/8315ea5f8dd18e60970b3022e3a8b93fd37e0b784fbbef86e10c8e6e5ca1/triton-3.7.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22bacffce443f54593dd20f05294d5a40622e0ea9ab632816f87154504356221", size = 201415942, upload-time = "2026-05-07T18:46:06.479Z" }, + { url = "https://files.pythonhosted.org/packages/f7/13/ec05adfcd87311d532ba61e3af143e8be59fcd26675884c4682841406a20/triton-3.7.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4bf49b00a7a377a68a6da603a876e797614e6455a80e9021669c476a953ad9a", size = 188505104, upload-time = "2026-05-07T19:05:09.843Z" }, + { url = "https://files.pythonhosted.org/packages/62/7b/468a576e35beef1426e0828e28e9ba9e65f5474d496f16ee126c15646324/triton-3.7.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f111161d49bf903c0eaedde3962353a3d841c08a836839b7cc1025b8426efcf", size = 201457567, upload-time = "2026-05-07T18:46:13.505Z" }, + { url = "https://files.pythonhosted.org/packages/01/e1/a59a583de59b8f62c495d67c80ee3ea97d09e91ac80c4c6e76456ed8d8ac/triton-3.7.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:abdf6beaa89b1bcfb9a43cd990536ce66091a997841a4814b260b7bee4c88c3c", size = 188503209, upload-time = "2026-05-07T19:05:17.935Z" }, + { url = "https://files.pythonhosted.org/packages/30/b1/b7507bb9815d403927c8dd51d4158ed2e11751a92dbc118a044f247b6848/triton-3.7.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a35d7afe3f3f058e7ec49fcce09794049e0ffc5c59019ac25ec3413741b8c4e7", size = 201453566, upload-time = "2026-05-07T18:46:20.427Z" }, + { url = "https://files.pythonhosted.org/packages/a6/8f/0bea7a6a0c989315c9135a1d7fb37e41905cfb3a17cbc1f10044ebd4cc3a/triton-3.7.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc1d61c172d257db80ddf42595131fb196ad2e9bdd751e90fe2ef13531734e8b", size = 188612899, upload-time = "2026-05-07T19:05:24.955Z" }, + { url = "https://files.pythonhosted.org/packages/e1/02/d96f57828d0912aec733b9bc7e0e7dbfd2c6f079a8fa433ac25cb93d1a30/triton-3.7.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:70fb9bbdc9f400afc54bbf6eb2670af28829a6ae3996863317964783141daf56", size = 201553816, upload-time = "2026-05-07T18:46:27.49Z" }, + { url = "https://files.pythonhosted.org/packages/40/fb/82a802dac4689f2a2fb2e69302e6a138eecc3e175bbe976ba3cfc717683a/triton-3.7.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a44a8476d0d3571eac4e4d1048e1ff75aad81a09ff4602ccfc56c6dea1672e", size = 188507879, upload-time = "2026-05-07T19:05:32.209Z" }, + { url = "https://files.pythonhosted.org/packages/8f/af/9904ec6d3c93d9b24e5ec360445bbdf758b7f00bfbeedb89cb0eb64eb8bb/triton-3.7.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b9b85e72968a9d8bba5ddb24e9b64aaabaf48affb042f2755cb7cfa92b7531ce", size = 201460637, upload-time = "2026-05-07T18:46:34.749Z" }, + { url = "https://files.pythonhosted.org/packages/a1/f9/4835a8ea746b88727d8899f4e3ccce4f9cacb38abfc3bb0a638266c53111/triton-3.7.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18a160de426fd99f92b0baf509045360afbd3bfaa0b4a5171dde800ec9f09684", size = 188608706, upload-time = "2026-05-07T19:05:39.218Z" }, + { url = "https://files.pythonhosted.org/packages/c1/68/fa86e5a39608000f645535b2c124920126327ab731f8c4fafd5b07ff8d4b/triton-3.7.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce061073102714b725f3660ec6939d94a1da7984b3aa99c921417cae273672f5", size = 201546766, upload-time = "2026-05-07T18:46:42.088Z" }, +] + [[package]] name = "ty" version = "0.0.24"