diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 5d39e3d..57ea95d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -23,7 +23,7 @@ jobs: - uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: "3.10" - name: Install documentation dependencies run: uv sync --group docs diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6808c2a..572644a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -38,7 +38,7 @@ jobs: - uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: "3.10" - name: Install core + sr + md17 + dev extras run: uv sync --extra sr --extra md17 --extra dev @@ -62,7 +62,7 @@ jobs: - uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: "3.10" - name: Install all task extras + dev run: uv sync --extra sr --extra md17 --extra lqa --extra dev @@ -88,7 +88,7 @@ jobs: - uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: "3.10" - name: Install all task extras + dev run: uv sync --extra sr --extra md17 --extra lqa --extra dev @@ -114,7 +114,7 @@ jobs: - uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: "3.10" - name: Install all task extras + dev run: uv sync --extra sr --extra md17 --extra lqa --extra dev diff --git a/README.md b/README.md index e38e3eb..147bc34 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Versor: A PyTorch Framework for Geometric Algebra Deep Learning -[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) [![Python](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/) [![PyTorch](https://img.shields.io/badge/PyTorch-2.0+-ee4c2c.svg)](https://pytorch.org/) [![Docs](https://img.shields.io/badge/docs-MkDocs-brightgreen)](https://concode0.github.io/Versor/) [![DOI](https://zenodo.org/badge/1149480519.svg)](https://doi.org/10.5281/zenodo.18939518) +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) [![Python](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/) [![PyTorch](https://img.shields.io/badge/PyTorch-2.0+-ee4c2c.svg)](https://pytorch.org/) [![Docs](https://img.shields.io/badge/docs-MkDocs-brightgreen)](https://concode0.github.io/Versor/) [![DOI](https://zenodo.org/badge/1149480519.svg)](https://doi.org/10.5281/zenodo.18939518) > **"There is a ceiling above standard Deep Learning that no one saw. Versor opens the door above it."** @@ -59,7 +59,7 @@ For code examples of each innovation, see [docs/innovations.md](docs/innovations ## Installation -Versor requires Python 3.9+ and PyTorch. +Versor requires Python 3.10+ and PyTorch. ```bash # Clone the repository @@ -83,7 +83,7 @@ uv sync --extra all # everything ```python import torch -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra from layers.primitives.rotor import RotorLayer from layers.linear import CliffordLinear from functional.activation import GeometricGELU @@ -361,4 +361,4 @@ By releasing this under Apache 2.0, we provide a **perpetual, royalty-free paten ``` ## Reference: - * Pence, T., Yamada, D., & Singh, V. (2025). "Composing Linear Layers from Irreducibles." arXiv:2507.11688v1 [cs.LG] \ No newline at end of file + * Pence, T., Yamada, D., & Singh, V. (2025). "Composing Linear Layers from Irreducibles." arXiv:2507.11688v1 [cs.LG] diff --git a/__init__.py b/__init__.py index d520a36..abe7e8b 100644 --- a/__init__.py +++ b/__init__.py @@ -2,14 +2,20 @@ __version__ = "1.0.0" -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.config import AlgebraConfig, make_algebra, make_algebra_from_config +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra +from core.runtime.context import AlgebraContext from layers import CliffordLinear, RotorLayer __all__ = [ "__version__", + "AlgebraConfig", + "AlgebraContext", "CliffordAlgebra", "CliffordModule", + "make_algebra", + "make_algebra_from_config", "RotorLayer", "CliffordLinear", ] diff --git a/benchmarks/benchmark_core.py b/benchmarks/benchmark_core.py index eb00aa1..07f059b 100644 --- a/benchmarks/benchmark_core.py +++ b/benchmarks/benchmark_core.py @@ -55,16 +55,13 @@ if str(_REPO_ROOT) not in sys.path: sys.path.insert(0, str(_REPO_ROOT)) -from core.algebra import CliffordAlgebra -from core.decomposition import ExpPolicy, compiled_safe_decomposed_exp # noqa: E402 -from core.device import resolve_device +from core.config import make_algebra +from core.runtime.decomposition import ExpPolicy, compiled_safe_decomposed_exp # noqa: E402 +from core.foundation.device import FLOAT_DTYPES, resolve_device +from core.foundation.device import dtype_name as _format_dtype_name +from core.foundation.module import AlgebraLike -DTYPES: dict[str, torch.dtype] = { - "float64": torch.float64, - "float32": torch.float32, - "bfloat16": torch.bfloat16, - "float16": torch.float16, -} +DTYPES: dict[str, torch.dtype] = FLOAT_DTYPES @dataclass(frozen=True) @@ -97,7 +94,7 @@ def label(self) -> str: class CoreOpModule(nn.Module): """Small wrapper so core operators can be passed to torch.compile.""" - def __init__(self, algebra: CliffordAlgebra, op: str): + def __init__(self, algebra: AlgebraLike, op: str): super().__init__() self.algebra = algebra self.op = op @@ -160,20 +157,42 @@ def _parse_signature_csv(value: str) -> list[SignatureSpec]: raise ValueError( f"invalid signature {raw!r}; use n, p:q, or p:q:r entries" ) - if p < 0 or q < 0 or r < 0 or p + q + r < 1 or p + q + r > 12: + if p < 0 or q < 0 or r < 0 or p + q + r < 1 or p + q + r > 16: raise ValueError( f"invalid signature {raw!r}; dimensions must be non-negative " - "and sum to 1..12" + "and sum to 1..16" ) specs.append(SignatureSpec(p, q, r)) return specs def _dtype_name(dtype: torch.dtype) -> str: - for name, candidate in DTYPES.items(): - if candidate == dtype: - return name - return str(dtype).replace("torch.", "") + return _format_dtype_name(dtype) + + +def setup_algebra( + p: int, + q: int = 0, + r: int = 0, + *, + device: str, + dtype: torch.dtype, + exp_policy: str | ExpPolicy = "balanced", + fixed_iterations: int | None = None, + args: argparse.Namespace | None = None, +) -> AlgebraLike: + """Construct benchmark algebras through the shared core factory.""" + return make_algebra( + p=p, + q=q, + r=r, + kernel=getattr(args, "algebra_kernel", "auto"), + dense_threshold=getattr(args, "dense_threshold", 8), + device=device, + dtype=dtype, + exp_policy=exp_policy, + fixed_iterations=fixed_iterations, + ) def _ordered_modes(value: str) -> list[str]: @@ -364,13 +383,13 @@ def _seed_all(seed: int, device: str) -> None: def _supported_dtypes( - device: str, + args: argparse.Namespace, requested: str, probe_n_values: Iterable[int], ) -> list[torch.dtype]: if requested == "auto": candidates = ["float64", "float32"] - if device.startswith("cuda") or device == "mps": + if args.device.startswith("cuda") or args.device == "mps": candidates += ["bfloat16", "float16"] else: candidates = _parse_csv(requested) @@ -383,15 +402,15 @@ def _supported_dtypes( if name not in DTYPES: raise ValueError(f"unknown dtype {name!r}; valid: {sorted(DTYPES)}") dtype = DTYPES[name] - algebra: CliffordAlgebra | None = None + algebra: AlgebraLike | None = None x: torch.Tensor | None = None y: torch.Tensor | None = None try: for n in probe_ns: - algebra = CliffordAlgebra(n, 0, device=device, dtype=dtype) - x = torch.randn(2, algebra.dim, device=device, dtype=dtype) + algebra = setup_algebra(n, 0, device=args.device, dtype=dtype, args=args) + x = torch.randn(2, algebra.dim, device=args.device, dtype=dtype) y = algebra.geometric_product(x, x) - _sync(device) + _sync(args.device) if not torch.isfinite(y.float()).all().item(): raise RuntimeError(f"n={n} probe produced non-finite values") algebra = None @@ -399,14 +418,14 @@ def _supported_dtypes( y = None supported.append(dtype) except Exception as exc: - print(f"Skipping dtype {name} on {device}: {exc}") + print(f"Skipping dtype {name} on {args.device}: {exc}") finally: algebra = None x = None y = None - _release_memory(device) + _release_memory(args.device) if not supported: - raise SystemExit(f"No requested dtypes are usable on {device}.") + raise SystemExit(f"No requested dtypes are usable on {args.device}.") return supported @@ -532,7 +551,7 @@ def _nonzero_terms(tensor: torch.Tensor) -> int: def _analytic_forward_metrics( - algebra: CliffordAlgebra, + algebra: AlgebraLike, op: str, batch: int, channels: int, @@ -544,16 +563,20 @@ def _analytic_forward_metrics( io_elements = 0 if op == "gp": - flops = 2.0 * batch * _nonzero_terms(algebra.gp_signs) + signs = getattr(algebra, "gp_signs", None) + flops = 2.0 * batch * _nonzero_terms(signs) if signs is not None else float("nan") io_elements = 3 * batch * dim elif op == "wedge": - flops = 2.0 * batch * _nonzero_terms(algebra.wedge_gp_signs) + signs = getattr(algebra, "wedge_gp_signs", None) + flops = 2.0 * batch * _nonzero_terms(signs) if signs is not None else float("nan") io_elements = 3 * batch * dim elif op == "inner": - flops = 2.0 * batch * _nonzero_terms(algebra.inner_gp_signs) + signs = getattr(algebra, "inner_gp_signs", None) + flops = 2.0 * batch * _nonzero_terms(signs) if signs is not None else float("nan") io_elements = 3 * batch * dim elif op == "commutator": - flops = 2.0 * batch * _nonzero_terms(algebra.comm_gp_signs) + signs = getattr(algebra, "comm_gp_signs", None) + flops = 2.0 * batch * _nonzero_terms(signs) if signs is not None else float("nan") io_elements = 3 * batch * dim elif op in {"grade2", "reverse"}: flops = float(batch * dim) @@ -1044,7 +1067,7 @@ def _commuting_pairs(n: int) -> list[tuple[int, int]]: def _make_commuting_nonsimple_bivector( - algebra: CliffordAlgebra, + algebra: AlgebraLike, batch: int, scale: float, ) -> torch.Tensor: @@ -1065,7 +1088,7 @@ def _make_commuting_nonsimple_bivector( return b -def _exact_commuting_exp(algebra: CliffordAlgebra, b: torch.Tensor) -> torch.Tensor: +def _exact_commuting_exp(algebra: AlgebraLike, b: torch.Tensor) -> torch.Tensor: """Exact exp for the controlled non-simple bivector used here. The bivector is a sum of disjoint coordinate-plane bivectors. Those @@ -1081,7 +1104,7 @@ def _exact_commuting_exp(algebra: CliffordAlgebra, b: torch.Tensor) -> torch.Ten return result -def _grade_leak(algebra: CliffordAlgebra, x: torch.Tensor, grade: int) -> float: +def _grade_leak(algebra: AlgebraLike, x: torch.Tensor, grade: int) -> float: mask = algebra.grade_masks_float[grade] if mask.dtype != x.dtype: mask = mask.to(dtype=x.dtype) @@ -1090,7 +1113,7 @@ def _grade_leak(algebra: CliffordAlgebra, x: torch.Tensor, grade: int) -> float: def _make_speed_inputs( - algebra: CliffordAlgebra, + algebra: AlgebraLike, op: str, batch: int, channels: int, @@ -1120,7 +1143,7 @@ def _make_speed_inputs( def _random_multivector( - algebra: CliffordAlgebra, + algebra: AlgebraLike, shape: tuple[int, ...], scale: float, ) -> torch.Tensor: @@ -1131,7 +1154,7 @@ def _random_multivector( def _random_grade( - algebra: CliffordAlgebra, + algebra: AlgebraLike, shape: tuple[int, ...], grade: int, scale: float, @@ -1164,13 +1187,14 @@ def run_stability_suite(args: argparse.Namespace, dtypes: list[torch.dtype]) -> for sig_index, signature in enumerate(signatures): _release_memory(args.device) try: - algebra = CliffordAlgebra( + algebra = setup_algebra( signature.p, signature.q, signature.r, device=args.device, dtype=dtype, exp_policy=ExpPolicy.PRECISE, + args=args, ) except Exception as exc: _record_stability_failure(rows, args, dtype_name, signature, "setup", exc) @@ -1368,13 +1392,14 @@ def per_channel_sandwich() -> tuple[float, float, str]: def exp_policy_consistency() -> tuple[float, float, str]: if algebra.n < 2: return 0.0, 0.0, "skipped: no bivectors" - balanced = CliffordAlgebra( + balanced = setup_algebra( signature.p, signature.q, signature.r, device=args.device, dtype=dtype, exp_policy=ExpPolicy.BALANCED, + args=args, ) with torch.no_grad(): b = torch.zeros( @@ -1569,12 +1594,13 @@ def run_speed_suite(args: argparse.Namespace, dtypes: list[torch.dtype]) -> list try: with _tf32_context(args.device, tf32_mode): row.update(_current_tf32_flags(args.device, tf32_mode)) - algebra = CliffordAlgebra( + algebra = setup_algebra( n, 0, device=args.device, dtype=dtype, exp_policy=_op_exp_policy(op), + args=args, ) row.update( _analytic_forward_metrics( @@ -1740,12 +1766,13 @@ def run_backward_suite(args: argparse.Namespace, dtypes: list[torch.dtype]) -> l try: with _tf32_context(args.device, tf32_mode): row.update(_current_tf32_flags(args.device, tf32_mode)) - algebra = CliffordAlgebra( + algebra = setup_algebra( n, 0, device=args.device, dtype=dtype, exp_policy=_op_exp_policy(op), + args=args, ) row.update( _analytic_forward_metrics( @@ -1920,12 +1947,13 @@ def run_fusion_suite( try: with _tf32_context(args.device, tf32_mode): row.update(_current_tf32_flags(args.device, tf32_mode)) - algebra = CliffordAlgebra( + algebra = setup_algebra( n, 0, device=args.device, dtype=dtype, exp_policy=_op_exp_policy(op), + args=args, ) row.update( _analytic_forward_metrics( @@ -2070,7 +2098,7 @@ def run_nonsimple_suite(args: argparse.Namespace, dtypes: list[torch.dtype]) -> for n in args.nonsimple_n_values: if n < 4: continue - ref_alg = CliffordAlgebra(n, 0, device="cpu", dtype=torch.float64, exp_policy="precise") + ref_alg = setup_algebra(n, 0, device="cpu", dtype=torch.float64, exp_policy="precise", args=args) b_ref = _make_commuting_nonsimple_bivector(ref_alg, args.error_batch, args.bivector_scale) r_ref = _exact_commuting_exp(ref_alg, b_ref) bb_ref = ref_alg.geometric_product(b_ref, b_ref) @@ -2094,12 +2122,13 @@ def run_nonsimple_suite(args: argparse.Namespace, dtypes: list[torch.dtype]) -> "nonscalar_BB_norm": nonscalar_norm, } try: - algebra = CliffordAlgebra( + algebra = setup_algebra( n, 0, device=args.device, dtype=dtype, exp_policy=policy, + args=args, ) b = _make_commuting_nonsimple_bivector( algebra, @@ -2169,7 +2198,7 @@ def run_cumulative_suite(args: argparse.Namespace, dtypes: list[torch.dtype]) -> if n < 4: continue for batch in args.cumulative_batch_sizes: - ref_alg = CliffordAlgebra(n, 0, device="cpu", dtype=torch.float64, exp_policy="precise") + ref_alg = setup_algebra(n, 0, device="cpu", dtype=torch.float64, exp_policy="precise", args=args) b_ref = _make_commuting_nonsimple_bivector( ref_alg, batch, @@ -2182,7 +2211,14 @@ def run_cumulative_suite(args: argparse.Namespace, dtypes: list[torch.dtype]) -> dtype_name = _dtype_name(dtype) _release_memory(args.device) try: - algebra = CliffordAlgebra(n, 0, device=args.device, dtype=dtype, exp_policy="balanced") + algebra = setup_algebra( + n, + 0, + device=args.device, + dtype=dtype, + exp_policy="balanced", + args=args, + ) b = _make_commuting_nonsimple_bivector( algebra, batch, @@ -2276,7 +2312,7 @@ def run_convergence_suite(args: argparse.Namespace, dtypes: list[torch.dtype]) - for n in args.convergence_n_values: if n < 4: continue - ref_alg = CliffordAlgebra(n, 0, device="cpu", dtype=torch.float64, exp_policy="precise") + ref_alg = setup_algebra(n, 0, device="cpu", dtype=torch.float64, exp_policy="precise", args=args) b_ref = _make_commuting_nonsimple_bivector(ref_alg, args.error_batch, args.bivector_scale) r_ref = _exact_commuting_exp(ref_alg, b_ref) @@ -2296,7 +2332,14 @@ def run_convergence_suite(args: argparse.Namespace, dtypes: list[torch.dtype]) - "error": "", } try: - algebra = CliffordAlgebra(n, 0, device=args.device, dtype=dtype, exp_policy="precise") + algebra = setup_algebra( + n, + 0, + device=args.device, + dtype=dtype, + exp_policy="precise", + args=args, + ) b = _make_commuting_nonsimple_bivector( algebra, args.error_batch, @@ -2307,7 +2350,7 @@ def run_convergence_suite(args: argparse.Namespace, dtypes: list[torch.dtype]) - def exp_fixed( inp: torch.Tensor, - alg: CliffordAlgebra = algebra, + alg: AlgebraLike = algebra, iterations: int = fixed_iterations, ) -> torch.Tensor: return compiled_safe_decomposed_exp( @@ -3441,6 +3484,8 @@ def _collect_runtime_metadata(device: str) -> dict[str, Any]: def make_argparser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description="Benchmark the Versor core package.") parser.add_argument("--device", default="auto", help="cpu, cuda, mps, or auto") + parser.add_argument("--algebra-kernel", default="auto", choices=("auto", "dense", "context")) + parser.add_argument("--dense-threshold", type=int, default=8) parser.add_argument("--out", default="benchmarks/results", help="artifact root") parser.add_argument("--sections", default="speed,backward,fusion,nonsimple,cumulative,convergence,stability") parser.add_argument("--n-values", type=_parse_int_csv, default=_parse_int_csv("2,3,4,5,6")) @@ -3594,7 +3639,7 @@ def main() -> None: else: args.dtype_probe_n_values = sorted(set(args.dtype_probe_n_values)) - dtypes = _supported_dtypes(args.device, args.dtypes, args.dtype_probe_n_values) + dtypes = _supported_dtypes(args, args.dtypes, args.dtype_probe_n_values) args.dtypes_resolved = dtypes timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") diff --git a/benchmarks/benchmark_framework_pipeline.py b/benchmarks/benchmark_framework_pipeline.py new file mode 100644 index 0000000..3b54e26 --- /dev/null +++ b/benchmarks/benchmark_framework_pipeline.py @@ -0,0 +1,348 @@ +#!/usr/bin/env python3 +"""Benchmark framework-level dense, compact, pairwise, and layer pipeline paths. + +The core benchmark suite measures many algebra kernels in detail. This script is +smaller and pipeline-oriented: it times the execution paths users hit when they +compose product layers, functional products, compact layouts, and planned +contexts. + +Examples: + uv run python benchmarks/benchmark_framework_pipeline.py --quick + uv run python benchmarks/benchmark_framework_pipeline.py --device cpu --n 8 --batch-size 1024 +""" + +from __future__ import annotations + +import argparse +import csv +import statistics +import sys +import time +from dataclasses import dataclass +from datetime import datetime +from pathlib import Path +from typing import Callable + +import torch +import torch.nn as nn + +_REPO_ROOT = Path(__file__).resolve().parent.parent +if str(_REPO_ROOT) not in sys.path: + sys.path.insert(0, str(_REPO_ROOT)) + +from core.foundation.device import FLOAT_DTYPES, resolve_device # noqa: E402 +from core.runtime.algebra import CliffordAlgebra # noqa: E402 +from core.runtime.context import AlgebraContext # noqa: E402 +from layers import ProductLayer, WedgeLayer # noqa: E402 + + +@dataclass(frozen=True) +class BenchmarkCase: + name: str + host: str + n: int + op: str + storage: str + batch_size: int + left_lanes: int + right_lanes: int + output_lanes: int + pairwise: bool + fn: Callable[[], torch.Tensor] + + +def _sync(device: str) -> None: + if device == "mps": + torch.mps.synchronize() + elif device.startswith("cuda"): + torch.cuda.synchronize() + + +def _time_case(case: BenchmarkCase, *, warmups: int, repeats: int, device: str) -> dict: + with torch.no_grad(): + start = time.perf_counter() + first_output = case.fn() + _sync(device) + first_ms = (time.perf_counter() - start) * 1000.0 + + for _ in range(warmups): + case.fn() + _sync(device) + + samples = [] + for _ in range(repeats): + start = time.perf_counter() + output = case.fn() + _sync(device) + samples.append((time.perf_counter() - start) * 1000.0) + + output_lanes = int(first_output.shape[-1]) + if output_lanes != case.output_lanes: + raise RuntimeError(f"{case.name} expected {case.output_lanes} output lanes, got {output_lanes}") + + median_ms = statistics.median(samples) + mean_ms = statistics.fmean(samples) + return { + "name": case.name, + "host": case.host, + "n": case.n, + "op": case.op, + "storage": case.storage, + "batch_size": case.batch_size, + "left_lanes": case.left_lanes, + "right_lanes": case.right_lanes, + "output_lanes": case.output_lanes, + "pairwise": case.pairwise, + "first_call_ms": first_ms, + "median_ms": median_ms, + "mean_ms": mean_ms, + "min_ms": min(samples), + "max_ms": max(samples), + "repeats": repeats, + "items_per_sec": case.batch_size * 1000.0 / median_ms if median_ms > 0 else float("inf"), + } + + +def _dense_product_case(args, dtype: torch.dtype, device: str) -> BenchmarkCase: + algebra = CliffordAlgebra(args.n, 0, device=device, dtype=dtype) + left = algebra.embed_vector(torch.randn(args.batch_size, algebra.n, device=device, dtype=dtype)) + right = algebra.embed_vector(torch.randn(args.batch_size, algebra.n, device=device, dtype=dtype)) + + return BenchmarkCase( + name="dense_full_gp_1x1", + host="CliffordAlgebra", + n=algebra.n, + op="gp", + storage="dense", + batch_size=args.batch_size, + left_lanes=algebra.dim, + right_lanes=algebra.dim, + output_lanes=algebra.dim, + pairwise=False, + fn=lambda: algebra.geometric_product(left, right), + ) + + +def _compact_product_case(args, dtype: torch.dtype, device: str) -> BenchmarkCase: + algebra = CliffordAlgebra(args.n, 0, device=device, dtype=dtype) + layout_1 = algebra.layout((1,)) + layout_02 = algebra.layout((0, 2)) + left = layout_1.compact(algebra.embed_vector(torch.randn(args.batch_size, algebra.n, device=device, dtype=dtype))) + right = layout_1.compact(algebra.embed_vector(torch.randn(args.batch_size, algebra.n, device=device, dtype=dtype))) + + layer = ProductLayer( + algebra, + left_grades=(1,), + right_grades=(1,), + output_grades=(0, 2), + left_compact=True, + right_compact=True, + compact_output=True, + ) + + return BenchmarkCase( + name="compact_layer_gp_1x1_to_02", + host="CliffordAlgebra", + n=algebra.n, + op="gp", + storage="compact", + batch_size=args.batch_size, + left_lanes=layout_1.dim, + right_lanes=layout_1.dim, + output_lanes=layout_02.dim, + pairwise=False, + fn=lambda: layer(left, right), + ) + + +def _context_compact_case(args, dtype: torch.dtype, device: str) -> BenchmarkCase: + context = AlgebraContext(args.context_n, 0, device=device, dtype=dtype) + layout_1 = context.layout((1,)) + layout_02 = context.layout((0, 2)) + left = torch.randn(args.batch_size, layout_1.dim, device=device, dtype=dtype) + right = torch.randn(args.batch_size, layout_1.dim, device=device, dtype=dtype) + + return BenchmarkCase( + name="context_compact_gp_1x1_to_02", + host="AlgebraContext", + n=context.n, + op="gp", + storage="compact", + batch_size=args.batch_size, + left_lanes=layout_1.dim, + right_lanes=layout_1.dim, + output_lanes=layout_02.dim, + pairwise=False, + fn=lambda: context.geometric_product( + left, + right, + left_grades=(1,), + right_grades=(1,), + output_grades=(0, 2), + left_compact=True, + right_compact=True, + compact_output=True, + ), + ) + + +def _pairwise_context_case(args, dtype: torch.dtype, device: str) -> BenchmarkCase: + context = AlgebraContext(args.context_n, 0, device=device, dtype=dtype) + layout_2 = context.layout((2,)) + layout_1 = context.layout((1,)) + layout_3 = context.layout((3,)) + left = torch.randn(args.batch_size, args.left_items, layout_2.dim, device=device, dtype=dtype) + right = torch.randn(args.batch_size, args.right_items, layout_1.dim, device=device, dtype=dtype) + + return BenchmarkCase( + name="context_pairwise_wedge_2x1_to_3", + host="AlgebraContext", + n=context.n, + op="wedge", + storage="compact_pairwise", + batch_size=args.batch_size * args.left_items * args.right_items, + left_lanes=layout_2.dim, + right_lanes=layout_1.dim, + output_lanes=layout_3.dim, + pairwise=True, + fn=lambda: context.wedge( + left, + right, + left_grades=(2,), + right_grades=(1,), + output_grades=(3,), + left_compact=True, + right_compact=True, + compact_output=True, + pairwise=True, + ), + ) + + +def _layer_pipeline_case(args, dtype: torch.dtype, device: str) -> BenchmarkCase: + context = AlgebraContext(args.context_n, 0, device=device, dtype=dtype) + layout_1 = context.layout((1,)) + layout_3 = context.layout((3,)) + + class Pipeline(nn.Module): + def __init__(self): + super().__init__() + self.wedge_vectors = WedgeLayer( + context, + left_grades=(1,), + right_grades=(1,), + output_grades=(2,), + compact_output=True, + ) + self.wedge_trivector = WedgeLayer( + context, + left_grades=(2,), + right_grades=(1,), + output_grades=(3,), + left_compact=True, + right_compact=True, + compact_output=True, + ) + + def forward(self, left, right, third): + bivector = self.wedge_vectors(left, right) + return self.wedge_trivector(bivector, third) + + model = Pipeline().to(device=device) + left = context.embed_vector(torch.randn(args.batch_size, context.n, device=device, dtype=dtype)) + right = context.embed_vector(torch.randn(args.batch_size, context.n, device=device, dtype=dtype)) + third = layout_1.compact(context.embed_vector(torch.randn(args.batch_size, context.n, device=device, dtype=dtype))) + + return BenchmarkCase( + name="layer_pipeline_wedge_wedge", + host="AlgebraContext", + n=context.n, + op="wedge_pipeline", + storage="dense_to_compact", + batch_size=args.batch_size, + left_lanes=context.dim, + right_lanes=layout_1.dim, + output_lanes=layout_3.dim, + pairwise=False, + fn=lambda: model(left, right, third), + ) + + +def _write_results(rows: list[dict], output_dir: Path) -> None: + output_dir.mkdir(parents=True, exist_ok=True) + csv_path = output_dir / "framework_pipeline.csv" + with csv_path.open("w", newline="") as handle: + writer = csv.DictWriter(handle, fieldnames=list(rows[0])) + writer.writeheader() + writer.writerows(rows) + + summary_path = output_dir / "summary.md" + with summary_path.open("w") as handle: + handle.write("# Framework Pipeline Benchmark\n\n") + handle.write("| case | host | storage | n | median ms | items/sec |\n") + handle.write("| --- | --- | --- | ---: | ---: | ---: |\n") + for row in rows: + handle.write( + f"| {row['name']} | {row['host']} | {row['storage']} | {row['n']} | " + f"{row['median_ms']:.4f} | {row['items_per_sec']:.2f} |\n" + ) + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--device", default="cpu", help="cpu, cuda, cuda:0, mps, or auto") + parser.add_argument("--dtype", default="float32", choices=sorted(FLOAT_DTYPES)) + parser.add_argument("--n", type=int, default=6, help="Dense algebra dimension for dense-vs-compact comparison") + parser.add_argument("--context-n", type=int, default=12, help="Planned context dimension") + parser.add_argument("--batch-size", type=int, default=512) + parser.add_argument("--left-items", type=int, default=16) + parser.add_argument("--right-items", type=int, default=16) + parser.add_argument("--warmups", type=int, default=5) + parser.add_argument("--repeats", type=int, default=20) + parser.add_argument("--quick", action="store_true", help="Use smaller dimensions and repeat counts") + parser.add_argument("--no-save", action="store_true", help="Print rows without writing benchmark artifacts") + parser.add_argument("--output-dir", type=Path, default=None) + return parser.parse_args() + + +def main() -> None: + args = parse_args() + if args.quick: + args.n = min(args.n, 5) + args.context_n = min(args.context_n, 8) + args.batch_size = min(args.batch_size, 64) + args.left_items = min(args.left_items, 8) + args.right_items = min(args.right_items, 8) + args.warmups = min(args.warmups, 2) + args.repeats = min(args.repeats, 5) + + device = resolve_device(args.device) if args.device == "auto" else args.device + dtype = FLOAT_DTYPES[args.dtype] + torch.manual_seed(2026) + + cases = [ + _dense_product_case(args, dtype, device), + _compact_product_case(args, dtype, device), + _context_compact_case(args, dtype, device), + _pairwise_context_case(args, dtype, device), + _layer_pipeline_case(args, dtype, device), + ] + rows = [_time_case(case, warmups=args.warmups, repeats=args.repeats, device=device) for case in cases] + + for row in rows: + print( + f"{row['name']}: median={row['median_ms']:.4f} ms, " + f"first={row['first_call_ms']:.4f} ms, items/sec={row['items_per_sec']:.2f}" + ) + + if not args.no_save: + output_dir = args.output_dir + if output_dir is None: + stamp = datetime.now().strftime("%Y%m%d_%H%M%S") + output_dir = _REPO_ROOT / "benchmarks" / "results" / f"framework_pipeline_{stamp}" + _write_results(rows, output_dir) + print(f"Wrote benchmark artifacts to {output_dir}") + + +if __name__ == "__main__": + main() diff --git a/conf/config.yaml b/conf/config.yaml index 6bf8d7d..919f14c 100644 --- a/conf/config.yaml +++ b/conf/config.yaml @@ -6,7 +6,12 @@ algebra: p: 3 q: 0 r: 0 + kernel: "auto" # auto: dense for n<=dense_threshold, AlgebraContext above it + dense_threshold: 8 device: "auto" # Auto-detect: cuda > mps > cpu + dtype: "float32" + exp_policy: "balanced" + default_grades: null # null = full layout fallback when no specific layout is declared training: epochs: 100 @@ -17,11 +22,15 @@ training: momentum: 0.9 betas: [0.9, 0.999] max_bivector_norm: 10.0 + scheduler: + factor: 0.5 + patience: 10 num_workers: 2 # DataLoader workers (0 to disable multiprocessing) pin_memory: null # null = auto (true for CUDA) compile: false # torch.compile() wrapper compile_backend: null # null=auto (aot_eager on MPS, inductor on CUDA/CPU) amp: false # Automatic mixed precision (CUDA only) + amp_dtype: null # null=PyTorch autocast default; or "float16"/"bfloat16" cudnn_benchmark: null # null = auto (true for CUDA) checkpoint: null diff --git a/conf/task/deap_eeg.yaml b/conf/task/deap_eeg.yaml index a7b10e2..80e6079 100644 --- a/conf/task/deap_eeg.yaml +++ b/conf/task/deap_eeg.yaml @@ -5,7 +5,6 @@ algebra: p: 3 q: 1 r: 0 - device: "auto" dataset: name: "deap" @@ -29,5 +28,3 @@ training: epochs: 100 lr: 0.0005 batch_size: 32 - optimizer_type: "riemannian_adam" - max_bivector_norm: 10.0 diff --git a/conf/task/lqa.yaml b/conf/task/lqa.yaml index 6f2d057..a4c83ff 100644 --- a/conf/task/lqa.yaml +++ b/conf/task/lqa.yaml @@ -8,7 +8,6 @@ algebra: p: 4 q: 1 r: 0 - device: auto model: channels: 16 @@ -33,7 +32,6 @@ training: epochs: 50 lr: 0.001 batch_size: 64 - optimizer_type: riemannian_adam # Auxiliary loss weights isometry_weight: 0.01 asymmetry_weight: 0.1 diff --git a/conf/task/md17.yaml b/conf/task/md17.yaml index c68d29b..f65dc6e 100644 --- a/conf/task/md17.yaml +++ b/conf/task/md17.yaml @@ -4,7 +4,6 @@ algebra: p: 3 q: 0 r: 1 - device: "auto" dataset: name: "rmd17" molecule: "ethanol" @@ -20,7 +19,6 @@ model: max_z: 100 num_rbf: 20 rbf_cutoff: 5.0 - exp_policy: "balanced" use_rotor_backend: true use_geo_square: true use_checkpoint: false @@ -28,7 +26,6 @@ training: epochs: 500 lr: 0.0015 batch_size: 96 - optimizer_type: "riemannian_adam" loss_weights: energy: 0.1 force: 100.0 @@ -36,4 +33,3 @@ training: conservative: 0.0 grade_reg: 0.01 target_spectrum: [0.35, 0.30, 0.20, 0.10, 0.05] - max_bivector_norm: 10.0 diff --git a/conf/task/sr.yaml b/conf/task/sr.yaml index 514ded9..d3cc5a1 100644 --- a/conf/task/sr.yaml +++ b/conf/task/sr.yaml @@ -6,7 +6,7 @@ algebra: q: 0 r: 0 device: "cpu" - auto: true + metric_search: true dataset: name: "sr" @@ -21,16 +21,12 @@ dataset: model: auto_capacity: true - exp_policy: "balanced" training: epochs: 200 lr: 0.003 batch_size: 128 - optimizer_type: "riemannian_adam" - max_bivector_norm: 10.0 sparsity_weight: 0.01 - scheduler_patience: 10 iterative: max_stages: 5 @@ -92,4 +88,4 @@ basis: corr_threshold: 0.05 max_expansion_factor: 3 dynamic_range_threshold: 100.0 - exp_max_input: 700.0 \ No newline at end of file + exp_max_input: 700.0 diff --git a/core/__init__.py b/core/__init__.py index 21934de..faf12f0 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -11,16 +11,48 @@ until first access, keeping ``import core`` lightweight. """ -from .algebra import CliffordAlgebra -from .decomposition import ( +from .config import AlgebraConfig, make_algebra, make_algebra_from_config +from .foundation.basis import ( + GradeProductOp, + basis_indices_for_grades, + basis_product, + expand_output_grades, + geometric_product_output_grades, + normalize_grades, + operation_coefficient, + operation_may_be_nonzero, + product_output_grades, + reverse_sign, +) +from .foundation.device import DeviceConfig, dtype_name, optional_dtype, resolve_device, resolve_dtype +from .foundation.layout import AlgebraSpec, GradeLayout +from .foundation.module import AlgebraLike, CliffordModule +from .foundation.validation import check_channels, check_multivector +from .planning.flow import GradeFlow +from .planning.layouts import ProductRequest, build_product_request +from .planning.planner import GradePlanner +from .planning.policy import DEFAULT_PLANNING_LIMITS, PlanCost, PlanningLimits +from .planning.product import GradeProductExecutor, GradeProductPlan, build_grade_product_plan +from .planning.tree import GradePathNode, GradePlanTree, build_grade_plan_tree +from .planning.unary import GradeUnaryExecutor, GradeUnaryOp, GradeUnaryPlan, UnaryRequest, build_unary_request +from .runtime.accessors import ( + as_multivector, + compact_values, + grade_indices, + hermitian_signs, + materialize_dense, + resolve_layout, +) +from .runtime.algebra import CliffordAlgebra +from .runtime.context import AlgebraContext +from .runtime.decomposition import ( ExpPolicy, compiled_safe_decomposed_exp, differentiable_invariant_decomposition, exp_simple_bivector, ga_power_iteration, ) -from .device import DeviceConfig, resolve_device -from .metric import ( +from .runtime.metric import ( clifford_conjugate, geometric_distance, grade_hermitian_norm, @@ -36,18 +68,30 @@ signature_norm_squared, signature_trace_form, ) -from .module import CliffordModule -from .multivector import Multivector -from .validation import check_channels, check_multivector +from .runtime.multivector import Multivector __all__ = [ # algebra + "AlgebraContext", "CliffordAlgebra", + "AlgebraConfig", + "AlgebraLike", "CliffordModule", "Multivector", + "AlgebraSpec", + "GradeLayout", + "GradePlanner", + "PlanningLimits", + "PlanCost", + "DEFAULT_PLANNING_LIMITS", + "make_algebra", + "make_algebra_from_config", # device / validation "DeviceConfig", + "dtype_name", + "optional_dtype", "resolve_device", + "resolve_dtype", "check_multivector", "check_channels", # metric @@ -65,12 +109,43 @@ "hermitian_grade_spectrum", "signature_trace_form", "signature_norm_squared", + "as_multivector", + "compact_values", + "grade_indices", + "hermitian_signs", + "materialize_dense", + "resolve_layout", # decomposition "ExpPolicy", "ga_power_iteration", "differentiable_invariant_decomposition", "exp_simple_bivector", "compiled_safe_decomposed_exp", + # static sparse grade planning + "GradeProductOp", + "GradeProductExecutor", + "GradeProductPlan", + "GradePathNode", + "GradePlanTree", + "GradeFlow", + "ProductRequest", + "GradeUnaryExecutor", + "GradeUnaryOp", + "GradeUnaryPlan", + "UnaryRequest", + "basis_indices_for_grades", + "basis_product", + "build_grade_product_plan", + "build_grade_plan_tree", + "build_product_request", + "build_unary_request", + "expand_output_grades", + "geometric_product_output_grades", + "normalize_grades", + "operation_coefficient", + "operation_may_be_nonzero", + "product_output_grades", + "reverse_sign", # analysis (lazy) "MetricSearch", "GeodesicFlow", diff --git a/core/analysis/commutator.py b/core/analysis/commutator.py index 4c46dd3..24e0119 100644 --- a/core/analysis/commutator.py +++ b/core/analysis/commutator.py @@ -16,7 +16,7 @@ import torch -from core.algebra import CliffordAlgebra +from core.foundation.module import AlgebraLike from ._types import CONSTANTS, CommutatorResult @@ -29,14 +29,14 @@ class CommutatorAnalyzer: elements (bivectors), directly related to rotation planes. Args: - algebra: :class:`CliffordAlgebra` instance. + algebra: algebra kernel or planning context. max_bivectors: Maximum number of bivectors for Lie-bracket closure analysis (guards combinatorial cost). """ def __init__( self, - algebra: CliffordAlgebra, + algebra: AlgebraLike, max_bivectors: int = 15, ): self.algebra = algebra @@ -86,28 +86,34 @@ def commutativity_matrix(self, mv_data: torch.Tensor) -> torch.Tensor: ``[n, n]`` symmetric matrix of commutativity indices. """ n = self.algebra.n - dim = self.algebra.dim device = mv_data.device dtype = mv_data.dtype - # Build per-direction projections: keep only the e_i component - g1_idx = (1 << torch.arange(n, device=device)).long() - + g1_idx = self.algebra.grade_indices((1,), device=device) N = mv_data.shape[0] - # Build all n projected multivectors: [n, N, dim] - xi_all = torch.zeros(n, N, dim, device=device, dtype=dtype) - coeffs = mv_data[:, g1_idx] # [N, n] - row_idx = torch.arange(n, device=device).unsqueeze(1).expand(n, N) - batch_idx = torch.arange(N, device=device).unsqueeze(0).expand(n, N) - col_idx = g1_idx.unsqueeze(1).expand(n, N) - xi_all[row_idx, batch_idx, col_idx] = coeffs.T - # All (i, j) pairs with i < j i_idx, j_idx = torch.triu_indices(n, n, offset=1, device=device) + if i_idx.numel() == 0: + return torch.zeros(n, n, device=device, dtype=dtype) - # Batched commutator: [n_pairs, N, dim] - comm = self.algebra.commutator(xi_all[i_idx], xi_all[j_idx]) + coeffs = mv_data[:, g1_idx] # [N, n] + left = torch.zeros(i_idx.numel(), N, n, device=device, dtype=dtype) + right = torch.zeros_like(left) + left.scatter_(-1, i_idx.view(-1, 1, 1).expand(-1, N, 1), coeffs[:, i_idx].T.unsqueeze(-1)) + right.scatter_(-1, j_idx.view(-1, 1, 1).expand(-1, N, 1), coeffs[:, j_idx].T.unsqueeze(-1)) + + # Batched compact commutator: [n_pairs, N, grade2_dim] + comm = self.algebra.commutator( + left, + right, + left_grades=(1,), + right_grades=(1,), + output_grades=(2,), + left_compact=True, + right_compact=True, + compact_output=True, + ) vals = comm.norm(dim=-1).mean(dim=-1) # [n_pairs] matrix = torch.zeros(n, n, device=device, dtype=dtype) @@ -178,7 +184,6 @@ def lie_bracket_closure(self, mv_data: torch.Tensor) -> Dict: ``"closure_error"`` (scalar), and ``"basis_indices"`` (list of multivector-coefficient indices of the chosen bivectors). """ - dim = self.algebra.dim n = self.algebra.n device = mv_data.device dtype = mv_data.dtype @@ -190,15 +195,13 @@ def lie_bracket_closure(self, mv_data: torch.Tensor) -> Dict: "basis_indices": [], } - # Extract grade-2 part of mean per-sample - bv_data = self.algebra.grade_projection(mv_data, 2) # [N, dim] - mean_bv = bv_data.mean(dim=0) # [dim] + # Extract compact grade-2 part of mean per-sample + bv_data = self.algebra.grade_projection(mv_data, 2, compact_output=True) # [N, grade2_dim] + mean_bv = bv_data.mean(dim=0) # [grade2_dim] - # Identify bivector basis indices - ii, jj = torch.triu_indices(n, n, offset=1) - bv_blade_indices = ((1 << ii) | (1 << jj)).tolist() + bv_blade_indices = self.algebra.grade_indices((2,), device=device) - if not bv_blade_indices: + if bv_blade_indices.numel() == 0: return { "structure_constants": torch.zeros(0, 0, 0, device=device), "closure_error": 0.0, @@ -206,24 +209,29 @@ def lie_bracket_closure(self, mv_data: torch.Tensor) -> Dict: } # Pick top-k by energy in the mean bivector - bv_idx_tensor = torch.tensor(bv_blade_indices, dtype=torch.long, device=device) - energies = mean_bv[bv_idx_tensor].abs() - k = min(self.max_bivectors, len(bv_blade_indices)) - topk_pos = energies.topk(k).indices.tolist() + energies = mean_bv.abs() + k = min(self.max_bivectors, int(bv_blade_indices.numel())) + topk_pos = energies.topk(k).indices + selected_indices = bv_blade_indices[topk_pos].tolist() - selected_indices = [bv_blade_indices[p] for p in topk_pos] - - # Build basis bivector multivectors - B = torch.zeros(k, dim, device=device, dtype=dtype) - sel_tensor = torch.tensor(selected_indices, dtype=torch.long, device=device) - B[torch.arange(k, device=device), sel_tensor] = 1.0 + # Build compact basis bivectors. + B = torch.zeros(k, bv_blade_indices.numel(), device=device, dtype=dtype) + B[torch.arange(k, device=device), topk_pos] = 1.0 # Compute structure constants c_{a,b,c} such that [B_a, B_b] ~= Sum_c c_{abc} B_c a_idx, b_idx = torch.triu_indices(k, k, offset=1, device=device) - # Batched commutator and grade-2 projection - brackets = self.algebra.commutator(B[a_idx], B[b_idx]) # [n_pairs, dim] - brackets_bv = self.algebra.grade_projection(brackets, 2) # [n_pairs, dim] + # Batched compact commutator and grade-2 projection. + brackets_bv = self.algebra.commutator( + B[a_idx], + B[b_idx], + left_grades=(2,), + right_grades=(2,), + output_grades=(2,), + left_compact=True, + right_compact=True, + compact_output=True, + ) # [n_pairs, grade2_dim] # Project onto basis: coeffs[p, c] = coeffs = brackets_bv @ B.T # [n_pairs, k] @@ -233,7 +241,7 @@ def lie_bracket_closure(self, mv_data: torch.Tensor) -> Dict: structure[b_idx, a_idx, :] = -coeffs # antisymmetry # Closure errors: residual norm / bracket norm - projected = coeffs @ B # [n_pairs, dim] + projected = coeffs @ B # [n_pairs, grade2_dim] residuals = brackets_bv - projected res_norms = residuals.norm(dim=-1) # [n_pairs] bracket_norms = brackets_bv.norm(dim=-1) # [n_pairs] @@ -251,14 +259,14 @@ def lie_bracket_closure(self, mv_data: torch.Tensor) -> Dict: } -def compute_uncertainty_and_alignment(algebra: CliffordAlgebra, data_tensor: torch.Tensor): +def compute_uncertainty_and_alignment(algebra: AlgebraLike, data_tensor: torch.Tensor): """Compute Geometric Uncertainty Index (U) and Procrustes Alignment (V). Used by :class:`~layers.adapters.mother.MotherEmbedding` to initialise per-group / per-subject alignment rotors. Args: - algebra: CliffordAlgebra instance. + algebra: algebra kernel or planning context. data_tensor: ``[N, D]`` tensor of raw features. Returns: @@ -275,11 +283,18 @@ def compute_uncertainty_and_alignment(algebra: CliffordAlgebra, data_tensor: tor else: x_n = data_tensor[:, :n] - x = algebra.embed_vector(x_n) # [N, dim] - - # 2. Mean multivector and commutator [x_i, mu] - mu = x.mean(dim=0, keepdim=True) # [1, dim] - comm = algebra.commutator(x, mu.expand_as(x)) + # 2. Mean grade-1 vector and compact commutator [x_i, mu] + mu = x_n.mean(dim=0, keepdim=True) # [1, n] + comm = algebra.commutator( + x_n, + mu.expand_as(x_n), + left_grades=(1,), + right_grades=(1,), + output_grades=(2,), + left_compact=True, + right_compact=True, + compact_output=True, + ) U = torch.norm(comm, p=2, dim=-1).mean().item() diff --git a/core/analysis/dimension.py b/core/analysis/dimension.py index 958e33b..628d9b3 100644 --- a/core/analysis/dimension.py +++ b/core/analysis/dimension.py @@ -17,7 +17,8 @@ import torch -from core.algebra import CliffordAlgebra +from core.config import make_algebra +from core.foundation.module import AlgebraLike from ._types import CONSTANTS, DimensionResult @@ -196,7 +197,7 @@ def __init__(self, device: str = "cpu"): def lift( self, data: torch.Tensor, - target_algebra: CliffordAlgebra, + target_algebra: AlgebraLike, fill: float = 1.0, ) -> torch.Tensor: """Lifts data into the grade-1 subspace of a higher-dimensional algebra. @@ -260,7 +261,7 @@ def test( data = data.to(self.device) results: Dict = {} - def _measure(alg: CliffordAlgebra, mv: torch.Tensor) -> Dict: + def _measure(alg: AlgebraLike, mv: torch.Tensor) -> Dict: gf = GeodesicFlow(alg, k=k) coh = gf.coherence(mv) curv = gf.curvature(mv) @@ -273,15 +274,15 @@ def _measure(alg: CliffordAlgebra, mv: torch.Tensor) -> Dict: "causal": (coh > coh_threshold) and (curv < CONSTANTS.curvature_causal_threshold), } - alg_orig = CliffordAlgebra(p, q, device=self.device) + alg_orig = make_algebra(p, q, device=self.device) mv_orig = alg_orig.embed_vector(data[..., : alg_orig.n]) results["original"] = _measure(alg_orig, mv_orig) - alg_pos = CliffordAlgebra(p + 1, q, device=self.device) + alg_pos = make_algebra(p + 1, q, device=self.device) mv_pos = self.lift(data, alg_pos, fill=1.0) results["lift_positive"] = _measure(alg_pos, mv_pos) - alg_null = CliffordAlgebra(p, q + 1, device=self.device) + alg_null = make_algebra(p, q + 1, device=self.device) mv_null = self.lift(data, alg_null, fill=0.0) results["lift_null"] = _measure(alg_null, mv_null) diff --git a/core/analysis/geodesic.py b/core/analysis/geodesic.py index 6785a43..599e916 100644 --- a/core/analysis/geodesic.py +++ b/core/analysis/geodesic.py @@ -16,7 +16,7 @@ import torch -from core.algebra import CliffordAlgebra +from core.foundation.module import AlgebraLike from ._types import CONSTANTS @@ -44,7 +44,7 @@ class GeodesicFlow: collides with itself. The signal is dominated by noise. """ - def __init__(self, algebra: CliffordAlgebra, k: int = 8): + def __init__(self, algebra: AlgebraLike, k: int = 8): """Initialize Geodesic Flow. Args: @@ -86,8 +86,7 @@ def _knn(self, mv: torch.Tensor) -> torch.Tensor: """ N = mv.shape[0] k = min(self.k, N - 1) - diff = mv.unsqueeze(1) - mv.unsqueeze(0) # [N, N, dim] - dists = diff.norm(dim=-1) # [N, N] + dists = torch.cdist(mv, mv) # [N, N] dists.fill_diagonal_(float("inf")) _, idx = dists.topk(k, dim=-1, largest=False) return idx # [N, k] @@ -112,10 +111,16 @@ def _connection_bivectors(self, mv: torch.Tensor) -> torch.Tensor: neighbors = mv[nn_idx] # [N, k, dim] xi = mv.unsqueeze(1).expand(N, k, D).reshape(N * k, D) - xj_rev = self.algebra.reverse(neighbors.reshape(N * k, D)) + xj_rev = self.algebra.reverse(neighbors.reshape(N * k, D), input_grades=(1,)) # For grade-1 inputs, _2 = wedge(xi, xj_rev) -- single pass - bv_raw = self.algebra.wedge(xi, xj_rev) # [N*k, dim] + bv_raw = self.algebra.wedge( + xi, + xj_rev, + left_grades=(1,), + right_grades=(1,), + output_grades=(2,), + ) # [N*k, dim] bv_norm = bv_raw.norm(dim=-1, keepdim=True).clamp(min=self.algebra.eps) return (bv_raw / bv_norm).reshape(N, k, D) # [N, k, dim] diff --git a/core/analysis/pipeline.py b/core/analysis/pipeline.py index 3f53b87..f7ed812 100644 --- a/core/analysis/pipeline.py +++ b/core/analysis/pipeline.py @@ -13,7 +13,8 @@ import torch -from core.algebra import CliffordAlgebra +from core.config import make_algebra +from core.foundation.module import AlgebraLike from ._types import AnalysisConfig, AnalysisReport, SamplingConfig from .commutator import CommutatorAnalyzer @@ -50,7 +51,7 @@ def __init__(self, config: Optional[AnalysisConfig] = None): def analyze( self, data: torch.Tensor, - algebra: Optional[CliffordAlgebra] = None, + algebra: Optional[AlgebraLike] = None, ) -> AnalysisReport: """Run the full geometric analysis pipeline. @@ -124,7 +125,7 @@ def _run_full_pipeline(self, data: torch.Tensor, report: AnalysisReport) -> Anal if p + q + r < 2: p = max(p, 2 - q - r) - algebra = CliffordAlgebra(p, q, r, device=cfg.device) + algebra = make_algebra(p, q, r, device=cfg.device) mv_data = self._embed_raw(sampled, algebra) # 5. GA analyses (parallel) @@ -135,7 +136,7 @@ def _run_full_pipeline(self, data: torch.Tensor, report: AnalysisReport) -> Anal def _run_ga_analyses( self, mv_data: torch.Tensor, - algebra: CliffordAlgebra, + algebra: AlgebraLike, report: AnalysisReport, ) -> AnalysisReport: cfg = self.config @@ -173,7 +174,7 @@ def _run_ga_analyses( return report @staticmethod - def _embed_raw(data: torch.Tensor, algebra: CliffordAlgebra) -> torch.Tensor: + def _embed_raw(data: torch.Tensor, algebra: AlgebraLike) -> torch.Tensor: """Embed raw ``[N, D]`` data as grade-1 multivectors ``[N, 1, dim]``.""" n = algebra.n D = data.shape[1] diff --git a/core/analysis/sampler.py b/core/analysis/sampler.py index 20d1665..a9804de 100644 --- a/core/analysis/sampler.py +++ b/core/analysis/sampler.py @@ -91,7 +91,7 @@ def _stratified(data: torch.Tensor, config: SamplingConfig) -> Tuple[torch.Tenso coherence) and unstructured (low coherence) regions of the data without assuming a specific metric signature. """ - from core.algebra import CliffordAlgebra + from core.config import make_algebra from .geodesic import GeodesicFlow @@ -102,7 +102,7 @@ def _stratified(data: torch.Tensor, config: SamplingConfig) -> Tuple[torch.Tenso # Cap algebra dimension for tractability alg_dim = min(D, 6) - algebra = CliffordAlgebra(alg_dim, 0, device=data.device) + algebra = make_algebra(alg_dim, 0, device=data.device) # Embed into algebra (truncate to alg_dim if needed) raw = data[:, :alg_dim].float() diff --git a/core/analysis/signature.py b/core/analysis/signature.py index 608cc25..60acc85 100644 --- a/core/analysis/signature.py +++ b/core/analysis/signature.py @@ -21,28 +21,85 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra -from layers import BladeSelector, CliffordLinear, RotorLayer +from core.config import make_algebra +from core.foundation.module import AlgebraLike, CliffordModule from ._types import CONSTANTS, DimensionResult, SamplingConfig, SignatureResult from .geodesic import GeodesicFlow +class _ProbeLinear(CliffordModule): + """Core-local channel mixer used by metric-search probes.""" + + def __init__(self, algebra: AlgebraLike, in_channels: int, out_channels: int): + super().__init__(algebra) + self.weight = nn.Parameter(torch.empty(out_channels, in_channels)) + self.bias = nn.Parameter(torch.empty(out_channels, algebra.dim)) + self.reset_parameters() + + def reset_parameters(self) -> None: + nn.init.xavier_uniform_(self.weight) + nn.init.zeros_(self.bias) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + return torch.einsum("oi,...id->...od", self.weight, x) + self.bias + + +class _ProbeRotor(CliffordModule): + """Core-local dense rotor used only for signature search analysis.""" + + def __init__(self, algebra: AlgebraLike, channels: int): + super().__init__(algebra) + self.channels = channels + self.register_buffer("bivector_indices", algebra.grade_indices((2,), device=algebra.device)) + self.bivector_weights = nn.Parameter(torch.empty(channels, self.bivector_indices.numel())) + self.bivector_weights._manifold = "spin" + self.reset_parameters() + + def reset_parameters(self) -> None: + nn.init.normal_(self.bivector_weights, std=0.01) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + if not hasattr(self.algebra, "exp") or not hasattr(self.algebra, "per_channel_sandwich"): + raise ValueError( + "Signature probes require dense rotor execution; reduce dimensions or request dense kernel." + ) + bivectors = x.new_zeros(self.channels, self.algebra.dim) + indices = self.bivector_indices.unsqueeze(0).expand(self.channels, -1) + bivectors.scatter_(1, indices, self.bivector_weights.to(dtype=x.dtype)) + rotor = self.algebra.exp(-0.5 * bivectors) + return self.algebra.per_channel_sandwich(rotor, x, self.algebra.reverse(rotor)) + + def sparsity_loss(self) -> torch.Tensor: + return torch.norm(self.bivector_weights, p=1) + + +class _ProbeBladeSelector(CliffordModule): + """Core-local blade gate for metric-search probes.""" + + def __init__(self, algebra: AlgebraLike, channels: int): + super().__init__(algebra) + self.weights = nn.Parameter(torch.ones(channels, algebra.dim)) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + return x * torch.sigmoid(self.weights).unsqueeze(0) + + class _SignatureProbe(nn.Module): """Minimal single-rotor probe for bivector energy analysis. - Architecture: CliffordLinear(1, C) -> RotorLayer(C) -> BladeSelector(C). + Architecture: channel mixer -> rotor -> channel mixer -> blade selector. Only one linear layer for channel expansion; the rotor bivector energy is the primary signal for signature discovery. """ - def __init__(self, algebra: CliffordAlgebra, channels: int = 4): + def __init__(self, algebra: AlgebraLike, channels: int = 4): super().__init__() self.algebra = algebra - self.linear_in = CliffordLinear(algebra, 1, channels) - self.rotor = RotorLayer(algebra, channels) - self.linear_out = CliffordLinear(algebra, channels, 1) - self.selector = BladeSelector(algebra, 1) + self.linear_in = _ProbeLinear(algebra, 1, channels) + self.rotor = _ProbeRotor(algebra, channels) + self.linear_out = _ProbeLinear(algebra, channels, 1) + self.selector = _ProbeBladeSelector(algebra, 1) def forward(self, x: torch.Tensor) -> torch.Tensor: x = self.linear_in(x) @@ -51,35 +108,35 @@ def forward(self, x: torch.Tensor) -> torch.Tensor: x = self.selector(x) return x - def get_rotor_layers(self) -> List[RotorLayer]: - return [m for m in self.modules() if isinstance(m, RotorLayer)] + def get_rotor_layers(self) -> List[_ProbeRotor]: + return [m for m in self.modules() if isinstance(m, _ProbeRotor)] def _apply_biased_init( probe: _SignatureProbe, - algebra: CliffordAlgebra, + algebra: AlgebraLike, bias_type: str = "random", ) -> None: """Biases RotorLayer bivector weights based on signature type. - Uses ``algebra.bv_sq_scalar`` to classify each basis bivector: + Uses ``algebra.bivector_squared_signs()`` to classify each basis bivector: - bv_sq = -1: elliptic (positive-signature base vectors) - bv_sq = +1: hyperbolic (mixed-signature base vectors) - bv_sq = 0: null (degenerate base vectors) """ - bv_sq = algebra.bv_sq_scalar + bv_sq = algebra.bivector_squared_signs(device=algebra.device, dtype=algebra.dtype) ell = CONSTANTS.bv_sq_elliptic_bound hyp = CONSTANTS.bv_sq_hyperbolic_bound for rotor in probe.get_rotor_layers(): with torch.no_grad(): if bias_type == "euclidean": - weights = torch.where(bv_sq < ell, torch.tensor(1.0), torch.tensor(0.1)) + weights = torch.where(bv_sq < ell, torch.ones_like(bv_sq), torch.full_like(bv_sq, 0.1)) rotor.bivector_weights.copy_( weights.unsqueeze(0).expand_as(rotor.bivector_weights) + torch.randn_like(rotor.bivector_weights) * 0.05 ) elif bias_type == "minkowski": - weights = torch.where(bv_sq.abs() > hyp, torch.tensor(1.0), torch.tensor(0.1)) + weights = torch.where(bv_sq.abs() > hyp, torch.ones_like(bv_sq), torch.full_like(bv_sq, 0.1)) rotor.bivector_weights.copy_( weights.unsqueeze(0).expand_as(rotor.bivector_weights) + torch.randn_like(rotor.bivector_weights) * 0.05 @@ -129,7 +186,7 @@ def __init__( self.micro_batch_size = micro_batch_size self.early_stop_patience = early_stop_patience - def _lift_data(self, data: torch.Tensor) -> Tuple[torch.Tensor, CliffordAlgebra]: + def _lift_data(self, data: torch.Tensor) -> Tuple[torch.Tensor, AlgebraLike]: """Lifts [N, X] data into Cl(X+1, 1, 0) via CGA-style embedding.""" data = data.to(self.device) N, X = data.shape @@ -137,14 +194,16 @@ def _lift_data(self, data: torch.Tensor) -> Tuple[torch.Tensor, CliffordAlgebra] if X + 2 > 12: warnings.warn( f"Data dimension {X} yields algebra dim 2^{X + 2}={2 ** (X + 2)}. " - f"Consider PCA pre-reduction to X <= 10 for tractable computation." + "MetricSearch probes require dense rotor execution; use SignatureSearchAnalyzer PCA " + "pre-reduction to X <= 10." ) + raise ValueError("MetricSearch requires X <= 10 so the conformal probe algebra stays within Cl12.") norm_sq = 0.5 * (data**2).sum(dim=-1, keepdim=True) ones = torch.ones(N, 1, device=self.device, dtype=data.dtype) lifted = torch.cat([data, norm_sq, ones], dim=-1) - algebra = CliffordAlgebra(X + 1, 1, 0, device=self.device) + algebra = make_algebra(X + 1, 1, 0, kernel="dense", device=self.device) mv = algebra.embed_vector(lifted) mv = mv.unsqueeze(1) return mv, algebra @@ -152,7 +211,7 @@ def _lift_data(self, data: torch.Tensor) -> Tuple[torch.Tensor, CliffordAlgebra] def _train_probe( self, mv_data: torch.Tensor, - algebra: CliffordAlgebra, + algebra: AlgebraLike, bias_type: str = "random", ) -> Dict: """Trains a single probe and returns results.""" @@ -217,13 +276,12 @@ def _train_probe( def _analyze_bivector_energy( self, probe: _SignatureProbe, - algebra: CliffordAlgebra, + algebra: AlgebraLike, original_dim: int, ) -> Tuple[Tuple[int, int, int], Dict]: """Maps learned bivector energy to (p, q, r) signature.""" - bv_sq = algebra.bv_sq_scalar - bv_mask = algebra.grade_masks[2] - bv_indices = bv_mask.nonzero(as_tuple=False).squeeze(-1) + bv_sq = algebra.bivector_squared_signs(device=self.device, dtype=algebra.dtype) + bv_indices = algebra.grade_indices((2,), device=self.device) total_energy = torch.zeros(len(bv_indices), device=self.device) n_layers = 0 diff --git a/core/analysis/spectral.py b/core/analysis/spectral.py index 4065c37..bc8d45b 100644 --- a/core/analysis/spectral.py +++ b/core/analysis/spectral.py @@ -15,9 +15,9 @@ import torch -from core.algebra import CliffordAlgebra -from core.decomposition import differentiable_invariant_decomposition -from core.metric import hermitian_grade_spectrum +from core.foundation.module import AlgebraLike +from core.runtime.decomposition import differentiable_invariant_decomposition +from core.runtime.metric import hermitian_grade_spectrum from ._types import CONSTANTS, SpectralResult @@ -35,14 +35,14 @@ class SpectralAnalyzer: operator :math:`L_x(y) = x \\cdot y` (only for small algebras). Args: - algebra: :class:`CliffordAlgebra` instance. + algebra: algebra kernel or planning context. max_simple_components: Maximum number of simple components to extract from the mean bivector. """ def __init__( self, - algebra: CliffordAlgebra, + algebra: AlgebraLike, max_simple_components: int = 5, ): self.algebra = algebra diff --git a/core/analysis/symmetry.py b/core/analysis/symmetry.py index df27776..578ce78 100644 --- a/core/analysis/symmetry.py +++ b/core/analysis/symmetry.py @@ -15,7 +15,7 @@ import torch -from core.algebra import CliffordAlgebra +from core.foundation.module import AlgebraLike from ._types import CONSTANTS, CommutatorResult, SymmetryResult @@ -24,14 +24,14 @@ class SymmetryDetector: """Detect symmetries, null directions, and invariances. Args: - algebra: :class:`CliffordAlgebra` instance. + algebra: algebra kernel or planning context. null_threshold: Energy threshold below which a direction is considered effectively null. """ def __init__( self, - algebra: CliffordAlgebra, + algebra: AlgebraLike, null_threshold: float = 0.01, ): self.algebra = algebra @@ -90,7 +90,7 @@ def detect_null_directions(self, mv_data: torch.Tensor) -> Tuple[List[int], torc *null_indices* lists those with score < threshold. """ n = self.algebra.n - g1_idx = (1 << torch.arange(n, device=mv_data.device)).long() + g1_idx = self.algebra.grade_indices((1,), device=mv_data.device) # Energy on each grade-1 component g1_coeffs = mv_data[:, g1_idx] # [N, n] @@ -141,7 +141,7 @@ def detect_reflection_symmetries(self, mv_data: torch.Tensor) -> List[Dict]: # Build all n basis vectors: [n, dim] basis_vecs = torch.zeros(n, dim, device=mv_data.device, dtype=mv_data.dtype) - blade_indices = (1 << torch.arange(n, device=mv_data.device)).long() + blade_indices = self.algebra.grade_indices((1,), device=mv_data.device) basis_vecs[torch.arange(n, device=mv_data.device), blade_indices] = 1.0 # Batch reflect: [n, N, dim] @@ -198,16 +198,14 @@ def detect_continuous_symmetries( dim = self.algebra.dim N = mv_data.shape[0] - ii, jj = torch.triu_indices(n, n, offset=1) - bv_indices = ((1 << ii) | (1 << jj)).tolist() + bv_idx_tensor = self.algebra.grade_indices((2,), device=mv_data.device) - if not bv_indices: + if bv_idx_tensor.numel() == 0: return 0 - n_bv = len(bv_indices) + n_bv = int(bv_idx_tensor.numel()) # Build all bivector bases: [n_bv, dim] bv_bases = torch.zeros(n_bv, dim, device=mv_data.device, dtype=mv_data.dtype) - bv_idx_tensor = torch.tensor(bv_indices, dtype=torch.long, device=mv_data.device) bv_bases[torch.arange(n_bv, device=mv_data.device), bv_idx_tensor] = 1.0 # Batch commutator: [n_bv, N, dim] diff --git a/core/config.py b/core/config.py new file mode 100644 index 0000000..b1d121f --- /dev/null +++ b/core/config.py @@ -0,0 +1,175 @@ +# Versor: Universal Geometric Algebra Neural Network +# Copyright (C) 2026 Eunkyum Kim +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# + +"""Algebra construction config and dense/planned context dispatch.""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Iterable, Literal, Mapping, Optional + +import torch + +from core.foundation.device import resolve_device, resolve_dtype +from core.foundation.module import AlgebraLike +from core.planning.policy import PlanningLimits +from core.runtime.algebra import CliffordAlgebra +from core.runtime.context import AlgebraContext + +AlgebraKernel = Literal["auto", "dense", "context"] + + +@dataclass(frozen=True) +class AlgebraConfig: + """Dense/context algebra declaration.""" + + p: int + q: int = 0 + r: int = 0 + kernel: AlgebraKernel = "auto" + dense_threshold: int = 8 + device: str = "cuda" + dtype: torch.dtype = torch.float32 + exp_policy: str = "balanced" + fixed_iterations: Optional[int] = None + default_grades: Optional[tuple[int, ...]] = None + allow_full_layout_products: Optional[bool] = None + planning_limits: Optional[PlanningLimits] = None + + @classmethod + def from_mapping(cls, config: Mapping[str, Any], **overrides) -> "AlgebraConfig": + """Build an algebra declaration from Hydra/OmegaConf config.""" + values = { + "p": int(_mapping_get(config, "p", 0)), + "q": int(_mapping_get(config, "q", 0)), + "r": int(_mapping_get(config, "r", 0)), + "kernel": _mapping_get(config, "kernel", "auto"), + "dense_threshold": int(_mapping_get(config, "dense_threshold", 8)), + "device": _mapping_get(config, "device", "cuda"), + "dtype": resolve_dtype(_mapping_get(config, "dtype", torch.float32)), + "exp_policy": _mapping_get(config, "exp_policy", "balanced"), + "fixed_iterations": _optional_int(_mapping_get(config, "fixed_iterations", None)), + "default_grades": _optional_grades(_mapping_get(config, "default_grades", None)), + "allow_full_layout_products": _optional_bool(_mapping_get(config, "allow_full_layout_products", None)), + } + values.update({key: value for key, value in overrides.items() if value is not None}) + values["dtype"] = resolve_dtype(values["dtype"]) + return cls(**values) + + +def make_algebra( + p: int, + q: int = 0, + r: int = 0, + *, + kernel: AlgebraKernel = "auto", + dense_threshold: int = 8, + device="cuda", + dtype: torch.dtype = torch.float32, + exp_policy: str = "balanced", + fixed_iterations: Optional[int] = None, + default_grades: Optional[Iterable[int]] = None, + allow_full_layout_products: Optional[bool] = None, + planning_limits: Optional[PlanningLimits] = None, + **deprecated_options, +) -> AlgebraLike: + """Construct a dense low-dimensional algebra or high-dimensional planning context.""" + kernel = _normalize_kernel(kernel) + n = p + q + r + if deprecated_options: + unknown = ", ".join(sorted(deprecated_options)) + raise TypeError(f"Unsupported algebra construction option(s): {unknown}") + + selected_kernel = "context" if kernel == "auto" and n > dense_threshold else kernel + if selected_kernel == "auto": + selected_kernel = "dense" + + resolved_device = resolve_device(device) if str(device) == "auto" else device + resolved_dtype = resolve_dtype(dtype) + + if selected_kernel == "dense": + return CliffordAlgebra( + p, + q, + r, + device=resolved_device, + dtype=resolved_dtype, + exp_policy=exp_policy, + fixed_iterations=fixed_iterations, + allow_large_dense=kernel == "dense", + planning_limits=planning_limits, + ) + + return AlgebraContext( + p, + q, + r, + device=resolved_device, + dtype=resolved_dtype, + default_grades=default_grades, + allow_full_layout_products=allow_full_layout_products, + planning_limits=planning_limits, + ) + + +def make_algebra_from_config(config: Mapping[str, Any], **overrides) -> AlgebraLike: + """Construct an algebra from a Hydra/OmegaConf-compatible config mapping.""" + algebra_config = AlgebraConfig.from_mapping(config, **overrides) + return make_algebra( + algebra_config.p, + algebra_config.q, + algebra_config.r, + kernel=algebra_config.kernel, + dense_threshold=algebra_config.dense_threshold, + device=algebra_config.device, + dtype=algebra_config.dtype, + exp_policy=algebra_config.exp_policy, + fixed_iterations=algebra_config.fixed_iterations, + default_grades=algebra_config.default_grades, + allow_full_layout_products=algebra_config.allow_full_layout_products, + planning_limits=algebra_config.planning_limits, + ) + + +def _mapping_get(config: Mapping[str, Any], key: str, default): + """Return a value from plain mappings or OmegaConf DictConfig objects.""" + if config is None: + return default + return config.get(key, default) + + +def _normalize_kernel(kernel: str) -> AlgebraKernel: + """Validate and normalize algebra kernel names.""" + normalized = str(kernel).lower() + if normalized not in {"auto", "dense", "context"}: + raise ValueError(f"Unknown algebra kernel {kernel!r}; expected 'auto', 'dense', or 'context'") + return normalized # type: ignore[return-value] + + +def _optional_int(value) -> Optional[int]: + if value is None: + return None + return int(value) + + +def _optional_grades(value) -> Optional[tuple[int, ...]]: + if value is None: + return None + return tuple(int(grade) for grade in value) + + +def _optional_bool(value) -> Optional[bool]: + if value is None: + return None + if isinstance(value, str): + lowered = value.strip().lower() + if lowered in {"true", "1", "yes", "on"}: + return True + if lowered in {"false", "0", "no", "off"}: + return False + raise ValueError(f"Cannot parse boolean value {value!r}") + return bool(value) diff --git a/core/foundation/__init__.py b/core/foundation/__init__.py new file mode 100644 index 0000000..5af000d --- /dev/null +++ b/core/foundation/__init__.py @@ -0,0 +1,71 @@ +# Versor: Universal Geometric Algebra Neural Network +# Copyright (C) 2026 Eunkyum Kim +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# + +"""Foundation value objects, basis utilities, validation, and device helpers.""" + +from .basis import ( + GradeProductOp, + basis_count_for_grades, + basis_index_tuple_for_grades, + basis_indices_for_grades, + basis_product, + expand_output_grades, + geometric_product_output_grades, + normalize_grades, + operation_coefficient, + operation_may_be_nonzero, + product_output_grades, + reverse_sign, +) +from .device import DeviceConfig, dtype_name, optional_dtype, resolve_device, resolve_dtype +from .layout import AlgebraSpec, GradeLayout +from .manifold import ( + MANIFOLD_EUCLIDEAN, + MANIFOLD_ORDER, + MANIFOLD_SPHERE, + MANIFOLD_SPIN, + VALID_MANIFOLDS, + format_valid_manifolds, + tag_manifold, + validate_manifold, +) +from .module import AlgebraLike, CliffordModule +from .validation import check_channels, check_multivector + +__all__ = [ + "AlgebraLike", + "AlgebraSpec", + "CliffordModule", + "DeviceConfig", + "GradeLayout", + "GradeProductOp", + "MANIFOLD_EUCLIDEAN", + "MANIFOLD_ORDER", + "MANIFOLD_SPHERE", + "MANIFOLD_SPIN", + "VALID_MANIFOLDS", + "basis_count_for_grades", + "basis_index_tuple_for_grades", + "basis_indices_for_grades", + "basis_product", + "check_channels", + "check_multivector", + "dtype_name", + "expand_output_grades", + "format_valid_manifolds", + "geometric_product_output_grades", + "normalize_grades", + "operation_coefficient", + "operation_may_be_nonzero", + "optional_dtype", + "product_output_grades", + "reverse_sign", + "resolve_device", + "resolve_dtype", + "tag_manifold", + "validate_manifold", +] diff --git a/core/foundation/basis.py b/core/foundation/basis.py new file mode 100644 index 0000000..71a691e --- /dev/null +++ b/core/foundation/basis.py @@ -0,0 +1,223 @@ +# Versor: Universal Geometric Algebra Neural Network +# Copyright (C) 2026 Eunkyum Kim +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# + +"""Canonical bitmask-basis utilities for Clifford algebra planning.""" + +from __future__ import annotations + +from itertools import combinations +from math import comb +from typing import Iterable, Literal, Optional + +import torch + +GradeProductOp = Literal["gp", "wedge", "inner", "commutator", "anti_commutator"] + +# NOTE: Torch-backed executors currently store canonical basis blades as signed +# int64 bitmasks. That makes n=63 the largest supported dimension: the highest +# usable basis bit is 1 << 62, while n=64 would require 1 << 63, which is outside +# torch.long's positive range. Supporting n>=64 requires kernel-level storage +# engineering, for example compact-position-only kernels, declared blade objects, +# multi-limb or variable-length bitsets, or another non-int64 basis identifier. +TORCH_LONG_BASIS_MAX_N = 63 +_TORCH_LONG_MAX = (1 << TORCH_LONG_BASIS_MAX_N) - 1 + + +def normalize_grades(grades: Iterable[int], n: int, *, name: str = "grades") -> tuple[int, ...]: + """Return sorted unique grades validated against ``0 <= grade <= n``.""" + normalized = tuple(sorted({int(grade) for grade in grades})) + if not normalized: + raise ValueError(f"{name} must contain at least one grade") + invalid = [grade for grade in normalized if grade < 0 or grade > n] + if invalid: + raise ValueError(f"{name} contains invalid grades for n={n}: {invalid}") + return normalized + + +def basis_index_tuple_for_grades(n: int, grades: Iterable[int]) -> tuple[int, ...]: + """Return canonical bitmask basis indices whose popcount is in ``grades``.""" + indices: list[int] = [] + for grade in normalize_grades(grades, n): + indices.extend(_basis_indices_for_grade(n, grade)) + return tuple(sorted(indices)) + + +def basis_count_for_grades(n: int, grades: Iterable[int]) -> int: + """Return the number of basis blades represented by ``grades``.""" + return sum(comb(n, grade) for grade in normalize_grades(grades, n)) + + +def basis_indices_for_grades(n: int, grades: Iterable[int], *, device=None) -> torch.Tensor: + """Return canonical bitmask basis indices as a tensor.""" + return basis_indices_tensor(basis_index_tuple_for_grades(n, grades), n=n, device=device) + + +def basis_indices_tensor( + indices: Iterable[int], + *, + n: Optional[int] = None, + role: str = "basis indices", + device=None, +) -> torch.Tensor: + """Tensorize canonical basis bitmasks with a clear signed-int64 boundary.""" + values = tuple(int(index) for index in indices) + _validate_torch_long_basis_indices(values, n=n, role=role) + return torch.tensor(values, dtype=torch.long, device=device) + + +def _basis_indices_for_grade(n: int, grade: int) -> tuple[int, ...]: + if grade == 0: + return (0,) + if grade == n: + return ((1 << n) - 1,) + return tuple(sum(1 << bit for bit in bits) for bits in combinations(range(n), grade)) + + +def _validate_torch_long_basis_indices(indices: tuple[int, ...], *, n: Optional[int], role: str) -> None: + if not indices: + return + if max(indices) <= _TORCH_LONG_MAX: + return + dimension = "" if n is None else f" for n={n}" + raise ValueError( + f"{role}{dimension} cannot be represented as torch.long basis bitmasks. " + f"Current Torch-backed executors support bitmask tensorization up to n={TORCH_LONG_BASIS_MAX_N}." + ) + + +def geometric_product_output_grades(left_grade: int, right_grade: int, n: int) -> tuple[int, ...]: + """Return the possible output grades of a homogeneous geometric product.""" + low = abs(int(left_grade) - int(right_grade)) + high = min(int(left_grade) + int(right_grade), 2 * n - int(left_grade) - int(right_grade)) + return tuple(range(low, high + 1, 2)) + + +def expand_output_grades( + left_grades: Iterable[int], + right_grades: Iterable[int], + n: int, + *, + op: GradeProductOp = "gp", + project_grades: Optional[Iterable[int]] = None, +) -> tuple[int, ...]: + """Expand input grade sets into output grades required by ``op``.""" + left = normalize_grades(left_grades, n, name="left_grades") + right = normalize_grades(right_grades, n, name="right_grades") + if op not in {"gp", "wedge", "inner", "commutator", "anti_commutator"}: + raise ValueError(f"Unsupported grade product op {op!r}") + + outputs: set[int] = set() + for left_grade in left: + for right_grade in right: + outputs.update(product_output_grades(left_grade, right_grade, n, op=op)) + + if project_grades is not None: + outputs &= set(normalize_grades(project_grades, n, name="project_grades")) + if not outputs: + raise ValueError( + f"Grade expansion is empty for op={op!r}, left_grades={left}, right_grades={right}, " + f"project_grades={None if project_grades is None else tuple(project_grades)}" + ) + return tuple(sorted(outputs)) + + +def product_output_grades(left_grade: int, right_grade: int, n: int, *, op: GradeProductOp = "gp") -> tuple[int, ...]: + """Return possible output grades for one homogeneous product route.""" + left_grade = int(left_grade) + right_grade = int(right_grade) + if op == "wedge": + grade = left_grade + right_grade + return (grade,) if grade <= n else () + + outputs = geometric_product_output_grades(left_grade, right_grade, n) + if op == "gp": + return outputs + if op not in {"inner", "commutator", "anti_commutator"}: + raise ValueError(f"Unsupported grade product op {op!r}") + + filtered = [] + for output_grade in outputs: + overlap = (left_grade + right_grade - output_grade) // 2 + parity_odd = ((left_grade * right_grade - overlap) % 2) == 1 + if op == "commutator": + keep = parity_odd + else: + keep = not parity_odd + if keep: + filtered.append(output_grade) + return tuple(filtered) + + +def basis_product(index_a: int, index_b: int, p: int, q: int, r: int) -> tuple[int, float]: + """Return ``(index, sign)`` for two canonical basis blade products.""" + n = p + q + r + swap_count = 0 + for bit in range(n): + if index_a & (1 << bit): + swap_count += (index_b & ((1 << bit) - 1)).bit_count() + + sign = -1.0 if swap_count % 2 else 1.0 + + negative_mask = sum(1 << bit for bit in range(p, p + q)) + if ((index_a & index_b & negative_mask).bit_count() % 2) == 1: + sign = -sign + + null_mask = sum(1 << bit for bit in range(p + q, n)) + if (index_a & index_b & null_mask) != 0: + sign = 0.0 + + return index_a ^ index_b, sign + + +def reverse_sign(index: int) -> float: + """Return the reversion sign for a canonical basis blade.""" + grade = int(index).bit_count() + return -1.0 if ((grade * (grade - 1) // 2) % 2) else 1.0 + + +def operation_coefficient(index_a: int, index_b: int, p: int, q: int, r: int, op: GradeProductOp) -> float: + """Return the scalar coefficient multiplying ``A_i * B_j`` for ``op``.""" + if not operation_may_be_nonzero(index_a, index_b, p, q, r, op): + return 0.0 + + _, sign_ab = basis_product(index_a, index_b, p, q, r) + if op in {"gp", "wedge", "inner"}: + return sign_ab + + if op == "commutator": + return 2.0 * sign_ab + if op == "anti_commutator": + return 2.0 * sign_ab + raise ValueError(f"Unsupported grade product op {op!r}") + + +def operation_may_be_nonzero(index_a: int, index_b: int, p: int, q: int, r: int, op: GradeProductOp) -> bool: + """Return whether an operator can have a non-zero coefficient for a basis pair.""" + overlap_mask = int(index_a) & int(index_b) + if op == "wedge": + return overlap_mask == 0 + + if r > 0: + null_mask = ((1 << r) - 1) << (p + q) + if overlap_mask & null_mask: + return False + if op == "gp": + return True + + parity_odd = _swap_parity_between_orders(index_a, index_b) == 1 + if op == "commutator": + return parity_odd + if op in {"inner", "anti_commutator"}: + return not parity_odd + raise ValueError(f"Unsupported grade product op {op!r}") + + +def _swap_parity_between_orders(index_a: int, index_b: int) -> int: + left_grade = int(index_a).bit_count() + right_grade = int(index_b).bit_count() + overlap = (int(index_a) & int(index_b)).bit_count() + return (left_grade * right_grade - overlap) % 2 diff --git a/core/device.py b/core/foundation/device.py similarity index 68% rename from core/device.py rename to core/foundation/device.py index bf96f69..b53961b 100644 --- a/core/device.py +++ b/core/foundation/device.py @@ -12,11 +12,55 @@ from contextlib import nullcontext from dataclasses import dataclass -from typing import ContextManager +from typing import Any, ContextManager, Optional import torch import torch.nn as nn +FLOAT_DTYPES: dict[str, torch.dtype] = { + "float64": torch.float64, + "float32": torch.float32, + "bfloat16": torch.bfloat16, + "float16": torch.float16, +} + +_DTYPE_ALIASES: dict[str, torch.dtype] = { + **FLOAT_DTYPES, + "fp64": torch.float64, + "double": torch.float64, + "fp32": torch.float32, + "float": torch.float32, + "bf16": torch.bfloat16, + "fp16": torch.float16, + "half": torch.float16, +} + + +def optional_dtype(value: Any) -> Optional[torch.dtype]: + """Parse a torch dtype declaration, preserving ``None`` as unset.""" + if value is None or isinstance(value, torch.dtype): + return value + if isinstance(value, str): + normalized = value.strip().lower() + if not normalized: + return None + if normalized in _DTYPE_ALIASES: + return _DTYPE_ALIASES[normalized] + raise ValueError(f"Unsupported torch dtype declaration: {value!r}") + + +def resolve_dtype(value: Any, default: torch.dtype = torch.float32) -> torch.dtype: + """Parse a torch dtype declaration and fall back to ``default`` when unset.""" + return optional_dtype(value) or default + + +def dtype_name(dtype: torch.dtype) -> str: + """Return the canonical short name for a torch dtype.""" + for name, candidate in FLOAT_DTYPES.items(): + if candidate == dtype: + return name + return str(dtype).replace("torch.", "") + def resolve_device(device: str = "auto") -> str: """Resolve ``'auto'`` to the best available accelerator. @@ -47,6 +91,8 @@ class DeviceConfig: (``aot_eager`` for MPS, ``inductor`` for CUDA/CPU). MPS does not fully support the inductor backend. amp: Enable automatic mixed precision (CUDA only). + amp_dtype: Optional autocast dtype. ``None`` uses PyTorch's autocast + default; explicit values should be ``float16`` or ``bfloat16``. cudnn_benchmark: Set :attr:`torch.backends.cudnn.benchmark`. ``None`` -> auto (``True`` for CUDA). """ @@ -57,10 +103,12 @@ class DeviceConfig: compile_model: bool = False compile_backend: str | None = None amp: bool = False + amp_dtype: torch.dtype | str | None = None cudnn_benchmark: bool | None = None def __post_init__(self) -> None: self.device = resolve_device(self.device) + self.amp_dtype = optional_dtype(self.amp_dtype) is_cuda = self.device.startswith("cuda") @@ -74,6 +122,8 @@ def __post_init__(self) -> None: # AMP only makes sense on CUDA if self.amp and not is_cuda: self.amp = False + if self.amp_dtype is not None and self.amp_dtype not in {torch.float16, torch.bfloat16}: + raise ValueError("amp_dtype must be 'float16', 'bfloat16', or null") # Public helpers @@ -115,23 +165,6 @@ def maybe_compile(self, model: nn.Module) -> nn.Module: ) return model - @property - def dtype(self) -> torch.dtype: - """Working dtype for algebra tables and model parameters. - - Returns ``torch.bfloat16`` when AMP is active on CUDA (bfloat16 is - native on Ampere+ GPUs and avoids fp16 overflow). All other - backends keep ``torch.float32``. - - Pass this to :class:`~core.algebra.CliffordAlgebra` as ``dtype`` and - to :meth:`~tasks.base.BaseTask` model setup so that tables and - parameters are created in the correct precision from the start - rather than requiring a post-hoc ``.to()`` cast. - """ - if self.amp and self.device.startswith("cuda"): - return torch.bfloat16 - return torch.float32 - def get_scaler(self) -> torch.amp.GradScaler | None: """Return a :class:`GradScaler` when AMP is active, else ``None``.""" if not self.amp: @@ -142,4 +175,6 @@ def autocast_context(self) -> ContextManager: """Return an ``autocast`` context manager or :func:`nullcontext`.""" if not self.amp: return nullcontext() - return torch.amp.autocast("cuda") + if self.amp_dtype is None: + return torch.amp.autocast("cuda") + return torch.amp.autocast("cuda", dtype=self.amp_dtype) diff --git a/core/foundation/layout.py b/core/foundation/layout.py new file mode 100644 index 0000000..254263e --- /dev/null +++ b/core/foundation/layout.py @@ -0,0 +1,173 @@ +# Versor: Universal Geometric Algebra Neural Network +# Copyright (C) 2026 Eunkyum Kim +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# + +"""Static algebra and compact grade-layout value objects.""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import Iterable + +import torch + +from core.foundation.basis import basis_index_tuple_for_grades, basis_indices_tensor, normalize_grades + + +@dataclass(frozen=True) +class AlgebraSpec: + """Immutable Clifford signature metadata used by grade planners.""" + + p: int + q: int = 0 + r: int = 0 + + def __post_init__(self) -> None: + if self.p < 0 or self.q < 0 or self.r < 0: + raise ValueError(f"signature counts must be non-negative, got Cl({self.p},{self.q},{self.r})") + + @classmethod + def from_algebra(cls, algebra) -> "AlgebraSpec": + """Build a spec from any algebra-like object with ``p``, ``q``, and ``r`` attributes.""" + return cls(int(algebra.p), int(algebra.q), int(algebra.r)) + + @property + def n(self) -> int: + """Number of basis vectors.""" + return self.p + self.q + self.r + + @property + def dim(self) -> int: + """Number of canonical basis blades.""" + return 1 << self.n + + def layout(self, grades: Iterable[int]) -> "GradeLayout": + """Return a compact layout for ``grades``.""" + return GradeLayout(self, normalize_grades(grades, self.n)) + + +@dataclass(frozen=True) +class GradeLayout: + """Compact basis-lane layout for a fixed grade set.""" + + spec: AlgebraSpec + grades: tuple[int, ...] + _basis_indices: tuple[int, ...] = field(init=False, repr=False) + _indices_cache: dict[str, torch.Tensor] = field(default_factory=dict, init=False, repr=False, compare=False) + _grade_index_cache: dict[str, torch.Tensor] = field(default_factory=dict, init=False, repr=False, compare=False) + _conversion_cache: dict[tuple[AlgebraSpec, tuple[int, ...], str], tuple[torch.Tensor, torch.Tensor]] = field( + default_factory=dict, + init=False, + repr=False, + compare=False, + ) + + def __post_init__(self) -> None: + grades = normalize_grades(self.grades, self.spec.n) + object.__setattr__(self, "grades", grades) + object.__setattr__(self, "_basis_indices", basis_index_tuple_for_grades(self.spec.n, grades)) + + @property + def basis_indices(self) -> tuple[int, ...]: + """Canonical dense basis indices represented by this compact layout.""" + return self._basis_indices + + @property + def dim(self) -> int: + """Number of compact lanes.""" + return len(self.basis_indices) + + @property + def dense_dim(self) -> int: + """Full dense multivector dimension.""" + return self.spec.dim + + def contains_grade(self, grade: int) -> bool: + """Return whether ``grade`` is present in this layout.""" + return int(grade) in self.grades + + def indices_tensor(self, *, device=None) -> torch.Tensor: + """Return basis indices as a tensor on ``device``.""" + key = _device_cache_key(device) + cached = self._indices_cache.get(key) + if cached is None: + cached = basis_indices_tensor(self.basis_indices, n=self.spec.n, role="layout basis indices", device=device) + self._indices_cache[key] = cached + return cached + + def grade_indices_tensor(self, *, device=None) -> torch.Tensor: + """Return per-lane grade ids on ``device``.""" + key = _device_cache_key(device) + cached = self._grade_index_cache.get(key) + if cached is None: + grades = tuple(index.bit_count() for index in self.basis_indices) + cached = torch.tensor(grades, dtype=torch.long, device=device) + self._grade_index_cache[key] = cached + return cached + + def convert(self, values: torch.Tensor, source: "GradeLayout") -> torch.Tensor: + """Convert compact values from ``source`` into this layout. + + Shared basis lanes are copied by canonical basis index. Lanes present in + this layout but absent from ``source`` are filled with zeros, which makes + the method usable for both projections and sparse layout unions without + materializing a full dense multivector. + """ + if source.spec != self.spec: + raise ValueError(f"source layout signature {source.spec} does not match target spec {self.spec}") + if values.shape[-1] != source.dim: + raise ValueError(f"source values last dimension must be {source.dim}, got {values.shape[-1]}") + if source == self: + return values + + output = values.new_zeros(*values.shape[:-1], self.dim) + gather, scatter = self._conversion_tensors(source, device=values.device) + if gather.numel() == 0: + return output + + copied = torch.index_select(values, -1, gather) + return output.index_copy(-1, scatter, copied) + + def compact(self, dense: torch.Tensor) -> torch.Tensor: + """Gather compact lanes from a dense multivector tensor.""" + if dense.shape[-1] != self.dense_dim: + raise ValueError(f"dense last dimension must be {self.dense_dim}, got {dense.shape[-1]}") + return torch.index_select(dense, -1, self.indices_tensor(device=dense.device)) + + def dense(self, values: torch.Tensor) -> torch.Tensor: + """Materialize compact lane values into a dense multivector tensor.""" + if values.shape[-1] != self.dim: + raise ValueError(f"values last dimension must be {self.dim}, got {values.shape[-1]}") + output = values.new_zeros(*values.shape[:-1], self.dense_dim) + return output.index_copy(-1, self.indices_tensor(device=values.device), values) + + def _conversion_tensors(self, source: "GradeLayout", *, device=None) -> tuple[torch.Tensor, torch.Tensor]: + key = (source.spec, source.grades, _device_cache_key(device)) + cached = self._conversion_cache.get(key) + if cached is not None: + return cached + + source_positions = {index: position for position, index in enumerate(source.basis_indices)} + gather_positions: list[int] = [] + scatter_positions: list[int] = [] + for target_position, index in enumerate(self.basis_indices): + source_position = source_positions.get(index) + if source_position is None: + continue + gather_positions.append(source_position) + scatter_positions.append(target_position) + + gather = torch.tensor(gather_positions, dtype=torch.long, device=device) + scatter = torch.tensor(scatter_positions, dtype=torch.long, device=device) + cached = (gather, scatter) + self._conversion_cache[key] = cached + return cached + + +def _device_cache_key(device) -> str: + if device is None: + return "cpu" + return str(torch.device(device)) diff --git a/core/foundation/manifold.py b/core/foundation/manifold.py new file mode 100644 index 0000000..efa0b3d --- /dev/null +++ b/core/foundation/manifold.py @@ -0,0 +1,30 @@ +"""Shared manifold metadata for framework parameters.""" + +from __future__ import annotations + +import torch.nn as nn + +MANIFOLD_SPIN = "spin" +MANIFOLD_SPHERE = "sphere" +MANIFOLD_EUCLIDEAN = "euclidean" + +MANIFOLD_ORDER = (MANIFOLD_SPIN, MANIFOLD_SPHERE, MANIFOLD_EUCLIDEAN) +VALID_MANIFOLDS = frozenset(MANIFOLD_ORDER) + + +def format_valid_manifolds() -> str: + """Return a stable, user-facing list of supported manifold names.""" + return ", ".join(repr(name) for name in MANIFOLD_ORDER) + + +def validate_manifold(manifold: str) -> str: + """Validate and return a manifold tag.""" + if manifold not in VALID_MANIFOLDS: + raise ValueError(f"Unknown manifold {manifold!r}. Must be one of {format_valid_manifolds()}") + return manifold + + +def tag_manifold(param: nn.Parameter, manifold: str) -> nn.Parameter: + """Tag a parameter with its manifold type and return the parameter.""" + param._manifold = validate_manifold(manifold) + return param diff --git a/core/foundation/module.py b/core/foundation/module.py new file mode 100644 index 0000000..8139fc7 --- /dev/null +++ b/core/foundation/module.py @@ -0,0 +1,169 @@ +# Versor: Universal Geometric Algebra Neural Network +# Copyright (C) 2026 Eunkyum Kim +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# + +"""Base PyTorch module for components that share a Clifford algebra.""" + +from typing import Iterable, Optional, Protocol, runtime_checkable + +import torch +import torch.nn as nn + +from core.foundation.layout import GradeLayout + + +@runtime_checkable +class AlgebraLike(Protocol): + """Protocol implemented by dense kernels and planned algebra contexts.""" + + p: int + q: int + r: int + n: int + dim: int + num_grades: int + eps: float + eps_sq: float + planner: object + planning_limits: object + allow_full_layout_products: bool + + @property + def device(self): + """Return the device of algebra-owned buffers.""" + ... + + @property + def dtype(self) -> torch.dtype: + """Return the dtype of algebra-owned floating-point buffers.""" + ... + + def _apply(self, fn): + """Move/cast algebra-owned buffers.""" + ... + + def layout(self, grades: Optional[Iterable[int]] = None) -> GradeLayout: + """Return a compact grade layout or the algebra's default layout.""" + ... + + def default_layout(self) -> GradeLayout: + """Return the algebra default layout.""" + ... + + def resolve_layout( + self, + *, + layout: Optional[GradeLayout] = None, + grades: Optional[Iterable[int]] = None, + mv=None, + allow_full: bool = True, + warn_full: bool = True, + ) -> GradeLayout: + """Resolve static layout metadata for tensors or multivectors.""" + ... + + def grade_indices(self, grades: Iterable[int], *, device=None) -> torch.Tensor: + """Return canonical dense basis indices for ``grades``.""" + ... + + def hermitian_signs( + self, + layout: Optional[GradeLayout] = None, + *, + grades: Optional[Iterable[int]] = None, + device=None, + dtype: Optional[torch.dtype] = None, + ) -> torch.Tensor: + """Return Hermitian signs for a dense or compact layout.""" + ... + + def projected_product(self, A: torch.Tensor, B: torch.Tensor, **kwargs) -> torch.Tensor: + """Apply a declared grade-restricted binary product.""" + ... + + def geometric_product(self, A: torch.Tensor, B: torch.Tensor, **kwargs) -> torch.Tensor: + """Apply the geometric product.""" + ... + + def wedge(self, A: torch.Tensor, B: torch.Tensor, **kwargs) -> torch.Tensor: + """Apply the exterior product.""" + ... + + def inner_product(self, A: torch.Tensor, B: torch.Tensor, **kwargs) -> torch.Tensor: + """Apply the inner product.""" + ... + + def commutator(self, A: torch.Tensor, B: torch.Tensor, **kwargs) -> torch.Tensor: + """Apply the commutator product.""" + ... + + def anti_commutator(self, A: torch.Tensor, B: torch.Tensor, **kwargs) -> torch.Tensor: + """Apply the anti-commutator product.""" + ... + + def grade_projection(self, mv: torch.Tensor, grade: int, **kwargs) -> torch.Tensor: + """Project to a single grade.""" + ... + + def reverse(self, mv: torch.Tensor, **kwargs) -> torch.Tensor: + """Apply reversion.""" + ... + + def grade_involution(self, mv: torch.Tensor, **kwargs) -> torch.Tensor: + """Apply grade involution.""" + ... + + def clifford_conjugation(self, mv: torch.Tensor, **kwargs) -> torch.Tensor: + """Apply Clifford conjugation.""" + ... + + +class CliffordModule(nn.Module): + """Base module for Clifford algebra-aware components. + + ``CliffordModule`` belongs to :mod:`core` because it is shared by layers, + functional losses/activations, models, examples, and experiments. Keeping it + out of :mod:`layers` prevents functional code from importing the eager layer + package just to subclass this base type. + + The module stores a shared algebra reference without registering it as a + PyTorch submodule. In Versor, one algebra instance often owns the + precomputed geometric tensors used by many modules. + """ + + def __init__(self, algebra: AlgebraLike): + """Set up the module with a shared algebra instance.""" + super().__init__() + # Bypass nn.Module.__setattr__ to avoid registering algebra as a child. + object.__setattr__(self, "_algebra", algebra) + + @property + def algebra(self) -> AlgebraLike: + """Return the shared algebra instance.""" + return self._algebra + + @property + def p(self): + return self._algebra.p + + @property + def q(self): + return self._algebra.q + + @property + def r(self): + return self._algebra.r + + def _apply(self, fn): + """Apply device/dtype moves to this module and its shared algebra.""" + result = super()._apply(fn) + if self._algebra is not None: + self._algebra._apply(fn) + return result + + def forward(self, x): + """Perform the forward pass computation.""" + raise NotImplementedError diff --git a/core/validation.py b/core/foundation/validation.py similarity index 100% rename from core/validation.py rename to core/foundation/validation.py diff --git a/core/metric.py b/core/metric.py deleted file mode 100644 index c9c23f9..0000000 --- a/core/metric.py +++ /dev/null @@ -1,351 +0,0 @@ -# Versor: Universal Geometric Algebra Neural Network -# Copyright (C) 2026 Eunkyum Kim -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# - -"""Metric definitions for Clifford algebras. - -Provides distances, norms, and inner products that respect -the metric signature. -""" - -import torch - -from core.algebra import CliffordAlgebra - - -def _hermitian_signs(algebra: CliffordAlgebra) -> torch.Tensor: - """Return the precomputed Hermitian sign tensor from the algebra. - - The Hermitian inner product on Cl(p,q) is: - _H = sum_I (conj_sign_I * metric_sign_I) * a_I * b_I - - This is precomputed as ``conj_signs * diagonal(cayley_signs)`` - and registered as a buffer on the algebra in ``_init_derived_tables()``. - - Returns: - Sign tensor [Dim] with values +1, -1, or 0 (null blades). - """ - return algebra._hermitian_signs - - -def inner_product(algebra: CliffordAlgebra, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: - """Compute the scalar product via projection onto grade 0. - - Computes _0. - - Args: - algebra (CliffordAlgebra): The algebra instance. - A (torch.Tensor): First multivector [Batch, Dim]. - B (torch.Tensor): Second multivector [Batch, Dim]. - - Returns: - torch.Tensor: Scalar part [Batch, 1]. - """ - # Optimized: A * B then extract grade 0 - prod = algebra.geometric_product(A, B) - scalar_part = prod[..., 0:1] # Grade 0 - return scalar_part - - -def induced_norm(algebra: CliffordAlgebra, A: torch.Tensor) -> torch.Tensor: - """Compute the induced norm respecting the metric signature. - - Computes ||A|| = sqrt(|_0|). - - Args: - algebra (CliffordAlgebra): The algebra instance. - A (torch.Tensor): Multivector [Batch, Dim]. - - Returns: - torch.Tensor: Norm [Batch, 1]. - """ - A_rev = algebra.reverse(A) - # Scalar product _0 - sq_norm = inner_product(algebra, A, A_rev) - - # In mixed signatures, sq_norm can be negative. - # We return sqrt(|sq_norm|) - return torch.sqrt(torch.abs(sq_norm)) - - -def geometric_distance(algebra: CliffordAlgebra, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: - """Computes geometric distance. - - dist(A, B) = ||A - B||. - - Args: - algebra (CliffordAlgebra): The algebra instance. - A (torch.Tensor): First multivector. - B (torch.Tensor): Second multivector. - - Returns: - torch.Tensor: Distance. - """ - diff = A - B - return induced_norm(algebra, diff) - - -def grade_purity(algebra: CliffordAlgebra, A: torch.Tensor, grade: int) -> torch.Tensor: - """Checks the purity of the grade by examining coefficient energy. - - Purity = ||_k||^2 / ||A||^2. - - Args: - algebra (CliffordAlgebra): The algebra instance. - A (torch.Tensor): Multivector [..., Dim]. - grade (int): Target grade. - - Returns: - torch.Tensor: Purity score [0, 1]. - """ - # Project to grade - A_k = algebra.grade_projection(A, grade) - - # Compute energies (using standard squared norm of coefficients for stability) - energy_k = (A_k**2).sum(dim=-1) - energy_total = (A**2).sum(dim=-1).clamp(min=algebra.eps) - - return energy_k / energy_total - - -def mean_active_grade(algebra: CliffordAlgebra, A: torch.Tensor) -> torch.Tensor: - """Average grade. Identifies the grade where the majority of the energy resides. - - Mean Grade = Sum(k * ||_k||^2) / ||A||^2. - - Args: - algebra (CliffordAlgebra): The algebra instance. - A (torch.Tensor): Multivector. - - Returns: - torch.Tensor: Average grade index. - """ - energy_total = (A**2).sum(dim=-1).clamp(min=algebra.eps) - weighted_sum = torch.zeros_like(energy_total) - - for k in range(algebra.n + 1): - A_k = algebra.grade_projection(A, k) - energy_k = (A_k**2).sum(dim=-1) - weighted_sum += k * energy_k - - return weighted_sum / energy_total - - -# Hermitian Metrics for Mixed-Signature Algebras -# -# In Cl(p,q) with q > 0, the standard norm _0 can be negative -# because basis blades involving negative-signature dimensions square -# to -1. This breaks gradient-based optimization. -# -# The Hermitian inner product uses the algebraically proper formula: -# -# _H = _0 = Sum_I (conj_sign_I * metric_sign_I) * a_I * b_I -# -# where conj_sign_I is the Clifford conjugation sign and metric_sign_I -# is the basis blade self-product sign. We precompute these signs once -# via _hermitian_signs(). For Euclidean algebras Cl(p,0), all signs are -# +1 and this reduces to the simple coefficient inner product. -# -# Additionally, we provide the Clifford conjugate (bar involution) -# and the signature-aware trace form for algebraic computations. - - -def clifford_conjugate(algebra: CliffordAlgebra, mv: torch.Tensor) -> torch.Tensor: - """Clifford conjugation (bar involution). - - Combines reversion with grade involution: - A_bar_k = (-1)^k * (-1)^{k(k-1)/2} * A_k - - This is the natural *-involution on Cl(p,q). Useful for - algebraic computations (e.g., spinor norms, Lipschitz groups). - - Args: - algebra: The algebra instance. - mv: Multivector [..., Dim]. - - Returns: - Conjugated multivector [..., Dim]. - """ - result = mv.clone() - for i in range(algebra.dim): - k = bin(i).count("1") - sign = ((-1) ** k) * ((-1) ** (k * (k - 1) // 2)) - if sign == -1: - result[..., i] = -result[..., i] - return result - - -def hermitian_inner_product(algebra: CliffordAlgebra, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: - """Hermitian inner product on Cl(p,q): _0. - - _H = Sum_I (conj_sign_I * metric_sign_I) * a_I * b_I - - Uses precomputed sign arrays so that the result equals the scalar - part of the geometric product of the Clifford conjugate of A with B. - For Euclidean algebras (q=0), all signs are +1 and this reduces to - the simple coefficient inner product Sum a_I b_I. - - Args: - algebra: The algebra instance. - A: First multivector [..., Dim]. - B: Second multivector [..., Dim]. - - Returns: - Scalar inner product [..., 1]. - """ - signs = _hermitian_signs(algebra) - if signs.dtype != A.dtype: - signs = signs.to(dtype=A.dtype) - return (signs * A * B).sum(dim=-1, keepdim=True) - - -def hermitian_norm(algebra: CliffordAlgebra, A: torch.Tensor) -> torch.Tensor: - """Hermitian norm: ||A||_H = sqrt(|_H|). - - Always real and non-negative for any signature. - Uses abs() since the signed inner product can produce negative - self-products in mixed-signature algebras. - - Args: - algebra: The algebra instance. - A: Multivector [..., Dim]. - - Returns: - Norm [..., 1]. Always >= 0. - """ - sq = hermitian_inner_product(algebra, A, A) - return torch.sqrt(torch.abs(sq)) - - -def hermitian_distance(algebra: CliffordAlgebra, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: - """Hermitian distance: d_H(A, B) = ||A - B||_H. - - Positive-definite metric distance for any signature. - Satisfies: non-negativity, symmetry, triangle inequality, identity. - - Args: - algebra: The algebra instance. - A: First multivector [..., Dim]. - B: Second multivector [..., Dim]. - - Returns: - Distance [..., 1]. Always >= 0. - """ - return hermitian_norm(algebra, A - B) - - -def hermitian_angle(algebra: CliffordAlgebra, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: - """Hermitian angle between multivectors. - - cos(theta) = _H / (||A||_H * ||B||_H) - - Args: - algebra: The algebra instance. - A: First multivector [..., Dim]. - B: Second multivector [..., Dim]. - - Returns: - Angle in radians [..., 1]. - """ - signs = _hermitian_signs(algebra) - if signs.dtype != A.dtype: - signs = signs.to(dtype=A.dtype) - ip = (signs * A * B).sum(dim=-1, keepdim=True) - sq_a = (signs * A * A).sum(dim=-1, keepdim=True) - sq_b = (signs * B * B).sum(dim=-1, keepdim=True) - # Use sqrt(sq_a * sq_b) instead of sqrt(sq_a)*sqrt(sq_b) to avoid - # float32 precision loss from two separate sqrt operations. - denom = torch.sqrt(torch.abs(sq_a) * torch.abs(sq_b)).clamp(min=algebra.eps) - cos_theta = ip / denom - cos_theta = torch.clamp(cos_theta, -1.0, 1.0) - return torch.acos(cos_theta) - - -def grade_hermitian_norm(algebra: CliffordAlgebra, A: torch.Tensor, grade: int) -> torch.Tensor: - """Hermitian norm restricted to a single grade. - - ||_k||_H = sqrt(Sum_{I: |I|=k} a_I**2) - - Measures the energy contribution of a specific grade - in a signature-independent way. - - Args: - algebra: The algebra instance. - A: Multivector [..., Dim]. - grade: Target grade. - - Returns: - Grade-specific norm [..., 1]. - """ - A_k = algebra.grade_projection(A, grade) - return hermitian_norm(algebra, A_k) - - -def hermitian_grade_spectrum(algebra: CliffordAlgebra, A: torch.Tensor) -> torch.Tensor: - """Full Hermitian grade spectrum. - - Returns |_H| for each grade k = 0, ..., n. - Uses abs() to ensure non-negative values in mixed signatures. - - Args: - algebra: The algebra instance. - A: Multivector [..., Dim]. - - Returns: - Grade energies [..., n+1]. Each entry >= 0. - """ - signs = _hermitian_signs(algebra) - if signs.dtype != A.dtype: - signs = signs.to(dtype=A.dtype) - spectrum = [] - for k in range(algebra.n + 1): - A_k = algebra.grade_projection(A, k) - sq = (signs * A_k * A_k).sum(dim=-1, keepdim=True) - spectrum.append(torch.abs(sq)) - return torch.cat(spectrum, dim=-1) - - -def signature_trace_form(algebra: CliffordAlgebra, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: - """Signature-aware trace form: <~A B>_0. - - The standard Clifford algebra scalar product. NOT positive-definite - in mixed signatures. Use hermitian_inner_product for optimization. - - This form is signature-aware and useful for: - - Rotor normalization (R~R = 1) - - Versor validation - - Spinor norm computation - - Args: - algebra: The algebra instance. - A: First multivector [..., Dim]. - B: Second multivector [..., Dim]. - - Returns: - Scalar trace form [..., 1]. Can be negative in mixed signatures. - """ - A_rev = algebra.reverse(A) - prod = algebra.geometric_product(A_rev, B) - return prod[..., 0:1] - - -def signature_norm_squared(algebra: CliffordAlgebra, A: torch.Tensor) -> torch.Tensor: - """Signature-aware squared norm: _0. - - Can be negative in mixed-signature algebras. Returns the raw value - without absolute value, preserving causal structure information. - - For Cl(n,0): always non-negative. - For Cl(p,q) with q>0: sign encodes causal character. - - Args: - algebra: The algebra instance. - A: Multivector [..., Dim]. - - Returns: - Signed squared norm [..., 1]. - """ - return signature_trace_form(algebra, A, A) diff --git a/core/module.py b/core/module.py deleted file mode 100644 index 4f28798..0000000 --- a/core/module.py +++ /dev/null @@ -1,60 +0,0 @@ -# Versor: Universal Geometric Algebra Neural Network -# Copyright (C) 2026 Eunkyum Kim -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# - -"""Base PyTorch module for components that share a Clifford algebra.""" - -import torch.nn as nn - -from .algebra import CliffordAlgebra - - -class CliffordModule(nn.Module): - """Base module for Clifford algebra-aware components. - - ``CliffordModule`` belongs to :mod:`core` because it is shared by layers, - functional losses/activations, models, examples, and experiments. Keeping it - out of :mod:`layers` prevents functional code from importing the eager layer - package just to subclass this base type. - - The module stores a shared :class:`CliffordAlgebra` reference without - registering it as a PyTorch submodule. In Versor, one algebra instance often - owns the precomputed geometric tensors used by many modules. - """ - - def __init__(self, algebra: CliffordAlgebra): - """Set up the module with a shared algebra instance.""" - super().__init__() - # Bypass nn.Module.__setattr__ to avoid registering algebra as a child. - object.__setattr__(self, "_algebra", algebra) - - @property - def algebra(self) -> CliffordAlgebra: - """Return the shared algebra instance.""" - return self._algebra - - @property - def p(self): - return self._algebra.p - - @property - def q(self): - return self._algebra.q - - @property - def r(self): - return self._algebra.r - - def _apply(self, fn): - """Apply device/dtype moves to this module and its shared algebra.""" - result = super()._apply(fn) - if self._algebra is not None: - self._algebra._apply(fn) - return result - - def forward(self, x): - """Perform the forward pass computation.""" - raise NotImplementedError diff --git a/core/multivector.py b/core/multivector.py deleted file mode 100644 index 313bd98..0000000 --- a/core/multivector.py +++ /dev/null @@ -1,267 +0,0 @@ -# Versor: Universal Geometric Algebra Neural Network -# Copyright (C) 2026 Eunkyum Kim -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# - -"""Object-oriented multivector wrapper with operator overloading.""" - -from __future__ import annotations - -import torch - -from core.algebra import CliffordAlgebra - - -class Multivector: - """Object-oriented multivector wrapper with operator overloading. - - Wraps a raw coefficient tensor and its parent ``CliffordAlgebra``, - exposing every core algebra operation as a method or Python operator. - - Attributes: - algebra (CliffordAlgebra): The backend. - tensor (torch.Tensor): The raw data [..., Dim]. - """ - - __slots__ = ("algebra", "tensor") - - def __init__(self, algebra: CliffordAlgebra, tensor: torch.Tensor): - self.algebra = algebra - self.tensor = tensor - - @classmethod - def from_vectors(cls, algebra: CliffordAlgebra, vectors: torch.Tensor) -> Multivector: - """Promotes vectors to multivectors (Grade 1).""" - return cls(algebra, algebra.embed_vector(vectors)) - - @classmethod - def scalar( - cls, algebra: CliffordAlgebra, value: float | torch.Tensor, batch_shape: tuple[int, ...] = () - ) -> Multivector: - """Creates a scalar multivector (grade 0 only).""" - dim = 2**algebra.n - t = torch.zeros(*batch_shape, dim, device=algebra.device, dtype=algebra.dtype) - t[..., 0] = value - return cls(algebra, t) - - def __repr__(self): - return f"Multivector(shape={self.tensor.shape}, algebra=Cl({self.algebra.p},{self.algebra.q},{self.algebra.r}))" - - def _check_algebra(self, other: Multivector) -> None: - s, o = self.algebra, other.algebra - if (s.p, s.q, s.r) != (o.p, o.q, o.r): - raise ValueError(f"Algebra mismatch: Cl({s.p},{s.q},{s.r}) vs Cl({o.p},{o.q},{o.r})") - - def _wrap(self, tensor: torch.Tensor) -> Multivector: - return Multivector(self.algebra, tensor) - - def __add__(self, other): - if isinstance(other, Multivector): - self._check_algebra(other) - return self._wrap(self.tensor + other.tensor) - if isinstance(other, (int, float, torch.Tensor)): - return self._wrap(self.tensor + other) - return NotImplemented - - def __radd__(self, other): - return self.__add__(other) - - def __sub__(self, other): - if isinstance(other, Multivector): - self._check_algebra(other) - return self._wrap(self.tensor - other.tensor) - if isinstance(other, (int, float, torch.Tensor)): - return self._wrap(self.tensor - other) - return NotImplemented - - def __rsub__(self, other): - if isinstance(other, (int, float, torch.Tensor)): - return self._wrap(other - self.tensor) - return NotImplemented - - def __neg__(self): - return self._wrap(-self.tensor) - - def __mul__(self, other): - """Geometric product ``A * B``, or scalar scaling.""" - if isinstance(other, Multivector): - self._check_algebra(other) - return self._wrap(self.algebra.geometric_product(self.tensor, other.tensor)) - if isinstance(other, (int, float)): - return self._wrap(self.tensor * other) - if isinstance(other, torch.Tensor): - return self._wrap(self.tensor * other) - return NotImplemented - - def __rmul__(self, other): - if isinstance(other, (int, float, torch.Tensor)): - return self._wrap(self.tensor * other) - return NotImplemented - - def __truediv__(self, other): - if isinstance(other, (int, float)): - return self._wrap(self.tensor / other) - if isinstance(other, torch.Tensor): - return self._wrap(self.tensor / other) - return NotImplemented - - def __xor__(self, other): - """Wedge (outer) product ``A ^ B``.""" - if isinstance(other, Multivector): - self._check_algebra(other) - return self._wrap(self.algebra.wedge(self.tensor, other.tensor)) - return NotImplemented - - def __or__(self, other): - """Inner product ``A | B``.""" - if isinstance(other, Multivector): - self._check_algebra(other) - return self._wrap(self.algebra.inner_product(self.tensor, other.tensor)) - return NotImplemented - - def __invert__(self): - """Reversion ``~A``.""" - return self._wrap(self.algebra.reverse(self.tensor)) - - def grade(self, k: int) -> Multivector: - """Extract the grade-k component.""" - return self._wrap(self.algebra.grade_projection(self.tensor, k)) - - def reverse(self) -> Multivector: - """Reversion (same as ``~self``).""" - return self._wrap(self.algebra.reverse(self.tensor)) - - def grade_involution(self) -> Multivector: - """Grade involution (main involution): flips odd-grade signs.""" - return self._wrap(self.algebra.grade_involution(self.tensor)) - - def clifford_conjugation(self) -> Multivector: - """Clifford conjugation: grade_involution(reverse(x)).""" - return self._wrap(self.algebra.clifford_conjugation(self.tensor)) - - def dual(self) -> Multivector: - """Hodge dual: maps grade-k to grade-(n-k).""" - return self._wrap(self.algebra.dual(self.tensor)) - - def inverse(self) -> Multivector: - """Blade inverse: B^{-1} = ~B / _0.""" - return self._wrap(self.algebra.blade_inverse(self.tensor)) - - def geometric_product(self, other: Multivector) -> Multivector: - """Explicit geometric product (same as ``self * other``).""" - self._check_algebra(other) - return self._wrap(self.algebra.geometric_product(self.tensor, other.tensor)) - - def wedge(self, other: Multivector) -> Multivector: - """Wedge (outer) product (same as ``self ^ other``).""" - self._check_algebra(other) - return self._wrap(self.algebra.wedge(self.tensor, other.tensor)) - - def inner(self, other: Multivector) -> Multivector: - """Inner product (same as ``self | other``).""" - self._check_algebra(other) - return self._wrap(self.algebra.inner_product(self.tensor, other.tensor)) - - def left_contraction(self, other: Multivector) -> Multivector: - """Left contraction: ``self _| other``.""" - self._check_algebra(other) - return self._wrap(self.algebra.left_contraction(self.tensor, other.tensor)) - - def right_contraction(self, other: Multivector) -> Multivector: - """Right contraction: ``self |_ other``.""" - self._check_algebra(other) - return self._wrap(self.algebra.right_contraction(self.tensor, other.tensor)) - - def commutator(self, other: Multivector) -> Multivector: - """Commutator (Lie bracket): ``[self, other] = self*other - other*self``.""" - self._check_algebra(other) - return self._wrap(self.algebra.commutator(self.tensor, other.tensor)) - - def anti_commutator(self, other: Multivector) -> Multivector: - """Anti-commutator: ``{self, other} = self*other + other*self``.""" - self._check_algebra(other) - return self._wrap(self.algebra.anti_commutator(self.tensor, other.tensor)) - - def norm(self) -> torch.Tensor: - """Induced metric norm (returns scalar tensor).""" - from core.metric import induced_norm - - return induced_norm(self.algebra, self.tensor) - - def norm_sq(self) -> torch.Tensor: - """Squared norm: _0 (returns scalar tensor).""" - return self.algebra.norm_sq(self.tensor) - - def get_grade_norms(self) -> torch.Tensor: - """Per-grade L2 norms.""" - return self.algebra.get_grade_norms(self.tensor) - - def exp(self) -> Multivector: - """Exponential map (bivector -> rotor).""" - return self._wrap(self.algebra.exp(self.tensor)) - - def sandwich(self, x: Multivector) -> Multivector: - """Sandwich product: ``self * x * ~self``. - - Falls back to two geometric products when the tensor shapes - don't match the optimized [N, D] + [N, C, D] layout. - """ - self._check_algebra(x) - R, xt = self.tensor, x.tensor - # Optimized path: R is [N, D], x is [N, C, D] - if R.dim() == 2 and xt.dim() == 3: - return self._wrap(self.algebra.sandwich_product(R, xt)) - # General fallback: two GPs - R_rev = self.algebra.reverse(R) - return self._wrap(self.algebra.geometric_product(self.algebra.geometric_product(R, xt), R_rev)) - - def reflect(self, n: Multivector) -> Multivector: - """Reflect self through hyperplane orthogonal to vector n.""" - self._check_algebra(n) - return self._wrap(self.algebra.reflect(self.tensor, n.tensor)) - - def versor_product(self, x: Multivector) -> Multivector: - """General versor action: ``hat(self) * x * self^{-1}``.""" - self._check_algebra(x) - return self._wrap(self.algebra.versor_product(self.tensor, x.tensor)) - - def blade_project(self, blade: Multivector) -> Multivector: - """Project onto blade subspace: ``(self · B) B^{-1}``.""" - self._check_algebra(blade) - return self._wrap(self.algebra.blade_project(self.tensor, blade.tensor)) - - def blade_reject(self, blade: Multivector) -> Multivector: - """Reject from blade subspace: ``self - proj_B(self)``.""" - self._check_algebra(blade) - return self._wrap(self.algebra.blade_reject(self.tensor, blade.tensor)) - - def to(self, *args, **kwargs) -> Multivector: - """Move/cast the underlying tensor (same API as ``torch.Tensor.to``).""" - return self._wrap(self.tensor.to(*args, **kwargs)) - - def detach(self) -> Multivector: - """Detach from computation graph.""" - return self._wrap(self.tensor.detach()) - - def clone(self) -> Multivector: - """Clone the underlying tensor.""" - return self._wrap(self.tensor.clone()) - - def requires_grad_(self, requires_grad: bool = True) -> Multivector: - """Set requires_grad in-place.""" - self.tensor.requires_grad_(requires_grad) - return self - - @property - def shape(self) -> torch.Size: - return self.tensor.shape - - @property - def device(self) -> torch.device: - return self.tensor.device - - @property - def dtype(self) -> torch.dtype: - return self.tensor.dtype diff --git a/core/planning/__init__.py b/core/planning/__init__.py new file mode 100644 index 0000000..570aa81 --- /dev/null +++ b/core/planning/__init__.py @@ -0,0 +1,37 @@ +# Versor: Universal Geometric Algebra Neural Network +# Copyright (C) 2026 Eunkyum Kim +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# + +"""Static grade planning and Torch executor lowering.""" + +from .flow import GradeFlow +from .layouts import ProductRequest, build_product_request +from .planner import GradePlanner +from .policy import DEFAULT_PLANNING_LIMITS, PlanCost, PlanningLimits +from .product import GradeProductExecutor, GradeProductPlan, build_grade_product_plan +from .tree import GradePathNode, GradePlanTree, build_grade_plan_tree +from .unary import GradeUnaryExecutor, GradeUnaryOp, GradeUnaryPlan, UnaryRequest, build_unary_request + +__all__ = [ + "GradeFlow", + "GradePathNode", + "GradeProductExecutor", + "GradeProductPlan", + "GradePlanTree", + "GradePlanner", + "PlanningLimits", + "PlanCost", + "DEFAULT_PLANNING_LIMITS", + "GradeUnaryExecutor", + "GradeUnaryOp", + "GradeUnaryPlan", + "ProductRequest", + "UnaryRequest", + "build_grade_product_plan", + "build_grade_plan_tree", + "build_product_request", + "build_unary_request", +] diff --git a/core/planning/flow.py b/core/planning/flow.py new file mode 100644 index 0000000..e32c82b --- /dev/null +++ b/core/planning/flow.py @@ -0,0 +1,97 @@ +# Versor: Universal Geometric Algebra Neural Network +# Copyright (C) 2026 Eunkyum Kim +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# + +"""Grade-flow metadata for AoT layout propagation.""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Iterable, Optional + +from core.foundation.basis import GradeProductOp, expand_output_grades, normalize_grades +from core.foundation.layout import AlgebraSpec, GradeLayout + + +@dataclass(frozen=True) +class GradeFlow: + """Static grade/layout metadata passed between planned operations.""" + + spec: AlgebraSpec + layout: GradeLayout + + @classmethod + def from_grades(cls, spec: AlgebraSpec, grades: Iterable[int]) -> "GradeFlow": + """Create a flow from active grades.""" + return cls(spec=spec, layout=spec.layout(grades)) + + @classmethod + def full(cls, spec: AlgebraSpec) -> "GradeFlow": + """Create a full-layout flow.""" + return cls.from_grades(spec, range(spec.n + 1)) + + @classmethod + def scalar(cls, spec: AlgebraSpec) -> "GradeFlow": + """Create a scalar-only flow.""" + return cls.from_grades(spec, (0,)) + + @classmethod + def vector(cls, spec: AlgebraSpec) -> "GradeFlow": + """Create a vector-only flow.""" + return cls.from_grades(spec, (1,)) + + @property + def grades(self) -> tuple[int, ...]: + """Active grades represented by this flow.""" + return self.layout.grades + + @property + def dim(self) -> int: + """Compact lane count represented by this flow.""" + return self.layout.dim + + def project(self, grades: Iterable[int]) -> "GradeFlow": + """Narrow the flow to a subset of active grades.""" + projected = normalize_grades(grades, self.spec.n, name="grades") + missing = tuple(grade for grade in projected if grade not in self.grades) + if missing: + raise ValueError(f"Cannot project missing grades {missing} from active grades {self.grades}") + return GradeFlow.from_grades(self.spec, projected) + + def unary(self, op: str, output_grades: Optional[Iterable[int]] = None) -> "GradeFlow": + """Propagate flow through a unary operation.""" + if output_grades is not None: + return self.project(output_grades) + if op in {"identity", "reverse", "grade_involution", "clifford_conjugation"}: + return self + if op == "grade_projection": + raise ValueError("grade_projection requires output_grades") + raise ValueError(f"Unsupported unary flow op {op!r}") + + def product( + self, + other: "GradeFlow", + *, + op: GradeProductOp = "gp", + output_grades: Optional[Iterable[int]] = None, + ) -> "GradeFlow": + """Propagate flow through a bilinear grade product.""" + self._check_spec(other) + grades = ( + expand_output_grades(self.grades, other.grades, self.spec.n, op=op) + if output_grades is None + else normalize_grades(output_grades, self.spec.n, name="output_grades") + ) + return GradeFlow.from_grades(self.spec, grades) + + def merge(self, other: "GradeFlow") -> "GradeFlow": + """Union two flows, e.g. for addition or concatenation.""" + self._check_spec(other) + return GradeFlow.from_grades(self.spec, (*self.grades, *other.grades)) + + def _check_spec(self, other: "GradeFlow") -> None: + if self.spec != other.spec: + raise ValueError(f"GradeFlow spec mismatch: {self.spec} vs {other.spec}") diff --git a/core/planning/layouts.py b/core/planning/layouts.py new file mode 100644 index 0000000..bcfc364 --- /dev/null +++ b/core/planning/layouts.py @@ -0,0 +1,221 @@ +# Versor: Universal Geometric Algebra Neural Network +# Copyright (C) 2026 Eunkyum Kim +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# + +"""Layout and request normalization for static grade planning.""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Optional + +import torch + +from core.foundation.basis import GradeProductOp, expand_output_grades, normalize_grades +from core.foundation.layout import AlgebraSpec, GradeLayout + +_VALID_PRODUCT_OPS = {"gp", "wedge", "inner", "commutator", "anti_commutator"} + + +@dataclass(frozen=True) +class ProductRequest: + """Fully resolved static request for one bilinear product. + + The request is the planner's intermediate representation. It removes + ambiguity from caller input before any executor is built: layouts are + normalized, compact-vs-dense operand storage is known, and output grades are + inferred when callers do not explicitly project them. + """ + + spec: AlgebraSpec + op: GradeProductOp + left_layout: GradeLayout + right_layout: GradeLayout + output_layout: GradeLayout + left_compact: bool + right_compact: bool + dtype: torch.dtype + device: torch.device + + @property + def left_grades(self) -> tuple[int, ...]: + return self.left_layout.grades + + @property + def right_grades(self) -> tuple[int, ...]: + return self.right_layout.grades + + @property + def output_grades(self) -> tuple[int, ...]: + return self.output_layout.grades + + @property + def cache_key(self) -> tuple[object, ...]: + """Stable key for executor caching.""" + return ( + self.spec, + str(self.device), + str(self.dtype), + self.op, + self.left_grades, + self.right_grades, + self.output_grades, + ) + + +def build_product_request( + spec: AlgebraSpec, + left: torch.Tensor, + right: torch.Tensor, + *, + op: str = "gp", + left_grades=None, + right_grades=None, + output_grades=None, + left_layout: Optional[GradeLayout] = None, + right_layout: Optional[GradeLayout] = None, + output_layout: Optional[GradeLayout] = None, + left_compact: bool = False, + right_compact: bool = False, + full_layout_allowed: bool = True, +) -> ProductRequest: + """Resolve caller input into a static product request.""" + normalized_op = normalize_product_op(op) + left_layout = resolve_operand_layout( + spec, + left, + grades=left_grades, + layout=left_layout, + compact=left_compact, + side="left", + full_layout_allowed=full_layout_allowed, + ) + right_layout = resolve_operand_layout( + spec, + right, + grades=right_grades, + layout=right_layout, + compact=right_compact, + side="right", + full_layout_allowed=full_layout_allowed, + ) + output_layout = resolve_output_layout( + spec, + op=normalized_op, + left_layout=left_layout, + right_layout=right_layout, + output_grades=output_grades, + output_layout=output_layout, + ) + + left_compact = left_compact or is_compact_tensor(spec, left, left_layout) + right_compact = right_compact or is_compact_tensor(spec, right, right_layout) + + return ProductRequest( + spec=spec, + op=normalized_op, + left_layout=left_layout, + right_layout=right_layout, + output_layout=output_layout, + left_compact=left_compact, + right_compact=right_compact, + dtype=torch.promote_types(left.dtype, right.dtype), + device=left.device, + ) + + +def normalize_product_op(op: str) -> GradeProductOp: + """Validate and normalize a product operation name.""" + normalized = str(op) + if normalized not in _VALID_PRODUCT_OPS: + raise ValueError(f"Unsupported grade product op {op!r}") + return normalized # type: ignore[return-value] + + +def resolve_operand_layout( + spec: AlgebraSpec, + tensor: torch.Tensor, + *, + grades=None, + layout: Optional[GradeLayout] = None, + compact: bool = False, + side: str, + full_layout_allowed: bool = True, +) -> GradeLayout: + """Resolve one operand's grade layout from explicit metadata or tensor shape.""" + if layout is not None: + check_layout_spec(spec, layout, f"{side}_layout") + if grades is not None and layout.grades != normalize_grades(grades, spec.n, name=f"{side}_grades"): + raise ValueError(f"{side}_layout and {side}_grades disagree") + _check_operand_shape(spec, tensor, layout, compact=compact, side=side) + return layout + + if grades is not None: + layout = spec.layout(grades) + _check_operand_shape(spec, tensor, layout, compact=compact, side=side) + return layout + + if compact: + raise ValueError(f"{side}_layout or {side}_grades is required for compact {side} input") + if tensor.shape[-1] != spec.dim: + raise ValueError( + f"{side} input has last dimension {tensor.shape[-1]}; declare {side}_layout or " + f"{side}_grades for compact planned execution" + ) + if not full_layout_allowed: + raise ValueError( + f"{side} input would require a full Cl({spec.p},{spec.q},{spec.r}) layout. " + "Declare active grades or enable an explicit low-dimensional full-layout fallback." + ) + return spec.layout(range(spec.n + 1)) + + +def resolve_output_layout( + spec: AlgebraSpec, + *, + op: GradeProductOp, + left_layout: GradeLayout, + right_layout: GradeLayout, + output_grades=None, + output_layout: Optional[GradeLayout] = None, +) -> GradeLayout: + """Resolve the output layout for a product request.""" + if output_layout is not None: + check_layout_spec(spec, output_layout, "output_layout") + if output_grades is not None and output_layout.grades != normalize_grades( + output_grades, spec.n, name="output_grades" + ): + raise ValueError("output_layout and output_grades disagree") + return output_layout + + if output_grades is None: + output_grades = expand_output_grades(left_layout.grades, right_layout.grades, spec.n, op=op) + return spec.layout(output_grades) + + +def check_layout_spec(spec: AlgebraSpec, layout: GradeLayout, name: str) -> None: + """Validate that a layout belongs to ``spec``.""" + if layout.spec != spec: + raise ValueError(f"{name} signature {layout.spec} does not match product spec {spec}") + + +def is_compact_tensor(spec: AlgebraSpec, tensor: torch.Tensor, layout: GradeLayout) -> bool: + """Return whether ``tensor`` already uses ``layout``'s compact lane count.""" + return layout.dim != spec.dim and tensor.shape[-1] == layout.dim + + +def _check_operand_shape( + spec: AlgebraSpec, + tensor: torch.Tensor, + layout: GradeLayout, + *, + compact: bool, + side: str, +) -> None: + expected = layout.dim if compact or is_compact_tensor(spec, tensor, layout) else spec.dim + if tensor.shape[-1] != expected: + storage = "compact" if expected == layout.dim else "dense" + raise ValueError(f"{side} {storage} last dimension must be {expected}, got {tensor.shape[-1]}") diff --git a/core/planning/planner.py b/core/planning/planner.py new file mode 100644 index 0000000..978111b --- /dev/null +++ b/core/planning/planner.py @@ -0,0 +1,335 @@ +# Versor: Universal Geometric Algebra Neural Network +# Copyright (C) 2026 Eunkyum Kim +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# + +"""Grade-aware planner from algebraic intent to static executors.""" + +from __future__ import annotations + +import torch + +from core.foundation.basis import operation_coefficient +from core.foundation.layout import AlgebraSpec, GradeLayout +from core.planning.layouts import ProductRequest, build_product_request, normalize_product_op +from core.planning.policy import ( + full_layout_allowed, + validate_grades_cost, + validate_layout_cost, + validate_product_grades_cost, + validate_product_request, + validate_unary_request, + warn_full_layout_fallback, +) +from core.planning.product import GradeProductExecutor, build_grade_product_plan_from_request +from core.planning.tree import build_grade_plan_tree +from core.planning.unary import ( + GradeUnaryExecutor, + UnaryRequest, + build_unary_plan_from_request, + build_unary_request, + normalize_unary_op, +) + + +class GradePlanner: + """Owns layout and product-plan lowering for one algebra instance. + + The planner is deliberately not an ``nn.Module``. It builds static + executor modules keyed by signature, grades, dtype, and device, while the + algebra remains the source of truth for buffers and dense reference paths. + """ + + def __init__(self, algebra): + self.algebra = algebra + self.spec = AlgebraSpec.from_algebra(algebra) + self._product_executors = {} + self._unary_executors = {} + + def layout(self, grades): + """Return the compact layout for ``grades``.""" + return self.spec.layout(validate_grades_cost(self.algebra, self.spec, grades)) + + def full_layout(self) -> GradeLayout: + """Return the full dense basis layout.""" + return self.layout(range(self.spec.n + 1)) + + def grade_indices(self, grades, *, device=None) -> torch.Tensor: + """Return canonical dense basis indices for ``grades``.""" + if device is None: + device = getattr(self.algebra, "device", None) + return self.layout(grades).indices_tensor(device=device) + + def convert_values(self, values: torch.Tensor, *, source_layout: GradeLayout, target_layout: GradeLayout): + """Convert compact values between layouts without full dense materialization.""" + return target_layout.convert(values, source_layout) + + def bivector_squared_signs(self, *, device=None, dtype: torch.dtype = None) -> torch.Tensor: + """Return ``(e_ab)^2`` signs in canonical grade-2 layout order.""" + if device is None: + device = getattr(self.algebra, "device", None) + if dtype is None: + dtype = getattr(self.algebra, "dtype", torch.float32) + layout = self.layout((2,)) + signs = [ + operation_coefficient(index, index, self.spec.p, self.spec.q, self.spec.r, "gp") + for index in layout.basis_indices + ] + return torch.tensor(signs, dtype=dtype, device=device) + + def clear_cache(self) -> None: + """Drop cached executor modules.""" + self._product_executors.clear() + self._unary_executors.clear() + + def _apply(self, fn): + """Apply a PyTorch module-style transform to cached executor buffers.""" + product_executors = list(self._product_executors.values()) + self._product_executors.clear() + for executor in product_executors: + executor._apply(fn) + self._product_executors[self._product_cache_key(executor)] = executor + + unary_executors = list(self._unary_executors.values()) + self._unary_executors.clear() + for executor in unary_executors: + executor._apply(fn) + self._unary_executors[self._unary_cache_key(executor)] = executor + return self + + def product_executor( + self, + *, + op: str, + left_grades, + right_grades, + output_grades, + dtype, + device, + cache: bool = True, + ): + """Return a cached static executor for a projected bilinear product.""" + left_grades, right_grades, output_grades = validate_product_grades_cost( + self.algebra, + self.spec, + op=op, + left_grades=left_grades, + right_grades=right_grades, + output_grades=output_grades, + ) + request = ProductRequest( + spec=self.spec, + op=normalize_product_op(op), + left_layout=self.layout(left_grades), + right_layout=self.layout(right_grades), + output_layout=self.layout(output_grades), + left_compact=False, + right_compact=False, + dtype=dtype, + device=torch.device(device), + ) + return self.product_executor_for_request(request, cache=cache) + + def product_request( + self, + left: torch.Tensor, + right: torch.Tensor, + *, + op: str = "gp", + left_grades=None, + right_grades=None, + output_grades=None, + left_layout: GradeLayout = None, + right_layout: GradeLayout = None, + output_layout: GradeLayout = None, + left_compact: bool = False, + right_compact: bool = False, + ) -> ProductRequest: + """Normalize product intent into a static request without executing tensors.""" + left_grades = self._default_operand_grades(left_grades, left_layout) + right_grades = self._default_operand_grades(right_grades, right_layout) + if self._implicit_full_operand(left, grades=left_grades, layout=left_layout, compact=left_compact) or ( + self._implicit_full_operand(right, grades=right_grades, layout=right_layout, compact=right_compact) + ): + warn_full_layout_fallback(self.algebra) + self._validate_product_grade_cost_before_layouts( + op=op, + left_grades=left_grades, + right_grades=right_grades, + output_grades=output_grades, + left_layout=left_layout, + right_layout=right_layout, + output_layout=output_layout, + ) + request = build_product_request( + self.spec, + left, + right, + op=op, + left_grades=left_grades, + right_grades=right_grades, + output_grades=output_grades, + left_layout=left_layout, + right_layout=right_layout, + output_layout=output_layout, + left_compact=left_compact, + right_compact=right_compact, + full_layout_allowed=self._full_layout_allowed(), + ) + validate_product_request(self.algebra, request) + return request + + def product_executor_for_request(self, request: ProductRequest, *, cache: bool = True) -> GradeProductExecutor: + """Return an executor for an already normalized product request.""" + validate_product_request(self.algebra, request) + key = request.cache_key + executor = self._product_executors.get(key) if cache else None + if executor is None: + plan = build_grade_product_plan_from_request(request) + executor = GradeProductExecutor(plan) + if cache: + self._product_executors[key] = executor + return executor + + def product_tree(self, *, op: str, left_grades, right_grades, output_grades=None): + """Return planner-only grade tree metadata for a product route.""" + return build_grade_plan_tree( + self.spec, + op=op, + left_grades=left_grades, + right_grades=right_grades, + output_grades=output_grades, + ) + + def unary_request( + self, + values: torch.Tensor, + *, + op: str, + input_grades=None, + output_grades=None, + input_layout: GradeLayout = None, + output_layout: GradeLayout = None, + input_compact: bool = False, + ) -> UnaryRequest: + """Normalize unary intent into a static request without executing tensors.""" + if not (op == "grade_projection" and output_grades is not None): + input_grades = self._default_operand_grades(input_grades, input_layout) + if self._implicit_full_operand(values, grades=input_grades, layout=input_layout, compact=input_compact): + warn_full_layout_fallback(self.algebra) + request = build_unary_request( + self.spec, + values, + op=op, + input_grades=input_grades, + output_grades=output_grades, + input_layout=input_layout, + output_layout=output_layout, + input_compact=input_compact, + full_layout_allowed=self._full_layout_allowed(), + ) + validate_unary_request(self.algebra, request) + return request + + def unary_executor( + self, + *, + op: str, + input_grades, + output_grades=None, + dtype, + device, + cache: bool = True, + ) -> GradeUnaryExecutor: + """Return a cached static executor for a unary operation.""" + op = normalize_unary_op(op) + if op == "grade_projection" and output_grades is None: + raise ValueError("output_grades is required for grade_projection") + input_layout = self.layout(input_grades) + output_layout = input_layout if output_grades is None else self.layout(output_grades) + request = UnaryRequest( + spec=self.spec, + op=op, + input_layout=input_layout, + output_layout=output_layout, + input_compact=False, + dtype=dtype, + device=torch.device(device), + ) + return self.unary_executor_for_request(request, cache=cache) + + def unary_executor_for_request(self, request: UnaryRequest, *, cache: bool = True) -> GradeUnaryExecutor: + """Return an executor for an already normalized unary request.""" + key = request.cache_key + executor = self._unary_executors.get(key) if cache else None + if executor is None: + plan = build_unary_plan_from_request(request) + executor = GradeUnaryExecutor(plan) + if cache: + self._unary_executors[key] = executor + return executor + + def _product_cache_key(self, executor: GradeProductExecutor) -> tuple[object, ...]: + return ( + self.spec, + str(executor.coefficients.device), + str(executor.coefficients.dtype), + executor.op, + executor.left_grades, + executor.right_grades, + executor.output_grades, + ) + + def _unary_cache_key(self, executor: GradeUnaryExecutor) -> tuple[object, ...]: + return ( + self.spec, + str(executor.signs.device), + str(executor.signs.dtype), + executor.op, + executor.input_layout.grades, + executor.output_layout.grades, + ) + + def _full_layout_allowed(self) -> bool: + return full_layout_allowed(self.algebra, self.spec) + + def _implicit_full_operand(self, tensor: torch.Tensor, *, grades, layout, compact: bool) -> bool: + return ( + grades is None + and layout is None + and not compact + and self._full_layout_allowed() + and tensor.shape[-1] == self.spec.dim + ) + + def _default_operand_grades(self, grades, layout: GradeLayout = None): + if grades is not None or layout is not None: + return grades + return getattr(self.algebra, "_default_grades", None) + + def _validate_product_grade_cost_before_layouts( + self, + *, + op: str, + left_grades, + right_grades, + output_grades, + left_layout: GradeLayout = None, + right_layout: GradeLayout = None, + output_layout: GradeLayout = None, + ) -> None: + left = left_layout.grades if left_layout is not None else left_grades + right = right_layout.grades if right_layout is not None else right_grades + if left is None or right is None: + return + output = output_layout.grades if output_layout is not None else output_grades + validate_product_grades_cost( + self.algebra, + self.spec, + op=op, + left_grades=left, + right_grades=right, + output_grades=output, + ) diff --git a/core/planning/policy.py b/core/planning/policy.py new file mode 100644 index 0000000..fb49967 --- /dev/null +++ b/core/planning/policy.py @@ -0,0 +1,233 @@ +# Versor: Universal Geometric Algebra Neural Network +# Copyright (C) 2026 Eunkyum Kim +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# + +"""Static planning policy for layout fallback and plan-cost diagnostics.""" + +from __future__ import annotations + +import warnings +from dataclasses import dataclass +from typing import Optional + +from core.foundation.basis import basis_count_for_grades, expand_output_grades, normalize_grades +from core.foundation.layout import AlgebraSpec, GradeLayout + +FULL_LAYOUT_WARN_N = 8 +FULL_LAYOUT_MAX_N = 12 + + +@dataclass(frozen=True) +class PlanningLimits: + """Static cost limits for planned grade execution. + + ``max_lanes`` protects compact tensor width. ``max_pairs`` protects the + gather/reduce interaction count generated by a bilinear product plan. + """ + + warn_lanes: int = 2048 + max_lanes: int = 4096 + warn_pairs: int = 1_000_000 + max_pairs: int = 8_000_000 + + +@dataclass(frozen=True) +class PlanCost: + """Static cost summary for a layout or planned operation.""" + + spec: AlgebraSpec + kind: str + op: str + left_lanes: int = 0 + right_lanes: int = 0 + output_lanes: int = 0 + pair_count: int = 0 + input_grades: tuple[int, ...] = () + left_grades: tuple[int, ...] = () + right_grades: tuple[int, ...] = () + output_grades: tuple[int, ...] = () + + @property + def max_lanes(self) -> int: + return max(self.left_lanes, self.right_lanes, self.output_lanes) + + +DEFAULT_PLANNING_LIMITS = PlanningLimits() +_WARNED_FULL_LAYOUT_SIGNATURES: set[tuple[int, int, int]] = set() +_WARNED_PLAN_COSTS: set[tuple[object, ...]] = set() + + +def planning_limits_for(algebra) -> PlanningLimits: + """Return per-algebra planning limits, falling back to defaults.""" + return getattr(algebra, "planning_limits", DEFAULT_PLANNING_LIMITS) + + +def full_layout_allowed(algebra, spec: AlgebraSpec) -> bool: + """Return whether implicit full-layout planning is enabled for this algebra.""" + return bool(getattr(algebra, "allow_full_layout_products", False)) and spec.n <= FULL_LAYOUT_MAX_N + + +def warn_full_layout_fallback(algebra) -> None: + """Warn once per signature when implicit full layout is used at n>=8.""" + if getattr(algebra, "n", 0) < FULL_LAYOUT_WARN_N: + return + signature = (int(algebra.p), int(algebra.q), int(algebra.r)) + if signature in _WARNED_FULL_LAYOUT_SIGNATURES: + return + _WARNED_FULL_LAYOUT_SIGNATURES.add(signature) + warnings.warn( + f"Using implicit full Cl({algebra.p},{algebra.q},{algebra.r}) layout at n={algebra.n}. " + "Declare active grades or default_grades to avoid full-layout planning.", + RuntimeWarning, + stacklevel=3, + ) + + +def validate_layout_cost(algebra, layout: GradeLayout, *, role: str = "layout") -> GradeLayout: + """Validate one compact layout against static lane limits.""" + cost = PlanCost( + spec=layout.spec, + kind="layout", + op=role, + output_lanes=layout.dim, + output_grades=layout.grades, + ) + validate_plan_cost(algebra, cost) + return layout + + +def validate_grades_cost(algebra, spec: AlgebraSpec, grades, *, role: str = "layout") -> tuple[int, ...]: + """Validate a grade set before materializing its basis indices.""" + normalized = normalize_grades(grades, spec.n) + cost = PlanCost( + spec=spec, + kind="layout", + op=role, + output_lanes=basis_count_for_grades(spec.n, normalized), + output_grades=normalized, + ) + validate_plan_cost(algebra, cost) + return normalized + + +def validate_product_grades_cost( + algebra, + spec: AlgebraSpec, + *, + op: str, + left_grades, + right_grades, + output_grades=None, +) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]: + """Validate product grade sets before materializing layouts.""" + left = normalize_grades(left_grades, spec.n, name="left_grades") + right = normalize_grades(right_grades, spec.n, name="right_grades") + output = ( + expand_output_grades(left, right, spec.n, op=op) + if output_grades is None + else normalize_grades(output_grades, spec.n, name="output_grades") + ) + left_lanes = basis_count_for_grades(spec.n, left) + right_lanes = basis_count_for_grades(spec.n, right) + output_lanes = basis_count_for_grades(spec.n, output) + validate_plan_cost( + algebra, + PlanCost( + spec=spec, + kind="product", + op=str(op), + left_lanes=left_lanes, + right_lanes=right_lanes, + output_lanes=output_lanes, + pair_count=left_lanes * right_lanes, + left_grades=left, + right_grades=right, + output_grades=output, + ), + ) + return left, right, output + + +def product_plan_cost(request) -> PlanCost: + """Return static cost summary for a product request.""" + return PlanCost( + spec=request.spec, + kind="product", + op=request.op, + left_lanes=request.left_layout.dim, + right_lanes=request.right_layout.dim, + output_lanes=request.output_layout.dim, + pair_count=request.left_layout.dim * request.right_layout.dim, + left_grades=request.left_grades, + right_grades=request.right_grades, + output_grades=request.output_grades, + ) + + +def unary_plan_cost(request) -> PlanCost: + """Return static cost summary for a unary request.""" + return PlanCost( + spec=request.spec, + kind="unary", + op=request.op, + left_lanes=request.input_layout.dim, + output_lanes=request.output_layout.dim, + input_grades=request.input_grades, + output_grades=request.output_grades, + ) + + +def validate_product_request(algebra, request) -> None: + """Validate product request static cost.""" + validate_plan_cost(algebra, product_plan_cost(request)) + + +def validate_unary_request(algebra, request) -> None: + """Validate unary request static cost.""" + validate_plan_cost(algebra, unary_plan_cost(request)) + + +def validate_plan_cost(algebra, cost: PlanCost, *, limits: Optional[PlanningLimits] = None) -> None: + """Raise on excessive static cost and warn once near configured limits.""" + limits = planning_limits_for(algebra) if limits is None else limits + errors = [] + if cost.max_lanes > limits.max_lanes: + errors.append(f"active lanes {cost.max_lanes} exceed max_lanes={limits.max_lanes}") + if cost.pair_count > limits.max_pairs: + errors.append(f"basis interactions {cost.pair_count} exceed max_pairs={limits.max_pairs}") + if errors: + detail = "; ".join(errors) + raise ValueError(f"Static {cost.kind} plan for {cost.op} is too large: {detail}. Declare fewer grades.") + + warnings_to_emit = [] + if cost.max_lanes >= limits.warn_lanes: + warnings_to_emit.append(f"active lanes {cost.max_lanes} are near max_lanes={limits.max_lanes}") + if cost.pair_count >= limits.warn_pairs: + warnings_to_emit.append(f"basis interactions {cost.pair_count} are near max_pairs={limits.max_pairs}") + if warnings_to_emit: + _warn_plan_cost_once(cost, "; ".join(warnings_to_emit)) + + +def _warn_plan_cost_once(cost: PlanCost, detail: str) -> None: + key = ( + cost.spec, + cost.kind, + cost.op, + cost.input_grades, + cost.left_grades, + cost.right_grades, + cost.output_grades, + cost.max_lanes, + cost.pair_count, + ) + if key in _WARNED_PLAN_COSTS: + return + _WARNED_PLAN_COSTS.add(key) + warnings.warn( + f"Static {cost.kind} plan for {cost.op} is large: {detail}.", + RuntimeWarning, + stacklevel=3, + ) diff --git a/core/planning/product.py b/core/planning/product.py new file mode 100644 index 0000000..e09afa6 --- /dev/null +++ b/core/planning/product.py @@ -0,0 +1,349 @@ +# Versor: Universal Geometric Algebra Neural Network +# Copyright (C) 2026 Eunkyum Kim +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# + +"""Static grade-path plans for sparse high-dimensional Clifford products. + +This module describes the lower-level AoT shape needed by high-dimensional +sparse execution: input grades are declared or inferred at construction time, +all basis interactions are expanded once, and forward execution is only gather, +multiply, and indexed reduction over the required output grade lanes. +""" + +from __future__ import annotations + +from typing import Iterable, Optional + +import torch +import torch.nn as nn + +from core.foundation.basis import ( + GradeProductOp, + basis_index_tuple_for_grades, + basis_indices_tensor, + expand_output_grades, + normalize_grades, + operation_coefficient, +) +from core.foundation.layout import AlgebraSpec +from core.planning.layouts import ProductRequest +from core.planning.tree import GradePlanTree, build_grade_plan_tree + + +class GradeProductPlan: + """AoT basis interaction plan for one grade-restricted bilinear product.""" + + def __init__( + self, + *, + p: int, + q: int, + r: int, + op: GradeProductOp, + left_grades: tuple[int, ...], + right_grades: tuple[int, ...], + output_grades: tuple[int, ...], + left_indices: torch.Tensor, + right_indices: torch.Tensor, + output_indices: torch.Tensor, + output_positions: torch.Tensor, + left_compact_positions: torch.Tensor, + right_compact_positions: torch.Tensor, + coefficients: torch.Tensor, + active_output_indices: torch.Tensor, + tree: GradePlanTree, + ): + self.spec = AlgebraSpec(p, q, r) + self.op = op + self.left_layout = self.spec.layout(left_grades) + self.right_layout = self.spec.layout(right_grades) + self.output_layout = self.spec.layout(output_grades) + self.left_indices = left_indices + self.right_indices = right_indices + self.output_indices = output_indices + self.output_positions = output_positions + self.left_compact_positions = left_compact_positions + self.right_compact_positions = right_compact_positions + self.coefficients = coefficients + self.active_output_indices = active_output_indices + self.tree = tree + + @property + def p(self) -> int: + return self.spec.p + + @property + def q(self) -> int: + return self.spec.q + + @property + def r(self) -> int: + return self.spec.r + + @property + def left_grades(self) -> tuple[int, ...]: + return self.left_layout.grades + + @property + def right_grades(self) -> tuple[int, ...]: + return self.right_layout.grades + + @property + def output_grades(self) -> tuple[int, ...]: + return self.output_layout.grades + + @property + def n(self) -> int: + return self.p + self.q + self.r + + @property + def dim(self) -> int: + return 1 << self.n + + @property + def pair_count(self) -> int: + return int(self.left_indices.numel()) + + @property + def output_dim(self) -> int: + return int(self.active_output_indices.numel()) + + @property + def is_empty(self) -> bool: + return self.pair_count == 0 + + @property + def density(self) -> float: + if self.tree.estimated_pairs == 0: + return 0.0 + return self.pair_count / self.tree.estimated_pairs + + +def build_grade_product_plan( + p: int, + q: int = 0, + r: int = 0, + *, + left_grades: Iterable[int], + right_grades: Iterable[int], + output_grades: Optional[Iterable[int]] = None, + op: GradeProductOp = "gp", + device=None, + dtype: torch.dtype = torch.float32, +) -> GradeProductPlan: + """Build an exact static basis-pair plan for a grade-restricted operation.""" + spec = AlgebraSpec(int(p), int(q), int(r)) + tree = build_grade_plan_tree( + spec, + left_grades=left_grades, + right_grades=right_grades, + output_grades=output_grades, + op=op, + ) + return build_grade_product_plan_from_tree(tree, device=device, dtype=dtype) + + +def build_grade_product_plan_from_request( + request: ProductRequest, + *, + device=None, + dtype: Optional[torch.dtype] = None, +) -> GradeProductPlan: + """Build a plan from a normalized product request.""" + tree = build_grade_plan_tree( + request.spec, + left_grades=request.left_grades, + right_grades=request.right_grades, + output_grades=request.output_grades, + op=request.op, + ) + return build_grade_product_plan_from_tree( + tree, + device=request.device if device is None else device, + dtype=request.dtype if dtype is None else dtype, + ) + + +def build_grade_product_plan_from_tree( + tree: GradePlanTree, + *, + device=None, + dtype: torch.dtype = torch.float32, +) -> GradeProductPlan: + """Lower a planner tree into flat Torch gather/reduce buffers.""" + spec = tree.spec + p, q, r = spec.p, spec.q, spec.r + n = spec.n + left_grade_tuple = tree.left_grades + right_grade_tuple = tree.right_grades + output_grade_tuple = tree.output_grades + + left_basis_by_grade = {grade: basis_index_tuple_for_grades(n, (grade,)) for grade in left_grade_tuple} + right_basis_by_grade = {grade: basis_index_tuple_for_grades(n, (grade,)) for grade in right_grade_tuple} + left_position_by_index = { + index: position for position, index in enumerate(spec.layout(left_grade_tuple).basis_indices) + } + right_position_by_index = { + index: position for position, index in enumerate(spec.layout(right_grade_tuple).basis_indices) + } + active_outputs = basis_index_tuple_for_grades(n, output_grade_tuple) + output_position_by_index = {index: position for position, index in enumerate(active_outputs)} + + plan_left: list[int] = [] + plan_right: list[int] = [] + plan_output: list[int] = [] + plan_positions: list[int] = [] + plan_left_compact_positions: list[int] = [] + plan_right_compact_positions: list[int] = [] + plan_coefficients: list[float] = [] + + for path in tree.paths: + path_output_grades = set(path.output_grades) + for left_index in left_basis_by_grade[path.left_grade]: + for right_index in right_basis_by_grade[path.right_grade]: + output_index = left_index ^ right_index + if output_index.bit_count() not in path_output_grades: + continue + output_position = output_position_by_index.get(output_index) + if output_position is None: + continue + coefficient = operation_coefficient(left_index, right_index, p, q, r, tree.op) + if coefficient == 0.0: + continue + plan_left.append(left_index) + plan_right.append(right_index) + plan_output.append(output_index) + plan_positions.append(output_position) + plan_left_compact_positions.append(left_position_by_index[left_index]) + plan_right_compact_positions.append(right_position_by_index[right_index]) + plan_coefficients.append(coefficient) + + return GradeProductPlan( + p=p, + q=q, + r=r, + op=tree.op, + left_grades=left_grade_tuple, + right_grades=right_grade_tuple, + output_grades=output_grade_tuple, + left_indices=basis_indices_tensor(plan_left, n=n, role="left product basis indices", device=device), + right_indices=basis_indices_tensor(plan_right, n=n, role="right product basis indices", device=device), + output_indices=basis_indices_tensor(plan_output, n=n, role="output product basis indices", device=device), + output_positions=torch.tensor(plan_positions, dtype=torch.long, device=device), + left_compact_positions=torch.tensor(plan_left_compact_positions, dtype=torch.long, device=device), + right_compact_positions=torch.tensor(plan_right_compact_positions, dtype=torch.long, device=device), + coefficients=torch.tensor(plan_coefficients, dtype=dtype, device=device), + active_output_indices=basis_indices_tensor(active_outputs, n=n, role="active output basis indices", device=device), + tree=tree, + ) + + +class GradeProductExecutor(nn.Module): + """Compile-friendly grade-restricted product using a static interaction plan. + + ``forward`` returns compact output lanes ordered by ``active_output_indices``. + ``forward_dense`` is an explicit materialization helper for parity checks and + dense callers. + """ + + def __init__(self, plan: GradeProductPlan): + super().__init__() + self.p = plan.p + self.q = plan.q + self.r = plan.r + self.n = plan.n + self.dim = plan.dim + self.op = plan.op + self.left_grades = plan.left_grades + self.right_grades = plan.right_grades + self.output_grades = plan.output_grades + self.left_layout = plan.left_layout + self.right_layout = plan.right_layout + self.output_layout = plan.output_layout + self._output_dim = plan.output_dim + self._pair_count = plan.pair_count + self.register_buffer("left_indices", plan.left_indices, persistent=False) + self.register_buffer("right_indices", plan.right_indices, persistent=False) + self.register_buffer("output_indices", plan.output_indices, persistent=False) + self.register_buffer("output_positions", plan.output_positions, persistent=False) + self.register_buffer("coefficients", plan.coefficients, persistent=False) + self.register_buffer("active_output_indices", plan.active_output_indices, persistent=False) + self.register_buffer("left_compact_positions", plan.left_compact_positions, persistent=False) + self.register_buffer("right_compact_positions", plan.right_compact_positions, persistent=False) + + @property + def output_dim(self) -> int: + return self._output_dim + + @property + def pair_count(self) -> int: + return self._pair_count + + def forward(self, left: torch.Tensor, right: torch.Tensor) -> torch.Tensor: + """Return compact grade-lane output for full dense input tensors.""" + if left.shape[-1] != self.dim: + raise ValueError(f"left last dimension must be {self.dim}, got {left.shape[-1]}") + if right.shape[-1] != self.dim: + raise ValueError(f"right last dimension must be {self.dim}, got {right.shape[-1]}") + + left_terms = torch.index_select(left, -1, self.left_indices) + right_terms = torch.index_select(right, -1, self.right_indices) + left_terms, right_terms = torch.broadcast_tensors(left_terms, right_terms) + terms = left_terms * right_terms * self._coefficients_for(left_terms, right_terms) + + output = terms.new_zeros(*terms.shape[:-1], self.output_dim) + return output.index_add(-1, self.output_positions, terms) + + def forward_compact(self, left: torch.Tensor, right: torch.Tensor) -> torch.Tensor: + """Return compact output for inputs already stored in this plan's compact layouts.""" + if left.shape[-1] != self.left_layout.dim: + raise ValueError(f"left compact dimension must be {self.left_layout.dim}, got {left.shape[-1]}") + if right.shape[-1] != self.right_layout.dim: + raise ValueError(f"right compact dimension must be {self.right_layout.dim}, got {right.shape[-1]}") + + left_terms = torch.index_select(left, -1, self.left_compact_positions) + right_terms = torch.index_select(right, -1, self.right_compact_positions) + left_terms, right_terms = torch.broadcast_tensors(left_terms, right_terms) + terms = left_terms * right_terms * self._coefficients_for(left_terms, right_terms) + + output = terms.new_zeros(*terms.shape[:-1], self.output_dim) + return output.index_add(-1, self.output_positions, terms) + + def forward_pairwise_compact(self, left: torch.Tensor, right: torch.Tensor) -> torch.Tensor: + """Pairwise compact product for sequence-style bilinear scoring. + + ``left`` is ``[..., left_items, left_layout.dim]`` and ``right`` is + ``[..., right_items, right_layout.dim]``. The result is + ``[..., left_items, right_items, output_layout.dim]``. + """ + if left.shape[-1] != self.left_layout.dim: + raise ValueError(f"left compact dimension must be {self.left_layout.dim}, got {left.shape[-1]}") + if right.shape[-1] != self.right_layout.dim: + raise ValueError(f"right compact dimension must be {self.right_layout.dim}, got {right.shape[-1]}") + + prefix = torch.broadcast_shapes(left.shape[:-2], right.shape[:-2]) + left = left.expand(*prefix, *left.shape[-2:]) + right = right.expand(*prefix, *right.shape[-2:]) + + left_terms = torch.index_select(left, -1, self.left_compact_positions) + right_terms = torch.index_select(right, -1, self.right_compact_positions) + terms = left_terms.unsqueeze(-2) * right_terms.unsqueeze(-3) * self._coefficients_for(left_terms, right_terms) + + output = terms.new_zeros(*terms.shape[:-1], self.output_dim) + return output.index_add(-1, self.output_positions, terms) + + def forward_dense(self, left: torch.Tensor, right: torch.Tensor) -> torch.Tensor: + """Return a full ``[..., 2**n]`` dense tensor for dense-kernel parity checks.""" + compact = self.forward(left, right) + output = compact.new_zeros(*compact.shape[:-1], self.dim) + return output.index_copy(-1, self.active_output_indices, compact) + + def _coefficients_for(self, left: torch.Tensor, right: torch.Tensor) -> torch.Tensor: + dtype = torch.promote_types(left.dtype, right.dtype) + coefficients = self.coefficients + if coefficients.dtype == dtype and coefficients.device == left.device: + return coefficients + return coefficients.to(device=left.device, dtype=dtype) diff --git a/core/planning/tree.py b/core/planning/tree.py new file mode 100644 index 0000000..c1312b9 --- /dev/null +++ b/core/planning/tree.py @@ -0,0 +1,136 @@ +# Versor: Universal Geometric Algebra Neural Network +# Copyright (C) 2026 Eunkyum Kim +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# + +"""Planner-only grade tree metadata. + +The tree here is not a runtime backend. It groups declared grade routes before +they are lowered into flat Torch executor buffers. +""" + +from __future__ import annotations + +import math +from dataclasses import dataclass +from typing import Iterable, Optional + +from core.foundation.basis import GradeProductOp, expand_output_grades, normalize_grades, product_output_grades +from core.foundation.layout import AlgebraSpec + + +@dataclass(frozen=True) +class GradePathNode: + """One homogeneous left-grade/right-grade route in a product plan.""" + + path_index: int + left_grade: int + right_grade: int + output_grades: tuple[int, ...] + left_dim: int + right_dim: int + + @property + def estimated_pairs(self) -> int: + """Upper-bound number of basis pairs before metric zero pruning.""" + return self.left_dim * self.right_dim + + +@dataclass(frozen=True) +class GradePlanTree: + """Planner-side grouping for a grade-restricted product.""" + + spec: AlgebraSpec + op: GradeProductOp + left_grades: tuple[int, ...] + right_grades: tuple[int, ...] + output_grades: tuple[int, ...] + paths: tuple[GradePathNode, ...] + chunk_pair_limit: Optional[int] = None + + @property + def path_count(self) -> int: + """Number of active homogeneous product routes.""" + return len(self.paths) + + @property + def estimated_pairs(self) -> int: + """Upper-bound number of basis pairs across all paths.""" + return sum(path.estimated_pairs for path in self.paths) + + @property + def estimated_chunks(self) -> int: + """Number of planner chunks implied by ``chunk_pair_limit``.""" + if self.chunk_pair_limit is None or self.chunk_pair_limit <= 0: + return 1 if self.paths else 0 + return sum(math.ceil(path.estimated_pairs / self.chunk_pair_limit) for path in self.paths) + + def path_for_grades(self, left_grade: int, right_grade: int) -> Optional[GradePathNode]: + """Return the active path for a homogeneous grade pair.""" + for path in self.paths: + if path.left_grade == left_grade and path.right_grade == right_grade: + return path + return None + + +def build_grade_plan_tree( + spec: AlgebraSpec, + *, + left_grades: Iterable[int], + right_grades: Iterable[int], + output_grades: Optional[Iterable[int]] = None, + op: GradeProductOp = "gp", + chunk_pair_limit: Optional[int] = None, +) -> GradePlanTree: + """Build planner metadata for grade route grouping.""" + left = normalize_grades(left_grades, spec.n, name="left_grades") + right = normalize_grades(right_grades, spec.n, name="right_grades") + output = ( + expand_output_grades(left, right, spec.n, op=op) + if output_grades is None + else normalize_grades(output_grades, spec.n, name="output_grades") + ) + output_set = set(output) + + paths = [] + for left_grade in left: + left_dim = _grade_dim(spec.n, left_grade) + for right_grade in right: + route_outputs = product_output_grades(left_grade, right_grade, spec.n, op=op) + route_outputs = tuple(grade for grade in route_outputs if grade in output_set) + if not route_outputs: + continue + paths.append( + GradePathNode( + path_index=len(paths), + left_grade=left_grade, + right_grade=right_grade, + output_grades=route_outputs, + left_dim=left_dim, + right_dim=_grade_dim(spec.n, right_grade), + ) + ) + + return GradePlanTree( + spec=spec, + op=op, + left_grades=left, + right_grades=right, + output_grades=output, + paths=tuple(paths), + chunk_pair_limit=chunk_pair_limit, + ) + + +def _grade_dim(n: int, grade: int) -> int: + if grade < 0 or grade > n: + return 0 + grade = min(grade, n - grade) + numerator = 1 + denominator = 1 + for i in range(1, grade + 1): + numerator *= n - grade + i + denominator *= i + return numerator // denominator diff --git a/core/planning/unary.py b/core/planning/unary.py new file mode 100644 index 0000000..5906b97 --- /dev/null +++ b/core/planning/unary.py @@ -0,0 +1,252 @@ +# Versor: Universal Geometric Algebra Neural Network +# Copyright (C) 2026 Eunkyum Kim +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# + +"""Compile-friendly planned unary operators.""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Literal, Optional + +import torch +import torch.nn as nn + +from core.foundation.basis import normalize_grades, reverse_sign +from core.foundation.layout import AlgebraSpec, GradeLayout +from core.planning.layouts import check_layout_spec, is_compact_tensor, resolve_operand_layout + +GradeUnaryOp = Literal["identity", "reverse", "grade_involution", "clifford_conjugation", "grade_projection"] +_VALID_UNARY_OPS = {"identity", "reverse", "grade_involution", "clifford_conjugation", "grade_projection"} + + +@dataclass(frozen=True) +class UnaryRequest: + """Fully resolved request for one unary planned operation.""" + + spec: AlgebraSpec + op: GradeUnaryOp + input_layout: GradeLayout + output_layout: GradeLayout + input_compact: bool + dtype: torch.dtype + device: torch.device + + @property + def input_grades(self) -> tuple[int, ...]: + return self.input_layout.grades + + @property + def output_grades(self) -> tuple[int, ...]: + return self.output_layout.grades + + @property + def cache_key(self) -> tuple[object, ...]: + return ( + self.spec, + str(self.device), + str(self.dtype), + self.op, + self.input_grades, + self.output_grades, + ) + + +class GradeUnaryPlan: + """Static gather/sign plan for one unary operation.""" + + def __init__( + self, + *, + spec: AlgebraSpec, + op: GradeUnaryOp, + input_grades: tuple[int, ...], + output_grades: tuple[int, ...], + input_positions: torch.Tensor, + output_indices: torch.Tensor, + signs: torch.Tensor, + ): + self.spec = spec + self.op = op + self.input_layout = spec.layout(input_grades) + self.output_layout = spec.layout(output_grades) + self.input_positions = input_positions + self.output_indices = output_indices + self.signs = signs + + @property + def dim(self) -> int: + return self.spec.dim + + @property + def output_dim(self) -> int: + return self.output_layout.dim + + +class GradeUnaryExecutor(nn.Module): + """Torch module for planned unary gather/sign execution.""" + + def __init__(self, plan: GradeUnaryPlan): + super().__init__() + self.spec = plan.spec + self.op = plan.op + self.input_layout = plan.input_layout + self.output_layout = plan.output_layout + self.dim = plan.dim + self.register_buffer("input_positions", plan.input_positions, persistent=False) + self.register_buffer("output_indices", plan.output_indices, persistent=False) + self.register_buffer("signs", plan.signs, persistent=False) + + @property + def output_dim(self) -> int: + return self.output_layout.dim + + def forward(self, values: torch.Tensor) -> torch.Tensor: + """Return compact output lanes for dense input coefficients.""" + if values.shape[-1] != self.dim: + raise ValueError(f"dense last dimension must be {self.dim}, got {values.shape[-1]}") + output = torch.index_select(values, -1, self.output_indices) + return output * self.signs.to(dtype=output.dtype) + + def forward_compact(self, values: torch.Tensor) -> torch.Tensor: + """Return compact output lanes for compact input coefficients.""" + if values.shape[-1] != self.input_layout.dim: + raise ValueError(f"compact last dimension must be {self.input_layout.dim}, got {values.shape[-1]}") + output = torch.index_select(values, -1, self.input_positions) + return output * self.signs.to(dtype=output.dtype) + + def forward_dense(self, values: torch.Tensor) -> torch.Tensor: + """Return dense output coefficients for dense input coefficients.""" + compact = self.forward(values) + return self.output_layout.dense(compact) + + +def build_unary_request( + spec: AlgebraSpec, + values: torch.Tensor, + *, + op: str, + input_grades=None, + output_grades=None, + input_layout: Optional[GradeLayout] = None, + output_layout: Optional[GradeLayout] = None, + input_compact: bool = False, + full_layout_allowed: bool = True, +) -> UnaryRequest: + """Resolve caller input into a static unary request.""" + op = normalize_unary_op(op) + if op == "grade_projection" and input_grades is None and input_layout is None and not input_compact: + if output_layout is not None: + input_layout = output_layout + elif output_grades is not None: + input_grades = output_grades + input_layout = resolve_operand_layout( + spec, + values, + grades=input_grades, + layout=input_layout, + compact=input_compact, + side="input", + full_layout_allowed=full_layout_allowed, + ) + output_layout = resolve_unary_output_layout( + spec, + op=op, + input_layout=input_layout, + output_grades=output_grades, + output_layout=output_layout, + ) + input_compact = input_compact or is_compact_tensor(spec, values, input_layout) + return UnaryRequest( + spec=spec, + op=op, + input_layout=input_layout, + output_layout=output_layout, + input_compact=input_compact, + dtype=values.dtype, + device=values.device, + ) + + +def build_unary_plan_from_request(request: UnaryRequest) -> GradeUnaryPlan: + """Lower a unary request into static gather/sign buffers.""" + input_position_by_index = {index: pos for pos, index in enumerate(request.input_layout.basis_indices)} + input_positions = [] + signs = [] + for index in request.output_layout.basis_indices: + position = input_position_by_index.get(index) + if position is None: + raise ValueError( + f"output basis index {index} is not available in input grades {request.input_layout.grades}" + ) + input_positions.append(position) + signs.append(_unary_sign(request.op, index)) + + return GradeUnaryPlan( + spec=request.spec, + op=request.op, + input_grades=request.input_grades, + output_grades=request.output_grades, + input_positions=torch.tensor(input_positions, dtype=torch.long, device=request.device), + output_indices=torch.tensor(request.output_layout.basis_indices, dtype=torch.long, device=request.device), + signs=torch.tensor(signs, dtype=request.dtype, device=request.device), + ) + + +def normalize_unary_op(op: str) -> GradeUnaryOp: + """Validate and normalize a unary operation name.""" + normalized = str(op) + if normalized not in _VALID_UNARY_OPS: + raise ValueError(f"Unsupported grade unary op {op!r}") + return normalized # type: ignore[return-value] + + +def resolve_unary_output_layout( + spec: AlgebraSpec, + *, + op: GradeUnaryOp, + input_layout: GradeLayout, + output_grades=None, + output_layout: Optional[GradeLayout] = None, +) -> GradeLayout: + """Resolve output layout for a planned unary operation.""" + if output_layout is not None: + check_layout_spec(spec, output_layout, "output_layout") + if output_grades is not None and output_layout.grades != normalize_grades( + output_grades, spec.n, name="output_grades" + ): + raise ValueError("output_layout and output_grades disagree") + return output_layout + + if op == "grade_projection": + if output_grades is None: + raise ValueError("output_grades is required for grade_projection") + projected = normalize_grades(output_grades, spec.n, name="output_grades") + missing = tuple(grade for grade in projected if grade not in input_layout.grades) + if missing: + raise ValueError(f"Cannot project missing grades {missing} from input grades {input_layout.grades}") + return spec.layout(projected) + + if output_grades is not None: + projected = normalize_grades(output_grades, spec.n, name="output_grades") + missing = tuple(grade for grade in projected if grade not in input_layout.grades) + if missing: + raise ValueError(f"Cannot project missing grades {missing} from input grades {input_layout.grades}") + return spec.layout(projected) + return input_layout + + +def _unary_sign(op: GradeUnaryOp, index: int) -> float: + grade = int(index).bit_count() + if op in {"identity", "grade_projection"}: + return 1.0 + if op == "reverse": + return reverse_sign(index) + if op == "grade_involution": + return -1.0 if grade % 2 else 1.0 + if op == "clifford_conjugation": + return (-1.0 if grade % 2 else 1.0) * reverse_sign(index) + raise ValueError(f"Unsupported grade unary op {op!r}") diff --git a/core/runtime/__init__.py b/core/runtime/__init__.py new file mode 100644 index 0000000..0c108e6 --- /dev/null +++ b/core/runtime/__init__.py @@ -0,0 +1,27 @@ +# Versor: Universal Geometric Algebra Neural Network +# Copyright (C) 2026 Eunkyum Kim +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# + +"""Runtime algebra hosts and dense reference operations.""" + +from .accessors import as_multivector, compact_values, grade_indices, hermitian_signs, materialize_dense, resolve_layout +from .algebra import CliffordAlgebra +from .context import AlgebraContext +from .multivector import Multivector +from .projected import AlgebraRuntimeMixin + +__all__ = [ + "AlgebraContext", + "AlgebraRuntimeMixin", + "CliffordAlgebra", + "Multivector", + "as_multivector", + "compact_values", + "grade_indices", + "hermitian_signs", + "materialize_dense", + "resolve_layout", +] diff --git a/core/runtime/accessors.py b/core/runtime/accessors.py new file mode 100644 index 0000000..7cbd44d --- /dev/null +++ b/core/runtime/accessors.py @@ -0,0 +1,220 @@ +# Versor: Universal Geometric Algebra Neural Network +# Copyright (C) 2026 Eunkyum Kim +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# + +"""Functional accessors for algebra layout, grade, and storage metadata.""" + +from __future__ import annotations + +from typing import Iterable, Optional + +import torch + +from core.foundation.basis import normalize_grades, operation_coefficient, reverse_sign +from core.foundation.layout import AlgebraSpec, GradeLayout +from core.planning.policy import ( + FULL_LAYOUT_MAX_N, + validate_grades_cost, + validate_layout_cost, + warn_full_layout_fallback, +) + + +def resolve_layout( + algebra, + *, + layout: Optional[GradeLayout] = None, + grades: Optional[Iterable[int]] = None, + mv=None, + allow_full: bool = True, + warn_full: bool = True, +) -> GradeLayout: + """Resolve static grade layout metadata without inspecting tensor values.""" + spec = AlgebraSpec.from_algebra(algebra) + if layout is not None: + _check_layout_spec(spec, layout, "layout") + if grades is not None and layout.grades != normalize_grades(grades, spec.n, name="grades"): + raise ValueError("layout and grades disagree") + return validate_layout_cost(algebra, layout) + + if grades is not None: + return spec.layout(validate_grades_cost(algebra, spec, grades)) + + if _is_multivector(mv) and getattr(mv, "layout", None) is not None: + mv_layout = mv.layout + _check_layout_spec(spec, mv_layout, "mv.layout") + return validate_layout_cost(algebra, mv_layout) + + default_grades = getattr(algebra, "_default_grades", None) + if default_grades is not None: + cached = getattr(algebra, "_default_layout", None) + if cached is not None: + _check_layout_spec(spec, cached, "default_layout") + return validate_layout_cost(algebra, cached, role="default_layout") + resolved = spec.layout(validate_grades_cost(algebra, spec, default_grades, role="default_layout")) + if hasattr(algebra, "_default_layout"): + algebra._default_layout = resolved + return validate_layout_cost(algebra, resolved, role="default_layout") + + if not allow_full or not bool(getattr(algebra, "allow_full_layout_products", True)): + raise ValueError("No grade layout is available. Declare active grades or configure default_grades.") + if spec.n > FULL_LAYOUT_MAX_N: + raise ValueError( + f"Implicit full Cl({spec.p},{spec.q},{spec.r}) layout is disabled for n>{FULL_LAYOUT_MAX_N}. " + "Declare active grades or configure default_grades." + ) + if warn_full: + warn_full_layout_fallback(algebra) + return spec.layout(validate_grades_cost(algebra, spec, range(spec.n + 1), role="full_layout")) + + +def default_layout(algebra) -> GradeLayout: + """Return the algebra default layout using the central fallback policy.""" + return resolve_layout(algebra) + + +def grade_indices(algebra, grades: Iterable[int], *, device=None) -> torch.Tensor: + """Return canonical dense basis indices for ``grades``.""" + if device is None: + device = getattr(algebra, "device", None) + return resolve_layout(algebra, grades=grades, warn_full=False).indices_tensor(device=device) + + +def hermitian_signs( + algebra, + layout: Optional[GradeLayout] = None, + *, + grades: Optional[Iterable[int]] = None, + device=None, + dtype: Optional[torch.dtype] = None, +) -> torch.Tensor: + """Return Hermitian metric signs for a dense or compact layout.""" + resolved = resolve_layout(algebra, layout=layout, grades=grades) + if device is None: + device = getattr(algebra, "device", None) + if dtype is None: + dtype = getattr(algebra, "dtype", torch.float32) + + dense_signs = getattr(algebra, "_hermitian_signs", None) + if dense_signs is not None: + indices = resolved.indices_tensor(device=dense_signs.device) + signs = torch.index_select(dense_signs, -1, indices) + return signs.to(device=device, dtype=dtype) + + values = [_hermitian_sign_for_index(algebra, index) for index in resolved.basis_indices] + return torch.tensor(values, dtype=dtype, device=device) + + +def compact_values( + algebra, + value, + *, + layout: Optional[GradeLayout] = None, + grades: Optional[Iterable[int]] = None, +) -> tuple[torch.Tensor, GradeLayout]: + """Return compact values plus layout for a tensor or ``Multivector``.""" + resolved = resolve_layout(algebra, layout=layout, grades=grades, mv=value) + if _is_multivector(value): + _check_algebra(algebra, value.algebra) + if value.is_compact: + return resolved.convert(value.values, value.layout), resolved + return resolved.compact(value.coefficients), resolved + + if not isinstance(value, torch.Tensor): + raise TypeError(f"Expected Tensor or Multivector-like value, got {type(value)!r}") + if value.shape[-1] == resolved.dim: + return value, resolved + if value.shape[-1] == resolved.dense_dim: + return resolved.compact(value), resolved + raise ValueError(f"value last dimension must be {resolved.dim} compact or {resolved.dense_dim} dense") + + +def materialize_dense( + algebra, + value, + *, + layout: Optional[GradeLayout] = None, + grades: Optional[Iterable[int]] = None, +) -> torch.Tensor: + """Return dense coefficients subject to the central full-layout policy.""" + if _is_multivector(value): + _check_algebra(algebra, value.algebra) + if not value.is_compact: + return value.coefficients + _check_dense_materialization_allowed(algebra) + return value.layout.dense(value.values) + + if not isinstance(value, torch.Tensor): + raise TypeError(f"Expected Tensor or Multivector-like value, got {type(value)!r}") + if value.shape[-1] == getattr(algebra, "dim"): + return value + resolved = resolve_layout(algebra, layout=layout, grades=grades) + if value.shape[-1] != resolved.dim: + raise ValueError(f"value compact last dimension must be {resolved.dim}, got {value.shape[-1]}") + _check_dense_materialization_allowed(algebra) + return resolved.dense(value) + + +def as_multivector( + algebra, + value, + *, + layout: Optional[GradeLayout] = None, + grades: Optional[Iterable[int]] = None, +): + """Wrap a tensor or return an existing ``Multivector``.""" + from core.runtime.multivector import Multivector + + if isinstance(value, Multivector): + _check_algebra(algebra, value.algebra) + if layout is None and grades is None: + return value + resolved = resolve_layout(algebra, layout=layout, grades=grades, mv=value) + return value.with_layout(resolved) + + if layout is None and grades is None: + if not isinstance(value, torch.Tensor): + raise TypeError(f"Expected Tensor or Multivector-like value, got {type(value)!r}") + return Multivector(algebra, value) + + resolved = resolve_layout(algebra, layout=layout, grades=grades) + if value.shape[-1] == resolved.dim: + return Multivector(algebra, values=value, layout=resolved) + return Multivector(algebra, tensor=value, layout=resolved) + + +def _hermitian_sign_for_index(algebra, index: int) -> float: + grade = int(index).bit_count() + grade_sign = -1.0 if grade % 2 else 1.0 + metric_sign = operation_coefficient(index, index, algebra.p, algebra.q, algebra.r, "gp") + return grade_sign * reverse_sign(index) * metric_sign + + +def _check_layout_spec(spec: AlgebraSpec, layout: GradeLayout, name: str) -> None: + if layout.spec != spec: + raise ValueError(f"{name} signature {layout.spec} does not match algebra signature {spec}") + + +def _check_algebra(expected, actual) -> None: + lhs = (expected.p, expected.q, expected.r) + rhs = (actual.p, actual.q, actual.r) + if lhs != rhs: + raise ValueError(f"Algebra mismatch: Cl{lhs} vs Cl{rhs}") + + +def _check_dense_materialization_allowed(algebra) -> None: + if not bool(getattr(algebra, "allow_full_layout_products", True)): + raise ValueError("Dense materialization is disabled for this algebra. Keep compact values.") + if getattr(algebra, "n", 0) > FULL_LAYOUT_MAX_N: + raise ValueError( + f"Dense materialization is disabled for n>{FULL_LAYOUT_MAX_N}. " + "Keep compact values or declare a smaller active layout." + ) + warn_full_layout_fallback(algebra) + + +def _is_multivector(value) -> bool: + return hasattr(value, "algebra") and hasattr(value, "layout") and hasattr(value, "is_compact") diff --git a/core/algebra.py b/core/runtime/algebra.py similarity index 88% rename from core/algebra.py rename to core/runtime/algebra.py index 56e749e..8dad30e 100644 --- a/core/algebra.py +++ b/core/runtime/algebra.py @@ -17,10 +17,12 @@ import torch import torch.nn as nn -from core.validation import check_multivector +from core.foundation.validation import check_multivector +from core.planning.policy import DEFAULT_PLANNING_LIMITS, PlanningLimits +from core.runtime.projected import AlgebraRuntimeMixin -class CliffordAlgebra(nn.Module): +class CliffordAlgebra(AlgebraRuntimeMixin, nn.Module): """Differentiable Clifford algebra kernel with memory-optimized blocked accumulation. Extends ``nn.Module`` so that all Cayley tables are registered as @@ -50,6 +52,8 @@ def __init__( dtype: torch.dtype = torch.float32, exp_policy: str = "balanced", fixed_iterations: Optional[int] = None, + allow_large_dense: bool = False, + planning_limits: Optional[PlanningLimits] = None, ): """Initialize the algebra and cache the Cayley table. @@ -64,11 +68,11 @@ def __init__( precision (e.g. AMP on CUDA bfloat16 mode). exp_policy (str or ExpPolicy, optional): Bivector exp policy. ``'balanced'`` (default) or ``'precise'``. - See :class:`core.decomposition.ExpPolicy`. + See :class:`core.runtime.decomposition.ExpPolicy`. fixed_iterations (int, optional): Power-iteration step count for the compiled-safe decomposed exp path (used when n>=4). ``None`` (default) auto-derives from ``(exp_policy, dtype, n)`` - via :func:`core.decomposition.resolve_fixed_iterations`, + via :func:`core.runtime.decomposition.resolve_fixed_iterations`, pinned statically at init. """ super().__init__() @@ -76,11 +80,17 @@ def __init__( assert p >= 0, f"p must be non-negative, got {p}" assert q >= 0, f"q must be non-negative, got {q}" assert r >= 0, f"r must be non-negative, got {r}" - assert p + q + r <= 12, f"p + q + r must be <= 12, got {p + q + r}" + max_dense_n = 12 if allow_large_dense else 8 + assert p + q + r <= max_dense_n, ( + f"p + q + r must be <= {max_dense_n} for dense CliffordAlgebra, got {p + q + r}. " + "Use make_algebra(..., kernel='auto') for AlgebraContext or kernel='dense' to explicitly allow Cl9-Cl12." + ) self.p, self.q, self.r = p, q, r self.n = p + q + r self.dim = 2**self.n + self.allow_full_layout_products = True + self.planning_limits = DEFAULT_PLANNING_LIMITS if planning_limits is None else planning_limits # Exp regime: dispatch at init if p == 0 or q == 0: @@ -91,7 +101,7 @@ def __init__( self._exp_regime = "mixed" # Exp policy: controls decomposition iteration budget - from core.decomposition import ExpPolicy, resolve_fixed_iterations + from core.runtime.decomposition import ExpPolicy, resolve_fixed_iterations self._exp_policy = exp_policy if isinstance(exp_policy, ExpPolicy) else ExpPolicy(exp_policy) @@ -146,6 +156,7 @@ def __init__( stacked = torch.stack(grade_masks_list) # [n+1, dim] self.register_buffer("_grade_masks", stacked, persistent=False) self.register_buffer("_grade_masks_float", stacked.to(dtype=cayley_signs.dtype), persistent=False) + self.register_buffer("_g1_indices", stacked[1].nonzero(as_tuple=False).squeeze(-1), persistent=False) # Bivector indices if self.n >= 2: @@ -163,6 +174,10 @@ def __init__( self.eps: float = float(_finfo.eps) self.eps_sq: float = float(_finfo.eps**2) + from core.planning.planner import GradePlanner + + self.planner = GradePlanner(self) + @property def device(self): """Return the device of the algebra tables.""" @@ -177,12 +192,23 @@ def dtype(self) -> torch.dtype: """ return self.cayley_signs.dtype + def bivector_squared_signs(self, *, device=None, dtype: Optional[torch.dtype] = None) -> torch.Tensor: + """Return ``(e_ab)^2`` signs in canonical grade-2 layout order.""" + signs = self.bv_sq_scalar + if device is not None or dtype is not None: + signs = signs.to( + device=self.device if device is None else device, + dtype=self.dtype if dtype is None else dtype, + ) + return signs + def _apply(self, fn): """Propagate device/dtype moves and keep eps tolerances in sync.""" result = super()._apply(fn) _finfo = torch.finfo(self.cayley_signs.dtype) self.eps = float(_finfo.eps) self.eps_sq = float(_finfo.eps**2) + self.planner._apply(fn) return result @property @@ -202,7 +228,7 @@ def exp_policy(self): @exp_policy.setter def exp_policy(self, value): - from core.decomposition import ExpPolicy, resolve_fixed_iterations + from core.runtime.decomposition import ExpPolicy, resolve_fixed_iterations self._exp_policy = value if isinstance(value, ExpPolicy) else ExpPolicy(value) self._exp_fixed_iterations = resolve_fixed_iterations(self._exp_policy, self.dtype, self.n) @@ -254,10 +280,9 @@ def embed_vector(self, vectors: torch.Tensor) -> torch.Tensor: Returns: torch.Tensor: Multivector coefficients [..., dim]. """ - g1_idx = (1 << torch.arange(self.n, device=vectors.device)).long() - mv = torch.zeros(*vectors.shape[:-1], self.dim, device=vectors.device, dtype=vectors.dtype) - mv.scatter_(-1, g1_idx.expand_as(vectors), vectors) - return mv + g1_idx = self._basis_vector_indices(vectors.device) + mv = vectors.new_zeros(*vectors.shape[:-1], self.dim) + return mv.index_copy(-1, g1_idx, vectors) def get_grade_norms(self, mv: torch.Tensor) -> torch.Tensor: """Calculates norms per grade. Useful for invariant features. @@ -329,12 +354,16 @@ def _generate_cayley_table(self, device, dtype: torch.dtype = torch.float32): else: bv_sq_scalar = torch.zeros(0, dtype=cayley_signs.dtype, device=device) - # Precomputed signs for single-pass wedge and inner product - # wedge(A,B) = (AB - BA)/2 uses antisymmetric part of signs + # Precomputed signs for single-pass exterior and symmetric products. + # wedge(A,B) is the exterior product: the grade-sum part of AB. + gi = grade_index.unsqueeze(1) # [D, 1] - left summation index grade + gj_for_result = grade_index[cayley_indices] # [D, D] - right index j = i^k grade + gk = grade_index.unsqueeze(0) # [1, D] - output index k grade + exterior_valid = gk == gi + gj_for_result + wedge_gp_signs = gp_signs * exterior_valid.to(dtype=gp_signs.dtype) + # inner(A,B) = (AB + BA)/2 uses symmetric part of signs - wedge_cayley_signs = (cayley_signs - cayley_signs.T) / 2.0 inner_cayley_signs = (cayley_signs + cayley_signs.T) / 2.0 - wedge_gp_signs = torch.gather(wedge_cayley_signs, 1, cayley_indices) inner_gp_signs = torch.gather(inner_cayley_signs, 1, cayley_indices) # Precomputed signs for commutator [A,B] = AB - BA and @@ -466,7 +495,7 @@ def _compute_signs(self, indices: torch.Tensor, device, dtype: torch.dtype = tor return (commutator_sign * metric_sign).to(dtype=dtype) - def geometric_product(self, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: + def geometric_product(self, A: torch.Tensor, B: torch.Tensor, **kwargs) -> torch.Tensor: """Computes the Geometric Product. Uses vectorized gather + broadcast multiply + sum. @@ -478,6 +507,8 @@ def geometric_product(self, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: Returns: torch.Tensor: The product AB [..., Dim]. """ + if kwargs: + return self.projected_product(A, B, op="gp", **kwargs) check_multivector(A, self, "geometric_product(A)") check_multivector(B, self, "geometric_product(B)") @@ -487,7 +518,7 @@ def geometric_product(self, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: # result[..., k] = sum_i A[..., i] * B[..., cayley[i,k]] * signs[i,k] return torch.matmul(A.unsqueeze(-2), B_gathered * self.gp_signs).squeeze(-2) - def grade_projection(self, mv: torch.Tensor, grade: int) -> torch.Tensor: + def grade_projection(self, mv: torch.Tensor, grade: int, **kwargs) -> torch.Tensor: """Isolates a specific grade. Uses multiplicative masking (mv * float_mask) instead of boolean @@ -500,12 +531,15 @@ def grade_projection(self, mv: torch.Tensor, grade: int) -> torch.Tensor: Returns: torch.Tensor: Projected multivector [..., Dim]. """ + if kwargs: + kwargs.setdefault("output_grades", (int(grade),)) + return self.planned_unary(mv, op="grade_projection", **kwargs) mask = self.grade_masks_float[grade] if mask.dtype != mv.dtype: mask = mask.to(dtype=mv.dtype) return mv * mask - def reverse(self, mv: torch.Tensor) -> torch.Tensor: + def reverse(self, mv: torch.Tensor, **kwargs) -> torch.Tensor: """Computes the reversion. The Clifford conjugate. Args: @@ -514,15 +548,19 @@ def reverse(self, mv: torch.Tensor) -> torch.Tensor: Returns: torch.Tensor: Reversed multivector [..., Dim]. """ + if kwargs: + return self.planned_unary(mv, op="reverse", **kwargs) rev = self.rev_signs if rev.dtype != mv.dtype: rev = rev.to(dtype=mv.dtype) return mv * rev - def wedge(self, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: - """Computes the wedge (outer) product: A ^ B = (AB - BA)/2. + def wedge(self, A: torch.Tensor, B: torch.Tensor, **kwargs) -> torch.Tensor: + """Computes the wedge/exterior product ``A ^ B``. - Single-pass implementation using precomputed antisymmetric signs. + For homogeneous inputs this is the grade-sum part of the + geometric product, ``_{grade(A)+grade(B)}``. For vectors this + coincides with ``(AB - BA) / 2``. Reference: Pence, T., Yamada, D., & Singh, V. (2025). "Composing Linear Layers @@ -535,6 +573,8 @@ def wedge(self, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: Returns: torch.Tensor: Wedge product A ^ B [..., dim]. """ + if kwargs: + return self.projected_product(A, B, op="wedge", **kwargs) B_gathered = B[..., self.cayley_indices] return torch.matmul(A.unsqueeze(-2), B_gathered * self.wedge_gp_signs).squeeze(-2) @@ -559,9 +599,7 @@ def right_contraction(self, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: bv_idx_exp = self._bv_indices.expand(*A.shape[:-1], -1) bv_coeffs = torch.gather(A, -1, bv_idx_exp) # [..., num_bv] - # Grade-1 indices: powers of 2 for basis vectors - g1_idx = torch.arange(self.n, device=A.device) - g1_idx = (1 << g1_idx).long() # [n] + g1_idx = self._basis_vector_indices(A.device) g1_idx_exp = g1_idx.expand(*B.shape[:-1], -1) v_coeffs = torch.gather(B, -1, g1_idx_exp) # [..., n] @@ -577,7 +615,19 @@ def right_contraction(self, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: result.scatter_(-1, g1_idx_exp, result_v) return result - def inner_product(self, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: + def _basis_vector_indices(self, device) -> torch.Tensor: + indices = self._g1_indices + if indices.device != torch.device(device): + indices = indices.to(device=device) + return indices + + def _bivector_indices_for(self, device) -> torch.Tensor: + indices = self._bv_indices + if indices.device != torch.device(device): + indices = indices.to(device=device) + return indices + + def inner_product(self, A: torch.Tensor, B: torch.Tensor, **kwargs) -> torch.Tensor: """Computes the inner product: A . B = (AB + BA)/2. Single-pass implementation using precomputed symmetric signs. @@ -593,14 +643,15 @@ def inner_product(self, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: Returns: torch.Tensor: Inner product A . B [..., dim]. """ + if kwargs: + return self.projected_product(A, B, op="inner", **kwargs) B_gathered = B[..., self.cayley_indices] return torch.matmul(A.unsqueeze(-2), B_gathered * self.inner_gp_signs).squeeze(-2) - def commutator(self, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: + def commutator(self, A: torch.Tensor, B: torch.Tensor, **kwargs) -> torch.Tensor: """Computes the commutator (Lie bracket): [A, B] = AB - BA. - Single-pass implementation using precomputed antisymmetric signs - (same structure as :meth:`wedge` but without the 1/2 factor). + Single-pass implementation using precomputed antisymmetric signs. Args: A (torch.Tensor): Left operand [..., dim]. @@ -609,10 +660,12 @@ def commutator(self, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: Returns: torch.Tensor: Commutator [A, B] [..., dim]. """ + if kwargs: + return self.projected_product(A, B, op="commutator", **kwargs) B_gathered = B[..., self.cayley_indices] return torch.matmul(A.unsqueeze(-2), B_gathered * self.comm_gp_signs).squeeze(-2) - def anti_commutator(self, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: + def anti_commutator(self, A: torch.Tensor, B: torch.Tensor, **kwargs) -> torch.Tensor: """Computes the anti-commutator: {A, B} = AB + BA. Single-pass implementation using precomputed symmetric signs @@ -625,6 +678,8 @@ def anti_commutator(self, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: Returns: torch.Tensor: Anti-commutator {A, B} [..., dim]. """ + if kwargs: + return self.projected_product(A, B, op="anti_commutator", **kwargs) B_gathered = B[..., self.cayley_indices] return torch.matmul(A.unsqueeze(-2), B_gathered * self.anti_comm_gp_signs).squeeze(-2) @@ -799,7 +854,7 @@ def blade_reject(self, mv: torch.Tensor, blade: torch.Tensor) -> torch.Tensor: """ return mv - self.blade_project(mv, blade) - def grade_involution(self, mv: torch.Tensor) -> torch.Tensor: + def grade_involution(self, mv: torch.Tensor, **kwargs) -> torch.Tensor: """Grade involution (main involution): x_hat = sum (-1)^k _k. Flips sign of all odd-grade components, preserves even-grade. @@ -811,12 +866,14 @@ def grade_involution(self, mv: torch.Tensor) -> torch.Tensor: Returns: torch.Tensor: Involuted multivector [..., dim]. """ + if kwargs: + return self.planned_unary(mv, op="grade_involution", **kwargs) signs = self._involution_signs if signs.dtype != mv.dtype: signs = signs.to(dtype=mv.dtype) return mv * signs - def clifford_conjugation(self, mv: torch.Tensor) -> torch.Tensor: + def clifford_conjugation(self, mv: torch.Tensor, **kwargs) -> torch.Tensor: """Clifford conjugation: bar{x} = grade_involution(reverse(x)). Combines reversion and grade involution. For a k-blade: @@ -830,6 +887,8 @@ def clifford_conjugation(self, mv: torch.Tensor) -> torch.Tensor: Returns: torch.Tensor: Conjugated multivector [..., dim]. """ + if kwargs: + return self.planned_unary(mv, op="clifford_conjugation", **kwargs) cs = self.conj_signs if cs.dtype != mv.dtype: cs = cs.to(dtype=mv.dtype) @@ -1057,15 +1116,23 @@ def _exp_compiled_safe(self, B: torch.Tensor) -> torch.Tensor: Returns: torch.Tensor: Rotor exp(B) [..., dim]. """ - from core.decomposition import compiled_safe_decomposed_exp + from core.runtime.decomposition import compiled_safe_decomposed_exp R_closed = self._exp_bivector_closed(B) R_decomposed = compiled_safe_decomposed_exp(self, B, fixed_iterations=self._exp_fixed_iterations) - BB = self.geometric_product(B, B) - # Subtract scalar part, check if residual is negligible - scalar_part = self.grade_projection(BB, 0) - non_scalar_energy = (BB - scalar_part).norm(dim=-1, keepdim=True) + # For bivectors, B*B has only scalar and grade-4 components; the + # grade-4 energy is therefore the simplicity residual. + grade4 = self.projected_product( + B, + B, + op="gp", + left_grades=(2,), + right_grades=(2,), + output_grades=(4,), + compact_output=True, + ) + non_scalar_energy = grade4.norm(dim=-1, keepdim=True) is_simple = non_scalar_energy < self.eps * 100 return torch.where(is_simple, R_closed, R_decomposed) diff --git a/core/runtime/context.py b/core/runtime/context.py new file mode 100644 index 0000000..44c1630 --- /dev/null +++ b/core/runtime/context.py @@ -0,0 +1,155 @@ +# Versor: Universal Geometric Algebra Neural Network +# Copyright (C) 2026 Eunkyum Kim +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# + +"""Lightweight algebra context for planned high-dimensional execution.""" + +from __future__ import annotations + +from typing import Iterable, Optional + +import torch + +from core.foundation.basis import normalize_grades +from core.foundation.device import resolve_device, resolve_dtype +from core.foundation.layout import AlgebraSpec, GradeLayout +from core.planning.planner import GradePlanner +from core.planning.policy import DEFAULT_PLANNING_LIMITS, FULL_LAYOUT_MAX_N, PlanningLimits +from core.runtime.projected import AlgebraRuntimeMixin + + +class AlgebraContext(AlgebraRuntimeMixin): + """Signature and planning host without dense Cayley-table materialization.""" + + def __init__( + self, + p: int, + q: int = 0, + r: int = 0, + *, + device="cuda", + dtype: torch.dtype = torch.float32, + default_grades: Optional[Iterable[int]] = None, + allow_full_layout_products: Optional[bool] = None, + planning_limits: Optional[PlanningLimits] = None, + ): + if p < 0 or q < 0 or r < 0: + raise ValueError(f"signature counts must be non-negative, got Cl({p},{q},{r})") + + self.p = int(p) + self.q = int(q) + self.r = int(r) + self.n = self.p + self.q + self.r + self.dim = 1 << self.n + self.num_grades = self.n + 1 + self.spec = AlgebraSpec(self.p, self.q, self.r) + self._device = torch.device(resolve_device(device) if str(device) == "auto" else device) + self._dtype = resolve_dtype(dtype) + requested_full_layout = False if allow_full_layout_products is None else bool(allow_full_layout_products) + self.allow_full_layout_products = requested_full_layout and self.n <= FULL_LAYOUT_MAX_N + self.planning_limits = DEFAULT_PLANNING_LIMITS if planning_limits is None else planning_limits + self._default_grades = None if default_grades is None else normalize_grades(default_grades, self.n) + self._default_layout: Optional[GradeLayout] = None + self._g1_indices_cache: dict[str, torch.Tensor] = {} + self.planner = GradePlanner(self) + self._sync_eps() + + @property + def device(self): + """Return the context device used for planned executor buffers.""" + return self._device + + @property + def dtype(self) -> torch.dtype: + """Return the context floating-point dtype.""" + return self._dtype + + def bivector_squared_signs(self, *, device=None, dtype: Optional[torch.dtype] = None) -> torch.Tensor: + """Return ``(e_ab)^2`` signs in canonical grade-2 layout order.""" + return self.planner.bivector_squared_signs( + device=self.device if device is None else device, + dtype=self.dtype if dtype is None else dtype, + ) + + def _apply(self, fn): + """Apply a PyTorch module-style device/dtype transform to cached executors.""" + probe = fn(torch.empty((), device=self.device, dtype=self.dtype)) + self._device = probe.device + if probe.dtype.is_floating_point: + self._dtype = probe.dtype + self._sync_eps() + self._g1_indices_cache.clear() + self.planner._apply(fn) + return self + + def to(self, device=None, dtype=None): + """Move the context and cached executors.""" + if device is not None: + self._device = torch.device(resolve_device(device) if str(device) == "auto" else device) + if dtype is not None: + self._dtype = resolve_dtype(dtype) + self._sync_eps() + self._g1_indices_cache.clear() + self.planner.clear_cache() + return self + + def geometric_product(self, A: torch.Tensor, B: torch.Tensor, **kwargs) -> torch.Tensor: + """Plan and execute a geometric product.""" + return self.projected_product(A, B, op="gp", **kwargs) + + def wedge(self, A: torch.Tensor, B: torch.Tensor, **kwargs) -> torch.Tensor: + """Plan and execute an exterior product.""" + return self.projected_product(A, B, op="wedge", **kwargs) + + def inner_product(self, A: torch.Tensor, B: torch.Tensor, **kwargs) -> torch.Tensor: + """Plan and execute the symmetric inner product route.""" + return self.projected_product(A, B, op="inner", **kwargs) + + def commutator(self, A: torch.Tensor, B: torch.Tensor, **kwargs) -> torch.Tensor: + """Plan and execute a commutator product.""" + return self.projected_product(A, B, op="commutator", **kwargs) + + def anti_commutator(self, A: torch.Tensor, B: torch.Tensor, **kwargs) -> torch.Tensor: + """Plan and execute an anti-commutator product.""" + return self.projected_product(A, B, op="anti_commutator", **kwargs) + + def grade_projection(self, mv: torch.Tensor, grade: int, **kwargs) -> torch.Tensor: + """Project a dense multivector tensor to one grade.""" + kwargs.setdefault("output_grades", (int(grade),)) + return self.planned_unary(mv, op="grade_projection", **kwargs) + + def embed_vector(self, vectors: torch.Tensor) -> torch.Tensor: + """Embed grade-1 vector coordinates into dense multivector coefficients.""" + if vectors.shape[-1] != self.n: + raise ValueError(f"vectors last dimension must be {self.n}, got {vectors.shape[-1]}") + output = vectors.new_zeros(*vectors.shape[:-1], self.dim) + return output.index_copy(-1, self._basis_vector_indices(vectors.device), vectors) + + def _basis_vector_indices(self, device) -> torch.Tensor: + resolved = torch.device(device) + key = str(resolved) + cached = self._g1_indices_cache.get(key) + if cached is None: + cached = torch.tensor([1 << bit for bit in range(self.n)], dtype=torch.long, device=resolved) + self._g1_indices_cache[key] = cached + return cached + + def reverse(self, mv: torch.Tensor, **kwargs) -> torch.Tensor: + """Reverse dense or compact multivector coefficients.""" + return self.planned_unary(mv, op="reverse", **kwargs) + + def grade_involution(self, mv: torch.Tensor, **kwargs) -> torch.Tensor: + """Apply grade involution to dense or compact multivector coefficients.""" + return self.planned_unary(mv, op="grade_involution", **kwargs) + + def clifford_conjugation(self, mv: torch.Tensor, **kwargs) -> torch.Tensor: + """Apply Clifford conjugation to dense or compact multivector coefficients.""" + return self.planned_unary(mv, op="clifford_conjugation", **kwargs) + + def _sync_eps(self) -> None: + finfo = torch.finfo(self.dtype) + self.eps = float(finfo.eps) + self.eps_sq = float(finfo.eps**2) diff --git a/core/decomposition.py b/core/runtime/decomposition.py similarity index 94% rename from core/decomposition.py rename to core/runtime/decomposition.py index a4880d4..9f57cd1 100644 --- a/core/decomposition.py +++ b/core/runtime/decomposition.py @@ -313,7 +313,7 @@ def compiled_safe_decomposed_exp( with torch.no_grad(): decomp = _decompose_compiled_safe(algebra, b.detach(), k=k_actual, fixed_iterations=fixed_iterations) - bv_mask = algebra.grade_masks[2] + bv_indices = _bivector_indices(algebra, b.device) # Re-project live bivector and compose rotors result = identity @@ -322,8 +322,8 @@ def compiled_safe_decomposed_exp( plane_norm = b_i_detached.norm(dim=-1, keepdim=True).clamp(min=algebra.eps_sq) plane_dir = b_i_detached / plane_norm - bv_live = residual[..., bv_mask] - plane_bv = plane_dir[..., bv_mask] + bv_live = torch.index_select(residual, -1, bv_indices) + plane_bv = torch.index_select(plane_dir, -1, bv_indices) coeff = (bv_live * plane_bv).sum(dim=-1, keepdim=True) b_i_live = coeff * plane_dir @@ -333,3 +333,14 @@ def compiled_safe_decomposed_exp( result = algebra.geometric_product(result, R_i) return result + + +def _bivector_indices(algebra, device) -> torch.Tensor: + if hasattr(algebra, "_bivector_indices_for"): + return algebra._bivector_indices_for(device) + indices = getattr(algebra, "_bv_indices", None) + if indices is not None: + if indices.device != torch.device(device): + indices = indices.to(device=device) + return indices + return algebra.grade_indices((2,), device=device) diff --git a/core/runtime/metric.py b/core/runtime/metric.py new file mode 100644 index 0000000..8d1c557 --- /dev/null +++ b/core/runtime/metric.py @@ -0,0 +1,517 @@ +# Versor: Universal Geometric Algebra Neural Network +# Copyright (C) 2026 Eunkyum Kim +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# + +"""Metric definitions for Clifford algebras. + +Provides distances, norms, and inner products that respect +the metric signature. +""" + +from typing import Iterable, Optional + +import torch + +from core.foundation.layout import GradeLayout +from core.foundation.module import AlgebraLike +from core.runtime.accessors import compact_values +from core.runtime.accessors import hermitian_signs as _layout_hermitian_signs + + +def _hermitian_signs( + algebra: AlgebraLike, + layout: Optional[GradeLayout] = None, + *, + grades: Optional[Iterable[int]] = None, + device=None, + dtype: Optional[torch.dtype] = None, +) -> torch.Tensor: + """Return Hermitian sign tensor for a dense or compact layout. + + The Hermitian inner product on Cl(p,q) is: + _H = sum_I (conj_sign_I * metric_sign_I) * a_I * b_I + + This is precomputed as ``conj_signs * diagonal(cayley_signs)`` + and registered as a buffer on the algebra in ``_init_derived_tables()``. + + Returns: + Sign tensor [Dim] with values +1, -1, or 0 (null blades). + """ + return _layout_hermitian_signs(algebra, layout=layout, grades=grades, device=device, dtype=dtype) + + +def inner_product(algebra: AlgebraLike, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: + """Compute the scalar product via projection onto grade 0. + + Computes _0. + + Args: + algebra (CliffordAlgebra): The algebra instance. + A (torch.Tensor): First multivector [Batch, Dim]. + B (torch.Tensor): Second multivector [Batch, Dim]. + + Returns: + torch.Tensor: Scalar part [Batch, 1]. + """ + return algebra.projected_geometric_product(A, B, output_grades=(0,), compact_output=True) + + +def induced_norm(algebra: AlgebraLike, A: torch.Tensor) -> torch.Tensor: + """Compute the induced norm respecting the metric signature. + + Computes ||A|| = sqrt(|_0|). + + Args: + algebra (CliffordAlgebra): The algebra instance. + A (torch.Tensor): Multivector [Batch, Dim]. + + Returns: + torch.Tensor: Norm [Batch, 1]. + """ + A_rev = algebra.reverse(A) + # Scalar product _0 + sq_norm = inner_product(algebra, A, A_rev) + + # In mixed signatures, sq_norm can be negative. + # We return sqrt(|sq_norm|) + return torch.sqrt(torch.abs(sq_norm)) + + +def geometric_distance(algebra: AlgebraLike, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: + """Computes geometric distance. + + dist(A, B) = ||A - B||. + + Args: + algebra (CliffordAlgebra): The algebra instance. + A (torch.Tensor): First multivector. + B (torch.Tensor): Second multivector. + + Returns: + torch.Tensor: Distance. + """ + diff = A - B + return induced_norm(algebra, diff) + + +def grade_purity(algebra: AlgebraLike, A: torch.Tensor, grade: int) -> torch.Tensor: + """Checks the purity of the grade by examining coefficient energy. + + Purity = ||_k||^2 / ||A||^2. + + Args: + algebra (CliffordAlgebra): The algebra instance. + A (torch.Tensor): Multivector [..., Dim]. + grade (int): Target grade. + + Returns: + torch.Tensor: Purity score [0, 1]. + """ + # Compute energies (using standard squared norm of coefficients for stability) + grade_masks = getattr(algebra, "grade_masks_float", None) + if grade_masks is not None and A.shape[-1] == getattr(algebra, "dim"): + mask = grade_masks[int(grade)] + if mask.device != A.device or mask.dtype != A.dtype: + mask = mask.to(device=A.device, dtype=A.dtype) + energy_k = (A * A * mask).sum(dim=-1) + else: + A_k = algebra.grade_projection(A, grade) + energy_k = (A_k**2).sum(dim=-1) + energy_total = (A**2).sum(dim=-1).clamp(min=algebra.eps) + + return energy_k / energy_total + + +def mean_active_grade(algebra: AlgebraLike, A: torch.Tensor) -> torch.Tensor: + """Average grade. Identifies the grade where the majority of the energy resides. + + Mean Grade = Sum(k * ||_k||^2) / ||A||^2. + + Args: + algebra (CliffordAlgebra): The algebra instance. + A (torch.Tensor): Multivector. + + Returns: + torch.Tensor: Average grade index. + """ + grade_energies = _dense_grade_energies(algebra, A) + if grade_energies is None: + energy_total = (A**2).sum(dim=-1).clamp(min=algebra.eps) + weighted_sum = torch.zeros_like(energy_total) + for k in range(algebra.n + 1): + A_k = algebra.grade_projection(A, k) + energy_k = (A_k**2).sum(dim=-1) + weighted_sum += k * energy_k + return weighted_sum / energy_total + + weights = torch.arange(algebra.n + 1, device=A.device, dtype=grade_energies.dtype) + weighted_sum = (grade_energies * weights).sum(dim=-1) + energy_total = grade_energies.sum(dim=-1).clamp(min=algebra.eps) + return weighted_sum / energy_total + + +# Hermitian Metrics for Mixed-Signature Algebras +# +# In Cl(p,q) with q > 0, the standard norm _0 can be negative +# because basis blades involving negative-signature dimensions square +# to -1. This breaks gradient-based optimization. +# +# The Hermitian inner product uses the algebraically proper formula: +# +# _H = _0 = Sum_I (conj_sign_I * metric_sign_I) * a_I * b_I +# +# where conj_sign_I is the Clifford conjugation sign and metric_sign_I +# is the basis blade self-product sign. We precompute these signs once +# via _hermitian_signs(). For Euclidean algebras Cl(p,0), all signs are +# +1 and this reduces to the simple coefficient inner product. +# +# Additionally, we provide the Clifford conjugate (bar involution) +# and the signature-aware trace form for algebraic computations. + + +def clifford_conjugate(algebra: AlgebraLike, mv: torch.Tensor) -> torch.Tensor: + """Clifford conjugation (bar involution). + + Combines reversion with grade involution: + A_bar_k = (-1)^k * (-1)^{k(k-1)/2} * A_k + + This is the natural *-involution on Cl(p,q). Useful for + algebraic computations (e.g., spinor norms, Lipschitz groups). + + Args: + algebra: The algebra instance. + mv: Multivector [..., Dim]. + + Returns: + Conjugated multivector [..., Dim]. + """ + return algebra.clifford_conjugation(mv) + + +def hermitian_inner_product( + algebra: AlgebraLike, + A: torch.Tensor, + B: torch.Tensor, + *, + layout: Optional[GradeLayout] = None, + left_layout: Optional[GradeLayout] = None, + right_layout: Optional[GradeLayout] = None, + grades: Optional[Iterable[int]] = None, + left_grades: Optional[Iterable[int]] = None, + right_grades: Optional[Iterable[int]] = None, +) -> torch.Tensor: + """Hermitian inner product on Cl(p,q): _0. + + _H = Sum_I (conj_sign_I * metric_sign_I) * a_I * b_I + + Uses precomputed sign arrays so that the result equals the scalar + part of the geometric product of the Clifford conjugate of A with B. + For Euclidean algebras (q=0), all signs are +1 and this reduces to + the simple coefficient inner product Sum a_I b_I. + + Args: + algebra: The algebra instance. + A: First multivector [..., Dim]. + B: Second multivector [..., Dim]. + + Returns: + Scalar inner product [..., 1]. + """ + A_values, B_values, resolved = _aligned_pair_values( + algebra, + A, + B, + layout=layout, + left_layout=left_layout, + right_layout=right_layout, + grades=grades, + left_grades=left_grades, + right_grades=right_grades, + ) + return _hermitian_inner_values(algebra, A_values, B_values, resolved) + + +def hermitian_norm( + algebra: AlgebraLike, + A: torch.Tensor, + *, + layout: Optional[GradeLayout] = None, + grades: Optional[Iterable[int]] = None, +) -> torch.Tensor: + """Hermitian norm: ||A||_H = sqrt(|_H|). + + Always real and non-negative for any signature. + Uses abs() since the signed inner product can produce negative + self-products in mixed-signature algebras. + + Args: + algebra: The algebra instance. + A: Multivector [..., Dim]. + + Returns: + Norm [..., 1]. Always >= 0. + """ + values, resolved = compact_values(algebra, A, layout=layout, grades=grades) + sq = _hermitian_inner_values(algebra, values, values, resolved) + return torch.sqrt(torch.abs(sq)) + + +def hermitian_distance( + algebra: AlgebraLike, + A: torch.Tensor, + B: torch.Tensor, + *, + layout: Optional[GradeLayout] = None, + left_layout: Optional[GradeLayout] = None, + right_layout: Optional[GradeLayout] = None, + grades: Optional[Iterable[int]] = None, + left_grades: Optional[Iterable[int]] = None, + right_grades: Optional[Iterable[int]] = None, +) -> torch.Tensor: + """Hermitian distance: d_H(A, B) = ||A - B||_H. + + Positive-definite metric distance for any signature. + Satisfies: non-negativity, symmetry, triangle inequality, identity. + + Args: + algebra: The algebra instance. + A: First multivector [..., Dim]. + B: Second multivector [..., Dim]. + + Returns: + Distance [..., 1]. Always >= 0. + """ + A_values, B_values, resolved = _aligned_pair_values( + algebra, + A, + B, + layout=layout, + left_layout=left_layout, + right_layout=right_layout, + grades=grades, + left_grades=left_grades, + right_grades=right_grades, + ) + diff = A_values - B_values + sq = _hermitian_inner_values(algebra, diff, diff, resolved) + return torch.sqrt(torch.abs(sq)) + + +def hermitian_angle( + algebra: AlgebraLike, + A: torch.Tensor, + B: torch.Tensor, + *, + layout: Optional[GradeLayout] = None, + left_layout: Optional[GradeLayout] = None, + right_layout: Optional[GradeLayout] = None, + grades: Optional[Iterable[int]] = None, + left_grades: Optional[Iterable[int]] = None, + right_grades: Optional[Iterable[int]] = None, +) -> torch.Tensor: + """Hermitian angle between multivectors. + + cos(theta) = _H / (||A||_H * ||B||_H) + + Args: + algebra: The algebra instance. + A: First multivector [..., Dim]. + B: Second multivector [..., Dim]. + + Returns: + Angle in radians [..., 1]. + """ + A_values, B_values, resolved = _aligned_pair_values( + algebra, + A, + B, + layout=layout, + left_layout=left_layout, + right_layout=right_layout, + grades=grades, + left_grades=left_grades, + right_grades=right_grades, + ) + signs = _signs_like(algebra, resolved, A_values, B_values) + ip = (signs * A_values * B_values).sum(dim=-1, keepdim=True) + sq_a = (signs * A_values * A_values).sum(dim=-1, keepdim=True) + sq_b = (signs * B_values * B_values).sum(dim=-1, keepdim=True) + # Use sqrt(sq_a * sq_b) instead of sqrt(sq_a)*sqrt(sq_b) to avoid + # float32 precision loss from two separate sqrt operations. + denom = torch.sqrt(torch.abs(sq_a) * torch.abs(sq_b)).clamp(min=algebra.eps) + cos_theta = ip / denom + cos_theta = torch.clamp(cos_theta, -1.0, 1.0) + return torch.acos(cos_theta) + + +def grade_hermitian_norm( + algebra: AlgebraLike, + A: torch.Tensor, + grade: int, + *, + layout: Optional[GradeLayout] = None, + grades: Optional[Iterable[int]] = None, +) -> torch.Tensor: + """Hermitian norm restricted to a single grade. + + ||_k||_H = sqrt(Sum_{I: |I|=k} a_I**2) + + Measures the energy contribution of a specific grade + in a signature-independent way. + + Args: + algebra: The algebra instance. + A: Multivector [..., Dim]. + grade: Target grade. + + Returns: + Grade-specific norm [..., 1]. + """ + values, source_layout = compact_values(algebra, A, layout=layout, grades=grades) + grade_layout = algebra.layout((int(grade),)) + grade_values = grade_layout.convert(values, source_layout) + sq = _hermitian_inner_values(algebra, grade_values, grade_values, grade_layout) + return torch.sqrt(torch.abs(sq)) + + +def hermitian_grade_spectrum( + algebra: AlgebraLike, + A: torch.Tensor, + *, + layout: Optional[GradeLayout] = None, + grades: Optional[Iterable[int]] = None, +) -> torch.Tensor: + """Full Hermitian grade spectrum. + + Returns |_H| for each grade k = 0, ..., n. + Uses abs() to ensure non-negative values in mixed signatures. + + Args: + algebra: The algebra instance. + A: Multivector [..., Dim]. + + Returns: + Grade energies [..., n+1]. Each entry >= 0. + """ + values, source_layout = compact_values(algebra, A, layout=layout, grades=grades) + signs = _signs_like(algebra, source_layout, values, values) + signed_energy = signs * values * values + flat = signed_energy.reshape(-1, source_layout.dim) + grade_ids = source_layout.grade_indices_tensor(device=values.device).unsqueeze(0).expand_as(flat) + spectrum = signed_energy.new_zeros(flat.shape[0], algebra.n + 1) + spectrum.scatter_add_(1, grade_ids, flat) + return spectrum.reshape(*values.shape[:-1], algebra.n + 1).abs() + + +def _aligned_pair_values( + algebra: AlgebraLike, + A, + B, + *, + layout: Optional[GradeLayout] = None, + left_layout: Optional[GradeLayout] = None, + right_layout: Optional[GradeLayout] = None, + grades: Optional[Iterable[int]] = None, + left_grades: Optional[Iterable[int]] = None, + right_grades: Optional[Iterable[int]] = None, +) -> tuple[torch.Tensor, torch.Tensor, GradeLayout]: + """Compact two values into one static layout without dense materialization.""" + shared_left_layout = left_layout if left_layout is not None else layout + shared_right_layout = right_layout if right_layout is not None else layout + shared_left_grades = left_grades if left_grades is not None else grades + shared_right_grades = right_grades if right_grades is not None else grades + + A_values, A_layout = compact_values(algebra, A, layout=shared_left_layout, grades=shared_left_grades) + B_values, B_layout = compact_values(algebra, B, layout=shared_right_layout, grades=shared_right_grades) + resolved = A_layout if A_layout == B_layout else _union_layout(algebra, A_layout, B_layout) + if A_layout != resolved: + A_values = resolved.convert(A_values, A_layout) + if B_layout != resolved: + B_values = resolved.convert(B_values, B_layout) + return A_values, B_values, resolved + + +def _union_layout(algebra: AlgebraLike, left: GradeLayout, right: GradeLayout) -> GradeLayout: + basis = set(left.basis_indices).union(right.basis_indices) + grades = sorted({index.bit_count() for index in basis}) + return algebra.layout(grades) + + +def _hermitian_inner_values( + algebra: AlgebraLike, + A_values: torch.Tensor, + B_values: torch.Tensor, + layout: GradeLayout, +) -> torch.Tensor: + signs = _signs_like(algebra, layout, A_values, B_values) + return (signs * A_values * B_values).sum(dim=-1, keepdim=True) + + +def _signs_like( + algebra: AlgebraLike, + layout: GradeLayout, + A_values: torch.Tensor, + B_values: torch.Tensor, +) -> torch.Tensor: + dtype = torch.promote_types(A_values.dtype, B_values.dtype) + return _hermitian_signs(algebra, layout=layout, device=A_values.device, dtype=dtype) + + +def _dense_grade_energies(algebra: AlgebraLike, A: torch.Tensor) -> Optional[torch.Tensor]: + grade_index = getattr(algebra, "grade_index", None) + if grade_index is None or A.shape[-1] != getattr(algebra, "dim"): + return None + if grade_index.device != A.device: + grade_index = grade_index.to(device=A.device) + + flat = (A * A).reshape(-1, algebra.dim) + idx = grade_index.unsqueeze(0).expand_as(flat) + energies = flat.new_zeros(flat.shape[0], algebra.n + 1) + energies.scatter_add_(1, idx, flat) + return energies.reshape(*A.shape[:-1], algebra.n + 1) + + +def signature_trace_form(algebra: AlgebraLike, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: + """Signature-aware trace form: <~A B>_0. + + The standard Clifford algebra scalar product. NOT positive-definite + in mixed signatures. Use hermitian_inner_product for optimization. + + This form is signature-aware and useful for: + - Rotor normalization (R~R = 1) + - Versor validation + - Spinor norm computation + + Args: + algebra: The algebra instance. + A: First multivector [..., Dim]. + B: Second multivector [..., Dim]. + + Returns: + Scalar trace form [..., 1]. Can be negative in mixed signatures. + """ + A_rev = algebra.reverse(A) + prod = algebra.geometric_product(A_rev, B) + return prod[..., 0:1] + + +def signature_norm_squared(algebra: AlgebraLike, A: torch.Tensor) -> torch.Tensor: + """Signature-aware squared norm: _0. + + Can be negative in mixed-signature algebras. Returns the raw value + without absolute value, preserving causal structure information. + + For Cl(n,0): always non-negative. + For Cl(p,q) with q>0: sign encodes causal character. + + Args: + algebra: The algebra instance. + A: Multivector [..., Dim]. + + Returns: + Signed squared norm [..., 1]. + """ + return signature_trace_form(algebra, A, A) diff --git a/core/runtime/multivector.py b/core/runtime/multivector.py new file mode 100644 index 0000000..9fc2231 --- /dev/null +++ b/core/runtime/multivector.py @@ -0,0 +1,563 @@ +# Versor: Universal Geometric Algebra Neural Network +# Copyright (C) 2026 Eunkyum Kim +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# + +"""Object-oriented multivector wrapper with operator overloading.""" + +from __future__ import annotations + +import torch + +from core.foundation.layout import GradeLayout +from core.foundation.module import AlgebraLike +from core.runtime.accessors import as_multivector as _as_multivector +from core.runtime.accessors import materialize_dense + + +class Multivector: + """Object-oriented multivector wrapper with operator overloading. + + Wraps a raw coefficient tensor and its parent algebra kernel, + exposing every core algebra operation as a method or Python operator. + + Attributes: + algebra (AlgebraLike): The backend. + tensor (torch.Tensor): Dense coefficients [..., Dim]. + values (torch.Tensor): Optional compact lane values [..., layout.dim]. + layout (GradeLayout): Optional compact layout for ``values``. + """ + + __slots__ = ("_tensor", "algebra", "layout", "values") + + def __init__( + self, + algebra: AlgebraLike, + tensor: torch.Tensor = None, + *, + values: torch.Tensor = None, + layout: GradeLayout = None, + ): + self.algebra = algebra + self.layout = layout + if layout is None: + if tensor is None: + raise ValueError("tensor is required when layout is not provided") + self._tensor = tensor + self.values = None + else: + self._check_layout(layout) + if values is None: + if tensor is None: + raise ValueError("values or tensor is required when layout is provided") + values = layout.compact(tensor) + if values.shape[-1] != layout.dim: + raise ValueError(f"compact values last dimension must be {layout.dim}, got {values.shape[-1]}") + self._tensor = None + self.values = values + + @classmethod + def from_tensor( + cls, + algebra: AlgebraLike, + tensor: torch.Tensor, + *, + grades=None, + layout: GradeLayout = None, + ) -> Multivector: + """Wrap dense coefficients or compact coefficients with declared layout metadata.""" + if layout is None and grades is None: + return cls(algebra, tensor) + return _as_multivector(algebra, tensor, layout=layout, grades=grades) + + @classmethod + def from_compact( + cls, + algebra: AlgebraLike, + values: torch.Tensor, + *, + grades=None, + layout: GradeLayout = None, + ) -> Multivector: + """Create a compact multivector from active lane values.""" + if layout is None and grades is None: + raise ValueError("layout or grades is required for compact multivectors") + resolved = algebra.resolve_layout(layout=layout, grades=grades, warn_full=False) + return cls(algebra, values=values, layout=resolved) + + @classmethod + def from_vectors(cls, algebra: AlgebraLike, vectors: torch.Tensor) -> Multivector: + """Promote vectors to compact grade-1 multivectors.""" + layout = algebra.layout((1,)) + if vectors.shape[-1] != layout.dim: + raise ValueError(f"vectors last dimension must be {layout.dim}, got {vectors.shape[-1]}") + return cls(algebra, values=vectors, layout=layout) + + @classmethod + def scalar( + cls, algebra: AlgebraLike, value: float | torch.Tensor, batch_shape: tuple[int, ...] = () + ) -> Multivector: + """Creates a scalar multivector (grade 0 only).""" + layout = algebra.layout((0,)) + values = torch.as_tensor(value, device=algebra.device, dtype=algebra.dtype) + if batch_shape: + target_shape = torch.Size(batch_shape) + if values.ndim == 0: + values = values.expand(*batch_shape).clone() + elif values.shape == torch.Size((*batch_shape, 1)): + return cls(algebra, values=values, layout=layout) + elif values.shape != target_shape: + values = values.expand(*batch_shape).clone() + if values.ndim == 0: + values = values.reshape(1) + elif values.shape[-1] != 1: + values = values.unsqueeze(-1) + return cls(algebra, values=values, layout=layout) + + def __repr__(self): + storage = "compact" if self.is_compact else "dense" + return ( + f"Multivector(shape={self.shape}, storage={storage}, " + f"algebra=Cl({self.algebra.p},{self.algebra.q},{self.algebra.r}))" + ) + + @property + def tensor(self) -> torch.Tensor: + """Dense coefficient tensor. + + This property is an explicit dense boundary. Planned paths that operate + on compact data should use ``values`` or ``coefficients`` directly. + """ + if self._tensor is not None: + return self._tensor + # Do not call this inside core operations that can preserve compact + # ``values`` and ``layout``; materialization belongs at API boundaries. + return materialize_dense(self.algebra, self) + + @tensor.setter + def tensor(self, value: torch.Tensor) -> None: + self._tensor = value + self.values = None + self.layout = None + + @property + def is_compact(self) -> bool: + """Whether this multivector stores compact grade lanes.""" + return self.layout is not None + + @property + def is_dense(self) -> bool: + """Whether this multivector stores dense coefficients.""" + return not self.is_compact + + @property + def grades(self) -> tuple[int, ...] | None: + """Active grades when compact metadata is available.""" + return None if self.layout is None else self.layout.grades + + @property + def lane_count(self) -> int: + """Number of stored coefficient lanes.""" + return self.coefficients.shape[-1] + + @property + def storage(self) -> str: + """Storage mode name.""" + return "compact" if self.is_compact else "dense" + + @property + def coefficients(self) -> torch.Tensor: + """Return the active storage tensor without dense materialization.""" + return self.values if self.is_compact else self._tensor + + def dense(self) -> Multivector: + """Return a dense-storage multivector.""" + return Multivector(self.algebra, self.tensor) + + def compact(self, grades) -> Multivector: + """Return a compact-storage multivector containing ``grades``.""" + layout = self.algebra.layout(grades) + return self.with_layout(layout) + + def with_layout(self, layout: GradeLayout) -> Multivector: + """Return this multivector represented by ``layout``.""" + self._check_layout(layout) + if self.layout == layout: + return Multivector(self.algebra, values=self.values, layout=layout) + if self.is_compact: + return Multivector(self.algebra, values=layout.convert(self.values, self.layout), layout=layout) + return Multivector(self.algebra, values=layout.compact(self.tensor), layout=layout) + + def _check_layout(self, layout: GradeLayout) -> None: + spec = layout.spec + if (spec.p, spec.q, spec.r) != (self.algebra.p, self.algebra.q, self.algebra.r): + raise ValueError( + f"Layout mismatch: Cl({spec.p},{spec.q},{spec.r}) vs " + f"Cl({self.algebra.p},{self.algebra.q},{self.algebra.r})" + ) + + def _check_algebra(self, other: Multivector) -> None: + s, o = self.algebra, other.algebra + if (s.p, s.q, s.r) != (o.p, o.q, o.r): + raise ValueError(f"Algebra mismatch: Cl({s.p},{s.q},{s.r}) vs Cl({o.p},{o.q},{o.r})") + + def _wrap(self, tensor: torch.Tensor) -> Multivector: + return Multivector(self.algebra, tensor) + + def _wrap_compact(self, values: torch.Tensor, layout: GradeLayout) -> Multivector: + return Multivector(self.algebra, values=values, layout=layout) + + def _values_for_layout(self, layout: GradeLayout) -> torch.Tensor: + self._check_layout(layout) + if self.is_compact: + return layout.convert(self.values, self.layout) + return layout.compact(self.tensor) + + def _combined_layout(self, other: Multivector) -> GradeLayout: + left = self.layout if self.is_compact else self.algebra.layout() + right = other.layout if other.is_compact else other.algebra.layout() + basis = set(left.basis_indices).union(right.basis_indices) + grades = sorted({index.bit_count() for index in basis}) + return self.algebra.layout(grades) + + def __add__(self, other): + if isinstance(other, Multivector): + self._check_algebra(other) + if self.is_compact or other.is_compact: + layout = self._combined_layout(other) + return self._wrap_compact(self._values_for_layout(layout) + other._values_for_layout(layout), layout) + return self._wrap(self.tensor + other.tensor) + if isinstance(other, (int, float, torch.Tensor)): + if self.is_compact: + return self._wrap_compact(self.values + other, self.layout) + return self._wrap(self.tensor + other) + return NotImplemented + + def __radd__(self, other): + return self.__add__(other) + + def __sub__(self, other): + if isinstance(other, Multivector): + self._check_algebra(other) + if self.is_compact or other.is_compact: + layout = self._combined_layout(other) + return self._wrap_compact(self._values_for_layout(layout) - other._values_for_layout(layout), layout) + return self._wrap(self.tensor - other.tensor) + if isinstance(other, (int, float, torch.Tensor)): + if self.is_compact: + return self._wrap_compact(self.values - other, self.layout) + return self._wrap(self.tensor - other) + return NotImplemented + + def __rsub__(self, other): + if isinstance(other, (int, float, torch.Tensor)): + if self.is_compact: + return self._wrap_compact(other - self.values, self.layout) + return self._wrap(other - self.tensor) + return NotImplemented + + def __neg__(self): + if self.is_compact: + return self._wrap_compact(-self.values, self.layout) + return self._wrap(-self.tensor) + + def __mul__(self, other): + """Geometric product ``A * B``, or scalar scaling.""" + if isinstance(other, Multivector): + self._check_algebra(other) + return self.geometric_product(other) + if isinstance(other, (int, float)): + if self.is_compact: + return self._wrap_compact(self.values * other, self.layout) + return self._wrap(self.tensor * other) + if isinstance(other, torch.Tensor): + if self.is_compact: + return self._wrap_compact(self.values * other, self.layout) + return self._wrap(self.tensor * other) + return NotImplemented + + def __rmul__(self, other): + if isinstance(other, (int, float, torch.Tensor)): + if self.is_compact: + return self._wrap_compact(self.values * other, self.layout) + return self._wrap(self.tensor * other) + return NotImplemented + + def __truediv__(self, other): + if isinstance(other, (int, float)): + if self.is_compact: + return self._wrap_compact(self.values / other, self.layout) + return self._wrap(self.tensor / other) + if isinstance(other, torch.Tensor): + if self.is_compact: + return self._wrap_compact(self.values / other, self.layout) + return self._wrap(self.tensor / other) + return NotImplemented + + def __xor__(self, other): + """Wedge/exterior product ``A ^ B``.""" + if isinstance(other, Multivector): + self._check_algebra(other) + return self.wedge(other) + return NotImplemented + + def __or__(self, other): + """Inner product ``A | B``.""" + if isinstance(other, Multivector): + self._check_algebra(other) + return self.inner(other) + return NotImplemented + + def __invert__(self): + """Reversion ``~A``.""" + return self.reverse() + + def grade(self, k: int) -> Multivector: + """Extract the grade-k component.""" + if self.is_compact: + layout = self.algebra.layout((int(k),)) + if not self.layout.contains_grade(k): + values = self.values.new_zeros(*self.values.shape[:-1], layout.dim) + return self._wrap_compact(values, layout) + values, output_layout = self.algebra.planned_unary( + self.values, + op="grade_projection", + input_layout=self.layout, + output_layout=layout, + input_compact=True, + compact_output=True, + return_layout=True, + ) + return self._wrap_compact(values, output_layout) + return self._wrap(self.algebra.grade_projection(self.tensor, k)) + + def reverse(self) -> Multivector: + """Reversion (same as ``~self``).""" + if self.is_compact: + values, layout = self.algebra.planned_unary( + self.values, + op="reverse", + input_layout=self.layout, + input_compact=True, + compact_output=True, + return_layout=True, + ) + return self._wrap_compact(values, layout) + return self._wrap(self.algebra.reverse(self.tensor)) + + def grade_involution(self) -> Multivector: + """Grade involution (main involution): flips odd-grade signs.""" + if self.is_compact: + values, layout = self.algebra.planned_unary( + self.values, + op="grade_involution", + input_layout=self.layout, + input_compact=True, + compact_output=True, + return_layout=True, + ) + return self._wrap_compact(values, layout) + return self._wrap(self.algebra.grade_involution(self.tensor)) + + def clifford_conjugation(self) -> Multivector: + """Clifford conjugation: grade_involution(reverse(x)).""" + if self.is_compact: + values, layout = self.algebra.planned_unary( + self.values, + op="clifford_conjugation", + input_layout=self.layout, + input_compact=True, + compact_output=True, + return_layout=True, + ) + return self._wrap_compact(values, layout) + return self._wrap(self.algebra.clifford_conjugation(self.tensor)) + + def dual(self) -> Multivector: + """Hodge dual: maps grade-k to grade-(n-k).""" + # Dense-only algebra APIs below intentionally cross the dense boundary. + # Planned high-dimensional paths should use grade-declared primitives. + return self._wrap(self.algebra.dual(self.tensor)) + + def inverse(self) -> Multivector: + """Blade inverse: B^{-1} = ~B / _0.""" + return self._wrap(self.algebra.blade_inverse(self.tensor)) + + def geometric_product(self, other: Multivector) -> Multivector: + """Explicit geometric product (same as ``self * other``).""" + self._check_algebra(other) + if self.is_compact or other.is_compact: + return self.projected_product(other, op="gp") + return self._wrap(self.algebra.geometric_product(self.tensor, other.tensor)) + + def projected_product( + self, + other: Multivector, + *, + output_grades=None, + op: str = "gp", + left_grades=None, + right_grades=None, + ) -> Multivector: + """Grade-projected product using compact layouts when available.""" + self._check_algebra(other) + left_layout = self.layout if self.is_compact else None + right_layout = other.layout if other.is_compact else None + left_grades = left_grades if left_grades is not None else _layout_grades(left_layout) + right_grades = right_grades if right_grades is not None else _layout_grades(right_layout) + + values, layout = self.algebra.projected_product( + self.values if self.is_compact else self.tensor, + other.values if other.is_compact else other.tensor, + left_grades=left_grades, + right_grades=right_grades, + output_grades=output_grades, + left_layout=left_layout, + right_layout=right_layout, + op=op, + left_compact=self.is_compact, + right_compact=other.is_compact, + return_layout=True, + ) + return self._wrap_compact(values, layout) + + def wedge(self, other: Multivector) -> Multivector: + """Wedge/exterior product (same as ``self ^ other``).""" + self._check_algebra(other) + if self.is_compact or other.is_compact: + return self.projected_product(other, op="wedge") + return self._wrap(self.algebra.wedge(self.tensor, other.tensor)) + + def inner(self, other: Multivector) -> Multivector: + """Inner product (same as ``self | other``).""" + self._check_algebra(other) + if self.is_compact or other.is_compact: + return self.projected_product(other, op="inner") + return self._wrap(self.algebra.inner_product(self.tensor, other.tensor)) + + def left_contraction(self, other: Multivector) -> Multivector: + """Left contraction: ``self _| other``.""" + self._check_algebra(other) + return self._wrap(self.algebra.left_contraction(self.tensor, other.tensor)) + + def right_contraction(self, other: Multivector) -> Multivector: + """Right contraction: ``self |_ other``.""" + self._check_algebra(other) + return self._wrap(self.algebra.right_contraction(self.tensor, other.tensor)) + + def commutator(self, other: Multivector) -> Multivector: + """Commutator (Lie bracket): ``[self, other] = self*other - other*self``.""" + self._check_algebra(other) + if self.is_compact or other.is_compact: + return self.projected_product(other, op="commutator") + return self._wrap(self.algebra.commutator(self.tensor, other.tensor)) + + def anti_commutator(self, other: Multivector) -> Multivector: + """Anti-commutator: ``{self, other} = self*other + other*self``.""" + self._check_algebra(other) + if self.is_compact or other.is_compact: + return self.projected_product(other, op="anti_commutator") + return self._wrap(self.algebra.anti_commutator(self.tensor, other.tensor)) + + def norm(self) -> torch.Tensor: + """Induced metric norm (returns scalar tensor).""" + from core.runtime.metric import induced_norm + + return induced_norm(self.algebra, self.tensor) + + def norm_sq(self) -> torch.Tensor: + """Squared norm: _0 (returns scalar tensor).""" + return self.algebra.norm_sq(self.tensor) + + def get_grade_norms(self) -> torch.Tensor: + """Per-grade L2 norms.""" + if self.is_compact: + flat = self.values.pow(2).reshape(-1, self.layout.dim) + grade_ids = self.layout.grade_indices_tensor(device=self.values.device).unsqueeze(0).expand_as(flat) + result = flat.new_zeros(flat.shape[0], self.algebra.num_grades) + result.scatter_add_(1, grade_ids, flat) + return result.reshape(*self.values.shape[:-1], self.algebra.num_grades).clamp(min=self.algebra.eps).sqrt() + return self.algebra.get_grade_norms(self.tensor) + + def exp(self) -> Multivector: + """Exponential map (bivector -> rotor).""" + return self._wrap(self.algebra.exp(self.tensor)) + + def sandwich(self, x: Multivector) -> Multivector: + """Sandwich product: ``self * x * ~self``. + + Falls back to two geometric products when the tensor shapes + don't match the optimized [N, D] + [N, C, D] layout. + """ + self._check_algebra(x) + R, xt = self.tensor, x.tensor + # Optimized path: R is [N, D], x is [N, C, D] + if R.dim() == 2 and xt.dim() == 3: + return self._wrap(self.algebra.sandwich_product(R, xt)) + # General fallback: two GPs + R_rev = self.algebra.reverse(R) + return self._wrap(self.algebra.geometric_product(self.algebra.geometric_product(R, xt), R_rev)) + + def reflect(self, n: Multivector) -> Multivector: + """Reflect self through hyperplane orthogonal to vector n.""" + self._check_algebra(n) + return self._wrap(self.algebra.reflect(self.tensor, n.tensor)) + + def versor_product(self, x: Multivector) -> Multivector: + """General versor action: ``hat(self) * x * self^{-1}``.""" + self._check_algebra(x) + return self._wrap(self.algebra.versor_product(self.tensor, x.tensor)) + + def blade_project(self, blade: Multivector) -> Multivector: + """Project onto blade subspace: ``(self · B) B^{-1}``.""" + self._check_algebra(blade) + return self._wrap(self.algebra.blade_project(self.tensor, blade.tensor)) + + def blade_reject(self, blade: Multivector) -> Multivector: + """Reject from blade subspace: ``self - proj_B(self)``.""" + self._check_algebra(blade) + return self._wrap(self.algebra.blade_reject(self.tensor, blade.tensor)) + + def to(self, *args, **kwargs) -> Multivector: + """Move/cast the underlying tensor (same API as ``torch.Tensor.to``).""" + if self.is_compact: + return Multivector(self.algebra, values=self.values.to(*args, **kwargs), layout=self.layout) + return self._wrap(self.tensor.to(*args, **kwargs)) + + def detach(self) -> Multivector: + """Detach from computation graph.""" + if self.is_compact: + return Multivector(self.algebra, values=self.values.detach(), layout=self.layout) + return self._wrap(self.tensor.detach()) + + def clone(self) -> Multivector: + """Clone the underlying tensor.""" + if self.is_compact: + return Multivector(self.algebra, values=self.values.clone(), layout=self.layout) + return self._wrap(self.tensor.clone()) + + def requires_grad_(self, requires_grad: bool = True) -> Multivector: + """Set requires_grad in-place.""" + if self.is_compact: + self.values.requires_grad_(requires_grad) + else: + self.tensor.requires_grad_(requires_grad) + return self + + @property + def shape(self) -> torch.Size: + return self.values.shape if self.is_compact else self.tensor.shape + + @property + def device(self) -> torch.device: + return self.values.device if self.is_compact else self.tensor.device + + @property + def dtype(self) -> torch.dtype: + return self.values.dtype if self.is_compact else self.tensor.dtype + + +def _layout_grades(layout: GradeLayout) -> tuple[int, ...] | None: + return None if layout is None else layout.grades diff --git a/core/runtime/projected.py b/core/runtime/projected.py new file mode 100644 index 0000000..da3e863 --- /dev/null +++ b/core/runtime/projected.py @@ -0,0 +1,261 @@ +# Versor: Universal Geometric Algebra Neural Network +# Copyright (C) 2026 Eunkyum Kim +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# + +"""Shared projected-product facade for algebra hosts.""" + +from __future__ import annotations + +from typing import Iterable, Optional + +import torch + +from core.foundation.layout import GradeLayout +from core.foundation.validation import check_multivector +from core.runtime.accessors import default_layout as _default_layout +from core.runtime.accessors import grade_indices as _grade_indices +from core.runtime.accessors import hermitian_signs as _hermitian_signs +from core.runtime.accessors import materialize_dense +from core.runtime.accessors import resolve_layout as _resolve_layout + + +class AlgebraRuntimeMixin: + """Shared runtime protocol for dense kernels and planned contexts.""" + + def layout(self, grades: Optional[Iterable[int]] = None) -> GradeLayout: + """Return a compact grade layout or the algebra's default layout.""" + if grades is None: + return self.default_layout() + return self.planner.layout(grades) + + def default_layout(self) -> GradeLayout: + """Return the default layout using the central fallback policy.""" + return _default_layout(self) + + def product_executor( + self, + *, + left_grades, + right_grades, + op: str = "gp", + output_grades=None, + dtype: Optional[torch.dtype] = None, + device=None, + cache: bool = True, + ): + """Return a preplanned product executor suitable for ``torch.compile``.""" + if dtype is None: + dtype = getattr(self, "dtype", torch.float32) + if device is None: + device = getattr(self, "device", None) + return self.planner.product_executor( + op=op, + left_grades=left_grades, + right_grades=right_grades, + output_grades=output_grades, + dtype=dtype, + device=device, + cache=cache, + ) + + def resolve_layout( + self, + *, + layout: Optional[GradeLayout] = None, + grades: Optional[Iterable[int]] = None, + mv=None, + allow_full: bool = True, + warn_full: bool = True, + ) -> GradeLayout: + """Resolve static layout metadata for tensors or multivectors.""" + return _resolve_layout( + self, + layout=layout, + grades=grades, + mv=mv, + allow_full=allow_full, + warn_full=warn_full, + ) + + def grade_indices(self, grades: Iterable[int], *, device=None) -> torch.Tensor: + """Return canonical dense basis indices for ``grades``.""" + return _grade_indices(self, grades, device=self.device if device is None else device) + + def hermitian_signs( + self, + layout: Optional[GradeLayout] = None, + *, + grades: Optional[Iterable[int]] = None, + device=None, + dtype: Optional[torch.dtype] = None, + ) -> torch.Tensor: + """Return Hermitian signs for a dense or compact layout.""" + return _hermitian_signs(self, layout=layout, grades=grades, device=device, dtype=dtype) + + def projected_product( + self, + A: torch.Tensor, + B: torch.Tensor, + *, + left_grades=None, + right_grades=None, + output_grades=None, + left_layout=None, + right_layout=None, + output_layout=None, + op: str = "gp", + left_compact: bool = False, + right_compact: bool = False, + compact_output: bool = False, + return_layout: bool = False, + pairwise: bool = False, + ): + """Compute a declared grade-restricted product through a static executor. + + By default, operands are multiplied elementwise over broadcastable + prefix dimensions. Set ``pairwise=True`` when the dimension before each + compact lane axis is an independent left/right item axis. + """ + left_layout = self._declared_layout(left_grades, left_layout) + right_layout = self._declared_layout(right_grades, right_layout) + if not left_compact and left_layout is not None and A.shape[-1] == left_layout.dim: + left_compact = left_layout.dim != self.dim + if not right_compact and right_layout is not None and B.shape[-1] == right_layout.dim: + right_compact = right_layout.dim != self.dim + if not left_compact: + check_multivector(A, self, "projected_product(A)") + if not right_compact: + check_multivector(B, self, "projected_product(B)") + + request = self.planner.product_request( + A, + B, + left_grades=left_grades, + right_grades=right_grades, + output_grades=output_grades, + left_layout=left_layout, + right_layout=right_layout, + output_layout=output_layout, + op=op, + left_compact=left_compact, + right_compact=right_compact, + ) + executor = self.planner.product_executor_for_request(request) + + if pairwise: + values = self._execute_pairwise_product(A, B, request, executor) + else: + values = self._execute_elementwise_product(A, B, request, executor) + + if return_layout: + return values, executor.output_layout + if compact_output: + return values + return materialize_dense(self, values, layout=executor.output_layout) + + def projected_geometric_product(self, A: torch.Tensor, B: torch.Tensor, **kwargs): + """Projected geometric product convenience wrapper.""" + return self.projected_product(A, B, op="gp", **kwargs) + + def projected_wedge(self, A: torch.Tensor, B: torch.Tensor, **kwargs): + """Projected wedge product convenience wrapper.""" + return self.projected_product(A, B, op="wedge", **kwargs) + + def projected_inner_product(self, A: torch.Tensor, B: torch.Tensor, **kwargs): + """Projected inner product convenience wrapper.""" + return self.projected_product(A, B, op="inner", **kwargs) + + def projected_commutator(self, A: torch.Tensor, B: torch.Tensor, **kwargs): + """Projected commutator convenience wrapper.""" + return self.projected_product(A, B, op="commutator", **kwargs) + + def projected_anti_commutator(self, A: torch.Tensor, B: torch.Tensor, **kwargs): + """Projected anti-commutator convenience wrapper.""" + return self.projected_product(A, B, op="anti_commutator", **kwargs) + + def planned_unary( + self, + values: torch.Tensor, + *, + op: str, + input_grades=None, + output_grades=None, + input_layout: Optional[GradeLayout] = None, + output_layout: Optional[GradeLayout] = None, + input_compact: bool = False, + compact_output: bool = False, + return_layout: bool = False, + ): + """Execute a unary operation through the shared static grade planner.""" + request = self.planner.unary_request( + values, + op=op, + input_grades=input_grades, + output_grades=output_grades, + input_layout=input_layout, + output_layout=output_layout, + input_compact=input_compact, + ) + executor = self.planner.unary_executor_for_request(request) + output = executor.forward_compact(values) if request.input_compact else executor(values) + + if return_layout: + return output, executor.output_layout + if compact_output: + return output + return materialize_dense(self, output, layout=executor.output_layout) + + def _declared_layout(self, grades, layout): + if layout is not None: + return layout + if grades is not None: + return self.layout(grades) + default_grades = getattr(self, "_default_grades", None) + if default_grades is None: + return None + return self.layout(default_grades) + + def _execute_elementwise_product(self, left, right, request, executor): + if request.left_compact or request.right_compact: + left_values = left if request.left_compact else executor.left_layout.compact(left) + right_values = right if request.right_compact else executor.right_layout.compact(right) + self._check_elementwise_prefix(left_values, right_values) + return executor.forward_compact(left_values, right_values) + + self._check_elementwise_prefix(left, right) + return executor(left, right) + + def _execute_pairwise_product(self, left, right, request, executor): + left_values = left if request.left_compact else executor.left_layout.compact(left) + right_values = right if request.right_compact else executor.right_layout.compact(right) + self._check_pairwise_prefix(left_values, right_values) + return executor.forward_pairwise_compact(left_values, right_values) + + @staticmethod + def _check_elementwise_prefix(left: torch.Tensor, right: torch.Tensor) -> None: + try: + torch.broadcast_shapes(left.shape[:-1], right.shape[:-1]) + except RuntimeError as exc: + raise ValueError( + "projected_product elementwise prefixes must be broadcastable; " + f"got left prefix {tuple(left.shape[:-1])} and right prefix {tuple(right.shape[:-1])}. " + "Use pairwise=True when left and right have distinct item axes." + ) from exc + + @staticmethod + def _check_pairwise_prefix(left: torch.Tensor, right: torch.Tensor) -> None: + if left.ndim < 2 or right.ndim < 2: + raise ValueError( + "pairwise projected_product requires explicit item axes before the compact lane dimension; " + f"got left shape {tuple(left.shape)} and right shape {tuple(right.shape)}" + ) + try: + torch.broadcast_shapes(left.shape[:-2], right.shape[:-2]) + except RuntimeError as exc: + raise ValueError( + "pairwise projected_product batch prefixes must be broadcastable; " + f"got left prefix {tuple(left.shape[:-2])} and right prefix {tuple(right.shape[:-2])}" + ) from exc diff --git a/core/visualizer.py b/core/visualizer.py index 64d021d..10be229 100644 --- a/core/visualizer.py +++ b/core/visualizer.py @@ -12,7 +12,7 @@ from sklearn.decomposition import PCA from sklearn.manifold import TSNE -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra class GeneralVisualizer: diff --git a/docs/Architecture.md b/docs/Architecture.md new file mode 100644 index 0000000..4ce674f --- /dev/null +++ b/docs/Architecture.md @@ -0,0 +1,307 @@ +# Architecture + +Versor is organized around one rule: algebraic identity, tensor layout, runtime +execution, and training behavior should stay separable. The core owns the +mathematics and the execution contracts; layers, functionals, and optimizers +expose those contracts in forms that are convenient for actual model code. + +## Framework Layers + +```mermaid +flowchart TB + User["User code
layers, functionals, optimizers"] + + subgraph Framework["Versor framework"] + Layers["layers/
nn.Module composition"] + Functional["functional/
stateless algebra helpers, losses, activations"] + Optimizers["optimizers/
manifold-aware parameter updates"] + end + + subgraph Core["core/"] + Foundation["foundation/
basis rules, layouts, validation, manifold tags"] + Planning["planning/
grade requests, lane limits, executors"] + Runtime["runtime/
dense algebra, planned context, multivectors"] + Config["config.py
algebra factory"] + Analysis["analysis/
metric/geodesic/symmetry tooling"] + end + + User --> Layers + User --> Functional + User --> Optimizers + Layers --> Runtime + Layers --> Functional + Layers --> Foundation + Functional --> Runtime + Optimizers --> Foundation + Optimizers --> Runtime + Config --> Runtime + Runtime --> Planning + Runtime --> Foundation + Planning --> Foundation + Analysis --> Runtime +``` + +`core/` is the authority for algebra and execution. `layers/` should be thin +module wrappers around core operations. `functional/` should expose stateless +helpers and losses without duplicating runtime logic. `optimizers/` should read +parameter manifold tags and apply updates; it should not decide product plans. + +## Algebra Host Selection + +```mermaid +flowchart LR + Factory["make_algebra(p, q, r, kernel)"] + Dense["CliffordAlgebra
dense tables, full dim = 2^n"] + Context["AlgebraContext
planned compact execution"] + Spec["AlgebraSpec
signature and dimension metadata"] + Planner["GradePlanner
cached layouts and executors"] + + Factory -->|"small n or explicit dense"| Dense + Factory -->|"large n or explicit context"| Context + Dense --> Spec + Context --> Spec + Dense --> Planner + Context --> Planner + Planner --> Spec +``` + +`CliffordAlgebra` owns dense Cayley-table buffers and fast full-layout kernels. +`AlgebraContext` exposes the same high-level product API but routes products +through static grade planning by default. Both hosts share the runtime facade, so +declared products use the same layout, cost, and executor path. + +## Tensor And Layout Flow + +```mermaid +flowchart LR + Grades["Grade set
for example (1, 2)"] + Basis["basis_index_tuple_for_grades(n, grades)"] + Layout["GradeLayout
grades, dense basis indices, lane count"] + Dense["Dense multivector
last dim = 2^n"] + Compact["Compact values
last dim = layout.dim"] + Materialized["Dense materialization
zeros outside layout"] + + Grades --> Basis --> Layout + Dense -->|"layout.compact"| Compact + Compact -->|"layout.dense"| Materialized + Layout --> Compact + Layout --> Materialized +``` + +A compact tensor is not just a shorter tensor. It is coefficient values plus a +`GradeLayout` identity that defines which dense basis blades the lanes +represent. Raw tensors do not carry that identity, so framework pipeline code +must declare `*_grades`, pass layouts, or use `Multivector` wrappers when layout +metadata needs to travel with values. + +## Product Execution Flow + +```mermaid +flowchart TB + A["A tensor
dense [*, 2^n] or compact [*, left_lanes]"] + B["B tensor
dense [*, 2^n] or compact [*, right_lanes]"] + Metadata["Declared metadata
left_grades, right_grades, output_grades
left_layout, right_layout, compact flags"] + + Request["ProductRequest
normalized op, layouts, dtype, device"] + Cost["PlanningLimits and cost checks
lanes, output width, estimated pairs"] + Tree["GradePlanTree
homogeneous route nodes"] + Plan["GradeProductPlan
left lanes, right lanes, output lanes, coefficients"] + Exec["GradeProductExecutor
forward, forward_compact, forward_pairwise_compact"] + Values["Compact output values
[*, output_lanes]"] + DenseOut["Dense materialized output
[*, 2^n]"] + + A --> Request + B --> Request + Metadata --> Request + Request --> Cost + Cost --> Tree + Tree --> Plan + Plan --> Exec + A --> Exec + B --> Exec + Exec --> Values + Values -->|"compact_output=True"| CompactReturn["Return compact values"] + Values -->|"compact_output=False"| DenseOut +``` + +Planning uses static grade metadata and tensor shapes. It does not inspect +runtime tensor values. This keeps compiled paths stable and avoids +data-dependent symbolic shape extraction. + +## Layer Pipeline Contract + +```mermaid +sequenceDiagram + participant Model as Model/Layers + participant ProductLayer as ProductLayer + participant Algebra as Algebra Runtime API + participant Planner as GradePlanner + participant Executor as GradeProductExecutor + participant Optimizer as Optimizer + + Model->>ProductLayer: forward(left, right) + ProductLayer->>Algebra: projected_product(...grades, compact flags) + Algebra->>Planner: product_request(...) + Planner->>Executor: cached or newly built executor + Algebra->>Executor: forward / forward_compact / forward_pairwise_compact + Executor-->>Model: dense or compact tensor + Model->>Optimizer: loss.backward(); step() + Optimizer->>Optimizer: update tagged parameters +``` + +Optimizers do not run planning. They update parameters after the forward pass. +Planning happens when the model calls algebra operations through direct runtime +APIs, functional helpers, `ProductLayer`, or `Multivector` methods. + +## Operator Rules + +```mermaid +flowchart TB + Route["Route
left grade r, right grade s"] + GP["geometric product
grades abs(r-s), abs(r-s)+2, ..."] + Wedge["wedge / exterior
grade r+s only"] + Symmetric["inner route
symmetric parity grades"] + Comm["commutator
odd swap-parity grades"] + Anti["anti-commutator
even swap-parity grades, doubled coefficient"] + Pair["Basis pair
left index i, right index j"] + Output["output index = i XOR j"] + Nonzero["operation_may_be_nonzero
wedge requires no shared basis bit
null self-overlap is zero"] + Coeff["operation_coefficient
metric sign and op scale"] + + Route --> GP + Route --> Wedge + Route --> Symmetric + Route --> Comm + Route --> Anti + GP --> Pair + Wedge --> Pair + Symmetric --> Pair + Comm --> Pair + Anti --> Pair + Pair --> Output + Output --> Nonzero + Nonzero --> Coeff +``` + +The wedge implementation is the exterior product. For homogeneous inputs: + +```text +A_r ^ B_s = _{r+s} +``` + +For vectors this coincides with `(AB - BA) / 2`, but higher-grade wedge routes +follow the grade-sum exterior definition. + +## Dense Runtime + +```mermaid +flowchart TB + Init["CliffordAlgebra init"] + Cayley["Cayley indices and signs
basis_product over all pairs"] + GP["gp_signs
single-pass geometric product"] + Wedge["wedge_gp_signs
grade(output) = grade(left) + grade(right)"] + Inner["inner_gp_signs
symmetric sign table"] + Comm["comm_gp_signs and anti_comm_gp_signs"] + Contractions["left and right contraction helpers"] + Product["Runtime product call"] + Matmul["gather B by Cayley indices
multiply signs
batched matmul over lanes"] + + Init --> Cayley + Cayley --> GP + Cayley --> Wedge + Cayley --> Inner + Cayley --> Comm + Cayley --> Contractions + GP --> Product + Wedge --> Product + Inner --> Product + Comm --> Product + Product --> Matmul +``` + +Dense products are table-driven. The input tensors carry coefficients; the +precomputed tables carry basis multiplication structure. + +## Planning Limits + +`PlanningLimits` centralizes static guardrails for compact planning: + +```python +from core.planning import PlanningLimits +from core.runtime.context import AlgebraContext + +limits = PlanningLimits(max_lanes=8192, max_pairs=16_000_000) +algebra = AlgebraContext(32, 0, device="cpu", planning_limits=limits) +``` + +`max_lanes` protects compact tensor width. `max_pairs` protects the +gather/reduce interaction count generated by product plans. Dense algebra hosts +and planned contexts both accept `planning_limits`, so the same policy object can +be used across framework construction. + +## Analysis Flow + +```mermaid +flowchart LR + Data["Input data
vectors or multivectors"] + Algebra["Algebra host
dense or context"] + Metric["Metric and dimension analysis
norms, distances, effective dimension"] + Geodesic["GeodesicFlow
grade-1 wedge to connection bivectors"] + Symmetry["SymmetryDetector
group and continuous symmetry"] + Comm["CommutatorAnalyzer
bracket spectra and exchange structure"] + Report["Analysis result dataclasses"] + + Data --> Algebra + Algebra --> Metric + Algebra --> Geodesic + Algebra --> Symmetry + Algebra --> Comm + Metric --> Report + Geodesic --> Report + Symmetry --> Report + Comm --> Report +``` + +Analysis code should call algebra APIs rather than reconstructing basis rules. +When active grades are known, analysis should pass `left_grades`, +`right_grades`, and `output_grades` so the planner can avoid full-layout work. + +## End-To-End Product Call + +```mermaid +sequenceDiagram + participant Caller as Caller + participant Algebra as Algebra Runtime API + participant Planner as GradePlanner + participant Executor as GradeProductExecutor + participant Layout as GradeLayout + + Caller->>Algebra: wedge(A, B, left_grades, right_grades, output_grades) + Algebra->>Planner: product_request(...) + Planner->>Layout: resolve operand and output layouts + Planner->>Planner: build or fetch cached executor + Planner->>Executor: return executor + Algebra->>Executor: forward, forward_compact, or forward_pairwise_compact + Executor-->>Algebra: compact output values + Algebra->>Layout: materialize dense unless compact_output=True + Algebra-->>Caller: tensor output +``` + +The same path applies to geometric product, wedge, inner route, commutator, and +anti-commutator. The operator name changes route grades and coefficients, not +the overall tensor plumbing. + +## Framework Verification Map + +Framework-level tests are grouped by the behavior they prove: + +- Core algebra and dense kernel identities: `tests/test_core.py` +- Static grade planning and compact execution: `tests/test_grade_plan.py` +- Multivector layout-preserving wrappers: `tests/test_multivector.py` +- Layer pipeline and optimizer integration: `tests/test_framework_pipeline.py` +- Functional product helpers: `tests/test_functional_products.py` +- Optimizer manifold grouping and factories: `tests/test_riemannian_optimizer.py` + +Performance checks live in `benchmarks/`. The framework pipeline benchmark +measures dense-vs-compact products, planned contexts, pairwise compact products, +and composed layer pipelines. diff --git a/docs/api/core.md b/docs/api/core.md index 1970930..3776c78 100644 --- a/docs/api/core.md +++ b/docs/api/core.md @@ -3,19 +3,22 @@ The mathematical kernel of Versor. ## Algebra -::: core.algebra.CliffordAlgebra +::: core.runtime.algebra.CliffordAlgebra + +## Algebra Config +::: core.config ## Module -::: core.module.CliffordModule +::: core.foundation.module.CliffordModule ## Multivector -::: core.multivector.Multivector +::: core.runtime.multivector.Multivector ## Metric -::: core.metric +::: core.runtime.metric ## Decomposition -::: core.decomposition +::: core.runtime.decomposition ## Analysis ::: core.analysis.MetricSearch diff --git a/docs/design_guide.md b/docs/design_guide.md index b52c632..6926e0d 100644 --- a/docs/design_guide.md +++ b/docs/design_guide.md @@ -75,7 +75,7 @@ The canonical Geometric Blade Network block, annotated: ```python import torch.nn as nn -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra from layers.primitives.linear import CliffordLinear from layers.primitives.rotor import RotorLayer from layers.primitives.normalization import CliffordLayerNorm @@ -147,7 +147,7 @@ Versor models are intentionally hybrid. Standard `nn.Linear` and `CliffordLinear ```python import torch import torch.nn as nn -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra from layers.primitives.linear import CliffordLinear from layers.primitives.rotor import RotorLayer from layers.primitives.normalization import CliffordLayerNorm @@ -237,7 +237,7 @@ End-to-end: choose algebra, build a 3-layer GBN, train, evaluate. Runs in under ```python import torch import torch.nn as nn -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra from layers.primitives.linear import CliffordLinear from layers.primitives.rotor import RotorLayer from layers.primitives.normalization import CliffordLayerNorm diff --git a/docs/index.md b/docs/index.md index 74b8163..11f47b6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,6 @@ # Versor: A PyTorch Framework for Geometric Algebra Deep Learning -[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) [![Python](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/) [![PyTorch](https://img.shields.io/badge/PyTorch-2.0+-ee4c2c.svg)](https://pytorch.org/) [![Docs](https://img.shields.io/badge/docs-MkDocs-brightgreen)](https://concode0.github.io/Versor/) [![DOI](https://zenodo.org/badge/1149480519.svg)](https://doi.org/10.5281/zenodo.18939518) +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) [![Python](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/) [![PyTorch](https://img.shields.io/badge/PyTorch-2.0+-ee4c2c.svg)](https://pytorch.org/) [![Docs](https://img.shields.io/badge/docs-MkDocs-brightgreen)](https://concode0.github.io/Versor/) [![DOI](https://zenodo.org/badge/1149480519.svg)](https://doi.org/10.5281/zenodo.18939518) > **"There is a ceiling above standard Deep Learning that no one saw. Versor opens the door above it."** @@ -53,7 +53,7 @@ For code examples of each innovation, see [Innovations](innovations.md). ## Installation -Versor requires Python 3.9+ and PyTorch. +Versor requires Python 3.10+ and PyTorch. ```bash # Clone the repository @@ -80,7 +80,7 @@ uv sync --extra all # everything ```python import torch -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra from layers.primitives.rotor import RotorLayer from layers.linear import CliffordLinear from functional.activation import GeometricGELU diff --git a/docs/tutorial.md b/docs/tutorial.md index 1e3e05b..1792103 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -7,7 +7,7 @@ A step-by-step guide to using Versor's geometric layers in your own models. Everything starts with a `CliffordAlgebra` instance. The signature $(p, q, r)$ determines the geometry: ```python -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra # 3D Euclidean (rotations in 3-space) algebra = CliffordAlgebra(p=3, q=0, r=0, device='cpu') @@ -183,7 +183,7 @@ All tasks inherit from `BaseTask` and implement 7 methods: ```python from tasks.base import BaseTask -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra from functional.loss import GeometricMSELoss class MyTask(BaseTask): diff --git a/examples/conf/task/cgenn.yaml b/examples/conf/task/cgenn.yaml index 70152b0..71ddea5 100644 --- a/examples/conf/task/cgenn.yaml +++ b/examples/conf/task/cgenn.yaml @@ -4,7 +4,6 @@ name: "cgenn" algebra: p: 3 q: 0 - device: "cpu" dataset: name: "point_cloud_invariant" diff --git a/examples/conf/task/clifford_pde.yaml b/examples/conf/task/clifford_pde.yaml index 6568289..1ca7d10 100644 --- a/examples/conf/task/clifford_pde.yaml +++ b/examples/conf/task/clifford_pde.yaml @@ -4,7 +4,6 @@ name: "clifford_pde" algebra: p: 2 q: 0 - device: "cpu" dataset: name: "taylor_green" diff --git a/examples/conf/task/gatr.yaml b/examples/conf/task/gatr.yaml index deb8273..dfbdb37 100644 --- a/examples/conf/task/gatr.yaml +++ b/examples/conf/task/gatr.yaml @@ -5,7 +5,6 @@ algebra: p: 3 q: 0 r: 1 - device: "cpu" dataset: name: "nbody_spring" diff --git a/examples/conf/task/hyperbolic.yaml b/examples/conf/task/hyperbolic.yaml index 8a32a1f..1d5cd45 100644 --- a/examples/conf/task/hyperbolic.yaml +++ b/examples/conf/task/hyperbolic.yaml @@ -1,9 +1,10 @@ # @package _global_ name: "hyperbolic" + algebra: p: 1 q: 1 - device: "cpu" + dataset: name: "lorentz_boost" samples: 10000 diff --git a/examples/conf/task/manifold.yaml b/examples/conf/task/manifold.yaml index 458c145..ab61ae1 100644 --- a/examples/conf/task/manifold.yaml +++ b/examples/conf/task/manifold.yaml @@ -1,10 +1,11 @@ # @package _global_ name: "manifold" + algebra: p: 3 q: 0 - device: "cpu" + dataset: name: "figure8" samples: 1000 - noise_std: 0.1 \ No newline at end of file + noise_std: 0.1 diff --git a/examples/conf/task/sanity.yaml b/examples/conf/task/sanity.yaml index 1ad2b3e..137455c 100644 --- a/examples/conf/task/sanity.yaml +++ b/examples/conf/task/sanity.yaml @@ -1,9 +1,10 @@ # @package _global_ name: "sanity" + algebra: p: 3 q: 0 - device: "cpu" + dataset: name: "random_noise" samples: 1000 diff --git a/examples/datasets/amass.py b/examples/datasets/amass.py index 434b10e..81df07d 100644 --- a/examples/datasets/amass.py +++ b/examples/datasets/amass.py @@ -9,7 +9,7 @@ import torch from torch.utils.data import Dataset -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra class AMASSDataset(Dataset): diff --git a/examples/datasets/synthetic.py b/examples/datasets/synthetic.py index 84587c2..3ce5f14 100644 --- a/examples/datasets/synthetic.py +++ b/examples/datasets/synthetic.py @@ -9,7 +9,7 @@ import torch from torch.utils.data import Dataset -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra class Figure8Dataset(Dataset): diff --git a/examples/demo.py b/examples/demo.py index 4914e1a..eebca02 100644 --- a/examples/demo.py +++ b/examples/demo.py @@ -25,7 +25,7 @@ import torch import torch.optim as optim -from core.algebra import CliffordAlgebra +from core.config import make_algebra from layers import BladeSelector, RotorLayer # Setup Page @@ -102,7 +102,7 @@ def plot_3d_manifold(data, title, color_data=None): # Main app logic # 1. Generate Data -algebra = CliffordAlgebra(3, 0, device="cpu") +algebra = make_algebra(3, 0, device="cpu") t = torch.linspace(0, 2 * np.pi, samples) x = torch.sin(t) y = torch.sin(t) * torch.cos(t) diff --git a/examples/tasks/cgenn.py b/examples/tasks/cgenn.py index 259914c..7be6ef0 100644 --- a/examples/tasks/cgenn.py +++ b/examples/tasks/cgenn.py @@ -60,8 +60,8 @@ import torch.nn as nn from torch.utils.data import DataLoader, TensorDataset -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.config import make_algebra_from_config +from core.foundation.module import CliffordModule from functional.activation import GeometricSquare from layers import ( BladeSelector, @@ -205,9 +205,11 @@ class CGENNTask(BaseTask): """ def setup_algebra(self): - return CliffordAlgebra( + return make_algebra_from_config( + self.cfg.algebra, p=self.cfg.algebra.p, q=self.cfg.algebra.q, + r=self.cfg.algebra.get("r", 0), device=self.device, ) diff --git a/examples/tasks/clifford_pde.py b/examples/tasks/clifford_pde.py index 4950672..9c9379e 100644 --- a/examples/tasks/clifford_pde.py +++ b/examples/tasks/clifford_pde.py @@ -66,8 +66,8 @@ import torch.nn as nn from torch.utils.data import DataLoader, TensorDataset -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.config import make_algebra_from_config +from core.foundation.module import CliffordModule from functional.activation import GeometricGELU from layers import ( CliffordLayerNorm, @@ -276,9 +276,11 @@ class CliffordPDETask(BaseTask): def setup_algebra(self): # Cl(2,0): dim = 2^2 = 4 components # grade-0: 1 scalar, grade-1: 2 vectors, grade-2: 1 bivector - return CliffordAlgebra( + return make_algebra_from_config( + self.cfg.algebra, p=self.cfg.algebra.p, q=self.cfg.algebra.q, + r=self.cfg.algebra.get("r", 0), device=self.device, ) diff --git a/examples/tasks/gatr.py b/examples/tasks/gatr.py index 45c3bfb..7ab4e74 100644 --- a/examples/tasks/gatr.py +++ b/examples/tasks/gatr.py @@ -63,7 +63,7 @@ import torch.nn as nn from torch.utils.data import DataLoader, TensorDataset -from core.algebra import CliffordAlgebra +from core.config import make_algebra_from_config from layers import CliffordLinear, GeometricTransformerBlock from layers.adapters.projective import ProjectiveEmbedding from tasks.base import BaseTask @@ -220,7 +220,8 @@ def setup_algebra(self): # Cl(3,0,1): 3 Euclidean + 1 degenerate = Projective Geometric Algebra # dim = 2^4 = 16 multivector components r = self.cfg.algebra.get("r", 1) - return CliffordAlgebra( + return make_algebra_from_config( + self.cfg.algebra, p=self.cfg.algebra.p, q=self.cfg.algebra.q, r=r, diff --git a/examples/tasks/hyperbolic.py b/examples/tasks/hyperbolic.py index b30e6fd..34e4059 100644 --- a/examples/tasks/hyperbolic.py +++ b/examples/tasks/hyperbolic.py @@ -9,8 +9,8 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.config import make_algebra_from_config +from core.foundation.module import CliffordModule from core.visualizer import GeneralVisualizer from functional.loss import GeometricMSELoss from layers import RotorLayer @@ -44,7 +44,7 @@ def __init__(self, cfg): def setup_algebra(self): """2D Spacetime Cl(1, 1).""" - return CliffordAlgebra(p=1, q=1, device=self.device) + return make_algebra_from_config(self.cfg.algebra, p=1, q=1, r=0, device=self.device) def setup_model(self): """The Booster.""" diff --git a/examples/tasks/manifold.py b/examples/tasks/manifold.py index 29fa06c..e3a4ce1 100644 --- a/examples/tasks/manifold.py +++ b/examples/tasks/manifold.py @@ -9,8 +9,8 @@ import torch.nn as nn from torch.utils.data import DataLoader -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.config import make_algebra_from_config +from core.foundation.module import CliffordModule from core.visualizer import GeneralVisualizer from examples.datasets.synthetic import Figure8Dataset from functional.loss import SubspaceLoss @@ -47,7 +47,13 @@ def __init__(self, cfg): def setup_algebra(self): """3D Euclidean.""" - return CliffordAlgebra(p=self.cfg.algebra.p, q=self.cfg.algebra.q, device=self.device) + return make_algebra_from_config( + self.cfg.algebra, + p=self.cfg.algebra.p, + q=self.cfg.algebra.q, + r=self.cfg.algebra.get("r", 0), + device=self.device, + ) def setup_model(self): """The Unbender.""" diff --git a/examples/tasks/sanity_check.py b/examples/tasks/sanity_check.py index fe7e1bd..394fc30 100644 --- a/examples/tasks/sanity_check.py +++ b/examples/tasks/sanity_check.py @@ -8,8 +8,8 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.config import make_algebra_from_config +from core.foundation.module import CliffordModule from core.visualizer import GeneralVisualizer from functional.loss import GeometricMSELoss from layers import RotorLayer @@ -40,7 +40,7 @@ def __init__(self, cfg): def setup_algebra(self): """Standard 3D Euclidean.""" - return CliffordAlgebra(p=3, q=0, device=self.device) + return make_algebra_from_config(self.cfg.algebra, p=3, q=0, r=0, device=self.device) def setup_model(self): """Identity Net.""" diff --git a/experiments/_gdo/benchmarks.py b/experiments/_gdo/benchmarks.py index 850efa0..76c6ef6 100644 --- a/experiments/_gdo/benchmarks.py +++ b/experiments/_gdo/benchmarks.py @@ -31,8 +31,8 @@ import torch.nn as nn import torch.nn.functional as F -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from experiments._lib import setup_algebra from functional.activation import GeometricGELU from layers import ( BladeSelector, @@ -57,7 +57,7 @@ class SmallGBNModel(CliffordModule): """ def __init__(self, p: int = 3, q: int = 0, channels: int = 4, device: str = "cpu"): - algebra = CliffordAlgebra(p, q, device=device) + algebra = setup_algebra(p, q, device=device) super().__init__(algebra) self.norm = CliffordLayerNorm(self.algebra, channels) self.rotor = RotorLayer(self.algebra, channels) @@ -87,7 +87,7 @@ class MultiRotorRegistrationModel(CliffordModule): """ def __init__(self, n_clusters: int = 3, points_per_cluster: int = 20, device: str = "cpu"): - algebra = CliffordAlgebra(3, 0, device=device) + algebra = setup_algebra(3, 0, device=device) super().__init__(algebra) dim = self.algebra.dim @@ -158,7 +158,7 @@ def __init__( batch_size: int = 4, device: str = "cpu", ): - algebra = CliffordAlgebra(p, q, device=device) + algebra = setup_algebra(p, q, device=device) super().__init__(algebra) self.vocab_size = vocab_size self.seq_len = seq_len diff --git a/experiments/_gdo/controller.py b/experiments/_gdo/controller.py index 4773066..3c06593 100644 --- a/experiments/_gdo/controller.py +++ b/experiments/_gdo/controller.py @@ -9,7 +9,7 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra from optimizers.riemannian import MANIFOLD_EUCLIDEAN from .config import GDOConfig diff --git a/experiments/_gdo/harness.py b/experiments/_gdo/harness.py index 17db9e2..affdcc8 100644 --- a/experiments/_gdo/harness.py +++ b/experiments/_gdo/harness.py @@ -9,7 +9,7 @@ import torch.nn as nn from torch.optim import Optimizer -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra from layers import MultiRotorLayer, RotorLayer from optimizers.riemannian import ExponentialSGD, RiemannianAdam diff --git a/experiments/_gdo/optimizer.py b/experiments/_gdo/optimizer.py index 4412466..1bceb7f 100644 --- a/experiments/_gdo/optimizer.py +++ b/experiments/_gdo/optimizer.py @@ -8,7 +8,7 @@ import torch.nn as nn from torch.optim import Optimizer -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra from optimizers.riemannian import ( MANIFOLD_EUCLIDEAN, MANIFOLD_SPHERE, diff --git a/experiments/_gdo/parameter_groups.py b/experiments/_gdo/parameter_groups.py index 2686079..12fb99f 100644 --- a/experiments/_gdo/parameter_groups.py +++ b/experiments/_gdo/parameter_groups.py @@ -8,7 +8,6 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra from core.analysis import ( CommutatorAnalyzer as CoreCommutatorAnalyzer, ) @@ -16,6 +15,7 @@ GeodesicFlow, SpectralAnalyzer, ) +from core.runtime.algebra import CliffordAlgebra from layers import MultiRotorLayer, RotorLayer from .config import GDOConfig diff --git a/experiments/_gdo/pre_exploration.py b/experiments/_gdo/pre_exploration.py index ee07428..fece19b 100644 --- a/experiments/_gdo/pre_exploration.py +++ b/experiments/_gdo/pre_exploration.py @@ -24,7 +24,6 @@ import torch.nn as nn import torch.nn.functional as F -from core.algebra import CliffordAlgebra from core.analysis import ( CommutatorAnalyzer as CoreCommutatorAnalyzer, ) @@ -43,6 +42,8 @@ SpectralResult, SymmetryResult, ) +from core.foundation.module import AlgebraLike +from experiments._lib import setup_algebra from .config import GDOConfig from .parameter_groups import GeometricParameterController @@ -124,7 +125,7 @@ class PreExplorationAnalyzer: def __init__( self, - algebra: Optional[CliffordAlgebra] = None, + algebra: Optional[AlgebraLike] = None, n_samples: int = 200, sample_radius: float = 0.5, device: str = "cpu", @@ -520,7 +521,7 @@ def analyze( if dim_result is not None and dim_result.intrinsic_dim >= 2: try: land_dim = min(dim_result.intrinsic_dim, 6) - temp_algebra = CliffordAlgebra(land_dim, 0, device=self.device) + temp_algebra = setup_algebra(land_dim, 0, device=self.device) reduced = eda.reduce(sampled, land_dim) mv_land = temp_algebra.embed_vector(reduced) k = min(8, mv_land.shape[0] - 1) diff --git a/experiments/_lib.py b/experiments/_lib.py index 837d90f..b6470f8 100644 --- a/experiments/_lib.py +++ b/experiments/_lib.py @@ -40,8 +40,9 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra -from core.metric import hermitian_grade_spectrum +from core.config import make_algebra +from core.foundation.module import AlgebraLike +from core.runtime.metric import hermitian_grade_spectrum from functional.activation import GeometricGELU from layers import CliffordLayerNorm, CliffordLinear, RotorLayer @@ -67,9 +68,30 @@ def set_seed(seed: int, deterministic: bool = False) -> None: # --------------------------------------------------------------------------- -def setup_algebra(p: int, q: int = 0, r: int = 0, device: str = "cpu") -> CliffordAlgebra: - """One-line ``CliffordAlgebra(p, q, r, device=device).to(device)`` wrapper.""" - return CliffordAlgebra(p=p, q=q, r=r, device=device).to(device) +def setup_algebra( + p: int, + q: int = 0, + r: int = 0, + device: str = "cpu", + *, + dtype: torch.dtype | str = torch.float32, + kernel: str = "auto", + dense_threshold: int = 8, + exp_policy: str = "balanced", + fixed_iterations: Optional[int] = None, +) -> AlgebraLike: + """Construct the shared experiment algebra through the core factory.""" + return make_algebra( + p=p, + q=q, + r=r, + kernel=kernel, + dense_threshold=dense_threshold, + device=device, + dtype=dtype, + exp_policy=exp_policy, + fixed_iterations=fixed_iterations, + ) # --------------------------------------------------------------------------- @@ -208,12 +230,12 @@ def count_parameters(model: nn.Module) -> int: # --------------------------------------------------------------------------- -def grade1_indices(algebra: CliffordAlgebra) -> List[int]: +def grade1_indices(algebra: AlgebraLike) -> List[int]: """Multivector indices of grade-1 basis elements ``[e1, e2, ..., e_n]``.""" return [1 << i for i in range(algebra.n)] -def extract_grade1(mv: torch.Tensor, algebra: CliffordAlgebra, n: Optional[int] = None) -> torch.Tensor: +def extract_grade1(mv: torch.Tensor, algebra: AlgebraLike, n: Optional[int] = None) -> torch.Tensor: """Slice grade-1 components from a multivector ``[..., dim] → [..., n]``. ``n`` defaults to ``algebra.n`` (all grade-1 slots). Inverse of @@ -228,7 +250,7 @@ def extract_grade1(mv: torch.Tensor, algebra: CliffordAlgebra, n: Optional[int] # --------------------------------------------------------------------------- -def gbn_residual_block(algebra: CliffordAlgebra, channels: int) -> nn.ModuleDict: +def gbn_residual_block(algebra: AlgebraLike, channels: int) -> nn.ModuleDict: """The four-step block every GBN experiment shares. Returns ``{'norm', 'rotor', 'act', 'linear'}`` — no skip, no outer module @@ -262,7 +284,7 @@ def apply_residual_block(block: nn.ModuleDict, h: torch.Tensor) -> torch.Tensor: @torch.no_grad() -def mean_grade_spectrum(mv_iter: Iterable[torch.Tensor], algebra: CliffordAlgebra) -> np.ndarray: +def mean_grade_spectrum(mv_iter: Iterable[torch.Tensor], algebra: AlgebraLike) -> np.ndarray: """Mean Hermitian grade spectrum across an iterable of multivectors. Each element may be any shape ending in ``algebra.dim``; it is flattened diff --git a/experiments/_templates/inc_template.py b/experiments/_templates/inc_template.py index 10d2876..5ffd1b4 100644 --- a/experiments/_templates/inc_template.py +++ b/experiments/_templates/inc_template.py @@ -50,7 +50,7 @@ # Bootstrap project root so the file runs both via ``-m`` and as a bare script. sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))) -from core.module import CliffordModule +from core.foundation.module import CliffordModule from experiments._lib import ( count_parameters, ensure_output_dir, diff --git a/experiments/dbg_linear_basis_mixing.py b/experiments/dbg_linear_basis_mixing.py index 55f1fb5..2074890 100644 --- a/experiments/dbg_linear_basis_mixing.py +++ b/experiments/dbg_linear_basis_mixing.py @@ -55,8 +55,8 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))) -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra from experiments._lib import ( RawDefaultsHelpFormatter, build_visualization_metadata, diff --git a/experiments/dbg_lorentz.py b/experiments/dbg_lorentz.py index 7cc554a..b117626 100644 --- a/experiments/dbg_lorentz.py +++ b/experiments/dbg_lorentz.py @@ -56,12 +56,12 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))) -from core.metric import ( +from core.foundation.module import CliffordModule +from core.runtime.metric import ( hermitian_grade_spectrum, signature_norm_squared, signature_trace_form, ) -from core.module import CliffordModule from experiments._lib import ( build_visualization_metadata, ensure_output_dir, diff --git a/experiments/dbg_maxwell_equations.py b/experiments/dbg_maxwell_equations.py index eff8a56..391203e 100644 --- a/experiments/dbg_maxwell_equations.py +++ b/experiments/dbg_maxwell_equations.py @@ -57,8 +57,8 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))) -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra from experiments._lib import ( build_visualization_metadata, ensure_output_dir, diff --git a/experiments/dbg_navier_stokes.py b/experiments/dbg_navier_stokes.py index 1aa86b3..42f6eec 100644 --- a/experiments/dbg_navier_stokes.py +++ b/experiments/dbg_navier_stokes.py @@ -56,8 +56,8 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))) -from core.metric import hermitian_grade_spectrum, hermitian_inner_product -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.metric import hermitian_grade_spectrum, hermitian_inner_product from experiments._lib import ( build_visualization_metadata, count_parameters, diff --git a/experiments/dbg_yang_mills.py b/experiments/dbg_yang_mills.py index cf56a71..4d55eab 100644 --- a/experiments/dbg_yang_mills.py +++ b/experiments/dbg_yang_mills.py @@ -54,9 +54,9 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))) -from core.algebra import CliffordAlgebra -from core.metric import hermitian_grade_spectrum, hermitian_inner_product -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra +from core.runtime.metric import hermitian_grade_spectrum, hermitian_inner_product from experiments._lib import ( build_visualization_metadata, count_parameters, diff --git a/experiments/inc_embed_compress.py b/experiments/inc_embed_compress.py index dce84c8..4474dd4 100644 --- a/experiments/inc_embed_compress.py +++ b/experiments/inc_embed_compress.py @@ -61,11 +61,11 @@ from datasets import load_dataset from sentence_transformers import SentenceTransformer -from core.algebra import CliffordAlgebra from core.analysis._types import DimensionResult from core.analysis.dimension import DimensionLifter, EffectiveDimensionAnalyzer from core.analysis.spectral import SpectralAnalyzer -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra from experiments._lib import ( build_visualization_metadata, ensure_output_dir, diff --git a/experiments/inc_lattice_morph.py b/experiments/inc_lattice_morph.py index 61c94b3..84b6fa1 100644 --- a/experiments/inc_lattice_morph.py +++ b/experiments/inc_lattice_morph.py @@ -48,10 +48,9 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))) -from core.algebra import CliffordAlgebra -from core.decomposition import ExpPolicy -from core.metric import induced_norm -from core.module import CliffordModule +from core.foundation.module import AlgebraLike, CliffordModule +from core.runtime.decomposition import ExpPolicy +from core.runtime.metric import induced_norm from experiments._lib import ( build_visualization_metadata, ensure_output_dir, @@ -59,6 +58,7 @@ save_experiment_figure, section_header, set_seed, + setup_algebra, signature_metadata, ) from optimizers.riemannian import RiemannianAdam @@ -118,14 +118,11 @@ class MorphMode(str, Enum): class StructureTracker: """Computes lattice geometric invariants from basis multivectors.""" - def __init__(self, algebra: CliffordAlgebra): + def __init__(self, algebra: AlgebraLike): self.algebra = algebra def compute_volume(self, basis_mvs: torch.Tensor) -> torch.Tensor: - """det(L) = ||b_1 ^ b_2 ^ ... ^ b_n|| via iterative outer product. - - Uses grade_projection(GP(blade, b_i), k+1) instead of wedge(), - because wedge() is (AB-BA)/2 which only works for two vectors. + """det(L) = ||b_1 ^ b_2 ^ ... ^ b_n|| via iterative exterior product. Args: basis_mvs: [n, D] grade-1 multivectors. @@ -135,9 +132,15 @@ def compute_volume(self, basis_mvs: torch.Tensor) -> torch.Tensor: blade = basis_mvs[0] current_grade = 1 for i in range(1, basis_mvs.shape[0]): - product = self.algebra.geometric_product(blade, basis_mvs[i]) - current_grade += 1 - blade = self.algebra.grade_projection(product, current_grade) + next_grade = current_grade + 1 + blade = self.algebra.wedge( + blade, + basis_mvs[i], + left_grades=(current_grade,), + right_grades=(1,), + output_grades=(next_grade,), + ) + current_grade = next_grade return induced_norm(self.algebra, blade).squeeze(-1) def compute_gram_matrix(self, basis_mvs: torch.Tensor) -> torch.Tensor: @@ -212,10 +215,10 @@ class MorphStage(CliffordModule): When ``compound_blades >= 2`` and ``n >= 4`` the global and twist rotors learn a *sum* of independent simple bivectors per slot, producing non-simple bivectors that are handled by ``algebra.exp()`` via the active - :class:`~core.decomposition.ExpPolicy`. + :class:`~core.runtime.decomposition.ExpPolicy`. """ - def __init__(self, algebra: CliffordAlgebra, n: int, compound_blades: int = 1): + def __init__(self, algebra: AlgebraLike, n: int, compound_blades: int = 1): """ Args: algebra: Clifford algebra instance. @@ -386,7 +389,7 @@ def inverse(self, basis_mvs: torch.Tensor) -> torch.Tensor: class MorphPipeline(nn.Module): """Sequential composition of MorphStages with intermediate tracking.""" - def __init__(self, algebra: CliffordAlgebra, n: int, num_stages: int = 3, compound_blades: int = 1): + def __init__(self, algebra: AlgebraLike, n: int, num_stages: int = 3, compound_blades: int = 1): super().__init__() self.compound_blades = compound_blades self.stages = nn.ModuleList( @@ -456,8 +459,7 @@ def __init__( p, q = n - 1, 1 else: p, q = n, 0 - self.algebra = CliffordAlgebra(p=p, q=q, device=device, dtype=dtype).to(device) - self.algebra.exp_policy = ExpPolicy.PRECISE + self.algebra = setup_algebra(p=p, q=q, device=device, dtype=dtype, exp_policy=ExpPolicy.PRECISE) self.signature_q = q self.tracker = StructureTracker(self.algebra) self.pipeline = MorphPipeline( @@ -711,7 +713,7 @@ def verify_reconstruction(self, tolerance: float = 1e-5) -> dict: class MorphVisualizer: """Visualization for lattice morphing.""" - def __init__(self, algebra: CliffordAlgebra, n: int, output_dir: str, metadata: str, args: argparse.Namespace): + def __init__(self, algebra: AlgebraLike, n: int, output_dir: str, metadata: str, args: argparse.Namespace): self.algebra = algebra self.n = n self.output_dir = ensure_output_dir(output_dir) diff --git a/experiments/inc_pendulum_dynamics.py b/experiments/inc_pendulum_dynamics.py index 5770c87..94178d3 100644 --- a/experiments/inc_pendulum_dynamics.py +++ b/experiments/inc_pendulum_dynamics.py @@ -52,9 +52,9 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))) -from core.algebra import CliffordAlgebra -from core.metric import hermitian_norm -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra +from core.runtime.metric import hermitian_norm from experiments._lib import ( apply_residual_block, build_visualization_metadata, diff --git a/experiments/inc_sta_trajectory.py b/experiments/inc_sta_trajectory.py index 4bda41e..9011840 100644 --- a/experiments/inc_sta_trajectory.py +++ b/experiments/inc_sta_trajectory.py @@ -56,9 +56,9 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))) -from core.algebra import CliffordAlgebra -from core.metric import signature_norm_squared -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra +from core.runtime.metric import signature_norm_squared from experiments._lib import ( build_visualization_metadata, count_parameters, diff --git a/functional/__init__.py b/functional/__init__.py index 595a9b1..13c025c 100644 --- a/functional/__init__.py +++ b/functional/__init__.py @@ -3,7 +3,7 @@ Includes activation functions, loss functions, and orthogonality enforcement. """ -from .activation import GeometricGELU, GradeSwish +from .activation import GeometricGELU, GeometricSquare, GradeSwish from .loss import ( BivectorRegularization, ChamferDistance, @@ -15,11 +15,43 @@ SubspaceLoss, ) from .orthogonality import OrthogonalitySettings, StrictOrthogonality +from .products import ( + anti_commutator, + clifford_conjugation, + commutator, + dual, + embed_vector, + geometric_product, + grade_involution, + grade_projection, + inner_product, + norm_sq, + product, + projected_product, + reverse, + wedge, +) __all__ = [ # activations "GeometricGELU", + "GeometricSquare", "GradeSwish", + # products + "product", + "projected_product", + "geometric_product", + "wedge", + "inner_product", + "commutator", + "anti_commutator", + "grade_projection", + "reverse", + "grade_involution", + "clifford_conjugation", + "dual", + "norm_sq", + "embed_vector", # losses "GeometricMSELoss", "SubspaceLoss", diff --git a/functional/activation.py b/functional/activation.py index c66d84b..92510c2 100644 --- a/functional/activation.py +++ b/functional/activation.py @@ -14,7 +14,7 @@ import torch.nn as nn import torch.nn.functional as F -from core.module import CliffordModule +from core.foundation.module import CliffordModule class GeometricGELU(CliffordModule): diff --git a/functional/loss.py b/functional/loss.py index a14a9fd..1a8f365 100644 --- a/functional/loss.py +++ b/functional/loss.py @@ -9,8 +9,8 @@ import torch.nn as nn import torch.nn.functional as F -from core.metric import hermitian_grade_spectrum -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.metric import hermitian_grade_spectrum class GeometricMSELoss(CliffordModule): diff --git a/functional/orthogonality.py b/functional/orthogonality.py index 6730f4c..c906015 100644 --- a/functional/orthogonality.py +++ b/functional/orthogonality.py @@ -52,7 +52,7 @@ import torch import torch.nn as nn -from core.module import CliffordModule +from core.foundation.module import CliffordModule @dataclass diff --git a/functional/products.py b/functional/products.py new file mode 100644 index 0000000..bc3f254 --- /dev/null +++ b/functional/products.py @@ -0,0 +1,130 @@ +"""Stateless geometric algebra product helpers. + +These wrappers keep model code concise while preserving the algebra host as the +single execution authority. Dense kernels, compact planned kernels, and pairwise +planned kernels all flow through the same public calls. +""" + +from __future__ import annotations + +from typing import Any + +import torch + +_PRODUCT_METHODS = { + "gp": ("gp", "geometric_product"), + "geometric_product": ("gp", "geometric_product"), + "wedge": ("wedge", "wedge"), + "outer": ("wedge", "wedge"), + "inner": ("inner", "inner_product"), + "inner_product": ("inner", "inner_product"), + "commutator": ("commutator", "commutator"), + "anti_commutator": ("anti_commutator", "anti_commutator"), + "anticommutator": ("anti_commutator", "anti_commutator"), +} + + +def canonical_product_op(op: str) -> str: + """Return the planner op name for a supported product alias.""" + return _resolve_product_op(op)[0] + + +def _resolve_product_op(op: str) -> tuple[str, str]: + op_key = op.lower() + if op_key not in _PRODUCT_METHODS: + supported = ", ".join(sorted(_PRODUCT_METHODS)) + raise ValueError(f"Unsupported product op {op!r}. Supported ops: {supported}") + return _PRODUCT_METHODS[op_key] + + +def product(algebra, left: torch.Tensor, right: torch.Tensor, *, op: str = "gp", **kwargs: Any) -> torch.Tensor: + """Apply a binary geometric algebra product. + + Args: + algebra: Dense ``CliffordAlgebra`` or planned ``AlgebraContext``. + left: Left operand. + right: Right operand. + op: ``"gp"``, ``"wedge"``, ``"inner"``, ``"commutator"``, or + ``"anti_commutator"``. + **kwargs: Optional grade/layout declarations accepted by + ``algebra.projected_product``. + + Returns: + Product values in dense or compact form according to ``kwargs``. + """ + planned_op, method_name = _resolve_product_op(op) + if kwargs: + return algebra.projected_product(left, right, op=planned_op, **kwargs) + return getattr(algebra, method_name)(left, right) + + +def projected_product( + algebra, + left: torch.Tensor, + right: torch.Tensor, + *, + op: str = "gp", + **kwargs: Any, +) -> torch.Tensor: + """Apply a declared grade-restricted product through the planner.""" + return product(algebra, left, right, op=op, **kwargs) + + +def geometric_product(algebra, left: torch.Tensor, right: torch.Tensor, **kwargs: Any) -> torch.Tensor: + """Apply the geometric product.""" + return product(algebra, left, right, op="gp", **kwargs) + + +def wedge(algebra, left: torch.Tensor, right: torch.Tensor, **kwargs: Any) -> torch.Tensor: + """Apply the exterior product.""" + return product(algebra, left, right, op="wedge", **kwargs) + + +def inner_product(algebra, left: torch.Tensor, right: torch.Tensor, **kwargs: Any) -> torch.Tensor: + """Apply the inner product.""" + return product(algebra, left, right, op="inner", **kwargs) + + +def commutator(algebra, left: torch.Tensor, right: torch.Tensor, **kwargs: Any) -> torch.Tensor: + """Apply the commutator product.""" + return product(algebra, left, right, op="commutator", **kwargs) + + +def anti_commutator(algebra, left: torch.Tensor, right: torch.Tensor, **kwargs: Any) -> torch.Tensor: + """Apply the anti-commutator product.""" + return product(algebra, left, right, op="anti_commutator", **kwargs) + + +def grade_projection(algebra, values: torch.Tensor, grade: int, **kwargs: Any) -> torch.Tensor: + """Project multivectors to one grade.""" + return algebra.grade_projection(values, grade, **kwargs) + + +def reverse(algebra, values: torch.Tensor, **kwargs: Any) -> torch.Tensor: + """Apply reversion.""" + return algebra.reverse(values, **kwargs) + + +def grade_involution(algebra, values: torch.Tensor, **kwargs: Any) -> torch.Tensor: + """Apply grade involution.""" + return algebra.grade_involution(values, **kwargs) + + +def clifford_conjugation(algebra, values: torch.Tensor, **kwargs: Any) -> torch.Tensor: + """Apply Clifford conjugation.""" + return algebra.clifford_conjugation(values, **kwargs) + + +def dual(algebra, values: torch.Tensor) -> torch.Tensor: + """Apply the Hodge dual.""" + return algebra.dual(values) + + +def norm_sq(algebra, values: torch.Tensor) -> torch.Tensor: + """Return the algebraic squared norm.""" + return algebra.norm_sq(values) + + +def embed_vector(algebra, vectors: torch.Tensor) -> torch.Tensor: + """Embed coordinate vectors into the grade-1 subspace.""" + return algebra.embed_vector(vectors) diff --git a/layers/__init__.py b/layers/__init__.py index 2e2061c..457847b 100644 --- a/layers/__init__.py +++ b/layers/__init__.py @@ -3,7 +3,7 @@ Organized into Primitives, Canonical Blocks, and Task-Specific Adapters. """ -from core.module import CliffordModule +from core.foundation.module import CliffordModule from .adapters.embedding import MultivectorEmbedding, RotaryBivectorPE from .adapters.mother import EntropyGatedAttention, MotherEmbedding, PhaseShiftHead @@ -13,7 +13,16 @@ from .primitives.linear import CliffordLinear from .primitives.multi_rotor import MultiRotorLayer from .primitives.normalization import CliffordLayerNorm +from .primitives.product import ( + AntiCommutatorLayer, + CommutatorLayer, + GeometricProductLayer, + InnerProductLayer, + ProductLayer, + WedgeLayer, +) from .primitives.projection import BladeSelector, GeometricNeutralizer +from .primitives.reflection import ReflectionLayer from .primitives.rotor import RotorLayer from .primitives.rotor_gadget import RotorGadget @@ -30,8 +39,15 @@ "CliffordLinear", "RotorGadget", "CliffordLayerNorm", + "ProductLayer", + "GeometricProductLayer", + "WedgeLayer", + "InnerProductLayer", + "CommutatorLayer", + "AntiCommutatorLayer", "BladeSelector", "GeometricNeutralizer", + "ReflectionLayer", "MultivectorEmbedding", "RotaryBivectorPE", "MotherEmbedding", diff --git a/layers/adapters/conformal.py b/layers/adapters/conformal.py index cc48056..dd6f958 100644 --- a/layers/adapters/conformal.py +++ b/layers/adapters/conformal.py @@ -7,8 +7,8 @@ import torch -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra class ConformalEmbedding(CliffordModule): diff --git a/layers/adapters/embedding.py b/layers/adapters/embedding.py index beb6300..1b0bfee 100644 --- a/layers/adapters/embedding.py +++ b/layers/adapters/embedding.py @@ -8,8 +8,9 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.foundation.manifold import MANIFOLD_SPIN, tag_manifold +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra class MultivectorEmbedding(CliffordModule): @@ -122,7 +123,7 @@ def __init__( if learnable: self.bivector_weights = nn.Parameter(init) - self.bivector_weights._manifold = "spin" + tag_manifold(self.bivector_weights, MANIFOLD_SPIN) else: self.register_buffer("bivector_weights", init) diff --git a/layers/adapters/gnn.py b/layers/adapters/gnn.py index 71b6591..de87277 100644 --- a/layers/adapters/gnn.py +++ b/layers/adapters/gnn.py @@ -8,8 +8,8 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra from ..primitives.linear import CliffordLinear diff --git a/layers/adapters/mother.py b/layers/adapters/mother.py index 29079a0..883fb5a 100644 --- a/layers/adapters/mother.py +++ b/layers/adapters/mother.py @@ -8,8 +8,8 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra from ..blocks.attention import GeometricProductAttention from ..primitives.normalization import CliffordLayerNorm diff --git a/layers/adapters/projective.py b/layers/adapters/projective.py index 4058915..200ca9a 100644 --- a/layers/adapters/projective.py +++ b/layers/adapters/projective.py @@ -7,8 +7,8 @@ import torch -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra class ProjectiveEmbedding(CliffordModule): diff --git a/layers/blocks/attention.py b/layers/blocks/attention.py index 3f54985..2caebbe 100644 --- a/layers/blocks/attention.py +++ b/layers/blocks/attention.py @@ -11,13 +11,15 @@ import torch.nn as nn import torch.nn.functional as F -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra from ..primitives.linear import CliffordLinear # Memory-bounded block size for chunked attention computation _BLOCK_SIZE = 64 +_G2_BLADE_CHUNK_SIZE = 16 +_SCORE_PRECOMPUTE_LIMIT = 8_000_000 class GeometricProductAttention(CliffordModule): @@ -50,6 +52,8 @@ def __init__( causal: bool = True, bivector_weight: float = 0.5, dropout: float = 0.0, + score_blade_chunk_size: int = _G2_BLADE_CHUNK_SIZE, + score_precompute_limit: int = _SCORE_PRECOMPUTE_LIMIT, ): """Sets up geometric product attention. @@ -60,6 +64,10 @@ def __init__( causal: Apply causal mask for autoregressive generation. bivector_weight: lambda_ weight on bivector score component. dropout: Dropout rate on attention weights. + score_blade_chunk_size: Grade-2 output blades processed per dense + chunk when exact dense scoring is used. + score_precompute_limit: Maximum temporary ``K_g2`` elements allowed + before exact dense scoring switches to chunked grade-2 blades. """ super().__init__(algebra) assert channels % num_heads == 0, f"channels ({channels}) must be divisible by num_heads ({num_heads})" @@ -69,6 +77,8 @@ def __init__( self.head_channels = channels // num_heads self.causal = causal self.bivector_weight = bivector_weight + self.score_blade_chunk_size = max(1, int(score_blade_chunk_size)) + self.score_precompute_limit = max(0, int(score_precompute_limit)) # Q, K, V projections operate on [B*L, channels, dim] self.q_proj = CliffordLinear(algebra, channels, channels) @@ -78,83 +88,37 @@ def __init__( self.attn_dropout = nn.Dropout(dropout) if dropout > 0.0 else None - # Precompute bilinear score tables (replaces pairwise geometric product) + # Precompute bilinear score routes (replaces pairwise geometric product) self._precompute_score_tables() def _precompute_score_tables(self): - """Precomputes lookup tables for efficient attention scoring. - - Replaces the O(L**2) full pairwise geometric product with direct bilinear - forms for grade-0 and grade-2 components of Q * reverse(K): - - Grade-0: _0 = Sum_a Q[a] * K[a] * metric_rev[a] - -> simple weighted dot product, no pairwise expansion needed. - - Grade-2: _r = Sum_a Q[a] * K[a^r] * g2_sign[r, a] - -> precompute K_g2 once, then batched matmul. - - Memory: ~4 MB peak vs ~256 MB for the naive B_gathered approach. - """ + """Precompute exact dense attention score routes.""" alg = self.algebra D = alg.dim + if not hasattr(alg, "gp_signs") or not hasattr(alg, "rev_signs"): + raise ValueError("GeometricProductAttention currently requires dense CliffordAlgebra inputs.") + # Grade-0 metric: metric_rev[a] = gp_signs[a, 0] * rev_signs[a] # gp_signs[a, 0] is the sign when A[a] * B[a] contributes to output blade 0 metric_rev = alg.gp_signs[:, 0].float() * alg.rev_signs.float() self.register_buffer("_metric_rev", metric_rev) # [D] - # Grade-2 tables: for each grade-2 blade r, for each A-blade a: - # B-blade = a XOR r - # sign = rev_sign[a^r] * gp_signs[a, r] g2_blades = [i for i in range(D) if bin(i).count("1") == 2] - n_g2 = len(g2_blades) - self.n_g2 = n_g2 - - if n_g2 > 0: - a_idx = torch.arange(D, device=alg.device) - r_vals = torch.tensor(g2_blades, dtype=torch.long, device=alg.device) # [n_g2] - - # b_idx[r, a] = a XOR r_vals[r] - b_idx = a_idx.unsqueeze(0) ^ r_vals.unsqueeze(1) # [n_g2, D] - - # rev_sign at the B-blade position - rev_b = alg.rev_signs.float()[b_idx] # [n_g2, D] - - # gp_signs[a, r_val]: sign when A[a] pairs with B[a^r] to give output r - # alg.gp_signs[:, r_vals] -> [D, n_g2]; transpose -> [n_g2, D] - gp_ar = alg.gp_signs[:, r_vals].float().T # [n_g2, D] - - g2_sign = rev_b * gp_ar # [n_g2, D] - else: - b_idx = torch.zeros(0, D, dtype=torch.long, device=alg.device) - g2_sign = torch.zeros(0, D, device=alg.device) - - self.register_buffer("_g2_b_idx", b_idx) # [n_g2, D] long - self.register_buffer("_g2_sign", g2_sign) # [n_g2, D] float + self.n_g2 = len(g2_blades) + self.register_buffer("_g2_blades", torch.tensor(g2_blades, dtype=torch.long, device=alg.device)) + self.register_buffer("_basis_indices", torch.arange(D, dtype=torch.long, device=alg.device)) def _compute_score( self, q_head: torch.Tensor, k_head: torch.Tensor, - k_g2: torch.Tensor, ) -> torch.Tensor: - """Computes GA attention score using precomputed bilinear form tables. - - Avoids the O(B.H.Lq.Lk.Hc.D.BLOCK) memory of the full pairwise - geometric product. Instead: - - Grade-0: score_g0 = Q_weighted @ K^T (weighted dot product, peak ~1 MB) - Grade-2: batched matmul via precomputed k_g2 (peak ~4 MB) + """Compute GA attention scores for one query block.""" + return self._compute_score_dense(q_head, k_head) - Args: - q_head: Query block [B, H, Lq, Hc, D] - k_head: Keys [B, H, Lk, Hc, D] - k_g2: Precomputed [B, H, Lk, Hc, n_g2, D] - k_g2[b,h,j,c,r,d] = K[b,h,j,c, d^r] * g2_sign[r, d] - - Returns: - scores: [B, H, Lq, Lk] - """ + def _compute_score_dense(self, q_head: torch.Tensor, k_head: torch.Tensor) -> torch.Tensor: + """Exact dense score with automatic full/prechunked grade-2 routing.""" B, H, Lq, Hc, D = q_head.shape Lk = k_head.shape[2] n_g2 = self.n_g2 @@ -169,21 +133,15 @@ def _compute_score( # == Grade-2 score ==================================================== # ||_2||_F = sqrt(Sum_c Sum_r (Sum_d Q[c,d]*k_g2[j,c,r,d])^2) - # Batched matmul merging (B, H, Hc) into one batch dimension: - # q_2d: [B*H*Hc, Lq, D] - # k_g2_2d: [B*H*Hc, Lk*n_g2, D] (Lk and n_g2 merged, n_g2 varies fast) - # comp: [B*H*Hc, Lq, Lk*n_g2] - # Peak ~4 MB vs ~256 MB for the naive B_gathered approach. if n_g2 > 0: q_2d = q_head.permute(0, 1, 3, 2, 4).reshape(B * H * Hc, Lq, D) - # k_g2: [B, H, Lk, Hc, n_g2, D] -> permute to [B, H, Hc, Lk, n_g2, D] - k_g2_t = k_g2.permute(0, 1, 3, 2, 4, 5) - k_g2_2d = k_g2_t.reshape(B * H * Hc, Lk * n_g2, D) - # [B*H*Hc, Lq, D] @ [B*H*Hc, D, Lk*n_g2] -> [B*H*Hc, Lq, Lk*n_g2] - comp = torch.bmm(q_2d, k_g2_2d.transpose(-2, -1)) - # Sum squared components over n_g2, then sum over Hc -> [B, H, Lq, Lk] - comp_sq = comp.reshape(B * H * Hc, Lq, Lk, n_g2).pow(2).sum(-1) # [B*H*Hc, Lq, Lk] - score_g2_sq = comp_sq.reshape(B, H, Hc, Lq, Lk).sum(2) # [B, H, Lq, Lk] + + full_k_g2_elements = B * H * Lk * Hc * n_g2 * D + if full_k_g2_elements <= self.score_precompute_limit: + score_g2_sq = self._dense_score_g2_precomputed(q_2d, k_head, B, H, Hc, Lq, Lk, D, n_g2) + else: + k_2d = k_head.permute(0, 1, 3, 2, 4).reshape(B * H * Hc, Lk, D) + score_g2_sq = self._dense_score_g2_chunked(q_2d, k_2d, B, H, Hc, Lq, Lk, D, n_g2) score_g2 = score_g2_sq.sqrt() else: score_g2 = torch.zeros_like(score_g0) @@ -192,6 +150,39 @@ def _compute_score( scale = math.sqrt(self.head_channels * self.algebra.dim) return (score_g0 + self.bivector_weight * score_g2) / scale + def _dense_score_g2_precomputed(self, q_2d, k_head, B, H, Hc, Lq, Lk, D, n_g2): + """Dense grade-2 score using one full shifted-key materialization.""" + r_vals = self._g2_blades + b_idx = self._basis_indices.unsqueeze(0) ^ r_vals.unsqueeze(1) + rev_b = self.algebra.rev_signs[b_idx].to(dtype=k_head.dtype) + gp_ar = self.algebra.gp_signs[:, r_vals].T.to(dtype=k_head.dtype) + g2_sign = rev_b * gp_ar + + k_g2 = k_head[..., b_idx] * g2_sign + k_g2_2d = k_g2.permute(0, 1, 3, 2, 4, 5).reshape(B * H * Hc, Lk * n_g2, D) + comp = torch.bmm(q_2d, k_g2_2d.transpose(-2, -1)) + comp_sq = comp.reshape(B * H * Hc, Lq, Lk, n_g2).pow(2).sum(-1) + return comp_sq.reshape(B, H, Hc, Lq, Lk).sum(2) + + def _dense_score_g2_chunked(self, q_2d, k_2d, B, H, Hc, Lq, Lk, D, n_g2): + """Dense grade-2 score using bounded output-blade chunks.""" + score_g2_sq = q_2d.new_zeros(B, H, Lq, Lk) + for start in range(0, n_g2, self.score_blade_chunk_size): + end = min(start + self.score_blade_chunk_size, n_g2) + r_vals = self._g2_blades[start:end] + b_idx = self._basis_indices.unsqueeze(0) ^ r_vals.unsqueeze(1) + rev_b = self.algebra.rev_signs[b_idx].to(dtype=k_2d.dtype) + gp_ar = self.algebra.gp_signs[:, r_vals].T.to(dtype=k_2d.dtype) + g2_sign = rev_b * gp_ar + + k_shifted = torch.index_select(k_2d, -1, b_idx.reshape(-1)) + k_shifted = k_shifted * g2_sign.reshape(-1) + k_g2_2d = k_shifted.reshape(B * H * Hc, Lk * (end - start), D) + comp = torch.bmm(q_2d, k_g2_2d.transpose(-2, -1)) + comp_sq = comp.reshape(B * H * Hc, Lq, Lk, end - start).pow(2).sum(-1) + score_g2_sq = score_g2_sq + comp_sq.reshape(B, H, Hc, Lq, Lk).sum(2) + return score_g2_sq + def forward(self, x: torch.Tensor, key_padding_mask: torch.Tensor = None) -> torch.Tensor: """Computes geometric product attention. @@ -226,11 +217,6 @@ def forward(self, x: torch.Tensor, key_padding_mask: torch.Tensor = None) -> tor else: causal_mask = None - # Precompute K_g2 once for all query blocks - much cheaper than recomputing - # k_g2[b,h,j,c,r,d] = K[b,h,j,c, d^r_val] * g2_sign[r, d] - # Shape: [B, H, L, Hc, n_g2, D] ~= 768 KB for the small MPS config - K_g2 = K[..., self._g2_b_idx] * self._g2_sign # [B, H, L, Hc, n_g2, D] - # Chunked attention over query positions to bound memory output_chunks = [] for q_start in range(0, L, _BLOCK_SIZE): @@ -239,7 +225,7 @@ def forward(self, x: torch.Tensor, key_padding_mask: torch.Tensor = None) -> tor Q_block = Q[:, :, q_start:q_end] # [B, H, Lq, Hc, D] # Compute scores: [B, H, Lq, L] - scores = self._compute_score(Q_block, K, K_g2) + scores = self._compute_score(Q_block, K) # Apply causal mask if causal_mask is not None: diff --git a/layers/blocks/multi_rotor_ffn.py b/layers/blocks/multi_rotor_ffn.py index fbc6891..1feb76f 100644 --- a/layers/blocks/multi_rotor_ffn.py +++ b/layers/blocks/multi_rotor_ffn.py @@ -7,8 +7,8 @@ import torch -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra from functional.activation import GeometricGELU from ..primitives.linear import CliffordLinear diff --git a/layers/blocks/transformer.py b/layers/blocks/transformer.py index e750083..0c5459f 100644 --- a/layers/blocks/transformer.py +++ b/layers/blocks/transformer.py @@ -8,8 +8,8 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra from ..adapters.mother import EntropyGatedAttention from ..primitives.normalization import CliffordLayerNorm diff --git a/layers/primitives/__init__.py b/layers/primitives/__init__.py index e69de29..451f879 100644 --- a/layers/primitives/__init__.py +++ b/layers/primitives/__init__.py @@ -0,0 +1,34 @@ +"""Primitive Clifford neural network layers.""" + +from .linear import CliffordLinear +from .multi_rotor import MultiRotorLayer +from .normalization import CliffordLayerNorm +from .product import ( + AntiCommutatorLayer, + CommutatorLayer, + GeometricProductLayer, + InnerProductLayer, + ProductLayer, + WedgeLayer, +) +from .projection import BladeSelector, GeometricNeutralizer +from .reflection import ReflectionLayer +from .rotor import RotorLayer +from .rotor_gadget import RotorGadget + +__all__ = [ + "CliffordLinear", + "MultiRotorLayer", + "CliffordLayerNorm", + "ProductLayer", + "GeometricProductLayer", + "WedgeLayer", + "InnerProductLayer", + "CommutatorLayer", + "AntiCommutatorLayer", + "BladeSelector", + "GeometricNeutralizer", + "ReflectionLayer", + "RotorLayer", + "RotorGadget", +] diff --git a/layers/primitives/linear.py b/layers/primitives/linear.py index 6cd5003..a678506 100644 --- a/layers/primitives/linear.py +++ b/layers/primitives/linear.py @@ -15,9 +15,9 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra -from core.module import CliffordModule -from core.validation import check_channels, check_multivector +from core.foundation.module import CliffordModule +from core.foundation.validation import check_channels, check_multivector +from core.runtime.algebra import CliffordAlgebra class CliffordLinear(CliffordModule): diff --git a/layers/primitives/multi_rotor.py b/layers/primitives/multi_rotor.py index d51d385..0198f0b 100644 --- a/layers/primitives/multi_rotor.py +++ b/layers/primitives/multi_rotor.py @@ -13,9 +13,10 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra -from core.module import CliffordModule -from core.validation import check_channels, check_multivector +from core.foundation.manifold import MANIFOLD_SPIN, tag_manifold +from core.foundation.module import CliffordModule +from core.foundation.validation import check_channels, check_multivector +from core.runtime.algebra import CliffordAlgebra class MultiRotorLayer(CliffordModule): @@ -63,7 +64,7 @@ def __init__( self.rotor_grade_weights = nn.Parameter(torch.Tensor(num_rotors, self.num_grade_elements)) if grade == 2: - self.rotor_grade_weights._manifold = "spin" + tag_manifold(self.rotor_grade_weights, MANIFOLD_SPIN) # Mixing weights (Euclidean — intentionally untagged) self.weights = nn.Parameter(torch.Tensor(channels, num_rotors)) diff --git a/layers/primitives/normalization.py b/layers/primitives/normalization.py index c10cf2c..149566b 100644 --- a/layers/primitives/normalization.py +++ b/layers/primitives/normalization.py @@ -8,8 +8,8 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra class CliffordLayerNorm(CliffordModule): diff --git a/layers/primitives/product.py b/layers/primitives/product.py new file mode 100644 index 0000000..90c9819 --- /dev/null +++ b/layers/primitives/product.py @@ -0,0 +1,138 @@ +"""Module wrappers around declared geometric algebra products.""" + +from __future__ import annotations + +from typing import Iterable, Optional + +import torch + +from core.foundation.basis import normalize_grades +from core.foundation.module import CliffordModule +from functional.products import canonical_product_op, product + + +def _normalize_optional_grades(grades: Optional[Iterable[int]], n: int, *, name: str) -> Optional[tuple[int, ...]]: + if grades is None: + return None + if isinstance(grades, int): + grades = (grades,) + return normalize_grades(grades, n, name=name) + + +class ProductLayer(CliffordModule): + """Apply a geometric algebra product inside ``nn.Module`` graphs. + + The layer is intentionally thin: grade declarations and compact/pairwise + behavior are forwarded to ``algebra.projected_product`` when supplied, + while the dense no-declaration path uses the algebra's direct kernels. + """ + + def __init__( + self, + algebra, + *, + op: str = "gp", + left_grades: Optional[Iterable[int]] = None, + right_grades: Optional[Iterable[int]] = None, + output_grades: Optional[Iterable[int]] = None, + left_compact: bool = False, + right_compact: bool = False, + compact_output: bool = False, + pairwise: bool = False, + ): + """Initialize a product layer. + + Args: + algebra: Dense ``CliffordAlgebra`` or planned ``AlgebraContext``. + op: Product route: ``"gp"``, ``"wedge"``, ``"inner"``, + ``"commutator"``, or ``"anti_commutator"``. + left_grades: Declared input grades for the left operand. + right_grades: Declared input grades for the right operand. + output_grades: Optional output grade projection. + left_compact: Whether the left operand is already compact. + right_compact: Whether the right operand is already compact. + compact_output: Return compact output instead of dense coefficients. + pairwise: Treat the penultimate dimension of each compact operand + as independent left/right item axes. + """ + super().__init__(algebra) + self.op = canonical_product_op(op) + self.left_grades = _normalize_optional_grades(left_grades, algebra.n, name="left_grades") + self.right_grades = _normalize_optional_grades(right_grades, algebra.n, name="right_grades") + self.output_grades = _normalize_optional_grades(output_grades, algebra.n, name="output_grades") + self.left_compact = bool(left_compact) + self.right_compact = bool(right_compact) + self.compact_output = bool(compact_output) + self.pairwise = bool(pairwise) + + def forward(self, left: torch.Tensor, right: torch.Tensor) -> torch.Tensor: + """Apply the configured product to ``left`` and ``right``.""" + kwargs = self._product_kwargs() + return product(self.algebra, left, right, op=self.op, **kwargs) + + def _product_kwargs(self) -> dict: + kwargs = {} + if self.left_grades is not None: + kwargs["left_grades"] = self.left_grades + if self.right_grades is not None: + kwargs["right_grades"] = self.right_grades + if self.output_grades is not None: + kwargs["output_grades"] = self.output_grades + if self.left_compact: + kwargs["left_compact"] = True + if self.right_compact: + kwargs["right_compact"] = True + if self.compact_output: + kwargs["compact_output"] = True + if self.pairwise: + kwargs["pairwise"] = True + return kwargs + + def extra_repr(self) -> str: + parts = [f"op={self.op!r}"] + if self.left_grades is not None: + parts.append(f"left_grades={self.left_grades}") + if self.right_grades is not None: + parts.append(f"right_grades={self.right_grades}") + if self.output_grades is not None: + parts.append(f"output_grades={self.output_grades}") + if self.compact_output: + parts.append("compact_output=True") + if self.pairwise: + parts.append("pairwise=True") + return ", ".join(parts) + + +class GeometricProductLayer(ProductLayer): + """Layer form of the geometric product.""" + + def __init__(self, algebra, **kwargs): + super().__init__(algebra, op="gp", **kwargs) + + +class WedgeLayer(ProductLayer): + """Layer form of the exterior product.""" + + def __init__(self, algebra, **kwargs): + super().__init__(algebra, op="wedge", **kwargs) + + +class InnerProductLayer(ProductLayer): + """Layer form of the inner product.""" + + def __init__(self, algebra, **kwargs): + super().__init__(algebra, op="inner", **kwargs) + + +class CommutatorLayer(ProductLayer): + """Layer form of the commutator product.""" + + def __init__(self, algebra, **kwargs): + super().__init__(algebra, op="commutator", **kwargs) + + +class AntiCommutatorLayer(ProductLayer): + """Layer form of the anti-commutator product.""" + + def __init__(self, algebra, **kwargs): + super().__init__(algebra, op="anti_commutator", **kwargs) diff --git a/layers/primitives/projection.py b/layers/primitives/projection.py index 4034292..4df6aa3 100644 --- a/layers/primitives/projection.py +++ b/layers/primitives/projection.py @@ -8,8 +8,8 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra from utils.compat import safe_linalg_solve diff --git a/layers/primitives/reflection.py b/layers/primitives/reflection.py index b37f252..0d83b47 100644 --- a/layers/primitives/reflection.py +++ b/layers/primitives/reflection.py @@ -8,9 +8,10 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra -from core.module import CliffordModule -from core.validation import check_channels, check_multivector +from core.foundation.manifold import MANIFOLD_SPHERE, tag_manifold +from core.foundation.module import CliffordModule +from core.foundation.validation import check_channels, check_multivector +from core.runtime.algebra import CliffordAlgebra class ReflectionLayer(CliffordModule): @@ -46,7 +47,7 @@ def __init__(self, algebra: CliffordAlgebra, channels: int): self.num_vectors = algebra.n self.vector_weights = nn.Parameter(torch.Tensor(channels, self.num_vectors)) - self.vector_weights._manifold = "sphere" + tag_manifold(self.vector_weights, MANIFOLD_SPHERE) # Cache for eval mode self._cached_n = None diff --git a/layers/primitives/rotor.py b/layers/primitives/rotor.py index 51732ef..2fc064c 100644 --- a/layers/primitives/rotor.py +++ b/layers/primitives/rotor.py @@ -8,9 +8,10 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra -from core.module import CliffordModule -from core.validation import check_channels, check_multivector +from core.foundation.manifold import MANIFOLD_SPIN, tag_manifold +from core.foundation.module import CliffordModule +from core.foundation.validation import check_channels, check_multivector +from core.runtime.algebra import CliffordAlgebra class RotorLayer(CliffordModule): @@ -23,7 +24,7 @@ class RotorLayer(CliffordModule): Preserves origin. For grade=2, also preserves lengths and angles (isometry). The exp strategy (closed-form vs decomposition) is controlled by - ``algebra.exp_policy`` -- see :class:`core.decomposition.ExpPolicy`. + ``algebra.exp_policy`` -- see :class:`core.runtime.decomposition.ExpPolicy`. Attributes: channels (int): Number of versors. @@ -57,7 +58,7 @@ def __init__( self.grade_weights = nn.Parameter(torch.Tensor(channels, self.num_grade_elements)) if grade == 2: - self.grade_weights._manifold = "spin" + tag_manifold(self.grade_weights, MANIFOLD_SPIN) # Versor cache for eval mode self._cached_V_left = None diff --git a/layers/primitives/rotor_gadget.py b/layers/primitives/rotor_gadget.py index 80b653e..bd283ba 100644 --- a/layers/primitives/rotor_gadget.py +++ b/layers/primitives/rotor_gadget.py @@ -14,9 +14,10 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra -from core.module import CliffordModule -from core.validation import check_channels, check_multivector +from core.foundation.manifold import MANIFOLD_SPIN, tag_manifold +from core.foundation.module import CliffordModule +from core.foundation.validation import check_channels, check_multivector +from core.runtime.algebra import CliffordAlgebra class RotorGadget(CliffordModule): @@ -89,10 +90,10 @@ def __init__( # Rotor parameters: bivector coefficients for exponential map # Left rotors: [num_rotor_pairs, num_bivectors] self.bivector_left = nn.Parameter(torch.randn(num_rotor_pairs, self.num_bivectors) * 0.1) - self.bivector_left._manifold = "spin" + tag_manifold(self.bivector_left, MANIFOLD_SPIN) # Right rotors: [num_rotor_pairs, num_bivectors] self.bivector_right = nn.Parameter(torch.randn(num_rotor_pairs, self.num_bivectors) * 0.1) - self.bivector_right._manifold = "spin" + tag_manifold(self.bivector_right, MANIFOLD_SPIN) # Channel routing: block diagonal partitioning (paper style) # Each rotor pair processes a subset of input channels diff --git a/mkdocs.yml b/mkdocs.yml index 8727d0f..d126966 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -37,6 +37,7 @@ nav: - Home: index.md - Guide: - Design Guide: design_guide.md + - Architecture: Architecture.md - Philosophy: philosophy.md - Mathematics: mathematical.md - Innovations: innovations.md diff --git a/models/blocks/gbn.py b/models/blocks/gbn.py index 79db066..2000f22 100644 --- a/models/blocks/gbn.py +++ b/models/blocks/gbn.py @@ -8,8 +8,8 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra from functional.activation import GeometricGELU from layers import BladeSelector, CliffordLinear, RotorLayer diff --git a/models/blocks/multi_rotor.py b/models/blocks/multi_rotor.py index 60fb452..145afab 100644 --- a/models/blocks/multi_rotor.py +++ b/models/blocks/multi_rotor.py @@ -8,8 +8,8 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra from functional.activation import GeometricGELU from layers import CliffordLinear, MultiRotorLayer diff --git a/models/blocks/time_series.py b/models/blocks/time_series.py index f301e9d..f674eaf 100644 --- a/models/blocks/time_series.py +++ b/models/blocks/time_series.py @@ -8,8 +8,8 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra from layers import CliffordLinear, RotorLayer diff --git a/models/deap/eeg_net.py b/models/deap/eeg_net.py index 6d2e2b9..bf4de26 100644 --- a/models/deap/eeg_net.py +++ b/models/deap/eeg_net.py @@ -18,11 +18,13 @@ (immediate) and Grade-4 (long-range) for VADL prediction. """ +from typing import Optional + import torch import torch.nn as nn -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.config import make_algebra, make_algebra_from_config +from core.foundation.module import AlgebraLike, CliffordModule from layers import ( CliffordLayerNorm, GeometricNeutralizer, @@ -39,7 +41,7 @@ class MultiTargetPhaseShiftHead(CliffordModule): each target can independently shift its prediction range. """ - def __init__(self, algebra: CliffordAlgebra, channels: int, num_targets: int = 4): + def __init__(self, algebra: AlgebraLike, channels: int, num_targets: int = 4): super().__init__(algebra) self.channels = channels self.num_targets = num_targets @@ -66,7 +68,7 @@ class EEGNet(CliffordModule): brain region's Grade-0 is cleaned of its own Grade-2 artifacts independently. """ - def __init__(self, group_sizes, profiles=None, device=None, config=None): + def __init__(self, group_sizes, profiles=None, device=None, config=None, algebra: Optional[AlgebraLike] = None): """Initialize EEGNet. Args: @@ -77,16 +79,21 @@ def __init__(self, group_sizes, profiles=None, device=None, config=None): device: Torch device. config: Hydra DictConfig or plain dict with model hyperparameters. """ - p, q = 3, 1 - if config is not None: - if hasattr(config, "algebra"): - p = config.algebra.get("p", 3) - q = config.algebra.get("q", 1) - elif isinstance(config, dict): - p = config.get("p", 3) - q = config.get("q", 1) - - algebra = CliffordAlgebra(p, q, device=device) + if algebra is None: + algebra_config = None + if config is not None: + if hasattr(config, "algebra"): + algebra_config = config.algebra + elif isinstance(config, dict): + algebra_config = config.get("algebra", config) + + p = algebra_config.get("p", 3) if algebra_config is not None else 3 + q = algebra_config.get("q", 1) if algebra_config is not None else 1 + r = algebra_config.get("r", 0) if algebra_config is not None else 0 + if algebra_config is not None: + algebra = make_algebra_from_config(algebra_config, p=p, q=q, r=r, device=device) + else: + algebra = make_algebra(p=p, q=q, r=r, device=device or "cpu") super().__init__(algebra) if config is not None and hasattr(config, "model"): diff --git a/models/lqa/glr_net.py b/models/lqa/glr_net.py index 74969a3..f91e075 100644 --- a/models/lqa/glr_net.py +++ b/models/lqa/glr_net.py @@ -24,8 +24,8 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra from layers.adapters.embedding import RotaryBivectorPE from layers.adapters.mother import MotherEmbedding from layers.blocks.transformer import GeometricTransformerBlock diff --git a/models/lqa/heads.py b/models/lqa/heads.py index 7eb985d..13bdc3f 100644 --- a/models/lqa/heads.py +++ b/models/lqa/heads.py @@ -17,8 +17,8 @@ import torch.nn as nn import torch.nn.functional as F -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra from layers.primitives.projection import GeometricNeutralizer from layers.primitives.rotor import RotorLayer diff --git a/models/md17/forcenet.py b/models/md17/forcenet.py index 8c630f9..c96bffa 100644 --- a/models/md17/forcenet.py +++ b/models/md17/forcenet.py @@ -9,8 +9,8 @@ import torch.nn as nn import torch.nn.functional as F -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra from functional.activation import GeometricGELU, GeometricSquare from layers import BladeSelector, CliffordLayerNorm, CliffordLinear, MultiRotorLayer diff --git a/models/sr/estimator.py b/models/sr/estimator.py index 121890f..b802786 100644 --- a/models/sr/estimator.py +++ b/models/sr/estimator.py @@ -16,8 +16,8 @@ import torch from sklearn.base import BaseEstimator, RegressorMixin -from core.algebra import CliffordAlgebra -from core.decomposition import ExpPolicy +from core.config import make_algebra +from core.runtime.decomposition import ExpPolicy from models.sr.net import SRGBN from models.sr.utils import make_lambdify_fn from optimizers.riemannian import RiemannianAdam @@ -93,7 +93,7 @@ def fit(self, X, y): y_t = torch.from_numpy(y_norm).unsqueeze(-1) n_vars = X.shape[1] - algebra = CliffordAlgebra(p=self.p, q=self.q, r=self.r, device="cpu", exp_policy=self.exp_policy) + algebra = make_algebra(p=self.p, q=self.q, r=self.r, device="cpu", exp_policy=self.exp_policy) self.model_ = SRGBN( algebra=algebra, diff --git a/models/sr/grouper.py b/models/sr/grouper.py index fd79ed9..67a02f9 100644 --- a/models/sr/grouper.py +++ b/models/sr/grouper.py @@ -20,7 +20,8 @@ import numpy as np import torch -from core.algebra import CliffordAlgebra +from core.config import make_algebra +from core.foundation.module import AlgebraLike from models.sr.utils import safe_svd, standardize, subsample logger = logging.getLogger(__name__) @@ -34,7 +35,7 @@ class VariableGroup: var_indices: Indices into original X columns. var_names: Human-readable variable names. signature: (p, q, r) from MetricSearch. - algebra: CliffordAlgebra for this group. + algebra: Shared dense algebra or planning context for this group. svd_Vt: SVD right-singular vectors for this group (or None). mother_offset: Bit offset in mother algebra basis. internal_edges: VariableEdge list within this group. @@ -45,7 +46,7 @@ class VariableGroup: var_indices: list var_names: list signature: tuple - algebra: CliffordAlgebra + algebra: AlgebraLike svd_Vt: np.ndarray = None mother_offset: int = 0 internal_edges: list = field(default_factory=list) @@ -223,7 +224,7 @@ def _build_relationship_graph(self, X, y, var_names): n_analysis, ) - algebra = CliffordAlgebra(p, q, r, device=self.device) + algebra = make_algebra(p, q, r, device=self.device) # 5. Embed as grade-1 multivectors alg_n = algebra.n @@ -469,13 +470,13 @@ def build_mother_algebra(self, groups): Q = sum(g.signature[1] for g in groups) R = sum(g.signature[2] for g in groups) - if P + Q + R > 12: - self._reduce_groups(groups, target_n=12) + if P + Q + R > 16: + self._reduce_groups(groups, target_n=16) P = sum(g.signature[0] for g in groups) Q = sum(g.signature[1] for g in groups) R = sum(g.signature[2] for g in groups) - mother = CliffordAlgebra(P, Q, R, device=self.device) + mother = make_algebra(P, Q, R, device=self.device) p_offset = 0 q_offset = P @@ -570,7 +571,7 @@ def _single_group(self, X, y, var_names): p, q, r = safe_metric_search(data, self.device, n_vars) - algebra = CliffordAlgebra(p, q, r, device=self.device) + algebra = make_algebra(p, q, r, device=self.device) return VariableGroup( var_indices=indices, var_names=[var_names[i] for i in indices], @@ -612,7 +613,7 @@ def _build_group(self, X, y, indices, var_names): probe_epochs=20, ) - algebra = CliffordAlgebra(p, q, r, device=self.device) + algebra = make_algebra(p, q, r, device=self.device) return VariableGroup( var_indices=indices, var_names=names_sub, @@ -692,7 +693,7 @@ def _reduce_groups(self, groups, target_n=12): new_r = 0 reduction = (p + q + r) - (new_p + new_q + new_r) g.signature = (new_p, new_q, new_r) - g.algebra = CliffordAlgebra(new_p, new_q, new_r, device=self.device) + g.algebra = make_algebra(new_p, new_q, new_r, device=self.device) total -= reduction if reduction == 0: diff --git a/models/sr/implicit.py b/models/sr/implicit.py index a7272f1..05e1725 100644 --- a/models/sr/implicit.py +++ b/models/sr/implicit.py @@ -24,7 +24,7 @@ import torch import torch.nn.functional as F -from core.algebra import CliffordAlgebra +from core.config import make_algebra from models.sr.net import SRGBN from models.sr.utils import safe_sympy_solve from optimizers.riemannian import RiemannianAdam @@ -111,7 +111,7 @@ def probe_best_mode(self, algebra, X, y, geometric_report=None): # Build implicit algebra: add 1 to p for the y variable p, q, r = algebra.p, algebra.q, algebra.r - impl_algebra = CliffordAlgebra(p + 1, q, r, device=self.device) + impl_algebra = make_algebra(p + 1, q, r, device=self.device) implicit_loss = self._probe_implicit(impl_algebra, Z) diff --git a/models/sr/net.py b/models/sr/net.py index a9aa722..8901003 100644 --- a/models/sr/net.py +++ b/models/sr/net.py @@ -17,8 +17,8 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra -from core.module import CliffordModule +from core.foundation.module import CliffordModule +from core.runtime.algebra import CliffordAlgebra from functional.activation import GeometricGELU, GeometricSquare from layers import BladeSelector, CliffordLayerNorm, CliffordLinear, RotorLayer diff --git a/models/sr/phases/extraction.py b/models/sr/phases/extraction.py index ab11032..c062003 100644 --- a/models/sr/phases/extraction.py +++ b/models/sr/phases/extraction.py @@ -18,8 +18,8 @@ import torch import torch.nn.functional as F -from core.algebra import CliffordAlgebra from core.analysis import GeodesicFlow, MetricSearch +from core.config import make_algebra from models.sr.net import SRGBN from models.sr.translator import RotorTerm, RotorTranslator from models.sr.utils import ( @@ -83,7 +83,7 @@ def _process_group_implicit(self, group, group_idx, prep, X_orig, y_orig, X_norm # Build augmented algebra (k+1 variables) p, q, r = group.signature - impl_algebra = CliffordAlgebra(p + 1, q, r, device=self.device) + impl_algebra = make_algebra(p + 1, q, r, device=self.device) # Augmented data Z = [X_group_norm, y_norm] X_group_norm = standardize(torch.tensor(X_orig[:, var_indices], dtype=torch.float32, device=self.device)) diff --git a/models/sr/phases/prep.py b/models/sr/phases/prep.py index b5f5649..d6162ed 100644 --- a/models/sr/phases/prep.py +++ b/models/sr/phases/prep.py @@ -17,7 +17,7 @@ import sympy import torch -from core.algebra import CliffordAlgebra +from core.config import make_algebra from models.sr.translator import RotorTerm from models.sr.utils import ( make_lambdify_fn, @@ -130,7 +130,7 @@ def _single_group_fallback(self, X_orig, y_orig, var_names, Vt): max_p=max(n_vars, 2), ) - algebra = CliffordAlgebra(p, q, r, device=self.device) + algebra = make_algebra(p, q, r, device=self.device) return VariableGroup( var_indices=list(range(n_vars)), var_names=var_names or [f"x{i + 1}" for i in range(n_vars)], diff --git a/models/sr/translator.py b/models/sr/translator.py index 890ced0..8833177 100644 --- a/models/sr/translator.py +++ b/models/sr/translator.py @@ -30,7 +30,7 @@ import sympy import torch -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra from models.sr.utils import LAMBDIFY_MODULES, make_lambdify_fn logger = logging.getLogger(__name__) diff --git a/optimizers/__init__.py b/optimizers/__init__.py index 4524aa9..2eea3d6 100644 --- a/optimizers/__init__.py +++ b/optimizers/__init__.py @@ -12,6 +12,7 @@ RiemannianAdam, exponential_retraction, group_parameters_by_manifold, + make_riemannian_optimizer, project_to_tangent_space, tag_manifold, ) @@ -23,6 +24,7 @@ "exponential_retraction", "tag_manifold", "group_parameters_by_manifold", + "make_riemannian_optimizer", "MANIFOLD_SPIN", "MANIFOLD_SPHERE", "MANIFOLD_EUCLIDEAN", diff --git a/optimizers/riemannian.py b/optimizers/riemannian.py index 474db4a..932ed78 100644 --- a/optimizers/riemannian.py +++ b/optimizers/riemannian.py @@ -24,31 +24,14 @@ import torch.nn as nn from torch.optim import Optimizer -MANIFOLD_SPIN = "spin" -MANIFOLD_SPHERE = "sphere" -MANIFOLD_EUCLIDEAN = "euclidean" - -_VALID_MANIFOLDS = {MANIFOLD_SPIN, MANIFOLD_SPHERE, MANIFOLD_EUCLIDEAN} - - -def tag_manifold(param: nn.Parameter, manifold: str) -> nn.Parameter: - """Tag a parameter with its Riemannian manifold type. - - Layers should call this (or set ``param._manifold`` directly) in their - ``__init__`` so that :meth:`RiemannianAdam.from_model` can auto-group - parameters for correct per-manifold retraction. - - Args: - param: The parameter to tag. - manifold: One of ``'spin'``, ``'sphere'``, ``'euclidean'``. - - Returns: - The same parameter (for chaining). - """ - if manifold not in _VALID_MANIFOLDS: - raise ValueError(f"Unknown manifold '{manifold}'. Must be one of {_VALID_MANIFOLDS}") - param._manifold = manifold - return param +from core.foundation.manifold import ( + MANIFOLD_EUCLIDEAN, + MANIFOLD_ORDER, + MANIFOLD_SPHERE, + MANIFOLD_SPIN, + tag_manifold, + validate_manifold, +) def group_parameters_by_manifold( @@ -71,11 +54,50 @@ def group_parameters_by_manifold( MANIFOLD_EUCLIDEAN: [], } for p in model.parameters(): - manifold = getattr(p, "_manifold", MANIFOLD_EUCLIDEAN) + manifold = validate_manifold(getattr(p, "_manifold", MANIFOLD_EUCLIDEAN)) groups[manifold].append(p) return groups +def _parameter_groups_for_model(model: nn.Module) -> list[dict]: + grouped = group_parameters_by_manifold(model) + param_groups = [] + for manifold in MANIFOLD_ORDER: + params = grouped[manifold] + if params: + param_groups.append({"params": params, "manifold": manifold}) + if not param_groups: + raise ValueError("Model has no parameters") + return param_groups + + +def make_riemannian_optimizer( + model: nn.Module, + algebra, + *, + optimizer: str = "adam", + **kwargs, +) -> Optimizer: + """Create a manifold-aware optimizer from a tagged model. + + Args: + model: Model whose parameters may be tagged with ``_manifold``. + algebra: Clifford algebra instance used by the optimizer. + optimizer: ``"adam"``/``"riemannian_adam"`` or + ``"sgd"``/``"exponential_sgd"``. + **kwargs: Optimizer-specific keyword arguments. + + Returns: + ``RiemannianAdam`` or ``ExponentialSGD`` with per-manifold groups. + """ + key = optimizer.lower().replace("-", "_") + if key in {"adam", "riemannian_adam"}: + return RiemannianAdam.from_model(model, algebra=algebra, **kwargs) + if key in {"sgd", "exponential_sgd"}: + return ExponentialSGD.from_model(model, algebra=algebra, **kwargs) + raise ValueError("optimizer must be one of 'adam', 'riemannian_adam', 'sgd', or 'exponential_sgd'") + + class ExponentialSGD(Optimizer): """SGD with exponential map retraction for rotor parameters. @@ -154,14 +176,7 @@ def from_model( Returns: ExponentialSGD instance with per-manifold parameter groups. """ - grouped = group_parameters_by_manifold(model) - param_groups = [] - for manifold in (MANIFOLD_SPIN, MANIFOLD_SPHERE, MANIFOLD_EUCLIDEAN): - params = grouped[manifold] - if params: - param_groups.append({"params": params, "manifold": manifold}) - if not param_groups: - raise ValueError("Model has no parameters") + param_groups = _parameter_groups_for_model(model) return cls(param_groups, lr=lr, momentum=momentum, algebra=algebra, max_bivector_norm=max_bivector_norm) @torch.no_grad() @@ -294,14 +309,7 @@ def from_model( Returns: RiemannianAdam instance with per-manifold parameter groups. """ - grouped = group_parameters_by_manifold(model) - param_groups = [] - for manifold in (MANIFOLD_SPIN, MANIFOLD_SPHERE, MANIFOLD_EUCLIDEAN): - params = grouped[manifold] - if params: - param_groups.append({"params": params, "manifold": manifold}) - if not param_groups: - raise ValueError("Model has no parameters") + param_groups = _parameter_groups_for_model(model) return cls(param_groups, lr=lr, betas=betas, eps=eps, algebra=algebra, max_bivector_norm=max_bivector_norm) @torch.no_grad() diff --git a/pyproject.toml b/pyproject.toml index 40c6321..97fc84b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ name = "versor" version = "1.0.0" description = "A PyTorch framework for Geometric Algebra Deep Learning" readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.10" license = { file = "LICENSE" } authors = [ { name = "Eunkyum Kim", email = "nemonanconcode@gmail.com" }, @@ -24,7 +24,6 @@ classifiers = [ "Topic :: Scientific/Engineering :: Mathematics", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -34,6 +33,7 @@ dependencies = [ "numpy>=1.26.0", "hydra-core>=1.3.2", "tqdm>=4.67.3", + "setuptools>=80.10.2", ] [project.urls] @@ -77,7 +77,7 @@ all = [ include = ["core*", "layers*", "models*", "datalib*", "optimizers*", "functional*", "tasks*", "examples*", "experiments*"] [tool.ruff] -target-version = "py39" +target-version = "py310" line-length = 120 indent-width = 4 extend-exclude = [ diff --git a/tasks/base.py b/tasks/base.py index 8435e41..9d4c591 100644 --- a/tasks/base.py +++ b/tasks/base.py @@ -13,7 +13,7 @@ from omegaconf import DictConfig from tqdm import tqdm -from core.device import DeviceConfig, resolve_device +from core.foundation.device import DeviceConfig, resolve_device from log import get_logger logger = get_logger(__name__) @@ -50,6 +50,7 @@ def __init__(self, cfg: DictConfig): compile_model=cfg.training.get("compile", False), compile_backend=cfg.training.get("compile_backend", None), amp=cfg.training.get("amp", False), + amp_dtype=cfg.training.get("amp_dtype", None), cudnn_benchmark=cfg.training.get("cudnn_benchmark", None), ) self.device = self.device_config.device @@ -82,7 +83,7 @@ def _resolve_device(device: str) -> str: Priority: cuda > mps > cpu. .. deprecated:: - Use :func:`core.device.resolve_device` instead. + Use :func:`core.foundation.device.resolve_device` instead. """ return resolve_device(device) diff --git a/tasks/deap_eeg.py b/tasks/deap_eeg.py index 2595096..76ff22c 100644 --- a/tasks/deap_eeg.py +++ b/tasks/deap_eeg.py @@ -16,7 +16,7 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra +from core.config import make_algebra_from_config from datalib.deap import get_deap_loaders, get_group_sizes from log import get_logger from models.deap import EEGNet @@ -45,7 +45,8 @@ def __init__(self, cfg): super().__init__(cfg) def setup_algebra(self): - return CliffordAlgebra( + return make_algebra_from_config( + self.cfg.algebra, p=self.cfg.algebra.get("p", 3), q=self.cfg.algebra.get("q", 1), r=self.cfg.algebra.get("r", 0), @@ -64,6 +65,7 @@ def setup_model(self): profiles=profiles, device=self.device, config=self.cfg, + algebra=self.algebra, ) def _compute_profiles(self, group_sizes): diff --git a/tasks/lqa.py b/tasks/lqa.py index 2d049e5..17a4e63 100644 --- a/tasks/lqa.py +++ b/tasks/lqa.py @@ -21,7 +21,7 @@ import torch.nn.functional as F from omegaconf import DictConfig -from core.algebra import CliffordAlgebra +from core.config import make_algebra_from_config from datalib.lqa import get_lqa_loaders from functional.loss import AsymmetryLoss, InvolutionConsistencyLoss from log import get_logger @@ -57,7 +57,7 @@ def setup_algebra(self): p = self.cfg.algebra.get("p", 4) q = self.cfg.algebra.get("q", 1) r = self.cfg.algebra.get("r", 0) - return CliffordAlgebra(p, q, r, device=self.device) + return make_algebra_from_config(self.cfg.algebra, p=p, q=q, r=r, device=self.device) def setup_model(self): """GLRNet with probe-specific head.""" diff --git a/tasks/md17.py b/tasks/md17.py index 4b78566..7184a17 100644 --- a/tasks/md17.py +++ b/tasks/md17.py @@ -8,8 +8,8 @@ import torch import torch.nn as nn -from core.algebra import CliffordAlgebra -from core.metric import hermitian_grade_spectrum, hermitian_norm +from core.config import make_algebra_from_config +from core.runtime.metric import hermitian_grade_spectrum, hermitian_norm from datalib.md17 import get_md17_loaders from functional.loss import ConservativeLoss, HermitianGradeRegularization from log import get_logger @@ -49,8 +49,13 @@ def __init__(self, cfg): def setup_algebra(self): """Use Cl(3,0,1) PGA for SE(3) rigid-body motions.""" - exp_policy = self.cfg.model.get("exp_policy", "balanced") - return CliffordAlgebra(p=3, q=0, r=self.cfg.algebra.get("r", 1), device=self.device, exp_policy=exp_policy) + return make_algebra_from_config( + self.cfg.algebra, + p=3, + q=0, + r=self.cfg.algebra.get("r", 1), + device=self.device, + ) def setup_model(self): """Build MD17ForceNet model with PGA motors, dynamic rotors, and RBF.""" diff --git a/tasks/symbolic_regression.py b/tasks/symbolic_regression.py index 6156402..a7c0f9c 100644 --- a/tasks/symbolic_regression.py +++ b/tasks/symbolic_regression.py @@ -20,7 +20,8 @@ import torch.optim as optim from omegaconf import DictConfig -from core.algebra import CliffordAlgebra +from core.config import make_algebra_from_config +from core.foundation.module import AlgebraLike from datalib.symbolic_regression import _fetch_pmlb_data, get_dataset_ids, get_sr_loaders, get_sr_raw_splits from log import get_logger from models.sr import SRGBN @@ -47,7 +48,7 @@ class SRTask(BaseTask): model.hidden_channels : channel count C model.num_layers : residual block count model.num_rotors : K rotors per MultiRotorLayer - model.exp_policy : exp policy ('balanced', 'precise') + algebra.exp_policy : exp policy ('balanced', 'precise') iterative.max_stages : maximum unbending iterations iterative.stage_epochs : epochs per stage iterative.r2_target : R2 threshold to stop @@ -71,13 +72,11 @@ def __init__(self, cfg: DictConfig): # Optional automatic signature discovery self._searched_signature = None - if cfg.algebra.get("auto", False): + if cfg.algebra.get("metric_search", False): self._searched_signature = self._run_metric_search(cfg) # Iterative unbending config self.iterative_cfg = dict(cfg.get("iterative", {})) - # Remove 'enabled' key if present (legacy compat) - self.iterative_cfg.pop("enabled", None) # Merge new pipeline config sections self.iterative_cfg.update( { @@ -97,13 +96,13 @@ def __init__(self, cfg: DictConfig): super().__init__(cfg) - # Override scheduler patience (BaseTask defaults to 3) - sched_patience = cfg.training.get("scheduler_patience", 10) + # Override scheduler after SR-specific setup. + sched_cfg = cfg.training.get("scheduler", {}) self.scheduler = optim.lr_scheduler.ReduceLROnPlateau( self.optimizer, mode="min", - factor=0.5, - patience=sched_patience, + factor=sched_cfg.get("factor", 0.5), + patience=sched_cfg.get("patience", 10), ) def _probe_n_vars(self, cfg): @@ -155,7 +154,7 @@ def _run_metric_search(self, cfg): logger.info(f"MetricSearch: Cl({p},{q},{r}) for {self.dataset_name}") return (p, q, r) - def setup_algebra(self) -> CliffordAlgebra: + def setup_algebra(self) -> AlgebraLike: """Use searched signature or configured Cl(p,q,r).""" if self._searched_signature is not None: p, q, r = self._searched_signature @@ -163,8 +162,13 @@ def setup_algebra(self) -> CliffordAlgebra: p = self.cfg.algebra.p q = self.cfg.algebra.get("q", 0) r = self.cfg.algebra.get("r", 0) - exp_policy = self.cfg.model.get("exp_policy", "balanced") - return CliffordAlgebra(p=p, q=q, r=r, device=self.device, exp_policy=exp_policy) + return make_algebra_from_config( + self.cfg.algebra, + p=p, + q=q, + r=r, + device=self.device, + ) def setup_model(self) -> SRGBN: """Build SRGBN with config parameters, optionally auto-sizing.""" diff --git a/tests/conftest.py b/tests/conftest.py index 581da86..4642036 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,6 @@ import pytest -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra DEVICE = "cpu" @@ -35,7 +35,6 @@ def algebra_minkowski(): def algebra_conformal(): return CliffordAlgebra(p=4, q=1, device=DEVICE) - # -- Module-scoped (used by test_geodesic.py - exact name match) ---------- @pytest.fixture(scope="module") def alg2(): diff --git a/tests/test_analysis.py b/tests/test_analysis.py index 9330b04..6e29e63 100644 --- a/tests/test_analysis.py +++ b/tests/test_analysis.py @@ -16,7 +16,6 @@ import pytest import torch -from core.algebra import CliffordAlgebra from core.analysis import ( AnalysisConfig, AnalysisReport, @@ -33,6 +32,7 @@ SymmetryResult, compute_uncertainty_and_alignment, ) +from core.runtime.algebra import CliffordAlgebra DEVICE = "cpu" diff --git a/tests/test_attention.py b/tests/test_attention.py new file mode 100644 index 0000000..d3138ef --- /dev/null +++ b/tests/test_attention.py @@ -0,0 +1,74 @@ +import math + +import pytest +import torch + +from core.runtime.algebra import CliffordAlgebra +from layers.blocks.attention import GeometricProductAttention + +pytestmark = pytest.mark.unit + +DEVICE = "cpu" + + +def _reference_attention_score(algebra, q_head, k_head, bivector_weight): + product = algebra.geometric_product(q_head.unsqueeze(3), algebra.reverse(k_head).unsqueeze(2)) + score_g0 = product[..., 0].sum(-1) + + g2_idx = algebra.grade_masks[2].nonzero(as_tuple=False).squeeze(-1) + if g2_idx.numel() > 0: + g2 = torch.index_select(product, -1, g2_idx) + score_g2 = g2.pow(2).sum(dim=(-1, -2)).sqrt() + else: + score_g2 = torch.zeros_like(score_g0) + + scale = math.sqrt(q_head.shape[3] * algebra.dim) + return (score_g0 + bivector_weight * score_g2) / scale + + +def test_attention_dense_chunked_score_matches_direct_product(): + algebra = CliffordAlgebra(3, 0, 0, device=DEVICE, dtype=torch.float64) + attn = GeometricProductAttention( + algebra, + channels=4, + num_heads=2, + causal=False, + bivector_weight=0.25, + score_blade_chunk_size=1, + score_precompute_limit=0, + ) + q_head = torch.randn(2, 2, 3, 2, algebra.dim, dtype=torch.float64) + k_head = torch.randn(2, 2, 4, 2, algebra.dim, dtype=torch.float64) + + actual = attn._compute_score(q_head, k_head) + expected = _reference_attention_score(algebra, q_head, k_head, attn.bivector_weight) + + assert not hasattr(attn, "_g2_b_idx") + assert torch.allclose(actual, expected, atol=1e-12, rtol=1e-12) + + +def test_attention_forward_shape_after_score_refactor(): + algebra = CliffordAlgebra(3, 0, 0, device=DEVICE, dtype=torch.float32) + attn = GeometricProductAttention(algebra, channels=4, num_heads=2, causal=False) + x = torch.randn(2, 5, 4, algebra.dim) + + y = attn(x) + + assert y.shape == x.shape + + +@pytest.mark.skipif(not hasattr(torch, "compile"), reason="torch.compile not available") +def test_attention_dense_score_compiles_fullgraph(): + algebra = CliffordAlgebra(4, 0, 0, device=DEVICE, dtype=torch.float32) + attn = GeometricProductAttention(algebra, channels=4, num_heads=2, causal=False) + q_head = torch.randn(1, 2, 3, 2, algebra.dim) + k_head = torch.randn(1, 2, 4, 2, algebra.dim) + + def score(q, k): + return attn._compute_score(q, k) + + expected = score(q_head, k_head) + compiled = torch.compile(score, backend="aot_eager", fullgraph=True) + actual = compiled(q_head, k_head) + + assert torch.allclose(actual, expected, atol=1e-6, rtol=1e-6) diff --git a/tests/test_core.py b/tests/test_core.py index 2a51445..9296222 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -10,7 +10,7 @@ import pytest import torch -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra pytestmark = pytest.mark.unit diff --git a/tests/test_decomposition.py b/tests/test_decomposition.py index c423e48..1e2225c 100644 --- a/tests/test_decomposition.py +++ b/tests/test_decomposition.py @@ -15,10 +15,10 @@ import pytest import torch -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra pytestmark = pytest.mark.unit -from core.decomposition import ( +from core.runtime.decomposition import ( ExpPolicy, _power_iteration_compiled_safe, compiled_safe_decomposed_exp, diff --git a/tests/test_degenerate_algebra.py b/tests/test_degenerate_algebra.py index 608473f..8e8d7d7 100644 --- a/tests/test_degenerate_algebra.py +++ b/tests/test_degenerate_algebra.py @@ -17,7 +17,7 @@ pytestmark = pytest.mark.unit -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra DEVICE = "cpu" @@ -234,7 +234,7 @@ def test_non_simple_bivector_n4_decomposed_inference(self): B[0, bv_indices[0].item()] = 0.3 # e12 B[0, bv_indices[5].item()] = 0.4 # e34 - from core.decomposition import ExpPolicy + from core.runtime.decomposition import ExpPolicy alg.exp_policy = ExpPolicy.PRECISE with torch.no_grad(): diff --git a/tests/test_exp_signatures.py b/tests/test_exp_signatures.py index 73a59e4..bbf45aa 100644 --- a/tests/test_exp_signatures.py +++ b/tests/test_exp_signatures.py @@ -11,7 +11,7 @@ import pytest import torch -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra pytestmark = pytest.mark.unit @@ -443,7 +443,7 @@ class TestExpDecomposedGradient: def test_decomposed_gradient_cl40(self): """EXACT policy should produce finite gradients in Cl(4,0).""" - from core.decomposition import ExpPolicy + from core.runtime.decomposition import ExpPolicy alg = CliffordAlgebra(4, 0, device=DEVICE) alg.exp_policy = ExpPolicy.PRECISE @@ -461,7 +461,7 @@ def test_decomposed_gradient_cl40(self): def test_decomposed_gradient_cl15(self): """EXACT policy should produce finite gradients in Cl(1,5).""" - from core.decomposition import ExpPolicy + from core.runtime.decomposition import ExpPolicy alg = CliffordAlgebra(1, 5, device=DEVICE) alg.exp_policy = ExpPolicy.PRECISE @@ -481,7 +481,7 @@ def test_decomposed_gradient_cl15(self): def test_decomposed_matches_inference(self): """EXACT exp with grad should approximate inference result.""" - from core.decomposition import ExpPolicy + from core.runtime.decomposition import ExpPolicy alg = CliffordAlgebra(4, 0, device=DEVICE) alg.exp_policy = ExpPolicy.PRECISE @@ -596,7 +596,7 @@ class TestDecompositionConvergence: def test_simple_bivector_converges_fast(self): """A simple bivector should decompose into 1 component.""" - from core.decomposition import differentiable_invariant_decomposition + from core.runtime.decomposition import differentiable_invariant_decomposition alg = CliffordAlgebra(4, 0, device=DEVICE) bv_mask = alg.grade_masks[2] @@ -615,7 +615,7 @@ def test_simple_bivector_converges_fast(self): def test_non_simple_needs_two_components(self): """e12 + e34 in Cl(4,0) should decompose into 2 components.""" - from core.decomposition import differentiable_invariant_decomposition + from core.runtime.decomposition import differentiable_invariant_decomposition alg = CliffordAlgebra(4, 0, device=DEVICE) bv_mask = alg.grade_masks[2] @@ -633,7 +633,7 @@ def test_non_simple_needs_two_components(self): def test_residual_check_limits_components(self): """With tight threshold, simple bivector should yield exactly 1 component.""" - from core.decomposition import differentiable_invariant_decomposition + from core.runtime.decomposition import differentiable_invariant_decomposition alg = CliffordAlgebra(4, 0, device=DEVICE) bv_mask = alg.grade_masks[2] diff --git a/tests/test_extensions.py b/tests/test_extensions.py index e3a6fd5..9803ef2 100644 --- a/tests/test_extensions.py +++ b/tests/test_extensions.py @@ -8,7 +8,7 @@ import pytest import torch -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra from layers import CliffordGraphConv from layers.adapters.conformal import ConformalEmbedding from layers.adapters.projective import ProjectiveEmbedding diff --git a/tests/test_framework_pipeline.py b/tests/test_framework_pipeline.py new file mode 100644 index 0000000..a566a98 --- /dev/null +++ b/tests/test_framework_pipeline.py @@ -0,0 +1,140 @@ +import pytest +import torch +import torch.nn as nn + +from core.planning import PlanningLimits +from core.runtime.algebra import CliffordAlgebra +from core.runtime.context import AlgebraContext +from layers import ProductLayer, WedgeLayer +from optimizers import make_riemannian_optimizer + +pytestmark = pytest.mark.unit + + +def test_product_layer_dense_matches_algebra(algebra_3d): + left = torch.randn(4, 5, algebra_3d.dim) + right = torch.randn(4, 5, algebra_3d.dim) + layer = ProductLayer(algebra_3d) + + actual = layer(left, right) + expected = algebra_3d.geometric_product(left, right) + + assert torch.allclose(actual, expected) + + +def test_wedge_layer_declared_grades_match_planned_algebra(algebra_3d): + left = algebra_3d.embed_vector(torch.randn(4, 5, algebra_3d.n)) + right = algebra_3d.embed_vector(torch.randn(4, 5, algebra_3d.n)) + layer = WedgeLayer( + algebra_3d, + left_grades=(1,), + right_grades=(1,), + output_grades=(2,), + ) + + actual = layer(left, right) + expected = algebra_3d.wedge(left, right, left_grades=(1,), right_grades=(1,), output_grades=(2,)) + + assert torch.allclose(actual, expected) + + +def test_product_layer_pairwise_compact_widths_match_dense_reference(): + context = AlgebraContext(p=5, q=0, device="cpu") + dense = CliffordAlgebra(p=5, q=0, device="cpu") + left_layout = context.layout((2,)) + right_layout = context.layout((1,)) + output_layout = context.layout((3,)) + + left = torch.randn(2, 3, left_layout.dim) + right = torch.randn(2, 4, right_layout.dim) + layer = WedgeLayer( + context, + left_grades=(2,), + right_grades=(1,), + output_grades=(3,), + left_compact=True, + right_compact=True, + compact_output=True, + pairwise=True, + ) + + actual = layer(left, right) + expected_dense = dense.wedge( + left_layout.dense(left).unsqueeze(2), + right_layout.dense(right).unsqueeze(1), + left_grades=(2,), + right_grades=(1,), + output_grades=(3,), + ) + expected = output_layout.compact(expected_dense) + + assert actual.shape == (2, 3, 4, output_layout.dim) + assert torch.allclose(actual, expected) + + +def test_compact_layer_pipeline_trains_with_riemannian_optimizer_factory(): + context = AlgebraContext(p=6, q=0, device="cpu") + vector_layout = context.layout((1,)) + trivector_layout = context.layout((3,)) + + class CompactPipeline(nn.Module): + def __init__(self): + super().__init__() + self.wedge_vectors = WedgeLayer( + context, + left_grades=(1,), + right_grades=(1,), + output_grades=(2,), + compact_output=True, + ) + self.wedge_trivector = WedgeLayer( + context, + left_grades=(2,), + right_grades=(1,), + output_grades=(3,), + left_compact=True, + right_compact=True, + compact_output=True, + ) + self.scale = nn.Parameter(torch.ones(())) + + def forward(self, left_vector, right_vector, third_vector): + bivector = self.wedge_vectors(left_vector, right_vector) + return self.scale * self.wedge_trivector(bivector, third_vector) + + dense_left = context.embed_vector(torch.randn(8, context.n)) + dense_right = context.embed_vector(torch.randn(8, context.n)) + compact_third = vector_layout.compact(context.embed_vector(torch.randn(8, context.n))) + model = CompactPipeline() + optimizer = make_riemannian_optimizer(model, context, optimizer="adam", lr=0.01) + + output = model(dense_left, dense_right, compact_third) + assert output.shape == (8, trivector_layout.dim) + assert len(context.planner._product_executors) == 2 + + loss = output.square().mean() + loss.backward() + optimizer.step() + + assert model.scale.grad is not None + assert torch.isfinite(model.scale).all() + + +def test_product_layer_uses_context_planning_limits(): + limits = PlanningLimits(warn_lanes=32, max_lanes=512, warn_pairs=32, max_pairs=64) + context = AlgebraContext(p=16, q=0, device="cpu", planning_limits=limits) + vector_layout = context.layout((1,)) + left = torch.zeros(1, vector_layout.dim) + right = torch.zeros(1, vector_layout.dim) + layer = ProductLayer( + context, + left_grades=(1,), + right_grades=(1,), + output_grades=(0, 2), + left_compact=True, + right_compact=True, + compact_output=True, + ) + + with pytest.raises(ValueError, match="basis interactions"): + layer(left, right) diff --git a/tests/test_functional_products.py b/tests/test_functional_products.py new file mode 100644 index 0000000..e2e5dbb --- /dev/null +++ b/tests/test_functional_products.py @@ -0,0 +1,60 @@ +import pytest +import torch + +from functional import ( + anti_commutator, + commutator, + geometric_product, + grade_projection, + inner_product, + product, + reverse, + wedge, +) + +pytestmark = pytest.mark.unit + + +def test_functional_products_match_algebra_dense(algebra_3d): + left = torch.randn(3, algebra_3d.dim) + right = torch.randn(3, algebra_3d.dim) + + assert torch.allclose(geometric_product(algebra_3d, left, right), algebra_3d.geometric_product(left, right)) + assert torch.allclose(wedge(algebra_3d, left, right), algebra_3d.wedge(left, right)) + assert torch.allclose(inner_product(algebra_3d, left, right), algebra_3d.inner_product(left, right)) + assert torch.allclose(commutator(algebra_3d, left, right), algebra_3d.commutator(left, right)) + assert torch.allclose(anti_commutator(algebra_3d, left, right), algebra_3d.anti_commutator(left, right)) + + +def test_functional_projected_product_compact_output(algebra_3d): + left = algebra_3d.embed_vector(torch.randn(4, algebra_3d.n)) + right = algebra_3d.embed_vector(torch.randn(4, algebra_3d.n)) + layout = algebra_3d.layout((2,)) + + actual = wedge( + algebra_3d, + left, + right, + left_grades=(1,), + right_grades=(1,), + output_grades=(2,), + compact_output=True, + ) + expected = layout.compact(algebra_3d.wedge(left, right)) + + assert actual.shape == (4, layout.dim) + assert torch.allclose(actual, expected) + + +def test_functional_unary_helpers(algebra_3d): + values = torch.randn(2, algebra_3d.dim) + + assert torch.allclose(reverse(algebra_3d, values), algebra_3d.reverse(values)) + assert torch.allclose(grade_projection(algebra_3d, values, 1), algebra_3d.grade_projection(values, 1)) + + +def test_functional_product_rejects_unknown_op(algebra_3d): + values = torch.randn(2, algebra_3d.dim) + + with pytest.raises(ValueError, match="Unsupported product op"): + product(algebra_3d, values, values, op="unknown") diff --git a/tests/test_geodesic.py b/tests/test_geodesic.py index d76df9b..b726e31 100644 --- a/tests/test_geodesic.py +++ b/tests/test_geodesic.py @@ -12,8 +12,8 @@ import pytest import torch -from core.algebra import CliffordAlgebra from core.analysis import DimensionLifter, GeodesicFlow, MetricSearch +from core.runtime.algebra import CliffordAlgebra pytestmark = pytest.mark.unit diff --git a/tests/test_grade_plan.py b/tests/test_grade_plan.py new file mode 100644 index 0000000..cd4d801 --- /dev/null +++ b/tests/test_grade_plan.py @@ -0,0 +1,1058 @@ +import pytest +import torch + +from core.config import make_algebra +from core.foundation.basis import ( + basis_count_for_grades, + basis_index_tuple_for_grades, + basis_indices_for_grades, + expand_output_grades, + geometric_product_output_grades, + operation_coefficient, + product_output_grades, +) +from core.foundation.layout import AlgebraSpec +from core.planning.flow import GradeFlow +from core.planning.layouts import build_product_request +from core.planning.planner import GradePlanner +from core.planning.policy import PlanningLimits +from core.planning.product import ( + GradeProductExecutor, + build_grade_product_plan, +) +from core.planning.tree import build_grade_plan_tree +from core.planning.unary import build_unary_request +from core.runtime.algebra import CliffordAlgebra +from core.runtime.context import AlgebraContext +from core.runtime.multivector import Multivector + +pytestmark = pytest.mark.unit + +DEVICE = "cpu" + + +def _project_to_grades(algebra, mv: torch.Tensor, grades: tuple[int, ...]) -> torch.Tensor: + result = torch.zeros_like(mv) + for grade in grades: + result = result + algebra.grade_projection(mv, grade) + return result + + +def _grade_only_input(algebra, batch: int, grades: tuple[int, ...], seed: int) -> torch.Tensor: + generator = torch.Generator(device=DEVICE).manual_seed(seed) + mv = torch.zeros(batch, algebra.dim, dtype=torch.float64) + indices = basis_indices_for_grades(algebra.n, grades, device=DEVICE) + mv[:, indices] = torch.randn(batch, indices.numel(), dtype=torch.float64, generator=generator) * 0.1 + return mv + + +def test_grade_expansion_for_common_high_dim_paths(): + assert geometric_product_output_grades(1, 1, 16) == (0, 2) + assert geometric_product_output_grades(2, 1, 16) == (1, 3) + assert product_output_grades(2, 1, 16, op="wedge") == (3,) + assert product_output_grades(2, 1, 16, op="commutator") == (1,) + assert product_output_grades(2, 1, 16, op="anti_commutator") == (3,) + assert expand_output_grades((0, 2), (1,), 16, op="gp") == (1, 3) + assert expand_output_grades((1,), (1,), 16, op="wedge") == (2,) + assert expand_output_grades((1,), (1,), 16, op="gp", project_grades=(0,)) == (0,) + + +def test_operation_coefficients_keep_wedge_as_exterior_product(): + # e12 and e3 commute, so the antisymmetric formula would vanish. The + # exterior product is instead the grade-sum part of the geometric product. + assert operation_coefficient(3, 4, 3, 0, 0, "wedge") == 1.0 + assert operation_coefficient(3, 4, 3, 0, 0, "commutator") == 0.0 + assert operation_coefficient(3, 4, 3, 0, 0, "anti_commutator") == 2.0 + + +def test_basis_indices_for_grades_are_combinatorial_and_high_dimensional(): + assert basis_index_tuple_for_grades(4, (1, 2)) == tuple( + index for index in range(1 << 4) if index.bit_count() in {1, 2} + ) + high = basis_index_tuple_for_grades(32, (1, 2)) + assert len(high) == basis_count_for_grades(32, (1, 2)) + assert high[0] == 1 + assert high[-1] == (1 << 31) | (1 << 30) + + +def test_basis_tensorization_reports_int64_bitmask_boundary(): + with pytest.raises(ValueError, match="torch.long basis bitmasks"): + basis_indices_for_grades(64, (1,)) + + +def test_grade_plan_tree_groups_routes_without_runtime_partition_backend(): + spec = AlgebraSpec(10, 4, 2) + tree = build_grade_plan_tree( + spec, + left_grades=(1, 2), + right_grades=(1,), + output_grades=(0, 2), + op="gp", + chunk_pair_limit=128, + ) + + assert tree.output_grades == (0, 2) + assert [(path.left_grade, path.right_grade, path.output_grades) for path in tree.paths] == [ + (1, 1, (0, 2)), + ] + assert tree.path_count == 1 + assert tree.estimated_pairs == 16 * 16 + assert tree.estimated_chunks == 2 + assert tree.path_for_grades(1, 1) is tree.paths[0] + assert tree.path_for_grades(2, 1) is None + + +def test_product_request_infers_declared_layouts_and_output_grades(): + spec = AlgebraSpec(10, 4, 2) + left = torch.zeros(2, spec.dim) + right = torch.zeros(2, spec.dim) + + request = build_product_request( + spec, + left, + right, + left_grades=(1,), + right_grades=(1,), + op="gp", + full_layout_allowed=False, + ) + + assert request.left_grades == (1,) + assert request.right_grades == (1,) + assert request.output_grades == (0, 2) + assert not request.left_compact + assert not request.right_compact + + +def test_product_request_detects_compact_tensors_from_layout_shape(): + spec = AlgebraSpec(6, 0, 0) + layout = spec.layout((1,)) + left = torch.zeros(2, layout.dim) + right = torch.zeros(2, layout.dim) + + request = build_product_request( + spec, + left, + right, + left_layout=layout, + right_layout=layout, + output_grades=(0, 2), + op="gp", + ) + + assert request.left_compact + assert request.right_compact + + +def test_unary_request_infers_projection_layout_without_full_layout(): + spec = AlgebraSpec(10, 4, 2) + values = torch.zeros(2, spec.dim) + + request = build_unary_request( + spec, + values, + op="grade_projection", + output_grades=(1,), + full_layout_allowed=False, + ) + + assert request.input_grades == (1,) + assert request.output_grades == (1,) + assert not request.input_compact + + +def test_grade_flow_propagates_embedding_unary_product_and_merge(): + spec = AlgebraSpec(8, 0, 0) + vector = GradeFlow.vector(spec) + scalar = GradeFlow.scalar(spec) + + product = vector.product(vector) + projected = product.project((2,)) + merged = scalar.merge(projected) + + assert vector.grades == (1,) + assert vector.unary("reverse").grades == (1,) + assert product.grades == (0, 2) + assert projected.grades == (2,) + assert merged.grades == (0, 2) + + +def test_grade_layout_compact_dense_round_trip(): + spec = AlgebraSpec(4, 1, 1) + layout = spec.layout((0, 2)) + dense = torch.randn(2, spec.dim, dtype=torch.float64, generator=torch.Generator().manual_seed(97)) + + values = layout.compact(dense) + materialized = layout.dense(values) + + assert values.shape[-1] == layout.dim + assert torch.allclose(materialized[..., layout.indices_tensor(device=dense.device)], values) + outside = torch.ones(spec.dim, dtype=torch.bool) + outside[layout.indices_tensor()] = False + assert materialized[..., outside].abs().sum().item() == 0.0 + + +@pytest.mark.parametrize("op", ["gp", "wedge", "inner", "commutator", "anti_commutator"]) +def test_static_grade_product_matches_dense_kernel_for_selected_grade_paths(op): + algebra = CliffordAlgebra(4, 1, 1, device=DEVICE, dtype=torch.float64) + left_grades = (1,) + right_grades = (1, 2) + output_grades = expand_output_grades(left_grades, right_grades, algebra.n, op=op) + plan = build_grade_product_plan( + algebra.p, + algebra.q, + algebra.r, + left_grades=left_grades, + right_grades=right_grades, + output_grades=output_grades, + op=op, + device=DEVICE, + dtype=torch.float64, + ) + product = GradeProductExecutor(plan) + A = _grade_only_input(algebra, 3, left_grades, seed=101) + B = _grade_only_input(algebra, 3, right_grades, seed=103) + + expected = _project_to_grades(algebra, getattr(algebra, _dense_method_name(op))(A, B), output_grades) + actual = product.forward_dense(A, B) + + assert product.pair_count < algebra.dim * algebra.dim + assert actual.shape == expected.shape + assert torch.allclose(actual, expected, atol=1e-12, rtol=1e-12) + + +def test_wedge_dense_and_planned_paths_are_exterior_product_for_higher_grades(): + algebra = CliffordAlgebra(3, 0, 0, device=DEVICE, dtype=torch.float64) + spec = AlgebraSpec.from_algebra(algebra) + layout_2 = spec.layout((2,)) + layout_1 = spec.layout((1,)) + layout_3 = spec.layout((3,)) + + e12 = torch.zeros(1, algebra.dim, dtype=torch.float64) + e12[0, 3] = 1.0 + e3 = torch.zeros(1, algebra.dim, dtype=torch.float64) + e3[0, 4] = 1.0 + expected = torch.zeros_like(e12) + expected[0, 7] = 1.0 + + dense = algebra.wedge(e12, e3) + compact = algebra.wedge( + layout_2.compact(e12), + layout_1.compact(e3), + left_layout=layout_2, + right_layout=layout_1, + output_grades=(3,), + left_compact=True, + right_compact=True, + compact_output=True, + ) + + assert torch.allclose(dense, expected, atol=1e-12, rtol=1e-12) + assert torch.allclose(compact, layout_3.compact(expected), atol=1e-12, rtol=1e-12) + + +def test_wedge_chains_as_iterative_exterior_product(): + algebra = CliffordAlgebra(4, 0, 0, device=DEVICE, dtype=torch.float64) + e1 = torch.zeros(1, algebra.dim, dtype=torch.float64) + e2 = torch.zeros(1, algebra.dim, dtype=torch.float64) + e3 = torch.zeros(1, algebra.dim, dtype=torch.float64) + e1[0, 1] = 1.0 + e2[0, 2] = 1.0 + e3[0, 4] = 1.0 + expected = torch.zeros_like(e1) + expected[0, 7] = 1.0 + + e12 = algebra.wedge(e1, e2, left_grades=(1,), right_grades=(1,), output_grades=(2,)) + actual = algebra.wedge(e12, e3, left_grades=(2,), right_grades=(1,), output_grades=(3,)) + + assert torch.allclose(actual, expected, atol=1e-12, rtol=1e-12) + + +def test_wedge_plan_prunes_grade_route_pairs_before_coefficients(): + algebra = CliffordAlgebra(6, 0, 0, device=DEVICE, dtype=torch.float64) + broad = build_grade_product_plan( + algebra.p, + algebra.q, + algebra.r, + left_grades=(2,), + right_grades=(1,), + output_grades=(1, 3), + op="wedge", + device=DEVICE, + dtype=torch.float64, + ) + exterior_only = build_grade_product_plan( + algebra.p, + algebra.q, + algebra.r, + left_grades=(2,), + right_grades=(1,), + output_grades=(3,), + op="wedge", + device=DEVICE, + dtype=torch.float64, + ) + + assert broad.pair_count == exterior_only.pair_count + assert all(int(index).bit_count() == 3 for index in broad.output_indices.tolist()) + + +def test_product_plan_owns_compact_position_buffers(): + algebra = CliffordAlgebra(4, 1, 0, device=DEVICE, dtype=torch.float64) + plan = build_grade_product_plan( + algebra.p, + algebra.q, + algebra.r, + left_grades=(1,), + right_grades=(1,), + output_grades=(0, 2), + op="gp", + device=DEVICE, + dtype=torch.float64, + ) + product = GradeProductExecutor(plan) + A = _grade_only_input(algebra, 2, (1,), seed=109) + B = _grade_only_input(algebra, 2, (1,), seed=111) + + left_positions = {index: position for position, index in enumerate(plan.left_layout.basis_indices)} + right_positions = {index: position for position, index in enumerate(plan.right_layout.basis_indices)} + expected_left = torch.tensor([left_positions[int(index)] for index in plan.left_indices], dtype=torch.long) + expected_right = torch.tensor([right_positions[int(index)] for index in plan.right_indices], dtype=torch.long) + + assert torch.equal(plan.left_compact_positions.cpu(), expected_left) + assert torch.equal(plan.right_compact_positions.cpu(), expected_right) + assert torch.allclose( + product.forward_compact(plan.left_layout.compact(A), plan.right_layout.compact(B)), + product(A, B), + atol=1e-12, + rtol=1e-12, + ) + + +def test_product_executor_compact_forward_supports_different_layout_widths(): + algebra = CliffordAlgebra(4, 1, 0, device=DEVICE, dtype=torch.float64) + plan = build_grade_product_plan( + algebra.p, + algebra.q, + algebra.r, + left_grades=(1,), + right_grades=(1, 2), + output_grades=(0, 1, 2, 3), + op="gp", + device=DEVICE, + dtype=torch.float64, + ) + product = GradeProductExecutor(plan) + A = _grade_only_input(algebra, 2, (1,), seed=115) + B = _grade_only_input(algebra, 2, (1, 2), seed=117) + + compact = product.forward_compact(plan.left_layout.compact(A), plan.right_layout.compact(B)) + dense = product(A, B) + + assert plan.left_layout.dim != plan.right_layout.dim + assert torch.allclose(compact, dense, atol=1e-12, rtol=1e-12) + + +def test_algebra_projected_product_matches_dense_kernel_and_compact_output(): + algebra = CliffordAlgebra(4, 1, 1, device=DEVICE, dtype=torch.float64) + A = _grade_only_input(algebra, 2, (1,), seed=113) + B = _grade_only_input(algebra, 2, (1,), seed=127) + + dense_expected = _project_to_grades(algebra, algebra.geometric_product(A, B), (0, 2)) + dense_actual = algebra.projected_geometric_product( + A, + B, + left_grades=(1,), + right_grades=(1,), + output_grades=(0, 2), + ) + compact_actual = algebra.projected_geometric_product( + A, + B, + left_grades=(1,), + right_grades=(1,), + output_grades=(0, 2), + compact_output=True, + ) + + assert torch.allclose(dense_actual, dense_expected, atol=1e-12, rtol=1e-12) + assert compact_actual.shape[-1] == AlgebraSpec.from_algebra(algebra).layout((0, 2)).dim + + +def test_grade_planner_reuses_projected_product_executor(): + algebra = CliffordAlgebra(4, 1, 1, device=DEVICE, dtype=torch.float64) + planner = GradePlanner(algebra) + + first = planner.product_executor( + op="gp", + left_grades=(1,), + right_grades=(1,), + output_grades=(0, 2), + dtype=torch.float64, + device=DEVICE, + ) + second = planner.product_executor( + op="gp", + left_grades=(1,), + right_grades=(1,), + output_grades=(0, 2), + dtype=torch.float64, + device=DEVICE, + ) + + assert first is second + + +def test_algebra_product_executor_returns_preplanned_runtime_handle(): + algebra = AlgebraContext(6, 0, device=DEVICE, dtype=torch.float32) + + first = algebra.product_executor( + left_grades=(1,), + right_grades=(1,), + output_grades=(0, 2), + ) + second = algebra.product_executor( + left_grades=(1,), + right_grades=(1,), + output_grades=(0, 2), + ) + + assert first is second + assert first.left_grades == (1,) + assert first.right_grades == (1,) + assert first.output_grades == (0, 2) + assert first.coefficients.dtype == algebra.dtype + + +def test_grade_planner_rekeys_cached_executor_after_dtype_move(): + algebra = CliffordAlgebra(4, 1, 1, device=DEVICE, dtype=torch.float64) + executor = algebra.planner.product_executor( + op="gp", + left_grades=(1,), + right_grades=(1,), + output_grades=(0, 2), + dtype=algebra.dtype, + device=DEVICE, + ) + + algebra.to(dtype=torch.float32) + moved = algebra.planner.product_executor( + op="gp", + left_grades=(1,), + right_grades=(1,), + output_grades=(0, 2), + dtype=algebra.dtype, + device=DEVICE, + ) + + assert moved is executor + assert moved.coefficients.dtype == torch.float32 + + +def test_multivector_compact_projected_product_keeps_dense_tensor_compatibility(): + algebra = CliffordAlgebra(4, 1, 1, device=DEVICE, dtype=torch.float64) + A = Multivector(algebra, _grade_only_input(algebra, 2, (1,), seed=131)).compact((1,)) + B = Multivector(algebra, _grade_only_input(algebra, 2, (1,), seed=137)).compact((1,)) + + result = A.projected_product(B, output_grades=(0, 2)) + expected = _project_to_grades(algebra, algebra.geometric_product(A.tensor, B.tensor), (0, 2)) + + assert result.is_compact + assert result.values.shape[-1] == result.layout.dim + assert result.tensor.shape[-1] == algebra.dim + assert torch.allclose(result.tensor, expected, atol=1e-12, rtol=1e-12) + + +def test_multivector_compact_projected_product_supports_mixed_dense_operand(): + algebra = CliffordAlgebra(4, 1, 1, device=DEVICE, dtype=torch.float64) + A = Multivector(algebra, _grade_only_input(algebra, 2, (1,), seed=139)).compact((1,)) + B = Multivector(algebra, _grade_only_input(algebra, 2, (1,), seed=149)) + + result = A.projected_product(B, output_grades=(0, 2), right_grades=(1,)) + expected = _project_to_grades(algebra, algebra.geometric_product(A.tensor, B.tensor), (0, 2)) + + assert result.is_compact + assert torch.allclose(result.tensor, expected, atol=1e-12, rtol=1e-12) + + +def test_make_algebra_returns_context_above_dense_threshold(): + algebra = make_algebra(10, 4, 2, device=DEVICE, dtype=torch.float32) + + assert isinstance(algebra, AlgebraContext) + assert algebra.n == 16 + assert not algebra.allow_full_layout_products + + +def test_dense_policy_uses_context_by_default_and_explicit_dense_up_to_twelve(): + auto_dense = make_algebra(8, 0, 0, device=DEVICE, dtype=torch.float32) + auto_context = make_algebra(9, 0, 0, device=DEVICE, dtype=torch.float32) + explicit_dense = make_algebra(9, 0, 0, kernel="dense", device=DEVICE, dtype=torch.float32) + + assert isinstance(auto_dense, CliffordAlgebra) + assert isinstance(auto_context, AlgebraContext) + assert isinstance(explicit_dense, CliffordAlgebra) + with pytest.raises(AssertionError): + CliffordAlgebra(9, 0, 0, device=DEVICE, dtype=torch.float32) + with pytest.raises(AssertionError): + make_algebra(13, 0, 0, kernel="dense", device=DEVICE, dtype=torch.float32) + + +def test_dense_kernel_accepts_shared_planned_operation_kwargs(): + algebra = CliffordAlgebra(4, 1, 1, device=DEVICE, dtype=torch.float64) + A = _grade_only_input(algebra, 2, (1,), seed=191) + B = _grade_only_input(algebra, 2, (1,), seed=193) + + actual = algebra.geometric_product( + A, + B, + left_grades=(1,), + right_grades=(1,), + output_grades=(0, 2), + compact_output=True, + ) + expected = algebra.projected_geometric_product( + A, + B, + left_grades=(1,), + right_grades=(1,), + output_grades=(0, 2), + compact_output=True, + ) + + assert torch.allclose(actual, expected, atol=1e-12, rtol=1e-12) + + +def test_dense_and_context_share_layout_indices_and_bivector_metric_signs(): + dense = CliffordAlgebra(3, 1, 0, device=DEVICE, dtype=torch.float64) + context = make_algebra(3, 1, 0, kernel="context", device=DEVICE, dtype=torch.float64) + + assert torch.equal(dense.grade_indices((2,)), context.grade_indices((2,))) + assert torch.allclose(dense.bivector_squared_signs(), context.bivector_squared_signs()) + + +def test_context_projected_product_handles_high_dim_vector_product(): + algebra = make_algebra(10, 4, 2, device=DEVICE, dtype=torch.float32) + A = torch.zeros(1, algebra.dim) + B = torch.zeros(1, algebra.dim) + A[0, 1] = 1.0 + B[0, 1] = 1.0 + B[0, 2] = 1.0 + + values, layout = algebra.projected_geometric_product( + A, + B, + left_grades=(1,), + right_grades=(1,), + output_grades=(0, 2), + compact_output=True, + return_layout=True, + ) + + scalar_pos = layout.basis_indices.index(0) + bivector_pos = layout.basis_indices.index(3) + assert values.shape[-1] == layout.dim + assert torch.allclose(values[0, scalar_pos], torch.tensor(1.0)) + assert torch.allclose(values[0, bivector_pos], torch.tensor(1.0)) + + +def test_context_planned_unary_projection_and_reverse_avoid_full_layout(): + algebra = make_algebra(10, 4, 2, device=DEVICE, dtype=torch.float32) + mv = torch.zeros(1, algebra.dim) + mv[0, 1] = 2.0 + mv[0, 3] = 5.0 + + projected, projected_layout = algebra.grade_projection(mv, 1, compact_output=True, return_layout=True) + reversed_bivector = algebra.reverse( + mv, + input_grades=(2,), + compact_output=True, + ) + vector_pos = projected_layout.basis_indices.index(1) + bivector_layout = algebra.layout((2,)) + bivector_pos = bivector_layout.basis_indices.index(3) + + assert torch.allclose(projected[0, vector_pos], torch.tensor(2.0)) + assert torch.allclose(reversed_bivector[0, bivector_pos], torch.tensor(-5.0)) + + +def test_context_planned_unary_compact_reverse(): + algebra = make_algebra(10, 4, 2, device=DEVICE, dtype=torch.float32) + layout = algebra.layout((2,)) + values = torch.arange(layout.dim, dtype=torch.float32).unsqueeze(0) + + actual, output_layout = algebra.reverse( + values, + input_layout=layout, + input_compact=True, + compact_output=True, + return_layout=True, + ) + + assert output_layout == layout + assert torch.allclose(actual, -values) + + +def test_dense_kernel_planned_unary_handles_compact_layouts(): + algebra = CliffordAlgebra(6, 0, 0, device=DEVICE, dtype=torch.float32) + layout = algebra.layout((2,)) + values = torch.arange(layout.dim, dtype=torch.float32).unsqueeze(0) + + actual, output_layout = algebra.reverse( + values, + input_layout=layout, + input_compact=True, + compact_output=True, + return_layout=True, + ) + + assert output_layout == layout + assert torch.allclose(actual, -values) + + +def test_multivector_compact_geometric_product_stays_compact_in_high_dimensions(): + algebra = make_algebra(10, 4, 2, device=DEVICE, dtype=torch.float32) + vector_layout = algebra.layout((1,)) + left = torch.zeros(1, vector_layout.dim) + right = torch.zeros(1, vector_layout.dim) + left[0, 0] = 1.0 + right[0, 0] = 1.0 + + result = Multivector(algebra, values=left, layout=vector_layout) * Multivector( + algebra, + values=right, + layout=vector_layout, + ) + + assert result.is_compact + assert result.layout.grades == (0, 2) + scalar_pos = result.layout.basis_indices.index(0) + assert torch.allclose(result.values[0, scalar_pos], torch.tensor(1.0)) + + +def test_multivector_compact_binary_wrappers_do_not_unwrap_dense_tensors(): + algebra = make_algebra(10, 4, 2, device=DEVICE, dtype=torch.float32) + bivector_layout = algebra.layout((2,)) + vector_layout = algebra.layout((1,)) + bivector = Multivector(algebra, values=torch.randn(2, bivector_layout.dim), layout=bivector_layout) + vector = Multivector(algebra, values=torch.randn(2, vector_layout.dim), layout=vector_layout) + + results = [ + (bivector.geometric_product(vector), (1, 3)), + (bivector.wedge(vector), (3,)), + (bivector.inner(vector), (3,)), + (bivector.commutator(vector), (1,)), + (bivector.anti_commutator(vector), (3,)), + ] + + for result, expected_grades in results: + assert result.is_compact + assert result.layout.grades == expected_grades + + +def test_multivector_compact_addition_merges_layouts_without_dense_materialization(): + algebra = make_algebra(10, 4, 2, device=DEVICE, dtype=torch.float32) + vector_layout = algebra.layout((1,)) + bivector_layout = algebra.layout((2,)) + vector = Multivector(algebra, values=torch.ones(1, vector_layout.dim), layout=vector_layout) + bivector = Multivector(algebra, values=2.0 * torch.ones(1, bivector_layout.dim), layout=bivector_layout) + + result = vector + bivector + + assert result.is_compact + assert result.layout.grades == (1, 2) + vector_values = vector.with_layout(result.layout).values + bivector_values = bivector.with_layout(result.layout).values + assert torch.allclose(result.values, vector_values + bivector_values) + + +def test_context_default_grades_drive_compact_product_without_callsite_metadata(): + algebra = make_algebra(10, 4, 2, device=DEVICE, dtype=torch.float32, default_grades=(1,)) + vector_layout = algebra.layout() + left = torch.zeros(1, vector_layout.dim) + right = torch.zeros(1, vector_layout.dim) + left[0, 0] = 1.0 + right[0, 0] = 1.0 + + values, output_layout = algebra.geometric_product( + left, + right, + compact_output=True, + return_layout=True, + ) + + assert vector_layout.grades == (1,) + assert output_layout.grades == (0, 2) + assert torch.allclose(values[0, output_layout.basis_indices.index(0)], torch.tensor(1.0)) + + +def test_context_declared_grades_infer_compact_operand_shapes(): + algebra = make_algebra(10, 4, 2, device=DEVICE, dtype=torch.float32) + vector_layout = algebra.layout((1,)) + left = torch.zeros(1, vector_layout.dim) + right = torch.zeros(1, vector_layout.dim) + left[0, 0] = 1.0 + right[0, 0] = 1.0 + + values, output_layout = algebra.projected_geometric_product( + left, + right, + left_grades=(1,), + right_grades=(1,), + compact_output=True, + return_layout=True, + ) + + assert output_layout.grades == (0, 2) + assert torch.allclose(values[0, output_layout.basis_indices.index(0)], torch.tensor(1.0)) + + +def test_context_projected_product_pairwise_mixed_compact_widths(): + algebra = make_algebra(16, 0, 0, kernel="context", device=DEVICE, dtype=torch.float32) + left_layout = algebra.layout((2,)) + right_layout = algebra.layout((1,)) + left = torch.randn(3, left_layout.dim) + right = torch.randn(4, right_layout.dim) + + values, output_layout = algebra.projected_wedge( + left, + right, + left_layout=left_layout, + right_layout=right_layout, + output_grades=(3,), + pairwise=True, + compact_output=True, + return_layout=True, + ) + executor = algebra.product_executor( + op="wedge", + left_grades=(2,), + right_grades=(1,), + output_grades=(3,), + dtype=torch.float32, + device=DEVICE, + ) + + assert output_layout.grades == (3,) + assert values.shape == (3, 4, output_layout.dim) + assert torch.allclose(values, executor.forward_pairwise_compact(left, right), atol=1e-6, rtol=1e-6) + + +def test_context_projected_product_suggests_pairwise_for_mismatched_item_axes(): + algebra = make_algebra(16, 0, 0, kernel="context", device=DEVICE, dtype=torch.float32) + left_layout = algebra.layout((2,)) + right_layout = algebra.layout((1,)) + left = torch.randn(3, left_layout.dim) + right = torch.randn(4, right_layout.dim) + + with pytest.raises(ValueError, match="Use pairwise=True"): + algebra.projected_wedge( + left, + right, + left_layout=left_layout, + right_layout=right_layout, + output_grades=(3,), + compact_output=True, + ) + + +def test_context_pairwise_projected_product_requires_item_axes(): + algebra = make_algebra(16, 0, 0, kernel="context", device=DEVICE, dtype=torch.float32) + left_layout = algebra.layout((2,)) + right_layout = algebra.layout((1,)) + + with pytest.raises(ValueError, match="explicit item axes"): + algebra.projected_wedge( + torch.randn(left_layout.dim), + torch.randn(right_layout.dim), + left_layout=left_layout, + right_layout=right_layout, + output_grades=(3,), + pairwise=True, + compact_output=True, + ) + + +def test_context_declared_product_requires_compact_output_without_dense_materialization(): + algebra = make_algebra(10, 4, 2, device=DEVICE, dtype=torch.float32) + vector_layout = algebra.layout((1,)) + left = torch.zeros(1, vector_layout.dim) + right = torch.zeros(1, vector_layout.dim) + + with pytest.raises(ValueError, match="Dense materialization is disabled"): + algebra.projected_geometric_product( + left, + right, + left_layout=vector_layout, + right_layout=vector_layout, + ) + + +def test_high_dim_context_requires_declared_layout_for_products(): + algebra = make_algebra(13, 0, 0, device=DEVICE, dtype=torch.float32) + A = torch.zeros(1, algebra.dim) + B = torch.zeros(1, algebra.dim) + + with pytest.raises(ValueError, match="Declare active grades"): + algebra.geometric_product(A, B) + + with pytest.raises(ValueError, match="Declare active grades"): + algebra.reverse(A) + + +def test_context_requires_declared_grades_by_default_even_low_dimensional(): + context = make_algebra(4, 0, 0, kernel="context", device=DEVICE, dtype=torch.float64) + + with pytest.raises(ValueError, match="Declare active grades"): + context.layout() + + +def test_context_warns_for_explicit_implicit_full_layout_fallback_between_eight_and_twelve(): + context = make_algebra( + 9, + 0, + 0, + kernel="context", + device=DEVICE, + dtype=torch.float32, + allow_full_layout_products=True, + ) + + with pytest.warns(RuntimeWarning, match="implicit full Cl\\(9,0,0\\) layout"): + layout = context.layout() + + assert layout.grades == tuple(range(context.n + 1)) + assert context.allow_full_layout_products + + +def test_low_dim_context_can_use_full_layout_fallback(): + context = make_algebra( + 4, + 0, + 0, + kernel="context", + device=DEVICE, + dtype=torch.float64, + allow_full_layout_products=True, + ) + dense = CliffordAlgebra(4, 0, 0, device=DEVICE, dtype=torch.float64) + A = _grade_only_input(dense, 2, (1,), seed=163) + B = _grade_only_input(dense, 2, (1,), seed=167) + + actual = context.geometric_product(A, B) + expected = dense.geometric_product(A, B) + + assert context.allow_full_layout_products + assert torch.allclose(actual, expected, atol=1e-12, rtol=1e-12) + + +def test_context_static_product_cost_limits_raise_before_executor_build(): + limits = PlanningLimits(warn_lanes=512, max_lanes=512, warn_pairs=512, max_pairs=64) + algebra = make_algebra(10, 4, 2, device=DEVICE, dtype=torch.float32, planning_limits=limits) + layout = algebra.layout((1,)) + left = torch.zeros(1, layout.dim) + right = torch.zeros(1, layout.dim) + + with pytest.raises(ValueError, match="basis interactions"): + algebra.projected_geometric_product( + left, + right, + left_layout=layout, + right_layout=layout, + compact_output=True, + ) + + +def test_direct_product_executor_obeys_static_pair_limits(): + limits = PlanningLimits(warn_lanes=512, max_lanes=512, warn_pairs=512, max_pairs=64) + algebra = make_algebra(16, 0, 0, device=DEVICE, dtype=torch.float32, planning_limits=limits) + + with pytest.raises(ValueError, match="basis interactions"): + algebra.planner.product_executor( + op="gp", + left_grades=(1,), + right_grades=(1,), + output_grades=(0, 2), + dtype=torch.float32, + device=DEVICE, + ) + + +def test_context_static_layout_cost_limit_raises_before_basis_materialization(): + limits = PlanningLimits(warn_lanes=32, max_lanes=64, warn_pairs=512, max_pairs=1024) + algebra = make_algebra(32, 0, 0, device=DEVICE, dtype=torch.float32, planning_limits=limits) + + with pytest.raises(ValueError, match="active lanes"): + algebra.layout((1, 2)) + + +def test_high_dimensional_vector_product_plan_avoids_full_basis_enumeration(): + algebra = make_algebra( + 32, + 0, + 0, + device=DEVICE, + dtype=torch.float32, + planning_limits=PlanningLimits(max_lanes=4096, max_pairs=100_000), + ) + vector_layout = algebra.layout((1,)) + executor = algebra.planner.product_executor( + op="gp", + left_grades=(1,), + right_grades=(1,), + output_grades=(0, 2), + dtype=torch.float32, + device=DEVICE, + ) + + assert vector_layout.dim == 32 + assert executor.output_dim == 1 + 32 * 31 // 2 + assert executor.pair_count == 32 * 32 + + +def test_high_dimensional_product_plan_reports_int64_bitmask_boundary(): + algebra = make_algebra( + 64, + 0, + 0, + device=DEVICE, + dtype=torch.float32, + planning_limits=PlanningLimits(max_lanes=4096, max_pairs=100_000), + ) + + with pytest.raises(ValueError, match="Current Torch-backed executors support bitmask tensorization up to n=63"): + algebra.planner.product_executor( + op="gp", + left_grades=(1,), + right_grades=(1,), + output_grades=(0, 2), + dtype=torch.float32, + device=DEVICE, + ) + + +def test_context_static_product_cost_warns_near_configured_limits(): + limits = PlanningLimits(warn_lanes=512, max_lanes=512, warn_pairs=128, max_pairs=512) + algebra = make_algebra(10, 4, 2, device=DEVICE, dtype=torch.float32, planning_limits=limits) + layout = algebra.layout((1,)) + left = torch.zeros(1, layout.dim) + right = torch.zeros(1, layout.dim) + + with pytest.warns(RuntimeWarning, match="basis interactions"): + values = algebra.projected_geometric_product( + left, + right, + left_layout=layout, + right_layout=layout, + compact_output=True, + ) + + assert values.shape[-1] == algebra.layout((0, 2)).dim + + +@pytest.mark.skipif(not hasattr(torch, "compile"), reason="torch.compile not available") +def test_static_grade_product_compiles_fullgraph_with_aot_eager(): + algebra = CliffordAlgebra(5, 1, 0, device=DEVICE, dtype=torch.float32) + plan = build_grade_product_plan( + algebra.p, + algebra.q, + algebra.r, + left_grades=(1,), + right_grades=(1,), + output_grades=(0, 2), + op="gp", + device=DEVICE, + dtype=torch.float32, + ) + product = GradeProductExecutor(plan) + A = _grade_only_input(algebra, 2, (1,), seed=107).to(dtype=torch.float32) + B = _grade_only_input(algebra, 2, (1,), seed=109).to(dtype=torch.float32) + + compiled = torch.compile(product, backend="aot_eager", fullgraph=True) + + expected = product(A, B) + actual = compiled(A, B) + + assert actual.shape[-1] == product.output_dim + assert torch.allclose(actual, expected, atol=1e-6, rtol=1e-6) + + +@pytest.mark.skipif(not hasattr(torch, "compile"), reason="torch.compile not available") +def test_planned_unary_compiles_fullgraph_with_aot_eager(): + algebra = make_algebra(6, 0, 0, kernel="context", device=DEVICE, dtype=torch.float32) + executor = algebra.planner.unary_executor( + op="reverse", + input_grades=(2,), + dtype=torch.float32, + device=DEVICE, + ) + values = _grade_only_input(CliffordAlgebra(6, 0, 0, device=DEVICE), 2, (2,), seed=173).to(dtype=torch.float32) + + compiled = torch.compile(executor, backend="aot_eager", fullgraph=True) + + expected = executor(values) + actual = compiled(values) + + assert torch.allclose(actual, expected, atol=1e-6, rtol=1e-6) + + +@pytest.mark.skipif(not hasattr(torch, "compile"), reason="torch.compile not available") +def test_algebra_projected_product_compiles_fullgraph_after_cache_warm(): + algebra = CliffordAlgebra(5, 1, 0, device=DEVICE, dtype=torch.float32) + A = _grade_only_input(algebra, 2, (1,), seed=151).to(dtype=torch.float32) + B = _grade_only_input(algebra, 2, (1,), seed=157).to(dtype=torch.float32) + + def product(x, y): + return algebra.projected_geometric_product( + x, + y, + left_grades=(1,), + right_grades=(1,), + output_grades=(0, 2), + compact_output=True, + ) + + expected = product(A, B) + compiled = torch.compile(product, backend="aot_eager", fullgraph=True) + actual = compiled(A, B) + + assert torch.allclose(actual, expected, atol=1e-6, rtol=1e-6) + + +@pytest.mark.skipif(not hasattr(torch, "compile"), reason="torch.compile not available") +def test_context_projected_product_compiles_fullgraph_from_cold_planner_cache(): + if hasattr(torch, "_dynamo"): + torch._dynamo.reset() + algebra = AlgebraContext(6, 0, device=DEVICE, dtype=torch.float32) + generator = torch.Generator(device=DEVICE).manual_seed(181) + left = torch.randn(2, algebra.layout((1,)).dim, dtype=torch.float32, generator=generator) + right = torch.randn(2, algebra.layout((1,)).dim, dtype=torch.float32, generator=generator) + + def product(x, y): + return algebra.geometric_product( + x, + y, + left_grades=(1,), + right_grades=(1,), + output_grades=(0, 2), + left_compact=True, + right_compact=True, + compact_output=True, + ) + + assert not algebra.planner._product_executors + compiled = torch.compile(product, backend="aot_eager", fullgraph=True) + actual = compiled(left, right) + expected = product(left, right) + + assert len(algebra.planner._product_executors) == 1 + assert torch.allclose(actual, expected, atol=1e-6, rtol=1e-6) + + +def _dense_method_name(op: str) -> str: + if op == "gp": + return "geometric_product" + if op == "inner": + return "inner_product" + if op == "anti_commutator": + return "anti_commutator" + return op diff --git a/tests/test_hermitian_metrics.py b/tests/test_hermitian_metrics.py index bc01180..a85d58c 100644 --- a/tests/test_hermitian_metrics.py +++ b/tests/test_hermitian_metrics.py @@ -3,10 +3,12 @@ import pytest import torch -from core.algebra import CliffordAlgebra +from core.config import make_algebra +from core.runtime.algebra import CliffordAlgebra +from core.runtime.multivector import Multivector pytestmark = pytest.mark.unit -from core.metric import ( +from core.runtime.metric import ( _hermitian_signs, clifford_conjugate, geometric_distance, @@ -143,6 +145,19 @@ def test_has_negative_signs(self, algebra_minkowski): has_negative = (signs < 0).any() assert has_negative, "Cl(2,1) should have negative signs" + def test_compact_context_matches_dense_active_lanes(self): + dense = CliffordAlgebra(3, 1, 0, device="cpu", dtype=torch.float64) + context = make_algebra(3, 1, 0, kernel="context", device="cpu", dtype=torch.float64) + layout = context.layout((1, 2)) + generator = torch.Generator(device="cpu").manual_seed(719) + A = torch.randn(5, layout.dim, dtype=torch.float64, generator=generator) + B = torch.randn(5, layout.dim, dtype=torch.float64, generator=generator) + + actual = hermitian_inner_product(context, A, B, layout=layout) + expected = hermitian_inner_product(dense, layout.dense(A), layout.dense(B)) + + assert torch.allclose(actual, expected, atol=1e-12, rtol=1e-12) + class TestHermitianNorm: def test_non_negative(self, algebra_minkowski): @@ -279,6 +294,28 @@ def test_conformal_spectrum(self, algebra_conformal): assert spec.shape == (algebra_conformal.n + 1,) assert (spec >= -1e-6).all() + def test_compact_spectrum_fills_inactive_grades(self): + context = make_algebra(5, 0, 0, kernel="context", device="cpu", dtype=torch.float32) + layout = context.layout((1,)) + values = torch.ones(2, layout.dim) + + spec = hermitian_grade_spectrum(context, values, layout=layout) + + assert spec.shape == (2, context.n + 1) + assert torch.allclose(spec[:, 0], torch.zeros(2)) + assert torch.allclose(spec[:, 1], torch.full((2,), float(context.n))) + assert torch.allclose(spec[:, 2:], torch.zeros(2, context.n - 1)) + + def test_compact_multivector_norm_uses_layout_without_dense_materialization(self): + context = make_algebra(9, 0, 0, kernel="context", device="cpu", dtype=torch.float32) + mv = Multivector.from_vectors(context, torch.ones(3, context.n)) + + norm = hermitian_norm(context, mv) + + assert mv.is_compact + assert norm.shape == (3, 1) + assert torch.allclose(norm, torch.full((3, 1), context.n**0.5)) + class TestSignatureTraceForm: def test_matches_standard_for_euclidean(self, algebra_3d): diff --git a/tests/test_iterative_unbender.py b/tests/test_iterative_unbender.py index da87f70..4071ce4 100644 --- a/tests/test_iterative_unbender.py +++ b/tests/test_iterative_unbender.py @@ -11,7 +11,7 @@ pytestmark = pytest.mark.slow -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra from models.sr.unbender import ( IterativeUnbender, OrthogonalEliminationResult, diff --git a/tests/test_layer_optimization.py b/tests/test_layer_optimization.py index bbc573b..ab19ad5 100644 --- a/tests/test_layer_optimization.py +++ b/tests/test_layer_optimization.py @@ -8,7 +8,7 @@ import pytest import torch -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra from layers import RotorLayer pytestmark = pytest.mark.unit diff --git a/tests/test_layers.py b/tests/test_layers.py index ecdf0e8..e701727 100644 --- a/tests/test_layers.py +++ b/tests/test_layers.py @@ -8,10 +8,8 @@ import pytest import torch -from core.algebra import CliffordAlgebra -from core.decomposition import ExpPolicy -from layers import CliffordLinear, MultiRotorLayer, RotorLayer -from layers.primitives.reflection import ReflectionLayer +from core.runtime.algebra import CliffordAlgebra +from layers import CliffordLinear, MultiRotorLayer, ReflectionLayer, RotorLayer pytestmark = pytest.mark.unit @@ -68,7 +66,7 @@ def test_multi_rotor_invariants(self, algebra_3d): def test_rotor_layer_exact_policy(self, algebra_3d): """Test RotorLayer with EXACT exp policy.""" - from core.decomposition import ExpPolicy + from core.runtime.decomposition import ExpPolicy algebra_3d.exp_policy = ExpPolicy.PRECISE x = torch.randn(4, 5, 8) @@ -86,7 +84,7 @@ def test_rotor_layer_exact_policy(self, algebra_3d): def test_rotor_layer_policy_vs_standard(self, algebra_3d): """Compare RotorLayer with FAST vs EXACT policy.""" - from core.decomposition import ExpPolicy + from core.runtime.decomposition import ExpPolicy layer_a = RotorLayer(algebra_3d, 3) layer_b = RotorLayer(algebra_3d, 3) @@ -107,7 +105,7 @@ def test_rotor_layer_policy_vs_standard(self, algebra_3d): def test_rotor_layer_backward_exact(self, algebra_3d): """Test gradient flow through RotorLayer with EXACT policy.""" - from core.decomposition import ExpPolicy + from core.runtime.decomposition import ExpPolicy algebra_3d.exp_policy = ExpPolicy.PRECISE @@ -129,7 +127,7 @@ def test_rotor_layer_backward_exact(self, algebra_3d): def test_multi_rotor_layer_exact_policy(self, algebra_3d): """Test MultiRotorLayer with EXACT policy.""" - from core.decomposition import ExpPolicy + from core.runtime.decomposition import ExpPolicy algebra_3d.exp_policy = ExpPolicy.PRECISE x = torch.randn(4, 5, 8) @@ -141,7 +139,7 @@ def test_multi_rotor_layer_exact_policy(self, algebra_3d): def test_multi_rotor_layer_backward_exact(self, algebra_3d): """Test gradient flow through MultiRotorLayer with EXACT policy.""" - from core.decomposition import ExpPolicy + from core.runtime.decomposition import ExpPolicy algebra_3d.exp_policy = ExpPolicy.PRECISE @@ -164,7 +162,7 @@ def test_multi_rotor_layer_backward_exact(self, algebra_3d): def test_rotor_layer_rotor_property(self, algebra_3d): """Verify that exp-produced rotors satisfy R * ~R = 1.""" - from core.decomposition import ExpPolicy + from core.runtime.decomposition import ExpPolicy algebra_3d.exp_policy = ExpPolicy.PRECISE diff --git a/tests/test_md17_complete.py b/tests/test_md17_complete.py index 9093f8a..3a2dfc9 100644 --- a/tests/test_md17_complete.py +++ b/tests/test_md17_complete.py @@ -3,11 +3,11 @@ import pytest import torch -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra pytestmark = pytest.mark.unit -from core.decomposition import ExpPolicy -from core.metric import hermitian_grade_spectrum, hermitian_norm +from core.runtime.decomposition import ExpPolicy +from core.runtime.metric import hermitian_grade_spectrum, hermitian_norm from functional.loss import ConservativeLoss, HermitianGradeRegularization from models.md17 import DynamicRotorGenerator, GaussianRBF, MD17ForceNet, MD17InteractionBlock @@ -116,7 +116,7 @@ def test_forward_default(self, algebra): assert force.shape == (8, 3) def test_forward_with_exact_policy(self, algebra): - from core.decomposition import ExpPolicy + from core.runtime.decomposition import ExpPolicy algebra.exp_policy = ExpPolicy.PRECISE model = MD17ForceNet( diff --git a/tests/test_metric_search.py b/tests/test_metric_search.py index 7bc53c8..0f445e9 100644 --- a/tests/test_metric_search.py +++ b/tests/test_metric_search.py @@ -10,7 +10,7 @@ import pytest import torch -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra pytestmark = pytest.mark.slow from core.analysis import GeodesicFlow, MetricSearch diff --git a/tests/test_multivector.py b/tests/test_multivector.py index 3dd8a67..013630c 100644 --- a/tests/test_multivector.py +++ b/tests/test_multivector.py @@ -3,8 +3,8 @@ import pytest import torch -from core.algebra import CliffordAlgebra -from core.multivector import Multivector +from core.runtime.algebra import CliffordAlgebra +from core.runtime.multivector import Multivector @pytest.fixture @@ -28,7 +28,10 @@ def rand_mv(alg, rng, batch=4): def test_from_vectors(alg): v = torch.randn(4, 3) mv = Multivector.from_vectors(alg, v) - assert mv.shape[-1] == 2**alg.n + assert mv.is_compact + assert mv.grades == (1,) + assert mv.shape[-1] == alg.n + assert mv.tensor.shape[-1] == 2**alg.n def test_scalar(alg): diff --git a/tests/test_properties.py b/tests/test_properties.py index 4486a42..3a6df8e 100644 --- a/tests/test_properties.py +++ b/tests/test_properties.py @@ -8,7 +8,7 @@ import pytest import torch -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra from layers import CliffordLayerNorm pytestmark = pytest.mark.unit diff --git a/tests/test_riemannian_optimizer.py b/tests/test_riemannian_optimizer.py index 2fdae02..6fe3903 100644 --- a/tests/test_riemannian_optimizer.py +++ b/tests/test_riemannian_optimizer.py @@ -14,7 +14,7 @@ import torch.nn as nn import torch.nn.functional as F -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra from layers import MultiRotorLayer, RotorGadget, RotorLayer from optimizers.riemannian import ( MANIFOLD_EUCLIDEAN, @@ -23,6 +23,7 @@ ExponentialSGD, RiemannianAdam, group_parameters_by_manifold, + make_riemannian_optimizer, tag_manifold, ) @@ -556,18 +557,18 @@ def test_manifold_tagging(algebra_3d): from layers.primitives.reflection import ReflectionLayer rotor = RotorLayer(algebra_3d, channels=4) - assert getattr(rotor.bivector_weights, "_manifold", None) == "spin" + assert getattr(rotor.bivector_weights, "_manifold", None) == MANIFOLD_SPIN reflection = ReflectionLayer(algebra_3d, channels=4) - assert getattr(reflection.vector_weights, "_manifold", None) == "sphere" + assert getattr(reflection.vector_weights, "_manifold", None) == MANIFOLD_SPHERE multi = MultiRotorLayer(algebra_3d, channels=4, num_rotors=2) - assert getattr(multi.rotor_bivectors, "_manifold", None) == "spin" + assert getattr(multi.rotor_bivectors, "_manifold", None) == MANIFOLD_SPIN assert not hasattr(multi.weights, "_manifold") # Euclidean, untagged gadget = RotorGadget(algebra_3d, in_channels=4, out_channels=8) - assert getattr(gadget.bivector_left, "_manifold", None) == "spin" - assert getattr(gadget.bivector_right, "_manifold", None) == "spin" + assert getattr(gadget.bivector_left, "_manifold", None) == MANIFOLD_SPIN + assert getattr(gadget.bivector_right, "_manifold", None) == MANIFOLD_SPIN def test_tag_manifold_helper(): @@ -584,6 +585,17 @@ def test_tag_manifold_helper(): tag_manifold(p, "invalid") +def test_group_parameters_rejects_unknown_manifold(): + class BadModel(nn.Module): + def __init__(self): + super().__init__() + self.weight = nn.Parameter(torch.randn(2, 3)) + self.weight._manifold = "bad" + + with pytest.raises(ValueError, match="Unknown manifold"): + group_parameters_by_manifold(BadModel()) + + def test_from_model_groups(algebra_3d): """Verify from_model creates separate groups per manifold.""" from layers.primitives.reflection import ReflectionLayer @@ -612,6 +624,19 @@ def forward(self, x): assert "euclidean" in manifolds +def test_make_riemannian_optimizer_factory(algebra_3d): + layer = RotorLayer(algebra_3d, channels=4) + + adam = make_riemannian_optimizer(layer, algebra_3d, optimizer="adam", lr=0.001) + sgd = make_riemannian_optimizer(layer, algebra_3d, optimizer="exponential_sgd", lr=0.01) + + assert isinstance(adam, RiemannianAdam) + assert isinstance(sgd, ExponentialSGD) + + with pytest.raises(ValueError, match="optimizer must be"): + make_riemannian_optimizer(layer, algebra_3d, optimizer="rmsprop") + + def test_sphere_retraction(algebra_3d): """Verify sphere-tagged params are projected to unit sphere after step.""" from layers.primitives.reflection import ReflectionLayer diff --git a/tests/test_rotor_gadget.py b/tests/test_rotor_gadget.py index b380028..d2a9ab9 100644 --- a/tests/test_rotor_gadget.py +++ b/tests/test_rotor_gadget.py @@ -12,7 +12,7 @@ import pytest import torch -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra from layers import CliffordLinear, RotorGadget pytestmark = pytest.mark.unit @@ -286,7 +286,7 @@ class TestExpPolicy: def test_with_exact_policy(self, algebra_3d): """Test layer with EXACT exp policy.""" - from core.decomposition import ExpPolicy + from core.runtime.decomposition import ExpPolicy algebra_3d.exp_policy = ExpPolicy.PRECISE @@ -305,7 +305,7 @@ def test_with_exact_policy(self, algebra_3d): def test_policy_fast_vs_exact(self, algebra_3d): """Compare FAST and EXACT policies (n=3: should match).""" - from core.decomposition import ExpPolicy + from core.runtime.decomposition import ExpPolicy torch.manual_seed(42) layer_a = RotorGadget( diff --git a/tests/test_rotor_translate.py b/tests/test_rotor_translate.py index b64b4e8..780518a 100644 --- a/tests/test_rotor_translate.py +++ b/tests/test_rotor_translate.py @@ -7,7 +7,7 @@ import pytest import torch -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra pytestmark = pytest.mark.unit from models.sr.net import SRGBN diff --git a/tests/test_symbolic_regression.py b/tests/test_symbolic_regression.py index 37bddfa..64afd1f 100644 --- a/tests/test_symbolic_regression.py +++ b/tests/test_symbolic_regression.py @@ -8,7 +8,7 @@ import torch from omegaconf import OmegaConf -from core.algebra import CliffordAlgebra +from core.runtime.algebra import CliffordAlgebra from datalib.symbolic_regression import ( BLACKBOX_DATASETS, FIRST_PRINCIPLES_DATASETS, @@ -41,11 +41,22 @@ def small_algebra(): _TEST_CACHE = "./data/pmlb_cache" -def _make_cfg(dataset_name=_TEST_DATASET, hidden_channels=4, num_layers=1, n_samples=200, auto=False): +def _make_cfg(dataset_name=_TEST_DATASET, hidden_channels=4, num_layers=1, n_samples=200, metric_search=False): return OmegaConf.create( { "name": "sr", - "algebra": {"p": 4, "q": 0, "r": 0, "device": "cpu", "auto": auto}, + "algebra": { + "p": 4, + "q": 0, + "r": 0, + "device": "cpu", + "dtype": "float32", + "exp_policy": "balanced", + "kernel": "auto", + "metric_search": metric_search, + "dense_threshold": 8, + "default_grades": None, + }, "dataset": { "dataset_name": dataset_name, "category": "blackbox", @@ -56,7 +67,6 @@ def _make_cfg(dataset_name=_TEST_DATASET, hidden_channels=4, num_layers=1, n_sam "model": { "hidden_channels": hidden_channels, "num_layers": num_layers, - "exp_policy": "balanced", }, "training": { "epochs": 1, @@ -66,6 +76,7 @@ def _make_cfg(dataset_name=_TEST_DATASET, hidden_channels=4, num_layers=1, n_sam "max_bivector_norm": 10.0, "sparsity_weight": 0.01, "seed": 0, + "scheduler": {"factor": 0.5, "patience": 10}, }, "checkpoint": None, } diff --git a/uv.lock b/uv.lock index 1cd172e..07c1e35 100644 --- a/uv.lock +++ b/uv.lock @@ -1,22 +1,10 @@ version = 1 revision = 2 -requires-python = ">=3.9" +requires-python = ">=3.10" resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", - "python_full_version < '3.10'", + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version < '3.11'", ] [[package]] @@ -146,23 +134,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3c/5f/24155e30ba7f8c96918af1350eb0663e2430aad9e001c0489d89cd708ab1/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fc353029f176fd2b3ec6cfc71be166aba1936fe5d73dd1992ce289ca6647a9aa", size = 1769801, upload-time = "2026-01-03T17:32:20.25Z" }, { url = "https://files.pythonhosted.org/packages/eb/f8/7314031ff5c10e6ece114da79b338ec17eeff3a079e53151f7e9f43c4723/aiohttp-3.13.3-cp314-cp314t-win32.whl", hash = "sha256:2e41b18a58da1e474a057b3d35248d8320029f61d70a37629535b16a0c8f3767", size = 466523, upload-time = "2026-01-03T17:32:22.215Z" }, { url = "https://files.pythonhosted.org/packages/b4/63/278a98c715ae467624eafe375542d8ba9b4383a016df8fdefe0ae28382a7/aiohttp-3.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:44531a36aa2264a1860089ffd4dce7baf875ee5a6079d5fb42e261c704ef7344", size = 499694, upload-time = "2026-01-03T17:32:24.546Z" }, - { url = "https://files.pythonhosted.org/packages/bf/79/446655656861d3e7e2c32bfcf160c7aa9e9dc63776a691b124dba65cdd77/aiohttp-3.13.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:31a83ea4aead760dfcb6962efb1d861db48c34379f2ff72db9ddddd4cda9ea2e", size = 741433, upload-time = "2026-01-03T17:32:26.453Z" }, - { url = "https://files.pythonhosted.org/packages/cb/49/773c4b310b5140d2fb5e79bb0bf40b7b41dad80a288ca1a8759f5f72bda9/aiohttp-3.13.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:988a8c5e317544fdf0d39871559e67b6341065b87fceac641108c2096d5506b7", size = 497332, upload-time = "2026-01-03T17:32:28.37Z" }, - { url = "https://files.pythonhosted.org/packages/bc/31/1dcbc4b83a4e6f76a0ad883f07f21ffbfe29750c89db97381701508c9f45/aiohttp-3.13.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9b174f267b5cfb9a7dba9ee6859cecd234e9a681841eb85068059bc867fb8f02", size = 492365, upload-time = "2026-01-03T17:32:30.234Z" }, - { url = "https://files.pythonhosted.org/packages/5a/b5/b50657496c8754482cd7964e50aaf3aa84b3db61ed45daec4c1aec5b94b4/aiohttp-3.13.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:947c26539750deeaee933b000fb6517cc770bbd064bad6033f1cff4803881e43", size = 1660440, upload-time = "2026-01-03T17:32:32.586Z" }, - { url = "https://files.pythonhosted.org/packages/2a/73/9b69e5139d89d75127569298931444ad78ea86a5befd5599780b1e9a6880/aiohttp-3.13.3-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9ebf57d09e131f5323464bd347135a88622d1c0976e88ce15b670e7ad57e4bd6", size = 1632740, upload-time = "2026-01-03T17:32:34.793Z" }, - { url = "https://files.pythonhosted.org/packages/ef/fe/3ea9b5af694b4e3aec0d0613a806132ca744747146fca68e96bf056f61a7/aiohttp-3.13.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4ae5b5a0e1926e504c81c5b84353e7a5516d8778fbbff00429fe7b05bb25cbce", size = 1719782, upload-time = "2026-01-03T17:32:37.737Z" }, - { url = "https://files.pythonhosted.org/packages/fb/c2/46b3b06e60851cbb71efb0f79a3267279cbef7b12c58e68a1e897f269cca/aiohttp-3.13.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2ba0eea45eb5cc3172dbfc497c066f19c41bac70963ea1a67d51fc92e4cf9a80", size = 1813527, upload-time = "2026-01-03T17:32:39.973Z" }, - { url = "https://files.pythonhosted.org/packages/36/23/71ceb78c769ed65fe4c697692de232b63dab399210678d2b00961ccb0619/aiohttp-3.13.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bae5c2ed2eae26cc382020edad80d01f36cb8e746da40b292e68fec40421dc6a", size = 1661268, upload-time = "2026-01-03T17:32:42.082Z" }, - { url = "https://files.pythonhosted.org/packages/c4/8d/86e929523d955e85ebab7c0e2b9e0cb63604cfc27dc3280e10d0063cf682/aiohttp-3.13.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8a60e60746623925eab7d25823329941aee7242d559baa119ca2b253c88a7bd6", size = 1552742, upload-time = "2026-01-03T17:32:44.622Z" }, - { url = "https://files.pythonhosted.org/packages/3a/ea/3f5987cba1bab6bd151f0d97aa60f0ce04d3c83316692a6bb6ba2fb69f92/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e50a2e1404f063427c9d027378472316201a2290959a295169bcf25992d04558", size = 1632918, upload-time = "2026-01-03T17:32:46.749Z" }, - { url = "https://files.pythonhosted.org/packages/be/2c/7e1e85121f2e31ee938cb83a8f32dfafd4908530c10fabd6d46761c12ac7/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:9a9dc347e5a3dc7dfdbc1f82da0ef29e388ddb2ed281bfce9dd8248a313e62b7", size = 1644446, upload-time = "2026-01-03T17:32:49.063Z" }, - { url = "https://files.pythonhosted.org/packages/5d/35/ce6133d423ad0e8ca976a7c848f7146bca3520eea4ccf6b95e2d077c9d20/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b46020d11d23fe16551466c77823df9cc2f2c1e63cc965daf67fa5eec6ca1877", size = 1689487, upload-time = "2026-01-03T17:32:51.113Z" }, - { url = "https://files.pythonhosted.org/packages/50/f7/ff7a27c15603d460fd1366b3c22054f7ae4fa9310aca40b43bde35867fcd/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:69c56fbc1993fa17043e24a546959c0178fe2b5782405ad4559e6c13975c15e3", size = 1540715, upload-time = "2026-01-03T17:32:53.38Z" }, - { url = "https://files.pythonhosted.org/packages/17/02/053f11346e5b962e6d8a1c4f8c70c29d5970a1b4b8e7894c68e12c27a57f/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:b99281b0704c103d4e11e72a76f1b543d4946fea7dd10767e7e1b5f00d4e5704", size = 1711835, upload-time = "2026-01-03T17:32:56.088Z" }, - { url = "https://files.pythonhosted.org/packages/fb/71/9b9761ddf276fd6708d13720197cbac19b8d67ecfa9116777924056cfcaa/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:40c5e40ecc29ba010656c18052b877a1c28f84344825efa106705e835c28530f", size = 1649593, upload-time = "2026-01-03T17:32:58.181Z" }, - { url = "https://files.pythonhosted.org/packages/ae/72/5d817e9ea218acae12a5e3b9ad1178cf0c12fc3570c0b47eea2daf95f9ea/aiohttp-3.13.3-cp39-cp39-win32.whl", hash = "sha256:56339a36b9f1fc708260c76c87e593e2afb30d26de9ae1eb445b5e051b98a7a1", size = 434831, upload-time = "2026-01-03T17:33:00.577Z" }, - { url = "https://files.pythonhosted.org/packages/39/cb/22659d9bf3149b7a2927bc2769cc9c8f8f5a80eba098398e03c199a43a85/aiohttp-3.13.3-cp39-cp39-win_amd64.whl", hash = "sha256:c6b8568a3bb5819a0ad087f16d40e5a3fb6099f39ea1d5625a3edc1e923fc538", size = 457697, upload-time = "2026-01-03T17:33:03.167Z" }, ] [[package]] @@ -178,51 +149,16 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, ] -[[package]] -name = "altair" -version = "5.5.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "jinja2", marker = "python_full_version < '3.10'" }, - { name = "jsonschema", version = "4.25.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "narwhals", marker = "python_full_version < '3.10'" }, - { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "typing-extensions", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/16/b1/f2969c7bdb8ad8bbdda031687defdce2c19afba2aa2c8e1d2a17f78376d8/altair-5.5.0.tar.gz", hash = "sha256:d960ebe6178c56de3855a68c47b516be38640b73fb3b5111c2a9ca90546dd73d", size = 705305, upload-time = "2024-11-23T23:39:58.542Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/f3/0b6ced594e51cc95d8c1fc1640d3623770d01e4969d29c0bd09945fafefa/altair-5.5.0-py3-none-any.whl", hash = "sha256:91a310b926508d560fe0148d02a194f38b824122641ef528113d029fcd129f8c", size = 731200, upload-time = "2024-11-23T23:39:56.4Z" }, -] - [[package]] name = "altair" version = "6.0.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] dependencies = [ - { name = "jinja2", marker = "python_full_version >= '3.10'" }, - { name = "jsonschema", version = "4.26.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "narwhals", marker = "python_full_version >= '3.10'" }, - { name = "packaging", version = "26.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.10' and python_full_version < '3.15'" }, + { name = "jinja2" }, + { name = "jsonschema" }, + { name = "narwhals" }, + { name = "packaging" }, + { name = "typing-extensions", marker = "python_full_version < '3.15'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f7/c0/184a89bd5feba14ff3c41cfaf1dd8a82c05f5ceedbc92145e17042eb08a4/altair-6.0.0.tar.gz", hash = "sha256:614bf5ecbe2337347b590afb111929aa9c16c9527c4887d96c9bc7f6640756b4", size = 763834, upload-time = "2025-11-12T08:59:11.519Z" } wheels = [ @@ -240,9 +176,9 @@ name = "anyio" version = "4.12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "exceptiongroup", marker = "python_full_version == '3.10.*'" }, - { name = "idna", marker = "python_full_version >= '3.10'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.10' and python_full_version < '3.13'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } wheels = [ @@ -403,62 +339,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, - { url = "https://files.pythonhosted.org/packages/46/7c/0c4760bccf082737ca7ab84a4c2034fcc06b1f21cf3032ea98bd6feb1725/charset_normalizer-3.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9", size = 209609, upload-time = "2025-10-14T04:42:10.922Z" }, - { url = "https://files.pythonhosted.org/packages/bb/a4/69719daef2f3d7f1819de60c9a6be981b8eeead7542d5ec4440f3c80e111/charset_normalizer-3.4.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d", size = 149029, upload-time = "2025-10-14T04:42:12.38Z" }, - { url = "https://files.pythonhosted.org/packages/e6/21/8d4e1d6c1e6070d3672908b8e4533a71b5b53e71d16828cc24d0efec564c/charset_normalizer-3.4.4-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608", size = 144580, upload-time = "2025-10-14T04:42:13.549Z" }, - { url = "https://files.pythonhosted.org/packages/a7/0a/a616d001b3f25647a9068e0b9199f697ce507ec898cacb06a0d5a1617c99/charset_normalizer-3.4.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc", size = 162340, upload-time = "2025-10-14T04:42:14.892Z" }, - { url = "https://files.pythonhosted.org/packages/85/93/060b52deb249a5450460e0585c88a904a83aec474ab8e7aba787f45e79f2/charset_normalizer-3.4.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e", size = 159619, upload-time = "2025-10-14T04:42:16.676Z" }, - { url = "https://files.pythonhosted.org/packages/dd/21/0274deb1cc0632cd587a9a0ec6b4674d9108e461cb4cd40d457adaeb0564/charset_normalizer-3.4.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1", size = 153980, upload-time = "2025-10-14T04:42:17.917Z" }, - { url = "https://files.pythonhosted.org/packages/28/2b/e3d7d982858dccc11b31906976323d790dded2017a0572f093ff982d692f/charset_normalizer-3.4.4-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3", size = 152174, upload-time = "2025-10-14T04:42:19.018Z" }, - { url = "https://files.pythonhosted.org/packages/6e/ff/4a269f8e35f1e58b2df52c131a1fa019acb7ef3f8697b7d464b07e9b492d/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6", size = 151666, upload-time = "2025-10-14T04:42:20.171Z" }, - { url = "https://files.pythonhosted.org/packages/da/c9/ec39870f0b330d58486001dd8e532c6b9a905f5765f58a6f8204926b4a93/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88", size = 145550, upload-time = "2025-10-14T04:42:21.324Z" }, - { url = "https://files.pythonhosted.org/packages/75/8f/d186ab99e40e0ed9f82f033d6e49001701c81244d01905dd4a6924191a30/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1", size = 163721, upload-time = "2025-10-14T04:42:22.46Z" }, - { url = "https://files.pythonhosted.org/packages/96/b1/6047663b9744df26a7e479ac1e77af7134b1fcf9026243bb48ee2d18810f/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf", size = 152127, upload-time = "2025-10-14T04:42:23.712Z" }, - { url = "https://files.pythonhosted.org/packages/59/78/e5a6eac9179f24f704d1be67d08704c3c6ab9f00963963524be27c18ed87/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318", size = 161175, upload-time = "2025-10-14T04:42:24.87Z" }, - { url = "https://files.pythonhosted.org/packages/e5/43/0e626e42d54dd2f8dd6fc5e1c5ff00f05fbca17cb699bedead2cae69c62f/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c", size = 155375, upload-time = "2025-10-14T04:42:27.246Z" }, - { url = "https://files.pythonhosted.org/packages/e9/91/d9615bf2e06f35e4997616ff31248c3657ed649c5ab9d35ea12fce54e380/charset_normalizer-3.4.4-cp39-cp39-win32.whl", hash = "sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505", size = 99692, upload-time = "2025-10-14T04:42:28.425Z" }, - { url = "https://files.pythonhosted.org/packages/d1/a9/6c040053909d9d1ef4fcab45fddec083aedc9052c10078339b47c8573ea8/charset_normalizer-3.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966", size = 107192, upload-time = "2025-10-14T04:42:29.482Z" }, - { url = "https://files.pythonhosted.org/packages/f0/c6/4fa536b2c0cd3edfb7ccf8469fa0f363ea67b7213a842b90909ca33dd851/charset_normalizer-3.4.4-cp39-cp39-win_arm64.whl", hash = "sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50", size = 100220, upload-time = "2025-10-14T04:42:30.632Z" }, { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, ] -[[package]] -name = "click" -version = "8.1.8" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, -] - [[package]] name = "click" version = "8.3.1" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] dependencies = [ - { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } wheels = [ @@ -474,94 +363,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] -[[package]] -name = "contourpy" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f5/f6/31a8f28b4a2a4fa0e01085e542f3081ab0588eff8e589d39d775172c9792/contourpy-1.3.0.tar.gz", hash = "sha256:7ffa0db17717a8ffb127efd0c95a4362d996b892c2904db72428d5b52e1938a4", size = 13464370, upload-time = "2024-08-27T21:00:03.328Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/e0/be8dcc796cfdd96708933e0e2da99ba4bb8f9b2caa9d560a50f3f09a65f3/contourpy-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:880ea32e5c774634f9fcd46504bf9f080a41ad855f4fef54f5380f5133d343c7", size = 265366, upload-time = "2024-08-27T20:50:09.947Z" }, - { url = "https://files.pythonhosted.org/packages/50/d6/c953b400219443535d412fcbbc42e7a5e823291236bc0bb88936e3cc9317/contourpy-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:76c905ef940a4474a6289c71d53122a4f77766eef23c03cd57016ce19d0f7b42", size = 249226, upload-time = "2024-08-27T20:50:16.1Z" }, - { url = "https://files.pythonhosted.org/packages/6f/b4/6fffdf213ffccc28483c524b9dad46bb78332851133b36ad354b856ddc7c/contourpy-1.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92f8557cbb07415a4d6fa191f20fd9d2d9eb9c0b61d1b2f52a8926e43c6e9af7", size = 308460, upload-time = "2024-08-27T20:50:22.536Z" }, - { url = "https://files.pythonhosted.org/packages/cf/6c/118fc917b4050f0afe07179a6dcbe4f3f4ec69b94f36c9e128c4af480fb8/contourpy-1.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36f965570cff02b874773c49bfe85562b47030805d7d8360748f3eca570f4cab", size = 347623, upload-time = "2024-08-27T20:50:28.806Z" }, - { url = "https://files.pythonhosted.org/packages/f9/a4/30ff110a81bfe3abf7b9673284d21ddce8cc1278f6f77393c91199da4c90/contourpy-1.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cacd81e2d4b6f89c9f8a5b69b86490152ff39afc58a95af002a398273e5ce589", size = 317761, upload-time = "2024-08-27T20:50:35.126Z" }, - { url = "https://files.pythonhosted.org/packages/99/e6/d11966962b1aa515f5586d3907ad019f4b812c04e4546cc19ebf62b5178e/contourpy-1.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69375194457ad0fad3a839b9e29aa0b0ed53bb54db1bfb6c3ae43d111c31ce41", size = 322015, upload-time = "2024-08-27T20:50:40.318Z" }, - { url = "https://files.pythonhosted.org/packages/4d/e3/182383743751d22b7b59c3c753277b6aee3637049197624f333dac5b4c80/contourpy-1.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a52040312b1a858b5e31ef28c2e865376a386c60c0e248370bbea2d3f3b760d", size = 1262672, upload-time = "2024-08-27T20:50:55.643Z" }, - { url = "https://files.pythonhosted.org/packages/78/53/974400c815b2e605f252c8fb9297e2204347d1755a5374354ee77b1ea259/contourpy-1.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3faeb2998e4fcb256542e8a926d08da08977f7f5e62cf733f3c211c2a5586223", size = 1321688, upload-time = "2024-08-27T20:51:11.293Z" }, - { url = "https://files.pythonhosted.org/packages/52/29/99f849faed5593b2926a68a31882af98afbeac39c7fdf7de491d9c85ec6a/contourpy-1.3.0-cp310-cp310-win32.whl", hash = "sha256:36e0cff201bcb17a0a8ecc7f454fe078437fa6bda730e695a92f2d9932bd507f", size = 171145, upload-time = "2024-08-27T20:51:15.2Z" }, - { url = "https://files.pythonhosted.org/packages/a9/97/3f89bba79ff6ff2b07a3cbc40aa693c360d5efa90d66e914f0ff03b95ec7/contourpy-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:87ddffef1dbe5e669b5c2440b643d3fdd8622a348fe1983fad7a0f0ccb1cd67b", size = 216019, upload-time = "2024-08-27T20:51:19.365Z" }, - { url = "https://files.pythonhosted.org/packages/b3/1f/9375917786cb39270b0ee6634536c0e22abf225825602688990d8f5c6c19/contourpy-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fa4c02abe6c446ba70d96ece336e621efa4aecae43eaa9b030ae5fb92b309ad", size = 266356, upload-time = "2024-08-27T20:51:24.146Z" }, - { url = "https://files.pythonhosted.org/packages/05/46/9256dd162ea52790c127cb58cfc3b9e3413a6e3478917d1f811d420772ec/contourpy-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:834e0cfe17ba12f79963861e0f908556b2cedd52e1f75e6578801febcc6a9f49", size = 250915, upload-time = "2024-08-27T20:51:28.683Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5d/3056c167fa4486900dfbd7e26a2fdc2338dc58eee36d490a0ed3ddda5ded/contourpy-1.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbc4c3217eee163fa3984fd1567632b48d6dfd29216da3ded3d7b844a8014a66", size = 310443, upload-time = "2024-08-27T20:51:33.675Z" }, - { url = "https://files.pythonhosted.org/packages/ca/c2/1a612e475492e07f11c8e267ea5ec1ce0d89971be496c195e27afa97e14a/contourpy-1.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4865cd1d419e0c7a7bf6de1777b185eebdc51470800a9f42b9e9decf17762081", size = 348548, upload-time = "2024-08-27T20:51:39.322Z" }, - { url = "https://files.pythonhosted.org/packages/45/cf/2c2fc6bb5874158277b4faf136847f0689e1b1a1f640a36d76d52e78907c/contourpy-1.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:303c252947ab4b14c08afeb52375b26781ccd6a5ccd81abcdfc1fafd14cf93c1", size = 319118, upload-time = "2024-08-27T20:51:44.717Z" }, - { url = "https://files.pythonhosted.org/packages/03/33/003065374f38894cdf1040cef474ad0546368eea7e3a51d48b8a423961f8/contourpy-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637f674226be46f6ba372fd29d9523dd977a291f66ab2a74fbeb5530bb3f445d", size = 323162, upload-time = "2024-08-27T20:51:49.683Z" }, - { url = "https://files.pythonhosted.org/packages/42/80/e637326e85e4105a802e42959f56cff2cd39a6b5ef68d5d9aee3ea5f0e4c/contourpy-1.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:76a896b2f195b57db25d6b44e7e03f221d32fe318d03ede41f8b4d9ba1bff53c", size = 1265396, upload-time = "2024-08-27T20:52:04.926Z" }, - { url = "https://files.pythonhosted.org/packages/7c/3b/8cbd6416ca1bbc0202b50f9c13b2e0b922b64be888f9d9ee88e6cfabfb51/contourpy-1.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e1fd23e9d01591bab45546c089ae89d926917a66dceb3abcf01f6105d927e2cb", size = 1324297, upload-time = "2024-08-27T20:52:21.843Z" }, - { url = "https://files.pythonhosted.org/packages/4d/2c/021a7afaa52fe891f25535506cc861c30c3c4e5a1c1ce94215e04b293e72/contourpy-1.3.0-cp311-cp311-win32.whl", hash = "sha256:d402880b84df3bec6eab53cd0cf802cae6a2ef9537e70cf75e91618a3801c20c", size = 171808, upload-time = "2024-08-27T20:52:25.163Z" }, - { url = "https://files.pythonhosted.org/packages/8d/2f/804f02ff30a7fae21f98198828d0857439ec4c91a96e20cf2d6c49372966/contourpy-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:6cb6cc968059db9c62cb35fbf70248f40994dfcd7aa10444bbf8b3faeb7c2d67", size = 217181, upload-time = "2024-08-27T20:52:29.13Z" }, - { url = "https://files.pythonhosted.org/packages/c9/92/8e0bbfe6b70c0e2d3d81272b58c98ac69ff1a4329f18c73bd64824d8b12e/contourpy-1.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:570ef7cf892f0afbe5b2ee410c507ce12e15a5fa91017a0009f79f7d93a1268f", size = 267838, upload-time = "2024-08-27T20:52:33.911Z" }, - { url = "https://files.pythonhosted.org/packages/e3/04/33351c5d5108460a8ce6d512307690b023f0cfcad5899499f5c83b9d63b1/contourpy-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:da84c537cb8b97d153e9fb208c221c45605f73147bd4cadd23bdae915042aad6", size = 251549, upload-time = "2024-08-27T20:52:39.179Z" }, - { url = "https://files.pythonhosted.org/packages/51/3d/aa0fe6ae67e3ef9f178389e4caaaa68daf2f9024092aa3c6032e3d174670/contourpy-1.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0be4d8425bfa755e0fd76ee1e019636ccc7c29f77a7c86b4328a9eb6a26d0639", size = 303177, upload-time = "2024-08-27T20:52:44.789Z" }, - { url = "https://files.pythonhosted.org/packages/56/c3/c85a7e3e0cab635575d3b657f9535443a6f5d20fac1a1911eaa4bbe1aceb/contourpy-1.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c0da700bf58f6e0b65312d0a5e695179a71d0163957fa381bb3c1f72972537c", size = 341735, upload-time = "2024-08-27T20:52:51.05Z" }, - { url = "https://files.pythonhosted.org/packages/dd/8d/20f7a211a7be966a53f474bc90b1a8202e9844b3f1ef85f3ae45a77151ee/contourpy-1.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb8b141bb00fa977d9122636b16aa67d37fd40a3d8b52dd837e536d64b9a4d06", size = 314679, upload-time = "2024-08-27T20:52:58.473Z" }, - { url = "https://files.pythonhosted.org/packages/6e/be/524e377567defac0e21a46e2a529652d165fed130a0d8a863219303cee18/contourpy-1.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3634b5385c6716c258d0419c46d05c8aa7dc8cb70326c9a4fb66b69ad2b52e09", size = 320549, upload-time = "2024-08-27T20:53:06.593Z" }, - { url = "https://files.pythonhosted.org/packages/0f/96/fdb2552a172942d888915f3a6663812e9bc3d359d53dafd4289a0fb462f0/contourpy-1.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0dce35502151b6bd35027ac39ba6e5a44be13a68f55735c3612c568cac3805fd", size = 1263068, upload-time = "2024-08-27T20:53:23.442Z" }, - { url = "https://files.pythonhosted.org/packages/2a/25/632eab595e3140adfa92f1322bf8915f68c932bac468e89eae9974cf1c00/contourpy-1.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aea348f053c645100612b333adc5983d87be69acdc6d77d3169c090d3b01dc35", size = 1322833, upload-time = "2024-08-27T20:53:39.243Z" }, - { url = "https://files.pythonhosted.org/packages/73/e3/69738782e315a1d26d29d71a550dbbe3eb6c653b028b150f70c1a5f4f229/contourpy-1.3.0-cp312-cp312-win32.whl", hash = "sha256:90f73a5116ad1ba7174341ef3ea5c3150ddf20b024b98fb0c3b29034752c8aeb", size = 172681, upload-time = "2024-08-27T20:53:43.05Z" }, - { url = "https://files.pythonhosted.org/packages/0c/89/9830ba00d88e43d15e53d64931e66b8792b46eb25e2050a88fec4a0df3d5/contourpy-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:b11b39aea6be6764f84360fce6c82211a9db32a7c7de8fa6dd5397cf1d079c3b", size = 218283, upload-time = "2024-08-27T20:53:47.232Z" }, - { url = "https://files.pythonhosted.org/packages/53/a1/d20415febfb2267af2d7f06338e82171824d08614084714fb2c1dac9901f/contourpy-1.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3e1c7fa44aaae40a2247e2e8e0627f4bea3dd257014764aa644f319a5f8600e3", size = 267879, upload-time = "2024-08-27T20:53:51.597Z" }, - { url = "https://files.pythonhosted.org/packages/aa/45/5a28a3570ff6218d8bdfc291a272a20d2648104815f01f0177d103d985e1/contourpy-1.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:364174c2a76057feef647c802652f00953b575723062560498dc7930fc9b1cb7", size = 251573, upload-time = "2024-08-27T20:53:55.659Z" }, - { url = "https://files.pythonhosted.org/packages/39/1c/d3f51540108e3affa84f095c8b04f0aa833bb797bc8baa218a952a98117d/contourpy-1.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32b238b3b3b649e09ce9aaf51f0c261d38644bdfa35cbaf7b263457850957a84", size = 303184, upload-time = "2024-08-27T20:54:00.225Z" }, - { url = "https://files.pythonhosted.org/packages/00/56/1348a44fb6c3a558c1a3a0cd23d329d604c99d81bf5a4b58c6b71aab328f/contourpy-1.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d51fca85f9f7ad0b65b4b9fe800406d0d77017d7270d31ec3fb1cc07358fdea0", size = 340262, upload-time = "2024-08-27T20:54:05.234Z" }, - { url = "https://files.pythonhosted.org/packages/2b/23/00d665ba67e1bb666152131da07e0f24c95c3632d7722caa97fb61470eca/contourpy-1.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:732896af21716b29ab3e988d4ce14bc5133733b85956316fb0c56355f398099b", size = 313806, upload-time = "2024-08-27T20:54:09.889Z" }, - { url = "https://files.pythonhosted.org/packages/5a/42/3cf40f7040bb8362aea19af9a5fb7b32ce420f645dd1590edcee2c657cd5/contourpy-1.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d73f659398a0904e125280836ae6f88ba9b178b2fed6884f3b1f95b989d2c8da", size = 319710, upload-time = "2024-08-27T20:54:14.536Z" }, - { url = "https://files.pythonhosted.org/packages/05/32/f3bfa3fc083b25e1a7ae09197f897476ee68e7386e10404bdf9aac7391f0/contourpy-1.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c6c7c2408b7048082932cf4e641fa3b8ca848259212f51c8c59c45aa7ac18f14", size = 1264107, upload-time = "2024-08-27T20:54:29.735Z" }, - { url = "https://files.pythonhosted.org/packages/1c/1e/1019d34473a736664f2439542b890b2dc4c6245f5c0d8cdfc0ccc2cab80c/contourpy-1.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f317576606de89da6b7e0861cf6061f6146ead3528acabff9236458a6ba467f8", size = 1322458, upload-time = "2024-08-27T20:54:45.507Z" }, - { url = "https://files.pythonhosted.org/packages/22/85/4f8bfd83972cf8909a4d36d16b177f7b8bdd942178ea4bf877d4a380a91c/contourpy-1.3.0-cp313-cp313-win32.whl", hash = "sha256:31cd3a85dbdf1fc002280c65caa7e2b5f65e4a973fcdf70dd2fdcb9868069294", size = 172643, upload-time = "2024-08-27T20:55:52.754Z" }, - { url = "https://files.pythonhosted.org/packages/cc/4a/fb3c83c1baba64ba90443626c228ca14f19a87c51975d3b1de308dd2cf08/contourpy-1.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:4553c421929ec95fb07b3aaca0fae668b2eb5a5203d1217ca7c34c063c53d087", size = 218301, upload-time = "2024-08-27T20:55:56.509Z" }, - { url = "https://files.pythonhosted.org/packages/76/65/702f4064f397821fea0cb493f7d3bc95a5d703e20954dce7d6d39bacf378/contourpy-1.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:345af746d7766821d05d72cb8f3845dfd08dd137101a2cb9b24de277d716def8", size = 278972, upload-time = "2024-08-27T20:54:50.347Z" }, - { url = "https://files.pythonhosted.org/packages/80/85/21f5bba56dba75c10a45ec00ad3b8190dbac7fd9a8a8c46c6116c933e9cf/contourpy-1.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3bb3808858a9dc68f6f03d319acd5f1b8a337e6cdda197f02f4b8ff67ad2057b", size = 263375, upload-time = "2024-08-27T20:54:54.909Z" }, - { url = "https://files.pythonhosted.org/packages/0a/64/084c86ab71d43149f91ab3a4054ccf18565f0a8af36abfa92b1467813ed6/contourpy-1.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:420d39daa61aab1221567b42eecb01112908b2cab7f1b4106a52caaec8d36973", size = 307188, upload-time = "2024-08-27T20:55:00.184Z" }, - { url = "https://files.pythonhosted.org/packages/3d/ff/d61a4c288dc42da0084b8d9dc2aa219a850767165d7d9a9c364ff530b509/contourpy-1.3.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d63ee447261e963af02642ffcb864e5a2ee4cbfd78080657a9880b8b1868e18", size = 345644, upload-time = "2024-08-27T20:55:05.673Z" }, - { url = "https://files.pythonhosted.org/packages/ca/aa/00d2313d35ec03f188e8f0786c2fc61f589306e02fdc158233697546fd58/contourpy-1.3.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:167d6c890815e1dac9536dca00828b445d5d0df4d6a8c6adb4a7ec3166812fa8", size = 317141, upload-time = "2024-08-27T20:55:11.047Z" }, - { url = "https://files.pythonhosted.org/packages/8d/6a/b5242c8cb32d87f6abf4f5e3044ca397cb1a76712e3fa2424772e3ff495f/contourpy-1.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:710a26b3dc80c0e4febf04555de66f5fd17e9cf7170a7b08000601a10570bda6", size = 323469, upload-time = "2024-08-27T20:55:15.914Z" }, - { url = "https://files.pythonhosted.org/packages/6f/a6/73e929d43028a9079aca4bde107494864d54f0d72d9db508a51ff0878593/contourpy-1.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:75ee7cb1a14c617f34a51d11fa7524173e56551646828353c4af859c56b766e2", size = 1260894, upload-time = "2024-08-27T20:55:31.553Z" }, - { url = "https://files.pythonhosted.org/packages/2b/1e/1e726ba66eddf21c940821df8cf1a7d15cb165f0682d62161eaa5e93dae1/contourpy-1.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:33c92cdae89ec5135d036e7218e69b0bb2851206077251f04a6c4e0e21f03927", size = 1314829, upload-time = "2024-08-27T20:55:47.837Z" }, - { url = "https://files.pythonhosted.org/packages/b3/e3/b9f72758adb6ef7397327ceb8b9c39c75711affb220e4f53c745ea1d5a9a/contourpy-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a11077e395f67ffc2c44ec2418cfebed032cd6da3022a94fc227b6faf8e2acb8", size = 265518, upload-time = "2024-08-27T20:56:01.333Z" }, - { url = "https://files.pythonhosted.org/packages/ec/22/19f5b948367ab5260fb41d842c7a78dae645603881ea6bc39738bcfcabf6/contourpy-1.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e8134301d7e204c88ed7ab50028ba06c683000040ede1d617298611f9dc6240c", size = 249350, upload-time = "2024-08-27T20:56:05.432Z" }, - { url = "https://files.pythonhosted.org/packages/26/76/0c7d43263dd00ae21a91a24381b7e813d286a3294d95d179ef3a7b9fb1d7/contourpy-1.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e12968fdfd5bb45ffdf6192a590bd8ddd3ba9e58360b29683c6bb71a7b41edca", size = 309167, upload-time = "2024-08-27T20:56:10.034Z" }, - { url = "https://files.pythonhosted.org/packages/96/3b/cadff6773e89f2a5a492c1a8068e21d3fccaf1a1c1df7d65e7c8e3ef60ba/contourpy-1.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fd2a0fc506eccaaa7595b7e1418951f213cf8255be2600f1ea1b61e46a60c55f", size = 348279, upload-time = "2024-08-27T20:56:15.41Z" }, - { url = "https://files.pythonhosted.org/packages/e1/86/158cc43aa549d2081a955ab11c6bdccc7a22caacc2af93186d26f5f48746/contourpy-1.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4cfb5c62ce023dfc410d6059c936dcf96442ba40814aefbfa575425a3a7f19dc", size = 318519, upload-time = "2024-08-27T20:56:21.813Z" }, - { url = "https://files.pythonhosted.org/packages/05/11/57335544a3027e9b96a05948c32e566328e3a2f84b7b99a325b7a06d2b06/contourpy-1.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68a32389b06b82c2fdd68276148d7b9275b5f5cf13e5417e4252f6d1a34f72a2", size = 321922, upload-time = "2024-08-27T20:56:26.983Z" }, - { url = "https://files.pythonhosted.org/packages/0b/e3/02114f96543f4a1b694333b92a6dcd4f8eebbefcc3a5f3bbb1316634178f/contourpy-1.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:94e848a6b83da10898cbf1311a815f770acc9b6a3f2d646f330d57eb4e87592e", size = 1258017, upload-time = "2024-08-27T20:56:42.246Z" }, - { url = "https://files.pythonhosted.org/packages/f3/3b/bfe4c81c6d5881c1c643dde6620be0b42bf8aab155976dd644595cfab95c/contourpy-1.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d78ab28a03c854a873787a0a42254a0ccb3cb133c672f645c9f9c8f3ae9d0800", size = 1316773, upload-time = "2024-08-27T20:56:58.58Z" }, - { url = "https://files.pythonhosted.org/packages/f1/17/c52d2970784383cafb0bd918b6fb036d98d96bbf0bc1befb5d1e31a07a70/contourpy-1.3.0-cp39-cp39-win32.whl", hash = "sha256:81cb5ed4952aae6014bc9d0421dec7c5835c9c8c31cdf51910b708f548cf58e5", size = 171353, upload-time = "2024-08-27T20:57:02.718Z" }, - { url = "https://files.pythonhosted.org/packages/53/23/db9f69676308e094d3c45f20cc52e12d10d64f027541c995d89c11ad5c75/contourpy-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:14e262f67bd7e6eb6880bc564dcda30b15e351a594657e55b7eec94b6ef72843", size = 211817, upload-time = "2024-08-27T20:57:06.328Z" }, - { url = "https://files.pythonhosted.org/packages/d1/09/60e486dc2b64c94ed33e58dcfb6f808192c03dfc5574c016218b9b7680dc/contourpy-1.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fe41b41505a5a33aeaed2a613dccaeaa74e0e3ead6dd6fd3a118fb471644fd6c", size = 261886, upload-time = "2024-08-27T20:57:10.863Z" }, - { url = "https://files.pythonhosted.org/packages/19/20/b57f9f7174fcd439a7789fb47d764974ab646fa34d1790551de386457a8e/contourpy-1.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eca7e17a65f72a5133bdbec9ecf22401c62bcf4821361ef7811faee695799779", size = 311008, upload-time = "2024-08-27T20:57:15.588Z" }, - { url = "https://files.pythonhosted.org/packages/74/fc/5040d42623a1845d4f17a418e590fd7a79ae8cb2bad2b2f83de63c3bdca4/contourpy-1.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1ec4dc6bf570f5b22ed0d7efba0dfa9c5b9e0431aeea7581aa217542d9e809a4", size = 215690, upload-time = "2024-08-27T20:57:19.321Z" }, - { url = "https://files.pythonhosted.org/packages/2b/24/dc3dcd77ac7460ab7e9d2b01a618cb31406902e50e605a8d6091f0a8f7cc/contourpy-1.3.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:00ccd0dbaad6d804ab259820fa7cb0b8036bda0686ef844d24125d8287178ce0", size = 261894, upload-time = "2024-08-27T20:57:23.873Z" }, - { url = "https://files.pythonhosted.org/packages/b1/db/531642a01cfec39d1682e46b5457b07cf805e3c3c584ec27e2a6223f8f6c/contourpy-1.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ca947601224119117f7c19c9cdf6b3ab54c5726ef1d906aa4a69dfb6dd58102", size = 311099, upload-time = "2024-08-27T20:57:28.58Z" }, - { url = "https://files.pythonhosted.org/packages/38/1e/94bda024d629f254143a134eead69e21c836429a2a6ce82209a00ddcb79a/contourpy-1.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6ec93afeb848a0845a18989da3beca3eec2c0f852322efe21af1931147d12cb", size = 215838, upload-time = "2024-08-27T20:57:32.913Z" }, -] - [[package]] name = "contourpy" version = "1.3.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", + "python_full_version < '3.11'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" } wheels = [ @@ -628,18 +438,8 @@ name = "contourpy" version = "1.3.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", ] dependencies = [ { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, @@ -724,7 +524,7 @@ name = "cuda-bindings" version = "12.9.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cuda-pathfinder", marker = "(python_full_version == '3.10.*' and platform_machine != 'ARM64' and sys_platform == 'win32') or (python_full_version == '3.10.*' and sys_platform == 'emscripten') or (python_full_version >= '3.10' and sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { 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" }, @@ -734,7 +534,6 @@ wheels = [ { 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" }, - { url = "https://files.pythonhosted.org/packages/53/1d/f7f2bcffe788aebd4325a34d8a976b219a0751c06707aa89c9e70355ceae/cuda_bindings-12.9.4-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9866ceec83e39337d1a1d64837864c964ad902992478caa288a0bc1be95f21aa", size = 12152579, upload-time = "2025-10-21T14:52:16.731Z" }, ] [[package]] @@ -760,20 +559,15 @@ version = "3.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dill" }, - { name = "filelock", version = "3.19.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "filelock", version = "3.20.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "filelock" }, { name = "fsspec", extra = ["http"] }, - { name = "huggingface-hub", version = "0.36.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "huggingface-hub", version = "1.3.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "huggingface-hub" }, { name = "multiprocess" }, - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { 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 = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "packaging", version = "26.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "packaging" }, { name = "pandas" }, - { name = "pyarrow", version = "21.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "pyarrow", version = "23.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "pyarrow" }, { name = "pyyaml" }, { name = "requests" }, { name = "tqdm" }, @@ -814,131 +608,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec", size = 40708, upload-time = "2025-11-12T09:56:36.333Z" }, ] -[[package]] -name = "filelock" -version = "3.19.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687, upload-time = "2025-08-14T16:56:03.016Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" }, -] - [[package]] name = "filelock" version = "3.20.3" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] sdist = { url = "https://files.pythonhosted.org/packages/1d/65/ce7f1b70157833bf3cb851b556a37d4547ceafc158aa9b34b36782f23696/filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1", size = 19485, upload-time = "2026-01-09T17:55:05.421Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b5/36/7fb70f04bf00bc646cd5bb45aa9eddb15e19437a28b8fb2b4a5249fac770/filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1", size = 16701, upload-time = "2026-01-09T17:55:04.334Z" }, ] -[[package]] -name = "fonttools" -version = "4.60.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/3e/c4/db6a7b5eb0656534c3aa2596c2c5e18830d74f1b9aa5aa8a7dff63a0b11d/fonttools-4.60.2.tar.gz", hash = "sha256:d29552e6b155ebfc685b0aecf8d429cb76c14ab734c22ef5d3dea6fdf800c92c", size = 3562254, upload-time = "2025-12-09T13:38:11.835Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/de/9e10a99fb3070accb8884886a41a4ce54e49bf2fa4fc63f48a6cf2061713/fonttools-4.60.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4e36fadcf7e8ca6e34d490eef86ed638d6fd9c55d2f514b05687622cfc4a7050", size = 2850403, upload-time = "2025-12-09T13:35:53.14Z" }, - { url = "https://files.pythonhosted.org/packages/e4/40/d5b369d1073b134f600a94a287e13b5bdea2191ba6347d813fa3da00e94a/fonttools-4.60.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6e500fc9c04bee749ceabfc20cb4903f6981c2139050d85720ea7ada61b75d5c", size = 2398629, upload-time = "2025-12-09T13:35:56.471Z" }, - { url = "https://files.pythonhosted.org/packages/7c/b5/123819369aaf99d1e4dc49f1de1925d4edc7379114d15a56a7dd2e9d56e6/fonttools-4.60.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22efea5e784e1d1cd8d7b856c198e360a979383ebc6dea4604743b56da1cbc34", size = 4893471, upload-time = "2025-12-09T13:35:58.927Z" }, - { url = "https://files.pythonhosted.org/packages/24/29/f8f8acccb9716b899be4be45e9ce770d6aa76327573863e68448183091b0/fonttools-4.60.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:677aa92d84d335e4d301d8ba04afca6f575316bc647b6782cb0921943fcb6343", size = 4854686, upload-time = "2025-12-09T13:36:01.767Z" }, - { url = "https://files.pythonhosted.org/packages/5a/0d/f3f51d7519f44f2dd5c9a60d7cd41185ebcee4348f073e515a3a93af15ff/fonttools-4.60.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:edd49d3defbf35476e78b61ff737ff5efea811acff68d44233a95a5a48252334", size = 4871233, upload-time = "2025-12-09T13:36:06.094Z" }, - { url = "https://files.pythonhosted.org/packages/cc/3f/4d4fd47d3bc40ab4d76718555185f8adffb5602ea572eac4bbf200c47d22/fonttools-4.60.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:126839492b69cecc5baf2bddcde60caab2ffafd867bbae2a88463fce6078ca3a", size = 4988936, upload-time = "2025-12-09T13:36:08.42Z" }, - { url = "https://files.pythonhosted.org/packages/01/6f/83bbdefa43f2c3ae206fd8c4b9a481f3c913eef871b1ce9a453069239e39/fonttools-4.60.2-cp310-cp310-win32.whl", hash = "sha256:ffcab6f5537136046ca902ed2491ab081ba271b07591b916289b7c27ff845f96", size = 2278044, upload-time = "2025-12-09T13:36:10.641Z" }, - { url = "https://files.pythonhosted.org/packages/d4/04/7d9a137e919d6c9ef26704b7f7b2580d9cfc5139597588227aacebc0e3b7/fonttools-4.60.2-cp310-cp310-win_amd64.whl", hash = "sha256:9c68b287c7ffcd29dd83b5f961004b2a54a862a88825d52ea219c6220309ba45", size = 2326522, upload-time = "2025-12-09T13:36:12.981Z" }, - { url = "https://files.pythonhosted.org/packages/e0/80/b7693d37c02417e162cc83cdd0b19a4f58be82c638b5d4ce4de2dae050c4/fonttools-4.60.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a2aed0a7931401b3875265717a24c726f87ecfedbb7b3426c2ca4d2812e281ae", size = 2847809, upload-time = "2025-12-09T13:36:14.884Z" }, - { url = "https://files.pythonhosted.org/packages/f9/9a/9c2c13bf8a6496ac21607d704e74e9cc68ebf23892cf924c9a8b5c7566b9/fonttools-4.60.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dea6868e9d2b816c9076cfea77754686f3c19149873bdbc5acde437631c15df1", size = 2397302, upload-time = "2025-12-09T13:36:17.151Z" }, - { url = "https://files.pythonhosted.org/packages/56/f6/ce38ff6b2d2d58f6fd981d32f3942365bfa30eadf2b47d93b2d48bf6097f/fonttools-4.60.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2fa27f34950aa1fe0f0b1abe25eed04770a3b3b34ad94e5ace82cc341589678a", size = 5054418, upload-time = "2025-12-09T13:36:19.062Z" }, - { url = "https://files.pythonhosted.org/packages/88/06/5353bea128ff39e857c31de3dd605725b4add956badae0b31bc9a50d4c8e/fonttools-4.60.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:13a53d479d187b09bfaa4a35ffcbc334fc494ff355f0a587386099cb66674f1e", size = 5031652, upload-time = "2025-12-09T13:36:21.206Z" }, - { url = "https://files.pythonhosted.org/packages/71/05/ebca836437f6ebd57edd6428e7eff584e683ff0556ddb17d62e3b731f46c/fonttools-4.60.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fac5e921d3bd0ca3bb8517dced2784f0742bc8ca28579a68b139f04ea323a779", size = 5030321, upload-time = "2025-12-09T13:36:23.515Z" }, - { url = "https://files.pythonhosted.org/packages/57/f9/eb9d2a2ce30c99f840c1cc3940729a970923cf39d770caf88909d98d516b/fonttools-4.60.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:648f4f9186fd7f1f3cd57dbf00d67a583720d5011feca67a5e88b3a491952cfb", size = 5154255, upload-time = "2025-12-09T13:36:25.879Z" }, - { url = "https://files.pythonhosted.org/packages/08/a2/088b6ceba8272a9abb629d3c08f9c1e35e5ce42db0ccfe0c1f9f03e60d1d/fonttools-4.60.2-cp311-cp311-win32.whl", hash = "sha256:3274e15fad871bead5453d5ce02658f6d0c7bc7e7021e2a5b8b04e2f9e40da1a", size = 2276300, upload-time = "2025-12-09T13:36:27.772Z" }, - { url = "https://files.pythonhosted.org/packages/de/2f/8e4c3d908cc5dade7bb1316ce48589f6a24460c1056fd4b8db51f1fa309a/fonttools-4.60.2-cp311-cp311-win_amd64.whl", hash = "sha256:91d058d5a483a1525b367803abb69de0923fbd45e1f82ebd000f5c8aa65bc78e", size = 2327574, upload-time = "2025-12-09T13:36:30.89Z" }, - { url = "https://files.pythonhosted.org/packages/c0/30/530c9eddcd1c39219dc0aaede2b5a4c8ab80e0bb88d1b3ffc12944c4aac3/fonttools-4.60.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e0164b7609d2b5c5dd4e044b8085b7bd7ca7363ef8c269a4ab5b5d4885a426b2", size = 2847196, upload-time = "2025-12-09T13:36:33.262Z" }, - { url = "https://files.pythonhosted.org/packages/19/2f/4077a482836d5bbe3bc9dac1c004d02ee227cf04ed62b0a2dfc41d4f0dfd/fonttools-4.60.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1dd3d9574fc595c1e97faccae0f264dc88784ddf7fbf54c939528378bacc0033", size = 2395842, upload-time = "2025-12-09T13:36:35.47Z" }, - { url = "https://files.pythonhosted.org/packages/dd/05/aae5bb99c5398f8ed4a8b784f023fd9dd3568f0bd5d5b21e35b282550f11/fonttools-4.60.2-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:98d0719f1b11c2817307d2da2e94296a3b2a3503f8d6252a101dca3ee663b917", size = 4949713, upload-time = "2025-12-09T13:36:37.874Z" }, - { url = "https://files.pythonhosted.org/packages/b4/37/49067349fc78ff0efbf09fadefe80ddf41473ca8f8a25400e3770da38328/fonttools-4.60.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9d3ea26957dd07209f207b4fff64c702efe5496de153a54d3b91007ec28904dd", size = 4999907, upload-time = "2025-12-09T13:36:39.853Z" }, - { url = "https://files.pythonhosted.org/packages/16/31/d0f11c758bd0db36b664c92a0f9dfdcc2d7313749aa7d6629805c6946f21/fonttools-4.60.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ee301273b0850f3a515299f212898f37421f42ff9adfc341702582ca5073c13", size = 4939717, upload-time = "2025-12-09T13:36:43.075Z" }, - { url = "https://files.pythonhosted.org/packages/d9/bc/1cff0d69522e561bf1b99bee7c3911c08c25e919584827c3454a64651ce9/fonttools-4.60.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c6eb4694cc3b9c03b7c01d65a9cf35b577f21aa6abdbeeb08d3114b842a58153", size = 5089205, upload-time = "2025-12-09T13:36:45.468Z" }, - { url = "https://files.pythonhosted.org/packages/05/e6/fb174f0069b7122e19828c551298bfd34fdf9480535d2a6ac2ed37afacd3/fonttools-4.60.2-cp312-cp312-win32.whl", hash = "sha256:57f07b616c69c244cc1a5a51072eeef07dddda5ebef9ca5c6e9cf6d59ae65b70", size = 2264674, upload-time = "2025-12-09T13:36:49.238Z" }, - { url = "https://files.pythonhosted.org/packages/75/57/6552ffd6b582d3e6a9f01780c5275e6dfff1e70ca146101733aa1c12a129/fonttools-4.60.2-cp312-cp312-win_amd64.whl", hash = "sha256:310035802392f1fe5a7cf43d76f6ff4a24c919e4c72c0352e7b8176e2584b8a0", size = 2314701, upload-time = "2025-12-09T13:36:51.09Z" }, - { url = "https://files.pythonhosted.org/packages/2e/e4/8381d0ca6b6c6c484660b03517ec5b5b81feeefca3808726dece36c652a9/fonttools-4.60.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2bb5fd231e56ccd7403212636dcccffc96c5ae0d6f9e4721fa0a32cb2e3ca432", size = 2842063, upload-time = "2025-12-09T13:36:53.468Z" }, - { url = "https://files.pythonhosted.org/packages/b4/2c/4367117ee8ff4f4374787a1222da0bd413d80cf3522111f727a7b8f80d1d/fonttools-4.60.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:536b5fab7b6fec78ccf59b5c59489189d9d0a8b0d3a77ed1858be59afb096696", size = 2393792, upload-time = "2025-12-09T13:36:55.742Z" }, - { url = "https://files.pythonhosted.org/packages/49/b7/a76b6dffa193869e54e32ca2f9abb0d0e66784bc8a24e6f86eb093015481/fonttools-4.60.2-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6b9288fc38252ac86a9570f19313ecbc9ff678982e0f27c757a85f1f284d3400", size = 4924020, upload-time = "2025-12-09T13:36:58.229Z" }, - { url = "https://files.pythonhosted.org/packages/bd/4e/0078200e2259f0061c86a74075f507d64c43dd2ab38971956a5c0012d344/fonttools-4.60.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93fcb420791d839ef592eada2b69997c445d0ce9c969b5190f2e16828ec10607", size = 4980070, upload-time = "2025-12-09T13:37:00.311Z" }, - { url = "https://files.pythonhosted.org/packages/85/1f/d87c85a11cb84852c975251581862681e4a0c1c3bd456c648792203f311b/fonttools-4.60.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7916a381b094db4052ac284255186aebf74c5440248b78860cb41e300036f598", size = 4921411, upload-time = "2025-12-09T13:37:02.345Z" }, - { url = "https://files.pythonhosted.org/packages/75/c0/7efad650f5ed8e317c2633133ef3c64917e7adf2e4e2940c798f5d57ec6e/fonttools-4.60.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:58c8c393d5e16b15662cfc2d988491940458aa87894c662154f50c7b49440bef", size = 5063465, upload-time = "2025-12-09T13:37:04.836Z" }, - { url = "https://files.pythonhosted.org/packages/18/a8/750518c4f8cdd79393b386bc81226047ade80239e58c6c9f5dbe1fdd8ea1/fonttools-4.60.2-cp313-cp313-win32.whl", hash = "sha256:19c6e0afd8b02008caa0aa08ab896dfce5d0bcb510c49b2c499541d5cb95a963", size = 2263443, upload-time = "2025-12-09T13:37:06.762Z" }, - { url = "https://files.pythonhosted.org/packages/b8/22/026c60376f165981f80a0e90bd98a79ae3334e9d89a3d046c4d2e265c724/fonttools-4.60.2-cp313-cp313-win_amd64.whl", hash = "sha256:6a500dc59e11b2338c2dba1f8cf11a4ae8be35ec24af8b2628b8759a61457b76", size = 2313800, upload-time = "2025-12-09T13:37:08.713Z" }, - { url = "https://files.pythonhosted.org/packages/7e/ab/7cf1f5204e1366ddf9dc5cdc2789b571feb9eebcee0e3463c3f457df5f52/fonttools-4.60.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9387c532acbe323bbf2a920f132bce3c408a609d5f9dcfc6532fbc7e37f8ccbb", size = 2841690, upload-time = "2025-12-09T13:37:10.696Z" }, - { url = "https://files.pythonhosted.org/packages/00/3c/0bf83c6f863cc8b934952567fa2bf737cfcec8fc4ffb59b3f93820095f89/fonttools-4.60.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e6f1c824185b5b8fb681297f315f26ae55abb0d560c2579242feea8236b1cfef", size = 2392191, upload-time = "2025-12-09T13:37:12.954Z" }, - { url = "https://files.pythonhosted.org/packages/00/f0/40090d148b8907fbea12e9bdf1ff149f30cdf1769e3b2c3e0dbf5106b88d/fonttools-4.60.2-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:55a3129d1e4030b1a30260f1b32fe76781b585fb2111d04a988e141c09eb6403", size = 4873503, upload-time = "2025-12-09T13:37:15.142Z" }, - { url = "https://files.pythonhosted.org/packages/dc/e0/d8b13f99e58b8c293781288ba62fe634f1f0697c9c4c0ae104d3215f3a10/fonttools-4.60.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b196e63753abc33b3b97a6fd6de4b7c4fef5552c0a5ba5e562be214d1e9668e0", size = 4968493, upload-time = "2025-12-09T13:37:18.272Z" }, - { url = "https://files.pythonhosted.org/packages/46/c5/960764d12c92bc225f02401d3067048cb7b282293d9e48e39fe2b0ec38a9/fonttools-4.60.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:de76c8d740fb55745f3b154f0470c56db92ae3be27af8ad6c2e88f1458260c9a", size = 4920015, upload-time = "2025-12-09T13:37:20.334Z" }, - { url = "https://files.pythonhosted.org/packages/4b/ab/839d8caf253d1eef3653ef4d34427d0326d17a53efaec9eb04056b670fff/fonttools-4.60.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6ba6303225c95998c9fda2d410aa792c3d2c1390a09df58d194b03e17583fa25", size = 5031165, upload-time = "2025-12-09T13:37:23.57Z" }, - { url = "https://files.pythonhosted.org/packages/de/bf/3bc862796a6841cbe0725bb5512d272239b809dba631a4b0301df885e62d/fonttools-4.60.2-cp314-cp314-win32.whl", hash = "sha256:0a89728ce10d7c816fedaa5380c06d2793e7a8a634d7ce16810e536c22047384", size = 2267526, upload-time = "2025-12-09T13:37:25.821Z" }, - { url = "https://files.pythonhosted.org/packages/fc/a1/c1909cacf00c76dc37b4743451561fbaaf7db4172c22a6d9394081d114c3/fonttools-4.60.2-cp314-cp314-win_amd64.whl", hash = "sha256:fa8446e6ab8bd778b82cb1077058a2addba86f30de27ab9cc18ed32b34bc8667", size = 2319096, upload-time = "2025-12-09T13:37:28.058Z" }, - { url = "https://files.pythonhosted.org/packages/29/b3/f66e71433f08e3a931b2b31a665aeed17fcc5e6911fc73529c70a232e421/fonttools-4.60.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:4063bc81ac5a4137642865cb63dd270e37b3cd1f55a07c0d6e41d072699ccca2", size = 2925167, upload-time = "2025-12-09T13:37:30.348Z" }, - { url = "https://files.pythonhosted.org/packages/2e/13/eeb491ff743594bbd0bee6e49422c03a59fe9c49002d3cc60eeb77414285/fonttools-4.60.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:ebfdb66fa69732ed604ab8e2a0431e6deff35e933a11d73418cbc7823d03b8e1", size = 2430923, upload-time = "2025-12-09T13:37:32.817Z" }, - { url = "https://files.pythonhosted.org/packages/b2/e5/db609f785e460796e53c4dbc3874a5f4948477f27beceb5e2d24b2537666/fonttools-4.60.2-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50b10b3b1a72d1d54c61b0e59239e1a94c0958f4a06a1febf97ce75388dd91a4", size = 4877729, upload-time = "2025-12-09T13:37:35.858Z" }, - { url = "https://files.pythonhosted.org/packages/5f/d6/85e4484dd4bfb03fee7bd370d65888cccbd3dee2681ee48c869dd5ccb23f/fonttools-4.60.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:beae16891a13b4a2ddec9b39b4de76092a3025e4d1c82362e3042b62295d5e4d", size = 5096003, upload-time = "2025-12-09T13:37:37.862Z" }, - { url = "https://files.pythonhosted.org/packages/30/49/1a98e44b71030b83d2046f981373b80571868259d98e6dae7bc20099dac6/fonttools-4.60.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:522f017fdb3766fd5d2d321774ef351cc6ce88ad4e6ac9efe643e4a2b9d528db", size = 4974410, upload-time = "2025-12-09T13:37:40.166Z" }, - { url = "https://files.pythonhosted.org/packages/42/07/d6f775d950ee8a841012472c7303f8819423d8cc3b4530915de7265ebfa2/fonttools-4.60.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:82cceceaf9c09a965a75b84a4b240dd3768e596ffb65ef53852681606fe7c9ba", size = 5002036, upload-time = "2025-12-09T13:37:42.639Z" }, - { url = "https://files.pythonhosted.org/packages/73/f6/ba6458f83ce1a9f8c3b17bd8f7b8a2205a126aac1055796b7e7cfebbd38f/fonttools-4.60.2-cp314-cp314t-win32.whl", hash = "sha256:bbfbc918a75437fe7e6d64d1b1e1f713237df1cf00f3a36dedae910b2ba01cee", size = 2330985, upload-time = "2025-12-09T13:37:45.157Z" }, - { url = "https://files.pythonhosted.org/packages/91/24/fea0ba4d3a32d4ed1103a1098bfd99dc78b5fe3bb97202920744a37b73dc/fonttools-4.60.2-cp314-cp314t-win_amd64.whl", hash = "sha256:0e5cd9b0830f6550d58c84f3ab151a9892b50c4f9d538c5603c0ce6fff2eb3f1", size = 2396226, upload-time = "2025-12-09T13:37:47.355Z" }, - { url = "https://files.pythonhosted.org/packages/55/ae/a6d9446cb258d3fe87e311c2d7bacf8e8da3e5809fbdc3a8306db4f6b14e/fonttools-4.60.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a3c75b8b42f7f93906bdba9eb1197bb76aecbe9a0a7cf6feec75f7605b5e8008", size = 2857184, upload-time = "2025-12-09T13:37:49.96Z" }, - { url = "https://files.pythonhosted.org/packages/3a/f3/1b41d0b6a8b908aa07f652111155dd653ebbf0b3385e66562556c5206685/fonttools-4.60.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0f86c8c37bc0ec0b9c141d5e90c717ff614e93c187f06d80f18c7057097f71bc", size = 2401877, upload-time = "2025-12-09T13:37:52.307Z" }, - { url = "https://files.pythonhosted.org/packages/71/57/048fd781680c38b05c5463657d0d95d5f2391a51972176e175c01de29d42/fonttools-4.60.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe905403fe59683b0e9a45f234af2866834376b8821f34633b1c76fb731b6311", size = 4878073, upload-time = "2025-12-09T13:37:56.477Z" }, - { url = "https://files.pythonhosted.org/packages/45/bb/363364f052a893cebd3d449588b21244a9d873620fda03ad92702d2e1bc7/fonttools-4.60.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38ce703b60a906e421e12d9e3a7f064883f5e61bb23e8961f4be33cfe578500b", size = 4835385, upload-time = "2025-12-09T13:37:58.882Z" }, - { url = "https://files.pythonhosted.org/packages/1c/38/e392bb930b2436287e6021672345db26441bf1f85f1e98f8b9784334e41d/fonttools-4.60.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9e810c06f3e79185cecf120e58b343ea5a89b54dd695fd644446bcf8c026da5e", size = 4853084, upload-time = "2025-12-09T13:38:01.578Z" }, - { url = "https://files.pythonhosted.org/packages/65/60/0d77faeaecf7a3276a8a6dc49e2274357e6b3ed6a1774e2fdb2a7f142db0/fonttools-4.60.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:38faec8cc1d12122599814d15a402183f5123fb7608dac956121e7c6742aebc5", size = 4971144, upload-time = "2025-12-09T13:38:03.748Z" }, - { url = "https://files.pythonhosted.org/packages/ba/c7/6d3ac3afbcd598631bce24c3ecb919e7d0644a82fea8ddc4454312fc0be6/fonttools-4.60.2-cp39-cp39-win32.whl", hash = "sha256:80a45cf7bf659acb7b36578f300231873daba67bd3ca8cce181c73f861f14a37", size = 1499411, upload-time = "2025-12-09T13:38:05.586Z" }, - { url = "https://files.pythonhosted.org/packages/5a/1c/9dedf6420e23f9fa630bb97941839dddd2e1e57d1b2b85a902378dbe0bd2/fonttools-4.60.2-cp39-cp39-win_amd64.whl", hash = "sha256:c355d5972071938e1b1e0f5a1df001f68ecf1a62f34a3407dc8e0beccf052501", size = 1547943, upload-time = "2025-12-09T13:38:07.604Z" }, - { url = "https://files.pythonhosted.org/packages/79/6c/10280af05b44fafd1dff69422805061fa1af29270bc52dce031ac69540bf/fonttools-4.60.2-py3-none-any.whl", hash = "sha256:73cf92eeda67cf6ff10c8af56fc8f4f07c1647d989a979be9e388a49be26552a", size = 1144610, upload-time = "2025-12-09T13:38:09.5Z" }, -] - [[package]] name = "fonttools" version = "4.61.1" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] 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" }, @@ -1110,22 +792,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, - { url = "https://files.pythonhosted.org/packages/c2/59/ae5cdac87a00962122ea37bb346d41b66aec05f9ce328fa2b9e216f8967b/frozenlist-1.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47", size = 86967, upload-time = "2025-10-06T05:37:55.607Z" }, - { url = "https://files.pythonhosted.org/packages/8a/10/17059b2db5a032fd9323c41c39e9d1f5f9d0c8f04d1e4e3e788573086e61/frozenlist-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca", size = 49984, upload-time = "2025-10-06T05:37:57.049Z" }, - { url = "https://files.pythonhosted.org/packages/4b/de/ad9d82ca8e5fa8f0c636e64606553c79e2b859ad253030b62a21fe9986f5/frozenlist-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068", size = 50240, upload-time = "2025-10-06T05:37:58.145Z" }, - { url = "https://files.pythonhosted.org/packages/4e/45/3dfb7767c2a67d123650122b62ce13c731b6c745bc14424eea67678b508c/frozenlist-1.8.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95", size = 219472, upload-time = "2025-10-06T05:37:59.239Z" }, - { url = "https://files.pythonhosted.org/packages/0b/bf/5bf23d913a741b960d5c1dac7c1985d8a2a1d015772b2d18ea168b08e7ff/frozenlist-1.8.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459", size = 221531, upload-time = "2025-10-06T05:38:00.521Z" }, - { url = "https://files.pythonhosted.org/packages/d0/03/27ec393f3b55860859f4b74cdc8c2a4af3dbf3533305e8eacf48a4fd9a54/frozenlist-1.8.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675", size = 219211, upload-time = "2025-10-06T05:38:01.842Z" }, - { url = "https://files.pythonhosted.org/packages/3a/ad/0fd00c404fa73fe9b169429e9a972d5ed807973c40ab6b3cf9365a33d360/frozenlist-1.8.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61", size = 231775, upload-time = "2025-10-06T05:38:03.384Z" }, - { url = "https://files.pythonhosted.org/packages/8a/c3/86962566154cb4d2995358bc8331bfc4ea19d07db1a96f64935a1607f2b6/frozenlist-1.8.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6", size = 236631, upload-time = "2025-10-06T05:38:04.609Z" }, - { url = "https://files.pythonhosted.org/packages/ea/9e/6ffad161dbd83782d2c66dc4d378a9103b31770cb1e67febf43aea42d202/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5", size = 218632, upload-time = "2025-10-06T05:38:05.917Z" }, - { url = "https://files.pythonhosted.org/packages/58/b2/4677eee46e0a97f9b30735e6ad0bf6aba3e497986066eb68807ac85cf60f/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3", size = 235967, upload-time = "2025-10-06T05:38:07.614Z" }, - { url = "https://files.pythonhosted.org/packages/05/f3/86e75f8639c5a93745ca7addbbc9de6af56aebb930d233512b17e46f6493/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1", size = 228799, upload-time = "2025-10-06T05:38:08.845Z" }, - { url = "https://files.pythonhosted.org/packages/30/00/39aad3a7f0d98f5eb1d99a3c311215674ed87061aecee7851974b335c050/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178", size = 230566, upload-time = "2025-10-06T05:38:10.52Z" }, - { url = "https://files.pythonhosted.org/packages/0d/4d/aa144cac44568d137846ddc4d5210fb5d9719eb1d7ec6fa2728a54b5b94a/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda", size = 217715, upload-time = "2025-10-06T05:38:11.832Z" }, - { url = "https://files.pythonhosted.org/packages/64/4c/8f665921667509d25a0dd72540513bc86b356c95541686f6442a3283019f/frozenlist-1.8.0-cp39-cp39-win32.whl", hash = "sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087", size = 39933, upload-time = "2025-10-06T05:38:13.061Z" }, - { url = "https://files.pythonhosted.org/packages/79/bd/bcc926f87027fad5e59926ff12d136e1082a115025d33c032d1cd69ab377/frozenlist-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a", size = 44121, upload-time = "2025-10-06T05:38:14.572Z" }, - { url = "https://files.pythonhosted.org/packages/4c/07/9c2e4eb7584af4b705237b971b89a4155a8e57599c4483a131a39256a9a0/frozenlist-1.8.0-cp39-cp39-win_arm64.whl", hash = "sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103", size = 40312, upload-time = "2025-10-06T05:38:15.699Z" }, { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, ] @@ -1173,25 +839,12 @@ version = "3.1.46" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "gitdb" }, - { name = "typing-extensions", marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/df/b5/59d16470a1f0dfe8c793f9ef56fd3826093fc52b3bd96d6b9d6c26c7e27b/gitpython-3.1.46.tar.gz", hash = "sha256:400124c7d0ef4ea03f7310ac2fbf7151e09ff97f2a3288d64a440c584a29c37f", size = 215371, upload-time = "2026-01-01T15:37:32.073Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl", hash = "sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058", size = 208620, upload-time = "2026-01-01T15:37:30.574Z" }, ] -[[package]] -name = "griffe" -version = "1.14.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ec/d7/6c09dd7ce4c7837e4cdb11dce980cb45ae3cd87677298dc3b781b6bce7d3/griffe-1.14.0.tar.gz", hash = "sha256:9d2a15c1eca966d68e00517de5d69dd1bc5c9f2335ef6c1775362ba5b8651a13", size = 424684, upload-time = "2025-09-05T15:02:29.167Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/b1/9ff6578d789a89812ff21e4e0f80ffae20a65d5dd84e7a17873fe3b365be/griffe-1.14.0-py3-none-any.whl", hash = "sha256:0e9d52832cccf0f7188cfe585ba962d2674b241c01916d780925df34873bceb0", size = 144439, upload-time = "2025-09-05T15:02:27.511Z" }, -] - [[package]] name = "griffelib" version = "2.0.0" @@ -1244,8 +897,8 @@ name = "httpcore" version = "1.0.9" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "certifi", marker = "python_full_version >= '3.10'" }, - { name = "h11", marker = "python_full_version >= '3.10'" }, + { name = "certifi" }, + { name = "h11" }, ] sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } wheels = [ @@ -1257,69 +910,31 @@ name = "httpx" version = "0.28.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anyio", marker = "python_full_version >= '3.10'" }, - { name = "certifi", marker = "python_full_version >= '3.10'" }, - { name = "httpcore", marker = "python_full_version >= '3.10'" }, - { name = "idna", marker = "python_full_version >= '3.10'" }, + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] -[[package]] -name = "huggingface-hub" -version = "0.36.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "filelock", version = "3.19.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "fsspec", marker = "python_full_version < '3.10'" }, - { name = "hf-xet", marker = "(python_full_version < '3.10' and platform_machine == 'aarch64') or (python_full_version < '3.10' and platform_machine == 'amd64') or (python_full_version < '3.10' and platform_machine == 'arm64') or (python_full_version < '3.10' and platform_machine == 'x86_64')" }, - { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "pyyaml", marker = "python_full_version < '3.10'" }, - { name = "requests", marker = "python_full_version < '3.10'" }, - { name = "tqdm", marker = "python_full_version < '3.10'" }, - { name = "typing-extensions", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/45/54/096903f02ca14eb2670a4d11729da44a026c0bababec8c15f160441124c5/huggingface_hub-0.36.1.tar.gz", hash = "sha256:5a3b8bf87e182ad6f1692c196bb9ec9ade7755311d5d5e792dc45045f77283ad", size = 649681, upload-time = "2026-02-02T10:46:58.287Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/cb/8f5141b3c21d1ecdf87852506eb583fec497c7e9803a168fe4aec64252bb/huggingface_hub-0.36.1-py3-none-any.whl", hash = "sha256:c6fa8a8f7b8559bc624ebb7e218fb72171b30f6049ebe08f8bfc2a44b38ece50", size = 566283, upload-time = "2026-02-02T10:46:56.459Z" }, -] - [[package]] name = "huggingface-hub" version = "1.3.7" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] dependencies = [ - { name = "filelock", version = "3.20.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "fsspec", marker = "python_full_version >= '3.10'" }, - { name = "hf-xet", marker = "(python_full_version >= '3.10' and platform_machine == 'AMD64') or (python_full_version >= '3.10' and platform_machine == 'aarch64') or (python_full_version >= '3.10' and platform_machine == 'amd64') or (python_full_version >= '3.10' and platform_machine == 'arm64') or (python_full_version >= '3.10' and platform_machine == 'x86_64')" }, - { name = "httpx", marker = "python_full_version >= '3.10'" }, - { name = "packaging", version = "26.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "pyyaml", marker = "python_full_version >= '3.10'" }, - { name = "shellingham", marker = "python_full_version >= '3.10'" }, - { name = "tqdm", marker = "python_full_version >= '3.10'" }, - { name = "typer-slim", marker = "python_full_version >= '3.10'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.10'" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, + { name = "httpx" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "shellingham" }, + { name = "tqdm" }, + { name = "typer-slim" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6d/3f/352efd52136bfd8aa9280c6d4a445869226ae2ccd49ddad4f62e90cfd168/huggingface_hub-1.3.7.tar.gz", hash = "sha256:5f86cd48f27131cdbf2882699cbdf7a67dd4cbe89a81edfdc31211f42e4a5fd1", size = 627537, upload-time = "2026-02-02T10:40:10.61Z" } wheels = [ @@ -1333,8 +948,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "antlr4-python3-runtime" }, { name = "omegaconf" }, - { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "packaging", version = "26.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "packaging" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6d/8e/07e42bc434a847154083b315779b0a81d567154504624e181caf2c71cd98/hydra-core-1.3.2.tar.gz", hash = "sha256:8a878ed67216997c3e9d88a8e72e7b4767e81af37afb4ea3334b269a4390a824", size = 3263494, upload-time = "2023-02-23T18:33:43.03Z" } wheels = [ @@ -1350,62 +964,10 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, ] -[[package]] -name = "importlib-metadata" -version = "8.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, -] - -[[package]] -name = "importlib-resources" -version = "6.5.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, -] - [[package]] name = "iniconfig" version = "2.3.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, @@ -1432,49 +994,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" }, ] -[[package]] -name = "jsonschema" -version = "4.25.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "attrs", marker = "python_full_version < '3.10'" }, - { name = "jsonschema-specifications", marker = "python_full_version < '3.10'" }, - { name = "referencing", version = "0.36.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "rpds-py", version = "0.27.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/74/69/f7185de793a29082a9f3c7728268ffb31cb5095131a9c139a74078e27336/jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85", size = 357342, upload-time = "2025-08-18T17:03:50.038Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63", size = 90040, upload-time = "2025-08-18T17:03:48.373Z" }, -] - [[package]] name = "jsonschema" version = "4.26.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] dependencies = [ - { name = "attrs", marker = "python_full_version >= '3.10'" }, - { name = "jsonschema-specifications", marker = "python_full_version >= '3.10'" }, - { name = "referencing", version = "0.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "rpds-py", version = "0.30.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } wheels = [ @@ -1486,137 +1014,17 @@ name = "jsonschema-specifications" version = "2025.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "referencing", version = "0.36.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "referencing", version = "0.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "referencing" }, ] sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, ] -[[package]] -name = "kiwisolver" -version = "1.4.7" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/85/4d/2255e1c76304cbd60b48cee302b66d1dde4468dc5b1160e4b7cb43778f2a/kiwisolver-1.4.7.tar.gz", hash = "sha256:9893ff81bd7107f7b685d3017cc6583daadb4fc26e4a888350df530e41980a60", size = 97286, upload-time = "2024-09-04T09:39:44.302Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/97/14/fc943dd65268a96347472b4fbe5dcc2f6f55034516f80576cd0dd3a8930f/kiwisolver-1.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8a9c83f75223d5e48b0bc9cb1bf2776cf01563e00ade8775ffe13b0b6e1af3a6", size = 122440, upload-time = "2024-09-04T09:03:44.9Z" }, - { url = "https://files.pythonhosted.org/packages/1e/46/e68fed66236b69dd02fcdb506218c05ac0e39745d696d22709498896875d/kiwisolver-1.4.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:58370b1ffbd35407444d57057b57da5d6549d2d854fa30249771775c63b5fe17", size = 65758, upload-time = "2024-09-04T09:03:46.582Z" }, - { url = "https://files.pythonhosted.org/packages/ef/fa/65de49c85838681fc9cb05de2a68067a683717321e01ddafb5b8024286f0/kiwisolver-1.4.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aa0abdf853e09aff551db11fce173e2177d00786c688203f52c87ad7fcd91ef9", size = 64311, upload-time = "2024-09-04T09:03:47.973Z" }, - { url = "https://files.pythonhosted.org/packages/42/9c/cc8d90f6ef550f65443bad5872ffa68f3dee36de4974768628bea7c14979/kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8d53103597a252fb3ab8b5845af04c7a26d5e7ea8122303dd7a021176a87e8b9", size = 1637109, upload-time = "2024-09-04T09:03:49.281Z" }, - { url = "https://files.pythonhosted.org/packages/55/91/0a57ce324caf2ff5403edab71c508dd8f648094b18cfbb4c8cc0fde4a6ac/kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:88f17c5ffa8e9462fb79f62746428dd57b46eb931698e42e990ad63103f35e6c", size = 1617814, upload-time = "2024-09-04T09:03:51.444Z" }, - { url = "https://files.pythonhosted.org/packages/12/5d/c36140313f2510e20207708adf36ae4919416d697ee0236b0ddfb6fd1050/kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a9ca9c710d598fd75ee5de59d5bda2684d9db36a9f50b6125eaea3969c2599", size = 1400881, upload-time = "2024-09-04T09:03:53.357Z" }, - { url = "https://files.pythonhosted.org/packages/56/d0/786e524f9ed648324a466ca8df86298780ef2b29c25313d9a4f16992d3cf/kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f4d742cb7af1c28303a51b7a27aaee540e71bb8e24f68c736f6f2ffc82f2bf05", size = 1512972, upload-time = "2024-09-04T09:03:55.082Z" }, - { url = "https://files.pythonhosted.org/packages/67/5a/77851f2f201e6141d63c10a0708e996a1363efaf9e1609ad0441b343763b/kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e28c7fea2196bf4c2f8d46a0415c77a1c480cc0724722f23d7410ffe9842c407", size = 1444787, upload-time = "2024-09-04T09:03:56.588Z" }, - { url = "https://files.pythonhosted.org/packages/06/5f/1f5eaab84355885e224a6fc8d73089e8713dc7e91c121f00b9a1c58a2195/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e968b84db54f9d42046cf154e02911e39c0435c9801681e3fc9ce8a3c4130278", size = 2199212, upload-time = "2024-09-04T09:03:58.557Z" }, - { url = "https://files.pythonhosted.org/packages/b5/28/9152a3bfe976a0ae21d445415defc9d1cd8614b2910b7614b30b27a47270/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0c18ec74c0472de033e1bebb2911c3c310eef5649133dd0bedf2a169a1b269e5", size = 2346399, upload-time = "2024-09-04T09:04:00.178Z" }, - { url = "https://files.pythonhosted.org/packages/26/f6/453d1904c52ac3b400f4d5e240ac5fec25263716723e44be65f4d7149d13/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8f0ea6da6d393d8b2e187e6a5e3fb81f5862010a40c3945e2c6d12ae45cfb2ad", size = 2308688, upload-time = "2024-09-04T09:04:02.216Z" }, - { url = "https://files.pythonhosted.org/packages/5a/9a/d4968499441b9ae187e81745e3277a8b4d7c60840a52dc9d535a7909fac3/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:f106407dda69ae456dd1227966bf445b157ccc80ba0dff3802bb63f30b74e895", size = 2445493, upload-time = "2024-09-04T09:04:04.571Z" }, - { url = "https://files.pythonhosted.org/packages/07/c9/032267192e7828520dacb64dfdb1d74f292765f179e467c1cba97687f17d/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84ec80df401cfee1457063732d90022f93951944b5b58975d34ab56bb150dfb3", size = 2262191, upload-time = "2024-09-04T09:04:05.969Z" }, - { url = "https://files.pythonhosted.org/packages/6c/ad/db0aedb638a58b2951da46ddaeecf204be8b4f5454df020d850c7fa8dca8/kiwisolver-1.4.7-cp310-cp310-win32.whl", hash = "sha256:71bb308552200fb2c195e35ef05de12f0c878c07fc91c270eb3d6e41698c3bcc", size = 46644, upload-time = "2024-09-04T09:04:07.408Z" }, - { url = "https://files.pythonhosted.org/packages/12/ca/d0f7b7ffbb0be1e7c2258b53554efec1fd652921f10d7d85045aff93ab61/kiwisolver-1.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:44756f9fd339de0fb6ee4f8c1696cfd19b2422e0d70b4cefc1cc7f1f64045a8c", size = 55877, upload-time = "2024-09-04T09:04:08.869Z" }, - { url = "https://files.pythonhosted.org/packages/97/6c/cfcc128672f47a3e3c0d918ecb67830600078b025bfc32d858f2e2d5c6a4/kiwisolver-1.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:78a42513018c41c2ffd262eb676442315cbfe3c44eed82385c2ed043bc63210a", size = 48347, upload-time = "2024-09-04T09:04:10.106Z" }, - { url = "https://files.pythonhosted.org/packages/e9/44/77429fa0a58f941d6e1c58da9efe08597d2e86bf2b2cce6626834f49d07b/kiwisolver-1.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d2b0e12a42fb4e72d509fc994713d099cbb15ebf1103545e8a45f14da2dfca54", size = 122442, upload-time = "2024-09-04T09:04:11.432Z" }, - { url = "https://files.pythonhosted.org/packages/e5/20/8c75caed8f2462d63c7fd65e16c832b8f76cda331ac9e615e914ee80bac9/kiwisolver-1.4.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2a8781ac3edc42ea4b90bc23e7d37b665d89423818e26eb6df90698aa2287c95", size = 65762, upload-time = "2024-09-04T09:04:12.468Z" }, - { url = "https://files.pythonhosted.org/packages/f4/98/fe010f15dc7230f45bc4cf367b012d651367fd203caaa992fd1f5963560e/kiwisolver-1.4.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:46707a10836894b559e04b0fd143e343945c97fd170d69a2d26d640b4e297935", size = 64319, upload-time = "2024-09-04T09:04:13.635Z" }, - { url = "https://files.pythonhosted.org/packages/8b/1b/b5d618f4e58c0675654c1e5051bcf42c776703edb21c02b8c74135541f60/kiwisolver-1.4.7-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef97b8df011141c9b0f6caf23b29379f87dd13183c978a30a3c546d2c47314cb", size = 1334260, upload-time = "2024-09-04T09:04:14.878Z" }, - { url = "https://files.pythonhosted.org/packages/b8/01/946852b13057a162a8c32c4c8d2e9ed79f0bb5d86569a40c0b5fb103e373/kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab58c12a2cd0fc769089e6d38466c46d7f76aced0a1f54c77652446733d2d02", size = 1426589, upload-time = "2024-09-04T09:04:16.514Z" }, - { url = "https://files.pythonhosted.org/packages/70/d1/c9f96df26b459e15cf8a965304e6e6f4eb291e0f7a9460b4ad97b047561e/kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:803b8e1459341c1bb56d1c5c010406d5edec8a0713a0945851290a7930679b51", size = 1541080, upload-time = "2024-09-04T09:04:18.322Z" }, - { url = "https://files.pythonhosted.org/packages/d3/73/2686990eb8b02d05f3de759d6a23a4ee7d491e659007dd4c075fede4b5d0/kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9a9e8a507420fe35992ee9ecb302dab68550dedc0da9e2880dd88071c5fb052", size = 1470049, upload-time = "2024-09-04T09:04:20.266Z" }, - { url = "https://files.pythonhosted.org/packages/a7/4b/2db7af3ed3af7c35f388d5f53c28e155cd402a55432d800c543dc6deb731/kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18077b53dc3bb490e330669a99920c5e6a496889ae8c63b58fbc57c3d7f33a18", size = 1426376, upload-time = "2024-09-04T09:04:22.419Z" }, - { url = "https://files.pythonhosted.org/packages/05/83/2857317d04ea46dc5d115f0df7e676997bbd968ced8e2bd6f7f19cfc8d7f/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6af936f79086a89b3680a280c47ea90b4df7047b5bdf3aa5c524bbedddb9e545", size = 2222231, upload-time = "2024-09-04T09:04:24.526Z" }, - { url = "https://files.pythonhosted.org/packages/0d/b5/866f86f5897cd4ab6d25d22e403404766a123f138bd6a02ecb2cdde52c18/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3abc5b19d24af4b77d1598a585b8a719beb8569a71568b66f4ebe1fb0449460b", size = 2368634, upload-time = "2024-09-04T09:04:25.899Z" }, - { url = "https://files.pythonhosted.org/packages/c1/ee/73de8385403faba55f782a41260210528fe3273d0cddcf6d51648202d6d0/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:933d4de052939d90afbe6e9d5273ae05fb836cc86c15b686edd4b3560cc0ee36", size = 2329024, upload-time = "2024-09-04T09:04:28.523Z" }, - { url = "https://files.pythonhosted.org/packages/a1/e7/cd101d8cd2cdfaa42dc06c433df17c8303d31129c9fdd16c0ea37672af91/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:65e720d2ab2b53f1f72fb5da5fb477455905ce2c88aaa671ff0a447c2c80e8e3", size = 2468484, upload-time = "2024-09-04T09:04:30.547Z" }, - { url = "https://files.pythonhosted.org/packages/e1/72/84f09d45a10bc57a40bb58b81b99d8f22b58b2040c912b7eb97ebf625bf2/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3bf1ed55088f214ba6427484c59553123fdd9b218a42bbc8c6496d6754b1e523", size = 2284078, upload-time = "2024-09-04T09:04:33.218Z" }, - { url = "https://files.pythonhosted.org/packages/d2/d4/71828f32b956612dc36efd7be1788980cb1e66bfb3706e6dec9acad9b4f9/kiwisolver-1.4.7-cp311-cp311-win32.whl", hash = "sha256:4c00336b9dd5ad96d0a558fd18a8b6f711b7449acce4c157e7343ba92dd0cf3d", size = 46645, upload-time = "2024-09-04T09:04:34.371Z" }, - { url = "https://files.pythonhosted.org/packages/a1/65/d43e9a20aabcf2e798ad1aff6c143ae3a42cf506754bcb6a7ed8259c8425/kiwisolver-1.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:929e294c1ac1e9f615c62a4e4313ca1823ba37326c164ec720a803287c4c499b", size = 56022, upload-time = "2024-09-04T09:04:35.786Z" }, - { url = "https://files.pythonhosted.org/packages/35/b3/9f75a2e06f1b4ca00b2b192bc2b739334127d27f1d0625627ff8479302ba/kiwisolver-1.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:e33e8fbd440c917106b237ef1a2f1449dfbb9b6f6e1ce17c94cd6a1e0d438376", size = 48536, upload-time = "2024-09-04T09:04:37.525Z" }, - { url = "https://files.pythonhosted.org/packages/97/9c/0a11c714cf8b6ef91001c8212c4ef207f772dd84540104952c45c1f0a249/kiwisolver-1.4.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:5360cc32706dab3931f738d3079652d20982511f7c0ac5711483e6eab08efff2", size = 121808, upload-time = "2024-09-04T09:04:38.637Z" }, - { url = "https://files.pythonhosted.org/packages/f2/d8/0fe8c5f5d35878ddd135f44f2af0e4e1d379e1c7b0716f97cdcb88d4fd27/kiwisolver-1.4.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:942216596dc64ddb25adb215c3c783215b23626f8d84e8eff8d6d45c3f29f75a", size = 65531, upload-time = "2024-09-04T09:04:39.694Z" }, - { url = "https://files.pythonhosted.org/packages/80/c5/57fa58276dfdfa612241d640a64ca2f76adc6ffcebdbd135b4ef60095098/kiwisolver-1.4.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:48b571ecd8bae15702e4f22d3ff6a0f13e54d3d00cd25216d5e7f658242065ee", size = 63894, upload-time = "2024-09-04T09:04:41.6Z" }, - { url = "https://files.pythonhosted.org/packages/8b/e9/26d3edd4c4ad1c5b891d8747a4f81b1b0aba9fb9721de6600a4adc09773b/kiwisolver-1.4.7-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad42ba922c67c5f219097b28fae965e10045ddf145d2928bfac2eb2e17673640", size = 1369296, upload-time = "2024-09-04T09:04:42.886Z" }, - { url = "https://files.pythonhosted.org/packages/b6/67/3f4850b5e6cffb75ec40577ddf54f7b82b15269cc5097ff2e968ee32ea7d/kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:612a10bdae23404a72941a0fc8fa2660c6ea1217c4ce0dbcab8a8f6543ea9e7f", size = 1461450, upload-time = "2024-09-04T09:04:46.284Z" }, - { url = "https://files.pythonhosted.org/packages/52/be/86cbb9c9a315e98a8dc6b1d23c43cffd91d97d49318854f9c37b0e41cd68/kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e838bba3a3bac0fe06d849d29772eb1afb9745a59710762e4ba3f4cb8424483", size = 1579168, upload-time = "2024-09-04T09:04:47.91Z" }, - { url = "https://files.pythonhosted.org/packages/0f/00/65061acf64bd5fd34c1f4ae53f20b43b0a017a541f242a60b135b9d1e301/kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:22f499f6157236c19f4bbbd472fa55b063db77a16cd74d49afe28992dff8c258", size = 1507308, upload-time = "2024-09-04T09:04:49.465Z" }, - { url = "https://files.pythonhosted.org/packages/21/e4/c0b6746fd2eb62fe702118b3ca0cb384ce95e1261cfada58ff693aeec08a/kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693902d433cf585133699972b6d7c42a8b9f8f826ebcaf0132ff55200afc599e", size = 1464186, upload-time = "2024-09-04T09:04:50.949Z" }, - { url = "https://files.pythonhosted.org/packages/0a/0f/529d0a9fffb4d514f2782c829b0b4b371f7f441d61aa55f1de1c614c4ef3/kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4e77f2126c3e0b0d055f44513ed349038ac180371ed9b52fe96a32aa071a5107", size = 2247877, upload-time = "2024-09-04T09:04:52.388Z" }, - { url = "https://files.pythonhosted.org/packages/d1/e1/66603ad779258843036d45adcbe1af0d1a889a07af4635f8b4ec7dccda35/kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:657a05857bda581c3656bfc3b20e353c232e9193eb167766ad2dc58b56504948", size = 2404204, upload-time = "2024-09-04T09:04:54.385Z" }, - { url = "https://files.pythonhosted.org/packages/8d/61/de5fb1ca7ad1f9ab7970e340a5b833d735df24689047de6ae71ab9d8d0e7/kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4bfa75a048c056a411f9705856abfc872558e33c055d80af6a380e3658766038", size = 2352461, upload-time = "2024-09-04T09:04:56.307Z" }, - { url = "https://files.pythonhosted.org/packages/ba/d2/0edc00a852e369827f7e05fd008275f550353f1f9bcd55db9363d779fc63/kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:34ea1de54beef1c104422d210c47c7d2a4999bdecf42c7b5718fbe59a4cac383", size = 2501358, upload-time = "2024-09-04T09:04:57.922Z" }, - { url = "https://files.pythonhosted.org/packages/84/15/adc15a483506aec6986c01fb7f237c3aec4d9ed4ac10b756e98a76835933/kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:90da3b5f694b85231cf93586dad5e90e2d71b9428f9aad96952c99055582f520", size = 2314119, upload-time = "2024-09-04T09:04:59.332Z" }, - { url = "https://files.pythonhosted.org/packages/36/08/3a5bb2c53c89660863a5aa1ee236912269f2af8762af04a2e11df851d7b2/kiwisolver-1.4.7-cp312-cp312-win32.whl", hash = "sha256:18e0cca3e008e17fe9b164b55735a325140a5a35faad8de92dd80265cd5eb80b", size = 46367, upload-time = "2024-09-04T09:05:00.804Z" }, - { url = "https://files.pythonhosted.org/packages/19/93/c05f0a6d825c643779fc3c70876bff1ac221f0e31e6f701f0e9578690d70/kiwisolver-1.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:58cb20602b18f86f83a5c87d3ee1c766a79c0d452f8def86d925e6c60fbf7bfb", size = 55884, upload-time = "2024-09-04T09:05:01.924Z" }, - { url = "https://files.pythonhosted.org/packages/d2/f9/3828d8f21b6de4279f0667fb50a9f5215e6fe57d5ec0d61905914f5b6099/kiwisolver-1.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:f5a8b53bdc0b3961f8b6125e198617c40aeed638b387913bf1ce78afb1b0be2a", size = 48528, upload-time = "2024-09-04T09:05:02.983Z" }, - { url = "https://files.pythonhosted.org/packages/c4/06/7da99b04259b0f18b557a4effd1b9c901a747f7fdd84cf834ccf520cb0b2/kiwisolver-1.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2e6039dcbe79a8e0f044f1c39db1986a1b8071051efba3ee4d74f5b365f5226e", size = 121913, upload-time = "2024-09-04T09:05:04.072Z" }, - { url = "https://files.pythonhosted.org/packages/97/f5/b8a370d1aa593c17882af0a6f6755aaecd643640c0ed72dcfd2eafc388b9/kiwisolver-1.4.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a1ecf0ac1c518487d9d23b1cd7139a6a65bc460cd101ab01f1be82ecf09794b6", size = 65627, upload-time = "2024-09-04T09:05:05.119Z" }, - { url = "https://files.pythonhosted.org/packages/2a/fc/6c0374f7503522539e2d4d1b497f5ebad3f8ed07ab51aed2af988dd0fb65/kiwisolver-1.4.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ab9ccab2b5bd5702ab0803676a580fffa2aa178c2badc5557a84cc943fcf750", size = 63888, upload-time = "2024-09-04T09:05:06.191Z" }, - { url = "https://files.pythonhosted.org/packages/bf/3e/0b7172793d0f41cae5c923492da89a2ffcd1adf764c16159ca047463ebd3/kiwisolver-1.4.7-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f816dd2277f8d63d79f9c8473a79fe54047bc0467754962840782c575522224d", size = 1369145, upload-time = "2024-09-04T09:05:07.919Z" }, - { url = "https://files.pythonhosted.org/packages/77/92/47d050d6f6aced2d634258123f2688fbfef8ded3c5baf2c79d94d91f1f58/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf8bcc23ceb5a1b624572a1623b9f79d2c3b337c8c455405ef231933a10da379", size = 1461448, upload-time = "2024-09-04T09:05:10.01Z" }, - { url = "https://files.pythonhosted.org/packages/9c/1b/8f80b18e20b3b294546a1adb41701e79ae21915f4175f311a90d042301cf/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dea0bf229319828467d7fca8c7c189780aa9ff679c94539eed7532ebe33ed37c", size = 1578750, upload-time = "2024-09-04T09:05:11.598Z" }, - { url = "https://files.pythonhosted.org/packages/a4/fe/fe8e72f3be0a844f257cadd72689c0848c6d5c51bc1d60429e2d14ad776e/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c06a4c7cf15ec739ce0e5971b26c93638730090add60e183530d70848ebdd34", size = 1507175, upload-time = "2024-09-04T09:05:13.22Z" }, - { url = "https://files.pythonhosted.org/packages/39/fa/cdc0b6105d90eadc3bee525fecc9179e2b41e1ce0293caaf49cb631a6aaf/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:913983ad2deb14e66d83c28b632fd35ba2b825031f2fa4ca29675e665dfecbe1", size = 1463963, upload-time = "2024-09-04T09:05:15.925Z" }, - { url = "https://files.pythonhosted.org/packages/6e/5c/0c03c4e542720c6177d4f408e56d1c8315899db72d46261a4e15b8b33a41/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5337ec7809bcd0f424c6b705ecf97941c46279cf5ed92311782c7c9c2026f07f", size = 2248220, upload-time = "2024-09-04T09:05:17.434Z" }, - { url = "https://files.pythonhosted.org/packages/3d/ee/55ef86d5a574f4e767df7da3a3a7ff4954c996e12d4fbe9c408170cd7dcc/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c26ed10c4f6fa6ddb329a5120ba3b6db349ca192ae211e882970bfc9d91420b", size = 2404463, upload-time = "2024-09-04T09:05:18.997Z" }, - { url = "https://files.pythonhosted.org/packages/0f/6d/73ad36170b4bff4825dc588acf4f3e6319cb97cd1fb3eb04d9faa6b6f212/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c619b101e6de2222c1fcb0531e1b17bbffbe54294bfba43ea0d411d428618c27", size = 2352842, upload-time = "2024-09-04T09:05:21.299Z" }, - { url = "https://files.pythonhosted.org/packages/0b/16/fa531ff9199d3b6473bb4d0f47416cdb08d556c03b8bc1cccf04e756b56d/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:073a36c8273647592ea332e816e75ef8da5c303236ec0167196793eb1e34657a", size = 2501635, upload-time = "2024-09-04T09:05:23.588Z" }, - { url = "https://files.pythonhosted.org/packages/78/7e/aa9422e78419db0cbe75fb86d8e72b433818f2e62e2e394992d23d23a583/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3ce6b2b0231bda412463e152fc18335ba32faf4e8c23a754ad50ffa70e4091ee", size = 2314556, upload-time = "2024-09-04T09:05:25.907Z" }, - { url = "https://files.pythonhosted.org/packages/a8/b2/15f7f556df0a6e5b3772a1e076a9d9f6c538ce5f05bd590eca8106508e06/kiwisolver-1.4.7-cp313-cp313-win32.whl", hash = "sha256:f4c9aee212bc89d4e13f58be11a56cc8036cabad119259d12ace14b34476fd07", size = 46364, upload-time = "2024-09-04T09:05:27.184Z" }, - { url = "https://files.pythonhosted.org/packages/0b/db/32e897e43a330eee8e4770bfd2737a9584b23e33587a0812b8e20aac38f7/kiwisolver-1.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:8a3ec5aa8e38fc4c8af308917ce12c536f1c88452ce554027e55b22cbbfbff76", size = 55887, upload-time = "2024-09-04T09:05:28.372Z" }, - { url = "https://files.pythonhosted.org/packages/c8/a4/df2bdca5270ca85fd25253049eb6708d4127be2ed0e5c2650217450b59e9/kiwisolver-1.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:76c8094ac20ec259471ac53e774623eb62e6e1f56cd8690c67ce6ce4fcb05650", size = 48530, upload-time = "2024-09-04T09:05:30.225Z" }, - { url = "https://files.pythonhosted.org/packages/11/88/37ea0ea64512997b13d69772db8dcdc3bfca5442cda3a5e4bb943652ee3e/kiwisolver-1.4.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3f9362ecfca44c863569d3d3c033dbe8ba452ff8eed6f6b5806382741a1334bd", size = 122449, upload-time = "2024-09-04T09:05:55.311Z" }, - { url = "https://files.pythonhosted.org/packages/4e/45/5a5c46078362cb3882dcacad687c503089263c017ca1241e0483857791eb/kiwisolver-1.4.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e8df2eb9b2bac43ef8b082e06f750350fbbaf2887534a5be97f6cf07b19d9583", size = 65757, upload-time = "2024-09-04T09:05:56.906Z" }, - { url = "https://files.pythonhosted.org/packages/8a/be/a6ae58978772f685d48dd2e84460937761c53c4bbd84e42b0336473d9775/kiwisolver-1.4.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f32d6edbc638cde7652bd690c3e728b25332acbadd7cad670cc4a02558d9c417", size = 64312, upload-time = "2024-09-04T09:05:58.384Z" }, - { url = "https://files.pythonhosted.org/packages/f4/04/18ef6f452d311e1e1eb180c9bf5589187fa1f042db877e6fe443ef10099c/kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e2e6c39bd7b9372b0be21456caab138e8e69cc0fc1190a9dfa92bd45a1e6e904", size = 1626966, upload-time = "2024-09-04T09:05:59.855Z" }, - { url = "https://files.pythonhosted.org/packages/21/b1/40655f6c3fa11ce740e8a964fa8e4c0479c87d6a7944b95af799c7a55dfe/kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dda56c24d869b1193fcc763f1284b9126550eaf84b88bbc7256e15028f19188a", size = 1607044, upload-time = "2024-09-04T09:06:02.16Z" }, - { url = "https://files.pythonhosted.org/packages/fd/93/af67dbcfb9b3323bbd2c2db1385a7139d8f77630e4a37bb945b57188eb2d/kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79849239c39b5e1fd906556c474d9b0439ea6792b637511f3fe3a41158d89ca8", size = 1391879, upload-time = "2024-09-04T09:06:03.908Z" }, - { url = "https://files.pythonhosted.org/packages/40/6f/d60770ef98e77b365d96061d090c0cd9e23418121c55fff188fa4bdf0b54/kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e3bc157fed2a4c02ec468de4ecd12a6e22818d4f09cde2c31ee3226ffbefab2", size = 1504751, upload-time = "2024-09-04T09:06:05.58Z" }, - { url = "https://files.pythonhosted.org/packages/fa/3a/5f38667d313e983c432f3fcd86932177519ed8790c724e07d77d1de0188a/kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3da53da805b71e41053dc670f9a820d1157aae77b6b944e08024d17bcd51ef88", size = 1436990, upload-time = "2024-09-04T09:06:08.126Z" }, - { url = "https://files.pythonhosted.org/packages/cb/3b/1520301a47326e6a6043b502647e42892be33b3f051e9791cc8bb43f1a32/kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8705f17dfeb43139a692298cb6637ee2e59c0194538153e83e9ee0c75c2eddde", size = 2191122, upload-time = "2024-09-04T09:06:10.345Z" }, - { url = "https://files.pythonhosted.org/packages/cf/c4/eb52da300c166239a2233f1f9c4a1b767dfab98fae27681bfb7ea4873cb6/kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:82a5c2f4b87c26bb1a0ef3d16b5c4753434633b83d365cc0ddf2770c93829e3c", size = 2338126, upload-time = "2024-09-04T09:06:12.321Z" }, - { url = "https://files.pythonhosted.org/packages/1a/cb/42b92fd5eadd708dd9107c089e817945500685f3437ce1fd387efebc6d6e/kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce8be0466f4c0d585cdb6c1e2ed07232221df101a4c6f28821d2aa754ca2d9e2", size = 2298313, upload-time = "2024-09-04T09:06:14.562Z" }, - { url = "https://files.pythonhosted.org/packages/4f/eb/be25aa791fe5fc75a8b1e0c965e00f942496bc04635c9aae8035f6b76dcd/kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:409afdfe1e2e90e6ee7fc896f3df9a7fec8e793e58bfa0d052c8a82f99c37abb", size = 2437784, upload-time = "2024-09-04T09:06:16.767Z" }, - { url = "https://files.pythonhosted.org/packages/c5/22/30a66be7f3368d76ff95689e1c2e28d382383952964ab15330a15d8bfd03/kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5b9c3f4ee0b9a439d2415012bd1b1cc2df59e4d6a9939f4d669241d30b414327", size = 2253988, upload-time = "2024-09-04T09:06:18.705Z" }, - { url = "https://files.pythonhosted.org/packages/35/d3/5f2ecb94b5211c8a04f218a76133cc8d6d153b0f9cd0b45fad79907f0689/kiwisolver-1.4.7-cp39-cp39-win32.whl", hash = "sha256:a79ae34384df2b615eefca647a2873842ac3b596418032bef9a7283675962644", size = 46980, upload-time = "2024-09-04T09:06:20.106Z" }, - { url = "https://files.pythonhosted.org/packages/ef/17/cd10d020578764ea91740204edc6b3236ed8106228a46f568d716b11feb2/kiwisolver-1.4.7-cp39-cp39-win_amd64.whl", hash = "sha256:cf0438b42121a66a3a667de17e779330fc0f20b0d97d59d2f2121e182b0505e4", size = 55847, upload-time = "2024-09-04T09:06:21.407Z" }, - { url = "https://files.pythonhosted.org/packages/91/84/32232502020bd78d1d12be7afde15811c64a95ed1f606c10456db4e4c3ac/kiwisolver-1.4.7-cp39-cp39-win_arm64.whl", hash = "sha256:764202cc7e70f767dab49e8df52c7455e8de0df5d858fa801a11aa0d882ccf3f", size = 48494, upload-time = "2024-09-04T09:06:22.648Z" }, - { url = "https://files.pythonhosted.org/packages/ac/59/741b79775d67ab67ced9bb38552da688c0305c16e7ee24bba7a2be253fb7/kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:94252291e3fe68001b1dd747b4c0b3be12582839b95ad4d1b641924d68fd4643", size = 59491, upload-time = "2024-09-04T09:06:24.188Z" }, - { url = "https://files.pythonhosted.org/packages/58/cc/fb239294c29a5656e99e3527f7369b174dd9cc7c3ef2dea7cb3c54a8737b/kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b7dfa3b546da08a9f622bb6becdb14b3e24aaa30adba66749d38f3cc7ea9706", size = 57648, upload-time = "2024-09-04T09:06:25.559Z" }, - { url = "https://files.pythonhosted.org/packages/3b/ef/2f009ac1f7aab9f81efb2d837301d255279d618d27b6015780115ac64bdd/kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd3de6481f4ed8b734da5df134cd5a6a64fe32124fe83dde1e5b5f29fe30b1e6", size = 84257, upload-time = "2024-09-04T09:06:27.038Z" }, - { url = "https://files.pythonhosted.org/packages/81/e1/c64f50987f85b68b1c52b464bb5bf73e71570c0f7782d626d1eb283ad620/kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a91b5f9f1205845d488c928e8570dcb62b893372f63b8b6e98b863ebd2368ff2", size = 80906, upload-time = "2024-09-04T09:06:28.48Z" }, - { url = "https://files.pythonhosted.org/packages/fd/71/1687c5c0a0be2cee39a5c9c389e546f9c6e215e46b691d00d9f646892083/kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40fa14dbd66b8b8f470d5fc79c089a66185619d31645f9b0773b88b19f7223c4", size = 79951, upload-time = "2024-09-04T09:06:29.966Z" }, - { url = "https://files.pythonhosted.org/packages/ea/8b/d7497df4a1cae9367adf21665dd1f896c2a7aeb8769ad77b662c5e2bcce7/kiwisolver-1.4.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:eb542fe7933aa09d8d8f9d9097ef37532a7df6497819d16efe4359890a2f417a", size = 55715, upload-time = "2024-09-04T09:06:31.489Z" }, - { url = "https://files.pythonhosted.org/packages/d5/df/ce37d9b26f07ab90880923c94d12a6ff4d27447096b4c849bfc4339ccfdf/kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8b01aac285f91ca889c800042c35ad3b239e704b150cfd3382adfc9dcc780e39", size = 58666, upload-time = "2024-09-04T09:06:43.756Z" }, - { url = "https://files.pythonhosted.org/packages/b0/d3/e4b04f43bc629ac8e186b77b2b1a251cdfa5b7610fa189dc0db622672ce6/kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:48be928f59a1f5c8207154f935334d374e79f2b5d212826307d072595ad76a2e", size = 57088, upload-time = "2024-09-04T09:06:45.406Z" }, - { url = "https://files.pythonhosted.org/packages/30/1c/752df58e2d339e670a535514d2db4fe8c842ce459776b8080fbe08ebb98e/kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f37cfe618a117e50d8c240555331160d73d0411422b59b5ee217843d7b693608", size = 84321, upload-time = "2024-09-04T09:06:47.557Z" }, - { url = "https://files.pythonhosted.org/packages/f0/f8/fe6484e847bc6e238ec9f9828089fb2c0bb53f2f5f3a79351fde5b565e4f/kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:599b5c873c63a1f6ed7eead644a8a380cfbdf5db91dcb6f85707aaab213b1674", size = 80776, upload-time = "2024-09-04T09:06:49.235Z" }, - { url = "https://files.pythonhosted.org/packages/9b/57/d7163c0379f250ef763aba85330a19feefb5ce6cb541ade853aaba881524/kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:801fa7802e5cfabe3ab0c81a34c323a319b097dfb5004be950482d882f3d7225", size = 79984, upload-time = "2024-09-04T09:06:51.336Z" }, - { url = "https://files.pythonhosted.org/packages/8c/95/4a103776c265d13b3d2cd24fb0494d4e04ea435a8ef97e1b2c026d43250b/kiwisolver-1.4.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0c6c43471bc764fad4bc99c5c2d6d16a676b1abf844ca7c8702bdae92df01ee0", size = 55811, upload-time = "2024-09-04T09:06:53.078Z" }, -] - [[package]] name = "kiwisolver" version = "1.4.9" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] 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" }, @@ -1721,41 +1129,10 @@ wheels = [ { 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" }, ] -[[package]] -name = "markdown" -version = "3.9" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8d/37/02347f6d6d8279247a5837082ebc26fc0d5aaeaf75aa013fcbb433c777ab/markdown-3.9.tar.gz", hash = "sha256:d2900fe1782bd33bdbbd56859defef70c2e78fc46668f8eb9df3128138f2cb6a", size = 364585, upload-time = "2025-09-04T20:25:22.885Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/70/ae/44c4a6a4cbb496d93c6257954260fe3a6e91b7bed2240e5dad2a717f5111/markdown-3.9-py3-none-any.whl", hash = "sha256:9f4d91ed810864ea88a6f32c07ba8bee1346c0cc1f6b1f9f6c822f2a9667d280", size = 107441, upload-time = "2025-09-04T20:25:21.784Z" }, -] - [[package]] name = "markdown" version = "3.10.2" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] sdist = { url = "https://files.pythonhosted.org/packages/2b/f4/69fa6ed85ae003c2378ffa8f6d2e3234662abd02c10d216c0ba96081a238/markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", size = 368805, upload-time = "2026-02-09T14:57:26.942Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" }, @@ -1844,114 +1221,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, - { url = "https://files.pythonhosted.org/packages/56/23/0d8c13a44bde9154821586520840643467aee574d8ce79a17da539ee7fed/markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26", size = 11623, upload-time = "2025-09-27T18:37:29.296Z" }, - { url = "https://files.pythonhosted.org/packages/fd/23/07a2cb9a8045d5f3f0890a8c3bc0859d7a47bfd9a560b563899bec7b72ed/markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc", size = 12049, upload-time = "2025-09-27T18:37:30.234Z" }, - { url = "https://files.pythonhosted.org/packages/bc/e4/6be85eb81503f8e11b61c0b6369b6e077dcf0a74adbd9ebf6b349937b4e9/markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c", size = 21923, upload-time = "2025-09-27T18:37:31.177Z" }, - { url = "https://files.pythonhosted.org/packages/6f/bc/4dc914ead3fe6ddaef035341fee0fc956949bbd27335b611829292b89ee2/markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42", size = 20543, upload-time = "2025-09-27T18:37:32.168Z" }, - { url = "https://files.pythonhosted.org/packages/89/6e/5fe81fbcfba4aef4093d5f856e5c774ec2057946052d18d168219b7bd9f9/markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b", size = 20585, upload-time = "2025-09-27T18:37:33.166Z" }, - { url = "https://files.pythonhosted.org/packages/f6/f6/e0e5a3d3ae9c4020f696cd055f940ef86b64fe88de26f3a0308b9d3d048c/markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758", size = 21387, upload-time = "2025-09-27T18:37:34.185Z" }, - { url = "https://files.pythonhosted.org/packages/c8/25/651753ef4dea08ea790f4fbb65146a9a44a014986996ca40102e237aa49a/markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2", size = 20133, upload-time = "2025-09-27T18:37:35.138Z" }, - { url = "https://files.pythonhosted.org/packages/dc/0a/c3cf2b4fef5f0426e8a6d7fce3cb966a17817c568ce59d76b92a233fdbec/markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d", size = 20588, upload-time = "2025-09-27T18:37:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/cd/1b/a7782984844bd519ad4ffdbebbba2671ec5d0ebbeac34736c15fb86399e8/markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7", size = 14566, upload-time = "2025-09-27T18:37:37.09Z" }, - { url = "https://files.pythonhosted.org/packages/18/1f/8d9c20e1c9440e215a44be5ab64359e207fcb4f675543f1cf9a2a7f648d0/markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e", size = 15053, upload-time = "2025-09-27T18:37:38.054Z" }, - { url = "https://files.pythonhosted.org/packages/4e/d3/fe08482b5cd995033556d45041a4f4e76e7f0521112a9c9991d40d39825f/markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8", size = 13928, upload-time = "2025-09-27T18:37:39.037Z" }, -] - -[[package]] -name = "matplotlib" -version = "3.9.4" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "contourpy", version = "1.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "cycler", marker = "python_full_version < '3.10'" }, - { name = "fonttools", version = "4.60.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "importlib-resources", marker = "python_full_version < '3.10'" }, - { name = "kiwisolver", version = "1.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "pillow", version = "11.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "pyparsing", marker = "python_full_version < '3.10'" }, - { name = "python-dateutil", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/17/1747b4154034befd0ed33b52538f5eb7752d05bb51c5e2a31470c3bc7d52/matplotlib-3.9.4.tar.gz", hash = "sha256:1e00e8be7393cbdc6fedfa8a6fba02cf3e83814b285db1c60b906a023ba41bc3", size = 36106529, upload-time = "2024-12-13T05:56:34.184Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/94/27d2e2c30d54b56c7b764acc1874a909e34d1965a427fc7092bb6a588b63/matplotlib-3.9.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c5fdd7abfb706dfa8d307af64a87f1a862879ec3cd8d0ec8637458f0885b9c50", size = 7885089, upload-time = "2024-12-13T05:54:24.224Z" }, - { url = "https://files.pythonhosted.org/packages/c6/25/828273307e40a68eb8e9df832b6b2aaad075864fdc1de4b1b81e40b09e48/matplotlib-3.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d89bc4e85e40a71d1477780366c27fb7c6494d293e1617788986f74e2a03d7ff", size = 7770600, upload-time = "2024-12-13T05:54:27.214Z" }, - { url = "https://files.pythonhosted.org/packages/f2/65/f841a422ec994da5123368d76b126acf4fc02ea7459b6e37c4891b555b83/matplotlib-3.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ddf9f3c26aae695c5daafbf6b94e4c1a30d6cd617ba594bbbded3b33a1fcfa26", size = 8200138, upload-time = "2024-12-13T05:54:29.497Z" }, - { url = "https://files.pythonhosted.org/packages/07/06/272aca07a38804d93b6050813de41ca7ab0e29ba7a9dd098e12037c919a9/matplotlib-3.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18ebcf248030173b59a868fda1fe42397253f6698995b55e81e1f57431d85e50", size = 8312711, upload-time = "2024-12-13T05:54:34.396Z" }, - { url = "https://files.pythonhosted.org/packages/98/37/f13e23b233c526b7e27ad61be0a771894a079e0f7494a10d8d81557e0e9a/matplotlib-3.9.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:974896ec43c672ec23f3f8c648981e8bc880ee163146e0312a9b8def2fac66f5", size = 9090622, upload-time = "2024-12-13T05:54:36.808Z" }, - { url = "https://files.pythonhosted.org/packages/4f/8c/b1f5bd2bd70e60f93b1b54c4d5ba7a992312021d0ddddf572f9a1a6d9348/matplotlib-3.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:4598c394ae9711cec135639374e70871fa36b56afae17bdf032a345be552a88d", size = 7828211, upload-time = "2024-12-13T05:54:40.596Z" }, - { url = "https://files.pythonhosted.org/packages/74/4b/65be7959a8fa118a3929b49a842de5b78bb55475236fcf64f3e308ff74a0/matplotlib-3.9.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d4dd29641d9fb8bc4492420c5480398dd40a09afd73aebe4eb9d0071a05fbe0c", size = 7894430, upload-time = "2024-12-13T05:54:44.049Z" }, - { url = "https://files.pythonhosted.org/packages/e9/18/80f70d91896e0a517b4a051c3fd540daa131630fd75e02e250365353b253/matplotlib-3.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30e5b22e8bcfb95442bf7d48b0d7f3bdf4a450cbf68986ea45fca3d11ae9d099", size = 7780045, upload-time = "2024-12-13T05:54:46.414Z" }, - { url = "https://files.pythonhosted.org/packages/a2/73/ccb381026e3238c5c25c3609ba4157b2d1a617ec98d65a8b4ee4e1e74d02/matplotlib-3.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bb0030d1d447fd56dcc23b4c64a26e44e898f0416276cac1ebc25522e0ac249", size = 8209906, upload-time = "2024-12-13T05:54:49.459Z" }, - { url = "https://files.pythonhosted.org/packages/ab/33/1648da77b74741c89f5ea95cbf42a291b4b364f2660b316318811404ed97/matplotlib-3.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aca90ed222ac3565d2752b83dbb27627480d27662671e4d39da72e97f657a423", size = 8322873, upload-time = "2024-12-13T05:54:53.066Z" }, - { url = "https://files.pythonhosted.org/packages/57/d3/8447ba78bc6593c9044c372d1609f8ea10fb1e071e7a9e0747bea74fc16c/matplotlib-3.9.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a181b2aa2906c608fcae72f977a4a2d76e385578939891b91c2550c39ecf361e", size = 9099566, upload-time = "2024-12-13T05:54:55.522Z" }, - { url = "https://files.pythonhosted.org/packages/23/e1/4f0e237bf349c02ff9d1b6e7109f1a17f745263809b9714a8576dc17752b/matplotlib-3.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:1f6882828231eca17f501c4dcd98a05abb3f03d157fbc0769c6911fe08b6cfd3", size = 7838065, upload-time = "2024-12-13T05:54:58.337Z" }, - { url = "https://files.pythonhosted.org/packages/1a/2b/c918bf6c19d6445d1cefe3d2e42cb740fb997e14ab19d4daeb6a7ab8a157/matplotlib-3.9.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:dfc48d67e6661378a21c2983200a654b72b5c5cdbd5d2cf6e5e1ece860f0cc70", size = 7891131, upload-time = "2024-12-13T05:55:02.837Z" }, - { url = "https://files.pythonhosted.org/packages/c1/e5/b4e8fc601ca302afeeabf45f30e706a445c7979a180e3a978b78b2b681a4/matplotlib-3.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47aef0fab8332d02d68e786eba8113ffd6f862182ea2999379dec9e237b7e483", size = 7776365, upload-time = "2024-12-13T05:55:05.158Z" }, - { url = "https://files.pythonhosted.org/packages/99/06/b991886c506506476e5d83625c5970c656a491b9f80161458fed94597808/matplotlib-3.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fba1f52c6b7dc764097f52fd9ab627b90db452c9feb653a59945de16752e965f", size = 8200707, upload-time = "2024-12-13T05:55:09.48Z" }, - { url = "https://files.pythonhosted.org/packages/c3/e2/556b627498cb27e61026f2d1ba86a78ad1b836fef0996bef5440e8bc9559/matplotlib-3.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:173ac3748acaac21afcc3fa1633924609ba1b87749006bc25051c52c422a5d00", size = 8313761, upload-time = "2024-12-13T05:55:12.95Z" }, - { url = "https://files.pythonhosted.org/packages/58/ff/165af33ec766ff818306ea88e91f9f60d2a6ed543be1eb122a98acbf3b0d/matplotlib-3.9.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320edea0cadc07007765e33f878b13b3738ffa9745c5f707705692df70ffe0e0", size = 9095284, upload-time = "2024-12-13T05:55:16.199Z" }, - { url = "https://files.pythonhosted.org/packages/9f/8b/3d0c7a002db3b1ed702731c2a9a06d78d035f1f2fb0fb936a8e43cc1e9f4/matplotlib-3.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a4a4cfc82330b27042a7169533da7991e8789d180dd5b3daeaee57d75cd5a03b", size = 7841160, upload-time = "2024-12-13T05:55:19.991Z" }, - { url = "https://files.pythonhosted.org/packages/49/b1/999f89a7556d101b23a2f0b54f1b6e140d73f56804da1398f2f0bc0924bc/matplotlib-3.9.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:37eeffeeca3c940985b80f5b9a7b95ea35671e0e7405001f249848d2b62351b6", size = 7891499, upload-time = "2024-12-13T05:55:22.142Z" }, - { url = "https://files.pythonhosted.org/packages/87/7b/06a32b13a684977653396a1bfcd34d4e7539c5d55c8cbfaa8ae04d47e4a9/matplotlib-3.9.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3e7465ac859ee4abcb0d836137cd8414e7bb7ad330d905abced457217d4f0f45", size = 7776802, upload-time = "2024-12-13T05:55:25.947Z" }, - { url = "https://files.pythonhosted.org/packages/65/87/ac498451aff739e515891bbb92e566f3c7ef31891aaa878402a71f9b0910/matplotlib-3.9.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4c12302c34afa0cf061bea23b331e747e5e554b0fa595c96e01c7b75bc3b858", size = 8200802, upload-time = "2024-12-13T05:55:28.461Z" }, - { url = "https://files.pythonhosted.org/packages/f8/6b/9eb761c00e1cb838f6c92e5f25dcda3f56a87a52f6cb8fdfa561e6cf6a13/matplotlib-3.9.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b8c97917f21b75e72108b97707ba3d48f171541a74aa2a56df7a40626bafc64", size = 8313880, upload-time = "2024-12-13T05:55:30.965Z" }, - { url = "https://files.pythonhosted.org/packages/d7/a2/c8eaa600e2085eec7e38cbbcc58a30fc78f8224939d31d3152bdafc01fd1/matplotlib-3.9.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0229803bd7e19271b03cb09f27db76c918c467aa4ce2ae168171bc67c3f508df", size = 9094637, upload-time = "2024-12-13T05:55:33.701Z" }, - { url = "https://files.pythonhosted.org/packages/71/1f/c6e1daea55b7bfeb3d84c6cb1abc449f6a02b181e7e2a5e4db34c3afb793/matplotlib-3.9.4-cp313-cp313-win_amd64.whl", hash = "sha256:7c0d8ef442ebf56ff5e206f8083d08252ee738e04f3dc88ea882853a05488799", size = 7841311, upload-time = "2024-12-13T05:55:36.737Z" }, - { url = "https://files.pythonhosted.org/packages/c0/3a/2757d3f7d388b14dd48f5a83bea65b6d69f000e86b8f28f74d86e0d375bd/matplotlib-3.9.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a04c3b00066a688834356d196136349cb32f5e1003c55ac419e91585168b88fb", size = 7919989, upload-time = "2024-12-13T05:55:39.024Z" }, - { url = "https://files.pythonhosted.org/packages/24/28/f5077c79a4f521589a37fe1062d6a6ea3534e068213f7357e7cfffc2e17a/matplotlib-3.9.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:04c519587f6c210626741a1e9a68eefc05966ede24205db8982841826af5871a", size = 7809417, upload-time = "2024-12-13T05:55:42.412Z" }, - { url = "https://files.pythonhosted.org/packages/36/c8/c523fd2963156692916a8eb7d4069084cf729359f7955cf09075deddfeaf/matplotlib-3.9.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:308afbf1a228b8b525fcd5cec17f246bbbb63b175a3ef6eb7b4d33287ca0cf0c", size = 8226258, upload-time = "2024-12-13T05:55:47.259Z" }, - { url = "https://files.pythonhosted.org/packages/f6/88/499bf4b8fa9349b6f5c0cf4cead0ebe5da9d67769129f1b5651e5ac51fbc/matplotlib-3.9.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddb3b02246ddcffd3ce98e88fed5b238bc5faff10dbbaa42090ea13241d15764", size = 8335849, upload-time = "2024-12-13T05:55:49.763Z" }, - { url = "https://files.pythonhosted.org/packages/b8/9f/20a4156b9726188646a030774ee337d5ff695a965be45ce4dbcb9312c170/matplotlib-3.9.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8a75287e9cb9eee48cb79ec1d806f75b29c0fde978cb7223a1f4c5848d696041", size = 9102152, upload-time = "2024-12-13T05:55:51.997Z" }, - { url = "https://files.pythonhosted.org/packages/10/11/237f9c3a4e8d810b1759b67ff2da7c32c04f9c80aa475e7beb36ed43a8fb/matplotlib-3.9.4-cp313-cp313t-win_amd64.whl", hash = "sha256:488deb7af140f0ba86da003e66e10d55ff915e152c78b4b66d231638400b1965", size = 7896987, upload-time = "2024-12-13T05:55:55.941Z" }, - { url = "https://files.pythonhosted.org/packages/56/eb/501b465c9fef28f158e414ea3a417913dc2ac748564c7ed41535f23445b4/matplotlib-3.9.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3c3724d89a387ddf78ff88d2a30ca78ac2b4c89cf37f2db4bd453c34799e933c", size = 7885919, upload-time = "2024-12-13T05:55:59.66Z" }, - { url = "https://files.pythonhosted.org/packages/da/36/236fbd868b6c91309a5206bd90c3f881f4f44b2d997cd1d6239ef652f878/matplotlib-3.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d5f0a8430ffe23d7e32cfd86445864ccad141797f7d25b7c41759a5b5d17cfd7", size = 7771486, upload-time = "2024-12-13T05:56:04.264Z" }, - { url = "https://files.pythonhosted.org/packages/e0/4b/105caf2d54d5ed11d9f4335398f5103001a03515f2126c936a752ccf1461/matplotlib-3.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bb0141a21aef3b64b633dc4d16cbd5fc538b727e4958be82a0e1c92a234160e", size = 8201838, upload-time = "2024-12-13T05:56:06.792Z" }, - { url = "https://files.pythonhosted.org/packages/5d/a7/bb01188fb4013d34d274caf44a2f8091255b0497438e8b6c0a7c1710c692/matplotlib-3.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57aa235109e9eed52e2c2949db17da185383fa71083c00c6c143a60e07e0888c", size = 8314492, upload-time = "2024-12-13T05:56:09.964Z" }, - { url = "https://files.pythonhosted.org/packages/33/19/02e1a37f7141fc605b193e927d0a9cdf9dc124a20b9e68793f4ffea19695/matplotlib-3.9.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b18c600061477ccfdd1e6fd050c33d8be82431700f3452b297a56d9ed7037abb", size = 9092500, upload-time = "2024-12-13T05:56:13.55Z" }, - { url = "https://files.pythonhosted.org/packages/57/68/c2feb4667adbf882ffa4b3e0ac9967f848980d9f8b5bebd86644aa67ce6a/matplotlib-3.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:ef5f2d1b67d2d2145ff75e10f8c008bfbf71d45137c4b648c87193e7dd053eac", size = 7822962, upload-time = "2024-12-13T05:56:16.358Z" }, - { url = "https://files.pythonhosted.org/packages/0c/22/2ef6a364cd3f565442b0b055e0599744f1e4314ec7326cdaaa48a4d864d7/matplotlib-3.9.4-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:44e0ed786d769d85bc787b0606a53f2d8d2d1d3c8a2608237365e9121c1a338c", size = 7877995, upload-time = "2024-12-13T05:56:18.805Z" }, - { url = "https://files.pythonhosted.org/packages/87/b8/2737456e566e9f4d94ae76b8aa0d953d9acb847714f9a7ad80184474f5be/matplotlib-3.9.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:09debb9ce941eb23ecdbe7eab972b1c3e0276dcf01688073faff7b0f61d6c6ca", size = 7769300, upload-time = "2024-12-13T05:56:21.315Z" }, - { url = "https://files.pythonhosted.org/packages/b2/1f/e709c6ec7b5321e6568769baa288c7178e60a93a9da9e682b39450da0e29/matplotlib-3.9.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcc53cf157a657bfd03afab14774d54ba73aa84d42cfe2480c91bd94873952db", size = 8313423, upload-time = "2024-12-13T05:56:26.719Z" }, - { url = "https://files.pythonhosted.org/packages/5e/b6/5a1f868782cd13f053a679984e222007ecff654a9bfbac6b27a65f4eeb05/matplotlib-3.9.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ad45da51be7ad02387801fd154ef74d942f49fe3fcd26a64c94842ba7ec0d865", size = 7854624, upload-time = "2024-12-13T05:56:29.359Z" }, ] [[package]] name = "matplotlib" version = "3.10.8" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] dependencies = [ - { name = "contourpy", version = "1.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "contourpy", version = "1.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "contourpy", version = "1.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "cycler", marker = "python_full_version >= '3.10'" }, - { name = "fonttools", version = "4.61.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "kiwisolver", version = "1.4.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "cycler" }, + { 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 = "packaging", version = "26.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "pillow", version = "12.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "pyparsing", marker = "python_full_version >= '3.10'" }, - { name = "python-dateutil", marker = "python_full_version >= '3.10'" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "pyparsing" }, + { name = "python-dateutil" }, ] sdist = { url = "https://files.pythonhosted.org/packages/8a/76/d3c6e3a13fe484ebe7718d14e269c9569c4eb0020a968a327acb3b9a8fe6/matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3", size = 34806269, upload-time = "2025-12-10T22:56:51.155Z" } wheels = [ @@ -2025,19 +1312,15 @@ name = "mkdocs" version = "1.6.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "click", version = "8.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "click" }, { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "ghp-import" }, - { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, { name = "jinja2" }, - { name = "markdown", version = "3.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "markdown", version = "3.10.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "markdown" }, { name = "markupsafe" }, { name = "mergedeep" }, { name = "mkdocs-get-deps" }, - { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "packaging", version = "26.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "packaging" }, { name = "pathspec" }, { name = "pyyaml" }, { name = "pyyaml-env-tag" }, @@ -2053,8 +1336,7 @@ name = "mkdocs-autorefs" version = "1.4.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "markdown", version = "3.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "markdown", version = "3.10.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "markdown" }, { name = "markupsafe" }, { name = "mkdocs" }, ] @@ -2068,10 +1350,8 @@ name = "mkdocs-get-deps" version = "0.2.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, { name = "mergedeep" }, - { name = "platformdirs", version = "4.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "platformdirs", version = "4.9.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "platformdirs" }, { name = "pyyaml" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ce/25/b3cccb187655b9393572bde9b09261d267c3bf2f2cdabe347673be5976a6/mkdocs_get_deps-0.2.2.tar.gz", hash = "sha256:8ee8d5f316cdbbb2834bc1df6e69c08fe769a83e040060de26d3c19fad3599a1", size = 11047, upload-time = "2026-03-10T02:46:33.632Z" } @@ -2088,8 +1368,7 @@ dependencies = [ { name = "backrefs" }, { name = "colorama" }, { name = "jinja2" }, - { name = "markdown", version = "3.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "markdown", version = "3.10.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "markdown" }, { name = "mkdocs" }, { name = "mkdocs-material-extensions" }, { name = "paginate" }, @@ -2111,59 +1390,17 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728, upload-time = "2023-11-22T19:09:43.465Z" }, ] -[[package]] -name = "mkdocstrings" -version = "0.30.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, - { name = "jinja2", marker = "python_full_version < '3.10'" }, - { name = "markdown", version = "3.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "markupsafe", marker = "python_full_version < '3.10'" }, - { name = "mkdocs", marker = "python_full_version < '3.10'" }, - { name = "mkdocs-autorefs", marker = "python_full_version < '3.10'" }, - { name = "pymdown-extensions", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c5/33/2fa3243439f794e685d3e694590d28469a9b8ea733af4b48c250a3ffc9a0/mkdocstrings-0.30.1.tar.gz", hash = "sha256:84a007aae9b707fb0aebfc9da23db4b26fc9ab562eb56e335e9ec480cb19744f", size = 106350, upload-time = "2025-09-19T10:49:26.446Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/2c/f0dc4e1ee7f618f5bff7e05898d20bf8b6e7fa612038f768bfa295f136a4/mkdocstrings-0.30.1-py3-none-any.whl", hash = "sha256:41bd71f284ca4d44a668816193e4025c950b002252081e387433656ae9a70a82", size = 36704, upload-time = "2025-09-19T10:49:24.805Z" }, -] - -[package.optional-dependencies] -python = [ - { name = "mkdocstrings-python", version = "1.18.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, -] - [[package]] name = "mkdocstrings" version = "1.0.3" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] dependencies = [ - { name = "jinja2", marker = "python_full_version >= '3.10'" }, - { name = "markdown", version = "3.10.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "markupsafe", marker = "python_full_version >= '3.10'" }, - { name = "mkdocs", marker = "python_full_version >= '3.10'" }, - { name = "mkdocs-autorefs", marker = "python_full_version >= '3.10'" }, - { name = "pymdown-extensions", marker = "python_full_version >= '3.10'" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "markupsafe" }, + { name = "mkdocs" }, + { name = "mkdocs-autorefs" }, + { name = "pymdown-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/46/62/0dfc5719514115bf1781f44b1d7f2a0923fcc01e9c5d7990e48a05c9ae5d/mkdocstrings-1.0.3.tar.gz", hash = "sha256:ab670f55040722b49bb45865b2e93b824450fb4aef638b00d7acb493a9020434", size = 100946, upload-time = "2026-02-07T14:31:40.973Z" } wheels = [ @@ -2172,52 +1409,18 @@ wheels = [ [package.optional-dependencies] python = [ - { name = "mkdocstrings-python", version = "2.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, -] - -[[package]] -name = "mkdocstrings-python" -version = "1.18.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "griffe", marker = "python_full_version < '3.10'" }, - { name = "mkdocs-autorefs", marker = "python_full_version < '3.10'" }, - { name = "mkdocstrings", version = "0.30.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "typing-extensions", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/95/ae/58ab2bfbee2792e92a98b97e872f7c003deb903071f75d8d83aa55db28fa/mkdocstrings_python-1.18.2.tar.gz", hash = "sha256:4ad536920a07b6336f50d4c6d5603316fafb1172c5c882370cbbc954770ad323", size = 207972, upload-time = "2025-08-28T16:11:19.847Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/8f/ce008599d9adebf33ed144e7736914385e8537f5fc686fdb7cceb8c22431/mkdocstrings_python-1.18.2-py3-none-any.whl", hash = "sha256:944fe6deb8f08f33fa936d538233c4036e9f53e840994f6146e8e94eb71b600d", size = 138215, upload-time = "2025-08-28T16:11:18.176Z" }, + { name = "mkdocstrings-python" }, ] [[package]] name = "mkdocstrings-python" version = "2.0.3" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] dependencies = [ - { name = "griffelib", marker = "python_full_version >= '3.10'" }, - { name = "mkdocs-autorefs", marker = "python_full_version >= '3.10'" }, - { name = "mkdocstrings", version = "1.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "typing-extensions", marker = "python_full_version == '3.10.*'" }, + { name = "griffelib" }, + { name = "mkdocs-autorefs" }, + { name = "mkdocstrings" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/29/33/c225eaf898634bdda489a6766fc35d1683c640bffe0e0acd10646b13536d/mkdocstrings_python-2.0.3.tar.gz", hash = "sha256:c518632751cc869439b31c9d3177678ad2bfa5c21b79b863956ad68fc92c13b8", size = 199083, upload-time = "2026-02-20T10:38:36.368Z" } wheels = [ @@ -2368,24 +1571,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008, upload-time = "2026-01-26T02:46:07.468Z" }, { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542, upload-time = "2026-01-26T02:46:08.809Z" }, { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719, upload-time = "2026-01-26T02:46:11.146Z" }, - { url = "https://files.pythonhosted.org/packages/9e/ee/74525ebe3eb5fddcd6735fc03cbea3feeed4122b53bc798ac32d297ac9ae/multidict-6.7.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:65573858d27cdeaca41893185677dc82395159aa28875a8867af66532d413a8f", size = 77107, upload-time = "2026-01-26T02:46:12.608Z" }, - { url = "https://files.pythonhosted.org/packages/f0/9a/ce8744e777a74b3050b1bf56be3eed1053b3457302ea055f1ea437200a23/multidict-6.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c524c6fb8fc342793708ab111c4dbc90ff9abd568de220432500e47e990c0358", size = 44943, upload-time = "2026-01-26T02:46:14.016Z" }, - { url = "https://files.pythonhosted.org/packages/83/9c/1d2a283d9c6f31e260cb6c2fccadc3edcf6c4c14ee0929cd2af4d2606dd7/multidict-6.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:aa23b001d968faef416ff70dc0f1ab045517b9b42a90edd3e9bcdb06479e31d5", size = 44603, upload-time = "2026-01-26T02:46:15.391Z" }, - { url = "https://files.pythonhosted.org/packages/87/9d/3b186201671583d8e8d6d79c07481a5aafd0ba7575e3d8566baec80c1e82/multidict-6.7.1-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6704fa2b7453b2fb121740555fa1ee20cd98c4d011120caf4d2b8d4e7c76eec0", size = 240573, upload-time = "2026-01-26T02:46:16.783Z" }, - { url = "https://files.pythonhosted.org/packages/42/7d/a52f5d4d0754311d1ac78478e34dff88de71259a8585e05ee14e5f877caf/multidict-6.7.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:121a34e5bfa410cdf2c8c49716de160de3b1dbcd86b49656f5681e4543bcd1a8", size = 240106, upload-time = "2026-01-26T02:46:18.432Z" }, - { url = "https://files.pythonhosted.org/packages/84/9f/d80118e6c30ff55b7d171bdc5520aad4b9626e657520b8d7c8ca8c2fad12/multidict-6.7.1-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:026d264228bcd637d4e060844e39cdc60f86c479e463d49075dedc21b18fbbe0", size = 219418, upload-time = "2026-01-26T02:46:20.526Z" }, - { url = "https://files.pythonhosted.org/packages/c7/bd/896e60b3457f194de77c7de64f9acce9f75da0518a5230ce1df534f6747b/multidict-6.7.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0e697826df7eb63418ee190fd06ce9f1803593bb4b9517d08c60d9b9a7f69d8f", size = 252124, upload-time = "2026-01-26T02:46:22.157Z" }, - { url = "https://files.pythonhosted.org/packages/f4/de/ba6b30447c36a37078d0ba604aa12c1a52887af0c355236ca6e0a9d5286f/multidict-6.7.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb08271280173720e9fea9ede98e5231defcbad90f1624bea26f32ec8a956e2f", size = 249402, upload-time = "2026-01-26T02:46:23.718Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b2/50a383c96230e432895a2fd3bcfe1b65785899598259d871d5de6b93180c/multidict-6.7.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6b3228e1d80af737b72925ce5fb4daf5a335e49cd7ab77ed7b9fdfbf58c526e", size = 240346, upload-time = "2026-01-26T02:46:25.393Z" }, - { url = "https://files.pythonhosted.org/packages/89/37/16d391fd8da544b1489306e38a46785fa41dd0f0ef766837ed7d4676dde0/multidict-6.7.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3943debf0fbb57bdde5901695c11094a9a36723e5c03875f87718ee15ca2f4d2", size = 237010, upload-time = "2026-01-26T02:46:27.408Z" }, - { url = "https://files.pythonhosted.org/packages/b0/24/3152ee026eda86d5d3e3685182911e6951af7a016579da931080ce6ac9ad/multidict-6.7.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:98c5787b0a0d9a41d9311eae44c3b76e6753def8d8870ab501320efe75a6a5f8", size = 232018, upload-time = "2026-01-26T02:46:29.941Z" }, - { url = "https://files.pythonhosted.org/packages/9c/1f/48d3c27a72be7fd23a55d8847193c459959bf35a5bb5844530dab00b739b/multidict-6.7.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:08ccb2a6dc72009093ebe7f3f073e5ec5964cba9a706fa94b1a1484039b87941", size = 241498, upload-time = "2026-01-26T02:46:32.052Z" }, - { url = "https://files.pythonhosted.org/packages/1a/45/413643ae2952d0decdf6c1250f86d08a43e143271441e81027e38d598bd7/multidict-6.7.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb351f72c26dc9abe338ca7294661aa22969ad8ffe7ef7d5541d19f368dc854a", size = 247957, upload-time = "2026-01-26T02:46:33.666Z" }, - { url = "https://files.pythonhosted.org/packages/50/f8/f1d0ac23df15e0470776388bdb261506f63af1f81d28bacb5e262d6e12b6/multidict-6.7.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ac1c665bad8b5d762f5f85ebe4d94130c26965f11de70c708c75671297c776de", size = 241651, upload-time = "2026-01-26T02:46:35.7Z" }, - { url = "https://files.pythonhosted.org/packages/2c/c9/1a2a18f383cf129add66b6c36b75c3911a7ba95cf26cb141482de085cc12/multidict-6.7.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fa6609d0364f4f6f58351b4659a1f3e0e898ba2a8c5cac04cb2c7bc556b0bc5", size = 236371, upload-time = "2026-01-26T02:46:37.37Z" }, - { url = "https://files.pythonhosted.org/packages/bb/aa/77d87e3fca31325b87e0eb72d5fe9a7472dcb51391a42df7ac1f3842f6c0/multidict-6.7.1-cp39-cp39-win32.whl", hash = "sha256:6f77ce314a29263e67adadc7e7c1bc699fcb3a305059ab973d038f87caa42ed0", size = 41426, upload-time = "2026-01-26T02:46:39.026Z" }, - { url = "https://files.pythonhosted.org/packages/e3/b3/e8863e6a2da15a9d7e98976ff402e871b7352c76566df6c18d0378e0d9cf/multidict-6.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:f537b55778cd3cbee430abe3131255d3a78202e0f9ea7ffc6ada893a4bcaeea4", size = 46180, upload-time = "2026-01-26T02:46:40.422Z" }, - { url = "https://files.pythonhosted.org/packages/93/d3/dd4fa951ad5b5fa216bf30054d705683d13405eea7459833d78f31b74c9c/multidict-6.7.1-cp39-cp39-win_arm64.whl", hash = "sha256:749aa54f578f2e5f439538706a475aa844bfa8ef75854b1401e6e528e4937cf9", size = 43231, upload-time = "2026-01-26T02:46:41.945Z" }, { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, ] @@ -2400,8 +1585,6 @@ sdist = { url = "https://files.pythonhosted.org/packages/b5/ae/04f39c5d0d0def032 wheels = [ { url = "https://files.pythonhosted.org/packages/ef/76/6e712a2623d146d314f17598df5de7224c85c0060ef63fd95cc15a25b3fa/multiprocess-0.70.16-pp310-pypy310_pp73-macosx_10_13_x86_64.whl", hash = "sha256:476887be10e2f59ff183c006af746cb6f1fd0eadcfd4ef49e605cbe2659920ee", size = 134980, upload-time = "2024-01-28T18:52:15.731Z" }, { url = "https://files.pythonhosted.org/packages/0f/ab/1e6e8009e380e22254ff539ebe117861e5bdb3bff1fc977920972237c6c7/multiprocess-0.70.16-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d951bed82c8f73929ac82c61f01a7b5ce8f3e5ef40f5b52553b4f547ce2b08ec", size = 134982, upload-time = "2024-01-28T18:52:17.783Z" }, - { url = "https://files.pythonhosted.org/packages/d8/94/8638a89f93c80df329116e6781a060506c7e91e1f4370dc831e9d17a041d/multiprocess-0.70.16-pp39-pypy39_pp73-macosx_10_13_x86_64.whl", hash = "sha256:0dfd078c306e08d46d7a8d06fb120313d87aa43af60d66da43ffff40b44d2f41", size = 133497, upload-time = "2024-01-28T18:52:22.644Z" }, - { url = "https://files.pythonhosted.org/packages/89/21/222066f6bb8d8af287923ae3bd26cf4699a9ce020228ac273caca1de8250/multiprocess-0.70.16-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e7b9d0f307cd9bd50851afaac0dba2cb6c44449efff697df7c7645f7d3f2be3a", size = 133498, upload-time = "2024-01-28T18:52:24.576Z" }, { url = "https://files.pythonhosted.org/packages/bc/f7/7ec7fddc92e50714ea3745631f79bd9c96424cb2702632521028e57d3a36/multiprocess-0.70.16-py310-none-any.whl", hash = "sha256:c4a9944c67bd49f823687463660a2d6daae94c289adff97e0f9d696ba6371d02", size = 134824, upload-time = "2024-01-28T18:52:26.062Z" }, { url = "https://files.pythonhosted.org/packages/50/15/b56e50e8debaf439f44befec5b2af11db85f6e0f344c3113ae0be0593a91/multiprocess-0.70.16-py311-none-any.whl", hash = "sha256:af4cabb0dac72abfb1e794fa7855c325fd2b55a10a44628a3c1ad3311c04127a", size = 143519, upload-time = "2024-01-28T18:52:28.115Z" }, { url = "https://files.pythonhosted.org/packages/0a/7d/a988f258104dcd2ccf1ed40fdc97e26c4ac351eeaf81d76e266c52d84e2f/multiprocess-0.70.16-py312-none-any.whl", hash = "sha256:fc0544c531920dde3b00c29863377f87e1632601092ea2daca74e4beb40faa2e", size = 146741, upload-time = "2024-01-28T18:52:29.395Z" }, @@ -2418,25 +1601,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/03/cc/7cb74758e6df95e0c4e1253f203b6dd7f348bf2f29cf89e9210a2416d535/narwhals-2.16.0-py3-none-any.whl", hash = "sha256:846f1fd7093ac69d63526e50732033e86c30ea0026a44d9b23991010c7d1485d", size = 443951, upload-time = "2026-02-02T10:30:58.635Z" }, ] -[[package]] -name = "networkx" -version = "3.2.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/c4/80/a84676339aaae2f1cfdf9f418701dd634aef9cc76f708ef55c36ff39c3ca/networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6", size = 2073928, upload-time = "2023-10-28T08:41:39.364Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/f0/8fbc882ca80cf077f1b246c0e3c3465f7f415439bdea6b899f6b19f61f70/networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2", size = 1647772, upload-time = "2023-10-28T08:41:36.945Z" }, -] - [[package]] name = "networkx" version = "3.4.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", + "python_full_version < '3.11'", ] sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368, upload-time = "2024-10-21T12:39:38.695Z" } wheels = [ @@ -2448,86 +1618,20 @@ name = "networkx" version = "3.6.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, ] -[[package]] -name = "numpy" -version = "2.0.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/a9/75/10dd1f8116a8b796cb2c737b674e02d02e80454bda953fa7e65d8c12b016/numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78", size = 18902015, upload-time = "2024-08-26T20:19:40.945Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/21/91/3495b3237510f79f5d81f2508f9f13fea78ebfdf07538fc7444badda173d/numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece", size = 21165245, upload-time = "2024-08-26T20:04:14.625Z" }, - { url = "https://files.pythonhosted.org/packages/05/33/26178c7d437a87082d11019292dce6d3fe6f0e9026b7b2309cbf3e489b1d/numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04", size = 13738540, upload-time = "2024-08-26T20:04:36.784Z" }, - { url = "https://files.pythonhosted.org/packages/ec/31/cc46e13bf07644efc7a4bf68df2df5fb2a1a88d0cd0da9ddc84dc0033e51/numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66", size = 5300623, upload-time = "2024-08-26T20:04:46.491Z" }, - { url = "https://files.pythonhosted.org/packages/6e/16/7bfcebf27bb4f9d7ec67332ffebee4d1bf085c84246552d52dbb548600e7/numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b", size = 6901774, upload-time = "2024-08-26T20:04:58.173Z" }, - { url = "https://files.pythonhosted.org/packages/f9/a3/561c531c0e8bf082c5bef509d00d56f82e0ea7e1e3e3a7fc8fa78742a6e5/numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd", size = 13907081, upload-time = "2024-08-26T20:05:19.098Z" }, - { url = "https://files.pythonhosted.org/packages/fa/66/f7177ab331876200ac7563a580140643d1179c8b4b6a6b0fc9838de2a9b8/numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318", size = 19523451, upload-time = "2024-08-26T20:05:47.479Z" }, - { url = "https://files.pythonhosted.org/packages/25/7f/0b209498009ad6453e4efc2c65bcdf0ae08a182b2b7877d7ab38a92dc542/numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8", size = 19927572, upload-time = "2024-08-26T20:06:17.137Z" }, - { url = "https://files.pythonhosted.org/packages/3e/df/2619393b1e1b565cd2d4c4403bdd979621e2c4dea1f8532754b2598ed63b/numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326", size = 14400722, upload-time = "2024-08-26T20:06:39.16Z" }, - { url = "https://files.pythonhosted.org/packages/22/ad/77e921b9f256d5da36424ffb711ae79ca3f451ff8489eeca544d0701d74a/numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97", size = 6472170, upload-time = "2024-08-26T20:06:50.361Z" }, - { url = "https://files.pythonhosted.org/packages/10/05/3442317535028bc29cf0c0dd4c191a4481e8376e9f0db6bcf29703cadae6/numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131", size = 15905558, upload-time = "2024-08-26T20:07:13.881Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cf/034500fb83041aa0286e0fb16e7c76e5c8b67c0711bb6e9e9737a717d5fe/numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448", size = 21169137, upload-time = "2024-08-26T20:07:45.345Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d9/32de45561811a4b87fbdee23b5797394e3d1504b4a7cf40c10199848893e/numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195", size = 13703552, upload-time = "2024-08-26T20:08:06.666Z" }, - { url = "https://files.pythonhosted.org/packages/c1/ca/2f384720020c7b244d22508cb7ab23d95f179fcfff33c31a6eeba8d6c512/numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57", size = 5298957, upload-time = "2024-08-26T20:08:15.83Z" }, - { url = "https://files.pythonhosted.org/packages/0e/78/a3e4f9fb6aa4e6fdca0c5428e8ba039408514388cf62d89651aade838269/numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a", size = 6905573, upload-time = "2024-08-26T20:08:27.185Z" }, - { url = "https://files.pythonhosted.org/packages/a0/72/cfc3a1beb2caf4efc9d0b38a15fe34025230da27e1c08cc2eb9bfb1c7231/numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669", size = 13914330, upload-time = "2024-08-26T20:08:48.058Z" }, - { url = "https://files.pythonhosted.org/packages/ba/a8/c17acf65a931ce551fee11b72e8de63bf7e8a6f0e21add4c937c83563538/numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951", size = 19534895, upload-time = "2024-08-26T20:09:16.536Z" }, - { url = "https://files.pythonhosted.org/packages/ba/86/8767f3d54f6ae0165749f84648da9dcc8cd78ab65d415494962c86fac80f/numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9", size = 19937253, upload-time = "2024-08-26T20:09:46.263Z" }, - { url = "https://files.pythonhosted.org/packages/df/87/f76450e6e1c14e5bb1eae6836478b1028e096fd02e85c1c37674606ab752/numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15", size = 14414074, upload-time = "2024-08-26T20:10:08.483Z" }, - { url = "https://files.pythonhosted.org/packages/5c/ca/0f0f328e1e59f73754f06e1adfb909de43726d4f24c6a3f8805f34f2b0fa/numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4", size = 6470640, upload-time = "2024-08-26T20:10:19.732Z" }, - { url = "https://files.pythonhosted.org/packages/eb/57/3a3f14d3a759dcf9bf6e9eda905794726b758819df4663f217d658a58695/numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc", size = 15910230, upload-time = "2024-08-26T20:10:43.413Z" }, - { url = "https://files.pythonhosted.org/packages/45/40/2e117be60ec50d98fa08c2f8c48e09b3edea93cfcabd5a9ff6925d54b1c2/numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b", size = 20895803, upload-time = "2024-08-26T20:11:13.916Z" }, - { url = "https://files.pythonhosted.org/packages/46/92/1b8b8dee833f53cef3e0a3f69b2374467789e0bb7399689582314df02651/numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e", size = 13471835, upload-time = "2024-08-26T20:11:34.779Z" }, - { url = "https://files.pythonhosted.org/packages/7f/19/e2793bde475f1edaea6945be141aef6c8b4c669b90c90a300a8954d08f0a/numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c", size = 5038499, upload-time = "2024-08-26T20:11:43.902Z" }, - { url = "https://files.pythonhosted.org/packages/e3/ff/ddf6dac2ff0dd50a7327bcdba45cb0264d0e96bb44d33324853f781a8f3c/numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c", size = 6633497, upload-time = "2024-08-26T20:11:55.09Z" }, - { url = "https://files.pythonhosted.org/packages/72/21/67f36eac8e2d2cd652a2e69595a54128297cdcb1ff3931cfc87838874bd4/numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692", size = 13621158, upload-time = "2024-08-26T20:12:14.95Z" }, - { url = "https://files.pythonhosted.org/packages/39/68/e9f1126d757653496dbc096cb429014347a36b228f5a991dae2c6b6cfd40/numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a", size = 19236173, upload-time = "2024-08-26T20:12:44.049Z" }, - { url = "https://files.pythonhosted.org/packages/d1/e9/1f5333281e4ebf483ba1c888b1d61ba7e78d7e910fdd8e6499667041cc35/numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c", size = 19634174, upload-time = "2024-08-26T20:13:13.634Z" }, - { url = "https://files.pythonhosted.org/packages/71/af/a469674070c8d8408384e3012e064299f7a2de540738a8e414dcfd639996/numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded", size = 14099701, upload-time = "2024-08-26T20:13:34.851Z" }, - { url = "https://files.pythonhosted.org/packages/d0/3d/08ea9f239d0e0e939b6ca52ad403c84a2bce1bde301a8eb4888c1c1543f1/numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5", size = 6174313, upload-time = "2024-08-26T20:13:45.653Z" }, - { url = "https://files.pythonhosted.org/packages/b2/b5/4ac39baebf1fdb2e72585c8352c56d063b6126be9fc95bd2bb5ef5770c20/numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a", size = 15606179, upload-time = "2024-08-26T20:14:08.786Z" }, - { url = "https://files.pythonhosted.org/packages/43/c1/41c8f6df3162b0c6ffd4437d729115704bd43363de0090c7f913cfbc2d89/numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c", size = 21169942, upload-time = "2024-08-26T20:14:40.108Z" }, - { url = "https://files.pythonhosted.org/packages/39/bc/fd298f308dcd232b56a4031fd6ddf11c43f9917fbc937e53762f7b5a3bb1/numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd", size = 13711512, upload-time = "2024-08-26T20:15:00.985Z" }, - { url = "https://files.pythonhosted.org/packages/96/ff/06d1aa3eeb1c614eda245c1ba4fb88c483bee6520d361641331872ac4b82/numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b", size = 5306976, upload-time = "2024-08-26T20:15:10.876Z" }, - { url = "https://files.pythonhosted.org/packages/2d/98/121996dcfb10a6087a05e54453e28e58694a7db62c5a5a29cee14c6e047b/numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729", size = 6906494, upload-time = "2024-08-26T20:15:22.055Z" }, - { url = "https://files.pythonhosted.org/packages/15/31/9dffc70da6b9bbf7968f6551967fc21156207366272c2a40b4ed6008dc9b/numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1", size = 13912596, upload-time = "2024-08-26T20:15:42.452Z" }, - { url = "https://files.pythonhosted.org/packages/b9/14/78635daab4b07c0930c919d451b8bf8c164774e6a3413aed04a6d95758ce/numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd", size = 19526099, upload-time = "2024-08-26T20:16:11.048Z" }, - { url = "https://files.pythonhosted.org/packages/26/4c/0eeca4614003077f68bfe7aac8b7496f04221865b3a5e7cb230c9d055afd/numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d", size = 19932823, upload-time = "2024-08-26T20:16:40.171Z" }, - { url = "https://files.pythonhosted.org/packages/f1/46/ea25b98b13dccaebddf1a803f8c748680d972e00507cd9bc6dcdb5aa2ac1/numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d", size = 14404424, upload-time = "2024-08-26T20:17:02.604Z" }, - { url = "https://files.pythonhosted.org/packages/c8/a6/177dd88d95ecf07e722d21008b1b40e681a929eb9e329684d449c36586b2/numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa", size = 6476809, upload-time = "2024-08-26T20:17:13.553Z" }, - { url = "https://files.pythonhosted.org/packages/ea/2b/7fc9f4e7ae5b507c1a3a21f0f15ed03e794c1242ea8a242ac158beb56034/numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73", size = 15911314, upload-time = "2024-08-26T20:17:36.72Z" }, - { url = "https://files.pythonhosted.org/packages/8f/3b/df5a870ac6a3be3a86856ce195ef42eec7ae50d2a202be1f5a4b3b340e14/numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8", size = 21025288, upload-time = "2024-08-26T20:18:07.732Z" }, - { url = "https://files.pythonhosted.org/packages/2c/97/51af92f18d6f6f2d9ad8b482a99fb74e142d71372da5d834b3a2747a446e/numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4", size = 6762793, upload-time = "2024-08-26T20:18:19.125Z" }, - { url = "https://files.pythonhosted.org/packages/12/46/de1fbd0c1b5ccaa7f9a005b66761533e2f6a3e560096682683a223631fe9/numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c", size = 19334885, upload-time = "2024-08-26T20:18:47.237Z" }, - { url = "https://files.pythonhosted.org/packages/cc/dc/d330a6faefd92b446ec0f0dfea4c3207bb1fef3c4771d19cf4543efd2c78/numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385", size = 15828784, upload-time = "2024-08-26T20:19:11.19Z" }, -] - [[package]] name = "numpy" version = "2.2.6" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", + "python_full_version < '3.11'", ] sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } wheels = [ @@ -2592,18 +1696,8 @@ name = "numpy" version = "2.4.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "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 = [ @@ -2717,7 +1811,7 @@ name = "nvidia-cudnn-cu12" version = "9.10.2.21" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12", marker = "(python_full_version < '3.11' and platform_machine != 'ARM64' and sys_platform == 'win32') or (python_full_version < '3.10' and platform_machine == 'ARM64' and sys_platform == 'win32') or (python_full_version < '3.11' and sys_platform == 'emscripten') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { 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" }, @@ -2728,7 +1822,7 @@ name = "nvidia-cufft-cu12" version = "11.3.3.83" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "(python_full_version < '3.11' and platform_machine != 'ARM64' and sys_platform == 'win32') or (python_full_version < '3.10' and platform_machine == 'ARM64' and sys_platform == 'win32') or (python_full_version < '3.11' and sys_platform == 'emscripten') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { 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" }, @@ -2755,9 +1849,9 @@ name = "nvidia-cusolver-cu12" version = "11.7.3.90" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12", marker = "(python_full_version < '3.11' and platform_machine != 'ARM64' and sys_platform == 'win32') or (python_full_version < '3.10' and platform_machine == 'ARM64' and sys_platform == 'win32') or (python_full_version < '3.11' and sys_platform == 'emscripten') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, - { name = "nvidia-cusparse-cu12", marker = "(python_full_version < '3.11' and platform_machine != 'ARM64' and sys_platform == 'win32') or (python_full_version < '3.10' and platform_machine == 'ARM64' and sys_platform == 'win32') or (python_full_version < '3.11' and sys_platform == 'emscripten') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, - { name = "nvidia-nvjitlink-cu12", marker = "(python_full_version < '3.11' and platform_machine != 'ARM64' and sys_platform == 'win32') or (python_full_version < '3.10' and platform_machine == 'ARM64' and sys_platform == 'win32') or (python_full_version < '3.11' and sys_platform == 'emscripten') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { 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" }, @@ -2768,7 +1862,7 @@ name = "nvidia-cusparse-cu12" version = "12.5.8.93" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "(python_full_version < '3.11' and platform_machine != 'ARM64' and sys_platform == 'win32') or (python_full_version < '3.10' and platform_machine == 'ARM64' and sys_platform == 'win32') or (python_full_version < '3.11' and sys_platform == 'emscripten') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { 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" }, @@ -2782,27 +1876,10 @@ 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.3" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/5b/4e4fff7bad39adf89f735f2bc87248c81db71205b62bcc0d5ca5b606b3c3/nvidia_nccl_cu12-2.27.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adf27ccf4238253e0b826bce3ff5fa532d65fc42322c8bfdfaf28024c0fbe039", size = 322364134, upload-time = "2025-06-03T21:58:04.013Z" }, -] - [[package]] name = "nvidia-nccl-cu12" version = "2.27.5" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] 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" }, ] @@ -2844,38 +1921,10 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e3/94/1843518e420fa3ed6919835845df698c7e27e183cb997394e4a670973a65/omegaconf-2.3.0-py3-none-any.whl", hash = "sha256:7b4df175cdb08ba400f45cae3bdcae7ba8365db4d165fc65fd04b050ab63b46b", size = 79500, upload-time = "2022-12-08T20:59:19.686Z" }, ] -[[package]] -name = "packaging" -version = "25.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, -] - [[package]] name = "packaging" version = "26.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, @@ -2895,8 +1944,7 @@ name = "pandas" version = "2.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { 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 = "python-dateutil" }, { name = "pytz" }, @@ -2951,13 +1999,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" }, { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" }, { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" }, - { url = "https://files.pythonhosted.org/packages/56/b4/52eeb530a99e2a4c55ffcd352772b599ed4473a0f892d127f4147cf0f88e/pandas-2.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c503ba5216814e295f40711470446bc3fd00f0faea8a086cbc688808e26f92a2", size = 11567720, upload-time = "2025-09-29T23:33:06.209Z" }, - { url = "https://files.pythonhosted.org/packages/48/4a/2d8b67632a021bced649ba940455ed441ca854e57d6e7658a6024587b083/pandas-2.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a637c5cdfa04b6d6e2ecedcb81fc52ffb0fd78ce2ebccc9ea964df9f658de8c8", size = 10810302, upload-time = "2025-09-29T23:33:35.846Z" }, - { url = "https://files.pythonhosted.org/packages/13/e6/d2465010ee0569a245c975dc6967b801887068bc893e908239b1f4b6c1ac/pandas-2.3.3-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:854d00d556406bffe66a4c0802f334c9ad5a96b4f1f868adf036a21b11ef13ff", size = 12154874, upload-time = "2025-09-29T23:33:49.939Z" }, - { url = "https://files.pythonhosted.org/packages/1f/18/aae8c0aa69a386a3255940e9317f793808ea79d0a525a97a903366bb2569/pandas-2.3.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf1f8a81d04ca90e32a0aceb819d34dbd378a98bf923b6398b9a3ec0bf44de29", size = 12790141, upload-time = "2025-09-29T23:34:05.655Z" }, - { url = "https://files.pythonhosted.org/packages/f7/26/617f98de789de00c2a444fbe6301bb19e66556ac78cff933d2c98f62f2b4/pandas-2.3.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:23ebd657a4d38268c7dfbdf089fbc31ea709d82e4923c5ffd4fbd5747133ce73", size = 13208697, upload-time = "2025-09-29T23:34:21.835Z" }, - { url = "https://files.pythonhosted.org/packages/b9/fb/25709afa4552042bd0e15717c75e9b4a2294c3dc4f7e6ea50f03c5136600/pandas-2.3.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5554c929ccc317d41a5e3d1234f3be588248e61f08a74dd17c9eabb535777dc9", size = 13879233, upload-time = "2025-09-29T23:34:35.079Z" }, - { url = "https://files.pythonhosted.org/packages/98/af/7be05277859a7bc399da8ba68b88c96b27b48740b6cf49688899c6eb4176/pandas-2.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:d3e28b3e83862ccf4d85ff19cf8c20b2ae7e503881711ff2d534dc8f761131aa", size = 11359119, upload-time = "2025-09-29T23:34:46.339Z" }, ] [[package]] @@ -2969,142 +2010,10 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, ] -[[package]] -name = "pillow" -version = "11.3.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3d6767bb38a8fc10e33796ba4ba210cbab9354b6d238/pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523", size = 47113069, upload-time = "2025-07-01T09:16:30.666Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4c/5d/45a3553a253ac8763f3561371432a90bdbe6000fbdcf1397ffe502aa206c/pillow-11.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b9c17fd4ace828b3003dfd1e30bff24863e0eb59b535e8f80194d9cc7ecf860", size = 5316554, upload-time = "2025-07-01T09:13:39.342Z" }, - { url = "https://files.pythonhosted.org/packages/7c/c8/67c12ab069ef586a25a4a79ced553586748fad100c77c0ce59bb4983ac98/pillow-11.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:65dc69160114cdd0ca0f35cb434633c75e8e7fad4cf855177a05bf38678f73ad", size = 4686548, upload-time = "2025-07-01T09:13:41.835Z" }, - { url = "https://files.pythonhosted.org/packages/2f/bd/6741ebd56263390b382ae4c5de02979af7f8bd9807346d068700dd6d5cf9/pillow-11.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7107195ddc914f656c7fc8e4a5e1c25f32e9236ea3ea860f257b0436011fddd0", size = 5859742, upload-time = "2025-07-03T13:09:47.439Z" }, - { url = "https://files.pythonhosted.org/packages/ca/0b/c412a9e27e1e6a829e6ab6c2dca52dd563efbedf4c9c6aa453d9a9b77359/pillow-11.3.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc3e831b563b3114baac7ec2ee86819eb03caa1a2cef0b481a5675b59c4fe23b", size = 7633087, upload-time = "2025-07-03T13:09:51.796Z" }, - { url = "https://files.pythonhosted.org/packages/59/9d/9b7076aaf30f5dd17e5e5589b2d2f5a5d7e30ff67a171eb686e4eecc2adf/pillow-11.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1f182ebd2303acf8c380a54f615ec883322593320a9b00438eb842c1f37ae50", size = 5963350, upload-time = "2025-07-01T09:13:43.865Z" }, - { url = "https://files.pythonhosted.org/packages/f0/16/1a6bf01fb622fb9cf5c91683823f073f053005c849b1f52ed613afcf8dae/pillow-11.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4445fa62e15936a028672fd48c4c11a66d641d2c05726c7ec1f8ba6a572036ae", size = 6631840, upload-time = "2025-07-01T09:13:46.161Z" }, - { url = "https://files.pythonhosted.org/packages/7b/e6/6ff7077077eb47fde78739e7d570bdcd7c10495666b6afcd23ab56b19a43/pillow-11.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:71f511f6b3b91dd543282477be45a033e4845a40278fa8dcdbfdb07109bf18f9", size = 6074005, upload-time = "2025-07-01T09:13:47.829Z" }, - { url = "https://files.pythonhosted.org/packages/c3/3a/b13f36832ea6d279a697231658199e0a03cd87ef12048016bdcc84131601/pillow-11.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040a5b691b0713e1f6cbe222e0f4f74cd233421e105850ae3b3c0ceda520f42e", size = 6708372, upload-time = "2025-07-01T09:13:52.145Z" }, - { url = "https://files.pythonhosted.org/packages/6c/e4/61b2e1a7528740efbc70b3d581f33937e38e98ef3d50b05007267a55bcb2/pillow-11.3.0-cp310-cp310-win32.whl", hash = "sha256:89bd777bc6624fe4115e9fac3352c79ed60f3bb18651420635f26e643e3dd1f6", size = 6277090, upload-time = "2025-07-01T09:13:53.915Z" }, - { url = "https://files.pythonhosted.org/packages/a9/d3/60c781c83a785d6afbd6a326ed4d759d141de43aa7365725cbcd65ce5e54/pillow-11.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:19d2ff547c75b8e3ff46f4d9ef969a06c30ab2d4263a9e287733aa8b2429ce8f", size = 6985988, upload-time = "2025-07-01T09:13:55.699Z" }, - { url = "https://files.pythonhosted.org/packages/9f/28/4f4a0203165eefb3763939c6789ba31013a2e90adffb456610f30f613850/pillow-11.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:819931d25e57b513242859ce1876c58c59dc31587847bf74cfe06b2e0cb22d2f", size = 2422899, upload-time = "2025-07-01T09:13:57.497Z" }, - { url = "https://files.pythonhosted.org/packages/db/26/77f8ed17ca4ffd60e1dcd220a6ec6d71210ba398cfa33a13a1cd614c5613/pillow-11.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1cd110edf822773368b396281a2293aeb91c90a2db00d78ea43e7e861631b722", size = 5316531, upload-time = "2025-07-01T09:13:59.203Z" }, - { url = "https://files.pythonhosted.org/packages/cb/39/ee475903197ce709322a17a866892efb560f57900d9af2e55f86db51b0a5/pillow-11.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c412fddd1b77a75aa904615ebaa6001f169b26fd467b4be93aded278266b288", size = 4686560, upload-time = "2025-07-01T09:14:01.101Z" }, - { url = "https://files.pythonhosted.org/packages/d5/90/442068a160fd179938ba55ec8c97050a612426fae5ec0a764e345839f76d/pillow-11.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1aa4de119a0ecac0a34a9c8bde33f34022e2e8f99104e47a3ca392fd60e37d", size = 5870978, upload-time = "2025-07-03T13:09:55.638Z" }, - { url = "https://files.pythonhosted.org/packages/13/92/dcdd147ab02daf405387f0218dcf792dc6dd5b14d2573d40b4caeef01059/pillow-11.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:91da1d88226663594e3f6b4b8c3c8d85bd504117d043740a8e0ec449087cc494", size = 7641168, upload-time = "2025-07-03T13:10:00.37Z" }, - { url = "https://files.pythonhosted.org/packages/6e/db/839d6ba7fd38b51af641aa904e2960e7a5644d60ec754c046b7d2aee00e5/pillow-11.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:643f189248837533073c405ec2f0bb250ba54598cf80e8c1e043381a60632f58", size = 5973053, upload-time = "2025-07-01T09:14:04.491Z" }, - { url = "https://files.pythonhosted.org/packages/f2/2f/d7675ecae6c43e9f12aa8d58b6012683b20b6edfbdac7abcb4e6af7a3784/pillow-11.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:106064daa23a745510dabce1d84f29137a37224831d88eb4ce94bb187b1d7e5f", size = 6640273, upload-time = "2025-07-01T09:14:06.235Z" }, - { url = "https://files.pythonhosted.org/packages/45/ad/931694675ede172e15b2ff03c8144a0ddaea1d87adb72bb07655eaffb654/pillow-11.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd8ff254faf15591e724dc7c4ddb6bf4793efcbe13802a4ae3e863cd300b493e", size = 6082043, upload-time = "2025-07-01T09:14:07.978Z" }, - { url = "https://files.pythonhosted.org/packages/3a/04/ba8f2b11fc80d2dd462d7abec16351b45ec99cbbaea4387648a44190351a/pillow-11.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:932c754c2d51ad2b2271fd01c3d121daaa35e27efae2a616f77bf164bc0b3e94", size = 6715516, upload-time = "2025-07-01T09:14:10.233Z" }, - { url = "https://files.pythonhosted.org/packages/48/59/8cd06d7f3944cc7d892e8533c56b0acb68399f640786313275faec1e3b6f/pillow-11.3.0-cp311-cp311-win32.whl", hash = "sha256:b4b8f3efc8d530a1544e5962bd6b403d5f7fe8b9e08227c6b255f98ad82b4ba0", size = 6274768, upload-time = "2025-07-01T09:14:11.921Z" }, - { url = "https://files.pythonhosted.org/packages/f1/cc/29c0f5d64ab8eae20f3232da8f8571660aa0ab4b8f1331da5c2f5f9a938e/pillow-11.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:1a992e86b0dd7aeb1f053cd506508c0999d710a8f07b4c791c63843fc6a807ac", size = 6986055, upload-time = "2025-07-01T09:14:13.623Z" }, - { url = "https://files.pythonhosted.org/packages/c6/df/90bd886fabd544c25addd63e5ca6932c86f2b701d5da6c7839387a076b4a/pillow-11.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:30807c931ff7c095620fe04448e2c2fc673fcbb1ffe2a7da3fb39613489b1ddd", size = 2423079, upload-time = "2025-07-01T09:14:15.268Z" }, - { url = "https://files.pythonhosted.org/packages/40/fe/1bc9b3ee13f68487a99ac9529968035cca2f0a51ec36892060edcc51d06a/pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4", size = 5278800, upload-time = "2025-07-01T09:14:17.648Z" }, - { url = "https://files.pythonhosted.org/packages/2c/32/7e2ac19b5713657384cec55f89065fb306b06af008cfd87e572035b27119/pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69", size = 4686296, upload-time = "2025-07-01T09:14:19.828Z" }, - { url = "https://files.pythonhosted.org/packages/8e/1e/b9e12bbe6e4c2220effebc09ea0923a07a6da1e1f1bfbc8d7d29a01ce32b/pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d", size = 5871726, upload-time = "2025-07-03T13:10:04.448Z" }, - { url = "https://files.pythonhosted.org/packages/8d/33/e9200d2bd7ba00dc3ddb78df1198a6e80d7669cce6c2bdbeb2530a74ec58/pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6", size = 7644652, upload-time = "2025-07-03T13:10:10.391Z" }, - { url = "https://files.pythonhosted.org/packages/41/f1/6f2427a26fc683e00d985bc391bdd76d8dd4e92fac33d841127eb8fb2313/pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7", size = 5977787, upload-time = "2025-07-01T09:14:21.63Z" }, - { url = "https://files.pythonhosted.org/packages/e4/c9/06dd4a38974e24f932ff5f98ea3c546ce3f8c995d3f0985f8e5ba48bba19/pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024", size = 6645236, upload-time = "2025-07-01T09:14:23.321Z" }, - { url = "https://files.pythonhosted.org/packages/40/e7/848f69fb79843b3d91241bad658e9c14f39a32f71a301bcd1d139416d1be/pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809", size = 6086950, upload-time = "2025-07-01T09:14:25.237Z" }, - { url = "https://files.pythonhosted.org/packages/0b/1a/7cff92e695a2a29ac1958c2a0fe4c0b2393b60aac13b04a4fe2735cad52d/pillow-11.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6be31e3fc9a621e071bc17bb7de63b85cbe0bfae91bb0363c893cbe67247780d", size = 6723358, upload-time = "2025-07-01T09:14:27.053Z" }, - { url = "https://files.pythonhosted.org/packages/26/7d/73699ad77895f69edff76b0f332acc3d497f22f5d75e5360f78cbcaff248/pillow-11.3.0-cp312-cp312-win32.whl", hash = "sha256:7b161756381f0918e05e7cb8a371fff367e807770f8fe92ecb20d905d0e1c149", size = 6275079, upload-time = "2025-07-01T09:14:30.104Z" }, - { url = "https://files.pythonhosted.org/packages/8c/ce/e7dfc873bdd9828f3b6e5c2bbb74e47a98ec23cc5c74fc4e54462f0d9204/pillow-11.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a6444696fce635783440b7f7a9fc24b3ad10a9ea3f0ab66c5905be1c19ccf17d", size = 6986324, upload-time = "2025-07-01T09:14:31.899Z" }, - { url = "https://files.pythonhosted.org/packages/16/8f/b13447d1bf0b1f7467ce7d86f6e6edf66c0ad7cf44cf5c87a37f9bed9936/pillow-11.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:2aceea54f957dd4448264f9bf40875da0415c83eb85f55069d89c0ed436e3542", size = 2423067, upload-time = "2025-07-01T09:14:33.709Z" }, - { url = "https://files.pythonhosted.org/packages/1e/93/0952f2ed8db3a5a4c7a11f91965d6184ebc8cd7cbb7941a260d5f018cd2d/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd", size = 2128328, upload-time = "2025-07-01T09:14:35.276Z" }, - { url = "https://files.pythonhosted.org/packages/4b/e8/100c3d114b1a0bf4042f27e0f87d2f25e857e838034e98ca98fe7b8c0a9c/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8", size = 2170652, upload-time = "2025-07-01T09:14:37.203Z" }, - { url = "https://files.pythonhosted.org/packages/aa/86/3f758a28a6e381758545f7cdb4942e1cb79abd271bea932998fc0db93cb6/pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f", size = 2227443, upload-time = "2025-07-01T09:14:39.344Z" }, - { url = "https://files.pythonhosted.org/packages/01/f4/91d5b3ffa718df2f53b0dc109877993e511f4fd055d7e9508682e8aba092/pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c", size = 5278474, upload-time = "2025-07-01T09:14:41.843Z" }, - { url = "https://files.pythonhosted.org/packages/f9/0e/37d7d3eca6c879fbd9dba21268427dffda1ab00d4eb05b32923d4fbe3b12/pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd", size = 4686038, upload-time = "2025-07-01T09:14:44.008Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b0/3426e5c7f6565e752d81221af9d3676fdbb4f352317ceafd42899aaf5d8a/pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e", size = 5864407, upload-time = "2025-07-03T13:10:15.628Z" }, - { url = "https://files.pythonhosted.org/packages/fc/c1/c6c423134229f2a221ee53f838d4be9d82bab86f7e2f8e75e47b6bf6cd77/pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1", size = 7639094, upload-time = "2025-07-03T13:10:21.857Z" }, - { url = "https://files.pythonhosted.org/packages/ba/c9/09e6746630fe6372c67c648ff9deae52a2bc20897d51fa293571977ceb5d/pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805", size = 5973503, upload-time = "2025-07-01T09:14:45.698Z" }, - { url = "https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8", size = 6642574, upload-time = "2025-07-01T09:14:47.415Z" }, - { url = "https://files.pythonhosted.org/packages/36/de/d5cc31cc4b055b6c6fd990e3e7f0f8aaf36229a2698501bcb0cdf67c7146/pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2", size = 6084060, upload-time = "2025-07-01T09:14:49.636Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ea/502d938cbaeec836ac28a9b730193716f0114c41325db428e6b280513f09/pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b", size = 6721407, upload-time = "2025-07-01T09:14:51.962Z" }, - { url = "https://files.pythonhosted.org/packages/45/9c/9c5e2a73f125f6cbc59cc7087c8f2d649a7ae453f83bd0362ff7c9e2aee2/pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3", size = 6273841, upload-time = "2025-07-01T09:14:54.142Z" }, - { url = "https://files.pythonhosted.org/packages/23/85/397c73524e0cd212067e0c969aa245b01d50183439550d24d9f55781b776/pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51", size = 6978450, upload-time = "2025-07-01T09:14:56.436Z" }, - { url = "https://files.pythonhosted.org/packages/17/d2/622f4547f69cd173955194b78e4d19ca4935a1b0f03a302d655c9f6aae65/pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580", size = 2423055, upload-time = "2025-07-01T09:14:58.072Z" }, - { url = "https://files.pythonhosted.org/packages/dd/80/a8a2ac21dda2e82480852978416cfacd439a4b490a501a288ecf4fe2532d/pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e", size = 5281110, upload-time = "2025-07-01T09:14:59.79Z" }, - { url = "https://files.pythonhosted.org/packages/44/d6/b79754ca790f315918732e18f82a8146d33bcd7f4494380457ea89eb883d/pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d", size = 4689547, upload-time = "2025-07-01T09:15:01.648Z" }, - { url = "https://files.pythonhosted.org/packages/49/20/716b8717d331150cb00f7fdd78169c01e8e0c219732a78b0e59b6bdb2fd6/pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced", size = 5901554, upload-time = "2025-07-03T13:10:27.018Z" }, - { url = "https://files.pythonhosted.org/packages/74/cf/a9f3a2514a65bb071075063a96f0a5cf949c2f2fce683c15ccc83b1c1cab/pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c", size = 7669132, upload-time = "2025-07-03T13:10:33.01Z" }, - { url = "https://files.pythonhosted.org/packages/98/3c/da78805cbdbee9cb43efe8261dd7cc0b4b93f2ac79b676c03159e9db2187/pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8", size = 6005001, upload-time = "2025-07-01T09:15:03.365Z" }, - { url = "https://files.pythonhosted.org/packages/6c/fa/ce044b91faecf30e635321351bba32bab5a7e034c60187fe9698191aef4f/pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59", size = 6668814, upload-time = "2025-07-01T09:15:05.655Z" }, - { url = "https://files.pythonhosted.org/packages/7b/51/90f9291406d09bf93686434f9183aba27b831c10c87746ff49f127ee80cb/pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe", size = 6113124, upload-time = "2025-07-01T09:15:07.358Z" }, - { url = "https://files.pythonhosted.org/packages/cd/5a/6fec59b1dfb619234f7636d4157d11fb4e196caeee220232a8d2ec48488d/pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c", size = 6747186, upload-time = "2025-07-01T09:15:09.317Z" }, - { url = "https://files.pythonhosted.org/packages/49/6b/00187a044f98255225f172de653941e61da37104a9ea60e4f6887717e2b5/pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788", size = 6277546, upload-time = "2025-07-01T09:15:11.311Z" }, - { url = "https://files.pythonhosted.org/packages/e8/5c/6caaba7e261c0d75bab23be79f1d06b5ad2a2ae49f028ccec801b0e853d6/pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31", size = 6985102, upload-time = "2025-07-01T09:15:13.164Z" }, - { url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", size = 2424803, upload-time = "2025-07-01T09:15:15.695Z" }, - { url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", size = 5278520, upload-time = "2025-07-01T09:15:17.429Z" }, - { url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", size = 4686116, upload-time = "2025-07-01T09:15:19.423Z" }, - { url = "https://files.pythonhosted.org/packages/49/2d/ed8bc0ab219ae8768f529597d9509d184fe8a6c4741a6864fea334d25f3f/pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632", size = 5864597, upload-time = "2025-07-03T13:10:38.404Z" }, - { url = "https://files.pythonhosted.org/packages/b5/3d/b932bb4225c80b58dfadaca9d42d08d0b7064d2d1791b6a237f87f661834/pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673", size = 7638246, upload-time = "2025-07-03T13:10:44.987Z" }, - { url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", size = 5973336, upload-time = "2025-07-01T09:15:21.237Z" }, - { url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", size = 6642699, upload-time = "2025-07-01T09:15:23.186Z" }, - { url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", size = 6083789, upload-time = "2025-07-01T09:15:25.1Z" }, - { url = "https://files.pythonhosted.org/packages/fe/54/86b0cd9dbb683a9d5e960b66c7379e821a19be4ac5810e2e5a715c09a0c0/pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a", size = 6720386, upload-time = "2025-07-01T09:15:27.378Z" }, - { url = "https://files.pythonhosted.org/packages/e7/95/88efcaf384c3588e24259c4203b909cbe3e3c2d887af9e938c2022c9dd48/pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214", size = 6370911, upload-time = "2025-07-01T09:15:29.294Z" }, - { url = "https://files.pythonhosted.org/packages/2e/cc/934e5820850ec5eb107e7b1a72dd278140731c669f396110ebc326f2a503/pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635", size = 7117383, upload-time = "2025-07-01T09:15:31.128Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", size = 2511385, upload-time = "2025-07-01T09:15:33.328Z" }, - { url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", size = 5281129, upload-time = "2025-07-01T09:15:35.194Z" }, - { url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", size = 4689580, upload-time = "2025-07-01T09:15:37.114Z" }, - { url = "https://files.pythonhosted.org/packages/eb/1c/537e930496149fbac69efd2fc4329035bbe2e5475b4165439e3be9cb183b/pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6", size = 5902860, upload-time = "2025-07-03T13:10:50.248Z" }, - { url = "https://files.pythonhosted.org/packages/bd/57/80f53264954dcefeebcf9dae6e3eb1daea1b488f0be8b8fef12f79a3eb10/pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36", size = 7670694, upload-time = "2025-07-03T13:10:56.432Z" }, - { url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", size = 6005888, upload-time = "2025-07-01T09:15:39.436Z" }, - { url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", size = 6670330, upload-time = "2025-07-01T09:15:41.269Z" }, - { url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", size = 6114089, upload-time = "2025-07-01T09:15:43.13Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f5/06bfaa444c8e80f1a8e4bff98da9c83b37b5be3b1deaa43d27a0db37ef84/pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b", size = 6748206, upload-time = "2025-07-01T09:15:44.937Z" }, - { url = "https://files.pythonhosted.org/packages/f0/77/bc6f92a3e8e6e46c0ca78abfffec0037845800ea38c73483760362804c41/pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12", size = 6377370, upload-time = "2025-07-01T09:15:46.673Z" }, - { url = "https://files.pythonhosted.org/packages/4a/82/3a721f7d69dca802befb8af08b7c79ebcab461007ce1c18bd91a5d5896f9/pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db", size = 7121500, upload-time = "2025-07-01T09:15:48.512Z" }, - { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835, upload-time = "2025-07-01T09:15:50.399Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8e/9c089f01677d1264ab8648352dcb7773f37da6ad002542760c80107da816/pillow-11.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:48d254f8a4c776de343051023eb61ffe818299eeac478da55227d96e241de53f", size = 5316478, upload-time = "2025-07-01T09:15:52.209Z" }, - { url = "https://files.pythonhosted.org/packages/b5/a9/5749930caf674695867eb56a581e78eb5f524b7583ff10b01b6e5048acb3/pillow-11.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7aee118e30a4cf54fdd873bd3a29de51e29105ab11f9aad8c32123f58c8f8081", size = 4686522, upload-time = "2025-07-01T09:15:54.162Z" }, - { url = "https://files.pythonhosted.org/packages/43/46/0b85b763eb292b691030795f9f6bb6fcaf8948c39413c81696a01c3577f7/pillow-11.3.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:23cff760a9049c502721bdb743a7cb3e03365fafcdfc2ef9784610714166e5a4", size = 5853376, upload-time = "2025-07-03T13:11:01.066Z" }, - { url = "https://files.pythonhosted.org/packages/5e/c6/1a230ec0067243cbd60bc2dad5dc3ab46a8a41e21c15f5c9b52b26873069/pillow-11.3.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6359a3bc43f57d5b375d1ad54a0074318a0844d11b76abccf478c37c986d3cfc", size = 7626020, upload-time = "2025-07-03T13:11:06.479Z" }, - { url = "https://files.pythonhosted.org/packages/63/dd/f296c27ffba447bfad76c6a0c44c1ea97a90cb9472b9304c94a732e8dbfb/pillow-11.3.0-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:092c80c76635f5ecb10f3f83d76716165c96f5229addbd1ec2bdbbda7d496e06", size = 5956732, upload-time = "2025-07-01T09:15:56.111Z" }, - { url = "https://files.pythonhosted.org/packages/a5/a0/98a3630f0b57f77bae67716562513d3032ae70414fcaf02750279c389a9e/pillow-11.3.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cadc9e0ea0a2431124cde7e1697106471fc4c1da01530e679b2391c37d3fbb3a", size = 6624404, upload-time = "2025-07-01T09:15:58.245Z" }, - { url = "https://files.pythonhosted.org/packages/de/e6/83dfba5646a290edd9a21964da07674409e410579c341fc5b8f7abd81620/pillow-11.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6a418691000f2a418c9135a7cf0d797c1bb7d9a485e61fe8e7722845b95ef978", size = 6067760, upload-time = "2025-07-01T09:16:00.003Z" }, - { url = "https://files.pythonhosted.org/packages/bc/41/15ab268fe6ee9a2bc7391e2bbb20a98d3974304ab1a406a992dcb297a370/pillow-11.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:97afb3a00b65cc0804d1c7abddbf090a81eaac02768af58cbdcaaa0a931e0b6d", size = 6700534, upload-time = "2025-07-01T09:16:02.29Z" }, - { url = "https://files.pythonhosted.org/packages/64/79/6d4f638b288300bed727ff29f2a3cb63db054b33518a95f27724915e3fbc/pillow-11.3.0-cp39-cp39-win32.whl", hash = "sha256:ea944117a7974ae78059fcc1800e5d3295172bb97035c0c1d9345fca1419da71", size = 6277091, upload-time = "2025-07-01T09:16:04.4Z" }, - { url = "https://files.pythonhosted.org/packages/46/05/4106422f45a05716fd34ed21763f8ec182e8ea00af6e9cb05b93a247361a/pillow-11.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:e5c5858ad8ec655450a7c7df532e9842cf8df7cc349df7225c60d5d348c8aada", size = 6986091, upload-time = "2025-07-01T09:16:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/63/c6/287fd55c2c12761d0591549d48885187579b7c257bef0c6660755b0b59ae/pillow-11.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:6abdbfd3aea42be05702a8dd98832329c167ee84400a1d1f61ab11437f1717eb", size = 2422632, upload-time = "2025-07-01T09:16:08.142Z" }, - { url = "https://files.pythonhosted.org/packages/6f/8b/209bd6b62ce8367f47e68a218bffac88888fdf2c9fcf1ecadc6c3ec1ebc7/pillow-11.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3cee80663f29e3843b68199b9d6f4f54bd1d4a6b59bdd91bceefc51238bcb967", size = 5270556, upload-time = "2025-07-01T09:16:09.961Z" }, - { url = "https://files.pythonhosted.org/packages/2e/e6/231a0b76070c2cfd9e260a7a5b504fb72da0a95279410fa7afd99d9751d6/pillow-11.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b5f56c3f344f2ccaf0dd875d3e180f631dc60a51b314295a3e681fe8cf851fbe", size = 4654625, upload-time = "2025-07-01T09:16:11.913Z" }, - { url = "https://files.pythonhosted.org/packages/13/f4/10cf94fda33cb12765f2397fc285fa6d8eb9c29de7f3185165b702fc7386/pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e67d793d180c9df62f1f40aee3accca4829d3794c95098887edc18af4b8b780c", size = 4874207, upload-time = "2025-07-03T13:11:10.201Z" }, - { url = "https://files.pythonhosted.org/packages/72/c9/583821097dc691880c92892e8e2d41fe0a5a3d6021f4963371d2f6d57250/pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d000f46e2917c705e9fb93a3606ee4a819d1e3aa7a9b442f6444f07e77cf5e25", size = 6583939, upload-time = "2025-07-03T13:11:15.68Z" }, - { url = "https://files.pythonhosted.org/packages/3b/8e/5c9d410f9217b12320efc7c413e72693f48468979a013ad17fd690397b9a/pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:527b37216b6ac3a12d7838dc3bd75208ec57c1c6d11ef01902266a5a0c14fc27", size = 4957166, upload-time = "2025-07-01T09:16:13.74Z" }, - { url = "https://files.pythonhosted.org/packages/62/bb/78347dbe13219991877ffb3a91bf09da8317fbfcd4b5f9140aeae020ad71/pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be5463ac478b623b9dd3937afd7fb7ab3d79dd290a28e2b6df292dc75063eb8a", size = 5581482, upload-time = "2025-07-01T09:16:16.107Z" }, - { url = "https://files.pythonhosted.org/packages/d9/28/1000353d5e61498aaeaaf7f1e4b49ddb05f2c6575f9d4f9f914a3538b6e1/pillow-11.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8dc70ca24c110503e16918a658b869019126ecfe03109b754c402daff12b3d9f", size = 6984596, upload-time = "2025-07-01T09:16:18.07Z" }, - { url = "https://files.pythonhosted.org/packages/9e/e3/6fa84033758276fb31da12e5fb66ad747ae83b93c67af17f8c6ff4cc8f34/pillow-11.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7c8ec7a017ad1bd562f93dbd8505763e688d388cde6e4a010ae1486916e713e6", size = 5270566, upload-time = "2025-07-01T09:16:19.801Z" }, - { url = "https://files.pythonhosted.org/packages/5b/ee/e8d2e1ab4892970b561e1ba96cbd59c0d28cf66737fc44abb2aec3795a4e/pillow-11.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9ab6ae226de48019caa8074894544af5b53a117ccb9d3b3dcb2871464c829438", size = 4654618, upload-time = "2025-07-01T09:16:21.818Z" }, - { url = "https://files.pythonhosted.org/packages/f2/6d/17f80f4e1f0761f02160fc433abd4109fa1548dcfdca46cfdadaf9efa565/pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe27fb049cdcca11f11a7bfda64043c37b30e6b91f10cb5bab275806c32f6ab3", size = 4874248, upload-time = "2025-07-03T13:11:20.738Z" }, - { url = "https://files.pythonhosted.org/packages/de/5f/c22340acd61cef960130585bbe2120e2fd8434c214802f07e8c03596b17e/pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:465b9e8844e3c3519a983d58b80be3f668e2a7a5db97f2784e7079fbc9f9822c", size = 6583963, upload-time = "2025-07-03T13:11:26.283Z" }, - { url = "https://files.pythonhosted.org/packages/31/5e/03966aedfbfcbb4d5f8aa042452d3361f325b963ebbadddac05b122e47dd/pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5418b53c0d59b3824d05e029669efa023bbef0f3e92e75ec8428f3799487f361", size = 4957170, upload-time = "2025-07-01T09:16:23.762Z" }, - { url = "https://files.pythonhosted.org/packages/cc/2d/e082982aacc927fc2cab48e1e731bdb1643a1406acace8bed0900a61464e/pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:504b6f59505f08ae014f724b6207ff6222662aab5cc9542577fb084ed0676ac7", size = 5581505, upload-time = "2025-07-01T09:16:25.593Z" }, - { url = "https://files.pythonhosted.org/packages/34/e7/ae39f538fd6844e982063c3a5e4598b8ced43b9633baa3a85ef33af8c05c/pillow-11.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c84d689db21a1c397d001aa08241044aa2069e7587b398c8cc63020390b1c1b8", size = 6984598, upload-time = "2025-07-01T09:16:27.732Z" }, -] - [[package]] name = "pillow" version = "12.1.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] sdist = { url = "https://files.pythonhosted.org/packages/d0/02/d52c733a2452ef1ffcc123b68e6606d07276b0e358db70eabad7e40042b7/pillow-12.1.0.tar.gz", hash = "sha256:5c5ae0a06e9ea030ab786b0251b32c7e4ce10e58d983c0d5c56029455180b5b9", size = 46977283, upload-time = "2026-01-02T09:13:29.892Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/fe/41/f73d92b6b883a579e79600d391f2e21cb0df767b2714ecbd2952315dfeef/pillow-12.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:fb125d860738a09d363a88daa0f59c4533529a90e564785e20fe875b200b6dbd", size = 5304089, upload-time = "2026-01-02T09:10:24.953Z" }, @@ -3199,38 +2108,10 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2d/71/64e9b1c7f04ae0027f788a248e6297d7fcc29571371fe7d45495a78172c0/pillow-12.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:75af0b4c229ac519b155028fa1be632d812a519abba9b46b20e50c6caa184f19", size = 7029809, upload-time = "2026-01-02T09:13:26.541Z" }, ] -[[package]] -name = "platformdirs" -version = "4.4.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654, upload-time = "2025-08-26T14:32:02.735Z" }, -] - [[package]] name = "platformdirs" version = "4.9.4" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] sdist = { url = "https://files.pythonhosted.org/packages/19/56/8d4c30c8a1d07013911a8fdbd8f89440ef9f08d07a1b50ab8ca8be5a20f9/platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934", size = 28737, upload-time = "2026-03-05T18:34:13.271Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868", size = 21216, upload-time = "2026-03-05T18:34:12.172Z" }, @@ -3242,8 +2123,7 @@ version = "6.5.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "narwhals" }, - { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "packaging", version = "26.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "packaging" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e3/4f/8a10a9b9f5192cb6fdef62f1d77fa7d834190b2c50c0cd256bd62879212b/plotly-6.5.2.tar.gz", hash = "sha256:7478555be0198562d1435dee4c308268187553cc15516a2f4dd034453699e393", size = 7015695, upload-time = "2026-01-14T21:26:51.222Z" } wheels = [ @@ -3384,21 +2264,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, - { url = "https://files.pythonhosted.org/packages/9b/01/0ebaec9003f5d619a7475165961f8e3083cf8644d704b60395df3601632d/propcache-0.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3d233076ccf9e450c8b3bc6720af226b898ef5d051a2d145f7d765e6e9f9bcff", size = 80277, upload-time = "2025-10-08T19:48:36.647Z" }, - { url = "https://files.pythonhosted.org/packages/34/58/04af97ac586b4ef6b9026c3fd36ee7798b737a832f5d3440a4280dcebd3a/propcache-0.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:357f5bb5c377a82e105e44bd3d52ba22b616f7b9773714bff93573988ef0a5fb", size = 45865, upload-time = "2025-10-08T19:48:37.859Z" }, - { url = "https://files.pythonhosted.org/packages/7c/19/b65d98ae21384518b291d9939e24a8aeac4fdb5101b732576f8f7540e834/propcache-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbc3b6dfc728105b2a57c06791eb07a94229202ea75c59db644d7d496b698cac", size = 47636, upload-time = "2025-10-08T19:48:39.038Z" }, - { url = "https://files.pythonhosted.org/packages/b3/0f/317048c6d91c356c7154dca5af019e6effeb7ee15fa6a6db327cc19e12b4/propcache-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:182b51b421f0501952d938dc0b0eb45246a5b5153c50d42b495ad5fb7517c888", size = 201126, upload-time = "2025-10-08T19:48:40.774Z" }, - { url = "https://files.pythonhosted.org/packages/71/69/0b2a7a5a6ee83292b4b997dbd80549d8ce7d40b6397c1646c0d9495f5a85/propcache-0.4.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4b536b39c5199b96fc6245eb5fb796c497381d3942f169e44e8e392b29c9ebcc", size = 209837, upload-time = "2025-10-08T19:48:42.167Z" }, - { url = "https://files.pythonhosted.org/packages/a5/92/c699ac495a6698df6e497fc2de27af4b6ace10d8e76528357ce153722e45/propcache-0.4.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db65d2af507bbfbdcedb254a11149f894169d90488dd3e7190f7cdcb2d6cd57a", size = 215578, upload-time = "2025-10-08T19:48:43.56Z" }, - { url = "https://files.pythonhosted.org/packages/b3/ee/14de81c5eb02c0ee4f500b4e39c4e1bd0677c06e72379e6ab18923c773fc/propcache-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd2dbc472da1f772a4dae4fa24be938a6c544671a912e30529984dd80400cd88", size = 197187, upload-time = "2025-10-08T19:48:45.309Z" }, - { url = "https://files.pythonhosted.org/packages/1d/94/48dce9aaa6d8dd5a0859bad75158ec522546d4ac23f8e2f05fac469477dd/propcache-0.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:daede9cd44e0f8bdd9e6cc9a607fc81feb80fae7a5fc6cecaff0e0bb32e42d00", size = 193478, upload-time = "2025-10-08T19:48:47.743Z" }, - { url = "https://files.pythonhosted.org/packages/60/b5/0516b563e801e1ace212afde869a0596a0d7115eec0b12d296d75633fb29/propcache-0.4.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:71b749281b816793678ae7f3d0d84bd36e694953822eaad408d682efc5ca18e0", size = 190650, upload-time = "2025-10-08T19:48:49.373Z" }, - { url = "https://files.pythonhosted.org/packages/24/89/e0f7d4a5978cd56f8cd67735f74052f257dc471ec901694e430f0d1572fe/propcache-0.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e", size = 200251, upload-time = "2025-10-08T19:48:51.4Z" }, - { url = "https://files.pythonhosted.org/packages/06/7d/a1fac863d473876ed4406c914f2e14aa82d2f10dd207c9e16fc383cc5a24/propcache-0.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fe49d0a85038f36ba9e3ffafa1103e61170b28e95b16622e11be0a0ea07c6781", size = 200919, upload-time = "2025-10-08T19:48:53.227Z" }, - { url = "https://files.pythonhosted.org/packages/c3/4e/f86a256ff24944cf5743e4e6c6994e3526f6acfcfb55e21694c2424f758c/propcache-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:99d43339c83aaf4d32bda60928231848eee470c6bda8d02599cc4cebe872d183", size = 193211, upload-time = "2025-10-08T19:48:55.027Z" }, - { url = "https://files.pythonhosted.org/packages/6e/3f/3fbad5f4356b068f1b047d300a6ff2c66614d7030f078cd50be3fec04228/propcache-0.4.1-cp39-cp39-win32.whl", hash = "sha256:a129e76735bc792794d5177069691c3217898b9f5cee2b2661471e52ffe13f19", size = 38314, upload-time = "2025-10-08T19:48:56.792Z" }, - { url = "https://files.pythonhosted.org/packages/a4/45/d78d136c3a3d215677abb886785aae744da2c3005bcb99e58640c56529b1/propcache-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:948dab269721ae9a87fd16c514a0a2c2a1bdb23a9a61b969b0f9d9ee2968546f", size = 41912, upload-time = "2025-10-08T19:48:57.995Z" }, - { url = "https://files.pythonhosted.org/packages/fc/2a/b0632941f25139f4e58450b307242951f7c2717a5704977c6d5323a800af/propcache-0.4.1-cp39-cp39-win_arm64.whl", hash = "sha256:5fd37c406dd6dc85aa743e214cef35dc54bbdd1419baac4f6ae5e5b1a2976938", size = 38450, upload-time = "2025-10-08T19:48:59.349Z" }, { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, ] @@ -3414,8 +2279,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4e/b1/c79468184310de09d75095ed1314b839eb2f72df71097db9d1404a1b2717/protobuf-6.33.5-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:9b71e0281f36f179d00cbcb119cb19dec4d14a81393e5ea220f64b286173e190", size = 324638, upload-time = "2026-01-29T21:51:26.423Z" }, { url = "https://files.pythonhosted.org/packages/c5/f5/65d838092fd01c44d16037953fd4c2cc851e783de9b8f02b27ec4ffd906f/protobuf-6.33.5-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8afa18e1d6d20af15b417e728e9f60f3aa108ee76f23c3b2c07a2c3b546d3afd", size = 339411, upload-time = "2026-01-29T21:51:27.446Z" }, { url = "https://files.pythonhosted.org/packages/9b/53/a9443aa3ca9ba8724fdfa02dd1887c1bcd8e89556b715cfbacca6b63dbec/protobuf-6.33.5-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:cbf16ba3350fb7b889fca858fb215967792dc125b35c7976ca4818bee3521cf0", size = 323465, upload-time = "2026-01-29T21:51:28.925Z" }, - { url = "https://files.pythonhosted.org/packages/08/60/84d5f6dcda9165e4d6a56ac8433c9f40a8906bf2966150b8a0cfde097d78/protobuf-6.33.5-cp39-cp39-win32.whl", hash = "sha256:a3157e62729aafb8df6da2c03aa5c0937c7266c626ce11a278b6eb7963c4e37c", size = 425892, upload-time = "2026-01-29T21:51:30.382Z" }, - { url = "https://files.pythonhosted.org/packages/68/19/33d7dc2dc84439587fa1e21e1c0026c01ad2af0a62f58fd54002a7546307/protobuf-6.33.5-cp39-cp39-win_amd64.whl", hash = "sha256:8f04fa32763dcdb4973d537d6b54e615cc61108c7cb38fe59310c3192d29510a", size = 437137, upload-time = "2026-01-29T21:51:31.456Z" }, { url = "https://files.pythonhosted.org/packages/57/bf/2086963c69bdac3d7cff1cc7ff79b8ce5ea0bec6797a017e1be338a46248/protobuf-6.33.5-py3-none-any.whl", hash = "sha256:69915a973dd0f60f31a08b8318b73eab2bd6a392c79184b3612226b0a3f8ec02", size = 170687, upload-time = "2026-01-29T21:51:32.557Z" }, ] @@ -3447,79 +2310,10 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, ] -[[package]] -name = "pyarrow" -version = "21.0.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/ef/c2/ea068b8f00905c06329a3dfcd40d0fcc2b7d0f2e355bdb25b65e0a0e4cd4/pyarrow-21.0.0.tar.gz", hash = "sha256:5051f2dccf0e283ff56335760cbc8622cf52264d67e359d5569541ac11b6d5bc", size = 1133487, upload-time = "2025-07-18T00:57:31.761Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/17/d9/110de31880016e2afc52d8580b397dbe47615defbf09ca8cf55f56c62165/pyarrow-21.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:e563271e2c5ff4d4a4cbeb2c83d5cf0d4938b891518e676025f7268c6fe5fe26", size = 31196837, upload-time = "2025-07-18T00:54:34.755Z" }, - { url = "https://files.pythonhosted.org/packages/df/5f/c1c1997613abf24fceb087e79432d24c19bc6f7259cab57c2c8e5e545fab/pyarrow-21.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:fee33b0ca46f4c85443d6c450357101e47d53e6c3f008d658c27a2d020d44c79", size = 32659470, upload-time = "2025-07-18T00:54:38.329Z" }, - { url = "https://files.pythonhosted.org/packages/3e/ed/b1589a777816ee33ba123ba1e4f8f02243a844fed0deec97bde9fb21a5cf/pyarrow-21.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:7be45519b830f7c24b21d630a31d48bcebfd5d4d7f9d3bdb49da9cdf6d764edb", size = 41055619, upload-time = "2025-07-18T00:54:42.172Z" }, - { url = "https://files.pythonhosted.org/packages/44/28/b6672962639e85dc0ac36f71ab3a8f5f38e01b51343d7aa372a6b56fa3f3/pyarrow-21.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:26bfd95f6bff443ceae63c65dc7e048670b7e98bc892210acba7e4995d3d4b51", size = 42733488, upload-time = "2025-07-18T00:54:47.132Z" }, - { url = "https://files.pythonhosted.org/packages/f8/cc/de02c3614874b9089c94eac093f90ca5dfa6d5afe45de3ba847fd950fdf1/pyarrow-21.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bd04ec08f7f8bd113c55868bd3fc442a9db67c27af098c5f814a3091e71cc61a", size = 43329159, upload-time = "2025-07-18T00:54:51.686Z" }, - { url = "https://files.pythonhosted.org/packages/a6/3e/99473332ac40278f196e105ce30b79ab8affab12f6194802f2593d6b0be2/pyarrow-21.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9b0b14b49ac10654332a805aedfc0147fb3469cbf8ea951b3d040dab12372594", size = 45050567, upload-time = "2025-07-18T00:54:56.679Z" }, - { url = "https://files.pythonhosted.org/packages/7b/f5/c372ef60593d713e8bfbb7e0c743501605f0ad00719146dc075faf11172b/pyarrow-21.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:9d9f8bcb4c3be7738add259738abdeddc363de1b80e3310e04067aa1ca596634", size = 26217959, upload-time = "2025-07-18T00:55:00.482Z" }, - { url = "https://files.pythonhosted.org/packages/94/dc/80564a3071a57c20b7c32575e4a0120e8a330ef487c319b122942d665960/pyarrow-21.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c077f48aab61738c237802836fc3844f85409a46015635198761b0d6a688f87b", size = 31243234, upload-time = "2025-07-18T00:55:03.812Z" }, - { url = "https://files.pythonhosted.org/packages/ea/cc/3b51cb2db26fe535d14f74cab4c79b191ed9a8cd4cbba45e2379b5ca2746/pyarrow-21.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:689f448066781856237eca8d1975b98cace19b8dd2ab6145bf49475478bcaa10", size = 32714370, upload-time = "2025-07-18T00:55:07.495Z" }, - { url = "https://files.pythonhosted.org/packages/24/11/a4431f36d5ad7d83b87146f515c063e4d07ef0b7240876ddb885e6b44f2e/pyarrow-21.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:479ee41399fcddc46159a551705b89c05f11e8b8cb8e968f7fec64f62d91985e", size = 41135424, upload-time = "2025-07-18T00:55:11.461Z" }, - { url = "https://files.pythonhosted.org/packages/74/dc/035d54638fc5d2971cbf1e987ccd45f1091c83bcf747281cf6cc25e72c88/pyarrow-21.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:40ebfcb54a4f11bcde86bc586cbd0272bac0d516cfa539c799c2453768477569", size = 42823810, upload-time = "2025-07-18T00:55:16.301Z" }, - { url = "https://files.pythonhosted.org/packages/2e/3b/89fced102448a9e3e0d4dded1f37fa3ce4700f02cdb8665457fcc8015f5b/pyarrow-21.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8d58d8497814274d3d20214fbb24abcad2f7e351474357d552a8d53bce70c70e", size = 43391538, upload-time = "2025-07-18T00:55:23.82Z" }, - { url = "https://files.pythonhosted.org/packages/fb/bb/ea7f1bd08978d39debd3b23611c293f64a642557e8141c80635d501e6d53/pyarrow-21.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:585e7224f21124dd57836b1530ac8f2df2afc43c861d7bf3d58a4870c42ae36c", size = 45120056, upload-time = "2025-07-18T00:55:28.231Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0b/77ea0600009842b30ceebc3337639a7380cd946061b620ac1a2f3cb541e2/pyarrow-21.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:555ca6935b2cbca2c0e932bedd853e9bc523098c39636de9ad4693b5b1df86d6", size = 26220568, upload-time = "2025-07-18T00:55:32.122Z" }, - { url = "https://files.pythonhosted.org/packages/ca/d4/d4f817b21aacc30195cf6a46ba041dd1be827efa4a623cc8bf39a1c2a0c0/pyarrow-21.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:3a302f0e0963db37e0a24a70c56cf91a4faa0bca51c23812279ca2e23481fccd", size = 31160305, upload-time = "2025-07-18T00:55:35.373Z" }, - { url = "https://files.pythonhosted.org/packages/a2/9c/dcd38ce6e4b4d9a19e1d36914cb8e2b1da4e6003dd075474c4cfcdfe0601/pyarrow-21.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:b6b27cf01e243871390474a211a7922bfbe3bda21e39bc9160daf0da3fe48876", size = 32684264, upload-time = "2025-07-18T00:55:39.303Z" }, - { url = "https://files.pythonhosted.org/packages/4f/74/2a2d9f8d7a59b639523454bec12dba35ae3d0a07d8ab529dc0809f74b23c/pyarrow-21.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e72a8ec6b868e258a2cd2672d91f2860ad532d590ce94cdf7d5e7ec674ccf03d", size = 41108099, upload-time = "2025-07-18T00:55:42.889Z" }, - { url = "https://files.pythonhosted.org/packages/ad/90/2660332eeb31303c13b653ea566a9918484b6e4d6b9d2d46879a33ab0622/pyarrow-21.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b7ae0bbdc8c6674259b25bef5d2a1d6af5d39d7200c819cf99e07f7dfef1c51e", size = 42829529, upload-time = "2025-07-18T00:55:47.069Z" }, - { url = "https://files.pythonhosted.org/packages/33/27/1a93a25c92717f6aa0fca06eb4700860577d016cd3ae51aad0e0488ac899/pyarrow-21.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:58c30a1729f82d201627c173d91bd431db88ea74dcaa3885855bc6203e433b82", size = 43367883, upload-time = "2025-07-18T00:55:53.069Z" }, - { url = "https://files.pythonhosted.org/packages/05/d9/4d09d919f35d599bc05c6950095e358c3e15148ead26292dfca1fb659b0c/pyarrow-21.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:072116f65604b822a7f22945a7a6e581cfa28e3454fdcc6939d4ff6090126623", size = 45133802, upload-time = "2025-07-18T00:55:57.714Z" }, - { url = "https://files.pythonhosted.org/packages/71/30/f3795b6e192c3ab881325ffe172e526499eb3780e306a15103a2764916a2/pyarrow-21.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf56ec8b0a5c8c9d7021d6fd754e688104f9ebebf1bf4449613c9531f5346a18", size = 26203175, upload-time = "2025-07-18T00:56:01.364Z" }, - { url = "https://files.pythonhosted.org/packages/16/ca/c7eaa8e62db8fb37ce942b1ea0c6d7abfe3786ca193957afa25e71b81b66/pyarrow-21.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e99310a4ebd4479bcd1964dff9e14af33746300cb014aa4a3781738ac63baf4a", size = 31154306, upload-time = "2025-07-18T00:56:04.42Z" }, - { url = "https://files.pythonhosted.org/packages/ce/e8/e87d9e3b2489302b3a1aea709aaca4b781c5252fcb812a17ab6275a9a484/pyarrow-21.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:d2fe8e7f3ce329a71b7ddd7498b3cfac0eeb200c2789bd840234f0dc271a8efe", size = 32680622, upload-time = "2025-07-18T00:56:07.505Z" }, - { url = "https://files.pythonhosted.org/packages/84/52/79095d73a742aa0aba370c7942b1b655f598069489ab387fe47261a849e1/pyarrow-21.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:f522e5709379d72fb3da7785aa489ff0bb87448a9dc5a75f45763a795a089ebd", size = 41104094, upload-time = "2025-07-18T00:56:10.994Z" }, - { url = "https://files.pythonhosted.org/packages/89/4b/7782438b551dbb0468892a276b8c789b8bbdb25ea5c5eb27faadd753e037/pyarrow-21.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:69cbbdf0631396e9925e048cfa5bce4e8c3d3b41562bbd70c685a8eb53a91e61", size = 42825576, upload-time = "2025-07-18T00:56:15.569Z" }, - { url = "https://files.pythonhosted.org/packages/b3/62/0f29de6e0a1e33518dec92c65be0351d32d7ca351e51ec5f4f837a9aab91/pyarrow-21.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:731c7022587006b755d0bdb27626a1a3bb004bb56b11fb30d98b6c1b4718579d", size = 43368342, upload-time = "2025-07-18T00:56:19.531Z" }, - { url = "https://files.pythonhosted.org/packages/90/c7/0fa1f3f29cf75f339768cc698c8ad4ddd2481c1742e9741459911c9ac477/pyarrow-21.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc56bc708f2d8ac71bd1dcb927e458c93cec10b98eb4120206a4091db7b67b99", size = 45131218, upload-time = "2025-07-18T00:56:23.347Z" }, - { url = "https://files.pythonhosted.org/packages/01/63/581f2076465e67b23bc5a37d4a2abff8362d389d29d8105832e82c9c811c/pyarrow-21.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:186aa00bca62139f75b7de8420f745f2af12941595bbbfa7ed3870ff63e25636", size = 26087551, upload-time = "2025-07-18T00:56:26.758Z" }, - { url = "https://files.pythonhosted.org/packages/c9/ab/357d0d9648bb8241ee7348e564f2479d206ebe6e1c47ac5027c2e31ecd39/pyarrow-21.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:a7a102574faa3f421141a64c10216e078df467ab9576684d5cd696952546e2da", size = 31290064, upload-time = "2025-07-18T00:56:30.214Z" }, - { url = "https://files.pythonhosted.org/packages/3f/8a/5685d62a990e4cac2043fc76b4661bf38d06efed55cf45a334b455bd2759/pyarrow-21.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:1e005378c4a2c6db3ada3ad4c217b381f6c886f0a80d6a316fe586b90f77efd7", size = 32727837, upload-time = "2025-07-18T00:56:33.935Z" }, - { url = "https://files.pythonhosted.org/packages/fc/de/c0828ee09525c2bafefd3e736a248ebe764d07d0fd762d4f0929dbc516c9/pyarrow-21.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:65f8e85f79031449ec8706b74504a316805217b35b6099155dd7e227eef0d4b6", size = 41014158, upload-time = "2025-07-18T00:56:37.528Z" }, - { url = "https://files.pythonhosted.org/packages/6e/26/a2865c420c50b7a3748320b614f3484bfcde8347b2639b2b903b21ce6a72/pyarrow-21.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:3a81486adc665c7eb1a2bde0224cfca6ceaba344a82a971ef059678417880eb8", size = 42667885, upload-time = "2025-07-18T00:56:41.483Z" }, - { url = "https://files.pythonhosted.org/packages/0a/f9/4ee798dc902533159250fb4321267730bc0a107d8c6889e07c3add4fe3a5/pyarrow-21.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fc0d2f88b81dcf3ccf9a6ae17f89183762c8a94a5bdcfa09e05cfe413acf0503", size = 43276625, upload-time = "2025-07-18T00:56:48.002Z" }, - { url = "https://files.pythonhosted.org/packages/5a/da/e02544d6997037a4b0d22d8e5f66bc9315c3671371a8b18c79ade1cefe14/pyarrow-21.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6299449adf89df38537837487a4f8d3bd91ec94354fdd2a7d30bc11c48ef6e79", size = 44951890, upload-time = "2025-07-18T00:56:52.568Z" }, - { url = "https://files.pythonhosted.org/packages/e5/4e/519c1bc1876625fe6b71e9a28287c43ec2f20f73c658b9ae1d485c0c206e/pyarrow-21.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:222c39e2c70113543982c6b34f3077962b44fca38c0bd9e68bb6781534425c10", size = 26371006, upload-time = "2025-07-18T00:56:56.379Z" }, - { url = "https://files.pythonhosted.org/packages/3e/cc/ce4939f4b316457a083dc5718b3982801e8c33f921b3c98e7a93b7c7491f/pyarrow-21.0.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:a7f6524e3747e35f80744537c78e7302cd41deee8baa668d56d55f77d9c464b3", size = 31211248, upload-time = "2025-07-18T00:56:59.7Z" }, - { url = "https://files.pythonhosted.org/packages/1f/c2/7a860931420d73985e2f340f06516b21740c15b28d24a0e99a900bb27d2b/pyarrow-21.0.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:203003786c9fd253ebcafa44b03c06983c9c8d06c3145e37f1b76a1f317aeae1", size = 32676896, upload-time = "2025-07-18T00:57:03.884Z" }, - { url = "https://files.pythonhosted.org/packages/68/a8/197f989b9a75e59b4ca0db6a13c56f19a0ad8a298c68da9cc28145e0bb97/pyarrow-21.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:3b4d97e297741796fead24867a8dabf86c87e4584ccc03167e4a811f50fdf74d", size = 41067862, upload-time = "2025-07-18T00:57:07.587Z" }, - { url = "https://files.pythonhosted.org/packages/fa/82/6ecfa89487b35aa21accb014b64e0a6b814cc860d5e3170287bf5135c7d8/pyarrow-21.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:898afce396b80fdda05e3086b4256f8677c671f7b1d27a6976fa011d3fd0a86e", size = 42747508, upload-time = "2025-07-18T00:57:13.917Z" }, - { url = "https://files.pythonhosted.org/packages/3b/b7/ba252f399bbf3addc731e8643c05532cf32e74cebb5e32f8f7409bc243cf/pyarrow-21.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:067c66ca29aaedae08218569a114e413b26e742171f526e828e1064fcdec13f4", size = 43345293, upload-time = "2025-07-18T00:57:19.828Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0a/a20819795bd702b9486f536a8eeb70a6aa64046fce32071c19ec8230dbaa/pyarrow-21.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0c4e75d13eb76295a49e0ea056eb18dbd87d81450bfeb8afa19a7e5a75ae2ad7", size = 45060670, upload-time = "2025-07-18T00:57:24.477Z" }, - { url = "https://files.pythonhosted.org/packages/10/15/6b30e77872012bbfe8265d42a01d5b3c17ef0ac0f2fae531ad91b6a6c02e/pyarrow-21.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdc4c17afda4dab2a9c0b79148a43a7f4e1094916b3e18d8975bfd6d6d52241f", size = 26227521, upload-time = "2025-07-18T00:57:29.119Z" }, -] - [[package]] name = "pyarrow" version = "23.0.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] sdist = { url = "https://files.pythonhosted.org/packages/01/33/ffd9c3eb087fa41dd79c3cf20c4c0ae3cdb877c4f8e1107a446006344924/pyarrow-23.0.0.tar.gz", hash = "sha256:180e3150e7edfcd182d3d9afba72f7cf19839a497cc76555a8dce998a8f67615", size = 1167185, upload-time = "2026-01-18T16:19:42.218Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ae/2f/23e042a5aa99bcb15e794e14030e8d065e00827e846e53a66faec73c7cd6/pyarrow-23.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:cbdc2bf5947aa4d462adcf8453cf04aee2f7932653cb67a27acd96e5e8528a67", size = 34281861, upload-time = "2026-01-18T16:13:34.332Z" }, @@ -3579,8 +2373,7 @@ version = "0.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jinja2" }, - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { 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'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/40e14e196864a0f61a92abb14d09b3d3da98f94ccb03b49cf51688140dab/pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605", size = 3832240, upload-time = "2024-05-10T15:36:21.153Z" } @@ -3602,8 +2395,7 @@ name = "pymdown-extensions" version = "10.21" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "markdown", version = "3.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "markdown", version = "3.10.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "markdown" }, { name = "pyyaml" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ba/63/06673d1eb6d8f83c0ea1f677d770e12565fb516928b4109c9e2055656a9e/pymdown_extensions-10.21.tar.gz", hash = "sha256:39f4a020f40773f6b2ff31d2cd2546c2c04d0a6498c31d9c688d2be07e1767d5", size = 853363, upload-time = "2026-02-15T20:44:06.748Z" } @@ -3620,55 +2412,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, ] -[[package]] -name = "pytest" -version = "8.4.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.10'" }, - { name = "iniconfig", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "pluggy", marker = "python_full_version < '3.10'" }, - { name = "pygments", marker = "python_full_version < '3.10'" }, - { name = "tomli", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, -] - [[package]] name = "pytest" version = "9.0.2" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] dependencies = [ - { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version == '3.10.*'" }, - { name = "iniconfig", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "packaging", version = "26.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "pluggy", marker = "python_full_version >= '3.10'" }, - { name = "pygments", marker = "python_full_version >= '3.10'" }, - { name = "tomli", marker = "python_full_version == '3.10.*'" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } wheels = [ @@ -3681,8 +2436,7 @@ version = "3.8.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "execnet" }, - { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "pytest", version = "9.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "pytest" }, ] sdist = { url = "https://files.pythonhosted.org/packages/78/b4/439b179d1ff526791eb921115fca8e44e596a13efeda518b9d845a619450/pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1", size = 88069, upload-time = "2025-07-01T13:30:59.346Z" } wheels = [ @@ -3772,15 +2526,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, - { url = "https://files.pythonhosted.org/packages/9f/62/67fc8e68a75f738c9200422bf65693fb79a4cd0dc5b23310e5202e978090/pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da", size = 184450, upload-time = "2025-09-25T21:33:00.618Z" }, - { url = "https://files.pythonhosted.org/packages/ae/92/861f152ce87c452b11b9d0977952259aa7df792d71c1053365cc7b09cc08/pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917", size = 174319, upload-time = "2025-09-25T21:33:02.086Z" }, - { url = "https://files.pythonhosted.org/packages/d0/cd/f0cfc8c74f8a030017a2b9c771b7f47e5dd702c3e28e5b2071374bda2948/pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9", size = 737631, upload-time = "2025-09-25T21:33:03.25Z" }, - { url = "https://files.pythonhosted.org/packages/ef/b2/18f2bd28cd2055a79a46c9b0895c0b3d987ce40ee471cecf58a1a0199805/pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5", size = 836795, upload-time = "2025-09-25T21:33:05.014Z" }, - { url = "https://files.pythonhosted.org/packages/73/b9/793686b2d54b531203c160ef12bec60228a0109c79bae6c1277961026770/pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a", size = 750767, upload-time = "2025-09-25T21:33:06.398Z" }, - { url = "https://files.pythonhosted.org/packages/a9/86/a137b39a611def2ed78b0e66ce2fe13ee701a07c07aebe55c340ed2a050e/pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926", size = 727982, upload-time = "2025-09-25T21:33:08.708Z" }, - { url = "https://files.pythonhosted.org/packages/dd/62/71c27c94f457cf4418ef8ccc71735324c549f7e3ea9d34aba50874563561/pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7", size = 755677, upload-time = "2025-09-25T21:33:09.876Z" }, - { url = "https://files.pythonhosted.org/packages/29/3d/6f5e0d58bd924fb0d06c3a6bad00effbdae2de5adb5cda5648006ffbd8d3/pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0", size = 142592, upload-time = "2025-09-25T21:33:10.983Z" }, - { url = "https://files.pythonhosted.org/packages/f0/0c/25113e0b5e103d7f1490c0e947e303fe4a696c10b501dea7a9f49d4e876c/pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007", size = 158777, upload-time = "2025-09-25T21:33:15.55Z" }, ] [[package]] @@ -3795,47 +2540,14 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722, upload-time = "2025-05-13T15:23:59.629Z" }, ] -[[package]] -name = "referencing" -version = "0.36.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "attrs", marker = "python_full_version < '3.10'" }, - { name = "rpds-py", version = "0.27.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "typing-extensions", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744, upload-time = "2025-01-25T08:48:16.138Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775, upload-time = "2025-01-25T08:48:14.241Z" }, -] - [[package]] name = "referencing" version = "0.37.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] dependencies = [ - { name = "attrs", marker = "python_full_version >= '3.10'" }, - { name = "rpds-py", version = "0.30.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.10' and python_full_version < '3.13'" }, + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } wheels = [ @@ -3961,23 +2673,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e3/0d/3a6cfa9ae99606afb612d8fb7a66b245a9d5ff0f29bb347c8a30b6ad561b/regex-2026.1.15-cp314-cp314t-win32.whl", hash = "sha256:e90b8db97f6f2c97eb045b51a6b2c5ed69cedd8392459e0642d4199b94fabd7e", size = 274667, upload-time = "2026-01-14T23:17:19.301Z" }, { url = "https://files.pythonhosted.org/packages/5b/b2/297293bb0742fd06b8d8e2572db41a855cdf1cae0bf009b1cb74fe07e196/regex-2026.1.15-cp314-cp314t-win_amd64.whl", hash = "sha256:5ef19071f4ac9f0834793af85bd04a920b4407715624e40cb7a0631a11137cdf", size = 284386, upload-time = "2026-01-14T23:17:21.231Z" }, { url = "https://files.pythonhosted.org/packages/95/e4/a3b9480c78cf8ee86626cb06f8d931d74d775897d44201ccb813097ae697/regex-2026.1.15-cp314-cp314t-win_arm64.whl", hash = "sha256:ca89c5e596fc05b015f27561b3793dc2fa0917ea0d7507eebb448efd35274a70", size = 274837, upload-time = "2026-01-14T23:17:23.146Z" }, - { url = "https://files.pythonhosted.org/packages/a2/e7/0e1913dc52eee9c5cf8417c9813c4c55972a3f37d27cfa2e623b79b63dbc/regex-2026.1.15-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:55b4ea996a8e4458dd7b584a2f89863b1655dd3d17b88b46cbb9becc495a0ec5", size = 488185, upload-time = "2026-01-14T23:17:25.2Z" }, - { url = "https://files.pythonhosted.org/packages/78/df/c52c1ff4221529faad0953e197982fe9508c6dbb42327e31bf98ea07472a/regex-2026.1.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e1e28be779884189cdd57735e997f282b64fd7ccf6e2eef3e16e57d7a34a815", size = 290628, upload-time = "2026-01-14T23:17:27.125Z" }, - { url = "https://files.pythonhosted.org/packages/4b/d2/a2fef3717deaff647d7de2bccf899a576c7eaf042b6b271fc4474515fe97/regex-2026.1.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0057de9eaef45783ff69fa94ae9f0fd906d629d0bd4c3217048f46d1daa32e9b", size = 288509, upload-time = "2026-01-14T23:17:29.017Z" }, - { url = "https://files.pythonhosted.org/packages/70/89/faf5ee5c69168753c845a3d58b4683f61c899d162bfe1264fca88d5b3924/regex-2026.1.15-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc7cd0b2be0f0269283a45c0d8b2c35e149d1319dcb4a43c9c3689fa935c1ee6", size = 781088, upload-time = "2026-01-14T23:17:30.961Z" }, - { url = "https://files.pythonhosted.org/packages/7d/2c/707e5c380ad547c93686e21144e7e24dc2064dd84ec5b751b6dbdfc9be2b/regex-2026.1.15-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8db052bbd981e1666f09e957f3790ed74080c2229007c1dd67afdbf0b469c48b", size = 850516, upload-time = "2026-01-14T23:17:32.946Z" }, - { url = "https://files.pythonhosted.org/packages/5d/3b/baa816cdcad1c0f8195f9f40ab2b2a2246c8a2989dcd90641c0c6559e3fd/regex-2026.1.15-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:343db82cb3712c31ddf720f097ef17c11dab2f67f7a3e7be976c4f82eba4e6df", size = 898124, upload-time = "2026-01-14T23:17:36.019Z" }, - { url = "https://files.pythonhosted.org/packages/e7/74/1eb46bde30899825ed9fdf645eba16b7b97c49d12d300f5177989b9a09a4/regex-2026.1.15-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:55e9d0118d97794367309635df398bdfd7c33b93e2fdfa0b239661cd74b4c14e", size = 791290, upload-time = "2026-01-14T23:17:38.097Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5d/b72e176fb21e2ec248baed01151a342d1f44dd43c2b6bb6a41ad183b274e/regex-2026.1.15-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:008b185f235acd1e53787333e5690082e4f156c44c87d894f880056089e9bc7c", size = 781996, upload-time = "2026-01-14T23:17:40.109Z" }, - { url = "https://files.pythonhosted.org/packages/61/0e/d3b3710eaafd994a4a71205d114abc38cda8691692a2ce2313abe68e7eb7/regex-2026.1.15-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fd65af65e2aaf9474e468f9e571bd7b189e1df3a61caa59dcbabd0000e4ea839", size = 767578, upload-time = "2026-01-14T23:17:42.134Z" }, - { url = "https://files.pythonhosted.org/packages/09/51/c6a6311833e040f95d229a34d82ac1cec2af8a5c00d58b244f2fceecef87/regex-2026.1.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f42e68301ff4afee63e365a5fc302b81bb8ba31af625a671d7acb19d10168a8c", size = 774354, upload-time = "2026-01-14T23:17:44.392Z" }, - { url = "https://files.pythonhosted.org/packages/cc/97/c522d1f19fb2c549aaf680b115c110cd124c02062bc8c95f33db8583b4bb/regex-2026.1.15-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:f7792f27d3ee6e0244ea4697d92b825f9a329ab5230a78c1a68bd274e64b5077", size = 845297, upload-time = "2026-01-14T23:17:47.145Z" }, - { url = "https://files.pythonhosted.org/packages/99/a0/99468c386ab68a5e24c946c5c353c29c33a95523e275c17839f2446db15d/regex-2026.1.15-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:dbaf3c3c37ef190439981648ccbf0c02ed99ae066087dd117fcb616d80b010a4", size = 755132, upload-time = "2026-01-14T23:17:49.796Z" }, - { url = "https://files.pythonhosted.org/packages/70/33/d5748c7b6c9d3621f12570583561ba529e2d1b12e4f70b8f17979b133e65/regex-2026.1.15-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:adc97a9077c2696501443d8ad3fa1b4fc6d131fc8fd7dfefd1a723f89071cf0a", size = 835662, upload-time = "2026-01-14T23:17:52.559Z" }, - { url = "https://files.pythonhosted.org/packages/ad/15/1986972c276672505437f1ba3c9706c2d91f321cfb9b2f4d06e8bff1b999/regex-2026.1.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:069f56a7bf71d286a6ff932a9e6fb878f151c998ebb2519a9f6d1cee4bffdba3", size = 779513, upload-time = "2026-01-14T23:17:54.711Z" }, - { url = "https://files.pythonhosted.org/packages/bc/f9/124f6a5cb3969d8e30471ed4f46cfc17c47aef1a9863ee8b4ba1d98b1bc4/regex-2026.1.15-cp39-cp39-win32.whl", hash = "sha256:ea4e6b3566127fda5e007e90a8fd5a4169f0cf0619506ed426db647f19c8454a", size = 265923, upload-time = "2026-01-14T23:17:56.69Z" }, - { url = "https://files.pythonhosted.org/packages/7b/c2/bb8fad7d27f1d71fc9772befd544bccd22eddc62a6735f57b003b4aff005/regex-2026.1.15-cp39-cp39-win_amd64.whl", hash = "sha256:cda1ed70d2b264952e88adaa52eea653a33a1b98ac907ae2f86508eb44f65cdc", size = 277900, upload-time = "2026-01-14T23:17:58.72Z" }, - { url = "https://files.pythonhosted.org/packages/f7/fa/4e033327c1d8350bc812cac906d873984d3d4b39529252f392a47ccc356d/regex-2026.1.15-cp39-cp39-win_arm64.whl", hash = "sha256:b325d4714c3c48277bfea1accd94e193ad6ed42b4bad79ad64f3b8f8a31260a5", size = 270413, upload-time = "2026-01-14T23:18:00.764Z" }, ] [[package]] @@ -3995,191 +2690,10 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, ] -[[package]] -name = "rpds-py" -version = "0.27.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/e9/dd/2c0cbe774744272b0ae725f44032c77bdcab6e8bcf544bffa3b6e70c8dba/rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8", size = 27479, upload-time = "2025-08-27T12:16:36.024Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/ed/3aef893e2dd30e77e35d20d4ddb45ca459db59cead748cad9796ad479411/rpds_py-0.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:68afeec26d42ab3b47e541b272166a0b4400313946871cba3ed3a4fc0cab1cef", size = 371606, upload-time = "2025-08-27T12:12:25.189Z" }, - { url = "https://files.pythonhosted.org/packages/6d/82/9818b443e5d3eb4c83c3994561387f116aae9833b35c484474769c4a8faf/rpds_py-0.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74e5b2f7bb6fa38b1b10546d27acbacf2a022a8b5543efb06cfebc72a59c85be", size = 353452, upload-time = "2025-08-27T12:12:27.433Z" }, - { url = "https://files.pythonhosted.org/packages/99/c7/d2a110ffaaa397fc6793a83c7bd3545d9ab22658b7cdff05a24a4535cc45/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9024de74731df54546fab0bfbcdb49fae19159ecaecfc8f37c18d2c7e2c0bd61", size = 381519, upload-time = "2025-08-27T12:12:28.719Z" }, - { url = "https://files.pythonhosted.org/packages/5a/bc/e89581d1f9d1be7d0247eaef602566869fdc0d084008ba139e27e775366c/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31d3ebadefcd73b73928ed0b2fd696f7fefda8629229f81929ac9c1854d0cffb", size = 394424, upload-time = "2025-08-27T12:12:30.207Z" }, - { url = "https://files.pythonhosted.org/packages/ac/2e/36a6861f797530e74bb6ed53495f8741f1ef95939eed01d761e73d559067/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2e7f8f169d775dd9092a1743768d771f1d1300453ddfe6325ae3ab5332b4657", size = 523467, upload-time = "2025-08-27T12:12:31.808Z" }, - { url = "https://files.pythonhosted.org/packages/c4/59/c1bc2be32564fa499f988f0a5c6505c2f4746ef96e58e4d7de5cf923d77e/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d905d16f77eb6ab2e324e09bfa277b4c8e5e6b8a78a3e7ff8f3cdf773b4c013", size = 402660, upload-time = "2025-08-27T12:12:33.444Z" }, - { url = "https://files.pythonhosted.org/packages/0a/ec/ef8bf895f0628dd0a59e54d81caed6891663cb9c54a0f4bb7da918cb88cf/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50c946f048209e6362e22576baea09193809f87687a95a8db24e5fbdb307b93a", size = 384062, upload-time = "2025-08-27T12:12:34.857Z" }, - { url = "https://files.pythonhosted.org/packages/69/f7/f47ff154be8d9a5e691c083a920bba89cef88d5247c241c10b9898f595a1/rpds_py-0.27.1-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:3deab27804d65cd8289eb814c2c0e807c4b9d9916c9225e363cb0cf875eb67c1", size = 401289, upload-time = "2025-08-27T12:12:36.085Z" }, - { url = "https://files.pythonhosted.org/packages/3b/d9/ca410363efd0615814ae579f6829cafb39225cd63e5ea5ed1404cb345293/rpds_py-0.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8b61097f7488de4be8244c89915da8ed212832ccf1e7c7753a25a394bf9b1f10", size = 417718, upload-time = "2025-08-27T12:12:37.401Z" }, - { url = "https://files.pythonhosted.org/packages/e3/a0/8cb5c2ff38340f221cc067cc093d1270e10658ba4e8d263df923daa18e86/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8a3f29aba6e2d7d90528d3c792555a93497fe6538aa65eb675b44505be747808", size = 558333, upload-time = "2025-08-27T12:12:38.672Z" }, - { url = "https://files.pythonhosted.org/packages/6f/8c/1b0de79177c5d5103843774ce12b84caa7164dfc6cd66378768d37db11bf/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd6cd0485b7d347304067153a6dc1d73f7d4fd995a396ef32a24d24b8ac63ac8", size = 589127, upload-time = "2025-08-27T12:12:41.48Z" }, - { url = "https://files.pythonhosted.org/packages/c8/5e/26abb098d5e01266b0f3a2488d299d19ccc26849735d9d2b95c39397e945/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f4461bf931108c9fa226ffb0e257c1b18dc2d44cd72b125bec50ee0ab1248a9", size = 554899, upload-time = "2025-08-27T12:12:42.925Z" }, - { url = "https://files.pythonhosted.org/packages/de/41/905cc90ced13550db017f8f20c6d8e8470066c5738ba480d7ba63e3d136b/rpds_py-0.27.1-cp310-cp310-win32.whl", hash = "sha256:ee5422d7fb21f6a00c1901bf6559c49fee13a5159d0288320737bbf6585bd3e4", size = 217450, upload-time = "2025-08-27T12:12:44.813Z" }, - { url = "https://files.pythonhosted.org/packages/75/3d/6bef47b0e253616ccdf67c283e25f2d16e18ccddd38f92af81d5a3420206/rpds_py-0.27.1-cp310-cp310-win_amd64.whl", hash = "sha256:3e039aabf6d5f83c745d5f9a0a381d031e9ed871967c0a5c38d201aca41f3ba1", size = 228447, upload-time = "2025-08-27T12:12:46.204Z" }, - { url = "https://files.pythonhosted.org/packages/b5/c1/7907329fbef97cbd49db6f7303893bd1dd5a4a3eae415839ffdfb0762cae/rpds_py-0.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:be898f271f851f68b318872ce6ebebbc62f303b654e43bf72683dbdc25b7c881", size = 371063, upload-time = "2025-08-27T12:12:47.856Z" }, - { url = "https://files.pythonhosted.org/packages/11/94/2aab4bc86228bcf7c48760990273653a4900de89c7537ffe1b0d6097ed39/rpds_py-0.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:62ac3d4e3e07b58ee0ddecd71d6ce3b1637de2d373501412df395a0ec5f9beb5", size = 353210, upload-time = "2025-08-27T12:12:49.187Z" }, - { url = "https://files.pythonhosted.org/packages/3a/57/f5eb3ecf434342f4f1a46009530e93fd201a0b5b83379034ebdb1d7c1a58/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4708c5c0ceb2d034f9991623631d3d23cb16e65c83736ea020cdbe28d57c0a0e", size = 381636, upload-time = "2025-08-27T12:12:50.492Z" }, - { url = "https://files.pythonhosted.org/packages/ae/f4/ef95c5945e2ceb5119571b184dd5a1cc4b8541bbdf67461998cfeac9cb1e/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:abfa1171a9952d2e0002aba2ad3780820b00cc3d9c98c6630f2e93271501f66c", size = 394341, upload-time = "2025-08-27T12:12:52.024Z" }, - { url = "https://files.pythonhosted.org/packages/5a/7e/4bd610754bf492d398b61725eb9598ddd5eb86b07d7d9483dbcd810e20bc/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b507d19f817ebaca79574b16eb2ae412e5c0835542c93fe9983f1e432aca195", size = 523428, upload-time = "2025-08-27T12:12:53.779Z" }, - { url = "https://files.pythonhosted.org/packages/9f/e5/059b9f65a8c9149361a8b75094864ab83b94718344db511fd6117936ed2a/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168b025f8fd8d8d10957405f3fdcef3dc20f5982d398f90851f4abc58c566c52", size = 402923, upload-time = "2025-08-27T12:12:55.15Z" }, - { url = "https://files.pythonhosted.org/packages/f5/48/64cabb7daced2968dd08e8a1b7988bf358d7bd5bcd5dc89a652f4668543c/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb56c6210ef77caa58e16e8c17d35c63fe3f5b60fd9ba9d424470c3400bcf9ed", size = 384094, upload-time = "2025-08-27T12:12:57.194Z" }, - { url = "https://files.pythonhosted.org/packages/ae/e1/dc9094d6ff566bff87add8a510c89b9e158ad2ecd97ee26e677da29a9e1b/rpds_py-0.27.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:d252f2d8ca0195faa707f8eb9368955760880b2b42a8ee16d382bf5dd807f89a", size = 401093, upload-time = "2025-08-27T12:12:58.985Z" }, - { url = "https://files.pythonhosted.org/packages/37/8e/ac8577e3ecdd5593e283d46907d7011618994e1d7ab992711ae0f78b9937/rpds_py-0.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6e5e54da1e74b91dbc7996b56640f79b195d5925c2b78efaa8c5d53e1d88edde", size = 417969, upload-time = "2025-08-27T12:13:00.367Z" }, - { url = "https://files.pythonhosted.org/packages/66/6d/87507430a8f74a93556fe55c6485ba9c259949a853ce407b1e23fea5ba31/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ffce0481cc6e95e5b3f0a47ee17ffbd234399e6d532f394c8dce320c3b089c21", size = 558302, upload-time = "2025-08-27T12:13:01.737Z" }, - { url = "https://files.pythonhosted.org/packages/3a/bb/1db4781ce1dda3eecc735e3152659a27b90a02ca62bfeea17aee45cc0fbc/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a205fdfe55c90c2cd8e540ca9ceba65cbe6629b443bc05db1f590a3db8189ff9", size = 589259, upload-time = "2025-08-27T12:13:03.127Z" }, - { url = "https://files.pythonhosted.org/packages/7b/0e/ae1c8943d11a814d01b482e1f8da903f88047a962dff9bbdadf3bd6e6fd1/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:689fb5200a749db0415b092972e8eba85847c23885c8543a8b0f5c009b1a5948", size = 554983, upload-time = "2025-08-27T12:13:04.516Z" }, - { url = "https://files.pythonhosted.org/packages/b2/d5/0b2a55415931db4f112bdab072443ff76131b5ac4f4dc98d10d2d357eb03/rpds_py-0.27.1-cp311-cp311-win32.whl", hash = "sha256:3182af66048c00a075010bc7f4860f33913528a4b6fc09094a6e7598e462fe39", size = 217154, upload-time = "2025-08-27T12:13:06.278Z" }, - { url = "https://files.pythonhosted.org/packages/24/75/3b7ffe0d50dc86a6a964af0d1cc3a4a2cdf437cb7b099a4747bbb96d1819/rpds_py-0.27.1-cp311-cp311-win_amd64.whl", hash = "sha256:b4938466c6b257b2f5c4ff98acd8128ec36b5059e5c8f8372d79316b1c36bb15", size = 228627, upload-time = "2025-08-27T12:13:07.625Z" }, - { url = "https://files.pythonhosted.org/packages/8d/3f/4fd04c32abc02c710f09a72a30c9a55ea3cc154ef8099078fd50a0596f8e/rpds_py-0.27.1-cp311-cp311-win_arm64.whl", hash = "sha256:2f57af9b4d0793e53266ee4325535a31ba48e2f875da81a9177c9926dfa60746", size = 220998, upload-time = "2025-08-27T12:13:08.972Z" }, - { url = "https://files.pythonhosted.org/packages/bd/fe/38de28dee5df58b8198c743fe2bea0c785c6d40941b9950bac4cdb71a014/rpds_py-0.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ae2775c1973e3c30316892737b91f9283f9908e3cc7625b9331271eaaed7dc90", size = 361887, upload-time = "2025-08-27T12:13:10.233Z" }, - { url = "https://files.pythonhosted.org/packages/7c/9a/4b6c7eedc7dd90986bf0fab6ea2a091ec11c01b15f8ba0a14d3f80450468/rpds_py-0.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2643400120f55c8a96f7c9d858f7be0c88d383cd4653ae2cf0d0c88f668073e5", size = 345795, upload-time = "2025-08-27T12:13:11.65Z" }, - { url = "https://files.pythonhosted.org/packages/6f/0e/e650e1b81922847a09cca820237b0edee69416a01268b7754d506ade11ad/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16323f674c089b0360674a4abd28d5042947d54ba620f72514d69be4ff64845e", size = 385121, upload-time = "2025-08-27T12:13:13.008Z" }, - { url = "https://files.pythonhosted.org/packages/1b/ea/b306067a712988e2bff00dcc7c8f31d26c29b6d5931b461aa4b60a013e33/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a1f4814b65eacac94a00fc9a526e3fdafd78e439469644032032d0d63de4881", size = 398976, upload-time = "2025-08-27T12:13:14.368Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0a/26dc43c8840cb8fe239fe12dbc8d8de40f2365e838f3d395835dde72f0e5/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba32c16b064267b22f1850a34051121d423b6f7338a12b9459550eb2096e7ec", size = 525953, upload-time = "2025-08-27T12:13:15.774Z" }, - { url = "https://files.pythonhosted.org/packages/22/14/c85e8127b573aaf3a0cbd7fbb8c9c99e735a4a02180c84da2a463b766e9e/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5c20f33fd10485b80f65e800bbe5f6785af510b9f4056c5a3c612ebc83ba6cb", size = 407915, upload-time = "2025-08-27T12:13:17.379Z" }, - { url = "https://files.pythonhosted.org/packages/ed/7b/8f4fee9ba1fb5ec856eb22d725a4efa3deb47f769597c809e03578b0f9d9/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:466bfe65bd932da36ff279ddd92de56b042f2266d752719beb97b08526268ec5", size = 386883, upload-time = "2025-08-27T12:13:18.704Z" }, - { url = "https://files.pythonhosted.org/packages/86/47/28fa6d60f8b74fcdceba81b272f8d9836ac0340570f68f5df6b41838547b/rpds_py-0.27.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:41e532bbdcb57c92ba3be62c42e9f096431b4cf478da9bc3bc6ce5c38ab7ba7a", size = 405699, upload-time = "2025-08-27T12:13:20.089Z" }, - { url = "https://files.pythonhosted.org/packages/d0/fd/c5987b5e054548df56953a21fe2ebed51fc1ec7c8f24fd41c067b68c4a0a/rpds_py-0.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f149826d742b406579466283769a8ea448eed82a789af0ed17b0cd5770433444", size = 423713, upload-time = "2025-08-27T12:13:21.436Z" }, - { url = "https://files.pythonhosted.org/packages/ac/ba/3c4978b54a73ed19a7d74531be37a8bcc542d917c770e14d372b8daea186/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80c60cfb5310677bd67cb1e85a1e8eb52e12529545441b43e6f14d90b878775a", size = 562324, upload-time = "2025-08-27T12:13:22.789Z" }, - { url = "https://files.pythonhosted.org/packages/b5/6c/6943a91768fec16db09a42b08644b960cff540c66aab89b74be6d4a144ba/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7ee6521b9baf06085f62ba9c7a3e5becffbc32480d2f1b351559c001c38ce4c1", size = 593646, upload-time = "2025-08-27T12:13:24.122Z" }, - { url = "https://files.pythonhosted.org/packages/11/73/9d7a8f4be5f4396f011a6bb7a19fe26303a0dac9064462f5651ced2f572f/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a512c8263249a9d68cac08b05dd59d2b3f2061d99b322813cbcc14c3c7421998", size = 558137, upload-time = "2025-08-27T12:13:25.557Z" }, - { url = "https://files.pythonhosted.org/packages/6e/96/6772cbfa0e2485bcceef8071de7821f81aeac8bb45fbfd5542a3e8108165/rpds_py-0.27.1-cp312-cp312-win32.whl", hash = "sha256:819064fa048ba01b6dadc5116f3ac48610435ac9a0058bbde98e569f9e785c39", size = 221343, upload-time = "2025-08-27T12:13:26.967Z" }, - { url = "https://files.pythonhosted.org/packages/67/b6/c82f0faa9af1c6a64669f73a17ee0eeef25aff30bb9a1c318509efe45d84/rpds_py-0.27.1-cp312-cp312-win_amd64.whl", hash = "sha256:d9199717881f13c32c4046a15f024971a3b78ad4ea029e8da6b86e5aa9cf4594", size = 232497, upload-time = "2025-08-27T12:13:28.326Z" }, - { url = "https://files.pythonhosted.org/packages/e1/96/2817b44bd2ed11aebacc9251da03689d56109b9aba5e311297b6902136e2/rpds_py-0.27.1-cp312-cp312-win_arm64.whl", hash = "sha256:33aa65b97826a0e885ef6e278fbd934e98cdcfed80b63946025f01e2f5b29502", size = 222790, upload-time = "2025-08-27T12:13:29.71Z" }, - { url = "https://files.pythonhosted.org/packages/cc/77/610aeee8d41e39080c7e14afa5387138e3c9fa9756ab893d09d99e7d8e98/rpds_py-0.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e4b9fcfbc021633863a37e92571d6f91851fa656f0180246e84cbd8b3f6b329b", size = 361741, upload-time = "2025-08-27T12:13:31.039Z" }, - { url = "https://files.pythonhosted.org/packages/3a/fc/c43765f201c6a1c60be2043cbdb664013def52460a4c7adace89d6682bf4/rpds_py-0.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1441811a96eadca93c517d08df75de45e5ffe68aa3089924f963c782c4b898cf", size = 345574, upload-time = "2025-08-27T12:13:32.902Z" }, - { url = "https://files.pythonhosted.org/packages/20/42/ee2b2ca114294cd9847d0ef9c26d2b0851b2e7e00bf14cc4c0b581df0fc3/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55266dafa22e672f5a4f65019015f90336ed31c6383bd53f5e7826d21a0e0b83", size = 385051, upload-time = "2025-08-27T12:13:34.228Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e8/1e430fe311e4799e02e2d1af7c765f024e95e17d651612425b226705f910/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78827d7ac08627ea2c8e02c9e5b41180ea5ea1f747e9db0915e3adf36b62dcf", size = 398395, upload-time = "2025-08-27T12:13:36.132Z" }, - { url = "https://files.pythonhosted.org/packages/82/95/9dc227d441ff2670651c27a739acb2535ccaf8b351a88d78c088965e5996/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae92443798a40a92dc5f0b01d8a7c93adde0c4dc965310a29ae7c64d72b9fad2", size = 524334, upload-time = "2025-08-27T12:13:37.562Z" }, - { url = "https://files.pythonhosted.org/packages/87/01/a670c232f401d9ad461d9a332aa4080cd3cb1d1df18213dbd0d2a6a7ab51/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c46c9dd2403b66a2a3b9720ec4b74d4ab49d4fabf9f03dfdce2d42af913fe8d0", size = 407691, upload-time = "2025-08-27T12:13:38.94Z" }, - { url = "https://files.pythonhosted.org/packages/03/36/0a14aebbaa26fe7fab4780c76f2239e76cc95a0090bdb25e31d95c492fcd/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2efe4eb1d01b7f5f1939f4ef30ecea6c6b3521eec451fb93191bf84b2a522418", size = 386868, upload-time = "2025-08-27T12:13:40.192Z" }, - { url = "https://files.pythonhosted.org/packages/3b/03/8c897fb8b5347ff6c1cc31239b9611c5bf79d78c984430887a353e1409a1/rpds_py-0.27.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:15d3b4d83582d10c601f481eca29c3f138d44c92187d197aff663a269197c02d", size = 405469, upload-time = "2025-08-27T12:13:41.496Z" }, - { url = "https://files.pythonhosted.org/packages/da/07/88c60edc2df74850d496d78a1fdcdc7b54360a7f610a4d50008309d41b94/rpds_py-0.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4ed2e16abbc982a169d30d1a420274a709949e2cbdef119fe2ec9d870b42f274", size = 422125, upload-time = "2025-08-27T12:13:42.802Z" }, - { url = "https://files.pythonhosted.org/packages/6b/86/5f4c707603e41b05f191a749984f390dabcbc467cf833769b47bf14ba04f/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a75f305c9b013289121ec0f1181931975df78738cdf650093e6b86d74aa7d8dd", size = 562341, upload-time = "2025-08-27T12:13:44.472Z" }, - { url = "https://files.pythonhosted.org/packages/b2/92/3c0cb2492094e3cd9baf9e49bbb7befeceb584ea0c1a8b5939dca4da12e5/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:67ce7620704745881a3d4b0ada80ab4d99df390838839921f99e63c474f82cf2", size = 592511, upload-time = "2025-08-27T12:13:45.898Z" }, - { url = "https://files.pythonhosted.org/packages/10/bb/82e64fbb0047c46a168faa28d0d45a7851cd0582f850b966811d30f67ad8/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d992ac10eb86d9b6f369647b6a3f412fc0075cfd5d799530e84d335e440a002", size = 557736, upload-time = "2025-08-27T12:13:47.408Z" }, - { url = "https://files.pythonhosted.org/packages/00/95/3c863973d409210da7fb41958172c6b7dbe7fc34e04d3cc1f10bb85e979f/rpds_py-0.27.1-cp313-cp313-win32.whl", hash = "sha256:4f75e4bd8ab8db624e02c8e2fc4063021b58becdbe6df793a8111d9343aec1e3", size = 221462, upload-time = "2025-08-27T12:13:48.742Z" }, - { url = "https://files.pythonhosted.org/packages/ce/2c/5867b14a81dc217b56d95a9f2a40fdbc56a1ab0181b80132beeecbd4b2d6/rpds_py-0.27.1-cp313-cp313-win_amd64.whl", hash = "sha256:f9025faafc62ed0b75a53e541895ca272815bec18abe2249ff6501c8f2e12b83", size = 232034, upload-time = "2025-08-27T12:13:50.11Z" }, - { url = "https://files.pythonhosted.org/packages/c7/78/3958f3f018c01923823f1e47f1cc338e398814b92d83cd278364446fac66/rpds_py-0.27.1-cp313-cp313-win_arm64.whl", hash = "sha256:ed10dc32829e7d222b7d3b93136d25a406ba9788f6a7ebf6809092da1f4d279d", size = 222392, upload-time = "2025-08-27T12:13:52.587Z" }, - { url = "https://files.pythonhosted.org/packages/01/76/1cdf1f91aed5c3a7bf2eba1f1c4e4d6f57832d73003919a20118870ea659/rpds_py-0.27.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:92022bbbad0d4426e616815b16bc4127f83c9a74940e1ccf3cfe0b387aba0228", size = 358355, upload-time = "2025-08-27T12:13:54.012Z" }, - { url = "https://files.pythonhosted.org/packages/c3/6f/bf142541229374287604caf3bb2a4ae17f0a580798fd72d3b009b532db4e/rpds_py-0.27.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:47162fdab9407ec3f160805ac3e154df042e577dd53341745fc7fb3f625e6d92", size = 342138, upload-time = "2025-08-27T12:13:55.791Z" }, - { url = "https://files.pythonhosted.org/packages/1a/77/355b1c041d6be40886c44ff5e798b4e2769e497b790f0f7fd1e78d17e9a8/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb89bec23fddc489e5d78b550a7b773557c9ab58b7946154a10a6f7a214a48b2", size = 380247, upload-time = "2025-08-27T12:13:57.683Z" }, - { url = "https://files.pythonhosted.org/packages/d6/a4/d9cef5c3946ea271ce2243c51481971cd6e34f21925af2783dd17b26e815/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e48af21883ded2b3e9eb48cb7880ad8598b31ab752ff3be6457001d78f416723", size = 390699, upload-time = "2025-08-27T12:13:59.137Z" }, - { url = "https://files.pythonhosted.org/packages/3a/06/005106a7b8c6c1a7e91b73169e49870f4af5256119d34a361ae5240a0c1d/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f5b7bd8e219ed50299e58551a410b64daafb5017d54bbe822e003856f06a802", size = 521852, upload-time = "2025-08-27T12:14:00.583Z" }, - { url = "https://files.pythonhosted.org/packages/e5/3e/50fb1dac0948e17a02eb05c24510a8fe12d5ce8561c6b7b7d1339ab7ab9c/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08f1e20bccf73b08d12d804d6e1c22ca5530e71659e6673bce31a6bb71c1e73f", size = 402582, upload-time = "2025-08-27T12:14:02.034Z" }, - { url = "https://files.pythonhosted.org/packages/cb/b0/f4e224090dc5b0ec15f31a02d746ab24101dd430847c4d99123798661bfc/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dc5dceeaefcc96dc192e3a80bbe1d6c410c469e97bdd47494a7d930987f18b2", size = 384126, upload-time = "2025-08-27T12:14:03.437Z" }, - { url = "https://files.pythonhosted.org/packages/54/77/ac339d5f82b6afff1df8f0fe0d2145cc827992cb5f8eeb90fc9f31ef7a63/rpds_py-0.27.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:d76f9cc8665acdc0c9177043746775aa7babbf479b5520b78ae4002d889f5c21", size = 399486, upload-time = "2025-08-27T12:14:05.443Z" }, - { url = "https://files.pythonhosted.org/packages/d6/29/3e1c255eee6ac358c056a57d6d6869baa00a62fa32eea5ee0632039c50a3/rpds_py-0.27.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:134fae0e36022edad8290a6661edf40c023562964efea0cc0ec7f5d392d2aaef", size = 414832, upload-time = "2025-08-27T12:14:06.902Z" }, - { url = "https://files.pythonhosted.org/packages/3f/db/6d498b844342deb3fa1d030598db93937a9964fcf5cb4da4feb5f17be34b/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb11a4f1b2b63337cfd3b4d110af778a59aae51c81d195768e353d8b52f88081", size = 557249, upload-time = "2025-08-27T12:14:08.37Z" }, - { url = "https://files.pythonhosted.org/packages/60/f3/690dd38e2310b6f68858a331399b4d6dbb9132c3e8ef8b4333b96caf403d/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:13e608ac9f50a0ed4faec0e90ece76ae33b34c0e8656e3dceb9a7db994c692cd", size = 587356, upload-time = "2025-08-27T12:14:10.034Z" }, - { url = "https://files.pythonhosted.org/packages/86/e3/84507781cccd0145f35b1dc32c72675200c5ce8d5b30f813e49424ef68fc/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dd2135527aa40f061350c3f8f89da2644de26cd73e4de458e79606384f4f68e7", size = 555300, upload-time = "2025-08-27T12:14:11.783Z" }, - { url = "https://files.pythonhosted.org/packages/e5/ee/375469849e6b429b3516206b4580a79e9ef3eb12920ddbd4492b56eaacbe/rpds_py-0.27.1-cp313-cp313t-win32.whl", hash = "sha256:3020724ade63fe320a972e2ffd93b5623227e684315adce194941167fee02688", size = 216714, upload-time = "2025-08-27T12:14:13.629Z" }, - { url = "https://files.pythonhosted.org/packages/21/87/3fc94e47c9bd0742660e84706c311a860dcae4374cf4a03c477e23ce605a/rpds_py-0.27.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8ee50c3e41739886606388ba3ab3ee2aae9f35fb23f833091833255a31740797", size = 228943, upload-time = "2025-08-27T12:14:14.937Z" }, - { url = "https://files.pythonhosted.org/packages/70/36/b6e6066520a07cf029d385de869729a895917b411e777ab1cde878100a1d/rpds_py-0.27.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:acb9aafccaae278f449d9c713b64a9e68662e7799dbd5859e2c6b3c67b56d334", size = 362472, upload-time = "2025-08-27T12:14:16.333Z" }, - { url = "https://files.pythonhosted.org/packages/af/07/b4646032e0dcec0df9c73a3bd52f63bc6c5f9cda992f06bd0e73fe3fbebd/rpds_py-0.27.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b7fb801aa7f845ddf601c49630deeeccde7ce10065561d92729bfe81bd21fb33", size = 345676, upload-time = "2025-08-27T12:14:17.764Z" }, - { url = "https://files.pythonhosted.org/packages/b0/16/2f1003ee5d0af4bcb13c0cf894957984c32a6751ed7206db2aee7379a55e/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe0dd05afb46597b9a2e11c351e5e4283c741237e7f617ffb3252780cca9336a", size = 385313, upload-time = "2025-08-27T12:14:19.829Z" }, - { url = "https://files.pythonhosted.org/packages/05/cd/7eb6dd7b232e7f2654d03fa07f1414d7dfc980e82ba71e40a7c46fd95484/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b6dfb0e058adb12d8b1d1b25f686e94ffa65d9995a5157afe99743bf7369d62b", size = 399080, upload-time = "2025-08-27T12:14:21.531Z" }, - { url = "https://files.pythonhosted.org/packages/20/51/5829afd5000ec1cb60f304711f02572d619040aa3ec033d8226817d1e571/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed090ccd235f6fa8bb5861684567f0a83e04f52dfc2e5c05f2e4b1309fcf85e7", size = 523868, upload-time = "2025-08-27T12:14:23.485Z" }, - { url = "https://files.pythonhosted.org/packages/05/2c/30eebca20d5db95720ab4d2faec1b5e4c1025c473f703738c371241476a2/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf876e79763eecf3e7356f157540d6a093cef395b65514f17a356f62af6cc136", size = 408750, upload-time = "2025-08-27T12:14:24.924Z" }, - { url = "https://files.pythonhosted.org/packages/90/1a/cdb5083f043597c4d4276eae4e4c70c55ab5accec078da8611f24575a367/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12ed005216a51b1d6e2b02a7bd31885fe317e45897de81d86dcce7d74618ffff", size = 387688, upload-time = "2025-08-27T12:14:27.537Z" }, - { url = "https://files.pythonhosted.org/packages/7c/92/cf786a15320e173f945d205ab31585cc43969743bb1a48b6888f7a2b0a2d/rpds_py-0.27.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ee4308f409a40e50593c7e3bb8cbe0b4d4c66d1674a316324f0c2f5383b486f9", size = 407225, upload-time = "2025-08-27T12:14:28.981Z" }, - { url = "https://files.pythonhosted.org/packages/33/5c/85ee16df5b65063ef26017bef33096557a4c83fbe56218ac7cd8c235f16d/rpds_py-0.27.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b08d152555acf1f455154d498ca855618c1378ec810646fcd7c76416ac6dc60", size = 423361, upload-time = "2025-08-27T12:14:30.469Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8e/1c2741307fcabd1a334ecf008e92c4f47bb6f848712cf15c923becfe82bb/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dce51c828941973a5684d458214d3a36fcd28da3e1875d659388f4f9f12cc33e", size = 562493, upload-time = "2025-08-27T12:14:31.987Z" }, - { url = "https://files.pythonhosted.org/packages/04/03/5159321baae9b2222442a70c1f988cbbd66b9be0675dd3936461269be360/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c1476d6f29eb81aa4151c9a31219b03f1f798dc43d8af1250a870735516a1212", size = 592623, upload-time = "2025-08-27T12:14:33.543Z" }, - { url = "https://files.pythonhosted.org/packages/ff/39/c09fd1ad28b85bc1d4554a8710233c9f4cefd03d7717a1b8fbfd171d1167/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3ce0cac322b0d69b63c9cdb895ee1b65805ec9ffad37639f291dd79467bee675", size = 558800, upload-time = "2025-08-27T12:14:35.436Z" }, - { url = "https://files.pythonhosted.org/packages/c5/d6/99228e6bbcf4baa764b18258f519a9035131d91b538d4e0e294313462a98/rpds_py-0.27.1-cp314-cp314-win32.whl", hash = "sha256:dfbfac137d2a3d0725758cd141f878bf4329ba25e34979797c89474a89a8a3a3", size = 221943, upload-time = "2025-08-27T12:14:36.898Z" }, - { url = "https://files.pythonhosted.org/packages/be/07/c802bc6b8e95be83b79bdf23d1aa61d68324cb1006e245d6c58e959e314d/rpds_py-0.27.1-cp314-cp314-win_amd64.whl", hash = "sha256:a6e57b0abfe7cc513450fcf529eb486b6e4d3f8aee83e92eb5f1ef848218d456", size = 233739, upload-time = "2025-08-27T12:14:38.386Z" }, - { url = "https://files.pythonhosted.org/packages/c8/89/3e1b1c16d4c2d547c5717377a8df99aee8099ff050f87c45cb4d5fa70891/rpds_py-0.27.1-cp314-cp314-win_arm64.whl", hash = "sha256:faf8d146f3d476abfee026c4ae3bdd9ca14236ae4e4c310cbd1cf75ba33d24a3", size = 223120, upload-time = "2025-08-27T12:14:39.82Z" }, - { url = "https://files.pythonhosted.org/packages/62/7e/dc7931dc2fa4a6e46b2a4fa744a9fe5c548efd70e0ba74f40b39fa4a8c10/rpds_py-0.27.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:ba81d2b56b6d4911ce735aad0a1d4495e808b8ee4dc58715998741a26874e7c2", size = 358944, upload-time = "2025-08-27T12:14:41.199Z" }, - { url = "https://files.pythonhosted.org/packages/e6/22/4af76ac4e9f336bfb1a5f240d18a33c6b2fcaadb7472ac7680576512b49a/rpds_py-0.27.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:84f7d509870098de0e864cad0102711c1e24e9b1a50ee713b65928adb22269e4", size = 342283, upload-time = "2025-08-27T12:14:42.699Z" }, - { url = "https://files.pythonhosted.org/packages/1c/15/2a7c619b3c2272ea9feb9ade67a45c40b3eeb500d503ad4c28c395dc51b4/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e960fc78fecd1100539f14132425e1d5fe44ecb9239f8f27f079962021523e", size = 380320, upload-time = "2025-08-27T12:14:44.157Z" }, - { url = "https://files.pythonhosted.org/packages/a2/7d/4c6d243ba4a3057e994bb5bedd01b5c963c12fe38dde707a52acdb3849e7/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62f85b665cedab1a503747617393573995dac4600ff51869d69ad2f39eb5e817", size = 391760, upload-time = "2025-08-27T12:14:45.845Z" }, - { url = "https://files.pythonhosted.org/packages/b4/71/b19401a909b83bcd67f90221330bc1ef11bc486fe4e04c24388d28a618ae/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fed467af29776f6556250c9ed85ea5a4dd121ab56a5f8b206e3e7a4c551e48ec", size = 522476, upload-time = "2025-08-27T12:14:47.364Z" }, - { url = "https://files.pythonhosted.org/packages/e4/44/1a3b9715c0455d2e2f0f6df5ee6d6f5afdc423d0773a8a682ed2b43c566c/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2729615f9d430af0ae6b36cf042cb55c0936408d543fb691e1a9e36648fd35a", size = 403418, upload-time = "2025-08-27T12:14:49.991Z" }, - { url = "https://files.pythonhosted.org/packages/1c/4b/fb6c4f14984eb56673bc868a66536f53417ddb13ed44b391998100a06a96/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b207d881a9aef7ba753d69c123a35d96ca7cb808056998f6b9e8747321f03b8", size = 384771, upload-time = "2025-08-27T12:14:52.159Z" }, - { url = "https://files.pythonhosted.org/packages/c0/56/d5265d2d28b7420d7b4d4d85cad8ef891760f5135102e60d5c970b976e41/rpds_py-0.27.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:639fd5efec029f99b79ae47e5d7e00ad8a773da899b6309f6786ecaf22948c48", size = 400022, upload-time = "2025-08-27T12:14:53.859Z" }, - { url = "https://files.pythonhosted.org/packages/8f/e9/9f5fc70164a569bdd6ed9046486c3568d6926e3a49bdefeeccfb18655875/rpds_py-0.27.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fecc80cb2a90e28af8a9b366edacf33d7a91cbfe4c2c4544ea1246e949cfebeb", size = 416787, upload-time = "2025-08-27T12:14:55.673Z" }, - { url = "https://files.pythonhosted.org/packages/d4/64/56dd03430ba491db943a81dcdef115a985aac5f44f565cd39a00c766d45c/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42a89282d711711d0a62d6f57d81aa43a1368686c45bc1c46b7f079d55692734", size = 557538, upload-time = "2025-08-27T12:14:57.245Z" }, - { url = "https://files.pythonhosted.org/packages/3f/36/92cc885a3129993b1d963a2a42ecf64e6a8e129d2c7cc980dbeba84e55fb/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:cf9931f14223de59551ab9d38ed18d92f14f055a5f78c1d8ad6493f735021bbb", size = 588512, upload-time = "2025-08-27T12:14:58.728Z" }, - { url = "https://files.pythonhosted.org/packages/dd/10/6b283707780a81919f71625351182b4f98932ac89a09023cb61865136244/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f39f58a27cc6e59f432b568ed8429c7e1641324fbe38131de852cd77b2d534b0", size = 555813, upload-time = "2025-08-27T12:15:00.334Z" }, - { url = "https://files.pythonhosted.org/packages/04/2e/30b5ea18c01379da6272a92825dd7e53dc9d15c88a19e97932d35d430ef7/rpds_py-0.27.1-cp314-cp314t-win32.whl", hash = "sha256:d5fa0ee122dc09e23607a28e6d7b150da16c662e66409bbe85230e4c85bb528a", size = 217385, upload-time = "2025-08-27T12:15:01.937Z" }, - { url = "https://files.pythonhosted.org/packages/32/7d/97119da51cb1dd3f2f3c0805f155a3aa4a95fa44fe7d78ae15e69edf4f34/rpds_py-0.27.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6567d2bb951e21232c2f660c24cf3470bb96de56cdcb3f071a83feeaff8a2772", size = 230097, upload-time = "2025-08-27T12:15:03.961Z" }, - { url = "https://files.pythonhosted.org/packages/7f/6c/252e83e1ce7583c81f26d1d884b2074d40a13977e1b6c9c50bbf9a7f1f5a/rpds_py-0.27.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c918c65ec2e42c2a78d19f18c553d77319119bf43aa9e2edf7fb78d624355527", size = 372140, upload-time = "2025-08-27T12:15:05.441Z" }, - { url = "https://files.pythonhosted.org/packages/9d/71/949c195d927c5aeb0d0629d329a20de43a64c423a6aa53836290609ef7ec/rpds_py-0.27.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1fea2b1a922c47c51fd07d656324531adc787e415c8b116530a1d29c0516c62d", size = 354086, upload-time = "2025-08-27T12:15:07.404Z" }, - { url = "https://files.pythonhosted.org/packages/9f/02/e43e332ad8ce4f6c4342d151a471a7f2900ed1d76901da62eb3762663a71/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbf94c58e8e0cd6b6f38d8de67acae41b3a515c26169366ab58bdca4a6883bb8", size = 382117, upload-time = "2025-08-27T12:15:09.275Z" }, - { url = "https://files.pythonhosted.org/packages/d0/05/b0fdeb5b577197ad72812bbdfb72f9a08fa1e64539cc3940b1b781cd3596/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2a8fed130ce946d5c585eddc7c8eeef0051f58ac80a8ee43bd17835c144c2cc", size = 394520, upload-time = "2025-08-27T12:15:10.727Z" }, - { url = "https://files.pythonhosted.org/packages/67/1f/4cfef98b2349a7585181e99294fa2a13f0af06902048a5d70f431a66d0b9/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:037a2361db72ee98d829bc2c5b7cc55598ae0a5e0ec1823a56ea99374cfd73c1", size = 522657, upload-time = "2025-08-27T12:15:12.613Z" }, - { url = "https://files.pythonhosted.org/packages/44/55/ccf37ddc4c6dce7437b335088b5ca18da864b334890e2fe9aa6ddc3f79a9/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5281ed1cc1d49882f9997981c88df1a22e140ab41df19071222f7e5fc4e72125", size = 402967, upload-time = "2025-08-27T12:15:14.113Z" }, - { url = "https://files.pythonhosted.org/packages/74/e5/5903f92e41e293b07707d5bf00ef39a0eb2af7190aff4beaf581a6591510/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fd50659a069c15eef8aa3d64bbef0d69fd27bb4a50c9ab4f17f83a16cbf8905", size = 384372, upload-time = "2025-08-27T12:15:15.842Z" }, - { url = "https://files.pythonhosted.org/packages/8f/e3/fbb409e18aeefc01e49f5922ac63d2d914328430e295c12183ce56ebf76b/rpds_py-0.27.1-cp39-cp39-manylinux_2_31_riscv64.whl", hash = "sha256:c4b676c4ae3921649a15d28ed10025548e9b561ded473aa413af749503c6737e", size = 401264, upload-time = "2025-08-27T12:15:17.388Z" }, - { url = "https://files.pythonhosted.org/packages/55/79/529ad07794e05cb0f38e2f965fc5bb20853d523976719400acecc447ec9d/rpds_py-0.27.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:079bc583a26db831a985c5257797b2b5d3affb0386e7ff886256762f82113b5e", size = 418691, upload-time = "2025-08-27T12:15:19.144Z" }, - { url = "https://files.pythonhosted.org/packages/33/39/6554a7fd6d9906fda2521c6d52f5d723dca123529fb719a5b5e074c15e01/rpds_py-0.27.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4e44099bd522cba71a2c6b97f68e19f40e7d85399de899d66cdb67b32d7cb786", size = 558989, upload-time = "2025-08-27T12:15:21.087Z" }, - { url = "https://files.pythonhosted.org/packages/19/b2/76fa15173b6f9f445e5ef15120871b945fb8dd9044b6b8c7abe87e938416/rpds_py-0.27.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e202e6d4188e53c6661af813b46c37ca2c45e497fc558bacc1a7630ec2695aec", size = 589835, upload-time = "2025-08-27T12:15:22.696Z" }, - { url = "https://files.pythonhosted.org/packages/ee/9e/5560a4b39bab780405bed8a88ee85b30178061d189558a86003548dea045/rpds_py-0.27.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f41f814b8eaa48768d1bb551591f6ba45f87ac76899453e8ccd41dba1289b04b", size = 555227, upload-time = "2025-08-27T12:15:24.278Z" }, - { url = "https://files.pythonhosted.org/packages/52/d7/cd9c36215111aa65724c132bf709c6f35175973e90b32115dedc4ced09cb/rpds_py-0.27.1-cp39-cp39-win32.whl", hash = "sha256:9e71f5a087ead99563c11fdaceee83ee982fd39cf67601f4fd66cb386336ee52", size = 217899, upload-time = "2025-08-27T12:15:25.926Z" }, - { url = "https://files.pythonhosted.org/packages/5b/e0/d75ab7b4dd8ba777f6b365adbdfc7614bbfe7c5f05703031dfa4b61c3d6c/rpds_py-0.27.1-cp39-cp39-win_amd64.whl", hash = "sha256:71108900c9c3c8590697244b9519017a400d9ba26a36c48381b3f64743a44aab", size = 228725, upload-time = "2025-08-27T12:15:27.398Z" }, - { url = "https://files.pythonhosted.org/packages/d5/63/b7cc415c345625d5e62f694ea356c58fb964861409008118f1245f8c3347/rpds_py-0.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7ba22cb9693df986033b91ae1d7a979bc399237d45fccf875b76f62bb9e52ddf", size = 371360, upload-time = "2025-08-27T12:15:29.218Z" }, - { url = "https://files.pythonhosted.org/packages/e5/8c/12e1b24b560cf378b8ffbdb9dc73abd529e1adcfcf82727dfd29c4a7b88d/rpds_py-0.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b640501be9288c77738b5492b3fd3abc4ba95c50c2e41273c8a1459f08298d3", size = 353933, upload-time = "2025-08-27T12:15:30.837Z" }, - { url = "https://files.pythonhosted.org/packages/9b/85/1bb2210c1f7a1b99e91fea486b9f0f894aa5da3a5ec7097cbad7dec6d40f/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb08b65b93e0c6dd70aac7f7890a9c0938d5ec71d5cb32d45cf844fb8ae47636", size = 382962, upload-time = "2025-08-27T12:15:32.348Z" }, - { url = "https://files.pythonhosted.org/packages/cc/c9/a839b9f219cf80ed65f27a7f5ddbb2809c1b85c966020ae2dff490e0b18e/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d7ff07d696a7a38152ebdb8212ca9e5baab56656749f3d6004b34ab726b550b8", size = 394412, upload-time = "2025-08-27T12:15:33.839Z" }, - { url = "https://files.pythonhosted.org/packages/02/2d/b1d7f928b0b1f4fc2e0133e8051d199b01d7384875adc63b6ddadf3de7e5/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb7c72262deae25366e3b6c0c0ba46007967aea15d1eea746e44ddba8ec58dcc", size = 523972, upload-time = "2025-08-27T12:15:35.377Z" }, - { url = "https://files.pythonhosted.org/packages/a9/af/2cbf56edd2d07716df1aec8a726b3159deb47cb5c27e1e42b71d705a7c2f/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b002cab05d6339716b03a4a3a2ce26737f6231d7b523f339fa061d53368c9d8", size = 403273, upload-time = "2025-08-27T12:15:37.051Z" }, - { url = "https://files.pythonhosted.org/packages/c0/93/425e32200158d44ff01da5d9612c3b6711fe69f606f06e3895511f17473b/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23f6b69d1c26c4704fec01311963a41d7de3ee0570a84ebde4d544e5a1859ffc", size = 385278, upload-time = "2025-08-27T12:15:38.571Z" }, - { url = "https://files.pythonhosted.org/packages/eb/1a/1a04a915ecd0551bfa9e77b7672d1937b4b72a0fc204a17deef76001cfb2/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:530064db9146b247351f2a0250b8f00b289accea4596a033e94be2389977de71", size = 402084, upload-time = "2025-08-27T12:15:40.529Z" }, - { url = "https://files.pythonhosted.org/packages/51/f7/66585c0fe5714368b62951d2513b684e5215beaceab2c6629549ddb15036/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b90b0496570bd6b0321724a330d8b545827c4df2034b6ddfc5f5275f55da2ad", size = 419041, upload-time = "2025-08-27T12:15:42.191Z" }, - { url = "https://files.pythonhosted.org/packages/8e/7e/83a508f6b8e219bba2d4af077c35ba0e0cdd35a751a3be6a7cba5a55ad71/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:879b0e14a2da6a1102a3fc8af580fc1ead37e6d6692a781bd8c83da37429b5ab", size = 560084, upload-time = "2025-08-27T12:15:43.839Z" }, - { url = "https://files.pythonhosted.org/packages/66/66/bb945683b958a1b19eb0fe715594630d0f36396ebdef4d9b89c2fa09aa56/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:0d807710df3b5faa66c731afa162ea29717ab3be17bdc15f90f2d9f183da4059", size = 590115, upload-time = "2025-08-27T12:15:46.647Z" }, - { url = "https://files.pythonhosted.org/packages/12/00/ccfaafaf7db7e7adace915e5c2f2c2410e16402561801e9c7f96683002d3/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:3adc388fc3afb6540aec081fa59e6e0d3908722771aa1e37ffe22b220a436f0b", size = 556561, upload-time = "2025-08-27T12:15:48.219Z" }, - { url = "https://files.pythonhosted.org/packages/e1/b7/92b6ed9aad103bfe1c45df98453dfae40969eef2cb6c6239c58d7e96f1b3/rpds_py-0.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c796c0c1cc68cb08b0284db4229f5af76168172670c74908fdbd4b7d7f515819", size = 229125, upload-time = "2025-08-27T12:15:49.956Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ed/e1fba02de17f4f76318b834425257c8ea297e415e12c68b4361f63e8ae92/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdfe4bb2f9fe7458b7453ad3c33e726d6d1c7c0a72960bcc23800d77384e42df", size = 371402, upload-time = "2025-08-27T12:15:51.561Z" }, - { url = "https://files.pythonhosted.org/packages/af/7c/e16b959b316048b55585a697e94add55a4ae0d984434d279ea83442e460d/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8fabb8fd848a5f75a2324e4a84501ee3a5e3c78d8603f83475441866e60b94a3", size = 354084, upload-time = "2025-08-27T12:15:53.219Z" }, - { url = "https://files.pythonhosted.org/packages/de/c1/ade645f55de76799fdd08682d51ae6724cb46f318573f18be49b1e040428/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda8719d598f2f7f3e0f885cba8646644b55a187762bec091fa14a2b819746a9", size = 383090, upload-time = "2025-08-27T12:15:55.158Z" }, - { url = "https://files.pythonhosted.org/packages/1f/27/89070ca9b856e52960da1472efcb6c20ba27cfe902f4f23ed095b9cfc61d/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c64d07e95606ec402a0a1c511fe003873fa6af630bda59bac77fac8b4318ebc", size = 394519, upload-time = "2025-08-27T12:15:57.238Z" }, - { url = "https://files.pythonhosted.org/packages/b3/28/be120586874ef906aa5aeeae95ae8df4184bc757e5b6bd1c729ccff45ed5/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93a2ed40de81bcff59aabebb626562d48332f3d028ca2036f1d23cbb52750be4", size = 523817, upload-time = "2025-08-27T12:15:59.237Z" }, - { url = "https://files.pythonhosted.org/packages/a8/ef/70cc197bc11cfcde02a86f36ac1eed15c56667c2ebddbdb76a47e90306da/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:387ce8c44ae94e0ec50532d9cb0edce17311024c9794eb196b90e1058aadeb66", size = 403240, upload-time = "2025-08-27T12:16:00.923Z" }, - { url = "https://files.pythonhosted.org/packages/cf/35/46936cca449f7f518f2f4996e0e8344db4b57e2081e752441154089d2a5f/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaf94f812c95b5e60ebaf8bfb1898a7d7cb9c1af5744d4a67fa47796e0465d4e", size = 385194, upload-time = "2025-08-27T12:16:02.802Z" }, - { url = "https://files.pythonhosted.org/packages/e1/62/29c0d3e5125c3270b51415af7cbff1ec587379c84f55a5761cc9efa8cd06/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:4848ca84d6ded9b58e474dfdbad4b8bfb450344c0551ddc8d958bf4b36aa837c", size = 402086, upload-time = "2025-08-27T12:16:04.806Z" }, - { url = "https://files.pythonhosted.org/packages/8f/66/03e1087679227785474466fdd04157fb793b3b76e3fcf01cbf4c693c1949/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bde09cbcf2248b73c7c323be49b280180ff39fadcfe04e7b6f54a678d02a7cf", size = 419272, upload-time = "2025-08-27T12:16:06.471Z" }, - { url = "https://files.pythonhosted.org/packages/6a/24/e3e72d265121e00b063aef3e3501e5b2473cf1b23511d56e529531acf01e/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:94c44ee01fd21c9058f124d2d4f0c9dc7634bec93cd4b38eefc385dabe71acbf", size = 560003, upload-time = "2025-08-27T12:16:08.06Z" }, - { url = "https://files.pythonhosted.org/packages/26/ca/f5a344c534214cc2d41118c0699fffbdc2c1bc7046f2a2b9609765ab9c92/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:df8b74962e35c9249425d90144e721eed198e6555a0e22a563d29fe4486b51f6", size = 590482, upload-time = "2025-08-27T12:16:10.137Z" }, - { url = "https://files.pythonhosted.org/packages/ce/08/4349bdd5c64d9d193c360aa9db89adeee6f6682ab8825dca0a3f535f434f/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:dc23e6820e3b40847e2f4a7726462ba0cf53089512abe9ee16318c366494c17a", size = 556523, upload-time = "2025-08-27T12:16:12.188Z" }, - { url = "https://files.pythonhosted.org/packages/4e/ea/5463cd5048a7a2fcdae308b6e96432802132c141bfb9420260142632a0f1/rpds_py-0.27.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:aa8933159edc50be265ed22b401125c9eebff3171f570258854dbce3ecd55475", size = 371778, upload-time = "2025-08-27T12:16:13.851Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c8/f38c099db07f5114029c1467649d308543906933eebbc226d4527a5f4693/rpds_py-0.27.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a50431bf02583e21bf273c71b89d710e7a710ad5e39c725b14e685610555926f", size = 354394, upload-time = "2025-08-27T12:16:15.609Z" }, - { url = "https://files.pythonhosted.org/packages/7d/79/b76f97704d9dd8ddbd76fed4c4048153a847c5d6003afe20a6b5c3339065/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78af06ddc7fe5cc0e967085a9115accee665fb912c22a3f54bad70cc65b05fe6", size = 382348, upload-time = "2025-08-27T12:16:17.251Z" }, - { url = "https://files.pythonhosted.org/packages/8a/3f/ef23d3c1be1b837b648a3016d5bbe7cfe711422ad110b4081c0a90ef5a53/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:70d0738ef8fee13c003b100c2fbd667ec4f133468109b3472d249231108283a3", size = 394159, upload-time = "2025-08-27T12:16:19.251Z" }, - { url = "https://files.pythonhosted.org/packages/74/8a/9e62693af1a34fd28b1a190d463d12407bd7cf561748cb4745845d9548d3/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2f6fd8a1cea5bbe599b6e78a6e5ee08db434fc8ffea51ff201c8765679698b3", size = 522775, upload-time = "2025-08-27T12:16:20.929Z" }, - { url = "https://files.pythonhosted.org/packages/36/0d/8d5bb122bf7a60976b54c5c99a739a3819f49f02d69df3ea2ca2aff47d5c/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8177002868d1426305bb5de1e138161c2ec9eb2d939be38291d7c431c4712df8", size = 402633, upload-time = "2025-08-27T12:16:22.548Z" }, - { url = "https://files.pythonhosted.org/packages/0f/0e/237948c1f425e23e0cf5a566d702652a6e55c6f8fbd332a1792eb7043daf/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:008b839781d6c9bf3b6a8984d1d8e56f0ec46dc56df61fd669c49b58ae800400", size = 384867, upload-time = "2025-08-27T12:16:24.29Z" }, - { url = "https://files.pythonhosted.org/packages/d6/0a/da0813efcd998d260cbe876d97f55b0f469ada8ba9cbc47490a132554540/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:a55b9132bb1ade6c734ddd2759c8dc132aa63687d259e725221f106b83a0e485", size = 401791, upload-time = "2025-08-27T12:16:25.954Z" }, - { url = "https://files.pythonhosted.org/packages/51/78/c6c9e8a8aaca416a6f0d1b6b4a6ee35b88fe2c5401d02235d0a056eceed2/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a46fdec0083a26415f11d5f236b79fa1291c32aaa4a17684d82f7017a1f818b1", size = 419525, upload-time = "2025-08-27T12:16:27.659Z" }, - { url = "https://files.pythonhosted.org/packages/a3/69/5af37e1d71487cf6d56dd1420dc7e0c2732c1b6ff612aa7a88374061c0a8/rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:8a63b640a7845f2bdd232eb0d0a4a2dd939bcdd6c57e6bb134526487f3160ec5", size = 559255, upload-time = "2025-08-27T12:16:29.343Z" }, - { url = "https://files.pythonhosted.org/packages/40/7f/8b7b136069ef7ac3960eda25d832639bdb163018a34c960ed042dd1707c8/rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:7e32721e5d4922deaaf963469d795d5bde6093207c52fec719bd22e5d1bedbc4", size = 590384, upload-time = "2025-08-27T12:16:31.005Z" }, - { url = "https://files.pythonhosted.org/packages/d8/06/c316d3f6ff03f43ccb0eba7de61376f8ec4ea850067dddfafe98274ae13c/rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:2c426b99a068601b5f4623573df7a7c3d72e87533a2dd2253353a03e7502566c", size = 555959, upload-time = "2025-08-27T12:16:32.73Z" }, - { url = "https://files.pythonhosted.org/packages/60/94/384cf54c430b9dac742bbd2ec26c23feb78ded0d43d6d78563a281aec017/rpds_py-0.27.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4fc9b7fe29478824361ead6e14e4f5aed570d477e06088826537e202d25fe859", size = 228784, upload-time = "2025-08-27T12:16:34.428Z" }, -] - [[package]] name = "rpds-py" version = "0.30.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490, upload-time = "2025-11-30T20:21:33.256Z" }, @@ -4347,56 +2861,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dd/29/59ed8152b30f72c42d00d241e58eaca558ae9dbfa5695206e2e0f54c7063/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12f49080303fa6bb424b362149a12949dfbbf1e06811a88f2307276b0c131afd", size = 503977, upload-time = "2025-11-19T15:18:17.523Z" }, { url = "https://files.pythonhosted.org/packages/d3/0b/4811bfec67fa260e791369b16dab105e4bae82686120554cc484064e22b4/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0071bffba4150c2f46cae1432d31995d77acfd9f8db598b5d1a2ce67e8440ad2", size = 623890, upload-time = "2025-11-19T15:18:22.666Z" }, { url = "https://files.pythonhosted.org/packages/58/5b/632a58724221ef03d78ab65062e82a1010e1bef8e8e0b9d7c6d7b8044841/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:473b32699f4200e69801bf5abf93f1a4ecd432a70984df164fc22ccf39c4a6f3", size = 531885, upload-time = "2025-11-19T15:18:27.146Z" }, - { url = "https://files.pythonhosted.org/packages/94/60/13ccb63ea85bfe2e4fe6af602cf1272155f048906556d5ec8509da9dba42/safetensors-0.7.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b95a3fa7b3abb9b5b0e07668e808364d0d40f6bbbf9ae0faa8b5b210c97b140", size = 492627, upload-time = "2025-11-19T15:18:14.661Z" }, - { url = "https://files.pythonhosted.org/packages/2e/2b/e2fde0d6334439908b0b0c4cba18b8ad76ea6a03b569d4a3388f423b4046/safetensors-0.7.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cfdead2f57330d76aa7234051dadfa7d4eedc0e5a27fd08e6f96714a92b00f09", size = 503861, upload-time = "2025-11-19T15:18:19.418Z" }, - { url = "https://files.pythonhosted.org/packages/f0/71/566e3dd559a9cef1b4775c239daae09e6b6a32ca8b45eb1db9a4dfa1ba81/safetensors-0.7.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc92bc2db7b45bda4510e4f51c59b00fe80b2d6be88928346e4294ce1c2abe7c", size = 623577, upload-time = "2025-11-19T15:18:24.275Z" }, - { url = "https://files.pythonhosted.org/packages/82/fc/3035c5c30c8a5a82c31c6b2ad6f8bcd45ea2ddd9a8088840406bcf997413/safetensors-0.7.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6999421eb8ba9df4450a16d9184fcb7bef26240b9f98e95401f17af6c2210b71", size = 532524, upload-time = "2025-11-19T15:18:29.334Z" }, -] - -[[package]] -name = "scikit-learn" -version = "1.6.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "joblib", marker = "python_full_version < '3.10'" }, - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "scipy", version = "1.13.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "threadpoolctl", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9e/a5/4ae3b3a0755f7b35a280ac90b28817d1f380318973cff14075ab41ef50d9/scikit_learn-1.6.1.tar.gz", hash = "sha256:b4fc2525eca2c69a59260f583c56a7557c6ccdf8deafdba6e060f94c1c59738e", size = 7068312, upload-time = "2025-01-10T08:07:55.348Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/3a/f4597eb41049110b21ebcbb0bcb43e4035017545daa5eedcfeb45c08b9c5/scikit_learn-1.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d056391530ccd1e501056160e3c9673b4da4805eb67eb2bdf4e983e1f9c9204e", size = 12067702, upload-time = "2025-01-10T08:05:56.515Z" }, - { url = "https://files.pythonhosted.org/packages/37/19/0423e5e1fd1c6ec5be2352ba05a537a473c1677f8188b9306097d684b327/scikit_learn-1.6.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:0c8d036eb937dbb568c6242fa598d551d88fb4399c0344d95c001980ec1c7d36", size = 11112765, upload-time = "2025-01-10T08:06:00.272Z" }, - { url = "https://files.pythonhosted.org/packages/70/95/d5cb2297a835b0f5fc9a77042b0a2d029866379091ab8b3f52cc62277808/scikit_learn-1.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8634c4bd21a2a813e0a7e3900464e6d593162a29dd35d25bdf0103b3fce60ed5", size = 12643991, upload-time = "2025-01-10T08:06:04.813Z" }, - { url = "https://files.pythonhosted.org/packages/b7/91/ab3c697188f224d658969f678be86b0968ccc52774c8ab4a86a07be13c25/scikit_learn-1.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:775da975a471c4f6f467725dff0ced5c7ac7bda5e9316b260225b48475279a1b", size = 13497182, upload-time = "2025-01-10T08:06:08.42Z" }, - { url = "https://files.pythonhosted.org/packages/17/04/d5d556b6c88886c092cc989433b2bab62488e0f0dafe616a1d5c9cb0efb1/scikit_learn-1.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:8a600c31592bd7dab31e1c61b9bbd6dea1b3433e67d264d17ce1017dbdce8002", size = 11125517, upload-time = "2025-01-10T08:06:12.783Z" }, - { url = "https://files.pythonhosted.org/packages/6c/2a/e291c29670795406a824567d1dfc91db7b699799a002fdaa452bceea8f6e/scikit_learn-1.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:72abc587c75234935e97d09aa4913a82f7b03ee0b74111dcc2881cba3c5a7b33", size = 12102620, upload-time = "2025-01-10T08:06:16.675Z" }, - { url = "https://files.pythonhosted.org/packages/25/92/ee1d7a00bb6b8c55755d4984fd82608603a3cc59959245068ce32e7fb808/scikit_learn-1.6.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b3b00cdc8f1317b5f33191df1386c0befd16625f49d979fe77a8d44cae82410d", size = 11116234, upload-time = "2025-01-10T08:06:21.83Z" }, - { url = "https://files.pythonhosted.org/packages/30/cd/ed4399485ef364bb25f388ab438e3724e60dc218c547a407b6e90ccccaef/scikit_learn-1.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc4765af3386811c3ca21638f63b9cf5ecf66261cc4815c1db3f1e7dc7b79db2", size = 12592155, upload-time = "2025-01-10T08:06:27.309Z" }, - { url = "https://files.pythonhosted.org/packages/a8/f3/62fc9a5a659bb58a03cdd7e258956a5824bdc9b4bb3c5d932f55880be569/scikit_learn-1.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25fc636bdaf1cc2f4a124a116312d837148b5e10872147bdaf4887926b8c03d8", size = 13497069, upload-time = "2025-01-10T08:06:32.515Z" }, - { url = "https://files.pythonhosted.org/packages/a1/a6/c5b78606743a1f28eae8f11973de6613a5ee87366796583fb74c67d54939/scikit_learn-1.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:fa909b1a36e000a03c382aade0bd2063fd5680ff8b8e501660c0f59f021a6415", size = 11139809, upload-time = "2025-01-10T08:06:35.514Z" }, - { url = "https://files.pythonhosted.org/packages/0a/18/c797c9b8c10380d05616db3bfb48e2a3358c767affd0857d56c2eb501caa/scikit_learn-1.6.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:926f207c804104677af4857b2c609940b743d04c4c35ce0ddc8ff4f053cddc1b", size = 12104516, upload-time = "2025-01-10T08:06:40.009Z" }, - { url = "https://files.pythonhosted.org/packages/c4/b7/2e35f8e289ab70108f8cbb2e7a2208f0575dc704749721286519dcf35f6f/scikit_learn-1.6.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2c2cae262064e6a9b77eee1c8e768fc46aa0b8338c6a8297b9b6759720ec0ff2", size = 11167837, upload-time = "2025-01-10T08:06:43.305Z" }, - { url = "https://files.pythonhosted.org/packages/a4/f6/ff7beaeb644bcad72bcfd5a03ff36d32ee4e53a8b29a639f11bcb65d06cd/scikit_learn-1.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1061b7c028a8663fb9a1a1baf9317b64a257fcb036dae5c8752b2abef31d136f", size = 12253728, upload-time = "2025-01-10T08:06:47.618Z" }, - { url = "https://files.pythonhosted.org/packages/29/7a/8bce8968883e9465de20be15542f4c7e221952441727c4dad24d534c6d99/scikit_learn-1.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e69fab4ebfc9c9b580a7a80111b43d214ab06250f8a7ef590a4edf72464dd86", size = 13147700, upload-time = "2025-01-10T08:06:50.888Z" }, - { url = "https://files.pythonhosted.org/packages/62/27/585859e72e117fe861c2079bcba35591a84f801e21bc1ab85bce6ce60305/scikit_learn-1.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:70b1d7e85b1c96383f872a519b3375f92f14731e279a7b4c6cfd650cf5dffc52", size = 11110613, upload-time = "2025-01-10T08:06:54.115Z" }, - { url = "https://files.pythonhosted.org/packages/2e/59/8eb1872ca87009bdcdb7f3cdc679ad557b992c12f4b61f9250659e592c63/scikit_learn-1.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2ffa1e9e25b3d93990e74a4be2c2fc61ee5af85811562f1288d5d055880c4322", size = 12010001, upload-time = "2025-01-10T08:06:58.613Z" }, - { url = "https://files.pythonhosted.org/packages/9d/05/f2fc4effc5b32e525408524c982c468c29d22f828834f0625c5ef3d601be/scikit_learn-1.6.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:dc5cf3d68c5a20ad6d571584c0750ec641cc46aeef1c1507be51300e6003a7e1", size = 11096360, upload-time = "2025-01-10T08:07:01.556Z" }, - { url = "https://files.pythonhosted.org/packages/c8/e4/4195d52cf4f113573fb8ebc44ed5a81bd511a92c0228889125fac2f4c3d1/scikit_learn-1.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c06beb2e839ecc641366000ca84f3cf6fa9faa1777e29cf0c04be6e4d096a348", size = 12209004, upload-time = "2025-01-10T08:07:06.931Z" }, - { url = "https://files.pythonhosted.org/packages/94/be/47e16cdd1e7fcf97d95b3cb08bde1abb13e627861af427a3651fcb80b517/scikit_learn-1.6.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8ca8cb270fee8f1f76fa9bfd5c3507d60c6438bbee5687f81042e2bb98e5a97", size = 13171776, upload-time = "2025-01-10T08:07:11.715Z" }, - { url = "https://files.pythonhosted.org/packages/34/b0/ca92b90859070a1487827dbc672f998da95ce83edce1270fc23f96f1f61a/scikit_learn-1.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:7a1c43c8ec9fde528d664d947dc4c0789be4077a3647f232869f41d9bf50e0fb", size = 11071865, upload-time = "2025-01-10T08:07:16.088Z" }, - { url = "https://files.pythonhosted.org/packages/12/ae/993b0fb24a356e71e9a894e42b8a9eec528d4c70217353a1cd7a48bc25d4/scikit_learn-1.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a17c1dea1d56dcda2fac315712f3651a1fea86565b64b48fa1bc090249cbf236", size = 11955804, upload-time = "2025-01-10T08:07:20.385Z" }, - { url = "https://files.pythonhosted.org/packages/d6/54/32fa2ee591af44507eac86406fa6bba968d1eb22831494470d0a2e4a1eb1/scikit_learn-1.6.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6a7aa5f9908f0f28f4edaa6963c0a6183f1911e63a69aa03782f0d924c830a35", size = 11100530, upload-time = "2025-01-10T08:07:23.675Z" }, - { url = "https://files.pythonhosted.org/packages/3f/58/55856da1adec655bdce77b502e94a267bf40a8c0b89f8622837f89503b5a/scikit_learn-1.6.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0650e730afb87402baa88afbf31c07b84c98272622aaba002559b614600ca691", size = 12433852, upload-time = "2025-01-10T08:07:26.817Z" }, - { url = "https://files.pythonhosted.org/packages/ff/4f/c83853af13901a574f8f13b645467285a48940f185b690936bb700a50863/scikit_learn-1.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:3f59fe08dc03ea158605170eb52b22a105f238a5d512c4470ddeca71feae8e5f", size = 11337256, upload-time = "2025-01-10T08:07:31.084Z" }, - { url = "https://files.pythonhosted.org/packages/d2/37/b305b759cc65829fe1b8853ff3e308b12cdd9d8884aa27840835560f2b42/scikit_learn-1.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6849dd3234e87f55dce1db34c89a810b489ead832aaf4d4550b7ea85628be6c1", size = 12101868, upload-time = "2025-01-10T08:07:34.189Z" }, - { url = "https://files.pythonhosted.org/packages/83/74/f64379a4ed5879d9db744fe37cfe1978c07c66684d2439c3060d19a536d8/scikit_learn-1.6.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:e7be3fa5d2eb9be7d77c3734ff1d599151bb523674be9b834e8da6abe132f44e", size = 11144062, upload-time = "2025-01-10T08:07:37.67Z" }, - { url = "https://files.pythonhosted.org/packages/fd/dc/d5457e03dc9c971ce2b0d750e33148dd060fefb8b7dc71acd6054e4bb51b/scikit_learn-1.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44a17798172df1d3c1065e8fcf9019183f06c87609b49a124ebdf57ae6cb0107", size = 12693173, upload-time = "2025-01-10T08:07:42.713Z" }, - { url = "https://files.pythonhosted.org/packages/79/35/b1d2188967c3204c78fa79c9263668cf1b98060e8e58d1a730fe5b2317bb/scikit_learn-1.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b7a3b86e411e4bce21186e1c180d792f3d99223dcfa3b4f597ecc92fa1a422", size = 13518605, upload-time = "2025-01-10T08:07:46.551Z" }, - { url = "https://files.pythonhosted.org/packages/fb/d8/8d603bdd26601f4b07e2363032b8565ab82eb857f93d86d0f7956fcf4523/scikit_learn-1.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7a73d457070e3318e32bdb3aa79a8d990474f19035464dfd8bede2883ab5dc3b", size = 11155078, upload-time = "2025-01-10T08:07:51.376Z" }, ] [[package]] @@ -4404,14 +2868,13 @@ name = "scikit-learn" version = "1.7.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", + "python_full_version < '3.11'", ] dependencies = [ - { name = "joblib", marker = "python_full_version == '3.10.*'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "threadpoolctl", marker = "python_full_version == '3.10.*'" }, + { name = "joblib", 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 = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "threadpoolctl", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" } wheels = [ @@ -4452,18 +2915,8 @@ name = "scikit-learn" version = "1.8.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", ] dependencies = [ { name = "joblib", marker = "python_full_version >= '3.11'" }, @@ -4511,54 +2964,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/60/22/d7b2ebe4704a5e50790ba089d5c2ae308ab6bb852719e6c3bd4f04c3a363/scikit_learn-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f28dd15c6bb0b66ba09728cf09fd8736c304be29409bd8445a080c1280619e8c", size = 8002647, upload-time = "2025-12-10T07:08:51.601Z" }, ] -[[package]] -name = "scipy" -version = "1.13.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ae/00/48c2f661e2816ccf2ecd77982f6605b2950afe60f60a52b4cbbc2504aa8f/scipy-1.13.1.tar.gz", hash = "sha256:095a87a0312b08dfd6a6155cbbd310a8c51800fc931b8c0b84003014b874ed3c", size = 57210720, upload-time = "2024-05-23T03:29:26.079Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/33/59/41b2529908c002ade869623b87eecff3e11e3ce62e996d0bdcb536984187/scipy-1.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:20335853b85e9a49ff7572ab453794298bcf0354d8068c5f6775a0eabf350aca", size = 39328076, upload-time = "2024-05-23T03:19:01.687Z" }, - { url = "https://files.pythonhosted.org/packages/d5/33/f1307601f492f764062ce7dd471a14750f3360e33cd0f8c614dae208492c/scipy-1.13.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d605e9c23906d1994f55ace80e0125c587f96c020037ea6aa98d01b4bd2e222f", size = 30306232, upload-time = "2024-05-23T03:19:09.089Z" }, - { url = "https://files.pythonhosted.org/packages/c0/66/9cd4f501dd5ea03e4a4572ecd874936d0da296bd04d1c45ae1a4a75d9c3a/scipy-1.13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfa31f1def5c819b19ecc3a8b52d28ffdcc7ed52bb20c9a7589669dd3c250989", size = 33743202, upload-time = "2024-05-23T03:19:15.138Z" }, - { url = "https://files.pythonhosted.org/packages/a3/ba/7255e5dc82a65adbe83771c72f384d99c43063648456796436c9a5585ec3/scipy-1.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26264b282b9da0952a024ae34710c2aff7d27480ee91a2e82b7b7073c24722f", size = 38577335, upload-time = "2024-05-23T03:19:21.984Z" }, - { url = "https://files.pythonhosted.org/packages/49/a5/bb9ded8326e9f0cdfdc412eeda1054b914dfea952bda2097d174f8832cc0/scipy-1.13.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eccfa1906eacc02de42d70ef4aecea45415f5be17e72b61bafcfd329bdc52e94", size = 38820728, upload-time = "2024-05-23T03:19:28.225Z" }, - { url = "https://files.pythonhosted.org/packages/12/30/df7a8fcc08f9b4a83f5f27cfaaa7d43f9a2d2ad0b6562cced433e5b04e31/scipy-1.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:2831f0dc9c5ea9edd6e51e6e769b655f08ec6db6e2e10f86ef39bd32eb11da54", size = 46210588, upload-time = "2024-05-23T03:19:35.661Z" }, - { url = "https://files.pythonhosted.org/packages/b4/15/4a4bb1b15bbd2cd2786c4f46e76b871b28799b67891f23f455323a0cdcfb/scipy-1.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:27e52b09c0d3a1d5b63e1105f24177e544a222b43611aaf5bc44d4a0979e32f9", size = 39333805, upload-time = "2024-05-23T03:19:43.081Z" }, - { url = "https://files.pythonhosted.org/packages/ba/92/42476de1af309c27710004f5cdebc27bec62c204db42e05b23a302cb0c9a/scipy-1.13.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:54f430b00f0133e2224c3ba42b805bfd0086fe488835effa33fa291561932326", size = 30317687, upload-time = "2024-05-23T03:19:48.799Z" }, - { url = "https://files.pythonhosted.org/packages/80/ba/8be64fe225360a4beb6840f3cbee494c107c0887f33350d0a47d55400b01/scipy-1.13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e89369d27f9e7b0884ae559a3a956e77c02114cc60a6058b4e5011572eea9299", size = 33694638, upload-time = "2024-05-23T03:19:55.104Z" }, - { url = "https://files.pythonhosted.org/packages/36/07/035d22ff9795129c5a847c64cb43c1fa9188826b59344fee28a3ab02e283/scipy-1.13.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a78b4b3345f1b6f68a763c6e25c0c9a23a9fd0f39f5f3d200efe8feda560a5fa", size = 38569931, upload-time = "2024-05-23T03:20:01.82Z" }, - { url = "https://files.pythonhosted.org/packages/d9/10/f9b43de37e5ed91facc0cfff31d45ed0104f359e4f9a68416cbf4e790241/scipy-1.13.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45484bee6d65633752c490404513b9ef02475b4284c4cfab0ef946def50b3f59", size = 38838145, upload-time = "2024-05-23T03:20:09.173Z" }, - { url = "https://files.pythonhosted.org/packages/4a/48/4513a1a5623a23e95f94abd675ed91cfb19989c58e9f6f7d03990f6caf3d/scipy-1.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:5713f62f781eebd8d597eb3f88b8bf9274e79eeabf63afb4a737abc6c84ad37b", size = 46196227, upload-time = "2024-05-23T03:20:16.433Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7b/fb6b46fbee30fc7051913068758414f2721003a89dd9a707ad49174e3843/scipy-1.13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5d72782f39716b2b3509cd7c33cdc08c96f2f4d2b06d51e52fb45a19ca0c86a1", size = 39357301, upload-time = "2024-05-23T03:20:23.538Z" }, - { url = "https://files.pythonhosted.org/packages/dc/5a/2043a3bde1443d94014aaa41e0b50c39d046dda8360abd3b2a1d3f79907d/scipy-1.13.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:017367484ce5498445aade74b1d5ab377acdc65e27095155e448c88497755a5d", size = 30363348, upload-time = "2024-05-23T03:20:29.885Z" }, - { url = "https://files.pythonhosted.org/packages/e7/cb/26e4a47364bbfdb3b7fb3363be6d8a1c543bcd70a7753ab397350f5f189a/scipy-1.13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:949ae67db5fa78a86e8fa644b9a6b07252f449dcf74247108c50e1d20d2b4627", size = 33406062, upload-time = "2024-05-23T03:20:36.012Z" }, - { url = "https://files.pythonhosted.org/packages/88/ab/6ecdc526d509d33814835447bbbeedbebdec7cca46ef495a61b00a35b4bf/scipy-1.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de3ade0e53bc1f21358aa74ff4830235d716211d7d077e340c7349bc3542e884", size = 38218311, upload-time = "2024-05-23T03:20:42.086Z" }, - { url = "https://files.pythonhosted.org/packages/0b/00/9f54554f0f8318100a71515122d8f4f503b1a2c4b4cfab3b4b68c0eb08fa/scipy-1.13.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ac65fb503dad64218c228e2dc2d0a0193f7904747db43014645ae139c8fad16", size = 38442493, upload-time = "2024-05-23T03:20:48.292Z" }, - { url = "https://files.pythonhosted.org/packages/3e/df/963384e90733e08eac978cd103c34df181d1fec424de383cdc443f418dd4/scipy-1.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:cdd7dacfb95fea358916410ec61bbc20440f7860333aee6d882bb8046264e949", size = 45910955, upload-time = "2024-05-23T03:20:55.091Z" }, - { url = "https://files.pythonhosted.org/packages/7f/29/c2ea58c9731b9ecb30b6738113a95d147e83922986b34c685b8f6eefde21/scipy-1.13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:436bbb42a94a8aeef855d755ce5a465479c721e9d684de76bf61a62e7c2b81d5", size = 39352927, upload-time = "2024-05-23T03:21:01.95Z" }, - { url = "https://files.pythonhosted.org/packages/5c/c0/e71b94b20ccf9effb38d7147c0064c08c622309fd487b1b677771a97d18c/scipy-1.13.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:8335549ebbca860c52bf3d02f80784e91a004b71b059e3eea9678ba994796a24", size = 30324538, upload-time = "2024-05-23T03:21:07.634Z" }, - { url = "https://files.pythonhosted.org/packages/6d/0f/aaa55b06d474817cea311e7b10aab2ea1fd5d43bc6a2861ccc9caec9f418/scipy-1.13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d533654b7d221a6a97304ab63c41c96473ff04459e404b83275b60aa8f4b7004", size = 33732190, upload-time = "2024-05-23T03:21:14.41Z" }, - { url = "https://files.pythonhosted.org/packages/35/f5/d0ad1a96f80962ba65e2ce1de6a1e59edecd1f0a7b55990ed208848012e0/scipy-1.13.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637e98dcf185ba7f8e663e122ebf908c4702420477ae52a04f9908707456ba4d", size = 38612244, upload-time = "2024-05-23T03:21:21.827Z" }, - { url = "https://files.pythonhosted.org/packages/8d/02/1165905f14962174e6569076bcc3315809ae1291ed14de6448cc151eedfd/scipy-1.13.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a014c2b3697bde71724244f63de2476925596c24285c7a637364761f8710891c", size = 38845637, upload-time = "2024-05-23T03:21:28.729Z" }, - { url = "https://files.pythonhosted.org/packages/3e/77/dab54fe647a08ee4253963bcd8f9cf17509c8ca64d6335141422fe2e2114/scipy-1.13.1-cp39-cp39-win_amd64.whl", hash = "sha256:392e4ec766654852c25ebad4f64e4e584cf19820b980bc04960bca0b0cd6eaa2", size = 46227440, upload-time = "2024-05-23T03:21:35.888Z" }, -] - [[package]] name = "scipy" version = "1.15.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", + "python_full_version < '3.11'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" } wheels = [ @@ -4614,18 +3028,8 @@ name = "scipy" version = "1.17.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", ] dependencies = [ { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, @@ -4699,10 +3103,8 @@ name = "seaborn" version = "0.13.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "matplotlib", version = "3.9.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "matplotlib", version = "3.10.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "matplotlib" }, + { 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 = "pandas" }, ] @@ -4711,60 +3113,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987", size = 294914, upload-time = "2024-01-25T13:21:49.598Z" }, ] -[[package]] -name = "sentence-transformers" -version = "5.1.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "huggingface-hub", version = "0.36.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "pillow", version = "11.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "scikit-learn", version = "1.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "scipy", version = "1.13.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "torch", version = "2.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "tqdm", marker = "python_full_version < '3.10'" }, - { name = "transformers", version = "4.57.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "typing-extensions", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0f/96/f3f3409179d14dbfdbea8622e2e9eaa3c8836ddcaecd2cd5ff0a11731d20/sentence_transformers-5.1.2.tar.gz", hash = "sha256:0f6c8bd916a78dc65b366feb8d22fd885efdb37432e7630020d113233af2b856", size = 375185, upload-time = "2025-10-22T12:47:55.019Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/a6/a607a737dc1a00b7afe267b9bfde101b8cee2529e197e57471d23137d4e5/sentence_transformers-5.1.2-py3-none-any.whl", hash = "sha256:724ce0ea62200f413f1a5059712aff66495bc4e815a1493f7f9bca242414c333", size = 488009, upload-time = "2025-10-22T12:47:53.433Z" }, -] - [[package]] name = "sentence-transformers" version = "5.2.3" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] dependencies = [ - { name = "huggingface-hub", version = "1.3.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "huggingface-hub" }, + { 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 = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "scipy", version = "1.17.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "torch", version = "2.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "tqdm", marker = "python_full_version >= '3.10'" }, - { name = "transformers", version = "5.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.10'" }, + { name = "torch" }, + { name = "tqdm" }, + { name = "transformers" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5b/30/21664028fc0776eb1ca024879480bbbab36f02923a8ff9e4cae5a150fa35/sentence_transformers-5.2.3.tar.gz", hash = "sha256:3cd3044e1f3fe859b6a1b66336aac502eaae5d3dd7d5c8fc237f37fbf58137c7", size = 381623, upload-time = "2026-02-17T14:05:20.238Z" } wheels = [ @@ -4807,78 +3171,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303, upload-time = "2025-01-02T07:14:38.724Z" }, ] -[[package]] -name = "streamlit" -version = "1.50.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "altair", version = "5.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "blinker", marker = "python_full_version < '3.10'" }, - { name = "cachetools", marker = "python_full_version < '3.10'" }, - { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "gitpython", marker = "python_full_version < '3.10'" }, - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "pandas", marker = "python_full_version < '3.10'" }, - { name = "pillow", version = "11.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "protobuf", marker = "python_full_version < '3.10'" }, - { name = "pyarrow", version = "21.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "pydeck", marker = "python_full_version < '3.10'" }, - { name = "requests", marker = "python_full_version < '3.10'" }, - { name = "tenacity", marker = "python_full_version < '3.10'" }, - { name = "toml", marker = "python_full_version < '3.10'" }, - { name = "tornado", marker = "python_full_version < '3.10'" }, - { name = "typing-extensions", marker = "python_full_version < '3.10'" }, - { name = "watchdog", marker = "python_full_version < '3.10' and sys_platform != 'darwin'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d6/f6/f7d3a0146577c1918439d3163707040f7111a7d2e7e2c73fa7adeb169c06/streamlit-1.50.0.tar.gz", hash = "sha256:87221d568aac585274a05ef18a378b03df332b93e08103fffcf3cd84d852af46", size = 9664808, upload-time = "2025-09-23T19:24:00.31Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/38/991bbf9fa3ed3d9c8e69265fc449bdaade8131c7f0f750dbd388c3c477dc/streamlit-1.50.0-py3-none-any.whl", hash = "sha256:9403b8f94c0a89f80cf679c2fcc803d9a6951e0fba542e7611995de3f67b4bb3", size = 10068477, upload-time = "2025-09-23T19:23:57.245Z" }, -] - [[package]] name = "streamlit" version = "1.53.1" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] dependencies = [ - { name = "altair", version = "6.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "blinker", marker = "python_full_version >= '3.10'" }, - { name = "cachetools", marker = "python_full_version >= '3.10'" }, - { name = "click", version = "8.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "gitpython", marker = "python_full_version >= '3.10'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "altair" }, + { name = "blinker" }, + { name = "cachetools" }, + { name = "click" }, + { name = "gitpython" }, + { 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 = "packaging", version = "26.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "pandas", marker = "python_full_version >= '3.10'" }, - { name = "pillow", version = "12.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "protobuf", marker = "python_full_version >= '3.10'" }, - { name = "pyarrow", version = "23.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "pydeck", marker = "python_full_version >= '3.10'" }, - { name = "requests", marker = "python_full_version >= '3.10'" }, - { name = "tenacity", marker = "python_full_version >= '3.10'" }, - { name = "toml", marker = "python_full_version >= '3.10'" }, - { name = "tornado", marker = "python_full_version >= '3.10'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.10'" }, - { name = "watchdog", marker = "python_full_version >= '3.10' and sys_platform != 'darwin'" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pillow" }, + { name = "protobuf" }, + { name = "pyarrow" }, + { name = "pydeck" }, + { name = "requests" }, + { name = "tenacity" }, + { name = "toml" }, + { name = "tornado" }, + { name = "typing-extensions" }, + { name = "watchdog", marker = "sys_platform != 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/31/cc/347730d06e3950e426bf2ee06eaf9b281e18387bffa82bd69aa0f281eee3/streamlit-1.53.1.tar.gz", hash = "sha256:ae656af3b68b4bb2d669fa977606096f2021bcbaa14a454a290f8e0a37bab277", size = 8650843, upload-time = "2026-01-22T21:39:04.087Z" } wheels = [ @@ -4920,8 +3236,7 @@ name = "tokenizers" version = "0.22.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "huggingface-hub", version = "0.36.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "huggingface-hub", version = "1.3.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "huggingface-hub" }, ] sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115, upload-time = "2026-01-05T10:45:15.988Z" } wheels = [ @@ -4944,10 +3259,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/46/cd/e4851401f3d8f6f45d8480262ab6a5c8cb9c4302a790a35aa14eeed6d2fd/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e10bf9113d209be7cd046d40fbabbaf3278ff6d18eb4da4c500443185dc1896c", size = 3161308, upload-time = "2026-01-05T10:40:40.737Z" }, { url = "https://files.pythonhosted.org/packages/6f/6e/55553992a89982cd12d4a66dddb5e02126c58677ea3931efcbe601d419db/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64d94e84f6660764e64e7e0b22baa72f6cd942279fdbb21d46abd70d179f0195", size = 3718964, upload-time = "2026-01-05T10:40:46.56Z" }, { url = "https://files.pythonhosted.org/packages/59/8c/b1c87148aa15e099243ec9f0cf9d0e970cc2234c3257d558c25a2c5304e6/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f01a9c019878532f98927d2bacb79bbb404b43d3437455522a00a30718cdedb5", size = 3373542, upload-time = "2026-01-05T10:40:52.803Z" }, - { url = "https://files.pythonhosted.org/packages/27/46/8d7db1dff181be50b207ab0a7483a22d5c3a4f903a9afc7cf7e465ad8109/tokenizers-0.22.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:319f659ee992222f04e58f84cbf407cfa66a65fe3a8de44e8ad2bc53e7d99012", size = 3287784, upload-time = "2026-01-05T10:40:37.108Z" }, - { url = "https://files.pythonhosted.org/packages/5b/6e/3bc33cae8bf114afa5a98e35eb065c72b7c37d01d370906a893f33881767/tokenizers-0.22.2-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1e50f8554d504f617d9e9d6e4c2c2884a12b388a97c5c77f0bc6cf4cd032feee", size = 3164301, upload-time = "2026-01-05T10:40:42.367Z" }, - { url = "https://files.pythonhosted.org/packages/91/fc/6aa749d7d443aab4daa6f8bc00338389149fd2534e25b772285c3301993e/tokenizers-0.22.2-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a62ba2c5faa2dd175aaeed7b15abf18d20266189fb3406c5d0550dd34dd5f37", size = 3717771, upload-time = "2026-01-05T10:40:49.076Z" }, - { url = "https://files.pythonhosted.org/packages/fc/60/5b440d251863bd33f9b0a416c695b0309487b83abf6f2dafe9163a3aeac2/tokenizers-0.22.2-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:143b999bdc46d10febb15cbffb4207ddd1f410e2c755857b5a0797961bbdc113", size = 3377740, upload-time = "2026-01-05T10:40:54.859Z" }, ] [[package]] @@ -5013,109 +3324,36 @@ 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.8.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "filelock", version = "3.19.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "fsspec", marker = "python_full_version < '3.10'" }, - { name = "jinja2", marker = "python_full_version < '3.10'" }, - { name = "networkx", version = "3.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "nvidia-cublas-cu12", marker = "python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-cupti-cu12", marker = "python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-nvrtc-cu12", marker = "python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-runtime-cu12", marker = "python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cudnn-cu12", marker = "python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufft-cu12", marker = "python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufile-cu12", marker = "python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-curand-cu12", marker = "python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusolver-cu12", marker = "python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparse-cu12", marker = "python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparselt-cu12", marker = "python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nccl-cu12", version = "2.27.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvjitlink-cu12", marker = "python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvtx-cu12", marker = "python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "sympy", marker = "python_full_version < '3.10'" }, - { name = "triton", version = "3.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "typing-extensions", marker = "python_full_version < '3.10'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/63/28/110f7274254f1b8476c561dada127173f994afa2b1ffc044efb773c15650/torch-2.8.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:0be92c08b44009d4131d1ff7a8060d10bafdb7ddcb7359ef8d8c5169007ea905", size = 102052793, upload-time = "2025-08-06T14:53:15.852Z" }, - { url = "https://files.pythonhosted.org/packages/70/1c/58da560016f81c339ae14ab16c98153d51c941544ae568da3cb5b1ceb572/torch-2.8.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:89aa9ee820bb39d4d72b794345cccef106b574508dd17dbec457949678c76011", size = 888025420, upload-time = "2025-08-06T14:54:18.014Z" }, - { url = "https://files.pythonhosted.org/packages/70/87/f69752d0dd4ba8218c390f0438130c166fa264a33b7025adb5014b92192c/torch-2.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e8e5bf982e87e2b59d932769938b698858c64cc53753894be25629bdf5cf2f46", size = 241363614, upload-time = "2025-08-06T14:53:31.496Z" }, - { url = "https://files.pythonhosted.org/packages/ef/d6/e6d4c57e61c2b2175d3aafbfb779926a2cfd7c32eeda7c543925dceec923/torch-2.8.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:a3f16a58a9a800f589b26d47ee15aca3acf065546137fc2af039876135f4c760", size = 73611154, upload-time = "2025-08-06T14:53:10.919Z" }, - { url = "https://files.pythonhosted.org/packages/8f/c4/3e7a3887eba14e815e614db70b3b529112d1513d9dae6f4d43e373360b7f/torch-2.8.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:220a06fd7af8b653c35d359dfe1aaf32f65aa85befa342629f716acb134b9710", size = 102073391, upload-time = "2025-08-06T14:53:20.937Z" }, - { url = "https://files.pythonhosted.org/packages/5a/63/4fdc45a0304536e75a5e1b1bbfb1b56dd0e2743c48ee83ca729f7ce44162/torch-2.8.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:c12fa219f51a933d5f80eeb3a7a5d0cbe9168c0a14bbb4055f1979431660879b", size = 888063640, upload-time = "2025-08-06T14:55:05.325Z" }, - { url = "https://files.pythonhosted.org/packages/84/57/2f64161769610cf6b1c5ed782bd8a780e18a3c9d48931319f2887fa9d0b1/torch-2.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:8c7ef765e27551b2fbfc0f41bcf270e1292d9bf79f8e0724848b1682be6e80aa", size = 241366752, upload-time = "2025-08-06T14:53:38.692Z" }, - { url = "https://files.pythonhosted.org/packages/a4/5e/05a5c46085d9b97e928f3f037081d3d2b87fb4b4195030fc099aaec5effc/torch-2.8.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:5ae0524688fb6707c57a530c2325e13bb0090b745ba7b4a2cd6a3ce262572916", size = 73621174, upload-time = "2025-08-06T14:53:25.44Z" }, - { url = "https://files.pythonhosted.org/packages/49/0c/2fd4df0d83a495bb5e54dca4474c4ec5f9c62db185421563deeb5dabf609/torch-2.8.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e2fab4153768d433f8ed9279c8133a114a034a61e77a3a104dcdf54388838705", size = 101906089, upload-time = "2025-08-06T14:53:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/99/a8/6acf48d48838fb8fe480597d98a0668c2beb02ee4755cc136de92a0a956f/torch-2.8.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b2aca0939fb7e4d842561febbd4ffda67a8e958ff725c1c27e244e85e982173c", size = 887913624, upload-time = "2025-08-06T14:56:44.33Z" }, - { url = "https://files.pythonhosted.org/packages/af/8a/5c87f08e3abd825c7dfecef5a0f1d9aa5df5dd0e3fd1fa2f490a8e512402/torch-2.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:2f4ac52f0130275d7517b03a33d2493bab3693c83dcfadf4f81688ea82147d2e", size = 241326087, upload-time = "2025-08-06T14:53:46.503Z" }, - { url = "https://files.pythonhosted.org/packages/be/66/5c9a321b325aaecb92d4d1855421e3a055abd77903b7dab6575ca07796db/torch-2.8.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:619c2869db3ada2c0105487ba21b5008defcc472d23f8b80ed91ac4a380283b0", size = 73630478, upload-time = "2025-08-06T14:53:57.144Z" }, - { url = "https://files.pythonhosted.org/packages/10/4e/469ced5a0603245d6a19a556e9053300033f9c5baccf43a3d25ba73e189e/torch-2.8.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:2b2f96814e0345f5a5aed9bf9734efa913678ed19caf6dc2cddb7930672d6128", size = 101936856, upload-time = "2025-08-06T14:54:01.526Z" }, - { url = "https://files.pythonhosted.org/packages/16/82/3948e54c01b2109238357c6f86242e6ecbf0c63a1af46906772902f82057/torch-2.8.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:65616ca8ec6f43245e1f5f296603e33923f4c30f93d65e103d9e50c25b35150b", size = 887922844, upload-time = "2025-08-06T14:55:50.78Z" }, - { url = "https://files.pythonhosted.org/packages/e3/54/941ea0a860f2717d86a811adf0c2cd01b3983bdd460d0803053c4e0b8649/torch-2.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:659df54119ae03e83a800addc125856effda88b016dfc54d9f65215c3975be16", size = 241330968, upload-time = "2025-08-06T14:54:45.293Z" }, - { url = "https://files.pythonhosted.org/packages/de/69/8b7b13bba430f5e21d77708b616f767683629fc4f8037564a177d20f90ed/torch-2.8.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:1a62a1ec4b0498930e2543535cf70b1bef8c777713de7ceb84cd79115f553767", size = 73915128, upload-time = "2025-08-06T14:54:34.769Z" }, - { url = "https://files.pythonhosted.org/packages/15/0e/8a800e093b7f7430dbaefa80075aee9158ec22e4c4fc3c1a66e4fb96cb4f/torch-2.8.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:83c13411a26fac3d101fe8035a6b0476ae606deb8688e904e796a3534c197def", size = 102020139, upload-time = "2025-08-06T14:54:39.047Z" }, - { url = "https://files.pythonhosted.org/packages/4a/15/5e488ca0bc6162c86a33b58642bc577c84ded17c7b72d97e49b5833e2d73/torch-2.8.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:8f0a9d617a66509ded240add3754e462430a6c1fc5589f86c17b433dd808f97a", size = 887990692, upload-time = "2025-08-06T14:56:18.286Z" }, - { url = "https://files.pythonhosted.org/packages/b4/a8/6a04e4b54472fc5dba7ca2341ab219e529f3c07b6941059fbf18dccac31f/torch-2.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a7242b86f42be98ac674b88a4988643b9bc6145437ec8f048fea23f72feb5eca", size = 241603453, upload-time = "2025-08-06T14:55:22.945Z" }, - { url = "https://files.pythonhosted.org/packages/04/6e/650bb7f28f771af0cb791b02348db8b7f5f64f40f6829ee82aa6ce99aabe/torch-2.8.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:7b677e17f5a3e69fdef7eb3b9da72622f8d322692930297e4ccb52fefc6c8211", size = 73632395, upload-time = "2025-08-06T14:55:28.645Z" }, - { url = "https://files.pythonhosted.org/packages/5b/b0/a321f27270049baa12f5c3fb0d6ceea005634787e3af9a8d75dce8306b0a/torch-2.8.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:da6afa31c13b669d4ba49d8a2169f0db2c3ec6bec4af898aa714f401d4c38904", size = 102059214, upload-time = "2025-08-06T14:55:33.433Z" }, - { url = "https://files.pythonhosted.org/packages/fd/dd/1630cb51b10d3d2e97db95e5a84c32def81fc26b005bce6fc880b0e6db81/torch-2.8.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:06fcee8000e5c62a9f3e52a688b9c5abb7c6228d0e56e3452983416025c41381", size = 888024302, upload-time = "2025-08-06T14:57:28.23Z" }, - { url = "https://files.pythonhosted.org/packages/b9/dc/1f1f621afe15e3c496e1e8f94f8903f75f87e7d642d5a985e92210cc208d/torch-2.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:5128fe752a355d9308e56af1ad28b15266fe2da5948660fad44de9e3a9e36e8c", size = 241249338, upload-time = "2025-08-06T14:57:05.669Z" }, - { url = "https://files.pythonhosted.org/packages/ae/95/ae26263aceb3d57b821179f827d0e321373ed49423e603dd5906ab14a730/torch-2.8.0-cp39-none-macosx_11_0_arm64.whl", hash = "sha256:e9f071f5b52a9f6970dc8a919694b27a91ae9dc08898b2b988abbef5eddfd1ae", size = 73610795, upload-time = "2025-08-06T14:57:11.513Z" }, -] - [[package]] name = "torch" version = "2.10.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] dependencies = [ - { name = "cuda-bindings", marker = "python_full_version >= '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "filelock", version = "3.20.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "fsspec", marker = "python_full_version >= '3.10'" }, - { name = "jinja2", marker = "python_full_version >= '3.10'" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { 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 = "python_full_version >= '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-cupti-cu12", marker = "python_full_version >= '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-nvrtc-cu12", marker = "python_full_version >= '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-runtime-cu12", marker = "python_full_version >= '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cudnn-cu12", marker = "python_full_version >= '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufft-cu12", marker = "python_full_version >= '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufile-cu12", marker = "python_full_version >= '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-curand-cu12", marker = "python_full_version >= '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusolver-cu12", marker = "python_full_version >= '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparse-cu12", marker = "python_full_version >= '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparselt-cu12", marker = "python_full_version >= '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nccl-cu12", version = "2.27.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvjitlink-cu12", marker = "python_full_version >= '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvshmem-cu12", marker = "python_full_version >= '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvtx-cu12", marker = "python_full_version >= '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { 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", marker = "python_full_version >= '3.10'" }, - { name = "triton", version = "3.6.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.10'" }, + { 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" }, @@ -5159,59 +3397,21 @@ wheels = [ { 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 = "torch-geometric" -version = "2.6.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "aiohttp", marker = "python_full_version < '3.10'" }, - { name = "fsspec", marker = "python_full_version < '3.10'" }, - { name = "jinja2", marker = "python_full_version < '3.10'" }, - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "psutil", marker = "python_full_version < '3.10'" }, - { name = "pyparsing", marker = "python_full_version < '3.10'" }, - { name = "requests", marker = "python_full_version < '3.10'" }, - { name = "tqdm", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e8/81/e1b015494cb9e0bf4c47cc8426e49736120248733be0e22072a5628ae9ed/torch_geometric-2.6.1.tar.gz", hash = "sha256:1f18f9d0fc4d2239d526221e4f22606a4a3895b5d965a9856d27610a3df662c6", size = 771490, upload-time = "2024-09-26T08:11:30.25Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/9f/157e913626c1acfb3b19ce000b1a6e4e4fb177c0bc0ea0c67ca5bd714b5a/torch_geometric-2.6.1-py3-none-any.whl", hash = "sha256:8faeb353f9655f7dbec44c5e0b44c721773bdfb279994da96b9b8b12fd30f427", size = 1135632, upload-time = "2024-09-26T08:11:27.194Z" }, -] - [[package]] name = "torch-geometric" version = "2.7.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] dependencies = [ - { name = "aiohttp", marker = "python_full_version >= '3.10'" }, - { name = "fsspec", marker = "python_full_version >= '3.10'" }, - { name = "jinja2", marker = "python_full_version >= '3.10'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "aiohttp" }, + { name = "fsspec" }, + { name = "jinja2" }, + { 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 = "psutil", marker = "python_full_version >= '3.10'" }, - { name = "pyparsing", marker = "python_full_version >= '3.10'" }, - { name = "requests", marker = "python_full_version >= '3.10'" }, - { name = "tqdm", marker = "python_full_version >= '3.10'" }, - { name = "xxhash", marker = "python_full_version >= '3.10'" }, + { name = "psutil" }, + { name = "pyparsing" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "xxhash" }, ] sdist = { url = "https://files.pythonhosted.org/packages/75/63/b210152635902da7fe79fcdd16517fae108f457a0ed22c737e702a9afbae/torch_geometric-2.7.0.tar.gz", hash = "sha256:f9099e4aece1a9f618c84dbaac33a77f43139736698c7e8bddf3301ef1f2e8d4", size = 876725, upload-time = "2025-10-15T20:48:03.443Z" } wheels = [ @@ -5249,98 +3449,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" }, ] -[[package]] -name = "transformers" -version = "4.57.6" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "filelock", version = "3.19.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "huggingface-hub", version = "0.36.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "pyyaml", marker = "python_full_version < '3.10'" }, - { name = "regex", marker = "python_full_version < '3.10'" }, - { name = "requests", marker = "python_full_version < '3.10'" }, - { name = "safetensors", marker = "python_full_version < '3.10'" }, - { name = "tokenizers", marker = "python_full_version < '3.10'" }, - { name = "tqdm", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c4/35/67252acc1b929dc88b6602e8c4a982e64f31e733b804c14bc24b47da35e6/transformers-4.57.6.tar.gz", hash = "sha256:55e44126ece9dc0a291521b7e5492b572e6ef2766338a610b9ab5afbb70689d3", size = 10134912, upload-time = "2026-01-16T10:38:39.284Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/b8/e484ef633af3887baeeb4b6ad12743363af7cce68ae51e938e00aaa0529d/transformers-4.57.6-py3-none-any.whl", hash = "sha256:4c9e9de11333ddfe5114bc872c9f370509198acf0b87a832a0ab9458e2bd0550", size = 11993498, upload-time = "2026-01-16T10:38:31.289Z" }, -] - [[package]] name = "transformers" version = "5.0.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and platform_machine != 'ARM64' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*' and platform_machine == 'ARM64' and sys_platform == 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] dependencies = [ - { name = "filelock", version = "3.20.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "huggingface-hub", version = "1.3.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "filelock" }, + { name = "huggingface-hub" }, + { 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 = "packaging", version = "26.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "pyyaml", marker = "python_full_version >= '3.10'" }, - { name = "regex", marker = "python_full_version >= '3.10'" }, - { name = "safetensors", marker = "python_full_version >= '3.10'" }, - { name = "tokenizers", marker = "python_full_version >= '3.10'" }, - { name = "tqdm", marker = "python_full_version >= '3.10'" }, - { name = "typer-slim", marker = "python_full_version >= '3.10'" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "safetensors" }, + { name = "tokenizers" }, + { name = "tqdm" }, + { name = "typer-slim" }, ] sdist = { url = "https://files.pythonhosted.org/packages/bc/79/845941711811789c85fb7e2599cea425a14a07eda40f50896b9d3fda7492/transformers-5.0.0.tar.gz", hash = "sha256:5f5634efed6cf76ad068cc5834c7adbc32db78bbd6211fb70df2325a9c37dec8", size = 8424830, upload-time = "2026-01-26T10:46:46.813Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/52/f3/ac976fa8e305c9e49772527e09fbdc27cc6831b8a2f6b6063406626be5dd/transformers-5.0.0-py3-none-any.whl", hash = "sha256:587086f249ce64c817213cf36afdb318d087f790723e9b3d4500b97832afd52d", size = 10142091, upload-time = "2026-01-26T10:46:43.88Z" }, ] -[[package]] -name = "triton" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, - { name = "setuptools", marker = "python_full_version < '3.10'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/ee/0ee5f64a87eeda19bbad9bc54ae5ca5b98186ed00055281fd40fb4beb10e/triton-3.4.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ff2785de9bc02f500e085420273bb5cc9c9bb767584a4aa28d6e360cec70128", size = 155430069, upload-time = "2025-07-30T19:58:21.715Z" }, - { url = "https://files.pythonhosted.org/packages/7d/39/43325b3b651d50187e591eefa22e236b2981afcebaefd4f2fc0ea99df191/triton-3.4.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b70f5e6a41e52e48cfc087436c8a28c17ff98db369447bcaff3b887a3ab4467", size = 155531138, upload-time = "2025-07-30T19:58:29.908Z" }, - { url = "https://files.pythonhosted.org/packages/d0/66/b1eb52839f563623d185f0927eb3530ee4d5ffe9d377cdaf5346b306689e/triton-3.4.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:31c1d84a5c0ec2c0f8e8a072d7fd150cab84a9c239eaddc6706c081bfae4eb04", size = 155560068, upload-time = "2025-07-30T19:58:37.081Z" }, - { url = "https://files.pythonhosted.org/packages/30/7b/0a685684ed5322d2af0bddefed7906674f67974aa88b0fae6e82e3b766f6/triton-3.4.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00be2964616f4c619193cb0d1b29a99bd4b001d7dc333816073f92cf2a8ccdeb", size = 155569223, upload-time = "2025-07-30T19:58:44.017Z" }, - { url = "https://files.pythonhosted.org/packages/20/63/8cb444ad5cdb25d999b7d647abac25af0ee37d292afc009940c05b82dda0/triton-3.4.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7936b18a3499ed62059414d7df563e6c163c5e16c3773678a3ee3d417865035d", size = 155659780, upload-time = "2025-07-30T19:58:51.171Z" }, - { url = "https://files.pythonhosted.org/packages/12/34/1251beb5a3cb93f3950ebe68732752014646003ef6eb11eb5f1a37ca78cd/triton-3.4.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:98e5c1442eaeabae2e2452ae765801bd53cd4ce873cab0d1bdd59a32ab2d9397", size = 155430799, upload-time = "2025-07-30T19:58:57.664Z" }, -] - [[package]] name = "triton" version = "3.6.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "(python_full_version == '3.10.*' and platform_machine != 'ARM64') or (python_full_version == '3.10.*' and sys_platform != 'win32')", -] 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" }, @@ -5356,8 +3490,8 @@ name = "typer-slim" version = "0.21.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click", version = "8.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.10'" }, + { name = "click" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/17/d4/064570dec6358aa9049d4708e4a10407d74c99258f8b2136bb8702303f1a/typer_slim-0.21.1.tar.gz", hash = "sha256:73495dd08c2d0940d611c5a8c04e91c2a0a98600cbd4ee19192255a233b6dbfd", size = 110478, upload-time = "2026-01-06T11:21:11.176Z" } wheels = [ @@ -5397,79 +3531,63 @@ version = "1.0.0" source = { virtual = "." } dependencies = [ { name = "hydra-core" }, - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { 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", version = "2.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "torch", version = "2.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "setuptools" }, + { name = "torch" }, { name = "tqdm" }, ] [package.optional-dependencies] all = [ { name = "datasets" }, - { name = "matplotlib", version = "3.9.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "matplotlib", version = "3.10.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "matplotlib" }, { name = "plotly" }, { name = "pmlb" }, - { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "pytest", version = "9.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "pytest" }, { name = "pytest-xdist" }, { name = "ruff" }, - { name = "scikit-learn", version = "1.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "seaborn" }, - { name = "sentence-transformers", version = "5.1.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "sentence-transformers", version = "5.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "streamlit", version = "1.50.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "streamlit", version = "1.53.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "sentence-transformers" }, + { name = "streamlit" }, { name = "sympy" }, - { name = "torch-geometric", version = "2.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "torch-geometric", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "torch-geometric" }, ] all-tasks = [ { name = "datasets" }, { name = "pmlb" }, - { name = "scikit-learn", version = "1.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "sentence-transformers", version = "5.1.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "sentence-transformers", version = "5.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "sentence-transformers" }, { name = "sympy" }, - { name = "torch-geometric", version = "2.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "torch-geometric", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "torch-geometric" }, ] demo = [ { name = "plotly" }, - { name = "streamlit", version = "1.50.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "streamlit", version = "1.53.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "streamlit" }, ] dev = [ - { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "pytest", version = "9.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "pytest" }, { name = "pytest-xdist" }, { name = "ruff" }, ] lqa = [ { name = "datasets" }, - { name = "sentence-transformers", version = "5.1.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "sentence-transformers", version = "5.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "sentence-transformers" }, ] md17 = [ - { name = "torch-geometric", version = "2.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "torch-geometric", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "torch-geometric" }, ] sr = [ { name = "pmlb" }, - { name = "scikit-learn", version = "1.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sympy" }, ] viz = [ - { name = "matplotlib", version = "3.9.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "matplotlib", version = "3.10.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "matplotlib" }, { name = "seaborn" }, ] @@ -5477,8 +3595,7 @@ viz = [ docs = [ { name = "mkdocs" }, { name = "mkdocs-material" }, - { name = "mkdocstrings", version = "0.30.1", source = { registry = "https://pypi.org/simple" }, extra = ["python"], marker = "python_full_version < '3.10'" }, - { name = "mkdocstrings", version = "1.0.3", source = { registry = "https://pypi.org/simple" }, extra = ["python"], marker = "python_full_version >= '3.10'" }, + { name = "mkdocstrings", extra = ["python"] }, { name = "pymdown-extensions" }, ] @@ -5496,6 +3613,7 @@ requires-dist = [ { name = "scikit-learn", marker = "extra == 'sr'", specifier = ">=1.3.0" }, { name = "seaborn", marker = "extra == 'viz'", specifier = ">=0.13.0" }, { name = "sentence-transformers", marker = "extra == 'lqa'", specifier = ">=5.1.2" }, + { name = "setuptools", specifier = ">=80.10.2" }, { name = "streamlit", marker = "extra == 'demo'", specifier = ">=1.50.0" }, { name = "sympy", marker = "extra == 'sr'", specifier = ">=1.12" }, { name = "torch", specifier = ">=2.0.0" }, @@ -5532,13 +3650,8 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" }, { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" }, { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" }, - { url = "https://files.pythonhosted.org/packages/05/52/7223011bb760fce8ddc53416beb65b83a3ea6d7d13738dde75eeb2c89679/watchdog-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8", size = 96390, upload-time = "2024-11-01T14:06:49.325Z" }, - { url = "https://files.pythonhosted.org/packages/9c/62/d2b21bc4e706d3a9d467561f487c2938cbd881c69f3808c43ac1ec242391/watchdog-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a", size = 88386, upload-time = "2024-11-01T14:06:50.536Z" }, - { url = "https://files.pythonhosted.org/packages/ea/22/1c90b20eda9f4132e4603a26296108728a8bfe9584b006bd05dd94548853/watchdog-6.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c", size = 89017, upload-time = "2024-11-01T14:06:51.717Z" }, { url = "https://files.pythonhosted.org/packages/30/ad/d17b5d42e28a8b91f8ed01cb949da092827afb9995d4559fd448d0472763/watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881", size = 87902, upload-time = "2024-11-01T14:06:53.119Z" }, { url = "https://files.pythonhosted.org/packages/5c/ca/c3649991d140ff6ab67bfc85ab42b165ead119c9e12211e08089d763ece5/watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11", size = 88380, upload-time = "2024-11-01T14:06:55.19Z" }, - { url = "https://files.pythonhosted.org/packages/5b/79/69f2b0e8d3f2afd462029031baafb1b75d11bb62703f0e1022b2e54d49ee/watchdog-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa", size = 87903, upload-time = "2024-11-01T14:06:57.052Z" }, - { url = "https://files.pythonhosted.org/packages/e2/2b/dc048dd71c2e5f0f7ebc04dd7912981ec45793a03c0dc462438e0591ba5d/watchdog-6.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e", size = 88381, upload-time = "2024-11-01T14:06:58.193Z" }, { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, @@ -5662,21 +3775,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9a/9a/c19c42c5b3f5a4aad748a6d5b4f23df3bed7ee5445accc65a0fb3ff03953/xxhash-3.6.0-cp314-cp314t-win32.whl", hash = "sha256:5851f033c3030dd95c086b4a36a2683c2ff4a799b23af60977188b057e467119", size = 31586, upload-time = "2025-10-02T14:36:15.603Z" }, { url = "https://files.pythonhosted.org/packages/03/d6/4cc450345be9924fd5dc8c590ceda1db5b43a0a889587b0ae81a95511360/xxhash-3.6.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0444e7967dac37569052d2409b00a8860c2135cff05502df4da80267d384849f", size = 32526, upload-time = "2025-10-02T14:36:16.708Z" }, { url = "https://files.pythonhosted.org/packages/0f/c9/7243eb3f9eaabd1a88a5a5acadf06df2d83b100c62684b7425c6a11bcaa8/xxhash-3.6.0-cp314-cp314t-win_arm64.whl", hash = "sha256:bb79b1e63f6fd84ec778a4b1916dfe0a7c3fdb986c06addd5db3a0d413819d95", size = 28898, upload-time = "2025-10-02T14:36:17.843Z" }, - { url = "https://files.pythonhosted.org/packages/03/ff/1b4bb3f397552116c1df6266c1b83a21aeeb26061ab1f462984b499a3870/xxhash-3.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cc604dc06027dbeb8281aeac5899c35fcfe7c77b25212833709f0bff4ce74d2a", size = 32844, upload-time = "2025-10-02T14:36:39.157Z" }, - { url = "https://files.pythonhosted.org/packages/c1/db/27146d0bee4346a9a31f7b498a81fc02747f6f1e6c52a2e7989504278051/xxhash-3.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:277175a73900ad43a8caeb8b99b9604f21fe8d7c842f2f9061a364a7e220ddb7", size = 30806, upload-time = "2025-10-02T14:36:40.621Z" }, - { url = "https://files.pythonhosted.org/packages/e7/2b/4896188df564908817a75de19bf7f2384b99a75af2d528f9c49326f76458/xxhash-3.6.0-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cfbc5b91397c8c2972fdac13fb3e4ed2f7f8ccac85cd2c644887557780a9b6e2", size = 193448, upload-time = "2025-10-02T14:36:41.797Z" }, - { url = "https://files.pythonhosted.org/packages/51/c5/be8953f62e772340319a826ce1e07489935600089756cf83b628cd36ebe3/xxhash-3.6.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2762bfff264c4e73c0e507274b40634ff465e025f0eaf050897e88ec8367575d", size = 212547, upload-time = "2025-10-02T14:36:43.581Z" }, - { url = "https://files.pythonhosted.org/packages/51/1a/1e9f0b911d1cf00dd537c074ae3fae15b535a7f0d9e7edd42a9d2c4f78ce/xxhash-3.6.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2f171a900d59d51511209f7476933c34a0c2c711078d3c80e74e0fe4f38680ec", size = 211309, upload-time = "2025-10-02T14:36:45.307Z" }, - { url = "https://files.pythonhosted.org/packages/63/88/b284c6a128d88dc47f201957f926e707db79fb7415a87072e15c0e490de0/xxhash-3.6.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:780b90c313348f030b811efc37b0fa1431163cb8db8064cf88a7936b6ce5f222", size = 444480, upload-time = "2025-10-02T14:36:47.226Z" }, - { url = "https://files.pythonhosted.org/packages/87/e4/798293a2bf9e4fac5f6d53ce59cba4739930778dfc6c7c73f40044ab0e6e/xxhash-3.6.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b242455eccdfcd1fa4134c431a30737d2b4f045770f8fe84356b3469d4b919", size = 192957, upload-time = "2025-10-02T14:36:48.968Z" }, - { url = "https://files.pythonhosted.org/packages/78/55/bfd0d7db447a927897469048b953caececa3532e743b940dd1f5c1032d24/xxhash-3.6.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a75ffc1bd5def584129774c158e108e5d768e10b75813f2b32650bb041066ed6", size = 209850, upload-time = "2025-10-02T14:36:50.258Z" }, - { url = "https://files.pythonhosted.org/packages/31/06/d08ef9a792bfebfd2fb2bcbf04a541ad283bef74749ead6f089a0809d288/xxhash-3.6.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1fc1ed882d1e8df932a66e2999429ba6cc4d5172914c904ab193381fba825360", size = 197342, upload-time = "2025-10-02T14:36:51.651Z" }, - { url = "https://files.pythonhosted.org/packages/7b/1a/aebf90797c94e9ca407c28e23f54d71f7149d91a93406a08a09e44d06994/xxhash-3.6.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:44e342e8cc11b4e79dae5c57f2fb6360c3c20cc57d32049af8f567f5b4bcb5f4", size = 209757, upload-time = "2025-10-02T14:36:53.009Z" }, - { url = "https://files.pythonhosted.org/packages/3c/80/799eec3d0a144dc3edf8c19b4f139c27fb923c50b34352796089ca206429/xxhash-3.6.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c2f9ccd5c4be370939a2e17602fbc49995299203da72a3429db013d44d590e86", size = 412773, upload-time = "2025-10-02T14:36:54.691Z" }, - { url = "https://files.pythonhosted.org/packages/6a/f9/09df7545699de09219a205123b8463ce9ea83f48acc7aeeba0269507f9d3/xxhash-3.6.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:02ea4cb627c76f48cd9fb37cf7ab22bd51e57e1b519807234b473faebe526796", size = 190357, upload-time = "2025-10-02T14:36:56.363Z" }, - { url = "https://files.pythonhosted.org/packages/07/40/2f8327f94e64a3f34d6ce3347c55207c322abbc80ae486ea45df4c62e7b3/xxhash-3.6.0-cp39-cp39-win32.whl", hash = "sha256:6551880383f0e6971dc23e512c9ccc986147ce7bfa1cd2e4b520b876c53e9f3d", size = 30585, upload-time = "2025-10-02T14:36:57.664Z" }, - { url = "https://files.pythonhosted.org/packages/6a/c8/2ecbc6799be9c02e8bf7b5a66cd94832b6ac13d59808746f0d402481c6ad/xxhash-3.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:7c35c4cdc65f2a29f34425c446f2f5cdcd0e3c34158931e1cc927ece925ab802", size = 31512, upload-time = "2025-10-02T14:36:58.837Z" }, - { url = "https://files.pythonhosted.org/packages/19/94/1d5459a9c587c94d7b8bcc710bd08bbfa145cbd814ebde41b48494362a21/xxhash-3.6.0-cp39-cp39-win_arm64.whl", hash = "sha256:ffc578717a347baf25be8397cb10d2528802d24f94cfc005c0e44fef44b5cdd6", size = 27878, upload-time = "2025-10-02T14:37:00.201Z" }, { url = "https://files.pythonhosted.org/packages/93/1e/8aec23647a34a249f62e2398c42955acd9b4c6ed5cf08cbea94dc46f78d2/xxhash-3.6.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0f7b7e2ec26c1666ad5fc9dbfa426a6a3367ceaf79db5dd76264659d509d73b0", size = 30662, upload-time = "2025-10-02T14:37:01.743Z" }, { url = "https://files.pythonhosted.org/packages/b8/0b/b14510b38ba91caf43006209db846a696ceea6a847a0c9ba0a5b1adc53d6/xxhash-3.6.0-pp311-pypy311_pp73-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5dc1e14d14fa0f5789ec29a7062004b5933964bb9b02aae6622b8f530dc40296", size = 41056, upload-time = "2025-10-02T14:37:02.879Z" }, { url = "https://files.pythonhosted.org/packages/50/55/15a7b8a56590e66ccd374bbfa3f9ffc45b810886c8c3b614e3f90bd2367c/xxhash-3.6.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:881b47fc47e051b37d94d13e7455131054b56749b91b508b0907eb07900d1c13", size = 36251, upload-time = "2025-10-02T14:37:04.44Z" }, @@ -5807,30 +3905,5 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/35/18/55e6011f7c044dc80b98893060773cefcfdbf60dfefb8cb2f58b9bacbd83/yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e", size = 89056, upload-time = "2025-10-06T14:12:13.317Z" }, { url = "https://files.pythonhosted.org/packages/f9/86/0f0dccb6e59a9e7f122c5afd43568b1d31b8ab7dda5f1b01fb5c7025c9a9/yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27", size = 96292, upload-time = "2025-10-06T14:12:15.398Z" }, { url = "https://files.pythonhosted.org/packages/48/b7/503c98092fb3b344a179579f55814b613c1fbb1c23b3ec14a7b008a66a6e/yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1", size = 85171, upload-time = "2025-10-06T14:12:16.935Z" }, - { url = "https://files.pythonhosted.org/packages/94/fd/6480106702a79bcceda5fd9c63cb19a04a6506bd5ce7fd8d9b63742f0021/yarl-1.22.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3aa27acb6de7a23785d81557577491f6c38a5209a254d1191519d07d8fe51748", size = 141301, upload-time = "2025-10-06T14:12:19.01Z" }, - { url = "https://files.pythonhosted.org/packages/42/e1/6d95d21b17a93e793e4ec420a925fe1f6a9342338ca7a563ed21129c0990/yarl-1.22.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:af74f05666a5e531289cb1cc9c883d1de2088b8e5b4de48004e5ca8a830ac859", size = 93864, upload-time = "2025-10-06T14:12:21.05Z" }, - { url = "https://files.pythonhosted.org/packages/32/58/b8055273c203968e89808413ea4c984988b6649baabf10f4522e67c22d2f/yarl-1.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:62441e55958977b8167b2709c164c91a6363e25da322d87ae6dd9c6019ceecf9", size = 94706, upload-time = "2025-10-06T14:12:23.287Z" }, - { url = "https://files.pythonhosted.org/packages/18/91/d7bfbc28a88c2895ecd0da6a874def0c147de78afc52c773c28e1aa233a3/yarl-1.22.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b580e71cac3f8113d3135888770903eaf2f507e9421e5697d6ee6d8cd1c7f054", size = 347100, upload-time = "2025-10-06T14:12:28.527Z" }, - { url = "https://files.pythonhosted.org/packages/bd/e8/37a1e7b99721c0564b1fc7b0a4d1f595ef6fb8060d82ca61775b644185f7/yarl-1.22.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e81fda2fb4a07eda1a2252b216aa0df23ebcd4d584894e9612e80999a78fd95b", size = 318902, upload-time = "2025-10-06T14:12:30.528Z" }, - { url = "https://files.pythonhosted.org/packages/1c/ef/34724449d7ef2db4f22df644f2dac0b8a275d20f585e526937b3ae47b02d/yarl-1.22.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:99b6fc1d55782461b78221e95fc357b47ad98b041e8e20f47c1411d0aacddc60", size = 363302, upload-time = "2025-10-06T14:12:32.295Z" }, - { url = "https://files.pythonhosted.org/packages/8a/04/88a39a5dad39889f192cce8d66cc4c58dbeca983e83f9b6bf23822a7ed91/yarl-1.22.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:088e4e08f033db4be2ccd1f34cf29fe994772fb54cfe004bbf54db320af56890", size = 370816, upload-time = "2025-10-06T14:12:34.01Z" }, - { url = "https://files.pythonhosted.org/packages/6b/1f/5e895e547129413f56c76be2c3ce4b96c797d2d0ff3e16a817d9269b12e6/yarl-1.22.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e4e1f6f0b4da23e61188676e3ed027ef0baa833a2e633c29ff8530800edccba", size = 346465, upload-time = "2025-10-06T14:12:35.977Z" }, - { url = "https://files.pythonhosted.org/packages/11/13/a750e9fd6f9cc9ed3a52a70fe58ffe505322f0efe0d48e1fd9ffe53281f5/yarl-1.22.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:84fc3ec96fce86ce5aa305eb4aa9358279d1aa644b71fab7b8ed33fe3ba1a7ca", size = 341506, upload-time = "2025-10-06T14:12:37.788Z" }, - { url = "https://files.pythonhosted.org/packages/3c/67/bb6024de76e7186611ebe626aec5b71a2d2ecf9453e795f2dbd80614784c/yarl-1.22.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5dbeefd6ca588b33576a01b0ad58aa934bc1b41ef89dee505bf2932b22ddffba", size = 335030, upload-time = "2025-10-06T14:12:39.775Z" }, - { url = "https://files.pythonhosted.org/packages/a2/be/50b38447fd94a7992996a62b8b463d0579323fcfc08c61bdba949eef8a5d/yarl-1.22.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14291620375b1060613f4aab9ebf21850058b6b1b438f386cc814813d901c60b", size = 358560, upload-time = "2025-10-06T14:12:41.547Z" }, - { url = "https://files.pythonhosted.org/packages/e2/89/c020b6f547578c4e3dbb6335bf918f26e2f34ad0d1e515d72fd33ac0c635/yarl-1.22.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a4fcfc8eb2c34148c118dfa02e6427ca278bfd0f3df7c5f99e33d2c0e81eae3e", size = 357290, upload-time = "2025-10-06T14:12:43.861Z" }, - { url = "https://files.pythonhosted.org/packages/8c/52/c49a619ee35a402fa3a7019a4fa8d26878fec0d1243f6968bbf516789578/yarl-1.22.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:029866bde8d7b0878b9c160e72305bbf0a7342bcd20b9999381704ae03308dc8", size = 350700, upload-time = "2025-10-06T14:12:46.868Z" }, - { url = "https://files.pythonhosted.org/packages/ab/c9/f5042d87777bf6968435f04a2bbb15466b2f142e6e47fa4f34d1a3f32f0c/yarl-1.22.0-cp39-cp39-win32.whl", hash = "sha256:4dcc74149ccc8bba31ce1944acee24813e93cfdee2acda3c172df844948ddf7b", size = 82323, upload-time = "2025-10-06T14:12:48.633Z" }, - { url = "https://files.pythonhosted.org/packages/fd/58/d00f7cad9eba20c4eefac2682f34661d1d1b3a942fc0092eb60e78cfb733/yarl-1.22.0-cp39-cp39-win_amd64.whl", hash = "sha256:10619d9fdee46d20edc49d3479e2f8269d0779f1b031e6f7c2aa1c76be04b7ed", size = 87145, upload-time = "2025-10-06T14:12:50.241Z" }, - { url = "https://files.pythonhosted.org/packages/c2/a3/70904f365080780d38b919edd42d224b8c4ce224a86950d2eaa2a24366ad/yarl-1.22.0-cp39-cp39-win_arm64.whl", hash = "sha256:dd7afd3f8b0bfb4e0d9fc3c31bfe8a4ec7debe124cfd90619305def3c8ca8cd2", size = 82173, upload-time = "2025-10-06T14:12:51.869Z" }, { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, ] - -[[package]] -name = "zipp" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, -]