From 96994eca6e87f54ec28b5e6a202a55dad54fbd6e Mon Sep 17 00:00:00 2001 From: Kinvert Date: Thu, 4 Jun 2026 13:56:31 -0700 Subject: [PATCH 01/16] Add Pathfinder environment --- AGENTS.md | 216 +++ PATHFINDER_SPEC.md | 301 ++++ build.sh | 20 +- config/pathfinder.ini | 81 + .../plans/2026-06-04-pathfinder-env.md | 178 ++ ocean/pathfinder/binding.c | 32 + ocean/pathfinder/pathfinder.c | 37 + ocean/pathfinder/pathfinder.h | 427 +++++ ocean/pathfinder/tests/run_all.sh | 14 + ocean/pathfinder/tests/test_pathfinder_core.c | 166 ++ uv.lock | 1456 +++++++++++++++++ 11 files changed, 2926 insertions(+), 2 deletions(-) create mode 100644 AGENTS.md create mode 100644 PATHFINDER_SPEC.md create mode 100644 config/pathfinder.ini create mode 100644 docs/superpowers/plans/2026-06-04-pathfinder-env.md create mode 100644 ocean/pathfinder/binding.c create mode 100644 ocean/pathfinder/pathfinder.c create mode 100644 ocean/pathfinder/pathfinder.h create mode 100644 ocean/pathfinder/tests/run_all.sh create mode 100644 ocean/pathfinder/tests/test_pathfinder_core.c create mode 100644 uv.lock diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..c5c21be01d --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,216 @@ +# AGENTS.md + +Guidance for autonomous agents working on the Pathfinder PufferLib env. + +## Current Workspace + +This machine is `g240`. + +Writable working repo: + +- `/home/claude/pathfinder` +- Branch: `pathfinder` +- Base: official PufferLib `5.0` +- Remote: `origin https://github.com/PufferAI/PufferLib.git` + +This workspace is for a new PufferLib 5 Ocean environment named +`pathfinder`, based on Milton Bradley's 1977 Pathfinder board game. + +Reference repos: + +- `/home/claude/dogfight5` + - PufferLib 5 Dogfight port workspace. + - Use as the local reference for uv setup, CUDA 12.8 activation, native build + behavior, and PufferLib 5 state-memory conventions. + - Do not modify it unless the human explicitly redirects work there. +- `/home/claude/PufferLib` + - Dirty Dogfight 4 control workspace. + - Do not use as the implementation target for Pathfinder. + +## Goal + +Build a single-agent Pathfinder maze-solving environment first. + +The env should generate a hidden 6x6 barricade maze, hide a pawn, start the +agent at `A1`, and train the agent to discover walls and find a path to the +hidden pawn using only four movement actions. + +The current design source is: + +- `/home/claude/pathfinder/PATHFINDER_SPEC.md` + +Update that spec deliberately when requirements change. Do not let +implementation drift away from it silently. + +## Rule Source + +The board-game rules were read from: + +- User-provided rules scan: + `http://www.transformertoys.co.uk/images/instruction-scans/hasbro/Pathfinder.pdf` +- User-provided video transcript. + +Important game facts: + +- The board is 6x6 with rows `A-F` and columns `1-6`. +- Barriers live in slots around and between squares. +- A hidden short pawn sits on one square. +- There must be at least one route from a column-1 entrance to the hidden pawn. +- Movement is orthogonal only. +- Asking for a blocked edge reveals a wall and leaves the pawn in place. +- Asking for an open edge moves the pawn. +- Moving into the hidden pawn square wins. + +For v1, simplify the board-game entry/re-entry rule: + +- The agent always starts at `A1`. +- The generator must guarantee a route from `A1` to the hidden pawn. +- Action space is four moves: north, east, south, west. + +## Safety Rules + +- Do implementation work only in `/home/claude/pathfinder`. +- Keep Pathfinder implementation under `ocean/pathfinder/` whenever possible. +- Allowed repo-level additions for this env: + - `config/pathfinder.ini` + - root planning docs such as `PATHFINDER_SPEC.md` and `AGENTS.md` + - minimal build/integration fixes already needed for this PufferLib 5 branch. +- Do not modify `/home/claude/dogfight5`, `/home/claude/PufferLib`, + `/home/claude/dogfight3`, or `/home/claude/dogfight4`. +- Avoid destructive Git operations. Do not run `git reset --hard`, mass + deletes, or history rewrites unless the human explicitly asks. +- Do not add CPU fallback work unless explicitly requested. Native CUDA is the + expected path on this machine. + +## Environment Setup + +This repo uses `uv` and an editable install: + +```bash +cd /home/claude/pathfinder +uv venv --python 3.12 .venv +uv sync +uv pip install -e . +source .venv/bin/activate +python --version +``` + +The current venv is verified with: + +- Python `3.12.3` +- Torch `2.10.0+cu128` +- CUDA runtime `12.8` +- `CUDA_HOME=/usr/local/cuda-12.8` +- `CC=clang` + +The venv activation script has the same CUDA 12.8 hook pattern as +`dogfight5`: + +```bash +source .venv/bin/activate +echo "$CUDA_HOME" +which nvcc +``` + +Expected: + +```text +/usr/local/cuda-12.8 +/usr/local/cuda-12.8/bin/nvcc +``` + +The Codex sandbox may not expose GPU devices. That can make PyTorch report +`torch.cuda.is_available() == False` and make `nvcc -arch=native` warn that no +valid GPU is visible. Native extension builds can still compile in the sandbox. +GPU training/sweeps may need to run outside the sandbox. + +## Verified Build Baseline + +The fresh workspace has already been prepared with: + +- `uv sync` +- `uv pip install -e .` +- local `raylib-5.5_linux_amd64` copied from `/home/claude/dogfight5` +- the `dogfight5` `build.sh` fix that links versioned cuDNN/NCCL wheel + libraries correctly. + +Verified native CUDA extension builds: + +```bash +source .venv/bin/activate +./build.sh g2048 +./build.sh breakout +``` + +Both produced: + +```text +pufferlib/_C.cpython-312-x86_64-linux-gnu.so +``` + +Do not use `./build.sh ENV --cpu` as the default smoke path for Pathfinder. + +## Implementation Target + +Expected files: + +```text +ocean/pathfinder/pathfinder.h +ocean/pathfinder/pathfinder.c +ocean/pathfinder/binding.c +ocean/pathfinder/tests/ +config/pathfinder.ini +``` + +Follow PufferLib 5 state-aware env patterns from: + +```text +ocean/g2048/ +ocean/boxoban/ +ocean/craftax/ +``` + +`ocean/maze/` is useful for navigation ideas but should not be copied blindly; +it is not the best state-memory reference on this branch. + +## Current Design Defaults + +- Fixed board size: 6x6. +- Spawn: `A1`. +- Hidden pawn: random non-`A1` cell, and generated maze must connect `A1` to + it. +- Actions: four discrete moves. +- Observation: + - 84 wall slots as floats: + - `-1.0` unobserved + - `0.0` observed open + - `1.0` observed wall + - normalized current x + - normalized current y + - total `OBS_SIZE = 86` +- First implementation should keep `train.state_buffer_size = 0` until + deterministic state roundtrip tests exist. + +## Test Expectations + +Use test-driven development for Pathfinder behavior: + +1. Add focused tests for wall indexing, maze solvability, reset observation, + movement/wall reveal semantics, and terminal success. +2. Make the narrow tests pass. +3. Build native: + +```bash +source .venv/bin/activate +./build.sh pathfinder +``` + +4. Run a short training smoke only after build and behavior tests pass: + +```bash +source .venv/bin/activate +python -m pufferlib.pufferl train pathfinder --train.total-timesteps 2097152 +``` + +If tests require helper binaries, put them under `ocean/pathfinder/tests/` and +keep them scoped to Pathfinder. diff --git a/PATHFINDER_SPEC.md b/PATHFINDER_SPEC.md new file mode 100644 index 0000000000..47a2c4b7c9 --- /dev/null +++ b/PATHFINDER_SPEC.md @@ -0,0 +1,301 @@ +# Pathfinder Environment Spec + +Status: draft, ready for user review + +Workspace: `/home/claude/pathfinder` + +Target env name: `pathfinder` + +## Intent + +Build a PufferLib 5 Ocean environment inspired by Milton Bradley's 1977 +Pathfinder board game. The first version is a single-agent maze-solving task: +the environment generates a hidden, traversible barricade layout and a hidden +pawn, and the agent learns to discover walls while finding a path to the pawn. + +The full two-player race is intentionally out of scope for the first version. +The board-game rules remain the source model for the grid, hidden pawn, +barriers, entry from column 1, adjacent movement, wall discovery, and terminal +goal detection. + +## Rule Summary + +Rules source: + +- Official scanned rules PDF supplied by the user: + `http://www.transformertoys.co.uk/images/instruction-scans/hasbro/Pathfinder.pdf` +- User-supplied video transcript describing play. + +Board-game facts that matter for the env: + +- The board is a 6x6 grid with rows `A-F` and columns `1-6`. +- Each player hides a short pawn on any square of their home grid. +- Each player secretly places barrier chips in slots around and between grid + squares. +- The barricades must leave at least one route from a column-1 entrance square + to the hidden pawn. +- Some column-1 entrances may be blocked. Some open column-1 entrances may be + dead ends. +- A searching player starts by asking whether a column-1 square can be entered. +- After entering, the player asks to move to adjacent orthogonal squares. + Diagonal movement is not allowed. +- If the requested edge has no barrier, the tall pawn moves and the turn + continues. If the requested edge has a barrier, the pawn stays put, the wall + is marked on the tracking grid, and turn control would pass in the board game. +- Backtracking is legal and uses the same adjacent-square calls. +- If an unblocked requested square contains the hidden pawn, the searcher wins. + +The single-agent env treats every action as one RL step. It does not model +board-game turn passing or the pre-entry choice, because there is no opponent +policy in the first version. + +## Board Model + +Constants: + +- `PF_ROWS = 6` +- `PF_COLS = 6` +- Cells use zero-based `(row, col)` internally. +- Row names map `A-F -> 0-5`. +- Column names map `1-6 -> 0-5`. + +Wall slots are represented as graph edges: + +- Vertical slots: `V[row][edge_col]`, shape `6 x 7`. + - `edge_col = 0` is the left entry/exit edge for column 1. + - `edge_col = 1..5` are internal east/west edges. + - `edge_col = 6` is the right board boundary. +- Horizontal slots: `H[edge_row][col]`, shape `7 x 6`. + - `edge_row = 0` is the top board boundary. + - `edge_row = 1..5` are internal north/south edges. + - `edge_row = 6` is the bottom board boundary. + +Total wall observation slots: `6*7 + 7*6 = 84`. + +Boundary behavior: + +- Left boundary edges are real Pathfinder entry/exit edges and may be open or + blocked by maze generation. +- Top, right, and bottom boundary edges are hard board boundaries. Their truth + value is `1` and the agent can discover them by attempting to move off-board. + +## Maze Generation + +The generator must produce legal, solvable layouts without hand-authored maps. + +Episode generation: + +1. Pick a hidden pawn cell. Default: uniform over all non-`A1` cells. +2. Initialize all wall slots to blocked. +3. Open the left entry edge for `A1`. +4. Carve at least one orthogonal path from `A1` to the hidden pawn. +5. Store `A1` as the agent spawn for the episode. +6. Add false branches and optional loops without breaking the solved-path + invariant. +7. Optionally open additional column-1 entries. These can connect to useful + routes or dead-end branches. +8. Validate with BFS that `A1` reaches the pawn. + +Default generator style: + +- Use a randomized depth-first or randomized Prim-style graph carver over the + 6x6 cell graph. +- Preserve at least one path from `A1` to the pawn. +- Add branch density as a difficulty knob rather than trying to exactly copy + human barricade layouts. + +Config knobs: + +- `branch_prob`: probability of adding false branches from the main route. +- `loop_prob`: probability of opening extra internal edges after carving. +- `extra_entry_prob`: probability each non-`A1` column-1 entrance is open. +- `min_solution_len`: reject mazes with a shortest solution path below this. +- `max_steps`: timeout. +- `seed`: inherited from vector env config. + +The first implementation should keep board size fixed at 6x6. Larger synthetic +boards can be a later variant after the baseline trains. + +## Action Space + +Use one categorical action head. + +Action count: `4` + +- `0`: move north. +- `1`: move east. +- `2`: move south. +- `3`: move west. + +Action semantics: + +- Reset starts the agent at `A1`. +- Cardinal moves test the corresponding wall slot. +- If the tested slot is blocked, reveal it and keep the same position. +- If the tested slot is open, reveal it and move to the destination cell. +- Moving west from column 1 through an open left edge is treated as an exit + attempt and leaves the agent in place in v1. This keeps action space simple + and avoids an outside-grid state. Full retreat/re-entry can be added later. +- Attempts to move through top, right, or bottom board boundaries reveal a + blocked boundary slot and keep the agent in place. + +## Observation Space + +Use a float observation tensor. + +Observation layout: + +1. `wall_obs[84]` + - `-1.0`: unobserved wall slot. + - `0.0`: observed open slot. + - `1.0`: observed wall slot. +2. `pos_x` + - Current column normalized to `[0, 1]`. +3. `pos_y` + - Current row normalized to `[0, 1]`. + +Observation size: `86`. + +The hidden pawn location is not directly observed. It is only discovered when +the agent enters or moves into that square through an open edge. + +## Rewards and Termination + +Default reward model: + +- `+1.0` for reaching the hidden pawn. +- `-0.001` per step. +- `-0.01` for hitting a newly discovered wall. +- `-0.03` for hitting a wall that was already known. +- `-0.02` for impossible movement, such as attempting to exit through the + left edge in v1. +- `0.0` for a successful nonterminal move, except for the step penalty. + +Termination: + +- Success: agent reaches the hidden pawn. +- Timeout: `tick >= max_steps`. + +Logged metrics: + +- `perf`: `1.0` on success, `0.0` on timeout. +- `score`: success reward adjusted by path efficiency. +- `episode_return` +- `episode_length` +- `success` +- `wall_hits` +- `known_walls` +- `known_open_edges` +- `shortest_path_len` +- `agent_path_len` +- `n` + +## PufferLib Integration + +Expected files: + +- `ocean/pathfinder/pathfinder.h` +- `ocean/pathfinder/pathfinder.c` +- `ocean/pathfinder/binding.c` +- `ocean/pathfinder/tests/` +- `config/pathfinder.ini` + +The env should follow current PufferLib 5 Ocean patterns: + +- Define `Log`. +- Define `State`. +- Keep rollout-local future-affecting fields inside `State`. +- Define env struct `Pathfinder` with required pointers: + - `float* observations` + - `float* actions` + - `float* rewards` + - `float* terminals` + - `int num_agents` + - `Log log` + - `State state` + - `unsigned int rng` +- In `binding.c`, set: + - `OBS_SIZE 86` + - `NUM_ATNS 1` + - `ACT_SIZES {4}` + - `OBS_TENSOR_T FloatTensor` + - `Env Pathfinder` + - `puffer_state_refresh(Pathfinder* env)` to rebuild observations from + restored state. +- Keep `state_buffer_size = 0` initially unless state-memory tests exist. + +`g2048`, `boxoban`, and `craftax` are the nearest 5.0 state-aware references. +`maze` is useful for navigation ideas but is not state-memory complete on this +branch. + +## Testing + +Add focused tests before training: + +- Wall index mapping: + - Every cardinal move maps to the expected `V` or `H` slot. +- Observation initialization: + - All wall observations start at `-1.0`. + - Position is `A1` after reset. +- Maze legality: + - Every generated maze has at least one open column-1 entrance. + - BFS from open column-1 entrances reaches the hidden pawn. + - `A1` is connected to the hidden pawn. + - Generated truth walls never allow top, right, or bottom off-board movement. +- Step semantics: + - Open edge reveals `0.0` and moves. + - Closed edge reveals `1.0` and does not move. + - Repeated known wall hit keeps the same position. + - West move from column 1 through an open left edge reveals the edge but does + not move in v1. + - Reaching the hidden pawn sets terminal and success log. +- State roundtrip: + - Save state after partial exploration. + - Restore into a fresh env. + - Verify observations and scripted future steps match. + +Verification commands after implementation: + +```bash +source .venv/bin/activate +./build.sh pathfinder +python -m pufferlib.pufferl train pathfinder --train.total-timesteps 2097152 +``` + +Native GPU builds are the expected path for this workspace. + +## Initial Training Config + +Start conservative: + +- `vec.total_agents = 8192` or `16384` +- `vec.num_buffers = 2` +- `train.gpus = 1` +- `train.total_timesteps = 100M` for real runs, at least `2,097,152` for smoke + tests with the initial `8192 x 128` rollout geometry. +- `train.horizon = 128` +- `train.minibatch_size = 32768` +- `train.learning_rate = 0.001` +- `train.gamma = 0.995` +- `train.use_rnn = 1` +- `train.state_buffer_size = 0` +- `train.cl_frac = 0` + +Reasoning: the task is partially observable and memory-dependent. Recurrent +policy support is likely useful because the agent must remember discovered +walls and path context, even though the observation also contains discovered +wall memory. + +## Later Extensions + +After the single-agent solver works: + +- Add curriculum over branch density, loop density, and solution length. +- Add larger synthetic boards while retaining a 6x6 rules mode. +- Add direct coordinate-call action variants for closer board-game fidelity. +- Add explicit outside-grid entry/re-entry actions for closer board-game + fidelity. +- Add two-player self-play where each side generates or chooses a maze and + races to find the opposing pawn. +- Add render mode showing true hidden maze, observed tracking grid, current + pawn, and discovered path. diff --git a/build.sh b/build.sh index 99a898fc6f..101891644f 100755 --- a/build.sh +++ b/build.sh @@ -252,6 +252,22 @@ for lib_flag in "$CUDNN_LFLAG" "$NCCL_LFLAG"; do fi done +resolve_lib_arg() { + local lib_flag=$1 lib_name=$2 + local default_arg="-l$lib_name" + [[ "$lib_flag" == -L* ]] || { echo "$default_arg"; return; } + local lib_dir="${lib_flag#-L}" + [ -f "$lib_dir/lib$lib_name.so" ] && { echo "$default_arg"; return; } + local candidate + for candidate in "$lib_dir/lib$lib_name.so".*; do + [ -f "$candidate" ] && { echo "$candidate"; return; } + done + echo "$default_arg" +} + +CUDNN_LIB_ARG="$(resolve_lib_arg "$CUDNN_LFLAG" cudnn)" +NCCL_LIB_ARG="$(resolve_lib_arg "$NCCL_LFLAG" nccl)" + export CCACHE_DIR="${CCACHE_DIR:-$HOME/.ccache}" export CCACHE_BASEDIR="$(pwd)" export CCACHE_COMPILERCHECK=content @@ -312,7 +328,7 @@ if [ -z "$MODE" ]; then build/bindings.o "$RAYLIB_A" -L$CUDA_HOME/lib64 $CUDNN_LFLAG $NCCL_LFLAG "${WHEEL_RPATH_FLAGS[@]}" - -lcudart -lnccl -lnvidia-ml -lcublas -lcusolver -lcurand -lcudnn + -lcudart "$NCCL_LIB_ARG" -lnvidia-ml -lcublas -lcusolver -lcurand "$CUDNN_LIB_ARG" $OMP_LIB $LINK_OPT "${SHARED_LDFLAGS[@]}" -o "$OUTPUT" @@ -355,7 +371,7 @@ elif [ "$MODE" = "profile" ]; then -Xcompiler=-fopenmp \ tests/profile_kernels.cu vendor/ini.c \ "$RAYLIB_A" \ - -lnccl -lnvidia-ml -lcublas -lcurand -lcudnn \ + "$NCCL_LIB_ARG" -lnvidia-ml -lcublas -lcurand "$CUDNN_LIB_ARG" \ -lGL -lm -lpthread $OMP_LIB \ -o profile echo "Built: ./profile" diff --git a/config/pathfinder.ini b/config/pathfinder.ini new file mode 100644 index 0000000000..8d55d86a47 --- /dev/null +++ b/config/pathfinder.ini @@ -0,0 +1,81 @@ +[base] +env_name = pathfinder + +[vec] +total_agents = 8192 +num_buffers = 2 +num_threads = 0 + +[env] +branch_prob = 0.35 +loop_prob = 0.10 +extra_entry_prob = 0.0 +min_solution_len = 1 +max_steps = 128 + +[policy] +hidden_size = 512 +num_layers = 2 +expansion_factor = 1 + +[train] +gpus = 1 +seed = 42 +total_timesteps = 100000000 +learning_rate = 0.001 +anneal_lr = 1 +min_lr_ratio = 0 +gamma = 0.995 +gae_lambda = 0.95 +replay_ratio = 1 +clip_coef = 0.1 +vf_coef = 0.5 +vf_clip_coef = 5 +max_grad_norm = 1.0 +ent_coef = 0.005 +anneal_ent_coef = 0 +min_ent_coef_ratio = 0.1 +beta1 = 0.9 +beta2 = 0.999 +eps = 1e-8 +minibatch_size = 32768 +horizon = 128 +vtrace_rho_clip = 1 +vtrace_c_clip = 1 +prio_alpha = 0 +prio_beta0 = 0 +anneal_prio_beta = 0 +state_buffer_size = 0 +cl_frac = 0 +anneal_cl = 0 +warmup_states = 100 +state_checkpoint_interval = 16 +explore_alpha = 1 +explore_beta = 0 +explore_decay = 0.99 +use_rnn = 1 +env = 0 + +[sweep] +max_suggestion_cost = 7200 + +[sweep.env.branch_prob] +distribution = uniform +min = 0.05 +mean = 0.35 +max = 0.75 +scale = auto + +[sweep.env.loop_prob] +distribution = uniform +min = 0.0 +mean = 0.1 +max = 0.5 +scale = auto + +[sweep.train.learning_rate] +distribution = log_normal +min = 0.0001 +mean = 0.001 +max = 0.005 +scale = auto diff --git a/docs/superpowers/plans/2026-06-04-pathfinder-env.md b/docs/superpowers/plans/2026-06-04-pathfinder-env.md new file mode 100644 index 0000000000..0e149a638b --- /dev/null +++ b/docs/superpowers/plans/2026-06-04-pathfinder-env.md @@ -0,0 +1,178 @@ +# Pathfinder Environment Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Build the first PufferLib 5 `pathfinder` Ocean environment: a generated 6x6 hidden-wall maze, fixed `A1` spawn, hidden pawn target, four movement actions, and wall-memory observations. + +**Architecture:** Implement the environment as a compact C env under `ocean/pathfinder/`, following `g2048` for state ownership and `boxoban`/`maze` for navigation style. Put deterministic helpers in `pathfinder.h` so focused C tests can include them directly before the PufferLib CUDA binding is built. + +**Tech Stack:** C, PufferLib 5 Ocean static vec binding, `uv` editable install, native CUDA build via `./build.sh pathfinder`. + +--- + +### Task 1: Core Pathfinder Model and Tests + +**Files:** +- Create: `ocean/pathfinder/pathfinder.h` +- Create: `ocean/pathfinder/tests/test_pathfinder_core.c` +- Create: `ocean/pathfinder/tests/run_all.sh` + +- [ ] **Step 1: Write the failing core test** + +Create `ocean/pathfinder/tests/test_pathfinder_core.c` with tests that include `../pathfinder.h` and check: + +- `PATHFINDER_OBS_SIZE == 86` +- `PATHFINDER_NUM_WALLS == 84` +- reset starts at row `0`, col `0` +- all wall observations start at `-1.0f` +- generated mazes connect `A1` to the hidden pawn +- moving through an open edge reveals `0.0f` and moves +- moving into a blocked edge reveals `1.0f` and does not move + +- [ ] **Step 2: Run the core test to verify RED** + +Run: + +```bash +cd /home/claude/pathfinder +bash ocean/pathfinder/tests/run_all.sh +``` + +Expected: FAIL because `ocean/pathfinder/pathfinder.h` does not exist yet. + +- [ ] **Step 3: Implement minimal core model** + +Create `ocean/pathfinder/pathfinder.h` with: + +- constants for 6x6 cells, 84 wall slots, 86 obs size, 4 actions +- `Log`, `State`, and `Pathfinder` structs +- wall indexing helpers for vertical and horizontal slots +- deterministic `pathfinder_generate_maze` +- `pathfinder_update_observations` +- `pathfinder_reset` +- `pathfinder_step` +- `refresh_state`, `init`, `c_reset`, `c_step`, `c_close` + +- [ ] **Step 4: Run core test to verify GREEN** + +Run: + +```bash +cd /home/claude/pathfinder +bash ocean/pathfinder/tests/run_all.sh +``` + +Expected: PASS for core model tests. + +### Task 2: PufferLib Binding and Config + +**Files:** +- Create: `ocean/pathfinder/binding.c` +- Create: `ocean/pathfinder/pathfinder.c` +- Create: `config/pathfinder.ini` + +- [ ] **Step 1: Write the failing build expectation** + +Run: + +```bash +cd /home/claude/pathfinder +source .venv/bin/activate +./build.sh pathfinder +``` + +Expected: FAIL because `ocean/pathfinder/binding.c` does not exist. + +- [ ] **Step 2: Add binding** + +Create `ocean/pathfinder/binding.c` using the `g2048`/`boxoban` pattern: + +- `#include "pathfinder.h"` +- `#define OBS_SIZE PATHFINDER_OBS_SIZE` +- `#define NUM_ATNS 1` +- `#define ACT_SIZES {4}` +- `#define OBS_TENSOR_T PrecisionTensor` +- `#define Env Pathfinder` +- `static inline void puffer_state_refresh(Pathfinder* env) { refresh_state(env); }` +- `#include "vecenv.h"` +- `my_init` reads `branch_prob`, `loop_prob`, `extra_entry_prob`, `min_solution_len`, and `max_steps` +- `my_log` exports `perf`, `score`, `episode_return`, `episode_length`, `success`, `wall_hits`, `known_walls`, `known_open_edges`, `shortest_path_len`, `agent_path_len` + +- [ ] **Step 3: Add local demo/standalone file** + +Create `ocean/pathfinder/pathfinder.c` as a small random-action local runner matching the simple env style used by `g2048.c` and `boxoban.c`. + +- [ ] **Step 4: Add config** + +Create `config/pathfinder.ini` with: + +- `[base] env_name = pathfinder` +- `[vec] total_agents = 8192`, `num_buffers = 2`, `num_threads = 0` +- `[env] branch_prob`, `loop_prob`, `extra_entry_prob`, `min_solution_len`, `max_steps` +- `[policy] hidden_size = 512`, `num_layers = 2`, `expansion_factor = 1` +- `[train] gpus = 1`, `total_timesteps = 100000000`, `horizon = 128`, `minibatch_size = 32768`, `learning_rate = 0.001`, `gamma = 0.995`, `use_rnn = 1`, `state_buffer_size = 0`, `cl_frac = 0` + +- [ ] **Step 5: Run native build to verify GREEN** + +Run: + +```bash +cd /home/claude/pathfinder +source .venv/bin/activate +./build.sh pathfinder +``` + +Expected: PASS and build `pufferlib/_C.cpython-312-x86_64-linux-gnu.so`. + +### Task 3: Training Smoke and Documentation Check + +**Files:** +- Modify: `PATHFINDER_SPEC.md` only if implementation exposes a deliberate spec correction. +- Modify: `AGENTS.md` only if commands or defaults changed. + +- [ ] **Step 1: Run core tests** + +Run: + +```bash +cd /home/claude/pathfinder +bash ocean/pathfinder/tests/run_all.sh +``` + +Expected: PASS. + +- [ ] **Step 2: Run native build** + +Run: + +```bash +cd /home/claude/pathfinder +source .venv/bin/activate +./build.sh pathfinder +``` + +Expected: PASS. + +- [ ] **Step 3: Run short train smoke if GPU access permits** + +Run: + +```bash +cd /home/claude/pathfinder +source .venv/bin/activate +python -m pufferlib.pufferl train pathfinder --train.total-timesteps 2097152 +``` + +Expected: The run starts, validates the compiled env name, and completes at least one rollout/update. If sandbox GPU access blocks training, record the exact failure and do not treat it as an env logic failure. + +- [ ] **Step 4: Review docs and status** + +Run: + +```bash +cd /home/claude/pathfinder +rg -n "Action count|Observation size|OBS_SIZE|ACT_SIZES|A1|state_buffer_size" PATHFINDER_SPEC.md AGENTS.md +git status --short --branch +``` + +Expected: docs still say 4 actions, `OBS_SIZE = 86`, fixed `A1` spawn, and initial state memory off. diff --git a/ocean/pathfinder/binding.c b/ocean/pathfinder/binding.c new file mode 100644 index 0000000000..bf651fdfc3 --- /dev/null +++ b/ocean/pathfinder/binding.c @@ -0,0 +1,32 @@ +#include "pathfinder.h" +#define OBS_SIZE PATHFINDER_OBS_SIZE +#define NUM_ATNS 1 +#define ACT_SIZES {4} +#define OBS_TENSOR_T FloatTensor + +#define Env Pathfinder +static inline void puffer_state_refresh(Pathfinder* env) { refresh_state(env); } +#include "vecenv.h" + +void my_init(Env* env, Dict* kwargs) { + env->num_agents = 1; + env->branch_prob = (float)dict_get(kwargs, "branch_prob")->value; + env->loop_prob = (float)dict_get(kwargs, "loop_prob")->value; + env->extra_entry_prob = (float)dict_get(kwargs, "extra_entry_prob")->value; + env->min_solution_len = (int)dict_get(kwargs, "min_solution_len")->value; + env->max_steps = (int)dict_get(kwargs, "max_steps")->value; + init(env); +} + +void my_log(Log* log, Dict* out) { + dict_set(out, "perf", log->perf); + dict_set(out, "score", log->score); + dict_set(out, "episode_return", log->episode_return); + dict_set(out, "episode_length", log->episode_length); + dict_set(out, "success", log->success); + dict_set(out, "wall_hits", log->wall_hits); + dict_set(out, "known_walls", log->known_walls); + dict_set(out, "known_open_edges", log->known_open_edges); + dict_set(out, "shortest_path_len", log->shortest_path_len); + dict_set(out, "agent_path_len", log->agent_path_len); +} diff --git a/ocean/pathfinder/pathfinder.c b/ocean/pathfinder/pathfinder.c new file mode 100644 index 0000000000..fe7993c3a8 --- /dev/null +++ b/ocean/pathfinder/pathfinder.c @@ -0,0 +1,37 @@ +#include +#include "pathfinder.h" + +int main(void) { + Pathfinder env; + memset(&env, 0, sizeof(env)); + + float observations[PATHFINDER_OBS_SIZE] = {0}; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + + env.observations = observations; + env.actions = actions; + env.rewards = rewards; + env.terminals = terminals; + env.num_agents = 1; + env.rng = (unsigned int)time(NULL); + env.branch_prob = 0.35f; + env.loop_prob = 0.10f; + env.extra_entry_prob = 0.0f; + env.min_solution_len = 1; + env.max_steps = 128; + + init(&env); + c_reset(&env); + + for (int i = 0; i < 256; i++) { + actions[0] = (float)(pathfinder_rand(&env) % PATHFINDER_NUM_ACTIONS); + c_step(&env); + } + + printf("Pathfinder random smoke: episodes=%0.0f success=%0.3f return=%0.3f\n", + env.log.n, env.log.success, env.log.episode_return); + c_close(&env); + return 0; +} diff --git a/ocean/pathfinder/pathfinder.h b/ocean/pathfinder/pathfinder.h new file mode 100644 index 0000000000..0833a69b21 --- /dev/null +++ b/ocean/pathfinder/pathfinder.h @@ -0,0 +1,427 @@ +#pragma once + +#include +#include +#include +#include +#include + +#define PATHFINDER_ROWS 6 +#define PATHFINDER_COLS 6 +#define PATHFINDER_VERTICAL_WALLS (PATHFINDER_ROWS * (PATHFINDER_COLS + 1)) +#define PATHFINDER_HORIZONTAL_WALLS ((PATHFINDER_ROWS + 1) * PATHFINDER_COLS) +#define PATHFINDER_NUM_WALLS (PATHFINDER_VERTICAL_WALLS + PATHFINDER_HORIZONTAL_WALLS) +#define PATHFINDER_OBS_SIZE (PATHFINDER_NUM_WALLS + 2) +#define PATHFINDER_NUM_ACTIONS 4 + +#define PATHFINDER_ACT_NORTH 0 +#define PATHFINDER_ACT_EAST 1 +#define PATHFINDER_ACT_SOUTH 2 +#define PATHFINDER_ACT_WEST 3 + +#define PATHFINDER_UNKNOWN -1.0f +#define PATHFINDER_OPEN 0.0f +#define PATHFINDER_WALL 1.0f + +#define PATHFINDER_STEP_PENALTY -0.001f +#define PATHFINDER_NEW_WALL_PENALTY -0.01f +#define PATHFINDER_KNOWN_WALL_PENALTY -0.03f +#define PATHFINDER_IMPOSSIBLE_PENALTY -0.02f +#define PATHFINDER_GOAL_REWARD 1.0f + +typedef struct Log { + float perf; + float score; + float episode_return; + float episode_length; + float success; + float wall_hits; + float known_walls; + float known_open_edges; + float shortest_path_len; + float agent_path_len; + float n; +} Log; + +typedef struct State { + int tick; + int agent_row; + int agent_col; + int goal_row; + int goal_col; + int shortest_path_len; + int agent_path_len; + int wall_hits; + int known_wall_count; + int known_open_count; + int success; + float episode_return; + unsigned char true_walls[PATHFINDER_NUM_WALLS]; + float known_walls[PATHFINDER_NUM_WALLS]; +} State; + +typedef struct Pathfinder { + Log log; + float* observations; + float* actions; + float* rewards; + float* terminals; + int num_agents; + unsigned int rng; + float branch_prob; + float loop_prob; + float extra_entry_prob; + int min_solution_len; + int max_steps; + State state; +} Pathfinder; + +static inline int pathfinder_v_wall(int row, int edge_col) { + return row * (PATHFINDER_COLS + 1) + edge_col; +} + +static inline int pathfinder_h_wall(int edge_row, int col) { + return PATHFINDER_VERTICAL_WALLS + edge_row * PATHFINDER_COLS + col; +} + +static inline bool pathfinder_in_bounds(int row, int col) { + return row >= 0 && row < PATHFINDER_ROWS && col >= 0 && col < PATHFINDER_COLS; +} + +static inline unsigned int pathfinder_rand(Pathfinder* env) { + env->rng = 1664525u * env->rng + 1013904223u; + return env->rng; +} + +static inline float pathfinder_rand_float(Pathfinder* env) { + return (float)(pathfinder_rand(env) >> 8) / 16777216.0f; +} + +static inline int pathfinder_wall_between(int row, int col, int next_row, int next_col) { + if (next_row == row && next_col == col + 1) { + return pathfinder_v_wall(row, col + 1); + } + if (next_row == row && next_col == col - 1) { + return pathfinder_v_wall(row, col); + } + if (next_col == col && next_row == row + 1) { + return pathfinder_h_wall(row + 1, col); + } + if (next_col == col && next_row == row - 1) { + return pathfinder_h_wall(row, col); + } + return -1; +} + +static inline void pathfinder_open_edge(State* s, int row, int col, int next_row, int next_col) { + int wall = pathfinder_wall_between(row, col, next_row, next_col); + if (wall >= 0) { + s->true_walls[wall] = 0; + } +} + +static inline void pathfinder_action_delta(int action, int* d_row, int* d_col) { + *d_row = 0; + *d_col = 0; + if (action == PATHFINDER_ACT_NORTH) { + *d_row = -1; + } else if (action == PATHFINDER_ACT_EAST) { + *d_col = 1; + } else if (action == PATHFINDER_ACT_SOUTH) { + *d_row = 1; + } else if (action == PATHFINDER_ACT_WEST) { + *d_col = -1; + } +} + +static int pathfinder_shortest_path(const State* s) { + int dist[PATHFINDER_ROWS][PATHFINDER_COLS]; + int queue[PATHFINDER_ROWS * PATHFINDER_COLS]; + memset(dist, -1, sizeof(dist)); + + int head = 0; + int tail = 0; + dist[0][0] = 0; + queue[tail++] = 0; + + static const int d_rows[4] = {-1, 0, 1, 0}; + static const int d_cols[4] = {0, 1, 0, -1}; + while (head < tail) { + int cell = queue[head++]; + int row = cell / PATHFINDER_COLS; + int col = cell % PATHFINDER_COLS; + if (row == s->goal_row && col == s->goal_col) { + return dist[row][col]; + } + + for (int action = 0; action < PATHFINDER_NUM_ACTIONS; action++) { + int nr = row + d_rows[action]; + int nc = col + d_cols[action]; + if (!pathfinder_in_bounds(nr, nc)) { + continue; + } + int wall = pathfinder_wall_between(row, col, nr, nc); + if (wall < 0 || s->true_walls[wall]) { + continue; + } + if (dist[nr][nc] >= 0) { + continue; + } + dist[nr][nc] = dist[row][col] + 1; + queue[tail++] = nr * PATHFINDER_COLS + nc; + } + } + + return -1; +} + +static inline bool pathfinder_has_path_to_goal(const State* s) { + return pathfinder_shortest_path(s) >= 0; +} + +static void pathfinder_recount_known(State* s) { + s->known_wall_count = 0; + s->known_open_count = 0; + for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { + if (s->known_walls[i] == PATHFINDER_WALL) { + s->known_wall_count++; + } else if (s->known_walls[i] == PATHFINDER_OPEN) { + s->known_open_count++; + } + } +} + +static void pathfinder_update_observations(Pathfinder* env) { + pathfinder_recount_known(&env->state); + if (env->observations == NULL) { + return; + } + + memcpy(env->observations, env->state.known_walls, + sizeof(float) * PATHFINDER_NUM_WALLS); + env->observations[PATHFINDER_NUM_WALLS] = + (float)env->state.agent_col / (float)(PATHFINDER_COLS - 1); + env->observations[PATHFINDER_NUM_WALLS + 1] = + (float)env->state.agent_row / (float)(PATHFINDER_ROWS - 1); +} + +static void pathfinder_reset_known(State* s) { + for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { + s->known_walls[i] = PATHFINDER_UNKNOWN; + } + s->known_wall_count = 0; + s->known_open_count = 0; +} + +static void pathfinder_init_walls(State* s) { + memset(s->true_walls, 1, sizeof(s->true_walls)); + pathfinder_reset_known(s); + s->true_walls[pathfinder_v_wall(0, 0)] = 0; +} + +static void pathfinder_carve_solution(Pathfinder* env) { + State* s = &env->state; + int row = 0; + int col = 0; + + while (row != s->goal_row || col != s->goal_col) { + bool can_row = row != s->goal_row; + bool can_col = col != s->goal_col; + bool step_row = can_row && (!can_col || (pathfinder_rand(env) & 1u)); + + int next_row = row; + int next_col = col; + if (step_row) { + next_row += (s->goal_row > row) ? 1 : -1; + } else { + next_col += (s->goal_col > col) ? 1 : -1; + } + + pathfinder_open_edge(s, row, col, next_row, next_col); + row = next_row; + col = next_col; + } +} + +static void pathfinder_open_random_edges(Pathfinder* env) { + State* s = &env->state; + float open_prob = env->branch_prob + env->loop_prob; + if (open_prob < 0.0f) open_prob = 0.0f; + if (open_prob > 0.95f) open_prob = 0.95f; + + for (int row = 0; row < PATHFINDER_ROWS; row++) { + for (int col = 0; col < PATHFINDER_COLS - 1; col++) { + if (pathfinder_rand_float(env) < open_prob) { + pathfinder_open_edge(s, row, col, row, col + 1); + } + } + } + for (int row = 0; row < PATHFINDER_ROWS - 1; row++) { + for (int col = 0; col < PATHFINDER_COLS; col++) { + if (pathfinder_rand_float(env) < open_prob) { + pathfinder_open_edge(s, row, col, row + 1, col); + } + } + } + for (int row = 1; row < PATHFINDER_ROWS; row++) { + if (pathfinder_rand_float(env) < env->extra_entry_prob) { + s->true_walls[pathfinder_v_wall(row, 0)] = 0; + } + } +} + +static void pathfinder_generate_maze(Pathfinder* env) { + State* s = &env->state; + int min_solution_len = env->min_solution_len < 1 ? 1 : env->min_solution_len; + + for (int attempt = 0; attempt < 128; attempt++) { + pathfinder_init_walls(s); + + do { + s->goal_row = (int)(pathfinder_rand(env) % PATHFINDER_ROWS); + s->goal_col = (int)(pathfinder_rand(env) % PATHFINDER_COLS); + } while (s->goal_row == 0 && s->goal_col == 0); + + pathfinder_carve_solution(env); + pathfinder_open_random_edges(env); + s->shortest_path_len = pathfinder_shortest_path(s); + if (s->shortest_path_len >= min_solution_len) { + return; + } + } + + pathfinder_init_walls(s); + s->goal_row = 0; + s->goal_col = min_solution_len < PATHFINDER_COLS ? min_solution_len : PATHFINDER_COLS - 1; + for (int col = 0; col < s->goal_col; col++) { + pathfinder_open_edge(s, 0, col, 0, col + 1); + } + s->shortest_path_len = pathfinder_shortest_path(s); +} + +void add_log(Pathfinder* env) { + State* s = &env->state; + float success = (float)s->success; + float efficiency = 0.0f; + if (s->success && s->agent_path_len > 0 && s->shortest_path_len > 0) { + efficiency = (float)s->shortest_path_len / (float)s->agent_path_len; + if (efficiency > 1.0f) { + efficiency = 1.0f; + } + } + + env->log.perf += success; + env->log.score += success * efficiency; + env->log.episode_return += s->episode_return; + env->log.episode_length += (float)s->tick; + env->log.success += success; + env->log.wall_hits += (float)s->wall_hits; + env->log.known_walls += (float)s->known_wall_count; + env->log.known_open_edges += (float)s->known_open_count; + env->log.shortest_path_len += (float)s->shortest_path_len; + env->log.agent_path_len += (float)s->agent_path_len; + env->log.n += 1.0f; +} + +void refresh_state(Pathfinder* env) { + pathfinder_update_observations(env); +} + +void init(Pathfinder* env) { + if (env->num_agents == 0) { + env->num_agents = 1; + } + if (env->branch_prob == 0.0f) { + env->branch_prob = 0.35f; + } + if (env->loop_prob == 0.0f) { + env->loop_prob = 0.10f; + } + if (env->min_solution_len == 0) { + env->min_solution_len = 1; + } + if (env->max_steps == 0) { + env->max_steps = 128; + } +} + +void c_reset(Pathfinder* env) { + State* s = &env->state; + memset(s, 0, sizeof(*s)); + s->agent_row = 0; + s->agent_col = 0; + pathfinder_generate_maze(env); + pathfinder_update_observations(env); +} + +static void pathfinder_reveal_wall(Pathfinder* env, int wall) { + State* s = &env->state; + if (s->known_walls[wall] != PATHFINDER_UNKNOWN) { + return; + } + s->known_walls[wall] = s->true_walls[wall] ? PATHFINDER_WALL : PATHFINDER_OPEN; +} + +void c_step(Pathfinder* env) { + State* s = &env->state; + env->terminals[0] = 0.0f; + env->rewards[0] = 0.0f; + s->tick++; + + float reward = PATHFINDER_STEP_PENALTY; + int action = (int)env->actions[0]; + if (action < 0 || action >= PATHFINDER_NUM_ACTIONS) { + reward += PATHFINDER_IMPOSSIBLE_PENALTY; + } else { + int d_row; + int d_col; + pathfinder_action_delta(action, &d_row, &d_col); + int next_row = s->agent_row + d_row; + int next_col = s->agent_col + d_col; + int wall = pathfinder_wall_between(s->agent_row, s->agent_col, next_row, next_col); + + if (wall < 0) { + reward += PATHFINDER_IMPOSSIBLE_PENALTY; + } else { + bool was_known = s->known_walls[wall] != PATHFINDER_UNKNOWN; + pathfinder_reveal_wall(env, wall); + + if (s->true_walls[wall]) { + s->wall_hits++; + reward += was_known ? PATHFINDER_KNOWN_WALL_PENALTY : PATHFINDER_NEW_WALL_PENALTY; + } else if (!pathfinder_in_bounds(next_row, next_col)) { + reward += PATHFINDER_IMPOSSIBLE_PENALTY; + } else { + s->agent_row = next_row; + s->agent_col = next_col; + s->agent_path_len++; + if (s->agent_row == s->goal_row && s->agent_col == s->goal_col) { + s->success = 1; + reward += PATHFINDER_GOAL_REWARD; + env->terminals[0] = 1.0f; + } + } + } + } + + if (s->tick >= env->max_steps && env->terminals[0] == 0.0f) { + env->terminals[0] = 1.0f; + } + + env->rewards[0] = reward; + s->episode_return += reward; + pathfinder_update_observations(env); + + if (env->terminals[0]) { + add_log(env); + c_reset(env); + } +} + +void c_close(Pathfinder* env) { + (void)env; +} + +void c_render(Pathfinder* env) { + (void)env; +} diff --git a/ocean/pathfinder/tests/run_all.sh b/ocean/pathfinder/tests/run_all.sh new file mode 100644 index 0000000000..8a2dd4ba47 --- /dev/null +++ b/ocean/pathfinder/tests/run_all.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")/../../.." + +mkdir -p build/pathfinder-tests + +${CC:-clang} -std=c11 -Wall -Wextra -Werror \ + -I. -Iocean/pathfinder -Ivendor -Iraylib-5.5_linux_amd64/include \ + ocean/pathfinder/tests/test_pathfinder_core.c \ + -lm \ + -o build/pathfinder-tests/test_pathfinder_core + +build/pathfinder-tests/test_pathfinder_core diff --git a/ocean/pathfinder/tests/test_pathfinder_core.c b/ocean/pathfinder/tests/test_pathfinder_core.c new file mode 100644 index 0000000000..b22d29ec55 --- /dev/null +++ b/ocean/pathfinder/tests/test_pathfinder_core.c @@ -0,0 +1,166 @@ +#include +#include +#include +#include + +#include "../pathfinder.h" + +static void setup_env(Pathfinder* env, float* obs, float* actions, + float* rewards, float* terminals) { + memset(env, 0, sizeof(*env)); + env->observations = obs; + env->actions = actions; + env->rewards = rewards; + env->terminals = terminals; + env->num_agents = 1; + env->branch_prob = 0.35f; + env->loop_prob = 0.10f; + env->extra_entry_prob = 0.0f; + env->min_solution_len = 1; + env->max_steps = 128; + env->rng = 7; + init(env); +} + +static void test_constants(void) { + assert(PATHFINDER_ROWS == 6); + assert(PATHFINDER_COLS == 6); + assert(PATHFINDER_NUM_WALLS == 84); + assert(PATHFINDER_OBS_SIZE == 86); + assert(PATHFINDER_NUM_ACTIONS == 4); +} + +static void test_reset_initializes_a1_and_unknown_walls(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + + c_reset(&env); + + assert(env.state.agent_row == 0); + assert(env.state.agent_col == 0); + assert(fabsf(obs[PATHFINDER_NUM_WALLS] - 0.0f) < 1e-6f); + assert(fabsf(obs[PATHFINDER_NUM_WALLS + 1] - 0.0f) < 1e-6f); + for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { + assert(fabsf(obs[i] + 1.0f) < 1e-6f); + } +} + +static void test_generated_mazes_connect_a1_to_goal(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + + for (int i = 0; i < 100; i++) { + c_reset(&env); + assert(pathfinder_has_path_to_goal(&env.state)); + assert(env.state.shortest_path_len >= 0); + } +} + +static void test_open_edge_reveals_and_moves(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + c_reset(&env); + + memset(env.state.true_walls, 1, sizeof(env.state.true_walls)); + for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { + env.state.known_walls[i] = PATHFINDER_UNKNOWN; + } + env.state.agent_row = 0; + env.state.agent_col = 0; + env.state.goal_row = 5; + env.state.goal_col = 5; + int east_wall = pathfinder_wall_between(0, 0, 0, 1); + env.state.true_walls[east_wall] = 0; + refresh_state(&env); + + actions[0] = PATHFINDER_ACT_EAST; + c_step(&env); + + assert(env.state.agent_row == 0); + assert(env.state.agent_col == 1); + assert(fabsf(obs[east_wall] - 0.0f) < 1e-6f); + assert(terminals[0] == 0.0f); +} + +static void test_blocked_edge_reveals_and_stays(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + c_reset(&env); + + memset(env.state.true_walls, 1, sizeof(env.state.true_walls)); + for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { + env.state.known_walls[i] = PATHFINDER_UNKNOWN; + } + env.state.agent_row = 0; + env.state.agent_col = 0; + env.state.goal_row = 5; + env.state.goal_col = 5; + int east_wall = pathfinder_wall_between(0, 0, 0, 1); + env.state.true_walls[east_wall] = 1; + refresh_state(&env); + + actions[0] = PATHFINDER_ACT_EAST; + c_step(&env); + + assert(env.state.agent_row == 0); + assert(env.state.agent_col == 0); + assert(fabsf(obs[east_wall] - 1.0f) < 1e-6f); + assert(terminals[0] == 0.0f); +} + +static void test_reaching_goal_terminates(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + c_reset(&env); + + memset(env.state.true_walls, 1, sizeof(env.state.true_walls)); + for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { + env.state.known_walls[i] = PATHFINDER_UNKNOWN; + } + env.state.agent_row = 0; + env.state.agent_col = 0; + env.state.goal_row = 0; + env.state.goal_col = 1; + int east_wall = pathfinder_wall_between(0, 0, 0, 1); + env.state.true_walls[east_wall] = 0; + refresh_state(&env); + + actions[0] = PATHFINDER_ACT_EAST; + c_step(&env); + + assert(terminals[0] == 1.0f); + assert(rewards[0] > 0.9f); + assert(env.log.success >= 1.0f); + assert(env.log.n >= 1.0f); +} + +int main(void) { + test_constants(); + test_reset_initializes_a1_and_unknown_walls(); + test_generated_mazes_connect_a1_to_goal(); + test_open_edge_reveals_and_moves(); + test_blocked_edge_reveals_and_stays(); + test_reaching_goal_terminates(); + printf("pathfinder core tests passed\n"); + return 0; +} diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000000..940189f1b7 --- /dev/null +++ b/uv.lock @@ -0,0 +1,1456 @@ +version = 1 +revision = 3 +requires-python = ">=3.10" +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", + "python_full_version < '3.11'", +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "certifi" +version = "2026.5.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/08/0f303cb0b529e456bb116f2d50565a482694fbb94340bf56d44677e7ed03/charset_normalizer-3.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d", size = 315182, upload-time = "2026-04-02T09:25:40.673Z" }, + { url = "https://files.pythonhosted.org/packages/24/47/b192933e94b546f1b1fe4df9cc1f84fcdbf2359f8d1081d46dd029b50207/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8", size = 209329, upload-time = "2026-04-02T09:25:42.354Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b4/01fa81c5ca6141024d89a8fc15968002b71da7f825dd14113207113fabbd/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790", size = 231230, upload-time = "2026-04-02T09:25:44.281Z" }, + { url = "https://files.pythonhosted.org/packages/20/f7/7b991776844dfa058017e600e6e55ff01984a063290ca5622c0b63162f68/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc", size = 225890, upload-time = "2026-04-02T09:25:45.475Z" }, + { url = "https://files.pythonhosted.org/packages/20/e7/bed0024a0f4ab0c8a9c64d4445f39b30c99bd1acd228291959e3de664247/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393", size = 216930, upload-time = "2026-04-02T09:25:46.58Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ab/b18f0ab31cdd7b3ddb8bb76c4a414aeb8160c9810fdf1bc62f269a539d87/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153", size = 202109, upload-time = "2026-04-02T09:25:48.031Z" }, + { url = "https://files.pythonhosted.org/packages/82/e5/7e9440768a06dfb3075936490cb82dbf0ee20a133bf0dd8551fa096914ec/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af", size = 214684, upload-time = "2026-04-02T09:25:49.245Z" }, + { url = "https://files.pythonhosted.org/packages/71/94/8c61d8da9f062fdf457c80acfa25060ec22bf1d34bbeaca4350f13bcfd07/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34", size = 212785, upload-time = "2026-04-02T09:25:50.671Z" }, + { url = "https://files.pythonhosted.org/packages/66/cd/6e9889c648e72c0ab2e5967528bb83508f354d706637bc7097190c874e13/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1", size = 203055, upload-time = "2026-04-02T09:25:51.802Z" }, + { url = "https://files.pythonhosted.org/packages/92/2e/7a951d6a08aefb7eb8e1b54cdfb580b1365afdd9dd484dc4bee9e5d8f258/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752", size = 232502, upload-time = "2026-04-02T09:25:53.388Z" }, + { url = "https://files.pythonhosted.org/packages/58/d5/abcf2d83bf8e0a1286df55cd0dc1d49af0da4282aa77e986df343e7de124/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53", size = 214295, upload-time = "2026-04-02T09:25:54.765Z" }, + { url = "https://files.pythonhosted.org/packages/47/3a/7d4cd7ed54be99973a0dc176032cba5cb1f258082c31fa6df35cff46acfc/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616", size = 227145, upload-time = "2026-04-02T09:25:55.904Z" }, + { url = "https://files.pythonhosted.org/packages/1d/98/3a45bf8247889cf28262ebd3d0872edff11565b2a1e3064ccb132db3fbb0/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a", size = 218884, upload-time = "2026-04-02T09:25:57.074Z" }, + { url = "https://files.pythonhosted.org/packages/ad/80/2e8b7f8915ed5c9ef13aa828d82738e33888c485b65ebf744d615040c7ea/charset_normalizer-3.4.7-cp310-cp310-win32.whl", hash = "sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374", size = 148343, upload-time = "2026-04-02T09:25:58.199Z" }, + { url = "https://files.pythonhosted.org/packages/35/1b/3b8c8c77184af465ee9ad88b5aea46ea6b2e1f7b9dc9502891e37af21e30/charset_normalizer-3.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943", size = 159174, upload-time = "2026-04-02T09:25:59.322Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/feb40dca40dbb21e0a908801782d9288c64fc8d8e562c2098e9994c8c21b/charset_normalizer-3.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008", size = 147805, upload-time = "2026-04-02T09:26:00.756Z" }, + { url = "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", size = 309705, upload-time = "2026-04-02T09:26:02.191Z" }, + { url = "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", size = 206419, upload-time = "2026-04-02T09:26:03.583Z" }, + { url = "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", size = 227901, upload-time = "2026-04-02T09:26:04.738Z" }, + { url = "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", size = 222742, upload-time = "2026-04-02T09:26:06.36Z" }, + { url = "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", size = 214061, upload-time = "2026-04-02T09:26:08.347Z" }, + { url = "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", size = 199239, upload-time = "2026-04-02T09:26:09.823Z" }, + { url = "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", size = 210173, upload-time = "2026-04-02T09:26:10.953Z" }, + { url = "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", size = 209841, upload-time = "2026-04-02T09:26:12.142Z" }, + { url = "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", size = 200304, upload-time = "2026-04-02T09:26:13.711Z" }, + { url = "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", size = 229455, upload-time = "2026-04-02T09:26:14.941Z" }, + { url = "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", size = 210036, upload-time = "2026-04-02T09:26:16.478Z" }, + { url = "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", size = 224739, upload-time = "2026-04-02T09:26:17.751Z" }, + { url = "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", size = 216277, upload-time = "2026-04-02T09:26:18.981Z" }, + { url = "https://files.pythonhosted.org/packages/06/6d/3be70e827977f20db77c12a97e6a9f973631a45b8d186c084527e53e77a4/charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad", size = 147819, upload-time = "2026-04-02T09:26:20.295Z" }, + { url = "https://files.pythonhosted.org/packages/20/d9/5f67790f06b735d7c7637171bbfd89882ad67201891b7275e51116ed8207/charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00", size = 159281, upload-time = "2026-04-02T09:26:21.74Z" }, + { url = "https://files.pythonhosted.org/packages/ca/83/6413f36c5a34afead88ce6f66684d943d91f233d76dd083798f9602b75ae/charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1", size = 147843, upload-time = "2026-04-02T09:26:22.901Z" }, + { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, + { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, + { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, + { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, + { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, + { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, + { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, + { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, + { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, + { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, + { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, + { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, + { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, + { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, + { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, + { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, + { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, + { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, + { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, + { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, + { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, + { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, + { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, + { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, + { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, + { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, + { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, + { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, + { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, + { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, + { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, + { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, + { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, +] + +[[package]] +name = "click" +version = "8.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9b/98/518d8e5081007684232226f475082b30087d0f585e8457db087298259f49/click-8.4.1.tar.gz", hash = "sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96", size = 353007, upload-time = "2026-05-22T04:08:37.769Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/0d/67e5b4109ea4a837e80daa87c2c696711955e40449a97e8926672534def2/click-8.4.1-py3-none-any.whl", hash = "sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2", size = 116639, upload-time = "2026-05-22T04:08:35.26Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +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 = "cuda-bindings" +version = "12.9.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-pathfinder" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/d8/b546104b8da3f562c1ff8ab36d130c8fe1dd6a045ced80b4f6ad74f7d4e1/cuda_bindings-12.9.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d3c842c2a4303b2a580fe955018e31aea30278be19795ae05226235268032e5", size = 12148218, upload-time = "2025-10-21T14:51:28.855Z" }, + { url = "https://files.pythonhosted.org/packages/45/e7/b47792cc2d01c7e1d37c32402182524774dadd2d26339bd224e0e913832e/cuda_bindings-12.9.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c912a3d9e6b6651853eed8eed96d6800d69c08e94052c292fec3f282c5a817c9", size = 12210593, upload-time = "2025-10-21T14:51:36.574Z" }, + { url = "https://files.pythonhosted.org/packages/a9/c1/dabe88f52c3e3760d861401bb994df08f672ec893b8f7592dc91626adcf3/cuda_bindings-12.9.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fda147a344e8eaeca0c6ff113d2851ffca8f7dfc0a6c932374ee5c47caa649c8", size = 12151019, upload-time = "2025-10-21T14:51:43.167Z" }, + { url = "https://files.pythonhosted.org/packages/63/56/e465c31dc9111be3441a9ba7df1941fe98f4aa6e71e8788a3fb4534ce24d/cuda_bindings-12.9.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:32bdc5a76906be4c61eb98f546a6786c5773a881f3b166486449b5d141e4a39f", size = 11906628, upload-time = "2025-10-21T14:51:49.905Z" }, + { url = "https://files.pythonhosted.org/packages/a3/84/1e6be415e37478070aeeee5884c2022713c1ecc735e6d82d744de0252eee/cuda_bindings-12.9.4-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56e0043c457a99ac473ddc926fe0dc4046694d99caef633e92601ab52cbe17eb", size = 11925991, upload-time = "2025-10-21T14:51:56.535Z" }, + { url = "https://files.pythonhosted.org/packages/d1/af/6dfd8f2ed90b1d4719bc053ff8940e494640fe4212dc3dd72f383e4992da/cuda_bindings-12.9.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8b72ee72a9cc1b531db31eebaaee5c69a8ec3500e32c6933f2d3b15297b53686", size = 11922703, upload-time = "2025-10-21T14:52:03.585Z" }, + { url = "https://files.pythonhosted.org/packages/6c/19/90ac264acc00f6df8a49378eedec9fd2db3061bf9263bf9f39fd3d8377c3/cuda_bindings-12.9.4-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d80bffc357df9988dca279734bc9674c3934a654cab10cadeed27ce17d8635ee", size = 11924658, upload-time = "2025-10-21T14:52:10.411Z" }, +] + +[[package]] +name = "cuda-pathfinder" +version = "1.5.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/c8/26f2e4aae92f11522a96043892ba39a90eac610d5242523aa863212bc1c7/cuda_pathfinder-1.5.5-py3-none-any.whl", hash = "sha256:0228c023f95d1480f143ef5c8922d27a2ab052087a942e81dc289c9eb8f91689", size = 51671, upload-time = "2026-05-27T01:21:25.413Z" }, +] + +[[package]] +name = "filelock" +version = "3.29.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/f9/f38573ed5844586db374d085911740a501ccfa373b455fc9413f09f85237/filelock-3.29.1.tar.gz", hash = "sha256:d97e6b1b9757569626c58caa07dc4beb1613f4a2938b1e8cc81afca398906c9e", size = 59335, upload-time = "2026-06-03T15:19:04.053Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4c/a0/614c5fe402fd88951df45f4dda2fa3b4e17a99ecd92340771929169b3b95/filelock-3.29.1-py3-none-any.whl", hash = "sha256:85199dfd706869641b72b2e8955d5416a4b2b7dc4b0e8e6d97b4cc1299a6983b", size = 40750, upload-time = "2026-06-03T15:19:02.959Z" }, +] + +[[package]] +name = "fsspec" +version = "2026.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d5/8d/1c51c094345df128ca4a990d633fe1a0ff28726c9e6b3c41ba65087bba1d/fsspec-2026.4.0.tar.gz", hash = "sha256:301d8ac70ae90ef3ad05dcf94d6c3754a097f9b5fe4667d2787aa359ec7df7e4", size = 312760, upload-time = "2026-04-29T20:42:38.635Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/0c/043d5e551459da400957a1395e0febbf771446ff34291afcbe3d8be2a279/fsspec-2026.4.0-py3-none-any.whl", hash = "sha256:11ef7bb35dab8a394fde6e608221d5cf3e8499401c249bebaeaad760a1a8dec2", size = 203402, upload-time = "2026-04-29T20:42:36.842Z" }, +] + +[[package]] +name = "gitdb" +version = "4.0.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "smmap" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" }, +] + +[[package]] +name = "gitpython" +version = "3.1.50" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gitdb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/f6/354ae6491228b5eb40e10d89c4d13c651fe1cf7556e35ebdded50cff57ce/gitpython-3.1.50.tar.gz", hash = "sha256:80da2d12504d52e1f998772dc5baf6e553f8d2fcfe1fcc226c9d9a2ee3372dcc", size = 219798, upload-time = "2026-05-06T04:01:26.571Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/7a/1c6e3562dfd8950adbb11ffbc65d21e7c89d01a6e4f137fa981056de25c5/gitpython-3.1.50-py3-none-any.whl", hash = "sha256:d352abe2908d07355014abdd21ddf798c2a961469239afec4962e9da884858f9", size = 212507, upload-time = "2026-05-06T04:01:23.799Z" }, +] + +[[package]] +name = "gpytorch" +version = "1.15.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "linear-operator" }, + { name = "mpmath" }, + { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scikit-learn", version = "1.9.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.11'" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/23/00/cac3e309b2f66518d937ce4d9fa821f167d9070d004fcb070b1780cd1d94/gpytorch-1.15.2.tar.gz", hash = "sha256:380625e93f851b85f772b25c5fb0a6c6d2e3eb2ef667f1e566ab4f95b8775361", size = 2781831, upload-time = "2026-02-28T01:12:55.172Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/70/c419a361b09ccb618b5165daa919003ccdd9c148356f7ff71a710db6bcf0/gpytorch-1.15.2-py3-none-any.whl", hash = "sha256:2112fdc7c0c0bf56a7f2444663cfc80fdfc3e19724399d6303a83d8efdd71e9e", size = 291209, upload-time = "2026-02-28T01:12:52.587Z" }, +] + +[[package]] +name = "idna" +version = "3.18" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "joblib" +version = "1.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603, upload-time = "2025-12-15T08:41:46.427Z" } +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 = "linear-operator" +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "torch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/1a/0d6d0269cf7327e31a2fe80b274d5bf5001dc4462acbef26240da6ac5dfe/linear_operator-0.6.1.tar.gz", hash = "sha256:3fba49a8080d16f822a5d870f462279cd6afbcf4ed670f4511b38fad96f61831", size = 181898, upload-time = "2026-02-27T23:43:18.716Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/34/ee74708a93704baca5c1ef439817c4cb649f73382c518380ccc5ab335603/linear_operator-0.6.1-py3-none-any.whl", hash = "sha256:a5981c1fcda08df3a210dffb6e8019b4751f4afaf3ffc822c24eaaf56b11eed9", size = 174770, upload-time = "2026-02-27T23:43:17.074Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631, upload-time = "2025-09-27T18:36:05.558Z" }, + { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057, upload-time = "2025-09-27T18:36:07.165Z" }, + { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050, upload-time = "2025-09-27T18:36:08.005Z" }, + { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681, upload-time = "2025-09-27T18:36:08.881Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705, upload-time = "2025-09-27T18:36:10.131Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524, upload-time = "2025-09-27T18:36:11.324Z" }, + { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282, upload-time = "2025-09-27T18:36:12.573Z" }, + { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745, upload-time = "2025-09-27T18:36:13.504Z" }, + { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571, upload-time = "2025-09-27T18:36:14.779Z" }, + { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056, upload-time = "2025-09-27T18:36:16.125Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932, upload-time = "2025-09-27T18:36:17.311Z" }, + { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" }, + { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" }, + { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" }, + { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" }, + { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" }, + { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" }, + { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" }, + { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" }, + { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" }, + { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" }, + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { 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" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, +] + +[[package]] +name = "narwhals" +version = "2.22.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/1c/c80cb7719721a44846c6301ef118434bae30a423924bfad3a47f16bdc064/narwhals-2.22.0.tar.gz", hash = "sha256:6486282bb7e4b4ab55963efbd8be1451b764cc4874b74d1fd625eba9dc60b86f", size = 417565, upload-time = "2026-06-01T13:34:36.249Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/b6/e7cdde7b8e90d5dff25b622f95833ef26567ad184c977278b93a1cbd5717/narwhals-2.22.0-py3-none-any.whl", hash = "sha256:1421797ede01789cc1537619dbc3f36f840737240f748fdb24a60a0225fc80be", size = 453815, upload-time = "2026-06-01T13:34:34.127Z" }, +] + +[[package]] +name = "networkx" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "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 = [ + { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263, upload-time = "2024-10-21T12:39:36.247Z" }, +] + +[[package]] +name = "networkx" +version = "3.6.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/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.2.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "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 = [ + { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, + { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, + { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, + { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" }, + { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" }, + { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" }, + { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" }, + { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" }, + { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" }, + { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" }, + { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" }, + { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" }, + { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, + { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, + { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, + { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, + { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, + { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, + { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, + { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, + { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, + { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, + { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, + { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, + { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, + { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, + { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" }, + { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" }, + { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, + { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, + { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, + { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, + { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, + { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" }, + { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, + { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, + { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, + { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" }, +] + +[[package]] +name = "numpy" +version = "2.4.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/49/ec46835a70be8fa6446c495126ac84fdb28cb2558e1620ffb87a10c8b64c/numpy-2.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0280e0356c0829a18d9de1cb7eee50ec22ca639878d7240307ca0943d73cd2c4", size = 16969194, upload-time = "2026-05-18T23:33:13.503Z" }, + { url = "https://files.pythonhosted.org/packages/0e/0d/f5957185c0ee2f3e12f78715aa9e3b353fd83633316c8532b38faa37e3f6/numpy-2.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110f8b71aacb688ec69062bb7f6938a0f8acb01b7c1c4beb453c65b6d234584d", size = 14964111, upload-time = "2026-05-18T23:33:17.795Z" }, + { url = "https://files.pythonhosted.org/packages/ad/40/40a40ee0ddf7ceb782c49af278894b686e586d65d8c1889c8b5da01a3d7d/numpy-2.4.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4cfe66903cc32a9921a6733d96b19bb6abf310397581bbad89c228f5abaf0ee8", size = 5469159, upload-time = "2026-05-18T23:33:20.654Z" }, + { url = "https://files.pythonhosted.org/packages/63/13/f9a8046535cb21deae82f8d03de9617e08882d274fad2539630761888228/numpy-2.4.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8155154c7c691289fe18f510b5d4657c68c67989f293f0535a91360392ff6538", size = 6798936, upload-time = "2026-05-18T23:33:22.987Z" }, + { url = "https://files.pythonhosted.org/packages/33/a8/6fa8c1a345a8c85dbb21932c447bee07c30a2c2a3f31e369c0a84b300147/numpy-2.4.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab0a9c4ffb1a6d95ef519fe4247dba8eb6b18ad93999f76b7f657039acabd47", size = 15966692, upload-time = "2026-05-18T23:33:26.62Z" }, + { url = "https://files.pythonhosted.org/packages/02/03/74fe2a4cb3817d94d86402f2506554130a2f01414e299b5a843e5a8a957f/numpy-2.4.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cd468399cfd2504718f0ba50e410dca55a170b61a02ad92bb18c8a65186e93", size = 16918164, upload-time = "2026-05-18T23:33:29.955Z" }, + { url = "https://files.pythonhosted.org/packages/c5/80/3615be3313f7e7696609bc194b9f0101da809df79e859bdb84e0cd043f46/numpy-2.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2d37ab77531417474168eb79d6d80b14f821a966818505d03013d0833edb7a8", size = 17322877, upload-time = "2026-05-18T23:33:34.724Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ac/a691e0fe2675e370d0e08ff905adc49a1c8830e8cae03efe4477e92cd55d/numpy-2.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f407cb6b8e9d6d8c626bc73c945db1706035af8fd632295547bf1c9e46d092d6", size = 18651487, upload-time = "2026-05-18T23:33:38.217Z" }, + { url = "https://files.pythonhosted.org/packages/15/a7/9bc1cd626d7bf6869bfedf27b91b6ab5dd607758bf8e959d6fa80c6a59cb/numpy-2.4.6-cp311-cp311-win32.whl", hash = "sha256:ddea102b48f9e339f3948bf22040944184627a30fdf7f858667673b9c5f033c8", size = 6233945, upload-time = "2026-05-18T23:33:41.331Z" }, + { url = "https://files.pythonhosted.org/packages/c5/31/7fc6239c12bce7e931463251cca4426c465e1876ba3cc785402ef4dd8f4e/numpy-2.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:1e254a00cdf42b1e4d5b3d68d33af63268d41340d8885df2ab6470f2e1500147", size = 12608406, upload-time = "2026-05-18T23:33:44.131Z" }, + { url = "https://files.pythonhosted.org/packages/27/83/140f85a466595a16382996a1bf06b2b54bcd597488921b0c9daaeeda72af/numpy-2.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:ed9749eef4cbd126da3dc1d6bcb3a57f5eb7ac6a6484146bdbf743f552dfc577", size = 10479528, upload-time = "2026-05-18T23:33:50.725Z" }, + { url = "https://files.pythonhosted.org/packages/95/2a/3d7b5ac8aac24feaf9ad7ed58f45b0bbc06d37e4338ae84c9f2298b570f9/numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1", size = 16689119, upload-time = "2026-05-18T23:33:54.065Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/92c4c131527599e8288d6918e888d88726f84d805d784b771f32408aeaef/numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb", size = 14699246, upload-time = "2026-05-18T23:33:57.621Z" }, + { url = "https://files.pythonhosted.org/packages/ad/fe/c0a6b7b2ca128a8fb228575147073b660656734b8ebe4d76c8fd748dcc79/numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41", size = 5204410, upload-time = "2026-05-18T23:34:00.302Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d4/9770d14ba719432bb90a421bfd443872ed0f70f7264b64bec12ea363d5fd/numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698", size = 6551240, upload-time = "2026-05-18T23:34:02.852Z" }, + { url = "https://files.pythonhosted.org/packages/c9/c6/50a46a6205feba2343f1d6d17438107c5dc491ed1c736e6ea68689fd906b/numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f", size = 15671012, upload-time = "2026-05-18T23:34:05.485Z" }, + { url = "https://files.pythonhosted.org/packages/99/60/14115e6364fa676c5397c2ad3004e527e9aa487abf5d0706ec81bbd08529/numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853", size = 16645538, upload-time = "2026-05-18T23:34:09.265Z" }, + { url = "https://files.pythonhosted.org/packages/ae/c5/693cbe59e57db94d2231fa519ca3978dc9e19da5a8f088588f5c6e947ff2/numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a", size = 17020706, upload-time = "2026-05-18T23:34:13.053Z" }, + { url = "https://files.pythonhosted.org/packages/ef/fc/85b7c4eff9b4966ade25c2273cf7e7012e92366c032058653934b37de044/numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2", size = 18368541, upload-time = "2026-05-18T23:34:17.024Z" }, + { url = "https://files.pythonhosted.org/packages/f6/81/e1b27545deedce7f4a0b348618c6b62d74e36a4dc9ccd42f3eb2f85eee32/numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45", size = 5962825, upload-time = "2026-05-18T23:34:20.3Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ca/feab00bd44aa5fe1ad2c18f08b4d3bb92e26484b0b1d1443897809ed528c/numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751", size = 12321687, upload-time = "2026-05-18T23:34:23.095Z" }, + { url = "https://files.pythonhosted.org/packages/63/cf/5a6d34850a39d1093558564f77ee8e8e0bee5061151b8f05a55711001ec7/numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8", size = 10221482, upload-time = "2026-05-18T23:34:25.876Z" }, + { url = "https://files.pythonhosted.org/packages/fb/82/bdab26d7438c6791ca31b7c024ca37c1eab8b726ba236129005cd4a06e45/numpy-2.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:511dbaf848decaaaf4b4ca48032619fb3138710c4bf7da7617765edad1ef96b0", size = 16684648, upload-time = "2026-05-18T23:34:29.41Z" }, + { url = "https://files.pythonhosted.org/packages/1b/30/a80189bcc7f5e4258b3fbc3968d909d1756f54d023299ecc39ad6fdb9ef8/numpy-2.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf162abab1c1a736333192707cef898e735a5ca00f38f27eeedf44b39d9e85eb", size = 14693902, upload-time = "2026-05-18T23:34:33.013Z" }, + { url = "https://files.pythonhosted.org/packages/97/12/70b5d0d7c15e1ebb8a6a84a8caa1d19e181d84fb58bb6d70aca29099dec1/numpy-2.4.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:043191bfa8eab18c776647b62723ac9dddece59743b13f49b2016094129c2b3f", size = 5198992, upload-time = "2026-05-18T23:34:36.132Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/ebd2a8f8a83541f8d38cc5667e8c2b69cecfd30da6e45693e8158857d44b/numpy-2.4.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6180d8b35af935aed8ece3a85e0a43f87393ae0ac87c8d2c8bd2c993f7270ef3", size = 6546944, upload-time = "2026-05-18T23:34:38.484Z" }, + { url = "https://files.pythonhosted.org/packages/bb/c5/7b863a97a91671a0338f4253bd3b5a3d3852f0692dae91711c9f4a10e787/numpy-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72fbe16c6fac95aedf5937fa873445cec2110be35d8a4e9433d7501fd98dae6b", size = 15669392, upload-time = "2026-05-18T23:34:41.257Z" }, + { url = "https://files.pythonhosted.org/packages/a5/9d/3584b9984ca4c047aea75214ce1a4c4c73d849bd71b604264b7f5653f8a8/numpy-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7830bab239b79cda9c08c2da014761cafb48da6150e1da17ac06283f43b6089", size = 16633220, upload-time = "2026-05-18T23:34:45.075Z" }, + { url = "https://files.pythonhosted.org/packages/05/ae/7c67fba23bd98caec7c99261f3a16072ade14813486b0282cb29846de832/numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef4aea96ce4d3b074422cb4f2f64e216bf9e213004bb58ecfdf50ea02ea8eb9a", size = 17020800, upload-time = "2026-05-18T23:34:49.065Z" }, + { url = "https://files.pythonhosted.org/packages/d9/5d/3b6725cb31d983c5e66916f5d36f6d7e5521129e4c4404d64f918292a5b6/numpy-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfa20cc6ca228e6b155b11da03825975ce66aea520985dbbddf0f2a5a495c605", size = 18357600, upload-time = "2026-05-18T23:34:52.709Z" }, + { url = "https://files.pythonhosted.org/packages/f7/da/2ccc6c2fe8898dee01d90c75c5f5f914a23daf99e3e0f59516a08760c8b5/numpy-2.4.6-cp313-cp313-win32.whl", hash = "sha256:56b39e5e0622a09a25bf5baf62f4bcf0cb8a41ae6e2819cf49bbc5a74c083f91", size = 5961134, upload-time = "2026-05-18T23:34:55.618Z" }, + { url = "https://files.pythonhosted.org/packages/b5/cd/9cc4dc876fb065d5c220aae4d5e14826b2715331bb7618ce1fb07a679d99/numpy-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:c4fc99836233ea196540b17ab0983aff60ed07941751930f5f4d05bc3b3b7359", size = 12318598, upload-time = "2026-05-18T23:34:58.928Z" }, + { url = "https://files.pythonhosted.org/packages/39/1e/c0bcba1f8694116485fe28fd1be698c278fcda4141c5b0e53a2aed8b12a8/numpy-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a7c711e21628b52034bb5ab8d1bce291f752fcc5e92accc615778acee1ff4778", size = 10222272, upload-time = "2026-05-18T23:35:02.167Z" }, + { url = "https://files.pythonhosted.org/packages/63/6d/cc5619247c8f4204e507f5883528372e4ac4bb189e579fb859a12e480b1f/numpy-2.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:112b06a867b235ef466ed3508ddf0238050df9c727cafb5301ac385b899189a1", size = 14821197, upload-time = "2026-05-18T23:35:05.468Z" }, + { url = "https://files.pythonhosted.org/packages/00/58/f1c39161c87d9e9bed660f1ed4bafc0e403d5ec9650b6dd77aead07d489b/numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:eaf7fa2de5c0be8ae6ff8e9bea2ccd725e980541244521d8d4b5f3354a27babe", size = 5326287, upload-time = "2026-05-18T23:35:08.693Z" }, + { url = "https://files.pythonhosted.org/packages/af/57/3917ab0fd97f271a8694513581b8a36c655f111c446852c302f04ccdb6fc/numpy-2.4.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7265a2f3d436e54ef9f2b52b5c937e6be778781bd97a590319d7348f1c1ca997", size = 6646763, upload-time = "2026-05-18T23:35:11.459Z" }, + { url = "https://files.pythonhosted.org/packages/eb/0f/037e64c494b67581ae18193d770adef354c41f3f2c8ebf865602d949bf8f/numpy-2.4.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20", size = 15728070, upload-time = "2026-05-18T23:35:14.79Z" }, + { url = "https://files.pythonhosted.org/packages/21/a6/5d2bae9c9542eb4df16dc9c46dc79c186e9bad53805dfa5399a6023c6db0/numpy-2.4.6-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ede83e07a75dd06bc501566c1eca2afc0d61677c1472ac9ad93fdee6e638a48d", size = 16681752, upload-time = "2026-05-18T23:35:18.836Z" }, + { url = "https://files.pythonhosted.org/packages/92/14/23d1dfb410ae362cd59ce53e936b1513d545eb40db3949ced632e19a459e/numpy-2.4.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:68bb27509ac1b9a3443094260f6326150663b06abe40b73a2f81160623da5b67", size = 17086024, upload-time = "2026-05-18T23:35:22.52Z" }, + { url = "https://files.pythonhosted.org/packages/4b/6e/23595a2c642cdf3bc567877064bdd7f91c8b0038a4453cf2daf7248eafe9/numpy-2.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a0df0043bdb289bde1f62da130d20df23d58b45429f752bc7a8fc5325a225ecd", size = 18403398, upload-time = "2026-05-18T23:35:26.398Z" }, + { url = "https://files.pythonhosted.org/packages/8a/90/0ac3bc947217e66dec77e7cbc6a1979d1af70b6461b82f620d3bccd5e4c8/numpy-2.4.6-cp313-cp313t-win32.whl", hash = "sha256:29a287e0cf63ff528da061de6b9f64a4618da591ca1046aafc54062e40ca7eab", size = 6084971, upload-time = "2026-05-18T23:35:29.387Z" }, + { url = "https://files.pythonhosted.org/packages/77/71/5673e351671a1d2bd6063b91b44f70c0affea7d1516fa7a6572941ba4aa1/numpy-2.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:25c692919ac5a01f170a3bfcd62d745b24fd095c353d50812637d6fcab442e75", size = 12458532, upload-time = "2026-05-18T23:35:32.175Z" }, + { url = "https://files.pythonhosted.org/packages/3f/88/19d3503c5046e688f049274b27a3ef3d771152fa80d3ba3d01a3dff61abe/numpy-2.4.6-cp313-cp313t-win_arm64.whl", hash = "sha256:1e978ec1e8bd0e0e4de6bb75de9d30cbb74db6b6a2bb727618613703ca0167dd", size = 10291881, upload-time = "2026-05-18T23:35:35.465Z" }, + { url = "https://files.pythonhosted.org/packages/f8/91/3ab2044d05fd16d343c5ac2e69b127f1b2854040dd20b193257c78028bd3/numpy-2.4.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06ca2f61ec4385a07a6977c55ba998a4466c123642b4a32694d3128fce18c079", size = 16683458, upload-time = "2026-05-18T23:35:38.353Z" }, + { url = "https://files.pythonhosted.org/packages/8e/62/764ce66fa4147ae6d73071a3abf804ffe606f174618697c571acdf26a7c9/numpy-2.4.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:38efbc8de75c7a0fc1ac190162d892787f3f47b57cc291231aafee36b80982b7", size = 14704559, upload-time = "2026-05-18T23:35:42.14Z" }, + { url = "https://files.pythonhosted.org/packages/60/61/23f27c172f022e04025b7dc2367f4d63c1a398120607ec896228649a6f48/numpy-2.4.6-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:d581b735e177fdcdce6fed8e7e8880a3fb6ee4e3653a3ac6af01c6f4c03effc5", size = 5209716, upload-time = "2026-05-18T23:35:45.377Z" }, + { url = "https://files.pythonhosted.org/packages/03/71/21cf70dc6ea3e3acb95fc53a265b2fc248b981f0194ceb5b475271b8809d/numpy-2.4.6-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:0a041d3d761dc3c35cc56ce0351506a02bcbc25f7b169f652435141a17db9096", size = 6543947, upload-time = "2026-05-18T23:35:47.926Z" }, + { url = "https://files.pythonhosted.org/packages/d5/91/64288395ee1799bd2e0b04a305dce9666da90c961e1f3fe982a05ee1c036/numpy-2.4.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40fdc1ae7125e518ea98e53e69a4ebc27e1fd50510c47b7ea130cf21e5e1d42b", size = 15685197, upload-time = "2026-05-18T23:35:50.863Z" }, + { url = "https://files.pythonhosted.org/packages/f3/eb/ebffaa97dc55502df69584a8f0dcf07f69a3e0b3e2323670a2722db9aa39/numpy-2.4.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c306dea656c12c68f51f4cea133cbe78ca7435eb28c735eac1d3ebe73be6e8", size = 16638245, upload-time = "2026-05-18T23:35:54.752Z" }, + { url = "https://files.pythonhosted.org/packages/b8/0b/54f9da33128d7e350fab89c7455902eeae70349ee52bddb448dc4a576f45/numpy-2.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:33111801a01c12a8a1e3721f0a9232f8cfc8ae2c6b7098167e6f623c6073f402", size = 17036587, upload-time = "2026-05-18T23:35:58.355Z" }, + { url = "https://files.pythonhosted.org/packages/b6/f0/fdebc1052db1cc37c64beb22072d67cd6d1c71adca1299f53dec2b5e20d3/numpy-2.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae506e6902902557576a26ff33eda8695e7ecb3cb36c3b573a0765dee114ebdb", size = 18363226, upload-time = "2026-05-18T23:36:02.845Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b4/298628d98c72b57e57f7165ae6a481a1deaf6f3c28262a6e4c739c275930/numpy-2.4.6-cp314-cp314-win32.whl", hash = "sha256:aaf159caa35993cb1f56fb9b8e4610d35758e7ca005412eb1daa856a78c9c4b1", size = 6010196, upload-time = "2026-05-18T23:36:05.92Z" }, + { url = "https://files.pythonhosted.org/packages/df/ac/46de6dda46478f7942f839e094970be2d4a861e005c4b3bf07c92e291a09/numpy-2.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:b507f5c4c1d508876d1819b6bf9a49d365b96320b5d4993426b33a23ca4b8261", size = 12450334, upload-time = "2026-05-18T23:36:09.107Z" }, + { url = "https://files.pythonhosted.org/packages/78/92/b8b798ac784102c0da830d2257d59358e3d3d90d1e2b3f2575dad976c5cf/numpy-2.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:6f41ae150c4e32db4f3310cdaf64b1593a03dbabe29eec77fc9b50fe64061df6", size = 10495678, upload-time = "2026-05-18T23:36:12.766Z" }, + { url = "https://files.pythonhosted.org/packages/30/34/ec28d1aa8115971537c01469ab2011ee96827930f0a124de1000cc2a7ed7/numpy-2.4.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ece3d2cfe132e7d51f44a832b303895e6f2d499c5e74dfbdb06ee246147a304a", size = 14823672, upload-time = "2026-05-18T23:36:16.473Z" }, + { url = "https://files.pythonhosted.org/packages/16/bd/f6d1fede4e54e8042a7ff97bb495510f3c220f94bcd9e8b228e87c92cc0d/numpy-2.4.6-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e3e5193ef5a3dc73bceee50f7fdc2c90dbb76c42df8d8fae3d1067a583df579e", size = 5328731, upload-time = "2026-05-18T23:36:19.767Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f0/e105b9e2fd728a9910103884decd6951d9dd73896b914a98d9a231de02ee/numpy-2.4.6-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:17f9ade344e7d9b464a084d69bcf18fc691cb1db67c62ed80820bf4926d78f0e", size = 6649805, upload-time = "2026-05-18T23:36:22.266Z" }, + { url = "https://files.pythonhosted.org/packages/82/dd/1206a7ca6ab15e3f02069707ca96222e202af681bb73756da7527f3cb837/numpy-2.4.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd5ffd25db4e7ba6a375693b3fc0fc1791ec636c17db3720da19bde7180ec43", size = 15730496, upload-time = "2026-05-18T23:36:25.713Z" }, + { url = "https://files.pythonhosted.org/packages/51/e7/38d3ea825dcab85a591734decb2f6c67caa7c8367d374df1a1c3842f9b07/numpy-2.4.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d92c3819208a60205a12a245c91ad70cb0a85336659b19b834205573ac8456e", size = 16679616, upload-time = "2026-05-18T23:36:29.652Z" }, + { url = "https://files.pythonhosted.org/packages/93/b7/caabfdf53edf663e0b4eb74d7d405d83baef09eb5e83bcd32d601d72b93e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e85b752a1e912b70eaad4fafbd4d1238007ab221de2009b9a2f5ae7461239895", size = 17085145, upload-time = "2026-05-18T23:36:33.449Z" }, + { url = "https://files.pythonhosted.org/packages/f9/45/68d7c33a6bcf3e5aa3bdbd57a367e6f615286dfd6482f97e8ffeb734306e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:29cb7f67d10b479ff07c17d33e39f78c07f71c40ef30d63c153d340e96cd3fb4", size = 18403813, upload-time = "2026-05-18T23:36:37.369Z" }, + { url = "https://files.pythonhosted.org/packages/9c/50/0753655aa844c99cd9e018aacf76f130f1bd81d881bb74bc0aef5d73a8ba/numpy-2.4.6-cp314-cp314t-win32.whl", hash = "sha256:260a5d70215b61ab4fadf5c7baacd64821842975eea312125ed3c39a6391b063", size = 6156982, upload-time = "2026-05-18T23:36:40.817Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d4/7c67becf668f973cb490cec3e98dfd799d866f9c989a54d355672cfa0db6/numpy-2.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:81a1cca95ed5bb92aa8b10dd2cdc9a0d3853a50fad926c28b5d7e8ea54389627", size = 12638908, upload-time = "2026-05-18T23:36:43.996Z" }, + { url = "https://files.pythonhosted.org/packages/43/bb/e1c71a4295b1b1d1393d50dbb4f2a36283c6859d9d3892e84f00ec5a91d5/numpy-2.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:0c9136e14ed34a9e343a31c533d78a9813a69a3148332bce5e9821cb2f996e66", size = 10565867, upload-time = "2026-05-18T23:36:47.114Z" }, + { url = "https://files.pythonhosted.org/packages/de/12/b422cc84439adc0d00de605bf4a308890ae5c26f2c71fbd73e5d08fbb0dd/numpy-2.4.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:55cced7c52e981362f708ad635198e97a752dfba412cc03c23bbf3bd8d5cd662", size = 16847511, upload-time = "2026-05-18T23:36:50.673Z" }, + { url = "https://files.pythonhosted.org/packages/44/53/f481bef68011740f8849418d82db07230e825013f31f4eef5ba5b805316a/numpy-2.4.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d6da64deb6b8ed903e7560180a92f2d804ee1ba5eeb849ac2748b8c1aba1f6d7", size = 14889064, upload-time = "2026-05-18T23:36:53.879Z" }, + { url = "https://files.pythonhosted.org/packages/7f/57/42ed575c10ced8af951d426bc4e1f8aff16fd851db33f067036215a7f860/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:68a5124b13fa6cc2086764a20005d30bc0548146f7f5322f02fce212ca14317f", size = 5394157, upload-time = "2026-05-18T23:36:57.194Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ef/f66cc724fcc36c1e364c67f51ae9146090b8b584f27d58b97fdae3edd737/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:948424b06129ce883307e8cff868c31396d8dc7630a59c61d70d98dbe70f222c", size = 6708728, upload-time = "2026-05-18T23:36:59.575Z" }, + { url = "https://files.pythonhosted.org/packages/1a/9c/c531f2293b91265d8b48e9b329f54fdd7ffae73cb4134ea10cca4237e9cc/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbbdb29840ca3d91ee0fece42fc29278886d908280bfec0a5846c6f901a3eb0", size = 15798374, upload-time = "2026-05-18T23:37:02.674Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b0/413077f6b1153ed3cba361401c6783bbad6114804a000cc22eb71c13e190/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ad03c0965fb3c692200e74d458ca28c1dbb4ce96f9a479a8aa041ad5fabca02", size = 16747286, upload-time = "2026-05-18T23:37:06.327Z" }, + { url = "https://files.pythonhosted.org/packages/15/ce/e5ec180bc41812edcd8daeb8639d205622c0e8c02259d8ab25a0201b3c2a/numpy-2.4.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2803abfebfc990042cd494d8ce2d5f82e9d847af6d35ec486923aa19dbad5e73", size = 12504263, upload-time = "2026-05-18T23:37:09.715Z" }, +] + +[[package]] +name = "nvidia-cublas-cu12" +version = "12.8.4.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" }, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" }, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.8.93" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" }, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" }, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.10.2.21" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, +] + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.3.3.83" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, +] + +[[package]] +name = "nvidia-cufile-cu12" +version = "1.13.1.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" }, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.9.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" }, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.7.3.90" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12" }, + { name = "nvidia-cusparse-cu12" }, + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, +] + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.5.8.93" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, +] + +[[package]] +name = "nvidia-cusparselt-cu12" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" }, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.27.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229, upload-time = "2025-06-26T04:11:28.385Z" }, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.8.93" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" }, +] + +[[package]] +name = "nvidia-nvshmem-cu12" +version = "3.4.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/09/6ea3ea725f82e1e76684f0708bbedd871fc96da89945adeba65c3835a64c/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd", size = 139103095, upload-time = "2025-09-06T00:32:31.266Z" }, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" }, +] + +[[package]] +name = "packaging" +version = "26.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/47/e4501f49c178ae1d9f4a75073fda4204f52647993f075a9db4d14930e0c5/platformdirs-4.10.0.tar.gz", hash = "sha256:31e761a6a0ca04faf7353ea759bdba55652be214725111e5aac52dfa29d4bef7", size = 31224, upload-time = "2026-05-28T03:32:53.587Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/e6/cd9575ac904136b3cbf7aa7ee819ef86eedb7274e46f230e94ea4342e729/platformdirs-4.10.0-py3-none-any.whl", hash = "sha256:fb516cdb12eb0d857d0cd85a7c57cea4d060bee4578d6cf5a14dfdf8cbf8784a", size = 22743, upload-time = "2026-05-28T03:32:52.175Z" }, +] + +[[package]] +name = "protobuf" +version = "7.35.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/60/fd/5b1491d9e4b586d621c54f4c36b888714164b6875f8d6afa3f9072906a51/protobuf-7.35.0.tar.gz", hash = "sha256:a2efd84605f41e559f1881b0912b44099d0a2ac9bf46b3474823f10fb393b0e6", size = 458677, upload-time = "2026-05-19T23:02:29.197Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/ee/93d06e358a4aa32280b00e722d3ea0a1f25fc3cc5778d80581c9cca2c10e/protobuf-7.35.0-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:66be6c513931c794fa92c080ffee41671390da3d79da219cf9c0c0907f035dda", size = 433225, upload-time = "2026-05-19T23:02:19.884Z" }, + { url = "https://files.pythonhosted.org/packages/8b/39/1c76c2da93f3c507e958e0aecee2391cc44d4625de6c728bbc555195b5a8/protobuf-7.35.0-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:fcbe42a4ac09d3ec9c987ddfcd956afd0b15f1ff613bd8371bde9405ffd5c8e5", size = 328847, upload-time = "2026-05-19T23:02:22.3Z" }, + { url = "https://files.pythonhosted.org/packages/91/1a/39f7ce90a238c1a987a4d81ec26379e02ca0aff367de68e4a1fa474215b9/protobuf-7.35.0-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:4cbf5cc286130e06a6c9bbefac442431173906dfcc979712183d4adcc01b37ee", size = 344030, upload-time = "2026-05-19T23:02:23.591Z" }, + { url = "https://files.pythonhosted.org/packages/70/5b/6baf9008817964454055ff3fe65f1de0b5f1e26c80c82f7fb108b7cd4ea3/protobuf-7.35.0-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:6c0f98f10c8a05ea30f8993dfef2de093d27b490fdae78bb60c8343795d55011", size = 327130, upload-time = "2026-05-19T23:02:24.637Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e5/e46adb0badc388bfb84877a5f9f026aff63f60e611016cf64dbe77e05446/protobuf-7.35.0-cp310-abi3-win32.whl", hash = "sha256:4c4617b83ade0e279d1d2bfe04025a1adb87f9ed657de038620dc0ff959357f6", size = 428946, upload-time = "2026-05-19T23:02:25.741Z" }, + { url = "https://files.pythonhosted.org/packages/a7/ab/547fbd9e16d879dd13c167478f8ae0a83a428008ca07a5e06acdc23ad473/protobuf-7.35.0-cp310-abi3-win_amd64.whl", hash = "sha256:f05bcadf9a2a6b8dda047007075135fb7d08c73d9177aabc067e1be46881a201", size = 439996, upload-time = "2026-05-19T23:02:26.808Z" }, + { url = "https://files.pythonhosted.org/packages/b8/ef/50433d346c56657a70d27f156c7b349ac59a068b01de4eb796e747eecc43/protobuf-7.35.0-py3-none-any.whl", hash = "sha256:c13f325cf242bad135c350629eeb5d54b24228eb472fb3e2e9ebbd4c5dc20ca0", size = 171659, upload-time = "2026-05-19T23:02:27.842Z" }, +] + +[[package]] +name = "pufferlib" +version = "4.0.0" +source = { editable = "." } +dependencies = [ + { name = "gpytorch" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pybind11" }, + { name = "rich" }, + { name = "rich-argparse" }, + { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scikit-learn", version = "1.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "setuptools" }, + { name = "torch" }, + { name = "wandb" }, +] + +[package.metadata] +requires-dist = [ + { name = "gpytorch" }, + { name = "numpy" }, + { name = "pybind11" }, + { name = "rich" }, + { name = "rich-argparse" }, + { name = "scikit-learn" }, + { name = "setuptools" }, + { name = "torch", specifier = ">=2.9" }, + { name = "wandb" }, +] + +[[package]] +name = "pybind11" +version = "3.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/f0/35145a3c3baffeef55d4b8324caa33abaa8fa56ab345ecd4b2211d09163e/pybind11-3.0.4.tar.gz", hash = "sha256:3286b59c8a774b9ee650169302dd5a4eedc30a8617905a0560dd8ee44775130c", size = 589533, upload-time = "2026-04-19T03:08:15.925Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/06/c3a23c9a0263b136c519f033a58d4641e73065fefc7754e9667ec206d992/pybind11-3.0.4-py3-none-any.whl", hash = "sha256:961720ee652da51d531b7b2451a6bd2bc042b0106e6d9baa48ecb7d58034ce63", size = 314166, upload-time = "2026-04-19T03:08:14.091Z" }, +] + +[[package]] +name = "pydantic" +version = "2.13.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.46.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/08/f1ba952f1c8ae5581c70fa9c6da89f247b83e3dd8c09c035d5d7931fc23d/pydantic_core-2.46.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4", size = 2113146, upload-time = "2026-05-06T13:37:36.537Z" }, + { url = "https://files.pythonhosted.org/packages/56/c6/65f646c7ff09bd257f660434adb45c4dfcbbcebcc030562fecf6f5bf887d/pydantic_core-2.46.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5", size = 1949769, upload-time = "2026-05-06T13:37:46.365Z" }, + { url = "https://files.pythonhosted.org/packages/64/ba/bfb1d928fd5b49e1258935ff104ae356e9fd89384a55bf9f847e9193ad40/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb63e0198ca18aad131c089b9204c23079c3afa95487e561f4c522d519e55aba", size = 1974958, upload-time = "2026-05-06T13:37:28.611Z" }, + { url = "https://files.pythonhosted.org/packages/4e/74/76223bfb117b64af743c9b6670d1364516f5c0604f96b48f3272f6af6cc6/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f47286a97f0bc9b8859519809077b91b2cefe4ae47fcbf5e466a009c1c5d742b", size = 2042118, upload-time = "2026-05-06T13:36:55.216Z" }, + { url = "https://files.pythonhosted.org/packages/cb/7b/848732968bc8f48f3187542f08358b9d842db564147b256669426ebb1652/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:905a0ed8ea6f2d61c1738835f99b699348d7857379083e5fc497fa0c967a407c", size = 2222876, upload-time = "2026-05-06T13:38:25.455Z" }, + { url = "https://files.pythonhosted.org/packages/b5/2f/e90b63ee2e14bd8d3db8f705a6d75d64e6ee1b7c2c8833747ce706e1e0ce/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea793e075b70290d89d8142074262885d3f7da19634845135751bd6344f73b50", size = 2286703, upload-time = "2026-05-06T13:37:53.304Z" }, + { url = "https://files.pythonhosted.org/packages/ba/1e/acc4d70f88a0a277e4a1fa77ebb985ceabaf900430f875bf9338e11c9420/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395aebd9183f9d112f569aeb5b2214d1a10a33bec8456447f7fbdfa51d38d4cd", size = 2092042, upload-time = "2026-05-06T13:38:46.981Z" }, + { url = "https://files.pythonhosted.org/packages/a9/da/0a422b57bf8504102bf3c4ccea9c41bab5a5cee6a54650acf8faf67f5a24/pydantic_core-2.46.4-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:b078afbc25f3a1436c7a1d2cd3e322497ee99615ba97c563566fdf46aff1ee01", size = 2117231, upload-time = "2026-05-06T13:39:23.146Z" }, + { url = "https://files.pythonhosted.org/packages/bd/2a/2ac13c3af305843e23c5078c53d135656b3f05a2fd78cb7bbbb12e97b473/pydantic_core-2.46.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f747929cf940cddb5b3668a390056ddd5ba2e5010615ea2dcf4f9c4f3ab8791d", size = 2168388, upload-time = "2026-05-06T13:40:08.06Z" }, + { url = "https://files.pythonhosted.org/packages/72/04/2beacf7e1607e93eefe4aed1b4709f079b905fb77530179d4f7c71745f22/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:daa27d92c36f24388fe3ad306b174781c747627f134452e4f128ea00ce1fe8c4", size = 2184769, upload-time = "2026-05-06T13:38:13.901Z" }, + { url = "https://files.pythonhosted.org/packages/9e/29/d2b9fd9f539133548eaf622c06a4ce176cb46ac59f32d0359c4abc0de047/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:19e51f073cd3df251856a8a4189fbdf1de4012c3ebacfb1884f94f1eb406079f", size = 2319312, upload-time = "2026-05-06T13:39:08.24Z" }, + { url = "https://files.pythonhosted.org/packages/7c/af/0f7a5b85fec6075bea96e3ef9187de38fccced0de92c1e7feda8d5cc7bb9/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1747f85cee84c26985853c6f3d9bd3e75da5212912443fa111c113b9c246f39", size = 2361817, upload-time = "2026-05-06T13:38:43.2Z" }, + { url = "https://files.pythonhosted.org/packages/25/a4/73363fec545fd3ec025490bdda2743c56d0dd5b6266b1a53bbe9e4265375/pydantic_core-2.46.4-cp310-cp310-win32.whl", hash = "sha256:2f84c03c8607173d16b5a854ec68a2f9079ae03237a54fb506d13af47e1d018d", size = 1987085, upload-time = "2026-05-06T13:39:25.497Z" }, + { url = "https://files.pythonhosted.org/packages/01/aa/62f082da2c91fac1c234bc9ee0066257ce83f0604abd72e4c9d5991f2d84/pydantic_core-2.46.4-cp310-cp310-win_amd64.whl", hash = "sha256:8358a950c8909158e3df31538a7e4edc2d7265a7c54b47f0864d9e5bae9dcebf", size = 2074311, upload-time = "2026-05-06T13:39:59.922Z" }, + { url = "https://files.pythonhosted.org/packages/5c/fa/6d7708d2cfc1a832acb6aeb0cd16e801902df8a0f583bb3b4b527fde022e/pydantic_core-2.46.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594", size = 2111872, upload-time = "2026-05-06T13:40:27.596Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6f/aa064a3e74b5745afbdf250594f38e7ead05e2d651bcb35994b9417a0d4d/pydantic_core-2.46.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c", size = 1948255, upload-time = "2026-05-06T13:39:12.574Z" }, + { url = "https://files.pythonhosted.org/packages/43/3a/41114a9f7569b84b4d84e7a018c57c56347dac30c0d4a872946ec4e36c46/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826", size = 1972827, upload-time = "2026-05-06T13:38:19.841Z" }, + { url = "https://files.pythonhosted.org/packages/ef/25/1ab42e8048fe551934d9884e8d64daa7e990ad386f310a15981aeb6a5b08/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04", size = 2041051, upload-time = "2026-05-06T13:38:10.447Z" }, + { url = "https://files.pythonhosted.org/packages/94/c2/1a934597ddf08da410385b3b7aae91956a5a76c635effef456074fad7e88/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e", size = 2221314, upload-time = "2026-05-06T13:40:13.089Z" }, + { url = "https://files.pythonhosted.org/packages/02/6d/9e8ad178c9c4df27ad3c8f25d1fe2a7ab0d2ba0559fad4aee5d3d1f16771/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3", size = 2285146, upload-time = "2026-05-06T13:38:59.224Z" }, + { url = "https://files.pythonhosted.org/packages/80/50/540cd3aeefc041beb111125c4bff779831a2111fc6b15a9138cda277d32c/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4", size = 2089685, upload-time = "2026-05-06T13:38:17.762Z" }, + { url = "https://files.pythonhosted.org/packages/6b/a4/b440ad35f05f6a38f89fa0f149accb3f0e02be94ca5e15f3c449a61b4bc9/pydantic_core-2.46.4-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398", size = 2115420, upload-time = "2026-05-06T13:37:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/99/61/de4f55db8dfd57bfdfa9a12ec90fe1b57c4f41062f7ca86f08586b3e0ac0/pydantic_core-2.46.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3", size = 2165122, upload-time = "2026-05-06T13:37:01.167Z" }, + { url = "https://files.pythonhosted.org/packages/f7/52/7c529d7bdb2d1068bd52f51fe32572c8301f9a4febf1948f10639f1436f5/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848", size = 2182573, upload-time = "2026-05-06T13:38:45.04Z" }, + { url = "https://files.pythonhosted.org/packages/37/b3/7c40325848ba78247f2812dcf9c7274e38cd801820ca6dd9fe63bcfb0eb4/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3", size = 2317139, upload-time = "2026-05-06T13:37:15.539Z" }, + { url = "https://files.pythonhosted.org/packages/d9/37/f913f81a657c865b75da6c0dbed79876073c2a43b5bd9edbe8da785e4d49/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109", size = 2360433, upload-time = "2026-05-06T13:37:30.099Z" }, + { url = "https://files.pythonhosted.org/packages/c4/67/6acaa1be2567f9256b056d8477158cac7240813956ce86e49deae8e173b4/pydantic_core-2.46.4-cp311-cp311-win32.whl", hash = "sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda", size = 1985513, upload-time = "2026-05-06T13:38:15.669Z" }, + { url = "https://files.pythonhosted.org/packages/aa/e6/c505f83dfeda9a2e5c995cfd872949e4d05e12f7feb3dca72f633daefa94/pydantic_core-2.46.4-cp311-cp311-win_amd64.whl", hash = "sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33", size = 2071114, upload-time = "2026-05-06T13:40:35.416Z" }, + { url = "https://files.pythonhosted.org/packages/0f/da/7a263a96d965d9d0df5e8de8a475f33495451117035b09acb110288c381f/pydantic_core-2.46.4-cp311-cp311-win_arm64.whl", hash = "sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d", size = 2044298, upload-time = "2026-05-06T13:38:29.754Z" }, + { url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" }, + { url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" }, + { url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" }, + { url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" }, + { url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" }, + { url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" }, + { url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" }, + { url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" }, + { url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" }, + { url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" }, + { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" }, + { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" }, + { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" }, + { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" }, + { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" }, + { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" }, + { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" }, + { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" }, + { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" }, + { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" }, + { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" }, + { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" }, + { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" }, + { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" }, + { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" }, + { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" }, + { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" }, + { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" }, + { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" }, + { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" }, + { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" }, + { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" }, + { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" }, + { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" }, + { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" }, + { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" }, + { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" }, + { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" }, + { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" }, + { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" }, + { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" }, + { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" }, + { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" }, + { url = "https://files.pythonhosted.org/packages/ee/a4/73995fd4ebbb46ba0ee51e6fa049b8f02c40daebb762208feda8a6b7894d/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c", size = 2111589, upload-time = "2026-05-06T13:37:10.817Z" }, + { url = "https://files.pythonhosted.org/packages/fb/7f/f37d3a5e8bfcc2e403f5c57a730f2d815693fb42119e8ea48b3789335af1/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b", size = 1944552, upload-time = "2026-05-06T13:36:56.717Z" }, + { url = "https://files.pythonhosted.org/packages/15/3c/d7eb777b3ff43e8433a4efb39a17aa8fd98a4ee8561a24a67ef5db07b2d6/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b", size = 1982984, upload-time = "2026-05-06T13:39:06.207Z" }, + { url = "https://files.pythonhosted.org/packages/63/87/70b9f40170a81afd55ca26c9b2acb25c20d64bcfbf888fafecb3ba077d4c/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea", size = 2138417, upload-time = "2026-05-06T13:39:45.476Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" }, + { url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" }, + { url = "https://files.pythonhosted.org/packages/11/cb/428de0385b6c8d44b716feba566abfacfbd23ee3c4439faa789a1456242f/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0", size = 2112782, upload-time = "2026-05-06T13:37:04.016Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b5/6a17bdadd0fc1f170adfd05a20d37c832f52b117b4d9131da1f41bb097ce/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7", size = 1952146, upload-time = "2026-05-06T13:39:43.092Z" }, + { url = "https://files.pythonhosted.org/packages/2a/dc/03734d80e362cd43ef65428e9de77c730ce7f2f11c60d2b1e1b39f0fbf99/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2", size = 2134492, upload-time = "2026-05-06T13:36:58.124Z" }, + { url = "https://files.pythonhosted.org/packages/de/df/5e5ffc085ed07cc22d298134d3d911c63e91f6a0eb91fe646750a3209910/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9", size = 2156604, upload-time = "2026-05-06T13:37:49.88Z" }, + { url = "https://files.pythonhosted.org/packages/81/44/6e112a4253e56f5705467cbab7ab5e91ee7398ba3d56d358635958893d3e/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf", size = 2183828, upload-time = "2026-05-06T13:37:43.053Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ad/5565071e937d8e752842ac241463944c9eb14c87e2d269f2658a5bd05e98/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30", size = 2310000, upload-time = "2026-05-06T13:37:56.694Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c3/66883a5cec183e7fba4d024b4cbbe61851a63750ef606b0afecc46d1f2bf/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc", size = 2361286, upload-time = "2026-05-06T13:40:05.667Z" }, + { url = "https://files.pythonhosted.org/packages/4b/2d/69abac8f838090bbecd5df894befb2c2619e7996a98ddb949db9f3b93225/pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983", size = 2193071, upload-time = "2026-05-06T13:38:08.682Z" }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, + { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, + { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, + { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { 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" }, +] + +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, +] + +[[package]] +name = "rich" +version = "15.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, +] + +[[package]] +name = "rich-argparse" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "rich" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/e5/1064c43203a357d668cd42435f7a15fe6af51512d85b2104fecb937aa861/rich_argparse-1.8.0.tar.gz", hash = "sha256:679df3d832fa94ad6e4bdb07ded088cd7ea2dddc58ae9b2b46346a40b06cbc0c", size = 38940, upload-time = "2026-05-01T15:18:43.604Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/35/1cceccc5fcb50fa2ed53e2aa278cd032f3902682a73e763fb1ac3be8e6fa/rich_argparse-1.8.0-py3-none-any.whl", hash = "sha256:d2a3ce7854654e2253c578763ab0a32f05016f23a55fadba7b9a91b6c0e92142", size = 25616, upload-time = "2026-05-01T15:18:42.395Z" }, +] + +[[package]] +name = "scikit-learn" +version = "1.7.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { 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 = [ + { url = "https://files.pythonhosted.org/packages/ba/3e/daed796fd69cce768b8788401cc464ea90b306fb196ae1ffed0b98182859/scikit_learn-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b33579c10a3081d076ab403df4a4190da4f4432d443521674637677dc91e61f", size = 9336221, upload-time = "2025-09-09T08:20:19.328Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ce/af9d99533b24c55ff4e18d9b7b4d9919bbc6cd8f22fe7a7be01519a347d5/scikit_learn-1.7.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:36749fb62b3d961b1ce4fedf08fa57a1986cd409eff2d783bca5d4b9b5fce51c", size = 8653834, upload-time = "2025-09-09T08:20:22.073Z" }, + { url = "https://files.pythonhosted.org/packages/58/0e/8c2a03d518fb6bd0b6b0d4b114c63d5f1db01ff0f9925d8eb10960d01c01/scikit_learn-1.7.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7a58814265dfc52b3295b1900cfb5701589d30a8bb026c7540f1e9d3499d5ec8", size = 9660938, upload-time = "2025-09-09T08:20:24.327Z" }, + { url = "https://files.pythonhosted.org/packages/2b/75/4311605069b5d220e7cf5adabb38535bd96f0079313cdbb04b291479b22a/scikit_learn-1.7.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a847fea807e278f821a0406ca01e387f97653e284ecbd9750e3ee7c90347f18", size = 9477818, upload-time = "2025-09-09T08:20:26.845Z" }, + { url = "https://files.pythonhosted.org/packages/7f/9b/87961813c34adbca21a6b3f6b2bea344c43b30217a6d24cc437c6147f3e8/scikit_learn-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:ca250e6836d10e6f402436d6463d6c0e4d8e0234cfb6a9a47835bd392b852ce5", size = 8886969, upload-time = "2025-09-09T08:20:29.329Z" }, + { url = "https://files.pythonhosted.org/packages/43/83/564e141eef908a5863a54da8ca342a137f45a0bfb71d1d79704c9894c9d1/scikit_learn-1.7.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7509693451651cd7361d30ce4e86a1347493554f172b1c72a39300fa2aea79e", size = 9331967, upload-time = "2025-09-09T08:20:32.421Z" }, + { url = "https://files.pythonhosted.org/packages/18/d6/ba863a4171ac9d7314c4d3fc251f015704a2caeee41ced89f321c049ed83/scikit_learn-1.7.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:0486c8f827c2e7b64837c731c8feff72c0bd2b998067a8a9cbc10643c31f0fe1", size = 8648645, upload-time = "2025-09-09T08:20:34.436Z" }, + { url = "https://files.pythonhosted.org/packages/ef/0e/97dbca66347b8cf0ea8b529e6bb9367e337ba2e8be0ef5c1a545232abfde/scikit_learn-1.7.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89877e19a80c7b11a2891a27c21c4894fb18e2c2e077815bcade10d34287b20d", size = 9715424, upload-time = "2025-09-09T08:20:36.776Z" }, + { url = "https://files.pythonhosted.org/packages/f7/32/1f3b22e3207e1d2c883a7e09abb956362e7d1bd2f14458c7de258a26ac15/scikit_learn-1.7.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8da8bf89d4d79aaec192d2bda62f9b56ae4e5b4ef93b6a56b5de4977e375c1f1", size = 9509234, upload-time = "2025-09-09T08:20:38.957Z" }, + { url = "https://files.pythonhosted.org/packages/9f/71/34ddbd21f1da67c7a768146968b4d0220ee6831e4bcbad3e03dd3eae88b6/scikit_learn-1.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:9b7ed8d58725030568523e937c43e56bc01cadb478fc43c042a9aca1dacb3ba1", size = 8894244, upload-time = "2025-09-09T08:20:41.166Z" }, + { url = "https://files.pythonhosted.org/packages/a7/aa/3996e2196075689afb9fce0410ebdb4a09099d7964d061d7213700204409/scikit_learn-1.7.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8d91a97fa2b706943822398ab943cde71858a50245e31bc71dba62aab1d60a96", size = 9259818, upload-time = "2025-09-09T08:20:43.19Z" }, + { url = "https://files.pythonhosted.org/packages/43/5d/779320063e88af9c4a7c2cf463ff11c21ac9c8bd730c4a294b0000b666c9/scikit_learn-1.7.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:acbc0f5fd2edd3432a22c69bed78e837c70cf896cd7993d71d51ba6708507476", size = 8636997, upload-time = "2025-09-09T08:20:45.468Z" }, + { url = "https://files.pythonhosted.org/packages/5c/d0/0c577d9325b05594fdd33aa970bf53fb673f051a45496842caee13cfd7fe/scikit_learn-1.7.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e5bf3d930aee75a65478df91ac1225ff89cd28e9ac7bd1196853a9229b6adb0b", size = 9478381, upload-time = "2025-09-09T08:20:47.982Z" }, + { url = "https://files.pythonhosted.org/packages/82/70/8bf44b933837ba8494ca0fc9a9ab60f1c13b062ad0197f60a56e2fc4c43e/scikit_learn-1.7.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4d6e9deed1a47aca9fe2f267ab8e8fe82ee20b4526b2c0cd9e135cea10feb44", size = 9300296, upload-time = "2025-09-09T08:20:50.366Z" }, + { url = "https://files.pythonhosted.org/packages/c6/99/ed35197a158f1fdc2fe7c3680e9c70d0128f662e1fee4ed495f4b5e13db0/scikit_learn-1.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:6088aa475f0785e01bcf8529f55280a3d7d298679f50c0bb70a2364a82d0b290", size = 8731256, upload-time = "2025-09-09T08:20:52.627Z" }, + { url = "https://files.pythonhosted.org/packages/ae/93/a3038cb0293037fd335f77f31fe053b89c72f17b1c8908c576c29d953e84/scikit_learn-1.7.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0b7dacaa05e5d76759fb071558a8b5130f4845166d88654a0f9bdf3eb57851b7", size = 9212382, upload-time = "2025-09-09T08:20:54.731Z" }, + { url = "https://files.pythonhosted.org/packages/40/dd/9a88879b0c1104259136146e4742026b52df8540c39fec21a6383f8292c7/scikit_learn-1.7.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:abebbd61ad9e1deed54cca45caea8ad5f79e1b93173dece40bb8e0c658dbe6fe", size = 8592042, upload-time = "2025-09-09T08:20:57.313Z" }, + { url = "https://files.pythonhosted.org/packages/46/af/c5e286471b7d10871b811b72ae794ac5fe2989c0a2df07f0ec723030f5f5/scikit_learn-1.7.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:502c18e39849c0ea1a5d681af1dbcf15f6cce601aebb657aabbfe84133c1907f", size = 9434180, upload-time = "2025-09-09T08:20:59.671Z" }, + { url = "https://files.pythonhosted.org/packages/f1/fd/df59faa53312d585023b2da27e866524ffb8faf87a68516c23896c718320/scikit_learn-1.7.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a4c328a71785382fe3fe676a9ecf2c86189249beff90bf85e22bdb7efaf9ae0", size = 9283660, upload-time = "2025-09-09T08:21:01.71Z" }, + { url = "https://files.pythonhosted.org/packages/a7/c7/03000262759d7b6f38c836ff9d512f438a70d8a8ddae68ee80de72dcfb63/scikit_learn-1.7.2-cp313-cp313-win_amd64.whl", hash = "sha256:63a9afd6f7b229aad94618c01c252ce9e6fa97918c5ca19c9a17a087d819440c", size = 8702057, upload-time = "2025-09-09T08:21:04.234Z" }, + { url = "https://files.pythonhosted.org/packages/55/87/ef5eb1f267084532c8e4aef98a28b6ffe7425acbfd64b5e2f2e066bc29b3/scikit_learn-1.7.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9acb6c5e867447b4e1390930e3944a005e2cb115922e693c08a323421a6966e8", size = 9558731, upload-time = "2025-09-09T08:21:06.381Z" }, + { url = "https://files.pythonhosted.org/packages/93/f8/6c1e3fc14b10118068d7938878a9f3f4e6d7b74a8ddb1e5bed65159ccda8/scikit_learn-1.7.2-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:2a41e2a0ef45063e654152ec9d8bcfc39f7afce35b08902bfe290c2498a67a6a", size = 9038852, upload-time = "2025-09-09T08:21:08.628Z" }, + { url = "https://files.pythonhosted.org/packages/83/87/066cafc896ee540c34becf95d30375fe5cbe93c3b75a0ee9aa852cd60021/scikit_learn-1.7.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:98335fb98509b73385b3ab2bd0639b1f610541d3988ee675c670371d6a87aa7c", size = 9527094, upload-time = "2025-09-09T08:21:11.486Z" }, + { url = "https://files.pythonhosted.org/packages/9c/2b/4903e1ccafa1f6453b1ab78413938c8800633988c838aa0be386cbb33072/scikit_learn-1.7.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:191e5550980d45449126e23ed1d5e9e24b2c68329ee1f691a3987476e115e09c", size = 9367436, upload-time = "2025-09-09T08:21:13.602Z" }, + { url = "https://files.pythonhosted.org/packages/b5/aa/8444be3cfb10451617ff9d177b3c190288f4563e6c50ff02728be67ad094/scikit_learn-1.7.2-cp313-cp313t-win_amd64.whl", hash = "sha256:57dc4deb1d3762c75d685507fbd0bc17160144b2f2ba4ccea5dc285ab0d0e973", size = 9275749, upload-time = "2025-09-09T08:21:15.96Z" }, + { url = "https://files.pythonhosted.org/packages/d9/82/dee5acf66837852e8e68df6d8d3a6cb22d3df997b733b032f513d95205b7/scikit_learn-1.7.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fa8f63940e29c82d1e67a45d5297bdebbcb585f5a5a50c4914cc2e852ab77f33", size = 9208906, upload-time = "2025-09-09T08:21:18.557Z" }, + { url = "https://files.pythonhosted.org/packages/3c/30/9029e54e17b87cb7d50d51a5926429c683d5b4c1732f0507a6c3bed9bf65/scikit_learn-1.7.2-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:f95dc55b7902b91331fa4e5845dd5bde0580c9cd9612b1b2791b7e80c3d32615", size = 8627836, upload-time = "2025-09-09T08:21:20.695Z" }, + { url = "https://files.pythonhosted.org/packages/60/18/4a52c635c71b536879f4b971c2cedf32c35ee78f48367885ed8025d1f7ee/scikit_learn-1.7.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9656e4a53e54578ad10a434dc1f993330568cfee176dff07112b8785fb413106", size = 9426236, upload-time = "2025-09-09T08:21:22.645Z" }, + { url = "https://files.pythonhosted.org/packages/99/7e/290362f6ab582128c53445458a5befd471ed1ea37953d5bcf80604619250/scikit_learn-1.7.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96dc05a854add0e50d3f47a1ef21a10a595016da5b007c7d9cd9d0bffd1fcc61", size = 9312593, upload-time = "2025-09-09T08:21:24.65Z" }, + { url = "https://files.pythonhosted.org/packages/8e/87/24f541b6d62b1794939ae6422f8023703bbf6900378b2b34e0b4384dfefd/scikit_learn-1.7.2-cp314-cp314-win_amd64.whl", hash = "sha256:bb24510ed3f9f61476181e4db51ce801e2ba37541def12dc9333b946fc7a9cf8", size = 8820007, upload-time = "2025-09-09T08:21:26.713Z" }, +] + +[[package]] +name = "scikit-learn" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", +] +dependencies = [ + { name = "joblib", marker = "python_full_version >= '3.11'" }, + { name = "narwhals", marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.17.1", 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/fa/6f/37092bdb25f712817231799fc5674d8e704066a8a70c1d2d40517e18b4ab/scikit_learn-1.9.0.tar.gz", hash = "sha256:8833266989d3a5110178a9fae30783675460724d0e1efb13b14901d2c660c557", size = 7750767, upload-time = "2026-06-02T11:54:32.706Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/be/e844fd9586e66540a15b71924d17a6cbc1bb749e81ddd0a796bcdba4c055/scikit_learn-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9db6f4d34e68c8899e4cab27fdf8eafe6ed21f2ba52ceb25ea250cd237f8e47b", size = 8789686, upload-time = "2026-06-02T11:53:05.439Z" }, + { url = "https://files.pythonhosted.org/packages/42/e2/ff880f62677a17d035817d543cb0fc8727d01eccbee81c5f7fc733a9d856/scikit_learn-1.9.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f401448645a3e7bc115aa3c094097865155b34bff1cba8101857d9104e99074c", size = 8256782, upload-time = "2026-06-02T11:53:08.904Z" }, + { url = "https://files.pythonhosted.org/packages/25/64/eb40435e1a508ab1b4e284ce43ae80f6a162e5be5e38ed5a6fab467a9ea4/scikit_learn-1.9.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fd3a8ef0c758555a3b23c03adaa858af32f7736785ded50ad5991f59c4ed03fa", size = 8992419, upload-time = "2026-06-02T11:53:11.551Z" }, + { url = "https://files.pythonhosted.org/packages/8d/da/4810a28e473185429e45a57eebcc91fc991b33d889cc0676063e671db03d/scikit_learn-1.9.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7e254636164090da847715a27f8e5478feb98c40a9e0ee90cbd277de9e5ceb8", size = 9281411, upload-time = "2026-06-02T11:53:15.063Z" }, + { url = "https://files.pythonhosted.org/packages/3b/67/be3d369f40d8178ba3bd86635d132e08cb5329b023e4669d9426d84bc007/scikit_learn-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:5dc1818c77575d149e25fce9ef82dd7b7263ae372f03494158668ad632a69759", size = 8272736, upload-time = "2026-06-02T11:53:18.108Z" }, + { url = "https://files.pythonhosted.org/packages/37/79/a733f02dc2118da7e77a134b34f39f40201a353311b011d20859d2db3556/scikit_learn-1.9.0-cp311-cp311-win_arm64.whl", hash = "sha256:366652351f092b219c248f1e72821e841960a63d8f358f1dcfd54dc1cbdbbc28", size = 7919564, upload-time = "2026-06-02T11:53:21.2Z" }, + { url = "https://files.pythonhosted.org/packages/ac/20/75f915ff375d6249e6550ac740fdbbd66159a068fd3af1400ff62036b07a/scikit_learn-1.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2bd41b0d201bc81575531b96b713d3eb5e5f50fb0b82101ff0f92294fdc236ac", size = 8741122, upload-time = "2026-06-02T11:53:24.08Z" }, + { url = "https://files.pythonhosted.org/packages/cc/d5/2b5148f2279196775e1db2aeb85d14b70ac80e7e32b3b28e7ebeafb0901d/scikit_learn-1.9.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5be45aa4a42a68a533913a6ed736cf309de2226411c79ef8d609a5456f1939b1", size = 8261512, upload-time = "2026-06-02T11:53:27.183Z" }, + { url = "https://files.pythonhosted.org/packages/a0/ee/5adbc77656b71f9456a2f5a7a9fdb4bcf9207a6b962889f1c2f9323afa4e/scikit_learn-1.9.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e50ed4da51974e86e940690e9a3d82e729b62b5a49f7c9bac534d515d39d86f", size = 8837603, upload-time = "2026-06-02T11:53:30.328Z" }, + { url = "https://files.pythonhosted.org/packages/6c/c2/63fdda36c56437eeb44aaf9493c8bcd62ce230ab1598924fc626ffbfa943/scikit_learn-1.9.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:056c92bb67ad4c28463c2f2653d9701449201e7e7a9e94e321be0f71c4fef2b8", size = 9132097, upload-time = "2026-06-02T11:53:33.456Z" }, + { url = "https://files.pythonhosted.org/packages/83/a4/c8e67227c680e2259c8864ae72ff48b06e16a6f51253a22167aa02a8aa4e/scikit_learn-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:4306775fad04cc4b472a1b15af1ae9cede1540fbfcc17fbce3767cd8dc7ae283", size = 8211173, upload-time = "2026-06-02T11:53:36.602Z" }, + { url = "https://files.pythonhosted.org/packages/cf/fd/3c0863792e98e67e9184aa4029288a175935eb65443afcd30d4f143450cf/scikit_learn-1.9.0-cp312-cp312-win_arm64.whl", hash = "sha256:26e22435f63bcdcf396b574273f29f13dd531f5ea035801f5be10ba1540a4e60", size = 7867451, upload-time = "2026-06-02T11:53:39.075Z" }, + { url = "https://files.pythonhosted.org/packages/3c/01/cf3310626b6d48d3e9be69a1223f9180360b5e6edb045f50fade723ce494/scikit_learn-1.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:80746d63bd4b6eaca54d36fe5feaf4d28bb38dc6f9470f81c7cad7c40155f119", size = 8705188, upload-time = "2026-06-02T11:53:41.964Z" }, + { url = "https://files.pythonhosted.org/packages/3e/04/5acd7ae280c5f93b6ac5ef6cdec14eef4c8d1cd91d85b3292989c94d96b1/scikit_learn-1.9.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:5b934c45c252844a91d69fda3a34cff5e7307e1db10d77cb10a3980312c74713", size = 8228299, upload-time = "2026-06-02T11:53:44.817Z" }, + { url = "https://files.pythonhosted.org/packages/0c/39/ffe829a5b8ecb40a518724a997794657fdc354ada5e8fe8e64d998c0bac9/scikit_learn-1.9.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:38c3dcb9a1ffb85505ec53d54c7b4aea0cff70050425a7760c2af661ac85df05", size = 8789690, upload-time = "2026-06-02T11:53:47.461Z" }, + { url = "https://files.pythonhosted.org/packages/1f/88/8dab5de10c638c083772a6be83a3d8106ced492f74a928c8693638e5bb50/scikit_learn-1.9.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da76d09304a4706db7cc1e3ebaa3b6b98a67365cc11d2996c4f1e58ba47df714", size = 9087723, upload-time = "2026-06-02T11:53:50.702Z" }, + { url = "https://files.pythonhosted.org/packages/20/3f/7917ca72464038f6240ec70c29f94862d08a34a74291ae4d4ec5eb8186a0/scikit_learn-1.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:5808d98f15c6bf6d9d96d2348c1997392a5888ce7097e664105f930c4bca1277", size = 8184330, upload-time = "2026-06-02T11:53:53.396Z" }, + { url = "https://files.pythonhosted.org/packages/78/c7/15739eb2f61fda3c54639e9942414e5a19ad8a8d1f5a3266afad7cb7df80/scikit_learn-1.9.0-cp313-cp313-win_arm64.whl", hash = "sha256:d77f54c017633791bc0225a43e2f8d03745fdcfe4880268fcc4df15f505dec2e", size = 7840653, upload-time = "2026-06-02T11:53:56.035Z" }, + { url = "https://files.pythonhosted.org/packages/f4/7d/c9a35cf59b20a86fec24d306f1547b78dec194b08d367ce2a3e4854169d9/scikit_learn-1.9.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9656acd4e93f74e0b66c8a36c88830a99252dfa900044d36bc2212ae89a47162", size = 8713289, upload-time = "2026-06-02T11:53:58.788Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a7/552a7821597c632b907f7bfe8f36f9f572777af8ef8a48353041cf8e091a/scikit_learn-1.9.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:24360002ae845e7866522b0a5bbf690802e7bc388cac8663502e78aa98598aa2", size = 8245141, upload-time = "2026-06-02T11:54:01.694Z" }, + { url = "https://files.pythonhosted.org/packages/7d/79/f4a0c4fe9711154cddabf913471153af79056382ddc612cfe5ee0ff4b72e/scikit_learn-1.9.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5162ad10a418c8a282dde04c9aa06965de3e9a65f33c1440c0ae69bb1a09d913", size = 8847671, upload-time = "2026-06-02T11:54:04.448Z" }, + { url = "https://files.pythonhosted.org/packages/f0/af/4d72d9e475ac83719160c662619e4bf7b95c19507cd582e7d0167a3c3dae/scikit_learn-1.9.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fea2cc5677ab49d6f5bade978c866da44957b712d92e9635e8b4f723013c3cb", size = 9118104, upload-time = "2026-06-02T11:54:07.205Z" }, + { url = "https://files.pythonhosted.org/packages/a2/d5/6a58eea2cb9abbb9b3f2bb8b2cfb3243d1152d69f442d256c7af71304769/scikit_learn-1.9.0-cp314-cp314-win_amd64.whl", hash = "sha256:64fa347efc1c839c487433e40c5144d38c336e8a2b59c81aa8660373945c2673", size = 8290674, upload-time = "2026-06-02T11:54:10.087Z" }, + { url = "https://files.pythonhosted.org/packages/65/5b/d4c879cf358f1187141cf90ced473f087183489090244f50c124a2ee478b/scikit_learn-1.9.0-cp314-cp314-win_arm64.whl", hash = "sha256:1b944b6db288f6b926e3650026ddafb988929de95d11fc2cc5fa117773c9ba42", size = 7978807, upload-time = "2026-06-02T11:54:12.769Z" }, + { url = "https://files.pythonhosted.org/packages/8a/43/bfae3121ec67ae09150d453c442c7c1cc166e9aefe056e6ab3b7728a5cfc/scikit_learn-1.9.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:4ccacf04ca5f4b492158a5f28afe0ace43f81b2571e4b9a66d34848b46128949", size = 9031941, upload-time = "2026-06-02T11:54:15.436Z" }, + { url = "https://files.pythonhosted.org/packages/75/b0/20a4546eb17f3b25d3c66df15810411c14ed5065bcfab50b53c96fb627b2/scikit_learn-1.9.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:ee1a8db2c18c08e34c7412d4b10be1cac214cd4ea7dc9715a6a327eb49a37c96", size = 8613528, upload-time = "2026-06-02T11:54:18.842Z" }, + { url = "https://files.pythonhosted.org/packages/18/3c/e440e039bb82cd19004edaaad00acbde0fb9b461083c3ecf37941c557312/scikit_learn-1.9.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:147e9329ef0e39f75d4cffa02b2aa48d827832684926cd5210d9a2cb5c57246b", size = 8855050, upload-time = "2026-06-02T11:54:21.699Z" }, + { url = "https://files.pythonhosted.org/packages/43/26/b341b8dab5998da6270a3a42c2152c578501354d36f944b5856757035ef8/scikit_learn-1.9.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bad8f8b9950321b54c965fdcbac6c6c55e79e16646b49977bcf3668d3870a1a", size = 9097190, upload-time = "2026-06-02T11:54:24.454Z" }, + { url = "https://files.pythonhosted.org/packages/fb/de/b650b4d69b84468cfa2e28a3ff7b8103743029e6446ce1a97fe060ef688c/scikit_learn-1.9.0-cp314-cp314t-win_amd64.whl", hash = "sha256:78fc56eafd4edb9575d2d8950d1dd152061abb573341a1cb7e099fc40f6c6666", size = 8963204, upload-time = "2026-06-02T11:54:27.428Z" }, + { url = "https://files.pythonhosted.org/packages/ee/f3/ff83d76d7418112e5a61326443cdda87be3545dd8d6599c95b2481a4419e/scikit_learn-1.9.0-cp314-cp314t-win_arm64.whl", hash = "sha256:051075bda8b7aab87b1906ab3d4740a1e1224a19d7b3781a576736edc94e76aa", size = 8222661, upload-time = "2026-06-02T11:54:30.192Z" }, +] + +[[package]] +name = "scipy" +version = "1.15.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { 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 = [ + { url = "https://files.pythonhosted.org/packages/78/2f/4966032c5f8cc7e6a60f1b2e0ad686293b9474b65246b0c642e3ef3badd0/scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c", size = 38702770, upload-time = "2025-05-08T16:04:20.849Z" }, + { url = "https://files.pythonhosted.org/packages/a0/6e/0c3bf90fae0e910c274db43304ebe25a6b391327f3f10b5dcc638c090795/scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253", size = 30094511, upload-time = "2025-05-08T16:04:27.103Z" }, + { url = "https://files.pythonhosted.org/packages/ea/b1/4deb37252311c1acff7f101f6453f0440794f51b6eacb1aad4459a134081/scipy-1.15.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:aef683a9ae6eb00728a542b796f52a5477b78252edede72b8327a886ab63293f", size = 22368151, upload-time = "2025-05-08T16:04:31.731Z" }, + { url = "https://files.pythonhosted.org/packages/38/7d/f457626e3cd3c29b3a49ca115a304cebb8cc6f31b04678f03b216899d3c6/scipy-1.15.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:1c832e1bd78dea67d5c16f786681b28dd695a8cb1fb90af2e27580d3d0967e92", size = 25121732, upload-time = "2025-05-08T16:04:36.596Z" }, + { url = "https://files.pythonhosted.org/packages/db/0a/92b1de4a7adc7a15dcf5bddc6e191f6f29ee663b30511ce20467ef9b82e4/scipy-1.15.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:263961f658ce2165bbd7b99fa5135195c3a12d9bef045345016b8b50c315cb82", size = 35547617, upload-time = "2025-05-08T16:04:43.546Z" }, + { url = "https://files.pythonhosted.org/packages/8e/6d/41991e503e51fc1134502694c5fa7a1671501a17ffa12716a4a9151af3df/scipy-1.15.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2abc762b0811e09a0d3258abee2d98e0c703eee49464ce0069590846f31d40", size = 37662964, upload-time = "2025-05-08T16:04:49.431Z" }, + { url = "https://files.pythonhosted.org/packages/25/e1/3df8f83cb15f3500478c889be8fb18700813b95e9e087328230b98d547ff/scipy-1.15.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed7284b21a7a0c8f1b6e5977ac05396c0d008b89e05498c8b7e8f4a1423bba0e", size = 37238749, upload-time = "2025-05-08T16:04:55.215Z" }, + { url = "https://files.pythonhosted.org/packages/93/3e/b3257cf446f2a3533ed7809757039016b74cd6f38271de91682aa844cfc5/scipy-1.15.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5380741e53df2c566f4d234b100a484b420af85deb39ea35a1cc1be84ff53a5c", size = 40022383, upload-time = "2025-05-08T16:05:01.914Z" }, + { url = "https://files.pythonhosted.org/packages/d1/84/55bc4881973d3f79b479a5a2e2df61c8c9a04fcb986a213ac9c02cfb659b/scipy-1.15.3-cp310-cp310-win_amd64.whl", hash = "sha256:9d61e97b186a57350f6d6fd72640f9e99d5a4a2b8fbf4b9ee9a841eab327dc13", size = 41259201, upload-time = "2025-05-08T16:05:08.166Z" }, + { url = "https://files.pythonhosted.org/packages/96/ab/5cc9f80f28f6a7dff646c5756e559823614a42b1939d86dd0ed550470210/scipy-1.15.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:993439ce220d25e3696d1b23b233dd010169b62f6456488567e830654ee37a6b", size = 38714255, upload-time = "2025-05-08T16:05:14.596Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4a/66ba30abe5ad1a3ad15bfb0b59d22174012e8056ff448cb1644deccbfed2/scipy-1.15.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:34716e281f181a02341ddeaad584205bd2fd3c242063bd3423d61ac259ca7eba", size = 30111035, upload-time = "2025-05-08T16:05:20.152Z" }, + { url = "https://files.pythonhosted.org/packages/4b/fa/a7e5b95afd80d24313307f03624acc65801846fa75599034f8ceb9e2cbf6/scipy-1.15.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3b0334816afb8b91dab859281b1b9786934392aa3d527cd847e41bb6f45bee65", size = 22384499, upload-time = "2025-05-08T16:05:24.494Z" }, + { url = "https://files.pythonhosted.org/packages/17/99/f3aaddccf3588bb4aea70ba35328c204cadd89517a1612ecfda5b2dd9d7a/scipy-1.15.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:6db907c7368e3092e24919b5e31c76998b0ce1684d51a90943cb0ed1b4ffd6c1", size = 25152602, upload-time = "2025-05-08T16:05:29.313Z" }, + { url = "https://files.pythonhosted.org/packages/56/c5/1032cdb565f146109212153339f9cb8b993701e9fe56b1c97699eee12586/scipy-1.15.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721d6b4ef5dc82ca8968c25b111e307083d7ca9091bc38163fb89243e85e3889", size = 35503415, upload-time = "2025-05-08T16:05:34.699Z" }, + { url = "https://files.pythonhosted.org/packages/bd/37/89f19c8c05505d0601ed5650156e50eb881ae3918786c8fd7262b4ee66d3/scipy-1.15.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39cb9c62e471b1bb3750066ecc3a3f3052b37751c7c3dfd0fd7e48900ed52982", size = 37652622, upload-time = "2025-05-08T16:05:40.762Z" }, + { url = "https://files.pythonhosted.org/packages/7e/31/be59513aa9695519b18e1851bb9e487de66f2d31f835201f1b42f5d4d475/scipy-1.15.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:795c46999bae845966368a3c013e0e00947932d68e235702b5c3f6ea799aa8c9", size = 37244796, upload-time = "2025-05-08T16:05:48.119Z" }, + { url = "https://files.pythonhosted.org/packages/10/c0/4f5f3eeccc235632aab79b27a74a9130c6c35df358129f7ac8b29f562ac7/scipy-1.15.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:18aaacb735ab38b38db42cb01f6b92a2d0d4b6aabefeb07f02849e47f8fb3594", size = 40047684, upload-time = "2025-05-08T16:05:54.22Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a7/0ddaf514ce8a8714f6ed243a2b391b41dbb65251affe21ee3077ec45ea9a/scipy-1.15.3-cp311-cp311-win_amd64.whl", hash = "sha256:ae48a786a28412d744c62fd7816a4118ef97e5be0bee968ce8f0a2fba7acf3bb", size = 41246504, upload-time = "2025-05-08T16:06:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/37/4b/683aa044c4162e10ed7a7ea30527f2cbd92e6999c10a8ed8edb253836e9c/scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019", size = 38766735, upload-time = "2025-05-08T16:06:06.471Z" }, + { url = "https://files.pythonhosted.org/packages/7b/7e/f30be3d03de07f25dc0ec926d1681fed5c732d759ac8f51079708c79e680/scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6", size = 30173284, upload-time = "2025-05-08T16:06:11.686Z" }, + { url = "https://files.pythonhosted.org/packages/07/9c/0ddb0d0abdabe0d181c1793db51f02cd59e4901da6f9f7848e1f96759f0d/scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477", size = 22446958, upload-time = "2025-05-08T16:06:15.97Z" }, + { url = "https://files.pythonhosted.org/packages/af/43/0bce905a965f36c58ff80d8bea33f1f9351b05fad4beaad4eae34699b7a1/scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c", size = 25242454, upload-time = "2025-05-08T16:06:20.394Z" }, + { url = "https://files.pythonhosted.org/packages/56/30/a6f08f84ee5b7b28b4c597aca4cbe545535c39fe911845a96414700b64ba/scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45", size = 35210199, upload-time = "2025-05-08T16:06:26.159Z" }, + { url = "https://files.pythonhosted.org/packages/0b/1f/03f52c282437a168ee2c7c14a1a0d0781a9a4a8962d84ac05c06b4c5b555/scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49", size = 37309455, upload-time = "2025-05-08T16:06:32.778Z" }, + { url = "https://files.pythonhosted.org/packages/89/b1/fbb53137f42c4bf630b1ffdfc2151a62d1d1b903b249f030d2b1c0280af8/scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e", size = 36885140, upload-time = "2025-05-08T16:06:39.249Z" }, + { url = "https://files.pythonhosted.org/packages/2e/2e/025e39e339f5090df1ff266d021892694dbb7e63568edcfe43f892fa381d/scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539", size = 39710549, upload-time = "2025-05-08T16:06:45.729Z" }, + { url = "https://files.pythonhosted.org/packages/e6/eb/3bf6ea8ab7f1503dca3a10df2e4b9c3f6b3316df07f6c0ded94b281c7101/scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed", size = 40966184, upload-time = "2025-05-08T16:06:52.623Z" }, + { url = "https://files.pythonhosted.org/packages/73/18/ec27848c9baae6e0d6573eda6e01a602e5649ee72c27c3a8aad673ebecfd/scipy-1.15.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759", size = 38728256, upload-time = "2025-05-08T16:06:58.696Z" }, + { url = "https://files.pythonhosted.org/packages/74/cd/1aef2184948728b4b6e21267d53b3339762c285a46a274ebb7863c9e4742/scipy-1.15.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62", size = 30109540, upload-time = "2025-05-08T16:07:04.209Z" }, + { url = "https://files.pythonhosted.org/packages/5b/d8/59e452c0a255ec352bd0a833537a3bc1bfb679944c4938ab375b0a6b3a3e/scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb", size = 22383115, upload-time = "2025-05-08T16:07:08.998Z" }, + { url = "https://files.pythonhosted.org/packages/08/f5/456f56bbbfccf696263b47095291040655e3cbaf05d063bdc7c7517f32ac/scipy-1.15.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730", size = 25163884, upload-time = "2025-05-08T16:07:14.091Z" }, + { url = "https://files.pythonhosted.org/packages/a2/66/a9618b6a435a0f0c0b8a6d0a2efb32d4ec5a85f023c2b79d39512040355b/scipy-1.15.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825", size = 35174018, upload-time = "2025-05-08T16:07:19.427Z" }, + { url = "https://files.pythonhosted.org/packages/b5/09/c5b6734a50ad4882432b6bb7c02baf757f5b2f256041da5df242e2d7e6b6/scipy-1.15.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7", size = 37269716, upload-time = "2025-05-08T16:07:25.712Z" }, + { url = "https://files.pythonhosted.org/packages/77/0a/eac00ff741f23bcabd352731ed9b8995a0a60ef57f5fd788d611d43d69a1/scipy-1.15.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11", size = 36872342, upload-time = "2025-05-08T16:07:31.468Z" }, + { url = "https://files.pythonhosted.org/packages/fe/54/4379be86dd74b6ad81551689107360d9a3e18f24d20767a2d5b9253a3f0a/scipy-1.15.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126", size = 39670869, upload-time = "2025-05-08T16:07:38.002Z" }, + { url = "https://files.pythonhosted.org/packages/87/2e/892ad2862ba54f084ffe8cc4a22667eaf9c2bcec6d2bff1d15713c6c0703/scipy-1.15.3-cp313-cp313-win_amd64.whl", hash = "sha256:b90ab29d0c37ec9bf55424c064312930ca5f4bde15ee8619ee44e69319aab163", size = 40988851, upload-time = "2025-05-08T16:08:33.671Z" }, + { url = "https://files.pythonhosted.org/packages/1b/e9/7a879c137f7e55b30d75d90ce3eb468197646bc7b443ac036ae3fe109055/scipy-1.15.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8", size = 38863011, upload-time = "2025-05-08T16:07:44.039Z" }, + { url = "https://files.pythonhosted.org/packages/51/d1/226a806bbd69f62ce5ef5f3ffadc35286e9fbc802f606a07eb83bf2359de/scipy-1.15.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5", size = 30266407, upload-time = "2025-05-08T16:07:49.891Z" }, + { url = "https://files.pythonhosted.org/packages/e5/9b/f32d1d6093ab9eeabbd839b0f7619c62e46cc4b7b6dbf05b6e615bbd4400/scipy-1.15.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e", size = 22540030, upload-time = "2025-05-08T16:07:54.121Z" }, + { url = "https://files.pythonhosted.org/packages/e7/29/c278f699b095c1a884f29fda126340fcc201461ee8bfea5c8bdb1c7c958b/scipy-1.15.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb", size = 25218709, upload-time = "2025-05-08T16:07:58.506Z" }, + { url = "https://files.pythonhosted.org/packages/24/18/9e5374b617aba742a990581373cd6b68a2945d65cc588482749ef2e64467/scipy-1.15.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723", size = 34809045, upload-time = "2025-05-08T16:08:03.929Z" }, + { url = "https://files.pythonhosted.org/packages/e1/fe/9c4361e7ba2927074360856db6135ef4904d505e9b3afbbcb073c4008328/scipy-1.15.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb", size = 36703062, upload-time = "2025-05-08T16:08:09.558Z" }, + { url = "https://files.pythonhosted.org/packages/b7/8e/038ccfe29d272b30086b25a4960f757f97122cb2ec42e62b460d02fe98e9/scipy-1.15.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4", size = 36393132, upload-time = "2025-05-08T16:08:15.34Z" }, + { url = "https://files.pythonhosted.org/packages/10/7e/5c12285452970be5bdbe8352c619250b97ebf7917d7a9a9e96b8a8140f17/scipy-1.15.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5", size = 38979503, upload-time = "2025-05-08T16:08:21.513Z" }, + { url = "https://files.pythonhosted.org/packages/81/06/0a5e5349474e1cbc5757975b21bd4fad0e72ebf138c5592f191646154e06/scipy-1.15.3-cp313-cp313t-win_amd64.whl", hash = "sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca", size = 40308097, upload-time = "2025-05-08T16:08:27.627Z" }, +] + +[[package]] +name = "scipy" +version = "1.17.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", +] +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/75/b4ce781849931fef6fd529afa6b63711d5a733065722d0c3e2724af9e40a/scipy-1.17.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1f95b894f13729334fb990162e911c9e5dc1ab390c58aa6cbecb389c5b5e28ec", size = 31613675, upload-time = "2026-02-23T00:16:00.13Z" }, + { url = "https://files.pythonhosted.org/packages/f7/58/bccc2861b305abdd1b8663d6130c0b3d7cc22e8d86663edbc8401bfd40d4/scipy-1.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e18f12c6b0bc5a592ed23d3f7b891f68fd7f8241d69b7883769eb5d5dfb52696", size = 28162057, upload-time = "2026-02-23T00:16:09.456Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ee/18146b7757ed4976276b9c9819108adbc73c5aad636e5353e20746b73069/scipy-1.17.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a3472cfbca0a54177d0faa68f697d8ba4c80bbdc19908c3465556d9f7efce9ee", size = 20334032, upload-time = "2026-02-23T00:16:17.358Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e6/cef1cf3557f0c54954198554a10016b6a03b2ec9e22a4e1df734936bd99c/scipy-1.17.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:766e0dc5a616d026a3a1cffa379af959671729083882f50307e18175797b3dfd", size = 22709533, upload-time = "2026-02-23T00:16:25.791Z" }, + { url = "https://files.pythonhosted.org/packages/4d/60/8804678875fc59362b0fb759ab3ecce1f09c10a735680318ac30da8cd76b/scipy-1.17.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:744b2bf3640d907b79f3fd7874efe432d1cf171ee721243e350f55234b4cec4c", size = 33062057, upload-time = "2026-02-23T00:16:36.931Z" }, + { url = "https://files.pythonhosted.org/packages/09/7d/af933f0f6e0767995b4e2d705a0665e454d1c19402aa7e895de3951ebb04/scipy-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43af8d1f3bea642559019edfe64e9b11192a8978efbd1539d7bc2aaa23d92de4", size = 35349300, upload-time = "2026-02-23T00:16:49.108Z" }, + { url = "https://files.pythonhosted.org/packages/b4/3d/7ccbbdcbb54c8fdc20d3b6930137c782a163fa626f0aef920349873421ba/scipy-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd96a1898c0a47be4520327e01f874acfd61fb48a9420f8aa9f6483412ffa444", size = 35127333, upload-time = "2026-02-23T00:17:01.293Z" }, + { url = "https://files.pythonhosted.org/packages/e8/19/f926cb11c42b15ba08e3a71e376d816ac08614f769b4f47e06c3580c836a/scipy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4eb6c25dd62ee8d5edf68a8e1c171dd71c292fdae95d8aeb3dd7d7de4c364082", size = 37741314, upload-time = "2026-02-23T00:17:12.576Z" }, + { url = "https://files.pythonhosted.org/packages/95/da/0d1df507cf574b3f224ccc3d45244c9a1d732c81dcb26b1e8a766ae271a8/scipy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:d30e57c72013c2a4fe441c2fcb8e77b14e152ad48b5464858e07e2ad9fbfceff", size = 36607512, upload-time = "2026-02-23T00:17:23.424Z" }, + { url = "https://files.pythonhosted.org/packages/68/7f/bdd79ceaad24b671543ffe0ef61ed8e659440eb683b66f033454dcee90eb/scipy-1.17.1-cp311-cp311-win_arm64.whl", hash = "sha256:9ecb4efb1cd6e8c4afea0daa91a87fbddbce1b99d2895d151596716c0b2e859d", size = 24599248, upload-time = "2026-02-23T00:17:34.561Z" }, + { url = "https://files.pythonhosted.org/packages/35/48/b992b488d6f299dbe3f11a20b24d3dda3d46f1a635ede1c46b5b17a7b163/scipy-1.17.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:35c3a56d2ef83efc372eaec584314bd0ef2e2f0d2adb21c55e6ad5b344c0dcb8", size = 31610954, upload-time = "2026-02-23T00:17:49.855Z" }, + { url = "https://files.pythonhosted.org/packages/b2/02/cf107b01494c19dc100f1d0b7ac3cc08666e96ba2d64db7626066cee895e/scipy-1.17.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fcb310ddb270a06114bb64bbe53c94926b943f5b7f0842194d585c65eb4edd76", size = 28172662, upload-time = "2026-02-23T00:18:01.64Z" }, + { url = "https://files.pythonhosted.org/packages/cf/a9/599c28631bad314d219cf9ffd40e985b24d603fc8a2f4ccc5ae8419a535b/scipy-1.17.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cc90d2e9c7e5c7f1a482c9875007c095c3194b1cfedca3c2f3291cdc2bc7c086", size = 20344366, upload-time = "2026-02-23T00:18:12.015Z" }, + { url = "https://files.pythonhosted.org/packages/35/f5/906eda513271c8deb5af284e5ef0206d17a96239af79f9fa0aebfe0e36b4/scipy-1.17.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:c80be5ede8f3f8eded4eff73cc99a25c388ce98e555b17d31da05287015ffa5b", size = 22704017, upload-time = "2026-02-23T00:18:21.502Z" }, + { url = "https://files.pythonhosted.org/packages/da/34/16f10e3042d2f1d6b66e0428308ab52224b6a23049cb2f5c1756f713815f/scipy-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e19ebea31758fac5893a2ac360fedd00116cbb7628e650842a6691ba7ca28a21", size = 32927842, upload-time = "2026-02-23T00:18:35.367Z" }, + { url = "https://files.pythonhosted.org/packages/01/8e/1e35281b8ab6d5d72ebe9911edcdffa3f36b04ed9d51dec6dd140396e220/scipy-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02ae3b274fde71c5e92ac4d54bc06c42d80e399fec704383dcd99b301df37458", size = 35235890, upload-time = "2026-02-23T00:18:49.188Z" }, + { url = "https://files.pythonhosted.org/packages/c5/5c/9d7f4c88bea6e0d5a4f1bc0506a53a00e9fcb198de372bfe4d3652cef482/scipy-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a604bae87c6195d8b1045eddece0514d041604b14f2727bbc2b3020172045eb", size = 35003557, upload-time = "2026-02-23T00:18:54.74Z" }, + { url = "https://files.pythonhosted.org/packages/65/94/7698add8f276dbab7a9de9fb6b0e02fc13ee61d51c7c3f85ac28b65e1239/scipy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f590cd684941912d10becc07325a3eeb77886fe981415660d9265c4c418d0bea", size = 37625856, upload-time = "2026-02-23T00:19:00.307Z" }, + { url = "https://files.pythonhosted.org/packages/a2/84/dc08d77fbf3d87d3ee27f6a0c6dcce1de5829a64f2eae85a0ecc1f0daa73/scipy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:41b71f4a3a4cab9d366cd9065b288efc4d4f3c0b37a91a8e0947fb5bd7f31d87", size = 36549682, upload-time = "2026-02-23T00:19:07.67Z" }, + { url = "https://files.pythonhosted.org/packages/bc/98/fe9ae9ffb3b54b62559f52dedaebe204b408db8109a8c66fdd04869e6424/scipy-1.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:f4115102802df98b2b0db3cce5cb9b92572633a1197c77b7553e5203f284a5b3", size = 24547340, upload-time = "2026-02-23T00:19:12.024Z" }, + { url = "https://files.pythonhosted.org/packages/76/27/07ee1b57b65e92645f219b37148a7e7928b82e2b5dbeccecb4dff7c64f0b/scipy-1.17.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5e3c5c011904115f88a39308379c17f91546f77c1667cea98739fe0fccea804c", size = 31590199, upload-time = "2026-02-23T00:19:17.192Z" }, + { url = "https://files.pythonhosted.org/packages/ec/ae/db19f8ab842e9b724bf5dbb7db29302a91f1e55bc4d04b1025d6d605a2c5/scipy-1.17.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6fac755ca3d2c3edcb22f479fceaa241704111414831ddd3bc6056e18516892f", size = 28154001, upload-time = "2026-02-23T00:19:22.241Z" }, + { url = "https://files.pythonhosted.org/packages/5b/58/3ce96251560107b381cbd6e8413c483bbb1228a6b919fa8652b0d4090e7f/scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:7ff200bf9d24f2e4d5dc6ee8c3ac64d739d3a89e2326ba68aaf6c4a2b838fd7d", size = 20325719, upload-time = "2026-02-23T00:19:26.329Z" }, + { url = "https://files.pythonhosted.org/packages/b2/83/15087d945e0e4d48ce2377498abf5ad171ae013232ae31d06f336e64c999/scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4b400bdc6f79fa02a4d86640310dde87a21fba0c979efff5248908c6f15fad1b", size = 22683595, upload-time = "2026-02-23T00:19:30.304Z" }, + { url = "https://files.pythonhosted.org/packages/b4/e0/e58fbde4a1a594c8be8114eb4aac1a55bcd6587047efc18a61eb1f5c0d30/scipy-1.17.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b64ca7d4aee0102a97f3ba22124052b4bd2152522355073580bf4845e2550b6", size = 32896429, upload-time = "2026-02-23T00:19:35.536Z" }, + { url = "https://files.pythonhosted.org/packages/f5/5f/f17563f28ff03c7b6799c50d01d5d856a1d55f2676f537ca8d28c7f627cd/scipy-1.17.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:581b2264fc0aa555f3f435a5944da7504ea3a065d7029ad60e7c3d1ae09c5464", size = 35203952, upload-time = "2026-02-23T00:19:42.259Z" }, + { url = "https://files.pythonhosted.org/packages/8d/a5/9afd17de24f657fdfe4df9a3f1ea049b39aef7c06000c13db1530d81ccca/scipy-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:beeda3d4ae615106d7094f7e7cef6218392e4465cc95d25f900bebabfded0950", size = 34979063, upload-time = "2026-02-23T00:19:47.547Z" }, + { url = "https://files.pythonhosted.org/packages/8b/13/88b1d2384b424bf7c924f2038c1c409f8d88bb2a8d49d097861dd64a57b2/scipy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6609bc224e9568f65064cfa72edc0f24ee6655b47575954ec6339534b2798369", size = 37598449, upload-time = "2026-02-23T00:19:53.238Z" }, + { url = "https://files.pythonhosted.org/packages/35/e5/d6d0e51fc888f692a35134336866341c08655d92614f492c6860dc45bb2c/scipy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:37425bc9175607b0268f493d79a292c39f9d001a357bebb6b88fdfaff13f6448", size = 36510943, upload-time = "2026-02-23T00:20:50.89Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fd/3be73c564e2a01e690e19cc618811540ba5354c67c8680dce3281123fb79/scipy-1.17.1-cp313-cp313-win_arm64.whl", hash = "sha256:5cf36e801231b6a2059bf354720274b7558746f3b1a4efb43fcf557ccd484a87", size = 24545621, upload-time = "2026-02-23T00:20:55.871Z" }, + { url = "https://files.pythonhosted.org/packages/6f/6b/17787db8b8114933a66f9dcc479a8272e4b4da75fe03b0c282f7b0ade8cd/scipy-1.17.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:d59c30000a16d8edc7e64152e30220bfbd724c9bbb08368c054e24c651314f0a", size = 31936708, upload-time = "2026-02-23T00:19:58.694Z" }, + { url = "https://files.pythonhosted.org/packages/38/2e/524405c2b6392765ab1e2b722a41d5da33dc5c7b7278184a8ad29b6cb206/scipy-1.17.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:010f4333c96c9bb1a4516269e33cb5917b08ef2166d5556ca2fd9f082a9e6ea0", size = 28570135, upload-time = "2026-02-23T00:20:03.934Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c3/5bd7199f4ea8556c0c8e39f04ccb014ac37d1468e6cfa6a95c6b3562b76e/scipy-1.17.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2ceb2d3e01c5f1d83c4189737a42d9cb2fc38a6eeed225e7515eef71ad301dce", size = 20741977, upload-time = "2026-02-23T00:20:07.935Z" }, + { url = "https://files.pythonhosted.org/packages/d9/b8/8ccd9b766ad14c78386599708eb745f6b44f08400a5fd0ade7cf89b6fc93/scipy-1.17.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:844e165636711ef41f80b4103ed234181646b98a53c8f05da12ca5ca289134f6", size = 23029601, upload-time = "2026-02-23T00:20:12.161Z" }, + { url = "https://files.pythonhosted.org/packages/6d/a0/3cb6f4d2fb3e17428ad2880333cac878909ad1a89f678527b5328b93c1d4/scipy-1.17.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:158dd96d2207e21c966063e1635b1063cd7787b627b6f07305315dd73d9c679e", size = 33019667, upload-time = "2026-02-23T00:20:17.208Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c3/2d834a5ac7bf3a0c806ad1508efc02dda3c8c61472a56132d7894c312dea/scipy-1.17.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74cbb80d93260fe2ffa334efa24cb8f2f0f622a9b9febf8b483c0b865bfb3475", size = 35264159, upload-time = "2026-02-23T00:20:23.087Z" }, + { url = "https://files.pythonhosted.org/packages/4d/77/d3ed4becfdbd217c52062fafe35a72388d1bd82c2d0ba5ca19d6fcc93e11/scipy-1.17.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dbc12c9f3d185f5c737d801da555fb74b3dcfa1a50b66a1a93e09190f41fab50", size = 35102771, upload-time = "2026-02-23T00:20:28.636Z" }, + { url = "https://files.pythonhosted.org/packages/bd/12/d19da97efde68ca1ee5538bb261d5d2c062f0c055575128f11a2730e3ac1/scipy-1.17.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94055a11dfebe37c656e70317e1996dc197e1a15bbcc351bcdd4610e128fe1ca", size = 37665910, upload-time = "2026-02-23T00:20:34.743Z" }, + { url = "https://files.pythonhosted.org/packages/06/1c/1172a88d507a4baaf72c5a09bb6c018fe2ae0ab622e5830b703a46cc9e44/scipy-1.17.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e30bdeaa5deed6bc27b4cc490823cd0347d7dae09119b8803ae576ea0ce52e4c", size = 36562980, upload-time = "2026-02-23T00:20:40.575Z" }, + { url = "https://files.pythonhosted.org/packages/70/b0/eb757336e5a76dfa7911f63252e3b7d1de00935d7705cf772db5b45ec238/scipy-1.17.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a720477885a9d2411f94a93d16f9d89bad0f28ca23c3f8daa521e2dcc3f44d49", size = 24856543, upload-time = "2026-02-23T00:20:45.313Z" }, + { url = "https://files.pythonhosted.org/packages/cf/83/333afb452af6f0fd70414dc04f898647ee1423979ce02efa75c3b0f2c28e/scipy-1.17.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:a48a72c77a310327f6a3a920092fa2b8fd03d7deaa60f093038f22d98e096717", size = 31584510, upload-time = "2026-02-23T00:21:01.015Z" }, + { url = "https://files.pythonhosted.org/packages/ed/a6/d05a85fd51daeb2e4ea71d102f15b34fedca8e931af02594193ae4fd25f7/scipy-1.17.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:45abad819184f07240d8a696117a7aacd39787af9e0b719d00285549ed19a1e9", size = 28170131, upload-time = "2026-02-23T00:21:05.888Z" }, + { url = "https://files.pythonhosted.org/packages/db/7b/8624a203326675d7746a254083a187398090a179335b2e4a20e2ddc46e83/scipy-1.17.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:3fd1fcdab3ea951b610dc4cef356d416d5802991e7e32b5254828d342f7b7e0b", size = 20342032, upload-time = "2026-02-23T00:21:09.904Z" }, + { url = "https://files.pythonhosted.org/packages/c9/35/2c342897c00775d688d8ff3987aced3426858fd89d5a0e26e020b660b301/scipy-1.17.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7bdf2da170b67fdf10bca777614b1c7d96ae3ca5794fd9587dce41eb2966e866", size = 22678766, upload-time = "2026-02-23T00:21:14.313Z" }, + { url = "https://files.pythonhosted.org/packages/ef/f2/7cdb8eb308a1a6ae1e19f945913c82c23c0c442a462a46480ce487fdc0ac/scipy-1.17.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:adb2642e060a6549c343603a3851ba76ef0b74cc8c079a9a58121c7ec9fe2350", size = 32957007, upload-time = "2026-02-23T00:21:19.663Z" }, + { url = "https://files.pythonhosted.org/packages/0b/2e/7eea398450457ecb54e18e9d10110993fa65561c4f3add5e8eccd2b9cd41/scipy-1.17.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eee2cfda04c00a857206a4330f0c5e3e56535494e30ca445eb19ec624ae75118", size = 35221333, upload-time = "2026-02-23T00:21:25.278Z" }, + { url = "https://files.pythonhosted.org/packages/d9/77/5b8509d03b77f093a0d52e606d3c4f79e8b06d1d38c441dacb1e26cacf46/scipy-1.17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d2650c1fb97e184d12d8ba010493ee7b322864f7d3d00d3f9bb97d9c21de4068", size = 35042066, upload-time = "2026-02-23T00:21:31.358Z" }, + { url = "https://files.pythonhosted.org/packages/f9/df/18f80fb99df40b4070328d5ae5c596f2f00fffb50167e31439e932f29e7d/scipy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:08b900519463543aa604a06bec02461558a6e1cef8fdbb8098f77a48a83c8118", size = 37612763, upload-time = "2026-02-23T00:21:37.247Z" }, + { url = "https://files.pythonhosted.org/packages/4b/39/f0e8ea762a764a9dc52aa7dabcfad51a354819de1f0d4652b6a1122424d6/scipy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:3877ac408e14da24a6196de0ddcace62092bfc12a83823e92e49e40747e52c19", size = 37290984, upload-time = "2026-02-23T00:22:35.023Z" }, + { url = "https://files.pythonhosted.org/packages/7c/56/fe201e3b0f93d1a8bcf75d3379affd228a63d7e2d80ab45467a74b494947/scipy-1.17.1-cp314-cp314-win_arm64.whl", hash = "sha256:f8885db0bc2bffa59d5c1b72fad7a6a92d3e80e7257f967dd81abb553a90d293", size = 25192877, upload-time = "2026-02-23T00:22:39.798Z" }, + { url = "https://files.pythonhosted.org/packages/96/ad/f8c414e121f82e02d76f310f16db9899c4fcde36710329502a6b2a3c0392/scipy-1.17.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:1cc682cea2ae55524432f3cdff9e9a3be743d52a7443d0cba9017c23c87ae2f6", size = 31949750, upload-time = "2026-02-23T00:21:42.289Z" }, + { url = "https://files.pythonhosted.org/packages/7c/b0/c741e8865d61b67c81e255f4f0a832846c064e426636cd7de84e74d209be/scipy-1.17.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:2040ad4d1795a0ae89bfc7e8429677f365d45aa9fd5e4587cf1ea737f927b4a1", size = 28585858, upload-time = "2026-02-23T00:21:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/ed/1b/3985219c6177866628fa7c2595bfd23f193ceebbe472c98a08824b9466ff/scipy-1.17.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:131f5aaea57602008f9822e2115029b55d4b5f7c070287699fe45c661d051e39", size = 20757723, upload-time = "2026-02-23T00:21:52.039Z" }, + { url = "https://files.pythonhosted.org/packages/c0/19/2a04aa25050d656d6f7b9e7b685cc83d6957fb101665bfd9369ca6534563/scipy-1.17.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9cdc1a2fcfd5c52cfb3045feb399f7b3ce822abdde3a193a6b9a60b3cb5854ca", size = 23043098, upload-time = "2026-02-23T00:21:56.185Z" }, + { url = "https://files.pythonhosted.org/packages/86/f1/3383beb9b5d0dbddd030335bf8a8b32d4317185efe495374f134d8be6cce/scipy-1.17.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e3dcd57ab780c741fde8dc68619de988b966db759a3c3152e8e9142c26295ad", size = 33030397, upload-time = "2026-02-23T00:22:01.404Z" }, + { url = "https://files.pythonhosted.org/packages/41/68/8f21e8a65a5a03f25a79165ec9d2b28c00e66dc80546cf5eb803aeeff35b/scipy-1.17.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9956e4d4f4a301ebf6cde39850333a6b6110799d470dbbb1e25326ac447f52a", size = 35281163, upload-time = "2026-02-23T00:22:07.024Z" }, + { url = "https://files.pythonhosted.org/packages/84/8d/c8a5e19479554007a5632ed7529e665c315ae7492b4f946b0deb39870e39/scipy-1.17.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a4328d245944d09fd639771de275701ccadf5f781ba0ff092ad141e017eccda4", size = 35116291, upload-time = "2026-02-23T00:22:12.585Z" }, + { url = "https://files.pythonhosted.org/packages/52/52/e57eceff0e342a1f50e274264ed47497b59e6a4e3118808ee58ddda7b74a/scipy-1.17.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a77cbd07b940d326d39a1d1b37817e2ee4d79cb30e7338f3d0cddffae70fcaa2", size = 37682317, upload-time = "2026-02-23T00:22:18.513Z" }, + { url = "https://files.pythonhosted.org/packages/11/2f/b29eafe4a3fbc3d6de9662b36e028d5f039e72d345e05c250e121a230dd4/scipy-1.17.1-cp314-cp314t-win_amd64.whl", hash = "sha256:eb092099205ef62cd1782b006658db09e2fed75bffcae7cc0d44052d8aa0f484", size = 37345327, upload-time = "2026-02-23T00:22:24.442Z" }, + { url = "https://files.pythonhosted.org/packages/07/39/338d9219c4e87f3e708f18857ecd24d22a0c3094752393319553096b98af/scipy-1.17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:200e1050faffacc162be6a486a984a0497866ec54149a01270adc8a59b7c7d21", size = 25489165, upload-time = "2026-02-23T00:22:29.563Z" }, +] + +[[package]] +name = "sentry-sdk" +version = "2.61.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/3b/4bc6b348bbd331daa14d4babe9f2b99bc854f4da41560eefb9488d78481d/sentry_sdk-2.61.1.tar.gz", hash = "sha256:9c6adccb3feefa9ba032c8d295ca477575c2f11896046a2b0ad686c47c4af555", size = 459429, upload-time = "2026-06-01T07:24:18.875Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/54/c9218db183846e08efaf68534889ef42e499dde432778881104a42f7071b/sentry_sdk-2.61.1-py3-none-any.whl", hash = "sha256:fa36eaf4b8ad708f718500d4bdcc1532637526a22beb874d88cbc0a46458b5ae", size = 483735, upload-time = "2026-06-01T07:24:17.027Z" }, +] + +[[package]] +name = "setuptools" +version = "82.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" }, +] + +[[package]] +name = "smmap" +version = "5.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/ea/49c993d6dfdd7338c9b1000a0f36817ed7ec84577ae2e52f890d1a4ff909/smmap-5.0.3.tar.gz", hash = "sha256:4d9debb8b99007ae47165abc08670bd74cb74b5227dda7f643eccc4e9eb5642c", size = 22506, upload-time = "2026-03-09T03:43:26.1Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl", hash = "sha256:c106e05d5a61449cf6ba9a1e650227ecfb141590d2a98412103ff35d89fc7b2f", size = 24390, upload-time = "2026-03-09T03:43:24.361Z" }, +] + +[[package]] +name = "sympy" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, +] + +[[package]] +name = "threadpoolctl" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, +] + +[[package]] +name = "torch" +version = "2.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-bindings", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "setuptools", marker = "python_full_version >= '3.12'" }, + { name = "sympy" }, + { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/30/bfebdd8ec77db9a79775121789992d6b3b75ee5494971294d7b4b7c999bc/torch-2.10.0-2-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:2b980edd8d7c0a68c4e951ee1856334a43193f98730d97408fbd148c1a933313", size = 79411457, upload-time = "2026-02-10T21:44:59.189Z" }, + { url = "https://files.pythonhosted.org/packages/0f/8b/4b61d6e13f7108f36910df9ab4b58fd389cc2520d54d81b88660804aad99/torch-2.10.0-2-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:418997cb02d0a0f1497cf6a09f63166f9f5df9f3e16c8a716ab76a72127c714f", size = 79423467, upload-time = "2026-02-10T21:44:48.711Z" }, + { url = "https://files.pythonhosted.org/packages/d3/54/a2ba279afcca44bbd320d4e73675b282fcee3d81400ea1b53934efca6462/torch-2.10.0-2-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:13ec4add8c3faaed8d13e0574f5cd4a323c11655546f91fbe6afa77b57423574", size = 79498202, upload-time = "2026-02-10T21:44:52.603Z" }, + { url = "https://files.pythonhosted.org/packages/ec/23/2c9fe0c9c27f7f6cb865abcea8a4568f29f00acaeadfc6a37f6801f84cb4/torch-2.10.0-2-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:e521c9f030a3774ed770a9c011751fb47c4d12029a3d6522116e48431f2ff89e", size = 79498254, upload-time = "2026-02-10T21:44:44.095Z" }, + { url = "https://files.pythonhosted.org/packages/16/ee/efbd56687be60ef9af0c9c0ebe106964c07400eade5b0af8902a1d8cd58c/torch-2.10.0-3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a1ff626b884f8c4e897c4c33782bdacdff842a165fee79817b1dd549fdda1321", size = 915510070, upload-time = "2026-03-11T14:16:39.386Z" }, + { url = "https://files.pythonhosted.org/packages/36/ab/7b562f1808d3f65414cd80a4f7d4bb00979d9355616c034c171249e1a303/torch-2.10.0-3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ac5bdcbb074384c66fa160c15b1ead77839e3fe7ed117d667249afce0acabfac", size = 915518691, upload-time = "2026-03-11T14:15:43.147Z" }, + { url = "https://files.pythonhosted.org/packages/b3/7a/abada41517ce0011775f0f4eacc79659bc9bc6c361e6bfe6f7052a6b9363/torch-2.10.0-3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:98c01b8bb5e3240426dcde1446eed6f40c778091c8544767ef1168fc663a05a6", size = 915622781, upload-time = "2026-03-11T14:17:11.354Z" }, + { url = "https://files.pythonhosted.org/packages/ab/c6/4dfe238342ffdcec5aef1c96c457548762d33c40b45a1ab7033bb26d2ff2/torch-2.10.0-3-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:80b1b5bfe38eb0e9f5ff09f206dcac0a87aadd084230d4a36eea5ec5232c115b", size = 915627275, upload-time = "2026-03-11T14:16:11.325Z" }, + { url = "https://files.pythonhosted.org/packages/d8/f0/72bf18847f58f877a6a8acf60614b14935e2f156d942483af1ffc081aea0/torch-2.10.0-3-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:46b3574d93a2a8134b3f5475cfb98e2eb46771794c57015f6ad1fb795ec25e49", size = 915523474, upload-time = "2026-03-11T14:17:44.422Z" }, + { url = "https://files.pythonhosted.org/packages/f4/39/590742415c3030551944edc2ddc273ea1fdfe8ffb2780992e824f1ebee98/torch-2.10.0-3-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:b1d5e2aba4eb7f8e87fbe04f86442887f9167a35f092afe4c237dfcaaef6e328", size = 915632474, upload-time = "2026-03-11T14:15:13.666Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8e/34949484f764dde5b222b7fe3fede43e4a6f0da9d7f8c370bb617d629ee2/torch-2.10.0-3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:0228d20b06701c05a8f978357f657817a4a63984b0c90745def81c18aedfa591", size = 915523882, upload-time = "2026-03-11T14:14:46.311Z" }, + { url = "https://files.pythonhosted.org/packages/0c/1a/c61f36cfd446170ec27b3a4984f072fd06dab6b5d7ce27e11adb35d6c838/torch-2.10.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5276fa790a666ee8becaffff8acb711922252521b28fbce5db7db5cf9cb2026d", size = 145992962, upload-time = "2026-01-21T16:24:14.04Z" }, + { url = "https://files.pythonhosted.org/packages/b5/60/6662535354191e2d1555296045b63e4279e5a9dbad49acf55a5d38655a39/torch-2.10.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:aaf663927bcd490ae971469a624c322202a2a1e68936eb952535ca4cd3b90444", size = 915599237, upload-time = "2026-01-21T16:23:25.497Z" }, + { url = "https://files.pythonhosted.org/packages/40/b8/66bbe96f0d79be2b5c697b2e0b187ed792a15c6c4b8904613454651db848/torch-2.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:a4be6a2a190b32ff5c8002a0977a25ea60e64f7ba46b1be37093c141d9c49aeb", size = 113720931, upload-time = "2026-01-21T16:24:23.743Z" }, + { url = "https://files.pythonhosted.org/packages/76/bb/d820f90e69cda6c8169b32a0c6a3ab7b17bf7990b8f2c680077c24a3c14c/torch-2.10.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:35e407430795c8d3edb07a1d711c41cc1f9eaddc8b2f1cc0a165a6767a8fb73d", size = 79411450, upload-time = "2026-01-21T16:25:30.692Z" }, + { url = "https://files.pythonhosted.org/packages/78/89/f5554b13ebd71e05c0b002f95148033e730d3f7067f67423026cc9c69410/torch-2.10.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:3282d9febd1e4e476630a099692b44fdc214ee9bf8ee5377732d9d9dfe5712e4", size = 145992610, upload-time = "2026-01-21T16:25:26.327Z" }, + { url = "https://files.pythonhosted.org/packages/ae/30/a3a2120621bf9c17779b169fc17e3dc29b230c29d0f8222f499f5e159aa8/torch-2.10.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a2f9edd8dbc99f62bc4dfb78af7bf89499bca3d753423ac1b4e06592e467b763", size = 915607863, upload-time = "2026-01-21T16:25:06.696Z" }, + { url = "https://files.pythonhosted.org/packages/6f/3d/c87b33c5f260a2a8ad68da7147e105f05868c281c63d65ed85aa4da98c66/torch-2.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:29b7009dba4b7a1c960260fc8ac85022c784250af43af9fb0ebafc9883782ebd", size = 113723116, upload-time = "2026-01-21T16:25:21.916Z" }, + { url = "https://files.pythonhosted.org/packages/61/d8/15b9d9d3a6b0c01b883787bd056acbe5cc321090d4b216d3ea89a8fcfdf3/torch-2.10.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:b7bd80f3477b830dd166c707c5b0b82a898e7b16f59a7d9d42778dd058272e8b", size = 79423461, upload-time = "2026-01-21T16:24:50.266Z" }, + { url = "https://files.pythonhosted.org/packages/cc/af/758e242e9102e9988969b5e621d41f36b8f258bb4a099109b7a4b4b50ea4/torch-2.10.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:5fd4117d89ffd47e3dcc71e71a22efac24828ad781c7e46aaaf56bf7f2796acf", size = 145996088, upload-time = "2026-01-21T16:24:44.171Z" }, + { url = "https://files.pythonhosted.org/packages/23/8e/3c74db5e53bff7ed9e34c8123e6a8bfef718b2450c35eefab85bb4a7e270/torch-2.10.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:787124e7db3b379d4f1ed54dd12ae7c741c16a4d29b49c0226a89bea50923ffb", size = 915711952, upload-time = "2026-01-21T16:23:53.503Z" }, + { url = "https://files.pythonhosted.org/packages/6e/01/624c4324ca01f66ae4c7cd1b74eb16fb52596dce66dbe51eff95ef9e7a4c/torch-2.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:2c66c61f44c5f903046cc696d088e21062644cbe541c7f1c4eaae88b2ad23547", size = 113757972, upload-time = "2026-01-21T16:24:39.516Z" }, + { url = "https://files.pythonhosted.org/packages/c9/5c/dee910b87c4d5c0fcb41b50839ae04df87c1cfc663cf1b5fca7ea565eeaa/torch-2.10.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:6d3707a61863d1c4d6ebba7be4ca320f42b869ee657e9b2c21c736bf17000294", size = 79498198, upload-time = "2026-01-21T16:24:34.704Z" }, + { url = "https://files.pythonhosted.org/packages/c9/6f/f2e91e34e3fcba2e3fc8d8f74e7d6c22e74e480bbd1db7bc8900fdf3e95c/torch-2.10.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:5c4d217b14741e40776dd7074d9006fd28b8a97ef5654db959d8635b2fe5f29b", size = 146004247, upload-time = "2026-01-21T16:24:29.335Z" }, + { url = "https://files.pythonhosted.org/packages/98/fb/5160261aeb5e1ee12ee95fe599d0541f7c976c3701d607d8fc29e623229f/torch-2.10.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6b71486353fce0f9714ca0c9ef1c850a2ae766b409808acd58e9678a3edb7738", size = 915716445, upload-time = "2026-01-21T16:22:45.353Z" }, + { url = "https://files.pythonhosted.org/packages/6a/16/502fb1b41e6d868e8deb5b0e3ae926bbb36dab8ceb0d1b769b266ad7b0c3/torch-2.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:c2ee399c644dc92ef7bc0d4f7e74b5360c37cdbe7c5ba11318dda49ffac2bc57", size = 113757050, upload-time = "2026-01-21T16:24:19.204Z" }, + { url = "https://files.pythonhosted.org/packages/1a/0b/39929b148f4824bc3ad6f9f72a29d4ad865bcf7ebfc2fa67584773e083d2/torch-2.10.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:3202429f58309b9fa96a614885eace4b7995729f44beb54d3e4a47773649d382", size = 79851305, upload-time = "2026-01-21T16:24:09.209Z" }, + { url = "https://files.pythonhosted.org/packages/d8/14/21fbce63bc452381ba5f74a2c0a959fdf5ad5803ccc0c654e752e0dbe91a/torch-2.10.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:aae1b29cd68e50a9397f5ee897b9c24742e9e306f88a807a27d617f07adb3bd8", size = 146005472, upload-time = "2026-01-21T16:22:29.022Z" }, + { url = "https://files.pythonhosted.org/packages/54/fd/b207d1c525cb570ef47f3e9f836b154685011fce11a2f444ba8a4084d042/torch-2.10.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:6021db85958db2f07ec94e1bc77212721ba4920c12a18dc552d2ae36a3eb163f", size = 915612644, upload-time = "2026-01-21T16:21:47.019Z" }, + { url = "https://files.pythonhosted.org/packages/36/53/0197f868c75f1050b199fe58f9bf3bf3aecac9b4e85cc9c964383d745403/torch-2.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff43db38af76fda183156153983c9a096fc4c78d0cd1e07b14a2314c7f01c2c8", size = 113997015, upload-time = "2026-01-21T16:23:00.767Z" }, + { url = "https://files.pythonhosted.org/packages/0e/13/e76b4d9c160e89fff48bf16b449ea324bda84745d2ab30294c37c2434c0d/torch-2.10.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:cdf2a523d699b70d613243211ecaac14fe9c5df8a0b0a9c02add60fb2a413e0f", size = 79498248, upload-time = "2026-01-21T16:23:09.315Z" }, + { url = "https://files.pythonhosted.org/packages/4f/93/716b5ac0155f1be70ed81bacc21269c3ece8dba0c249b9994094110bfc51/torch-2.10.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:bf0d9ff448b0218e0433aeb198805192346c4fd659c852370d5cc245f602a06a", size = 79464992, upload-time = "2026-01-21T16:23:05.162Z" }, + { url = "https://files.pythonhosted.org/packages/69/2b/51e663ff190c9d16d4a8271203b71bc73a16aa7619b9f271a69b9d4a936b/torch-2.10.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:233aed0659a2503b831d8a67e9da66a62c996204c0bba4f4c442ccc0c68a3f60", size = 146018567, upload-time = "2026-01-21T16:22:23.393Z" }, + { url = "https://files.pythonhosted.org/packages/5e/cd/4b95ef7f293b927c283db0b136c42be91c8ec6845c44de0238c8c23bdc80/torch-2.10.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:682497e16bdfa6efeec8cde66531bc8d1fbbbb4d8788ec6173c089ed3cc2bfe5", size = 915721646, upload-time = "2026-01-21T16:21:16.983Z" }, + { url = "https://files.pythonhosted.org/packages/56/97/078a007208f8056d88ae43198833469e61a0a355abc0b070edd2c085eb9a/torch-2.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:6528f13d2a8593a1a412ea07a99812495bec07e9224c28b2a25c0a30c7da025c", size = 113752373, upload-time = "2026-01-21T16:22:13.471Z" }, + { url = "https://files.pythonhosted.org/packages/d8/94/71994e7d0d5238393df9732fdab607e37e2b56d26a746cb59fdb415f8966/torch-2.10.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:f5ab4ba32383061be0fb74bda772d470140a12c1c3b58a0cfbf3dae94d164c28", size = 79850324, upload-time = "2026-01-21T16:22:09.494Z" }, + { url = "https://files.pythonhosted.org/packages/e2/65/1a05346b418ea8ccd10360eef4b3e0ce688fba544e76edec26913a8d0ee0/torch-2.10.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:716b01a176c2a5659c98f6b01bf868244abdd896526f1c692712ab36dbaf9b63", size = 146006482, upload-time = "2026-01-21T16:22:18.42Z" }, + { url = "https://files.pythonhosted.org/packages/1d/b9/5f6f9d9e859fc3235f60578fa64f52c9c6e9b4327f0fe0defb6de5c0de31/torch-2.10.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:d8f5912ba938233f86361e891789595ff35ca4b4e2ac8fe3670895e5976731d6", size = 915613050, upload-time = "2026-01-21T16:20:49.035Z" }, + { url = "https://files.pythonhosted.org/packages/66/4d/35352043ee0eaffdeff154fad67cd4a31dbed7ff8e3be1cc4549717d6d51/torch-2.10.0-cp314-cp314t-win_amd64.whl", hash = "sha256:71283a373f0ee2c89e0f0d5f446039bdabe8dbc3c9ccf35f0f784908b0acd185", size = 113995816, upload-time = "2026-01-21T16:22:05.312Z" }, +] + +[[package]] +name = "triton" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/f7/f1c9d3424ab199ac53c2da567b859bcddbb9c9e7154805119f8bd95ec36f/triton-3.6.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6550fae429e0667e397e5de64b332d1e5695b73650ee75a6146e2e902770bea", size = 188105201, upload-time = "2026-01-20T16:00:29.272Z" }, + { url = "https://files.pythonhosted.org/packages/e0/12/b05ba554d2c623bffa59922b94b0775673de251f468a9609bc9e45de95e9/triton-3.6.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8e323d608e3a9bfcc2d9efcc90ceefb764a82b99dea12a86d643c72539ad5d3", size = 188214640, upload-time = "2026-01-20T16:00:35.869Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a8/cdf8b3e4c98132f965f88c2313a4b493266832ad47fb52f23d14d4f86bb5/triton-3.6.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74caf5e34b66d9f3a429af689c1c7128daba1d8208df60e81106b115c00d6fca", size = 188266850, upload-time = "2026-01-20T16:00:43.041Z" }, + { url = "https://files.pythonhosted.org/packages/f9/0b/37d991d8c130ce81a8728ae3c25b6e60935838e9be1b58791f5997b24a54/triton-3.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10c7f76c6e72d2ef08df639e3d0d30729112f47a56b0c81672edc05ee5116ac9", size = 188289450, upload-time = "2026-01-20T16:00:49.136Z" }, + { url = "https://files.pythonhosted.org/packages/35/f8/9c66bfc55361ec6d0e4040a0337fb5924ceb23de4648b8a81ae9d33b2b38/triton-3.6.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d002e07d7180fd65e622134fbd980c9a3d4211fb85224b56a0a0efbd422ab72f", size = 188400296, upload-time = "2026-01-20T16:00:56.042Z" }, + { url = "https://files.pythonhosted.org/packages/df/3d/9e7eee57b37c80cec63322c0231bb6da3cfe535a91d7a4d64896fcb89357/triton-3.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a17a5d5985f0ac494ed8a8e54568f092f7057ef60e1b0fa09d3fd1512064e803", size = 188273063, upload-time = "2026-01-20T16:01:07.278Z" }, + { url = "https://files.pythonhosted.org/packages/f6/56/6113c23ff46c00aae423333eb58b3e60bdfe9179d542781955a5e1514cb3/triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7", size = 188397994, upload-time = "2026-01-20T16:01:14.236Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + +[[package]] +name = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, +] + +[[package]] +name = "wandb" +version = "0.27.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "gitpython" }, + { name = "packaging" }, + { name = "platformdirs" }, + { name = "protobuf" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "sentry-sdk" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fa/a4/35a6e297ca22e0e2cbe77cbd21d33462874b8f508b41f0fae2f4c292ffea/wandb-0.27.1.tar.gz", hash = "sha256:214b3430c1e76e5eb55192c1bb4184a084f969e0c75cb15a709324c069efe10e", size = 40298729, upload-time = "2026-06-04T04:28:23.42Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/e2/3fa03632b11cbaa8b078a2c50fd985b6009a3ffcc566c3898d430c987cd1/wandb-0.27.1-py3-none-macosx_12_0_arm64.whl", hash = "sha256:abd80f9f74a4c28890c2a38d356fb0bceadfce497311ff13431ff387f4f23698", size = 23985424, upload-time = "2026-06-04T04:28:01.685Z" }, + { url = "https://files.pythonhosted.org/packages/91/4a/4dfa139ccce0f686eef44e3b8a3efc535333e4275e8fddcb68ff03b4f60d/wandb-0.27.1-py3-none-macosx_12_0_x86_64.whl", hash = "sha256:2fb189f186f2a41df9c559918a3308da22069dca5bc6f021bfface197a9aa1ff", size = 25164981, upload-time = "2026-06-04T04:28:04.593Z" }, + { url = "https://files.pythonhosted.org/packages/16/4e/872bd057b83a6c5da5c4e8c482c41df9338898a3c34d26172eb306388b8c/wandb-0.27.1-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:1c62f0daae156b1f56b2ac78e3dab25a19c8e4ef73b83547c0c877c77d433e9a", size = 24551609, upload-time = "2026-06-04T04:28:06.786Z" }, + { url = "https://files.pythonhosted.org/packages/02/cd/0cd5ef8723581cef8d6ecb9de29623a3da89c969b04f32809c1f22985ba2/wandb-0.27.1-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:77ccc56582d07671a867b78ced686096ca53c021d79fed38e5aaf4cebbd0450b", size = 26378746, upload-time = "2026-06-04T04:28:09.015Z" }, + { url = "https://files.pythonhosted.org/packages/46/3e/9fbaac13e1ffcecb3265dd08b8a76a15c21361ccbc0c65ab745847268240/wandb-0.27.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:af91e99c0dc02de03997169d218698e1175fca1cd06d5af5ef68a855c0cf6968", size = 24724687, upload-time = "2026-06-04T04:28:11.651Z" }, + { url = "https://files.pythonhosted.org/packages/3d/2b/f9c5bffd1f858c2c0a6dd41a8b5dea3d9337d4282dd247e603e1bea98d80/wandb-0.27.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a79f899de9861543bf20f273cdfe513c85d6841e88568aa1de05ce4a0a5a547b", size = 26690886, upload-time = "2026-06-04T04:28:14.364Z" }, + { url = "https://files.pythonhosted.org/packages/fa/14/91283eb2a17063091858f9f2b822437b16d75e53d742e295db1b479dcca4/wandb-0.27.1-py3-none-win32.whl", hash = "sha256:38dd902b56188789d3b17c03f8f77fef97a0b1c5aecee94c5bd70836e667b2cf", size = 24149484, upload-time = "2026-06-04T04:28:16.712Z" }, + { url = "https://files.pythonhosted.org/packages/7a/a9/7d25ba1386fe6b4dea65342aba7c82a9ac3294cb591b449622b31d75e0c5/wandb-0.27.1-py3-none-win_amd64.whl", hash = "sha256:2d7e4bb60a28756a0a6c78884dbad60bcd07e7cf840cdd8e3a3873bdf2af62af", size = 24149492, upload-time = "2026-06-04T04:28:18.85Z" }, + { url = "https://files.pythonhosted.org/packages/5d/b4/ed3e3410d0a86b9542c4cd3839f47bd72493063240759e7120f38637a9c0/wandb-0.27.1-py3-none-win_arm64.whl", hash = "sha256:356eda1fbce41101286935f5291e7db5ddfd062b23ddbbdd1d45ed03763ba2c4", size = 22059130, upload-time = "2026-06-04T04:28:20.985Z" }, +] From 402ac3a2474719640ad1f04130f7a52b87770be2 Mon Sep 17 00:00:00 2001 From: Kinvert Date: Thu, 4 Jun 2026 14:04:49 -0700 Subject: [PATCH 02/16] Tune Pathfinder rewards and early curriculum --- AGENTS.md | 5 ++ PATHFINDER_SPEC.md | 11 ++-- config/pathfinder.ini | 1 + ocean/pathfinder/binding.c | 1 + ocean/pathfinder/pathfinder.c | 1 + ocean/pathfinder/pathfinder.h | 20 ++++++-- ocean/pathfinder/tests/test_pathfinder_core.c | 50 +++++++++++++++++++ 7 files changed, 81 insertions(+), 8 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index c5c21be01d..380a1dd255 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -179,6 +179,8 @@ it is not the best state-memory reference on this branch. - Spawn: `A1`. - Hidden pawn: random non-`A1` cell, and generated maze must connect `A1` to it. +- Initial curriculum: `env.max_solution_len = 4` keeps early targets close to + `A1`; set it to `0` to disable the upper bound. - Actions: four discrete moves. - Observation: - 84 wall slots as floats: @@ -188,6 +190,9 @@ it is not the best state-memory reference on this branch. - normalized current x - normalized current y - total `OBS_SIZE = 86` +- Rewards: + - first-time wall discovery has no extra penalty beyond step cost + - repeated known-wall hits get a small penalty - First implementation should keep `train.state_buffer_size = 0` until deterministic state roundtrip tests exist. diff --git a/PATHFINDER_SPEC.md b/PATHFINDER_SPEC.md index 47a2c4b7c9..aecb17287b 100644 --- a/PATHFINDER_SPEC.md +++ b/PATHFINDER_SPEC.md @@ -110,6 +110,9 @@ Config knobs: - `loop_prob`: probability of opening extra internal edges after carving. - `extra_entry_prob`: probability each non-`A1` column-1 entrance is open. - `min_solution_len`: reject mazes with a shortest solution path below this. +- `max_solution_len`: reject mazes with a shortest solution path above this. + Use a small value for early curriculum so the hidden pawn starts close to + `A1`; `0` disables the upper bound. - `max_steps`: timeout. - `seed`: inherited from vector env config. @@ -165,9 +168,10 @@ Default reward model: - `+1.0` for reaching the hidden pawn. - `-0.001` per step. -- `-0.01` for hitting a newly discovered wall. -- `-0.03` for hitting a wall that was already known. -- `-0.02` for impossible movement, such as attempting to exit through the +- `0.0` extra penalty for hitting a newly discovered wall; the agent paid the + step cost but gained information. +- `-0.01` for hitting a wall that was already known. +- `-0.01` for impossible movement, such as attempting to exit through the left edge in v1. - `0.0` for a successful nonterminal move, except for the step penalty. @@ -270,6 +274,7 @@ Start conservative: - `vec.total_agents = 8192` or `16384` - `vec.num_buffers = 2` +- `env.max_solution_len = 4` for early closer-target curriculum - `train.gpus = 1` - `train.total_timesteps = 100M` for real runs, at least `2,097,152` for smoke tests with the initial `8192 x 128` rollout geometry. diff --git a/config/pathfinder.ini b/config/pathfinder.ini index 8d55d86a47..58ff658459 100644 --- a/config/pathfinder.ini +++ b/config/pathfinder.ini @@ -11,6 +11,7 @@ branch_prob = 0.35 loop_prob = 0.10 extra_entry_prob = 0.0 min_solution_len = 1 +max_solution_len = 4 max_steps = 128 [policy] diff --git a/ocean/pathfinder/binding.c b/ocean/pathfinder/binding.c index bf651fdfc3..654b80d091 100644 --- a/ocean/pathfinder/binding.c +++ b/ocean/pathfinder/binding.c @@ -14,6 +14,7 @@ void my_init(Env* env, Dict* kwargs) { env->loop_prob = (float)dict_get(kwargs, "loop_prob")->value; env->extra_entry_prob = (float)dict_get(kwargs, "extra_entry_prob")->value; env->min_solution_len = (int)dict_get(kwargs, "min_solution_len")->value; + env->max_solution_len = (int)dict_get(kwargs, "max_solution_len")->value; env->max_steps = (int)dict_get(kwargs, "max_steps")->value; init(env); } diff --git a/ocean/pathfinder/pathfinder.c b/ocean/pathfinder/pathfinder.c index fe7993c3a8..7ffdb363e4 100644 --- a/ocean/pathfinder/pathfinder.c +++ b/ocean/pathfinder/pathfinder.c @@ -20,6 +20,7 @@ int main(void) { env.loop_prob = 0.10f; env.extra_entry_prob = 0.0f; env.min_solution_len = 1; + env.max_solution_len = 4; env.max_steps = 128; init(&env); diff --git a/ocean/pathfinder/pathfinder.h b/ocean/pathfinder/pathfinder.h index 0833a69b21..bf7f0e77a2 100644 --- a/ocean/pathfinder/pathfinder.h +++ b/ocean/pathfinder/pathfinder.h @@ -24,9 +24,9 @@ #define PATHFINDER_WALL 1.0f #define PATHFINDER_STEP_PENALTY -0.001f -#define PATHFINDER_NEW_WALL_PENALTY -0.01f -#define PATHFINDER_KNOWN_WALL_PENALTY -0.03f -#define PATHFINDER_IMPOSSIBLE_PENALTY -0.02f +#define PATHFINDER_NEW_WALL_PENALTY 0.0f +#define PATHFINDER_KNOWN_WALL_PENALTY -0.01f +#define PATHFINDER_IMPOSSIBLE_PENALTY -0.01f #define PATHFINDER_GOAL_REWARD 1.0f typedef struct Log { @@ -72,6 +72,7 @@ typedef struct Pathfinder { float loop_prob; float extra_entry_prob; int min_solution_len; + int max_solution_len; int max_steps; State state; } Pathfinder; @@ -273,6 +274,10 @@ static void pathfinder_open_random_edges(Pathfinder* env) { static void pathfinder_generate_maze(Pathfinder* env) { State* s = &env->state; int min_solution_len = env->min_solution_len < 1 ? 1 : env->min_solution_len; + int max_solution_len = env->max_solution_len; + if (max_solution_len > 0 && max_solution_len < min_solution_len) { + max_solution_len = min_solution_len; + } for (int attempt = 0; attempt < 128; attempt++) { pathfinder_init_walls(s); @@ -285,14 +290,19 @@ static void pathfinder_generate_maze(Pathfinder* env) { pathfinder_carve_solution(env); pathfinder_open_random_edges(env); s->shortest_path_len = pathfinder_shortest_path(s); - if (s->shortest_path_len >= min_solution_len) { + if (s->shortest_path_len >= min_solution_len && + (max_solution_len <= 0 || s->shortest_path_len <= max_solution_len)) { return; } } pathfinder_init_walls(s); s->goal_row = 0; - s->goal_col = min_solution_len < PATHFINDER_COLS ? min_solution_len : PATHFINDER_COLS - 1; + int fallback_len = min_solution_len; + if (max_solution_len > 0 && fallback_len > max_solution_len) { + fallback_len = max_solution_len; + } + s->goal_col = fallback_len < PATHFINDER_COLS ? fallback_len : PATHFINDER_COLS - 1; for (int col = 0; col < s->goal_col; col++) { pathfinder_open_edge(s, 0, col, 0, col + 1); } diff --git a/ocean/pathfinder/tests/test_pathfinder_core.c b/ocean/pathfinder/tests/test_pathfinder_core.c index b22d29ec55..fe95d00e81 100644 --- a/ocean/pathfinder/tests/test_pathfinder_core.c +++ b/ocean/pathfinder/tests/test_pathfinder_core.c @@ -17,6 +17,7 @@ static void setup_env(Pathfinder* env, float* obs, float* actions, env->loop_prob = 0.10f; env->extra_entry_prob = 0.0f; env->min_solution_len = 1; + env->max_solution_len = 0; env->max_steps = 128; env->rng = 7; init(env); @@ -124,6 +125,53 @@ static void test_blocked_edge_reveals_and_stays(void) { assert(terminals[0] == 0.0f); } +static void test_known_wall_has_only_repeat_penalty(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + c_reset(&env); + + memset(env.state.true_walls, 1, sizeof(env.state.true_walls)); + for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { + env.state.known_walls[i] = PATHFINDER_UNKNOWN; + } + env.state.agent_row = 0; + env.state.agent_col = 0; + env.state.goal_row = 5; + env.state.goal_col = 5; + int east_wall = pathfinder_wall_between(0, 0, 0, 1); + env.state.true_walls[east_wall] = 1; + refresh_state(&env); + + actions[0] = PATHFINDER_ACT_EAST; + c_step(&env); + assert(fabsf(rewards[0] - PATHFINDER_STEP_PENALTY) < 1e-6f); + + c_step(&env); + assert(fabsf(rewards[0] - + (PATHFINDER_STEP_PENALTY + PATHFINDER_KNOWN_WALL_PENALTY)) < 1e-6f); +} + +static void test_max_solution_len_limits_curriculum_distance(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + env.max_solution_len = 2; + + for (int i = 0; i < 100; i++) { + c_reset(&env); + assert(pathfinder_has_path_to_goal(&env.state)); + assert(env.state.shortest_path_len >= 1); + assert(env.state.shortest_path_len <= 2); + } +} + static void test_reaching_goal_terminates(void) { Pathfinder env; float obs[PATHFINDER_OBS_SIZE]; @@ -160,6 +208,8 @@ int main(void) { test_generated_mazes_connect_a1_to_goal(); test_open_edge_reveals_and_moves(); test_blocked_edge_reveals_and_stays(); + test_known_wall_has_only_repeat_penalty(); + test_max_solution_len_limits_curriculum_distance(); test_reaching_goal_terminates(); printf("pathfinder core tests passed\n"); return 0; From aafac9c9869b79d68b1e7765700ee5a6194742a3 Mon Sep 17 00:00:00 2001 From: Kinvert Date: Thu, 4 Jun 2026 14:19:15 -0700 Subject: [PATCH 03/16] Add Pathfinder render debugger --- ocean/pathfinder/pathfinder.c | 29 +- ocean/pathfinder/pathfinder.h | 259 +++++++++++++++++- ocean/pathfinder/tests/run_all.sh | 1 + ocean/pathfinder/tests/test_pathfinder_core.c | 2 + 4 files changed, 285 insertions(+), 6 deletions(-) diff --git a/ocean/pathfinder/pathfinder.c b/ocean/pathfinder/pathfinder.c index 7ffdb363e4..dfe143a062 100644 --- a/ocean/pathfinder/pathfinder.c +++ b/ocean/pathfinder/pathfinder.c @@ -1,6 +1,14 @@ #include #include "pathfinder.h" +static int read_manual_action(void) { + if (IsKeyPressed(KEY_UP) || IsKeyPressed(KEY_W)) return PATHFINDER_ACT_NORTH; + if (IsKeyPressed(KEY_RIGHT) || IsKeyPressed(KEY_D)) return PATHFINDER_ACT_EAST; + if (IsKeyPressed(KEY_DOWN) || IsKeyPressed(KEY_S)) return PATHFINDER_ACT_SOUTH; + if (IsKeyPressed(KEY_LEFT) || IsKeyPressed(KEY_A)) return PATHFINDER_ACT_WEST; + return -1; +} + int main(void) { Pathfinder env; memset(&env, 0, sizeof(env)); @@ -26,13 +34,24 @@ int main(void) { init(&env); c_reset(&env); - for (int i = 0; i < 256; i++) { - actions[0] = (float)(pathfinder_rand(&env) % PATHFINDER_NUM_ACTIONS); - c_step(&env); + c_render(&env); + while (!WindowShouldClose()) { + if (IsKeyPressed(KEY_R)) { + c_reset(&env); + } + + int action = read_manual_action(); + if (action >= 0) { + actions[0] = (float)action; + c_step(&env); + } else if (IsKeyPressed(KEY_SPACE)) { + actions[0] = (float)(pathfinder_rand(&env) % PATHFINDER_NUM_ACTIONS); + c_step(&env); + } + + c_render(&env); } - printf("Pathfinder random smoke: episodes=%0.0f success=%0.3f return=%0.3f\n", - env.log.n, env.log.success, env.log.episode_return); c_close(&env); return 0; } diff --git a/ocean/pathfinder/pathfinder.h b/ocean/pathfinder/pathfinder.h index bf7f0e77a2..0bf7b94a14 100644 --- a/ocean/pathfinder/pathfinder.h +++ b/ocean/pathfinder/pathfinder.h @@ -6,6 +6,10 @@ #include #include +#if !defined(PATHFINDER_NO_RENDER) && !defined(PUFFER_PERF_NO_RENDER) +#include "raylib.h" +#endif + #define PATHFINDER_ROWS 6 #define PATHFINDER_COLS 6 #define PATHFINDER_VERTICAL_WALLS (PATHFINDER_ROWS * (PATHFINDER_COLS + 1)) @@ -14,6 +18,18 @@ #define PATHFINDER_OBS_SIZE (PATHFINDER_NUM_WALLS + 2) #define PATHFINDER_NUM_ACTIONS 4 +#define PATHFINDER_RENDER_TILE 72 +#define PATHFINDER_RENDER_MARGIN 40 +#define PATHFINDER_RENDER_BOARD_X PATHFINDER_RENDER_MARGIN +#define PATHFINDER_RENDER_BOARD_Y 92 +#define PATHFINDER_RENDER_BOARD_SIZE (PATHFINDER_RENDER_TILE * PATHFINDER_COLS) +#define PATHFINDER_RENDER_PANEL_WIDTH 332 +#define PATHFINDER_RENDER_WIDTH \ + (PATHFINDER_RENDER_BOARD_X + PATHFINDER_RENDER_BOARD_SIZE + \ + PATHFINDER_RENDER_PANEL_WIDTH + PATHFINDER_RENDER_MARGIN) +#define PATHFINDER_RENDER_HEIGHT \ + (PATHFINDER_RENDER_BOARD_Y + PATHFINDER_RENDER_BOARD_SIZE + PATHFINDER_RENDER_MARGIN) + #define PATHFINDER_ACT_NORTH 0 #define PATHFINDER_ACT_EAST 1 #define PATHFINDER_ACT_SOUTH 2 @@ -60,7 +76,12 @@ typedef struct State { float known_walls[PATHFINDER_NUM_WALLS]; } State; +typedef struct PathfinderClient { + bool show_truth; +} PathfinderClient; + typedef struct Pathfinder { + PathfinderClient* client; Log log; float* observations; float* actions; @@ -429,9 +450,245 @@ void c_step(Pathfinder* env) { } void c_close(Pathfinder* env) { - (void)env; +#if !defined(PATHFINDER_NO_RENDER) && !defined(PUFFER_PERF_NO_RENDER) + if (IsWindowReady()) { + CloseWindow(); + } +#endif + free(env->client); + env->client = NULL; } +#if defined(PATHFINDER_NO_RENDER) || defined(PUFFER_PERF_NO_RENDER) void c_render(Pathfinder* env) { (void)env; } +#else +static const Color PATHFINDER_BG = {6, 24, 24, 255}; +static const Color PATHFINDER_CELL_A = {16, 39, 42, 255}; +static const Color PATHFINDER_CELL_B = {19, 46, 49, 255}; +static const Color PATHFINDER_GRID = {54, 84, 86, 255}; +static const Color PATHFINDER_TEXT = {235, 242, 240, 255}; +static const Color PATHFINDER_MUTED = {145, 166, 164, 255}; +static const Color PATHFINDER_TRUE_WALL = {88, 96, 99, 255}; +static const Color PATHFINDER_UNKNOWN_EDGE = {42, 63, 65, 255}; +static const Color PATHFINDER_KNOWN_WALL = {218, 59, 54, 255}; +static const Color PATHFINDER_KNOWN_OPEN = {75, 196, 118, 255}; +static const Color PATHFINDER_AGENT = {0, 187, 187, 255}; +static const Color PATHFINDER_GOAL = {232, 184, 58, 255}; +static const Color PATHFINDER_START = {118, 146, 150, 255}; + +static PathfinderClient* pathfinder_make_client(void) { + PathfinderClient* client = (PathfinderClient*)calloc(1, sizeof(PathfinderClient)); + client->show_truth = true; + InitWindow(PATHFINDER_RENDER_WIDTH, PATHFINDER_RENDER_HEIGHT, "PufferLib Pathfinder"); + SetTargetFPS(30); + return client; +} + +static inline int pathfinder_cell_x(int col) { + return PATHFINDER_RENDER_BOARD_X + col * PATHFINDER_RENDER_TILE; +} + +static inline int pathfinder_cell_y(int row) { + return PATHFINDER_RENDER_BOARD_Y + row * PATHFINDER_RENDER_TILE; +} + +static inline Vector2 pathfinder_cell_center(int row, int col) { + return (Vector2){ + (float)(pathfinder_cell_x(col) + PATHFINDER_RENDER_TILE / 2), + (float)(pathfinder_cell_y(row) + PATHFINDER_RENDER_TILE / 2) + }; +} + +static const char* pathfinder_action_name(int action) { + if (action == PATHFINDER_ACT_NORTH) return "north"; + if (action == PATHFINDER_ACT_EAST) return "east"; + if (action == PATHFINDER_ACT_SOUTH) return "south"; + if (action == PATHFINDER_ACT_WEST) return "west"; + return "invalid"; +} + +static void pathfinder_draw_centered_text(const char* text, int cx, int y, + int font_size, Color color) { + int width = MeasureText(text, font_size); + DrawText(text, cx - width / 2, y, font_size, color); +} + +static void pathfinder_draw_edge(Pathfinder* env, int wall, Vector2 start, Vector2 end) { + State* s = &env->state; + float known = s->known_walls[wall]; + + DrawLineEx(start, end, 2.0f, PATHFINDER_UNKNOWN_EDGE); + if (env->client->show_truth && s->true_walls[wall]) { + DrawLineEx(start, end, 6.0f, PATHFINDER_TRUE_WALL); + } + + if (known == PATHFINDER_WALL) { + DrawLineEx(start, end, 8.0f, PATHFINDER_KNOWN_WALL); + } else if (known == PATHFINDER_OPEN) { + DrawLineEx(start, end, 4.0f, PATHFINDER_KNOWN_OPEN); + } +} + +static void pathfinder_draw_board(Pathfinder* env) { + State* s = &env->state; + + for (int row = 0; row < PATHFINDER_ROWS; row++) { + for (int col = 0; col < PATHFINDER_COLS; col++) { + Color cell_color = ((row + col) & 1) ? PATHFINDER_CELL_A : PATHFINDER_CELL_B; + DrawRectangle(pathfinder_cell_x(col), pathfinder_cell_y(row), + PATHFINDER_RENDER_TILE - 1, PATHFINDER_RENDER_TILE - 1, cell_color); + } + } + + DrawRectangleLinesEx((Rectangle){ + (float)PATHFINDER_RENDER_BOARD_X, + (float)PATHFINDER_RENDER_BOARD_Y, + (float)PATHFINDER_RENDER_BOARD_SIZE, + (float)PATHFINDER_RENDER_BOARD_SIZE + }, 2.0f, PATHFINDER_GRID); + + for (int col = 0; col < PATHFINDER_COLS; col++) { + char label[2] = {(char)('A' + col), '\0'}; + pathfinder_draw_centered_text(label, + pathfinder_cell_x(col) + PATHFINDER_RENDER_TILE / 2, + PATHFINDER_RENDER_BOARD_Y - 28, 20, PATHFINDER_TEXT); + } + for (int row = 0; row < PATHFINDER_ROWS; row++) { + DrawText(TextFormat("%i", row + 1), + PATHFINDER_RENDER_BOARD_X - 28, + pathfinder_cell_y(row) + PATHFINDER_RENDER_TILE / 2 - 10, + 20, PATHFINDER_TEXT); + } + + DrawRectangleLinesEx((Rectangle){ + (float)pathfinder_cell_x(0) + 4.0f, + (float)pathfinder_cell_y(0) + 4.0f, + (float)PATHFINDER_RENDER_TILE - 9.0f, + (float)PATHFINDER_RENDER_TILE - 9.0f + }, 2.0f, PATHFINDER_START); + pathfinder_draw_centered_text("A1", pathfinder_cell_x(0) + PATHFINDER_RENDER_TILE / 2, + pathfinder_cell_y(0) + PATHFINDER_RENDER_TILE - 24, 16, PATHFINDER_MUTED); + + for (int row = 0; row < PATHFINDER_ROWS; row++) { + for (int edge_col = 0; edge_col <= PATHFINDER_COLS; edge_col++) { + int wall = pathfinder_v_wall(row, edge_col); + float x = (float)(PATHFINDER_RENDER_BOARD_X + edge_col * PATHFINDER_RENDER_TILE); + float y0 = (float)(pathfinder_cell_y(row) + 7); + float y1 = (float)(pathfinder_cell_y(row + 1) - 7); + pathfinder_draw_edge(env, wall, (Vector2){x, y0}, (Vector2){x, y1}); + } + } + for (int edge_row = 0; edge_row <= PATHFINDER_ROWS; edge_row++) { + for (int col = 0; col < PATHFINDER_COLS; col++) { + int wall = pathfinder_h_wall(edge_row, col); + float x0 = (float)(pathfinder_cell_x(col) + 7); + float x1 = (float)(pathfinder_cell_x(col + 1) - 7); + float y = (float)(PATHFINDER_RENDER_BOARD_Y + edge_row * PATHFINDER_RENDER_TILE); + pathfinder_draw_edge(env, wall, (Vector2){x0, y}, (Vector2){x1, y}); + } + } + + if (env->client->show_truth) { + Vector2 goal = pathfinder_cell_center(s->goal_row, s->goal_col); + DrawCircleV(goal, 19.0f, PATHFINDER_GOAL); + pathfinder_draw_centered_text("T", (int)goal.x, (int)goal.y - 10, 22, PATHFINDER_BG); + } + + Vector2 agent = pathfinder_cell_center(s->agent_row, s->agent_col); + DrawCircleV(agent, 21.0f, PATHFINDER_AGENT); + DrawCircleLines((int)agent.x, (int)agent.y, 22.0f, PATHFINDER_TEXT); + pathfinder_draw_centered_text("P", (int)agent.x, (int)agent.y - 11, 24, PATHFINDER_BG); +} + +static void pathfinder_draw_panel(Pathfinder* env) { + State* s = &env->state; + int x = PATHFINDER_RENDER_BOARD_X + PATHFINDER_RENDER_BOARD_SIZE + 34; + int y = PATHFINDER_RENDER_BOARD_Y; + int action = env->actions == NULL ? -1 : (int)env->actions[0]; + float reward = env->rewards == NULL ? 0.0f : env->rewards[0]; + float terminal = env->terminals == NULL ? 0.0f : env->terminals[0]; + int unknown = PATHFINDER_NUM_WALLS - s->known_wall_count - s->known_open_count; + + DrawText("Pathfinder", x, y, 28, PATHFINDER_TEXT); + y += 38; + DrawText(env->client->show_truth ? "View: truth + observation" : "View: observation only", + x, y, 18, env->client->show_truth ? PATHFINDER_GOAL : PATHFINDER_KNOWN_OPEN); + y += 34; + + DrawText(TextFormat("Position: %c%i", 'A' + s->agent_col, s->agent_row + 1), + x, y, 20, PATHFINDER_TEXT); + y += 26; + if (env->client->show_truth) { + DrawText(TextFormat("Target: %c%i", 'A' + s->goal_col, s->goal_row + 1), + x, y, 20, PATHFINDER_GOAL); + } else { + DrawText("Target: hidden", x, y, 20, PATHFINDER_MUTED); + } + y += 34; + + DrawText(TextFormat("Tick: %i / %i", s->tick, env->max_steps), x, y, 18, PATHFINDER_TEXT); + y += 24; + DrawText(TextFormat("Action: %s", pathfinder_action_name(action)), x, y, 18, PATHFINDER_TEXT); + y += 24; + DrawText(TextFormat("Reward: %.3f", reward), x, y, 18, + reward >= 0.0f ? PATHFINDER_KNOWN_OPEN : PATHFINDER_KNOWN_WALL); + y += 24; + DrawText(TextFormat("Return: %.3f", s->episode_return), x, y, 18, PATHFINDER_TEXT); + y += 24; + DrawText(TextFormat("Terminal: %.0f", terminal), x, y, 18, PATHFINDER_TEXT); + y += 34; + + DrawText(TextFormat("Known walls: %i", s->known_wall_count), x, y, 18, PATHFINDER_KNOWN_WALL); + y += 24; + DrawText(TextFormat("Known open: %i", s->known_open_count), x, y, 18, PATHFINDER_KNOWN_OPEN); + y += 24; + DrawText(TextFormat("Unknown edges: %i", unknown), x, y, 18, PATHFINDER_MUTED); + y += 34; + + DrawText(TextFormat("Wall hits: %i", s->wall_hits), x, y, 18, PATHFINDER_TEXT); + y += 24; + DrawText(TextFormat("Shortest path: %i", s->shortest_path_len), x, y, 18, PATHFINDER_TEXT); + y += 24; + DrawText(TextFormat("Agent path: %i", s->agent_path_len), x, y, 18, PATHFINDER_TEXT); + y += 34; + + DrawText(TextFormat("Episodes: %.0f", env->log.n), x, y, 18, PATHFINDER_TEXT); + y += 24; + DrawText(TextFormat("Avg success: %.3f", env->log.n > 0.0f ? + env->log.success / env->log.n : 0.0f), x, y, 18, PATHFINDER_TEXT); + + DrawText("Arrows/WASD move | R reset", PATHFINDER_RENDER_BOARD_X, + PATHFINDER_RENDER_HEIGHT - 30, 18, PATHFINDER_MUTED); + DrawText("TAB view | SPACE random | ESC quit", + PATHFINDER_RENDER_BOARD_X + 310, PATHFINDER_RENDER_HEIGHT - 30, + 18, PATHFINDER_MUTED); +} + +void c_render(Pathfinder* env) { + if (!IsWindowReady()) { + env->client = pathfinder_make_client(); + } else if (env->client == NULL) { + env->client = (PathfinderClient*)calloc(1, sizeof(PathfinderClient)); + env->client->show_truth = true; + } + + if (IsKeyDown(KEY_ESCAPE)) { + c_close(env); + exit(0); + } + if (IsKeyPressed(KEY_TAB)) { + env->client->show_truth = !env->client->show_truth; + } + + BeginDrawing(); + ClearBackground(PATHFINDER_BG); + DrawText("Milton Bradley Pathfinder", PATHFINDER_RENDER_BOARD_X, 26, 30, PATHFINDER_TEXT); + DrawText("Red = known wall, green = known open, gray = true hidden wall", + PATHFINDER_RENDER_BOARD_X, 60, 18, PATHFINDER_MUTED); + pathfinder_draw_board(env); + pathfinder_draw_panel(env); + EndDrawing(); +} +#endif diff --git a/ocean/pathfinder/tests/run_all.sh b/ocean/pathfinder/tests/run_all.sh index 8a2dd4ba47..3196fd0a64 100644 --- a/ocean/pathfinder/tests/run_all.sh +++ b/ocean/pathfinder/tests/run_all.sh @@ -7,6 +7,7 @@ mkdir -p build/pathfinder-tests ${CC:-clang} -std=c11 -Wall -Wextra -Werror \ -I. -Iocean/pathfinder -Ivendor -Iraylib-5.5_linux_amd64/include \ + -DPATHFINDER_NO_RENDER \ ocean/pathfinder/tests/test_pathfinder_core.c \ -lm \ -o build/pathfinder-tests/test_pathfinder_core diff --git a/ocean/pathfinder/tests/test_pathfinder_core.c b/ocean/pathfinder/tests/test_pathfinder_core.c index fe95d00e81..e17b95a5d9 100644 --- a/ocean/pathfinder/tests/test_pathfinder_core.c +++ b/ocean/pathfinder/tests/test_pathfinder_core.c @@ -29,6 +29,8 @@ static void test_constants(void) { assert(PATHFINDER_NUM_WALLS == 84); assert(PATHFINDER_OBS_SIZE == 86); assert(PATHFINDER_NUM_ACTIONS == 4); + assert(PATHFINDER_RENDER_WIDTH > PATHFINDER_RENDER_BOARD_X + PATHFINDER_RENDER_BOARD_SIZE); + assert(PATHFINDER_RENDER_HEIGHT > PATHFINDER_RENDER_BOARD_Y + PATHFINDER_RENDER_BOARD_SIZE); } static void test_reset_initializes_a1_and_unknown_walls(void) { From d5548d6eb2f31ef67f81d8d1478fa8024b23210b Mon Sep 17 00:00:00 2001 From: Kinvert Date: Thu, 4 Jun 2026 14:39:52 -0700 Subject: [PATCH 04/16] Add Pathfinder penalties and curriculum --- ocean/pathfinder/binding.c | 4 + ocean/pathfinder/pathfinder.h | 182 ++++++++++++++++-- ocean/pathfinder/tests/test_pathfinder_core.c | 76 +++++++- 3 files changed, 239 insertions(+), 23 deletions(-) diff --git a/ocean/pathfinder/binding.c b/ocean/pathfinder/binding.c index 654b80d091..063386a6a2 100644 --- a/ocean/pathfinder/binding.c +++ b/ocean/pathfinder/binding.c @@ -26,8 +26,12 @@ void my_log(Log* log, Dict* out) { dict_set(out, "episode_length", log->episode_length); dict_set(out, "success", log->success); dict_set(out, "wall_hits", log->wall_hits); + dict_set(out, "revisits", log->revisits); + dict_set(out, "known_wall_deaths", log->known_wall_deaths); dict_set(out, "known_walls", log->known_walls); dict_set(out, "known_open_edges", log->known_open_edges); dict_set(out, "shortest_path_len", log->shortest_path_len); dict_set(out, "agent_path_len", log->agent_path_len); + dict_set(out, "curriculum_level", log->curriculum_level); + dict_set(out, "curriculum_max_solution_len", log->curriculum_max_solution_len); } diff --git a/ocean/pathfinder/pathfinder.h b/ocean/pathfinder/pathfinder.h index 0bf7b94a14..fec41cfbce 100644 --- a/ocean/pathfinder/pathfinder.h +++ b/ocean/pathfinder/pathfinder.h @@ -17,6 +17,9 @@ #define PATHFINDER_NUM_WALLS (PATHFINDER_VERTICAL_WALLS + PATHFINDER_HORIZONTAL_WALLS) #define PATHFINDER_OBS_SIZE (PATHFINDER_NUM_WALLS + 2) #define PATHFINDER_NUM_ACTIONS 4 +#define PATHFINDER_MAX_SOLUTION_LEN ((PATHFINDER_ROWS - 1) + (PATHFINDER_COLS - 1)) +#define PATHFINDER_CURRICULUM_WINDOW 32 +#define PATHFINDER_CURRICULUM_SUCCESS_THRESHOLD 24 #define PATHFINDER_RENDER_TILE 72 #define PATHFINDER_RENDER_MARGIN 40 @@ -28,7 +31,7 @@ (PATHFINDER_RENDER_BOARD_X + PATHFINDER_RENDER_BOARD_SIZE + \ PATHFINDER_RENDER_PANEL_WIDTH + PATHFINDER_RENDER_MARGIN) #define PATHFINDER_RENDER_HEIGHT \ - (PATHFINDER_RENDER_BOARD_Y + PATHFINDER_RENDER_BOARD_SIZE + PATHFINDER_RENDER_MARGIN) + (PATHFINDER_RENDER_BOARD_Y + PATHFINDER_RENDER_BOARD_SIZE + 168) #define PATHFINDER_ACT_NORTH 0 #define PATHFINDER_ACT_EAST 1 @@ -42,6 +45,7 @@ #define PATHFINDER_STEP_PENALTY -0.001f #define PATHFINDER_NEW_WALL_PENALTY 0.0f #define PATHFINDER_KNOWN_WALL_PENALTY -0.01f +#define PATHFINDER_REVISIT_PENALTY -0.01f #define PATHFINDER_IMPOSSIBLE_PENALTY -0.01f #define PATHFINDER_GOAL_REWARD 1.0f @@ -52,10 +56,14 @@ typedef struct Log { float episode_length; float success; float wall_hits; + float revisits; + float known_wall_deaths; float known_walls; float known_open_edges; float shortest_path_len; float agent_path_len; + float curriculum_level; + float curriculum_max_solution_len; float n; } Log; @@ -68,10 +76,14 @@ typedef struct State { int shortest_path_len; int agent_path_len; int wall_hits; + int revisit_count; + int known_wall_death; + int visited_count; int known_wall_count; int known_open_count; int success; float episode_return; + unsigned char visited[PATHFINDER_ROWS][PATHFINDER_COLS]; unsigned char true_walls[PATHFINDER_NUM_WALLS]; float known_walls[PATHFINDER_NUM_WALLS]; } State; @@ -95,9 +107,43 @@ typedef struct Pathfinder { int min_solution_len; int max_solution_len; int max_steps; + int curriculum_level; + int curriculum_episodes; + int curriculum_window_episodes; + int curriculum_window_successes; State state; } Pathfinder; +static inline int pathfinder_clamp_int(int value, int min_value, int max_value) { + if (value < min_value) return min_value; + if (value > max_value) return max_value; + return value; +} + +static inline int pathfinder_curriculum_base_solution_len(const Pathfinder* env) { + if (env->max_solution_len <= 0) { + return PATHFINDER_MAX_SOLUTION_LEN; + } + return pathfinder_clamp_int(env->max_solution_len, 1, PATHFINDER_MAX_SOLUTION_LEN); +} + +static inline int pathfinder_curriculum_max_solution_len(const Pathfinder* env) { + int max_len = pathfinder_curriculum_base_solution_len(env) + env->curriculum_level; + return pathfinder_clamp_int(max_len, 1, PATHFINDER_MAX_SOLUTION_LEN); +} + +static inline int pathfinder_curriculum_min_solution_len(const Pathfinder* env) { + int max_len = pathfinder_curriculum_max_solution_len(env); + int min_len = env->min_solution_len < 1 ? 1 : env->min_solution_len; + if (max_len >= 4) { + int staged_min = max_len - 2; + if (staged_min > min_len) { + min_len = staged_min; + } + } + return pathfinder_clamp_int(min_len, 1, max_len); +} + static inline int pathfinder_v_wall(int row, int edge_col) { return row * (PATHFINDER_COLS + 1) + edge_col; } @@ -142,6 +188,14 @@ static inline void pathfinder_open_edge(State* s, int row, int col, int next_row } } +static inline void pathfinder_mark_visited(State* s, int row, int col) { + if (!pathfinder_in_bounds(row, col) || s->visited[row][col]) { + return; + } + s->visited[row][col] = 1; + s->visited_count++; +} + static inline void pathfinder_action_delta(int action, int* d_row, int* d_col) { *d_row = 0; *d_col = 0; @@ -292,42 +346,97 @@ static void pathfinder_open_random_edges(Pathfinder* env) { } } -static void pathfinder_generate_maze(Pathfinder* env) { +static void pathfinder_choose_goal_at_distance(Pathfinder* env, int distance) { State* s = &env->state; - int min_solution_len = env->min_solution_len < 1 ? 1 : env->min_solution_len; - int max_solution_len = env->max_solution_len; - if (max_solution_len > 0 && max_solution_len < min_solution_len) { - max_solution_len = min_solution_len; + int candidates[PATHFINDER_ROWS * PATHFINDER_COLS]; + int count = 0; + distance = pathfinder_clamp_int(distance, 1, PATHFINDER_MAX_SOLUTION_LEN); + + for (int row = 0; row < PATHFINDER_ROWS; row++) { + for (int col = 0; col < PATHFINDER_COLS; col++) { + if (row == 0 && col == 0) { + continue; + } + if (row + col == distance) { + candidates[count++] = row * PATHFINDER_COLS + col; + } + } + } + + if (count == 0) { + s->goal_row = PATHFINDER_ROWS - 1; + s->goal_col = PATHFINDER_COLS - 1; + return; } + int cell = candidates[pathfinder_rand(env) % (unsigned int)count]; + s->goal_row = cell / PATHFINDER_COLS; + s->goal_col = cell % PATHFINDER_COLS; +} + +static void pathfinder_build_fallback_path(State* s, int solution_len) { + pathfinder_init_walls(s); + solution_len = pathfinder_clamp_int(solution_len, 1, PATHFINDER_MAX_SOLUTION_LEN); + + int row = 0; + int col = 0; + int remaining = solution_len; + while (remaining > 0 && col < PATHFINDER_COLS - 1) { + pathfinder_open_edge(s, row, col, row, col + 1); + col++; + remaining--; + } + while (remaining > 0 && row < PATHFINDER_ROWS - 1) { + pathfinder_open_edge(s, row, col, row + 1, col); + row++; + remaining--; + } + + s->goal_row = row; + s->goal_col = col; + s->shortest_path_len = pathfinder_shortest_path(s); +} + +static void pathfinder_generate_maze(Pathfinder* env) { + State* s = &env->state; + int min_solution_len = pathfinder_curriculum_min_solution_len(env); + int max_solution_len = pathfinder_curriculum_max_solution_len(env); + for (int attempt = 0; attempt < 128; attempt++) { pathfinder_init_walls(s); - do { - s->goal_row = (int)(pathfinder_rand(env) % PATHFINDER_ROWS); - s->goal_col = (int)(pathfinder_rand(env) % PATHFINDER_COLS); - } while (s->goal_row == 0 && s->goal_col == 0); + int span = max_solution_len - min_solution_len + 1; + int target_len = min_solution_len + (int)(pathfinder_rand(env) % (unsigned int)span); + pathfinder_choose_goal_at_distance(env, target_len); pathfinder_carve_solution(env); pathfinder_open_random_edges(env); s->shortest_path_len = pathfinder_shortest_path(s); - if (s->shortest_path_len >= min_solution_len && - (max_solution_len <= 0 || s->shortest_path_len <= max_solution_len)) { + if (s->shortest_path_len >= min_solution_len && s->shortest_path_len <= max_solution_len) { return; } } - pathfinder_init_walls(s); - s->goal_row = 0; - int fallback_len = min_solution_len; - if (max_solution_len > 0 && fallback_len > max_solution_len) { - fallback_len = max_solution_len; + pathfinder_build_fallback_path(s, max_solution_len); +} + +static void pathfinder_update_curriculum(Pathfinder* env, int success) { + env->curriculum_episodes++; + env->curriculum_window_episodes++; + if (success) { + env->curriculum_window_successes++; } - s->goal_col = fallback_len < PATHFINDER_COLS ? fallback_len : PATHFINDER_COLS - 1; - for (int col = 0; col < s->goal_col; col++) { - pathfinder_open_edge(s, 0, col, 0, col + 1); + + if (env->curriculum_window_episodes < PATHFINDER_CURRICULUM_WINDOW) { + return; } - s->shortest_path_len = pathfinder_shortest_path(s); + + if (env->curriculum_window_successes >= PATHFINDER_CURRICULUM_SUCCESS_THRESHOLD && + pathfinder_curriculum_max_solution_len(env) < PATHFINDER_MAX_SOLUTION_LEN) { + env->curriculum_level++; + } + env->curriculum_window_episodes = 0; + env->curriculum_window_successes = 0; } void add_log(Pathfinder* env) { @@ -341,16 +450,22 @@ void add_log(Pathfinder* env) { } } + pathfinder_update_curriculum(env, s->success); + env->log.perf += success; env->log.score += success * efficiency; env->log.episode_return += s->episode_return; env->log.episode_length += (float)s->tick; env->log.success += success; env->log.wall_hits += (float)s->wall_hits; + env->log.revisits += (float)s->revisit_count; + env->log.known_wall_deaths += (float)s->known_wall_death; env->log.known_walls += (float)s->known_wall_count; env->log.known_open_edges += (float)s->known_open_count; env->log.shortest_path_len += (float)s->shortest_path_len; env->log.agent_path_len += (float)s->agent_path_len; + env->log.curriculum_level += (float)env->curriculum_level; + env->log.curriculum_max_solution_len += (float)pathfinder_curriculum_max_solution_len(env); env->log.n += 1.0f; } @@ -382,6 +497,7 @@ void c_reset(Pathfinder* env) { s->agent_row = 0; s->agent_col = 0; pathfinder_generate_maze(env); + pathfinder_mark_visited(s, s->agent_row, s->agent_col); pathfinder_update_observations(env); } @@ -420,12 +536,23 @@ void c_step(Pathfinder* env) { if (s->true_walls[wall]) { s->wall_hits++; reward += was_known ? PATHFINDER_KNOWN_WALL_PENALTY : PATHFINDER_NEW_WALL_PENALTY; + if (was_known) { + s->known_wall_death = 1; + env->terminals[0] = 1.0f; + } } else if (!pathfinder_in_bounds(next_row, next_col)) { reward += PATHFINDER_IMPOSSIBLE_PENALTY; } else { + bool revisited = s->visited[next_row][next_col] != 0; s->agent_row = next_row; s->agent_col = next_col; s->agent_path_len++; + if (revisited) { + s->revisit_count++; + reward += PATHFINDER_REVISIT_PENALTY; + } else { + pathfinder_mark_visited(s, next_row, next_col); + } if (s->agent_row == s->goal_row && s->agent_col == s->goal_col) { s->success = 1; reward += PATHFINDER_GOAL_REWARD; @@ -477,6 +604,7 @@ static const Color PATHFINDER_KNOWN_OPEN = {75, 196, 118, 255}; static const Color PATHFINDER_AGENT = {0, 187, 187, 255}; static const Color PATHFINDER_GOAL = {232, 184, 58, 255}; static const Color PATHFINDER_START = {118, 146, 150, 255}; +static const Color PATHFINDER_VISITED = {0, 187, 187, 42}; static PathfinderClient* pathfinder_make_client(void) { PathfinderClient* client = (PathfinderClient*)calloc(1, sizeof(PathfinderClient)); @@ -539,6 +667,10 @@ static void pathfinder_draw_board(Pathfinder* env) { Color cell_color = ((row + col) & 1) ? PATHFINDER_CELL_A : PATHFINDER_CELL_B; DrawRectangle(pathfinder_cell_x(col), pathfinder_cell_y(row), PATHFINDER_RENDER_TILE - 1, PATHFINDER_RENDER_TILE - 1, cell_color); + if (s->visited[row][col]) { + DrawRectangle(pathfinder_cell_x(col) + 8, pathfinder_cell_y(row) + 8, + PATHFINDER_RENDER_TILE - 17, PATHFINDER_RENDER_TILE - 17, PATHFINDER_VISITED); + } } } @@ -649,6 +781,11 @@ static void pathfinder_draw_panel(Pathfinder* env) { DrawText(TextFormat("Wall hits: %i", s->wall_hits), x, y, 18, PATHFINDER_TEXT); y += 24; + DrawText(TextFormat("Revisits: %i", s->revisit_count), x, y, 18, PATHFINDER_TEXT); + y += 24; + DrawText(TextFormat("Known-wall deaths: %.0f", env->log.known_wall_deaths), + x, y, 18, PATHFINDER_KNOWN_WALL); + y += 24; DrawText(TextFormat("Shortest path: %i", s->shortest_path_len), x, y, 18, PATHFINDER_TEXT); y += 24; DrawText(TextFormat("Agent path: %i", s->agent_path_len), x, y, 18, PATHFINDER_TEXT); @@ -658,6 +795,9 @@ static void pathfinder_draw_panel(Pathfinder* env) { y += 24; DrawText(TextFormat("Avg success: %.3f", env->log.n > 0.0f ? env->log.success / env->log.n : 0.0f), x, y, 18, PATHFINDER_TEXT); + y += 24; + DrawText(TextFormat("Curriculum: %i / %i moves", env->curriculum_level, + pathfinder_curriculum_max_solution_len(env)), x, y, 18, PATHFINDER_GOAL); DrawText("Arrows/WASD move | R reset", PATHFINDER_RENDER_BOARD_X, PATHFINDER_RENDER_HEIGHT - 30, 18, PATHFINDER_MUTED); diff --git a/ocean/pathfinder/tests/test_pathfinder_core.c b/ocean/pathfinder/tests/test_pathfinder_core.c index e17b95a5d9..865f193ca7 100644 --- a/ocean/pathfinder/tests/test_pathfinder_core.c +++ b/ocean/pathfinder/tests/test_pathfinder_core.c @@ -127,7 +127,7 @@ static void test_blocked_edge_reveals_and_stays(void) { assert(terminals[0] == 0.0f); } -static void test_known_wall_has_only_repeat_penalty(void) { +static void test_known_wall_repeat_terminates_with_penalty(void) { Pathfinder env; float obs[PATHFINDER_OBS_SIZE]; float actions[1] = {0}; @@ -151,10 +151,46 @@ static void test_known_wall_has_only_repeat_penalty(void) { actions[0] = PATHFINDER_ACT_EAST; c_step(&env); assert(fabsf(rewards[0] - PATHFINDER_STEP_PENALTY) < 1e-6f); + assert(terminals[0] == 0.0f); c_step(&env); assert(fabsf(rewards[0] - (PATHFINDER_STEP_PENALTY + PATHFINDER_KNOWN_WALL_PENALTY)) < 1e-6f); + assert(terminals[0] == 1.0f); + assert(env.log.n >= 1.0f); + assert(env.log.success == 0.0f); +} + +static void test_revisiting_previously_left_square_has_penalty(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + c_reset(&env); + + memset(env.state.true_walls, 1, sizeof(env.state.true_walls)); + for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { + env.state.known_walls[i] = PATHFINDER_UNKNOWN; + } + env.state.agent_row = 0; + env.state.agent_col = 0; + env.state.goal_row = 5; + env.state.goal_col = 5; + int east_wall = pathfinder_wall_between(0, 0, 0, 1); + env.state.true_walls[east_wall] = 0; + refresh_state(&env); + + actions[0] = PATHFINDER_ACT_EAST; + c_step(&env); + assert(fabsf(rewards[0] - PATHFINDER_STEP_PENALTY) < 1e-6f); + + actions[0] = PATHFINDER_ACT_WEST; + c_step(&env); + assert(fabsf(rewards[0] - + (PATHFINDER_STEP_PENALTY + PATHFINDER_REVISIT_PENALTY)) < 1e-6f); + assert(terminals[0] == 0.0f); } static void test_max_solution_len_limits_curriculum_distance(void) { @@ -174,6 +210,40 @@ static void test_max_solution_len_limits_curriculum_distance(void) { } } +static void test_curriculum_graduates_to_longer_mazes(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + env.branch_prob = 0.0f; + env.loop_prob = 0.0f; + env.max_solution_len = 4; + c_reset(&env); + + assert(pathfinder_curriculum_max_solution_len(&env) == 4); + for (int i = 0; i < PATHFINDER_CURRICULUM_WINDOW; i++) { + env.state.success = 1; + env.state.tick = 1; + env.state.shortest_path_len = 1; + env.state.agent_path_len = 1; + add_log(&env); + } + assert(pathfinder_curriculum_max_solution_len(&env) == 5); + + bool saw_longer_than_four = false; + for (int i = 0; i < 100; i++) { + c_reset(&env); + assert(env.state.shortest_path_len >= 3); + assert(env.state.shortest_path_len <= 5); + if (env.state.shortest_path_len > 4) { + saw_longer_than_four = true; + } + } + assert(saw_longer_than_four); +} + static void test_reaching_goal_terminates(void) { Pathfinder env; float obs[PATHFINDER_OBS_SIZE]; @@ -210,8 +280,10 @@ int main(void) { test_generated_mazes_connect_a1_to_goal(); test_open_edge_reveals_and_moves(); test_blocked_edge_reveals_and_stays(); - test_known_wall_has_only_repeat_penalty(); + test_known_wall_repeat_terminates_with_penalty(); + test_revisiting_previously_left_square_has_penalty(); test_max_solution_len_limits_curriculum_distance(); + test_curriculum_graduates_to_longer_mazes(); test_reaching_goal_terminates(); printf("pathfinder core tests passed\n"); return 0; From 0e9e3bee422dfb8fee4f10609f03445bc2f4b60b Mon Sep 17 00:00:00 2001 From: Kinvert Date: Thu, 4 Jun 2026 15:21:14 -0700 Subject: [PATCH 05/16] Profile and optimize Pathfinder reset path --- ocean/pathfinder/binding.c | 1 + ocean/pathfinder/pathfinder.h | 102 ++--- ocean/pathfinder/tests/bench_pathfinder.c | 242 ++++++++++++ .../pathfinder/tests/benchmark_pathfinder.py | 160 ++++++++ .../pathfinder/tests/benchmark_pathfinder.sh | 22 ++ ocean/pathfinder/tests/test_pathfinder_core.c | 369 +++++++++++++++--- 6 files changed, 792 insertions(+), 104 deletions(-) create mode 100644 ocean/pathfinder/tests/bench_pathfinder.c create mode 100755 ocean/pathfinder/tests/benchmark_pathfinder.py create mode 100755 ocean/pathfinder/tests/benchmark_pathfinder.sh diff --git a/ocean/pathfinder/binding.c b/ocean/pathfinder/binding.c index 063386a6a2..94bc3a3a4f 100644 --- a/ocean/pathfinder/binding.c +++ b/ocean/pathfinder/binding.c @@ -3,6 +3,7 @@ #define NUM_ATNS 1 #define ACT_SIZES {4} #define OBS_TENSOR_T FloatTensor +#define MY_ACTION_MASK PATHFINDER_NUM_ACTIONS #define Env Pathfinder static inline void puffer_state_refresh(Pathfinder* env) { refresh_state(env); } diff --git a/ocean/pathfinder/pathfinder.h b/ocean/pathfinder/pathfinder.h index fec41cfbce..26c0e2a281 100644 --- a/ocean/pathfinder/pathfinder.h +++ b/ocean/pathfinder/pathfinder.h @@ -45,6 +45,8 @@ #define PATHFINDER_STEP_PENALTY -0.001f #define PATHFINDER_NEW_WALL_PENALTY 0.0f #define PATHFINDER_KNOWN_WALL_PENALTY -0.01f +#define PATHFINDER_KNOWN_WALL_DEATH_PENALTY -0.05f +#define PATHFINDER_NEW_CELL_REWARD 0.01f #define PATHFINDER_REVISIT_PENALTY -0.01f #define PATHFINDER_IMPOSSIBLE_PENALTY -0.01f #define PATHFINDER_GOAL_REWARD 1.0f @@ -99,6 +101,7 @@ typedef struct Pathfinder { float* actions; float* rewards; float* terminals; + unsigned char* action_mask; int num_agents; unsigned int rng; float branch_prob; @@ -165,6 +168,24 @@ static inline float pathfinder_rand_float(Pathfinder* env) { return (float)(pathfinder_rand(env) >> 8) / 16777216.0f; } +static inline bool pathfinder_rand_chance_u8(Pathfinder* env, int threshold, + unsigned int* samples, int* remaining) { + if (threshold <= 0) { + return false; + } + if (threshold >= 256) { + return true; + } + if (*remaining == 0) { + *samples = pathfinder_rand(env); + *remaining = 4; + } + unsigned int sample = *samples & 0xffu; + *samples >>= 8; + *remaining -= 1; + return (int)sample < threshold; +} + static inline int pathfinder_wall_between(int row, int col, int next_row, int next_col) { if (next_row == row && next_col == col + 1) { return pathfinder_v_wall(row, col + 1); @@ -267,8 +288,27 @@ static void pathfinder_recount_known(State* s) { } } +static void pathfinder_update_action_mask(Pathfinder* env) { + if (env->action_mask == NULL) { + return; + } + + for (int action = 0; action < PATHFINDER_NUM_ACTIONS; action++) { + int d_row; + int d_col; + pathfinder_action_delta(action, &d_row, &d_col); + int next_row = env->state.agent_row + d_row; + int next_col = env->state.agent_col + d_col; + int wall = pathfinder_wall_between( + env->state.agent_row, env->state.agent_col, next_row, next_col); + env->action_mask[action] = + (wall >= 0 && env->state.known_walls[wall] == PATHFINDER_WALL) ? 0 : 1; + } +} + static void pathfinder_update_observations(Pathfinder* env) { pathfinder_recount_known(&env->state); + pathfinder_update_action_mask(env); if (env->observations == NULL) { return; } @@ -324,23 +364,27 @@ static void pathfinder_open_random_edges(Pathfinder* env) { float open_prob = env->branch_prob + env->loop_prob; if (open_prob < 0.0f) open_prob = 0.0f; if (open_prob > 0.95f) open_prob = 0.95f; + int open_threshold = (int)(open_prob * 256.0f); + int entry_threshold = (int)(env->extra_entry_prob * 256.0f); + unsigned int samples = 0; + int remaining = 0; for (int row = 0; row < PATHFINDER_ROWS; row++) { for (int col = 0; col < PATHFINDER_COLS - 1; col++) { - if (pathfinder_rand_float(env) < open_prob) { - pathfinder_open_edge(s, row, col, row, col + 1); + if (pathfinder_rand_chance_u8(env, open_threshold, &samples, &remaining)) { + s->true_walls[pathfinder_v_wall(row, col + 1)] = 0; } } } for (int row = 0; row < PATHFINDER_ROWS - 1; row++) { for (int col = 0; col < PATHFINDER_COLS; col++) { - if (pathfinder_rand_float(env) < open_prob) { - pathfinder_open_edge(s, row, col, row + 1, col); + if (pathfinder_rand_chance_u8(env, open_threshold, &samples, &remaining)) { + s->true_walls[pathfinder_h_wall(row + 1, col)] = 0; } } } for (int row = 1; row < PATHFINDER_ROWS; row++) { - if (pathfinder_rand_float(env) < env->extra_entry_prob) { + if (pathfinder_rand_chance_u8(env, entry_threshold, &samples, &remaining)) { s->true_walls[pathfinder_v_wall(row, 0)] = 0; } } @@ -374,50 +418,18 @@ static void pathfinder_choose_goal_at_distance(Pathfinder* env, int distance) { s->goal_col = cell % PATHFINDER_COLS; } -static void pathfinder_build_fallback_path(State* s, int solution_len) { - pathfinder_init_walls(s); - solution_len = pathfinder_clamp_int(solution_len, 1, PATHFINDER_MAX_SOLUTION_LEN); - - int row = 0; - int col = 0; - int remaining = solution_len; - while (remaining > 0 && col < PATHFINDER_COLS - 1) { - pathfinder_open_edge(s, row, col, row, col + 1); - col++; - remaining--; - } - while (remaining > 0 && row < PATHFINDER_ROWS - 1) { - pathfinder_open_edge(s, row, col, row + 1, col); - row++; - remaining--; - } - - s->goal_row = row; - s->goal_col = col; - s->shortest_path_len = pathfinder_shortest_path(s); -} - static void pathfinder_generate_maze(Pathfinder* env) { State* s = &env->state; int min_solution_len = pathfinder_curriculum_min_solution_len(env); int max_solution_len = pathfinder_curriculum_max_solution_len(env); + int span = max_solution_len - min_solution_len + 1; + int target_len = min_solution_len + (int)(pathfinder_rand(env) % (unsigned int)span); - for (int attempt = 0; attempt < 128; attempt++) { - pathfinder_init_walls(s); - - int span = max_solution_len - min_solution_len + 1; - int target_len = min_solution_len + (int)(pathfinder_rand(env) % (unsigned int)span); - pathfinder_choose_goal_at_distance(env, target_len); - - pathfinder_carve_solution(env); - pathfinder_open_random_edges(env); - s->shortest_path_len = pathfinder_shortest_path(s); - if (s->shortest_path_len >= min_solution_len && s->shortest_path_len <= max_solution_len) { - return; - } - } - - pathfinder_build_fallback_path(s, max_solution_len); + pathfinder_init_walls(s); + pathfinder_choose_goal_at_distance(env, target_len); + pathfinder_carve_solution(env); + pathfinder_open_random_edges(env); + s->shortest_path_len = s->goal_row + s->goal_col; } static void pathfinder_update_curriculum(Pathfinder* env, int success) { @@ -537,6 +549,7 @@ void c_step(Pathfinder* env) { s->wall_hits++; reward += was_known ? PATHFINDER_KNOWN_WALL_PENALTY : PATHFINDER_NEW_WALL_PENALTY; if (was_known) { + reward += PATHFINDER_KNOWN_WALL_DEATH_PENALTY; s->known_wall_death = 1; env->terminals[0] = 1.0f; } @@ -552,6 +565,7 @@ void c_step(Pathfinder* env) { reward += PATHFINDER_REVISIT_PENALTY; } else { pathfinder_mark_visited(s, next_row, next_col); + reward += PATHFINDER_NEW_CELL_REWARD; } if (s->agent_row == s->goal_row && s->agent_col == s->goal_col) { s->success = 1; diff --git a/ocean/pathfinder/tests/bench_pathfinder.c b/ocean/pathfinder/tests/bench_pathfinder.c new file mode 100644 index 0000000000..d7a317c166 --- /dev/null +++ b/ocean/pathfinder/tests/bench_pathfinder.c @@ -0,0 +1,242 @@ +#define _POSIX_C_SOURCE 200809L +#define PATHFINDER_NO_RENDER + +#include +#include +#include "../pathfinder.h" + +static double now_seconds(void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0; +} + +static void setup_env(Pathfinder* env, float* obs, float* actions, + float* rewards, float* terminals) { + memset(env, 0, sizeof(*env)); + env->observations = obs; + env->actions = actions; + env->rewards = rewards; + env->terminals = terminals; + env->num_agents = 1; + env->branch_prob = 0.35f; + env->loop_prob = 0.10f; + env->extra_entry_prob = 0.0f; + env->min_solution_len = 1; + env->max_solution_len = 4; + env->max_steps = 128; + env->rng = 12345; + init(env); +} + +static long parse_long(const char* value, long fallback) { + char* end = NULL; + long parsed = strtol(value, &end, 10); + if (end == value || parsed <= 0) { + return fallback; + } + return parsed; +} + +static void bench_resets(Pathfinder* env, long resets, int curriculum_level) { + env->curriculum_level = curriculum_level; + double t0 = now_seconds(); + long shortest_sum = 0; + int min_shortest = PATHFINDER_MAX_SOLUTION_LEN; + int max_shortest = 0; + for (long i = 0; i < resets; i++) { + c_reset(env); + int sp = env->state.shortest_path_len; + shortest_sum += sp; + if (sp < min_shortest) min_shortest = sp; + if (sp > max_shortest) max_shortest = sp; + } + double elapsed = now_seconds() - t0; + double reset_sps = (double)resets / elapsed; + double avg_shortest = (double)shortest_sum / (double)resets; + printf("reset_bench curriculum_level=%d resets=%ld seconds=%.6f reset_sps=%.2f avg_shortest=%.3f min_shortest=%d max_shortest=%d\n", + curriculum_level, resets, elapsed, reset_sps, avg_shortest, min_shortest, max_shortest); +} + +static void bench_steps(Pathfinder* env, long steps) { + c_reset(env); + double t0 = now_seconds(); + double reward_sum = 0.0; + for (long i = 0; i < steps; i++) { + env->actions[0] = (float)(pathfinder_rand(env) % PATHFINDER_NUM_ACTIONS); + c_step(env); + reward_sum += env->rewards[0]; + } + double elapsed = now_seconds() - t0; + double step_sps = (double)steps / elapsed; + printf("step_bench steps=%ld seconds=%.6f step_sps=%.2f episodes=%.0f success=%.6f reward_sum=%.3f curriculum_level=%d curriculum_max_solution_len=%d\n", + steps, elapsed, step_sps, env->log.n, + env->log.n > 0.0f ? env->log.success / env->log.n : 0.0f, + reward_sum, env->curriculum_level, pathfinder_curriculum_max_solution_len(env)); +} + +static void bench_reset_components(Pathfinder* env, long iters) { + double t0 = now_seconds(); + for (long i = 0; i < iters; i++) { + memset(&env->state, 0, sizeof(env->state)); + } + double memset_sec = now_seconds() - t0; + + t0 = now_seconds(); + for (long i = 0; i < iters; i++) { + memset(&env->state, 0, sizeof(env->state)); + env->state.agent_row = 0; + env->state.agent_col = 0; + pathfinder_generate_maze(env); + } + double gen_sec = now_seconds() - t0; + + t0 = now_seconds(); + for (long i = 0; i < iters; i++) { + pathfinder_update_observations(env); + } + double obs_sec = now_seconds() - t0; + + t0 = now_seconds(); + for (long i = 0; i < iters; i++) { + pathfinder_update_action_mask(env); + } + double mask_sec = now_seconds() - t0; + + printf("reset_components iters=%ld memset_ns=%.2f gen_plus_memset_ns=%.2f obs_ns=%.2f mask_ns=%.2f\n", + iters, + 1e9 * memset_sec / (double)iters, + 1e9 * gen_sec / (double)iters, + 1e9 * obs_sec / (double)iters, + 1e9 * mask_sec / (double)iters); + + t0 = now_seconds(); + for (long i = 0; i < iters; i++) { + pathfinder_init_walls(&env->state); + } + double init_walls_sec = now_seconds() - t0; + + t0 = now_seconds(); + for (long i = 0; i < iters; i++) { + pathfinder_choose_goal_at_distance(env, 4); + } + double choose_goal_sec = now_seconds() - t0; + + pathfinder_init_walls(&env->state); + pathfinder_choose_goal_at_distance(env, 4); + t0 = now_seconds(); + for (long i = 0; i < iters; i++) { + pathfinder_init_walls(&env->state); + env->state.goal_row = 2; + env->state.goal_col = 2; + pathfinder_carve_solution(env); + } + double carve_sec = now_seconds() - t0; + + pathfinder_init_walls(&env->state); + env->state.goal_row = 2; + env->state.goal_col = 2; + pathfinder_carve_solution(env); + t0 = now_seconds(); + for (long i = 0; i < iters; i++) { + pathfinder_open_random_edges(env); + } + double random_edges_sec = now_seconds() - t0; + + printf("generation_components iters=%ld init_walls_ns=%.2f choose_goal_ns=%.2f init_plus_carve_ns=%.2f random_edges_ns=%.2f\n", + iters, + 1e9 * init_walls_sec / (double)iters, + 1e9 * choose_goal_sec / (double)iters, + 1e9 * carve_sec / (double)iters, + 1e9 * random_edges_sec / (double)iters); +} + +static void setup_open_line(Pathfinder* env) { + State* s = &env->state; + memset(s, 0, sizeof(*s)); + memset(s->true_walls, 1, sizeof(s->true_walls)); + pathfinder_reset_known(s); + s->agent_row = 0; + s->agent_col = 0; + s->goal_row = 5; + s->goal_col = 5; + for (int col = 0; col < PATHFINDER_COLS - 1; col++) { + pathfinder_open_edge(s, 0, col, 0, col + 1); + } + pathfinder_mark_visited(s, 0, 0); + pathfinder_update_observations(env); +} + +static void bench_forced_steps(Pathfinder* env, long iters) { + setup_open_line(env); + double t0 = now_seconds(); + for (long i = 0; i < iters; i++) { + if (env->state.agent_col == PATHFINDER_COLS - 1) { + setup_open_line(env); + } + env->actions[0] = PATHFINDER_ACT_EAST; + c_step(env); + } + double move_sec = now_seconds() - t0; + + memset(&env->log, 0, sizeof(env->log)); + setup_open_line(env); + int east_wall = pathfinder_wall_between(0, 0, 0, 1); + env->state.true_walls[east_wall] = 1; + pathfinder_update_observations(env); + t0 = now_seconds(); + for (long i = 0; i < iters; i++) { + env->actions[0] = PATHFINDER_ACT_EAST; + c_step(env); + if (env->terminals[0] == 0.0f) { + c_step(env); + } + setup_open_line(env); + env->state.true_walls[east_wall] = 1; + pathfinder_update_observations(env); + } + double known_wall_death_sec = now_seconds() - t0; + + printf("forced_step_components iters=%ld open_move_ns=%.2f known_wall_death_cycle_ns=%.2f\n", + iters, + 1e9 * move_sec / (double)iters, + 1e9 * known_wall_death_sec / (double)iters); +} + +int main(int argc, char** argv) { + long resets = 200000; + long steps = 5000000; + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--resets") == 0 && i + 1 < argc) { + resets = parse_long(argv[++i], resets); + } else if (strcmp(argv[i], "--steps") == 0 && i + 1 < argc) { + steps = parse_long(argv[++i], steps); + } else { + fprintf(stderr, "Usage: %s [--resets N] [--steps N]\n", argv[0]); + return 1; + } + } + + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE] = {0}; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + unsigned char action_mask[PATHFINDER_NUM_ACTIONS] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + env.action_mask = action_mask; + + bench_resets(&env, resets, 0); + bench_resets(&env, resets, PATHFINDER_MAX_SOLUTION_LEN); + bench_reset_components(&env, resets); + bench_forced_steps(&env, resets); + + memset(&env.log, 0, sizeof(env.log)); + env.curriculum_level = 0; + env.curriculum_episodes = 0; + env.curriculum_window_episodes = 0; + env.curriculum_window_successes = 0; + bench_steps(&env, steps); + c_close(&env); + return 0; +} diff --git a/ocean/pathfinder/tests/benchmark_pathfinder.py b/ocean/pathfinder/tests/benchmark_pathfinder.py new file mode 100755 index 0000000000..3840f3fd5a --- /dev/null +++ b/ocean/pathfinder/tests/benchmark_pathfinder.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +import argparse +import json +import sys +import time +from copy import deepcopy + +from pufferlib.pufferl import _resolve_backend, load_config, unroll_nested_dict + + +def base_args(): + saved_argv = sys.argv + try: + sys.argv = [saved_argv[0]] + args = load_config("pathfinder") + finally: + sys.argv = saved_argv + args["wandb"] = False + args["render_mode"] = "None" + args["checkpoint_interval"] = 10**12 + return args + + +def run_once(label, args, timesteps, eval_epochs_override): + cfg = deepcopy(args) + cfg["train"]["total_timesteps"] = timesteps + cfg["train"]["gpus"] = 1 + cfg["nccl_id"] = b"" + backend = _resolve_backend(cfg) + pufferl = backend.create_pufferl(cfg) + train_epochs = max(1, timesteps // (cfg["vec"]["total_agents"] * cfg["train"]["horizon"])) + flat = {} + eval_flat = {} + t0 = time.perf_counter() + train_t0 = t0 + try: + for _ in range(train_epochs): + backend.rollouts(pufferl) + backend.train(pufferl) + flat = {**flat, **dict(unroll_nested_dict(backend.log(pufferl)))} + train_elapsed = time.perf_counter() - train_t0 + train_steps = int(pufferl.global_step) + + eval_epochs = train_epochs // 2 if eval_epochs_override < 0 else eval_epochs_override + for _ in range(eval_epochs): + backend.rollouts(pufferl) + eval_flat = {**eval_flat, **dict(unroll_nested_dict(backend.eval_log(pufferl)))} + finally: + actual_steps = int(pufferl.global_step) + backend.close(pufferl) + + elapsed = time.perf_counter() - t0 + stats = eval_flat if eval_flat else flat + summary = { + "label": label, + "timesteps_requested": timesteps, + "train_timesteps_actual": train_steps, + "timesteps_actual": actual_steps, + "train_epochs": train_epochs, + "eval_epochs": train_epochs // 2 if eval_epochs_override < 0 else eval_epochs_override, + "train_seconds": train_elapsed, + "seconds": elapsed, + "wall_sps": train_steps / train_elapsed if train_elapsed > 0 else 0.0, + "total_wall_sps": actual_steps / elapsed if elapsed > 0 else 0.0, + "reported_sps": flat.get("SPS", 0.0), + "success": stats.get("env/success", 0.0), + "score": stats.get("env/score", 0.0), + "episode_return": stats.get("env/episode_return", 0.0), + "episode_length": stats.get("env/episode_length", 0.0), + "wall_hits": stats.get("env/wall_hits", 0.0), + "known_wall_deaths": stats.get("env/known_wall_deaths", 0.0), + "revisits": stats.get("env/revisits", 0.0), + "shortest_path_len": stats.get("env/shortest_path_len", 0.0), + "agent_path_len": stats.get("env/agent_path_len", 0.0), + "curriculum_level": stats.get("env/curriculum_level", 0.0), + "curriculum_max_solution_len": stats.get("env/curriculum_max_solution_len", 0.0), + "perf_rollout_sec": flat.get("perf/rollout", 0.0), + "perf_eval_env_sec": flat.get("perf/eval_env", 0.0), + "perf_train_sec": flat.get("perf/train", 0.0), + "policy_hidden_size": cfg["policy"]["hidden_size"], + "policy_num_layers": cfg["policy"]["num_layers"], + "total_agents": cfg["vec"]["total_agents"], + "horizon": cfg["train"]["horizon"], + "minibatch_size": cfg["train"]["minibatch_size"], + } + return summary + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--timesteps", type=int, default=2097152) + parser.add_argument("--eval-epochs", type=int, default=-1) + parser.add_argument("--hidden-size", type=int, default=None) + parser.add_argument("--num-layers", type=int, default=None) + parser.add_argument("--total-agents", type=int, default=None) + parser.add_argument("--horizon", type=int, default=None) + parser.add_argument("--minibatch-size", type=int, default=None) + parser.add_argument("--compare-hidden-size", type=int, default=None) + parser.add_argument("--min-wall-sps", type=float, default=0.0) + parser.add_argument("--min-success", type=float, default=0.0) + parser.add_argument("--require-sps-gain", type=float, default=0.0) + parser.add_argument("--require-success-ratio", type=float, default=0.0) + opts = parser.parse_args() + + args = base_args() + if opts.hidden_size is not None: + args["policy"]["hidden_size"] = opts.hidden_size + if opts.num_layers is not None: + args["policy"]["num_layers"] = opts.num_layers + if opts.total_agents is not None: + args["vec"]["total_agents"] = opts.total_agents + if opts.horizon is not None: + args["train"]["horizon"] = opts.horizon + if opts.minibatch_size is not None: + args["train"]["minibatch_size"] = opts.minibatch_size + + if opts.compare_hidden_size is None: + single = run_once("single", args, opts.timesteps, opts.eval_epochs) + result = { + "runs": [single], + "checks": { + "passes_min_wall_sps": single["wall_sps"] >= opts.min_wall_sps, + "passes_min_success": single["success"] >= opts.min_success, + }, + } + if not result["checks"]["passes_min_wall_sps"] or not result["checks"]["passes_min_success"]: + print(json.dumps(result, indent=2, sort_keys=True)) + return 2 + else: + baseline = run_once("baseline", args, opts.timesteps, opts.eval_epochs) + candidate_args = deepcopy(args) + candidate_args["policy"]["hidden_size"] = opts.compare_hidden_size + candidate = run_once("candidate", candidate_args, opts.timesteps, opts.eval_epochs) + sps_gain = ( + candidate["wall_sps"] / baseline["wall_sps"] - 1.0 + if baseline["wall_sps"] > 0.0 else 0.0 + ) + success_ratio = ( + candidate["success"] / baseline["success"] + if baseline["success"] > 0.0 else 1.0 + ) + result = { + "runs": [baseline, candidate], + "comparison": { + "sps_gain": sps_gain, + "success_ratio": success_ratio, + "passes_sps_gain": sps_gain >= opts.require_sps_gain, + "passes_success_ratio": success_ratio >= opts.require_success_ratio, + }, + } + if not result["comparison"]["passes_sps_gain"] or not result["comparison"]["passes_success_ratio"]: + print(json.dumps(result, indent=2, sort_keys=True)) + return 2 + + print(json.dumps(result, indent=2, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/ocean/pathfinder/tests/benchmark_pathfinder.sh b/ocean/pathfinder/tests/benchmark_pathfinder.sh new file mode 100755 index 0000000000..d2180636e0 --- /dev/null +++ b/ocean/pathfinder/tests/benchmark_pathfinder.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")/../../.." + +mkdir -p build/pathfinder-tests + +${CC:-clang} -std=c11 -O3 -DNDEBUG -Wall -Wextra -Werror \ + -I. -Iocean/pathfinder -Ivendor -Iraylib-5.5_linux_amd64/include \ + ocean/pathfinder/tests/bench_pathfinder.c \ + -lm \ + -o build/pathfinder-tests/bench_pathfinder + +build/pathfinder-tests/bench_pathfinder "$@" + +if [[ "${PATHFINDER_TRAIN_BENCH:-0}" != "0" ]]; then + source .venv/bin/activate + ./build.sh pathfinder + python ocean/pathfinder/tests/benchmark_pathfinder.py \ + --timesteps "${PATHFINDER_TRAIN_TIMESTEPS:-2097152}" \ + ${PATHFINDER_TRAIN_BENCH_ARGS:-} +fi diff --git a/ocean/pathfinder/tests/test_pathfinder_core.c b/ocean/pathfinder/tests/test_pathfinder_core.c index 865f193ca7..3516942ce3 100644 --- a/ocean/pathfinder/tests/test_pathfinder_core.c +++ b/ocean/pathfinder/tests/test_pathfinder_core.c @@ -23,6 +23,24 @@ static void setup_env(Pathfinder* env, float* obs, float* actions, init(env); } +static void setup_manual_state(Pathfinder* env, int goal_row, int goal_col) { + State* s = &env->state; + memset(s, 0, sizeof(*s)); + memset(s->true_walls, 1, sizeof(s->true_walls)); + pathfinder_reset_known(s); + s->agent_row = 0; + s->agent_col = 0; + s->goal_row = goal_row; + s->goal_col = goal_col; + pathfinder_mark_visited(s, 0, 0); +} + +static void open_manual_edge(Pathfinder* env, int row, int col, int next_row, int next_col) { + pathfinder_open_edge(&env->state, row, col, next_row, next_col); + env->state.shortest_path_len = pathfinder_shortest_path(&env->state); + refresh_state(env); +} + static void test_constants(void) { assert(PATHFINDER_ROWS == 6); assert(PATHFINDER_COLS == 6); @@ -33,6 +51,29 @@ static void test_constants(void) { assert(PATHFINDER_RENDER_HEIGHT > PATHFINDER_RENDER_BOARD_Y + PATHFINDER_RENDER_BOARD_SIZE); } +static void test_wall_indexing_and_action_deltas(void) { + assert(pathfinder_v_wall(0, 0) == 0); + assert(pathfinder_v_wall(5, 6) == PATHFINDER_VERTICAL_WALLS - 1); + assert(pathfinder_h_wall(0, 0) == PATHFINDER_VERTICAL_WALLS); + assert(pathfinder_h_wall(6, 5) == PATHFINDER_NUM_WALLS - 1); + assert(pathfinder_wall_between(0, 0, 0, 1) == pathfinder_v_wall(0, 1)); + assert(pathfinder_wall_between(0, 1, 0, 0) == pathfinder_v_wall(0, 1)); + assert(pathfinder_wall_between(0, 0, 1, 0) == pathfinder_h_wall(1, 0)); + assert(pathfinder_wall_between(1, 0, 0, 0) == pathfinder_h_wall(1, 0)); + assert(pathfinder_wall_between(0, 0, 1, 1) == -1); + + int d_row; + int d_col; + pathfinder_action_delta(PATHFINDER_ACT_NORTH, &d_row, &d_col); + assert(d_row == -1 && d_col == 0); + pathfinder_action_delta(PATHFINDER_ACT_EAST, &d_row, &d_col); + assert(d_row == 0 && d_col == 1); + pathfinder_action_delta(PATHFINDER_ACT_SOUTH, &d_row, &d_col); + assert(d_row == 1 && d_col == 0); + pathfinder_action_delta(PATHFINDER_ACT_WEST, &d_row, &d_col); + assert(d_row == 0 && d_col == -1); +} + static void test_reset_initializes_a1_and_unknown_walls(void) { Pathfinder env; float obs[PATHFINDER_OBS_SIZE]; @@ -45,6 +86,8 @@ static void test_reset_initializes_a1_and_unknown_walls(void) { assert(env.state.agent_row == 0); assert(env.state.agent_col == 0); + assert(env.state.visited[0][0] == 1); + assert(env.state.visited_count == 1); assert(fabsf(obs[PATHFINDER_NUM_WALLS] - 0.0f) < 1e-6f); assert(fabsf(obs[PATHFINDER_NUM_WALLS + 1] - 0.0f) < 1e-6f); for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { @@ -52,6 +95,75 @@ static void test_reset_initializes_a1_and_unknown_walls(void) { } } +static void test_action_mask_allows_unknown_edges_on_reset(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + unsigned char action_mask[PATHFINDER_NUM_ACTIONS] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + env.action_mask = action_mask; + + c_reset(&env); + + for (int i = 0; i < PATHFINDER_NUM_ACTIONS; i++) { + assert(action_mask[i] == 1); + } +} + +static void test_action_mask_blocks_known_wall_but_forced_hit_still_dies(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + unsigned char action_mask[PATHFINDER_NUM_ACTIONS] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + env.action_mask = action_mask; + setup_manual_state(&env, 5, 5); + refresh_state(&env); + + for (int i = 0; i < PATHFINDER_NUM_ACTIONS; i++) { + assert(action_mask[i] == 1); + } + + actions[0] = PATHFINDER_ACT_EAST; + c_step(&env); + + assert(terminals[0] == 0.0f); + assert(action_mask[PATHFINDER_ACT_EAST] == 0); + assert(action_mask[PATHFINDER_ACT_NORTH] == 1); + assert(action_mask[PATHFINDER_ACT_SOUTH] == 1); + assert(action_mask[PATHFINDER_ACT_WEST] == 1); + + c_step(&env); + + assert(terminals[0] == 1.0f); + assert(env.log.known_wall_deaths == 1.0f); +} + +static void test_position_observation_updates_after_move(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + setup_manual_state(&env, 5, 5); + open_manual_edge(&env, 0, 0, 0, 1); + + actions[0] = PATHFINDER_ACT_EAST; + c_step(&env); + + assert(fabsf(obs[PATHFINDER_NUM_WALLS] - 0.2f) < 1e-6f); + assert(fabsf(obs[PATHFINDER_NUM_WALLS + 1] - 0.0f) < 1e-6f); + assert(fabsf(rewards[0] - + (PATHFINDER_STEP_PENALTY + PATHFINDER_NEW_CELL_REWARD)) < 1e-6f); + assert(env.state.visited[0][1] == 1); + assert(env.state.visited_count == 2); +} + static void test_generated_mazes_connect_a1_to_goal(void) { Pathfinder env; float obs[PATHFINDER_OBS_SIZE]; @@ -67,26 +179,32 @@ static void test_generated_mazes_connect_a1_to_goal(void) { } } -static void test_open_edge_reveals_and_moves(void) { +static void test_generated_shortest_path_matches_goal_distance(void) { Pathfinder env; float obs[PATHFINDER_OBS_SIZE]; float actions[1] = {0}; float rewards[1] = {0}; float terminals[1] = {0}; setup_env(&env, obs, actions, rewards, terminals); - c_reset(&env); + env.max_solution_len = PATHFINDER_MAX_SOLUTION_LEN; - memset(env.state.true_walls, 1, sizeof(env.state.true_walls)); - for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { - env.state.known_walls[i] = PATHFINDER_UNKNOWN; + for (int i = 0; i < 200; i++) { + c_reset(&env); + assert(env.state.shortest_path_len == pathfinder_shortest_path(&env.state)); + assert(env.state.shortest_path_len == env.state.goal_row + env.state.goal_col); } - env.state.agent_row = 0; - env.state.agent_col = 0; - env.state.goal_row = 5; - env.state.goal_col = 5; +} + +static void test_open_edge_reveals_and_moves(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + setup_manual_state(&env, 5, 5); int east_wall = pathfinder_wall_between(0, 0, 0, 1); - env.state.true_walls[east_wall] = 0; - refresh_state(&env); + open_manual_edge(&env, 0, 0, 0, 1); actions[0] = PATHFINDER_ACT_EAST; c_step(&env); @@ -94,6 +212,8 @@ static void test_open_edge_reveals_and_moves(void) { assert(env.state.agent_row == 0); assert(env.state.agent_col == 1); assert(fabsf(obs[east_wall] - 0.0f) < 1e-6f); + assert(fabsf(rewards[0] - + (PATHFINDER_STEP_PENALTY + PATHFINDER_NEW_CELL_REWARD)) < 1e-6f); assert(terminals[0] == 0.0f); } @@ -104,18 +224,8 @@ static void test_blocked_edge_reveals_and_stays(void) { float rewards[1] = {0}; float terminals[1] = {0}; setup_env(&env, obs, actions, rewards, terminals); - c_reset(&env); - - memset(env.state.true_walls, 1, sizeof(env.state.true_walls)); - for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { - env.state.known_walls[i] = PATHFINDER_UNKNOWN; - } - env.state.agent_row = 0; - env.state.agent_col = 0; - env.state.goal_row = 5; - env.state.goal_col = 5; + setup_manual_state(&env, 5, 5); int east_wall = pathfinder_wall_between(0, 0, 0, 1); - env.state.true_walls[east_wall] = 1; refresh_state(&env); actions[0] = PATHFINDER_ACT_EAST; @@ -124,7 +234,9 @@ static void test_blocked_edge_reveals_and_stays(void) { assert(env.state.agent_row == 0); assert(env.state.agent_col == 0); assert(fabsf(obs[east_wall] - 1.0f) < 1e-6f); + assert(fabsf(rewards[0] - PATHFINDER_STEP_PENALTY) < 1e-6f); assert(terminals[0] == 0.0f); + assert(env.state.known_wall_death == 0); } static void test_known_wall_repeat_terminates_with_penalty(void) { @@ -134,18 +246,7 @@ static void test_known_wall_repeat_terminates_with_penalty(void) { float rewards[1] = {0}; float terminals[1] = {0}; setup_env(&env, obs, actions, rewards, terminals); - c_reset(&env); - - memset(env.state.true_walls, 1, sizeof(env.state.true_walls)); - for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { - env.state.known_walls[i] = PATHFINDER_UNKNOWN; - } - env.state.agent_row = 0; - env.state.agent_col = 0; - env.state.goal_row = 5; - env.state.goal_col = 5; - int east_wall = pathfinder_wall_between(0, 0, 0, 1); - env.state.true_walls[east_wall] = 1; + setup_manual_state(&env, 5, 5); refresh_state(&env); actions[0] = PATHFINDER_ACT_EAST; @@ -155,42 +256,104 @@ static void test_known_wall_repeat_terminates_with_penalty(void) { c_step(&env); assert(fabsf(rewards[0] - - (PATHFINDER_STEP_PENALTY + PATHFINDER_KNOWN_WALL_PENALTY)) < 1e-6f); + (PATHFINDER_STEP_PENALTY + PATHFINDER_KNOWN_WALL_PENALTY + + PATHFINDER_KNOWN_WALL_DEATH_PENALTY)) < 1e-6f); assert(terminals[0] == 1.0f); assert(env.log.n >= 1.0f); assert(env.log.success == 0.0f); + assert(env.log.known_wall_deaths == 1.0f); } -static void test_revisiting_previously_left_square_has_penalty(void) { +static void test_invalid_action_penalizes_without_terminating(void) { Pathfinder env; float obs[PATHFINDER_OBS_SIZE]; float actions[1] = {0}; float rewards[1] = {0}; float terminals[1] = {0}; setup_env(&env, obs, actions, rewards, terminals); - c_reset(&env); + setup_manual_state(&env, 5, 5); + refresh_state(&env); - memset(env.state.true_walls, 1, sizeof(env.state.true_walls)); - for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { - env.state.known_walls[i] = PATHFINDER_UNKNOWN; - } - env.state.agent_row = 0; - env.state.agent_col = 0; - env.state.goal_row = 5; - env.state.goal_col = 5; - int east_wall = pathfinder_wall_between(0, 0, 0, 1); - env.state.true_walls[east_wall] = 0; + actions[0] = 99; + c_step(&env); + + assert(env.state.agent_row == 0); + assert(env.state.agent_col == 0); + assert(fabsf(rewards[0] - + (PATHFINDER_STEP_PENALTY + PATHFINDER_IMPOSSIBLE_PENALTY)) < 1e-6f); + assert(terminals[0] == 0.0f); +} + +static void test_boundary_action_reveals_wall(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + setup_manual_state(&env, 5, 5); refresh_state(&env); - actions[0] = PATHFINDER_ACT_EAST; + actions[0] = PATHFINDER_ACT_NORTH; c_step(&env); + + int north_wall = pathfinder_wall_between(0, 0, -1, 0); + assert(north_wall == pathfinder_h_wall(0, 0)); + assert(env.state.agent_row == 0); + assert(env.state.agent_col == 0); + assert(fabsf(obs[north_wall] - PATHFINDER_WALL) < 1e-6f); assert(fabsf(rewards[0] - PATHFINDER_STEP_PENALTY) < 1e-6f); + assert(terminals[0] == 0.0f); +} + +static void test_revisiting_previously_left_square_has_penalty(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + setup_manual_state(&env, 5, 5); + open_manual_edge(&env, 0, 0, 0, 1); + + actions[0] = PATHFINDER_ACT_EAST; + c_step(&env); + assert(fabsf(rewards[0] - + (PATHFINDER_STEP_PENALTY + PATHFINDER_NEW_CELL_REWARD)) < 1e-6f); actions[0] = PATHFINDER_ACT_WEST; c_step(&env); assert(fabsf(rewards[0] - (PATHFINDER_STEP_PENALTY + PATHFINDER_REVISIT_PENALTY)) < 1e-6f); assert(terminals[0] == 0.0f); + assert(env.state.revisit_count == 1); +} + +static void test_known_open_edge_to_new_square_has_no_extra_penalty(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + setup_manual_state(&env, 5, 5); + open_manual_edge(&env, 0, 0, 0, 1); + open_manual_edge(&env, 0, 1, 0, 2); + + actions[0] = PATHFINDER_ACT_EAST; + c_step(&env); + int second_wall = pathfinder_wall_between(0, 1, 0, 2); + env.state.known_walls[second_wall] = PATHFINDER_OPEN; + refresh_state(&env); + + actions[0] = PATHFINDER_ACT_EAST; + c_step(&env); + + assert(env.state.agent_col == 2); + assert(fabsf(rewards[0] - + (PATHFINDER_STEP_PENALTY + PATHFINDER_NEW_CELL_REWARD)) < 1e-6f); + assert(env.state.revisit_count == 0); + assert(terminals[0] == 0.0f); } static void test_max_solution_len_limits_curriculum_distance(void) { @@ -244,46 +407,132 @@ static void test_curriculum_graduates_to_longer_mazes(void) { assert(saw_longer_than_four); } -static void test_reaching_goal_terminates(void) { +static void test_curriculum_does_not_graduate_below_success_threshold(void) { Pathfinder env; float obs[PATHFINDER_OBS_SIZE]; float actions[1] = {0}; float rewards[1] = {0}; float terminals[1] = {0}; setup_env(&env, obs, actions, rewards, terminals); + env.max_solution_len = 4; c_reset(&env); - memset(env.state.true_walls, 1, sizeof(env.state.true_walls)); - for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { - env.state.known_walls[i] = PATHFINDER_UNKNOWN; + for (int i = 0; i < PATHFINDER_CURRICULUM_WINDOW; i++) { + env.state.success = i < PATHFINDER_CURRICULUM_SUCCESS_THRESHOLD - 1; + env.state.tick = 1; + env.state.shortest_path_len = 1; + env.state.agent_path_len = 1; + add_log(&env); } - env.state.agent_row = 0; - env.state.agent_col = 0; - env.state.goal_row = 0; - env.state.goal_col = 1; - int east_wall = pathfinder_wall_between(0, 0, 0, 1); - env.state.true_walls[east_wall] = 0; - refresh_state(&env); + + assert(env.curriculum_level == 0); + assert(pathfinder_curriculum_max_solution_len(&env) == 4); + assert(env.curriculum_window_episodes == 0); + assert(env.curriculum_window_successes == 0); +} + +static void test_curriculum_caps_at_board_max(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + env.max_solution_len = 4; + env.curriculum_level = PATHFINDER_MAX_SOLUTION_LEN; + + assert(pathfinder_curriculum_max_solution_len(&env) == PATHFINDER_MAX_SOLUTION_LEN); + assert(pathfinder_curriculum_min_solution_len(&env) == PATHFINDER_MAX_SOLUTION_LEN - 2); +} + +static void test_generation_can_reach_board_max_distance(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + env.branch_prob = 0.0f; + env.loop_prob = 0.0f; + env.max_solution_len = PATHFINDER_MAX_SOLUTION_LEN; + + bool saw_max = false; + for (int i = 0; i < 200; i++) { + c_reset(&env); + assert(env.state.shortest_path_len >= PATHFINDER_MAX_SOLUTION_LEN - 2); + assert(env.state.shortest_path_len <= PATHFINDER_MAX_SOLUTION_LEN); + if (env.state.shortest_path_len == PATHFINDER_MAX_SOLUTION_LEN) { + saw_max = true; + } + } + assert(saw_max); +} + +static void test_max_steps_terminates_and_logs_failure(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + env.max_steps = 1; + setup_manual_state(&env, 5, 5); + open_manual_edge(&env, 0, 0, 0, 1); + + actions[0] = PATHFINDER_ACT_EAST; + c_step(&env); + + assert(terminals[0] == 1.0f); + assert(fabsf(rewards[0] - + (PATHFINDER_STEP_PENALTY + PATHFINDER_NEW_CELL_REWARD)) < 1e-6f); + assert(env.log.n == 1.0f); + assert(env.log.success == 0.0f); + assert(env.log.episode_length == 1.0f); +} + +static void test_reaching_goal_terminates(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + setup_manual_state(&env, 0, 1); + open_manual_edge(&env, 0, 0, 0, 1); actions[0] = PATHFINDER_ACT_EAST; c_step(&env); assert(terminals[0] == 1.0f); - assert(rewards[0] > 0.9f); + assert(fabsf(rewards[0] - + (PATHFINDER_STEP_PENALTY + PATHFINDER_NEW_CELL_REWARD + + PATHFINDER_GOAL_REWARD)) < 1e-6f); assert(env.log.success >= 1.0f); assert(env.log.n >= 1.0f); } int main(void) { test_constants(); + test_wall_indexing_and_action_deltas(); test_reset_initializes_a1_and_unknown_walls(); + test_action_mask_allows_unknown_edges_on_reset(); + test_action_mask_blocks_known_wall_but_forced_hit_still_dies(); + test_position_observation_updates_after_move(); test_generated_mazes_connect_a1_to_goal(); + test_generated_shortest_path_matches_goal_distance(); test_open_edge_reveals_and_moves(); test_blocked_edge_reveals_and_stays(); test_known_wall_repeat_terminates_with_penalty(); + test_invalid_action_penalizes_without_terminating(); + test_boundary_action_reveals_wall(); test_revisiting_previously_left_square_has_penalty(); + test_known_open_edge_to_new_square_has_no_extra_penalty(); test_max_solution_len_limits_curriculum_distance(); test_curriculum_graduates_to_longer_mazes(); + test_curriculum_does_not_graduate_below_success_threshold(); + test_curriculum_caps_at_board_max(); + test_generation_can_reach_board_max_distance(); + test_max_steps_terminates_and_logs_failure(); test_reaching_goal_terminates(); printf("pathfinder core tests passed\n"); return 0; From 76b0cfe77cbc6d01e70a020a65877571c512127a Mon Sep 17 00:00:00 2001 From: Kinvert Date: Thu, 4 Jun 2026 15:52:01 -0700 Subject: [PATCH 06/16] Retry Pathfinder failures on same map --- AGENTS.md | 15 +- PATHFINDER_SPEC.md | 62 ++++++-- ocean/pathfinder/pathfinder.h | 51 ++++--- ocean/pathfinder/tests/bench_pathfinder.c | 2 - ocean/pathfinder/tests/test_pathfinder_core.c | 138 ++++++++++++------ 5 files changed, 176 insertions(+), 92 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 380a1dd255..ae7ba67a06 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -177,10 +177,12 @@ it is not the best state-memory reference on this branch. - Fixed board size: 6x6. - Spawn: `A1`. -- Hidden pawn: random non-`A1` cell, and generated maze must connect `A1` to - it. -- Initial curriculum: `env.max_solution_len = 4` keeps early targets close to - `A1`; set it to `0` to disable the upper bound. +- Hidden pawn: generated at the current curriculum distance from `A1`, and the + maze must connect `A1` to it. +- Initial curriculum: `env.max_solution_len = 4` starts targets exactly 4 moves + from `A1`; each solve advances the next generated puzzle by 1 move, capped at + the board maximum. Set `max_solution_len = 0` only when you want to start at + the board maximum. - Actions: four discrete moves. - Observation: - 84 wall slots as floats: @@ -193,6 +195,11 @@ it is not the best state-memory reference on this branch. - Rewards: - first-time wall discovery has no extra penalty beyond step cost - repeated known-wall hits get a small penalty + - repeated known-wall hits terminate the attempt + - first visits get a small discovery reward + - revisits get a small penalty +- Failed attempts reset the agent to `A1` on the same map and keep discovered + wall/open knowledge. Solves generate the next map and advance curriculum. - First implementation should keep `train.state_buffer_size = 0` until deterministic state roundtrip tests exist. diff --git a/PATHFINDER_SPEC.md b/PATHFINDER_SPEC.md index aecb17287b..eb4f47aedd 100644 --- a/PATHFINDER_SPEC.md +++ b/PATHFINDER_SPEC.md @@ -1,6 +1,6 @@ # Pathfinder Environment Spec -Status: draft, ready for user review +Status: implemented baseline; update as behavior changes Workspace: `/home/claude/pathfinder` @@ -83,9 +83,9 @@ Boundary behavior: The generator must produce legal, solvable layouts without hand-authored maps. -Episode generation: +Puzzle generation: -1. Pick a hidden pawn cell. Default: uniform over all non-`A1` cells. +1. Pick a hidden pawn cell at the current curriculum distance from `A1`. 2. Initialize all wall slots to blocked. 3. Open the left entry edge for `A1`. 4. Carve at least one orthogonal path from `A1` to the hidden pawn. @@ -98,21 +98,34 @@ Episode generation: Default generator style: -- Use a randomized depth-first or randomized Prim-style graph carver over the - 6x6 cell graph. -- Preserve at least one path from `A1` to the pawn. -- Add branch density as a difficulty knob rather than trying to exactly copy - human barricade layouts. +- Choose a target with Manhattan distance equal to the current curriculum + length. +- Carve a randomized monotonic path from `A1` to that target. +- Open additional random internal edges as branch/loop density knobs rather + than trying to exactly copy human barricade layouts. +- Because the target distance is Manhattan distance from `A1`, random extra + edges cannot create a shorter path than the curriculum length. + +Curriculum: + +- `max_solution_len` is the starting curriculum distance. The default training + value is `4`. +- `max_solution_len = 0` starts at the board maximum distance. +- Each successful solve increments the next generated puzzle distance by one, + capped at `PATHFINDER_MAX_SOLUTION_LEN`. +- Failed attempts do not advance curriculum. +- Failed attempts restart the agent at `A1` on the same map and preserve + discovered wall/open observations, so map generation happens only after a + solve or external reset. Config knobs: - `branch_prob`: probability of adding false branches from the main route. - `loop_prob`: probability of opening extra internal edges after carving. - `extra_entry_prob`: probability each non-`A1` column-1 entrance is open. -- `min_solution_len`: reject mazes with a shortest solution path below this. -- `max_solution_len`: reject mazes with a shortest solution path above this. - Use a small value for early curriculum so the hidden pawn starts close to - `A1`; `0` disables the upper bound. +- `min_solution_len`: reserved for future variants; the current curriculum uses + an exact generated distance. +- `max_solution_len`: starting exact solution distance. - `max_steps`: timeout. - `seed`: inherited from vector env config. @@ -168,17 +181,30 @@ Default reward model: - `+1.0` for reaching the hidden pawn. - `-0.001` per step. +- `+0.01` for first entering a cell in the current attempt. - `0.0` extra penalty for hitting a newly discovered wall; the agent paid the step cost but gained information. - `-0.01` for hitting a wall that was already known. +- `-0.05` extra penalty and terminal attempt reset for hitting a known wall. +- `-0.01` for revisiting a square that was previously left in the current + attempt. - `-0.01` for impossible movement, such as attempting to exit through the left edge in v1. -- `0.0` for a successful nonterminal move, except for the step penalty. Termination: - Success: agent reaches the hidden pawn. - Timeout: `tick >= max_steps`. +- Known-wall death: agent tries to move through a wall that is already observed + as blocked. + +Reset after terminal: + +- Success logs the episode, advances curriculum by one move when not capped, + and generates a new map. +- Timeout and known-wall death log the episode, then reset only the attempt + state: position returns to `A1`, tick/path/revisit counters clear, and the + same true map plus known wall/open observations remain. Logged metrics: @@ -188,10 +214,14 @@ Logged metrics: - `episode_length` - `success` - `wall_hits` +- `revisits` +- `known_wall_deaths` - `known_walls` - `known_open_edges` - `shortest_path_len` - `agent_path_len` +- `curriculum_level` +- `curriculum_max_solution_len` - `n` ## PufferLib Integration @@ -223,6 +253,7 @@ The env should follow current PufferLib 5 Ocean patterns: - `NUM_ATNS 1` - `ACT_SIZES {4}` - `OBS_TENSOR_T FloatTensor` + - `MY_ACTION_MASK PATHFINDER_NUM_ACTIONS` - `Env Pathfinder` - `puffer_state_refresh(Pathfinder* env)` to rebuild observations from restored state. @@ -249,7 +280,10 @@ Add focused tests before training: - Step semantics: - Open edge reveals `0.0` and moves. - Closed edge reveals `1.0` and does not move. - - Repeated known wall hit keeps the same position. + - Repeated known wall hit terminates the attempt and retries the same map + with wall memory preserved. + - Timeout retries the same map with open/wall memory preserved. + - Success advances the next map by one solution step. - West move from column 1 through an open left edge reveals the edge but does not move in v1. - Reaching the hidden pawn sets terminal and success log. diff --git a/ocean/pathfinder/pathfinder.h b/ocean/pathfinder/pathfinder.h index 26c0e2a281..7d8d224e74 100644 --- a/ocean/pathfinder/pathfinder.h +++ b/ocean/pathfinder/pathfinder.h @@ -18,8 +18,6 @@ #define PATHFINDER_OBS_SIZE (PATHFINDER_NUM_WALLS + 2) #define PATHFINDER_NUM_ACTIONS 4 #define PATHFINDER_MAX_SOLUTION_LEN ((PATHFINDER_ROWS - 1) + (PATHFINDER_COLS - 1)) -#define PATHFINDER_CURRICULUM_WINDOW 32 -#define PATHFINDER_CURRICULUM_SUCCESS_THRESHOLD 24 #define PATHFINDER_RENDER_TILE 72 #define PATHFINDER_RENDER_MARGIN 40 @@ -112,8 +110,6 @@ typedef struct Pathfinder { int max_steps; int curriculum_level; int curriculum_episodes; - int curriculum_window_episodes; - int curriculum_window_successes; State state; } Pathfinder; @@ -137,14 +133,7 @@ static inline int pathfinder_curriculum_max_solution_len(const Pathfinder* env) static inline int pathfinder_curriculum_min_solution_len(const Pathfinder* env) { int max_len = pathfinder_curriculum_max_solution_len(env); - int min_len = env->min_solution_len < 1 ? 1 : env->min_solution_len; - if (max_len >= 4) { - int staged_min = max_len - 2; - if (staged_min > min_len) { - min_len = staged_min; - } - } - return pathfinder_clamp_int(min_len, 1, max_len); + return pathfinder_clamp_int(max_len, 1, PATHFINDER_MAX_SOLUTION_LEN); } static inline int pathfinder_v_wall(int row, int edge_col) { @@ -434,21 +423,9 @@ static void pathfinder_generate_maze(Pathfinder* env) { static void pathfinder_update_curriculum(Pathfinder* env, int success) { env->curriculum_episodes++; - env->curriculum_window_episodes++; - if (success) { - env->curriculum_window_successes++; - } - - if (env->curriculum_window_episodes < PATHFINDER_CURRICULUM_WINDOW) { - return; - } - - if (env->curriculum_window_successes >= PATHFINDER_CURRICULUM_SUCCESS_THRESHOLD && - pathfinder_curriculum_max_solution_len(env) < PATHFINDER_MAX_SOLUTION_LEN) { + if (success && pathfinder_curriculum_max_solution_len(env) < PATHFINDER_MAX_SOLUTION_LEN) { env->curriculum_level++; } - env->curriculum_window_episodes = 0; - env->curriculum_window_successes = 0; } void add_log(Pathfinder* env) { @@ -513,6 +490,23 @@ void c_reset(Pathfinder* env) { pathfinder_update_observations(env); } +static void pathfinder_reset_attempt(Pathfinder* env) { + State* s = &env->state; + s->tick = 0; + s->agent_row = 0; + s->agent_col = 0; + s->agent_path_len = 0; + s->wall_hits = 0; + s->revisit_count = 0; + s->known_wall_death = 0; + s->visited_count = 0; + s->success = 0; + s->episode_return = 0.0f; + memset(s->visited, 0, sizeof(s->visited)); + pathfinder_mark_visited(s, s->agent_row, s->agent_col); + pathfinder_update_observations(env); +} + static void pathfinder_reveal_wall(Pathfinder* env, int wall) { State* s = &env->state; if (s->known_walls[wall] != PATHFINDER_UNKNOWN) { @@ -585,8 +579,13 @@ void c_step(Pathfinder* env) { pathfinder_update_observations(env); if (env->terminals[0]) { + int solved = s->success; add_log(env); - c_reset(env); + if (solved) { + c_reset(env); + } else { + pathfinder_reset_attempt(env); + } } } diff --git a/ocean/pathfinder/tests/bench_pathfinder.c b/ocean/pathfinder/tests/bench_pathfinder.c index d7a317c166..0e00bcca4c 100644 --- a/ocean/pathfinder/tests/bench_pathfinder.c +++ b/ocean/pathfinder/tests/bench_pathfinder.c @@ -234,8 +234,6 @@ int main(int argc, char** argv) { memset(&env.log, 0, sizeof(env.log)); env.curriculum_level = 0; env.curriculum_episodes = 0; - env.curriculum_window_episodes = 0; - env.curriculum_window_successes = 0; bench_steps(&env, steps); c_close(&env); return 0; diff --git a/ocean/pathfinder/tests/test_pathfinder_core.c b/ocean/pathfinder/tests/test_pathfinder_core.c index 3516942ce3..f250d75bb7 100644 --- a/ocean/pathfinder/tests/test_pathfinder_core.c +++ b/ocean/pathfinder/tests/test_pathfinder_core.c @@ -143,6 +143,48 @@ static void test_action_mask_blocks_known_wall_but_forced_hit_still_dies(void) { assert(env.log.known_wall_deaths == 1.0f); } +static void test_known_wall_death_restarts_same_map_with_wall_memory(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + unsigned char action_mask[PATHFINDER_NUM_ACTIONS] = {0}; + unsigned char true_walls[PATHFINDER_NUM_WALLS]; + setup_env(&env, obs, actions, rewards, terminals); + env.action_mask = action_mask; + setup_manual_state(&env, 5, 5); + refresh_state(&env); + + int east_wall = pathfinder_wall_between(0, 0, 0, 1); + memcpy(true_walls, env.state.true_walls, sizeof(true_walls)); + + actions[0] = PATHFINDER_ACT_EAST; + c_step(&env); + + assert(terminals[0] == 0.0f); + assert(fabsf(obs[east_wall] - PATHFINDER_WALL) < 1e-6f); + assert(action_mask[PATHFINDER_ACT_EAST] == 0); + + c_step(&env); + + assert(terminals[0] == 1.0f); + assert(env.log.n == 1.0f); + assert(env.log.success == 0.0f); + assert(env.log.known_wall_deaths == 1.0f); + assert(env.state.tick == 0); + assert(env.state.agent_row == 0); + assert(env.state.agent_col == 0); + assert(env.state.goal_row == 5); + assert(env.state.goal_col == 5); + assert(env.state.visited_count == 1); + assert(env.state.visited[0][0] == 1); + assert(memcmp(env.state.true_walls, true_walls, sizeof(true_walls)) == 0); + assert(fabsf(env.state.known_walls[east_wall] - PATHFINDER_WALL) < 1e-6f); + assert(fabsf(obs[east_wall] - PATHFINDER_WALL) < 1e-6f); + assert(action_mask[PATHFINDER_ACT_EAST] == 0); +} + static void test_position_observation_updates_after_move(void) { Pathfinder env; float obs[PATHFINDER_OBS_SIZE]; @@ -368,12 +410,11 @@ static void test_max_solution_len_limits_curriculum_distance(void) { for (int i = 0; i < 100; i++) { c_reset(&env); assert(pathfinder_has_path_to_goal(&env.state)); - assert(env.state.shortest_path_len >= 1); - assert(env.state.shortest_path_len <= 2); + assert(env.state.shortest_path_len == 2); } } -static void test_curriculum_graduates_to_longer_mazes(void) { +static void test_success_generates_next_map_one_step_farther(void) { Pathfinder env; float obs[PATHFINDER_OBS_SIZE]; float actions[1] = {0}; @@ -382,53 +423,46 @@ static void test_curriculum_graduates_to_longer_mazes(void) { setup_env(&env, obs, actions, rewards, terminals); env.branch_prob = 0.0f; env.loop_prob = 0.0f; - env.max_solution_len = 4; - c_reset(&env); + env.max_solution_len = 1; + setup_manual_state(&env, 0, 1); + open_manual_edge(&env, 0, 0, 0, 1); - assert(pathfinder_curriculum_max_solution_len(&env) == 4); - for (int i = 0; i < PATHFINDER_CURRICULUM_WINDOW; i++) { - env.state.success = 1; - env.state.tick = 1; - env.state.shortest_path_len = 1; - env.state.agent_path_len = 1; - add_log(&env); - } - assert(pathfinder_curriculum_max_solution_len(&env) == 5); + actions[0] = PATHFINDER_ACT_EAST; + c_step(&env); - bool saw_longer_than_four = false; - for (int i = 0; i < 100; i++) { - c_reset(&env); - assert(env.state.shortest_path_len >= 3); - assert(env.state.shortest_path_len <= 5); - if (env.state.shortest_path_len > 4) { - saw_longer_than_four = true; - } + assert(terminals[0] == 1.0f); + assert(env.log.success == 1.0f); + assert(pathfinder_curriculum_max_solution_len(&env) == 2); + assert(env.state.agent_row == 0); + assert(env.state.agent_col == 0); + assert(env.state.shortest_path_len == 2); + assert(env.state.goal_row + env.state.goal_col == 2); + for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { + assert(fabsf(env.state.known_walls[i] - PATHFINDER_UNKNOWN) < 1e-6f); } - assert(saw_longer_than_four); } -static void test_curriculum_does_not_graduate_below_success_threshold(void) { +static void test_failure_retry_does_not_graduate_curriculum(void) { Pathfinder env; float obs[PATHFINDER_OBS_SIZE]; float actions[1] = {0}; float rewards[1] = {0}; float terminals[1] = {0}; + unsigned char true_walls[PATHFINDER_NUM_WALLS]; setup_env(&env, obs, actions, rewards, terminals); env.max_solution_len = 4; - c_reset(&env); + setup_manual_state(&env, 5, 5); + refresh_state(&env); + memcpy(true_walls, env.state.true_walls, sizeof(true_walls)); - for (int i = 0; i < PATHFINDER_CURRICULUM_WINDOW; i++) { - env.state.success = i < PATHFINDER_CURRICULUM_SUCCESS_THRESHOLD - 1; - env.state.tick = 1; - env.state.shortest_path_len = 1; - env.state.agent_path_len = 1; - add_log(&env); - } + actions[0] = PATHFINDER_ACT_EAST; + c_step(&env); + c_step(&env); + assert(terminals[0] == 1.0f); assert(env.curriculum_level == 0); assert(pathfinder_curriculum_max_solution_len(&env) == 4); - assert(env.curriculum_window_episodes == 0); - assert(env.curriculum_window_successes == 0); + assert(memcmp(env.state.true_walls, true_walls, sizeof(true_walls)) == 0); } static void test_curriculum_caps_at_board_max(void) { @@ -442,7 +476,7 @@ static void test_curriculum_caps_at_board_max(void) { env.curriculum_level = PATHFINDER_MAX_SOLUTION_LEN; assert(pathfinder_curriculum_max_solution_len(&env) == PATHFINDER_MAX_SOLUTION_LEN); - assert(pathfinder_curriculum_min_solution_len(&env) == PATHFINDER_MAX_SOLUTION_LEN - 2); + assert(pathfinder_curriculum_min_solution_len(&env) == PATHFINDER_MAX_SOLUTION_LEN); } static void test_generation_can_reach_board_max_distance(void) { @@ -456,28 +490,27 @@ static void test_generation_can_reach_board_max_distance(void) { env.loop_prob = 0.0f; env.max_solution_len = PATHFINDER_MAX_SOLUTION_LEN; - bool saw_max = false; for (int i = 0; i < 200; i++) { c_reset(&env); - assert(env.state.shortest_path_len >= PATHFINDER_MAX_SOLUTION_LEN - 2); - assert(env.state.shortest_path_len <= PATHFINDER_MAX_SOLUTION_LEN); - if (env.state.shortest_path_len == PATHFINDER_MAX_SOLUTION_LEN) { - saw_max = true; - } + assert(env.state.shortest_path_len == PATHFINDER_MAX_SOLUTION_LEN); } - assert(saw_max); } -static void test_max_steps_terminates_and_logs_failure(void) { +static void test_timeout_restarts_same_map_preserving_known_open_edges(void) { Pathfinder env; float obs[PATHFINDER_OBS_SIZE]; float actions[1] = {0}; float rewards[1] = {0}; float terminals[1] = {0}; + unsigned char action_mask[PATHFINDER_NUM_ACTIONS] = {0}; + unsigned char true_walls[PATHFINDER_NUM_WALLS]; setup_env(&env, obs, actions, rewards, terminals); + env.action_mask = action_mask; env.max_steps = 1; setup_manual_state(&env, 5, 5); open_manual_edge(&env, 0, 0, 0, 1); + int east_wall = pathfinder_wall_between(0, 0, 0, 1); + memcpy(true_walls, env.state.true_walls, sizeof(true_walls)); actions[0] = PATHFINDER_ACT_EAST; c_step(&env); @@ -488,6 +521,18 @@ static void test_max_steps_terminates_and_logs_failure(void) { assert(env.log.n == 1.0f); assert(env.log.success == 0.0f); assert(env.log.episode_length == 1.0f); + assert(env.state.tick == 0); + assert(env.state.agent_row == 0); + assert(env.state.agent_col == 0); + assert(env.state.goal_row == 5); + assert(env.state.goal_col == 5); + assert(env.state.visited_count == 1); + assert(env.state.visited[0][0] == 1); + assert(env.state.visited[0][1] == 0); + assert(memcmp(env.state.true_walls, true_walls, sizeof(true_walls)) == 0); + assert(fabsf(env.state.known_walls[east_wall] - PATHFINDER_OPEN) < 1e-6f); + assert(fabsf(obs[east_wall] - PATHFINDER_OPEN) < 1e-6f); + assert(action_mask[PATHFINDER_ACT_EAST] == 1); } static void test_reaching_goal_terminates(void) { @@ -517,6 +562,7 @@ int main(void) { test_reset_initializes_a1_and_unknown_walls(); test_action_mask_allows_unknown_edges_on_reset(); test_action_mask_blocks_known_wall_but_forced_hit_still_dies(); + test_known_wall_death_restarts_same_map_with_wall_memory(); test_position_observation_updates_after_move(); test_generated_mazes_connect_a1_to_goal(); test_generated_shortest_path_matches_goal_distance(); @@ -528,11 +574,11 @@ int main(void) { test_revisiting_previously_left_square_has_penalty(); test_known_open_edge_to_new_square_has_no_extra_penalty(); test_max_solution_len_limits_curriculum_distance(); - test_curriculum_graduates_to_longer_mazes(); - test_curriculum_does_not_graduate_below_success_threshold(); + test_success_generates_next_map_one_step_farther(); + test_failure_retry_does_not_graduate_curriculum(); test_curriculum_caps_at_board_max(); test_generation_can_reach_board_max_distance(); - test_max_steps_terminates_and_logs_failure(); + test_timeout_restarts_same_map_preserving_known_open_edges(); test_reaching_goal_terminates(); printf("pathfinder core tests passed\n"); return 0; From 3fb16a8c891379150554d348faac3a929a51b0da Mon Sep 17 00:00:00 2001 From: Kinvert Date: Thu, 4 Jun 2026 16:21:24 -0700 Subject: [PATCH 07/16] Reset Pathfinder failures blind --- AGENTS.md | 7 +- PATHFINDER_SPEC.md | 27 +++-- ocean/pathfinder/binding.c | 1 + ocean/pathfinder/pathfinder.h | 87 ++++++++++++--- ocean/pathfinder/tests/bench_pathfinder.c | 5 +- .../pathfinder/tests/benchmark_pathfinder.py | 1 + ocean/pathfinder/tests/test_pathfinder_core.c | 104 ++++++++++++++++-- 7 files changed, 195 insertions(+), 37 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index ae7ba67a06..799b3fc0d8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -198,8 +198,11 @@ it is not the best state-memory reference on this branch. - repeated known-wall hits terminate the attempt - first visits get a small discovery reward - revisits get a small penalty -- Failed attempts reset the agent to `A1` on the same map and keep discovered - wall/open knowledge. Solves generate the next map and advance curriculum. + - immediate two-cell oscillation like `A1 -> B1 -> A1 -> B1` terminates with + a large penalty +- Failed attempts reset the agent to `A1` on the same true map but clear all + discovered wall/open observations back to `-1.0`. Solves generate the next + map and advance curriculum. - First implementation should keep `train.state_buffer_size = 0` until deterministic state roundtrip tests exist. diff --git a/PATHFINDER_SPEC.md b/PATHFINDER_SPEC.md index eb4f47aedd..0cd5d9f4c5 100644 --- a/PATHFINDER_SPEC.md +++ b/PATHFINDER_SPEC.md @@ -114,9 +114,9 @@ Curriculum: - Each successful solve increments the next generated puzzle distance by one, capped at `PATHFINDER_MAX_SOLUTION_LEN`. - Failed attempts do not advance curriculum. -- Failed attempts restart the agent at `A1` on the same map and preserve - discovered wall/open observations, so map generation happens only after a - solve or external reset. +- Failed attempts restart the agent at `A1` on the same true map, but clear + discovered wall/open observations back to `-1.0`, so map generation happens + only after a solve or external reset. Config knobs: @@ -188,6 +188,8 @@ Default reward model: - `-0.05` extra penalty and terminal attempt reset for hitting a known wall. - `-0.01` for revisiting a square that was previously left in the current attempt. +- `-1.0` extra penalty and terminal attempt reset for immediate two-cell + oscillation such as `A1 -> B1 -> A1 -> B1`. - `-0.01` for impossible movement, such as attempting to exit through the left edge in v1. @@ -197,14 +199,17 @@ Termination: - Timeout: `tick >= max_steps`. - Known-wall death: agent tries to move through a wall that is already observed as blocked. +- Repeat-move death: agent repeats an immediate two-cell oscillation. For + example, `A1 -> B1 -> A1` is legal, but the next `A1 -> B1` dies. Reset after terminal: - Success logs the episode, advances curriculum by one move when not capped, and generates a new map. -- Timeout and known-wall death log the episode, then reset only the attempt - state: position returns to `A1`, tick/path/revisit counters clear, and the - same true map plus known wall/open observations remain. +- Timeout, known-wall death, and repeat-move death log the episode, then reset + only the attempt state: position returns to `A1`, tick/path/revisit/move + counters clear, the same true map remains, and all wall observations return + to `-1.0`. Logged metrics: @@ -216,6 +221,7 @@ Logged metrics: - `wall_hits` - `revisits` - `known_wall_deaths` +- `repeat_move_deaths` - `known_walls` - `known_open_edges` - `shortest_path_len` @@ -280,9 +286,12 @@ Add focused tests before training: - Step semantics: - Open edge reveals `0.0` and moves. - Closed edge reveals `1.0` and does not move. - - Repeated known wall hit terminates the attempt and retries the same map - with wall memory preserved. - - Timeout retries the same map with open/wall memory preserved. + - Repeated known wall hit terminates the attempt and retries the same true + map with wall observations reset to `-1.0`. + - Repeating an immediate two-cell oscillation, such as + `A1 -> B1 -> A1 -> B1`, terminates with a large penalty and retries the + same true map blind. + - Timeout retries the same true map with wall observations reset to `-1.0`. - Success advances the next map by one solution step. - West move from column 1 through an open left edge reveals the edge but does not move in v1. diff --git a/ocean/pathfinder/binding.c b/ocean/pathfinder/binding.c index 94bc3a3a4f..e252db3940 100644 --- a/ocean/pathfinder/binding.c +++ b/ocean/pathfinder/binding.c @@ -29,6 +29,7 @@ void my_log(Log* log, Dict* out) { dict_set(out, "wall_hits", log->wall_hits); dict_set(out, "revisits", log->revisits); dict_set(out, "known_wall_deaths", log->known_wall_deaths); + dict_set(out, "repeat_move_deaths", log->repeat_move_deaths); dict_set(out, "known_walls", log->known_walls); dict_set(out, "known_open_edges", log->known_open_edges); dict_set(out, "shortest_path_len", log->shortest_path_len); diff --git a/ocean/pathfinder/pathfinder.h b/ocean/pathfinder/pathfinder.h index 7d8d224e74..6bbcb82d07 100644 --- a/ocean/pathfinder/pathfinder.h +++ b/ocean/pathfinder/pathfinder.h @@ -44,6 +44,7 @@ #define PATHFINDER_NEW_WALL_PENALTY 0.0f #define PATHFINDER_KNOWN_WALL_PENALTY -0.01f #define PATHFINDER_KNOWN_WALL_DEATH_PENALTY -0.05f +#define PATHFINDER_REPEAT_MOVE_DEATH_PENALTY -1.0f #define PATHFINDER_NEW_CELL_REWARD 0.01f #define PATHFINDER_REVISIT_PENALTY -0.01f #define PATHFINDER_IMPOSSIBLE_PENALTY -0.01f @@ -58,6 +59,7 @@ typedef struct Log { float wall_hits; float revisits; float known_wall_deaths; + float repeat_move_deaths; float known_walls; float known_open_edges; float shortest_path_len; @@ -78,12 +80,16 @@ typedef struct State { int wall_hits; int revisit_count; int known_wall_death; + int repeat_move_death; int visited_count; int known_wall_count; int known_open_count; int success; float episode_return; unsigned char visited[PATHFINDER_ROWS][PATHFINDER_COLS]; + unsigned char recent_rows[3]; + unsigned char recent_cols[3]; + int recent_count; unsigned char true_walls[PATHFINDER_NUM_WALLS]; float known_walls[PATHFINDER_NUM_WALLS]; } State; @@ -206,6 +212,43 @@ static inline void pathfinder_mark_visited(State* s, int row, int col) { s->visited_count++; } +static inline void pathfinder_reset_move_history(State* s) { + s->recent_rows[0] = (unsigned char)s->agent_row; + s->recent_cols[0] = (unsigned char)s->agent_col; + s->recent_count = 1; +} + +static inline void pathfinder_ensure_move_history(State* s) { + if (s->recent_count <= 0) { + pathfinder_reset_move_history(s); + } +} + +static inline bool pathfinder_repeats_two_cell_cycle( + const State* s, int next_row, int next_col) { + return s->recent_count >= 3 && + s->recent_rows[0] == s->agent_row && + s->recent_cols[0] == s->agent_col && + s->recent_rows[1] == next_row && + s->recent_cols[1] == next_col; +} + +static inline void pathfinder_record_successful_move(State* s) { + if (s->recent_count < 3) { + int idx = s->recent_count++; + s->recent_rows[idx] = (unsigned char)s->agent_row; + s->recent_cols[idx] = (unsigned char)s->agent_col; + return; + } + + s->recent_rows[0] = s->recent_rows[1]; + s->recent_cols[0] = s->recent_cols[1]; + s->recent_rows[1] = s->recent_rows[2]; + s->recent_cols[1] = s->recent_cols[2]; + s->recent_rows[2] = (unsigned char)s->agent_row; + s->recent_cols[2] = (unsigned char)s->agent_col; +} + static inline void pathfinder_action_delta(int action, int* d_row, int* d_col) { *d_row = 0; *d_col = 0; @@ -449,6 +492,7 @@ void add_log(Pathfinder* env) { env->log.wall_hits += (float)s->wall_hits; env->log.revisits += (float)s->revisit_count; env->log.known_wall_deaths += (float)s->known_wall_death; + env->log.repeat_move_deaths += (float)s->repeat_move_death; env->log.known_walls += (float)s->known_wall_count; env->log.known_open_edges += (float)s->known_open_count; env->log.shortest_path_len += (float)s->shortest_path_len; @@ -487,6 +531,7 @@ void c_reset(Pathfinder* env) { s->agent_col = 0; pathfinder_generate_maze(env); pathfinder_mark_visited(s, s->agent_row, s->agent_col); + pathfinder_reset_move_history(s); pathfinder_update_observations(env); } @@ -499,11 +544,14 @@ static void pathfinder_reset_attempt(Pathfinder* env) { s->wall_hits = 0; s->revisit_count = 0; s->known_wall_death = 0; + s->repeat_move_death = 0; s->visited_count = 0; s->success = 0; s->episode_return = 0.0f; memset(s->visited, 0, sizeof(s->visited)); + pathfinder_reset_known(s); pathfinder_mark_visited(s, s->agent_row, s->agent_col); + pathfinder_reset_move_history(s); pathfinder_update_observations(env); } @@ -550,21 +598,29 @@ void c_step(Pathfinder* env) { } else if (!pathfinder_in_bounds(next_row, next_col)) { reward += PATHFINDER_IMPOSSIBLE_PENALTY; } else { - bool revisited = s->visited[next_row][next_col] != 0; - s->agent_row = next_row; - s->agent_col = next_col; - s->agent_path_len++; - if (revisited) { - s->revisit_count++; - reward += PATHFINDER_REVISIT_PENALTY; - } else { - pathfinder_mark_visited(s, next_row, next_col); - reward += PATHFINDER_NEW_CELL_REWARD; - } - if (s->agent_row == s->goal_row && s->agent_col == s->goal_col) { - s->success = 1; - reward += PATHFINDER_GOAL_REWARD; + pathfinder_ensure_move_history(s); + if (pathfinder_repeats_two_cell_cycle(s, next_row, next_col)) { + reward += PATHFINDER_REPEAT_MOVE_DEATH_PENALTY; + s->repeat_move_death = 1; env->terminals[0] = 1.0f; + } else { + bool revisited = s->visited[next_row][next_col] != 0; + s->agent_row = next_row; + s->agent_col = next_col; + s->agent_path_len++; + pathfinder_record_successful_move(s); + if (revisited) { + s->revisit_count++; + reward += PATHFINDER_REVISIT_PENALTY; + } else { + pathfinder_mark_visited(s, next_row, next_col); + reward += PATHFINDER_NEW_CELL_REWARD; + } + if (s->agent_row == s->goal_row && s->agent_col == s->goal_col) { + s->success = 1; + reward += PATHFINDER_GOAL_REWARD; + env->terminals[0] = 1.0f; + } } } } @@ -799,6 +855,9 @@ static void pathfinder_draw_panel(Pathfinder* env) { DrawText(TextFormat("Known-wall deaths: %.0f", env->log.known_wall_deaths), x, y, 18, PATHFINDER_KNOWN_WALL); y += 24; + DrawText(TextFormat("Repeat-move deaths: %.0f", env->log.repeat_move_deaths), + x, y, 18, PATHFINDER_KNOWN_WALL); + y += 24; DrawText(TextFormat("Shortest path: %i", s->shortest_path_len), x, y, 18, PATHFINDER_TEXT); y += 24; DrawText(TextFormat("Agent path: %i", s->agent_path_len), x, y, 18, PATHFINDER_TEXT); diff --git a/ocean/pathfinder/tests/bench_pathfinder.c b/ocean/pathfinder/tests/bench_pathfinder.c index 0e00bcca4c..5d722faeff 100644 --- a/ocean/pathfinder/tests/bench_pathfinder.c +++ b/ocean/pathfinder/tests/bench_pathfinder.c @@ -69,10 +69,11 @@ static void bench_steps(Pathfinder* env, long steps) { } double elapsed = now_seconds() - t0; double step_sps = (double)steps / elapsed; - printf("step_bench steps=%ld seconds=%.6f step_sps=%.2f episodes=%.0f success=%.6f reward_sum=%.3f curriculum_level=%d curriculum_max_solution_len=%d\n", + printf("step_bench steps=%ld seconds=%.6f step_sps=%.2f episodes=%.0f success=%.6f repeat_move_deaths=%.0f reward_sum=%.3f curriculum_level=%d curriculum_max_solution_len=%d\n", steps, elapsed, step_sps, env->log.n, env->log.n > 0.0f ? env->log.success / env->log.n : 0.0f, - reward_sum, env->curriculum_level, pathfinder_curriculum_max_solution_len(env)); + env->log.repeat_move_deaths, reward_sum, env->curriculum_level, + pathfinder_curriculum_max_solution_len(env)); } static void bench_reset_components(Pathfinder* env, long iters) { diff --git a/ocean/pathfinder/tests/benchmark_pathfinder.py b/ocean/pathfinder/tests/benchmark_pathfinder.py index 3840f3fd5a..b9ec7fafaa 100755 --- a/ocean/pathfinder/tests/benchmark_pathfinder.py +++ b/ocean/pathfinder/tests/benchmark_pathfinder.py @@ -69,6 +69,7 @@ def run_once(label, args, timesteps, eval_epochs_override): "episode_length": stats.get("env/episode_length", 0.0), "wall_hits": stats.get("env/wall_hits", 0.0), "known_wall_deaths": stats.get("env/known_wall_deaths", 0.0), + "repeat_move_deaths": stats.get("env/repeat_move_deaths", 0.0), "revisits": stats.get("env/revisits", 0.0), "shortest_path_len": stats.get("env/shortest_path_len", 0.0), "agent_path_len": stats.get("env/agent_path_len", 0.0), diff --git a/ocean/pathfinder/tests/test_pathfinder_core.c b/ocean/pathfinder/tests/test_pathfinder_core.c index f250d75bb7..bca9107a1b 100644 --- a/ocean/pathfinder/tests/test_pathfinder_core.c +++ b/ocean/pathfinder/tests/test_pathfinder_core.c @@ -41,6 +41,15 @@ static void open_manual_edge(Pathfinder* env, int row, int col, int next_row, in refresh_state(env); } +static void assert_wall_observations_unknown(Pathfinder* env, float* obs) { + assert(env->state.known_wall_count == 0); + assert(env->state.known_open_count == 0); + for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { + assert(fabsf(env->state.known_walls[i] - PATHFINDER_UNKNOWN) < 1e-6f); + assert(fabsf(obs[i] - PATHFINDER_UNKNOWN) < 1e-6f); + } +} + static void test_constants(void) { assert(PATHFINDER_ROWS == 6); assert(PATHFINDER_COLS == 6); @@ -143,7 +152,7 @@ static void test_action_mask_blocks_known_wall_but_forced_hit_still_dies(void) { assert(env.log.known_wall_deaths == 1.0f); } -static void test_known_wall_death_restarts_same_map_with_wall_memory(void) { +static void test_known_wall_death_restarts_same_map_with_unknown_wall_memory(void) { Pathfinder env; float obs[PATHFINDER_OBS_SIZE]; float actions[1] = {0}; @@ -180,9 +189,8 @@ static void test_known_wall_death_restarts_same_map_with_wall_memory(void) { assert(env.state.visited_count == 1); assert(env.state.visited[0][0] == 1); assert(memcmp(env.state.true_walls, true_walls, sizeof(true_walls)) == 0); - assert(fabsf(env.state.known_walls[east_wall] - PATHFINDER_WALL) < 1e-6f); - assert(fabsf(obs[east_wall] - PATHFINDER_WALL) < 1e-6f); - assert(action_mask[PATHFINDER_ACT_EAST] == 0); + assert_wall_observations_unknown(&env, obs); + assert(action_mask[PATHFINDER_ACT_EAST] == 1); } static void test_position_observation_updates_after_move(void) { @@ -371,6 +379,82 @@ static void test_revisiting_previously_left_square_has_penalty(void) { assert(env.state.revisit_count == 1); } +static void test_repeating_directed_move_dies_and_restarts_same_map_blind(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + unsigned char true_walls[PATHFINDER_NUM_WALLS]; + setup_env(&env, obs, actions, rewards, terminals); + setup_manual_state(&env, 5, 5); + open_manual_edge(&env, 0, 0, 0, 1); + int east_wall = pathfinder_wall_between(0, 0, 0, 1); + memcpy(true_walls, env.state.true_walls, sizeof(true_walls)); + + actions[0] = PATHFINDER_ACT_EAST; + c_step(&env); + assert(terminals[0] == 0.0f); + assert(env.state.agent_col == 1); + assert(fabsf(obs[east_wall] - PATHFINDER_OPEN) < 1e-6f); + + actions[0] = PATHFINDER_ACT_WEST; + c_step(&env); + assert(terminals[0] == 0.0f); + assert(env.state.agent_col == 0); + assert(env.state.revisit_count == 1); + + actions[0] = PATHFINDER_ACT_EAST; + c_step(&env); + + assert(terminals[0] == 1.0f); + assert(rewards[0] <= -1.0f); + assert(env.log.n == 1.0f); + assert(env.log.success == 0.0f); + assert(env.log.repeat_move_deaths == 1.0f); + assert(env.state.tick == 0); + assert(env.state.agent_row == 0); + assert(env.state.agent_col == 0); + assert(env.state.goal_row == 5); + assert(env.state.goal_col == 5); + assert(memcmp(env.state.true_walls, true_walls, sizeof(true_walls)) == 0); + assert_wall_observations_unknown(&env, obs); +} + +static void test_longer_backtrack_then_forward_move_is_allowed(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + setup_manual_state(&env, 5, 5); + open_manual_edge(&env, 0, 0, 0, 1); + open_manual_edge(&env, 0, 1, 0, 2); + + actions[0] = PATHFINDER_ACT_EAST; + c_step(&env); + assert(terminals[0] == 0.0f); + actions[0] = PATHFINDER_ACT_EAST; + c_step(&env); + assert(terminals[0] == 0.0f); + actions[0] = PATHFINDER_ACT_WEST; + c_step(&env); + assert(terminals[0] == 0.0f); + actions[0] = PATHFINDER_ACT_WEST; + c_step(&env); + assert(terminals[0] == 0.0f); + + actions[0] = PATHFINDER_ACT_EAST; + c_step(&env); + + assert(terminals[0] == 0.0f); + assert(env.state.agent_row == 0); + assert(env.state.agent_col == 1); + assert(fabsf(rewards[0] - + (PATHFINDER_STEP_PENALTY + PATHFINDER_REVISIT_PENALTY)) < 1e-6f); +} + static void test_known_open_edge_to_new_square_has_no_extra_penalty(void) { Pathfinder env; float obs[PATHFINDER_OBS_SIZE]; @@ -496,7 +580,7 @@ static void test_generation_can_reach_board_max_distance(void) { } } -static void test_timeout_restarts_same_map_preserving_known_open_edges(void) { +static void test_timeout_restarts_same_map_with_unknown_wall_memory(void) { Pathfinder env; float obs[PATHFINDER_OBS_SIZE]; float actions[1] = {0}; @@ -509,7 +593,6 @@ static void test_timeout_restarts_same_map_preserving_known_open_edges(void) { env.max_steps = 1; setup_manual_state(&env, 5, 5); open_manual_edge(&env, 0, 0, 0, 1); - int east_wall = pathfinder_wall_between(0, 0, 0, 1); memcpy(true_walls, env.state.true_walls, sizeof(true_walls)); actions[0] = PATHFINDER_ACT_EAST; @@ -530,8 +613,7 @@ static void test_timeout_restarts_same_map_preserving_known_open_edges(void) { assert(env.state.visited[0][0] == 1); assert(env.state.visited[0][1] == 0); assert(memcmp(env.state.true_walls, true_walls, sizeof(true_walls)) == 0); - assert(fabsf(env.state.known_walls[east_wall] - PATHFINDER_OPEN) < 1e-6f); - assert(fabsf(obs[east_wall] - PATHFINDER_OPEN) < 1e-6f); + assert_wall_observations_unknown(&env, obs); assert(action_mask[PATHFINDER_ACT_EAST] == 1); } @@ -562,7 +644,7 @@ int main(void) { test_reset_initializes_a1_and_unknown_walls(); test_action_mask_allows_unknown_edges_on_reset(); test_action_mask_blocks_known_wall_but_forced_hit_still_dies(); - test_known_wall_death_restarts_same_map_with_wall_memory(); + test_known_wall_death_restarts_same_map_with_unknown_wall_memory(); test_position_observation_updates_after_move(); test_generated_mazes_connect_a1_to_goal(); test_generated_shortest_path_matches_goal_distance(); @@ -572,13 +654,15 @@ int main(void) { test_invalid_action_penalizes_without_terminating(); test_boundary_action_reveals_wall(); test_revisiting_previously_left_square_has_penalty(); + test_repeating_directed_move_dies_and_restarts_same_map_blind(); + test_longer_backtrack_then_forward_move_is_allowed(); test_known_open_edge_to_new_square_has_no_extra_penalty(); test_max_solution_len_limits_curriculum_distance(); test_success_generates_next_map_one_step_farther(); test_failure_retry_does_not_graduate_curriculum(); test_curriculum_caps_at_board_max(); test_generation_can_reach_board_max_distance(); - test_timeout_restarts_same_map_preserving_known_open_edges(); + test_timeout_restarts_same_map_with_unknown_wall_memory(); test_reaching_goal_terminates(); printf("pathfinder core tests passed\n"); return 0; From 45bfee680a533c5582881a1ebb06924425b0031f Mon Sep 17 00:00:00 2001 From: Kinvert Date: Thu, 4 Jun 2026 21:32:34 -0700 Subject: [PATCH 08/16] Harden pathfinder maze generation and logging --- AGENTS.md | 10 + PATHFINDER_SPEC.md | 34 +- config/pathfinder.ini | 93 ++- ocean/pathfinder/binding.c | 4 + ocean/pathfinder/pathfinder.h | 564 +++++++++++------- .../pathfinder/tests/benchmark_pathfinder.py | 4 + ocean/pathfinder/tests/test_pathfinder_core.c | 5 +- 7 files changed, 455 insertions(+), 259 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 799b3fc0d8..9542bf00fe 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -229,3 +229,13 @@ python -m pufferlib.pufferl train pathfinder --train.total-timesteps 2097152 If tests require helper binaries, put them under `ocean/pathfinder/tests/` and keep them scoped to Pathfinder. + +## Quick Ops Notes + +- The eval command supports `--load-model-path latest` to automatically pick the + latest available checkpoint in the workspace. +- Verified working invocation: + + ```bash + source .venv/bin/activate && DISPLAY=:0 python -m pufferlib.pufferl eval pathfinder --load-model-path latest + ``` diff --git a/PATHFINDER_SPEC.md b/PATHFINDER_SPEC.md index 0cd5d9f4c5..9226897a48 100644 --- a/PATHFINDER_SPEC.md +++ b/PATHFINDER_SPEC.md @@ -85,26 +85,26 @@ The generator must produce legal, solvable layouts without hand-authored maps. Puzzle generation: -1. Pick a hidden pawn cell at the current curriculum distance from `A1`. -2. Initialize all wall slots to blocked. -3. Open the left entry edge for `A1`. -4. Carve at least one orthogonal path from `A1` to the hidden pawn. -5. Store `A1` as the agent spawn for the episode. -6. Add false branches and optional loops without breaking the solved-path - invariant. -7. Optionally open additional column-1 entries. These can connect to useful - routes or dead-end branches. -8. Validate with BFS that `A1` reaches the pawn. +1. Compute the curriculum span (`min_solution_len` to `max_solution_len`). +2. Sample a target path length with a bias toward the upper end of the span. +3. Initialize all wall slots to blocked. +4. Open the left entry edge for `A1`. +5. Carve one orthogonal randomized path from `A1` to a hidden pawn that is + exactly the sampled target length (including turns and winding moves). +6. Store `A1` as the agent spawn for the episode. +7. Add false branches and optional loops while preserving that the sampled + target length remains a valid shortest path lower bound. +8. Optionally open additional column-1 entries under the same shortest-path + guard. +9. Validate with BFS that `A1` reaches the pawn. Default generator style: -- Choose a target with Manhattan distance equal to the current curriculum - length. -- Carve a randomized monotonic path from `A1` to that target. -- Open additional random internal edges as branch/loop density knobs rather - than trying to exactly copy human barricade layouts. -- Because the target distance is Manhattan distance from `A1`, random extra - edges cannot create a shorter path than the curriculum length. +- Choose a target length from the curriculum span with an upper-biased random + sample. +- Carve a randomized, non-monotonic solution path of exact length to that target. +- Open additional random internal edges as branch/loop density knobs rather than + trying to exactly copy human barricade layouts. Curriculum: diff --git a/config/pathfinder.ini b/config/pathfinder.ini index 58ff658459..ee472b5599 100644 --- a/config/pathfinder.ini +++ b/config/pathfinder.ini @@ -7,44 +7,44 @@ num_buffers = 2 num_threads = 0 [env] -branch_prob = 0.35 -loop_prob = 0.10 +branch_prob = 0.11 +loop_prob = 0.04 extra_entry_prob = 0.0 min_solution_len = 1 max_solution_len = 4 max_steps = 128 [policy] -hidden_size = 512 +hidden_size = 128 num_layers = 2 expansion_factor = 1 [train] gpus = 1 seed = 42 -total_timesteps = 100000000 -learning_rate = 0.001 +total_timesteps = 20_000_000 +learning_rate = 0.004385555933422184 anneal_lr = 1 min_lr_ratio = 0 -gamma = 0.995 -gae_lambda = 0.95 -replay_ratio = 1 -clip_coef = 0.1 -vf_coef = 0.5 -vf_clip_coef = 5 -max_grad_norm = 1.0 -ent_coef = 0.005 +gamma = 0.9156890319524347 +gae_lambda = 0.995 +replay_ratio = 1.841268465367614 +clip_coef = 0.5499249092401194 +vf_coef = 3.2377097432640958 +vf_clip_coef = 1.8780705428234663 +max_grad_norm = 1.6383931004852927 +ent_coef = 0.006542649506789298 anneal_ent_coef = 0 min_ent_coef_ratio = 0.1 beta1 = 0.9 beta2 = 0.999 eps = 1e-8 -minibatch_size = 32768 -horizon = 128 -vtrace_rho_clip = 1 -vtrace_c_clip = 1 -prio_alpha = 0 -prio_beta0 = 0 +minibatch_size = 8192 +horizon = 256 +vtrace_rho_clip = 5 +vtrace_c_clip = 2.3816034577895158 +prio_alpha = 1 +prio_beta0 = 0.3976524344305938 anneal_prio_beta = 0 state_buffer_size = 0 cl_frac = 0 @@ -56,22 +56,25 @@ explore_beta = 0 explore_decay = 0.99 use_rnn = 1 env = 0 +eval_episodes = 2000 [sweep] max_suggestion_cost = 7200 +max_runs = 80 +downsample = 5 [sweep.env.branch_prob] distribution = uniform -min = 0.05 -mean = 0.35 -max = 0.75 +min = 0.0 +mean = 0.08 +max = 0.22 scale = auto [sweep.env.loop_prob] distribution = uniform min = 0.0 -mean = 0.1 -max = 0.5 +mean = 0.02 +max = 0.12 scale = auto [sweep.train.learning_rate] @@ -80,3 +83,45 @@ min = 0.0001 mean = 0.001 max = 0.005 scale = auto + +[sweep.train.total_timesteps] +distribution = log_normal +min = 1e7 +mean = 1.5e7 +max = 2.5e7 +scale = time + +[sweep.policy.hidden_size] +distribution = uniform_pow2 +min = 64 +mean = 256 +max = 512 +scale = auto + +[sweep.policy.num_layers] +distribution = uniform +min = 1 +mean = 2 +max = 4 +scale = auto + +[sweep.vec.total_agents] +distribution = uniform_pow2 +min = 256 +mean = 1024 +max = 2048 +scale = auto + +[sweep.train.horizon] +distribution = uniform_pow2 +min = 16 +mean = 64 +max = 128 +scale = auto + +[sweep.train.minibatch_size] +distribution = uniform_pow2 +min = 1024 +mean = 2048 +max = 8192 +scale = auto diff --git a/ocean/pathfinder/binding.c b/ocean/pathfinder/binding.c index e252db3940..fc2d4aa9ff 100644 --- a/ocean/pathfinder/binding.c +++ b/ocean/pathfinder/binding.c @@ -26,6 +26,7 @@ void my_log(Log* log, Dict* out) { dict_set(out, "episode_return", log->episode_return); dict_set(out, "episode_length", log->episode_length); dict_set(out, "success", log->success); + dict_set(out, "wins", log->wins); dict_set(out, "wall_hits", log->wall_hits); dict_set(out, "revisits", log->revisits); dict_set(out, "known_wall_deaths", log->known_wall_deaths); @@ -35,5 +36,8 @@ void my_log(Log* log, Dict* out) { dict_set(out, "shortest_path_len", log->shortest_path_len); dict_set(out, "agent_path_len", log->agent_path_len); dict_set(out, "curriculum_level", log->curriculum_level); + dict_set(out, "curriculum_min_solution_len", log->curriculum_min_solution_len); dict_set(out, "curriculum_max_solution_len", log->curriculum_max_solution_len); + dict_set(out, "curriculum_target_len", log->curriculum_target_len); + dict_set(out, "curriculum_next_target_len", log->curriculum_next_target_len); } diff --git a/ocean/pathfinder/pathfinder.h b/ocean/pathfinder/pathfinder.h index 6bbcb82d07..b491fecf1e 100644 --- a/ocean/pathfinder/pathfinder.h +++ b/ocean/pathfinder/pathfinder.h @@ -18,8 +18,9 @@ #define PATHFINDER_OBS_SIZE (PATHFINDER_NUM_WALLS + 2) #define PATHFINDER_NUM_ACTIONS 4 #define PATHFINDER_MAX_SOLUTION_LEN ((PATHFINDER_ROWS - 1) + (PATHFINDER_COLS - 1)) +#define PATHFINDER_MAX_PATH_CELLS (PATHFINDER_ROWS * PATHFINDER_COLS) -#define PATHFINDER_RENDER_TILE 72 +#define PATHFINDER_RENDER_TILE 72 // todo should this be derived from the number of rows/cols? #define PATHFINDER_RENDER_MARGIN 40 #define PATHFINDER_RENDER_BOARD_X PATHFINDER_RENDER_MARGIN #define PATHFINDER_RENDER_BOARD_Y 92 @@ -36,18 +37,18 @@ #define PATHFINDER_ACT_SOUTH 2 #define PATHFINDER_ACT_WEST 3 -#define PATHFINDER_UNKNOWN -1.0f +#define PATHFINDER_UNKNOWN -1.0f // todo might it learn better if 0 is unknown, and wall is -1, and open is +1? #define PATHFINDER_OPEN 0.0f #define PATHFINDER_WALL 1.0f -#define PATHFINDER_STEP_PENALTY -0.001f +#define PATHFINDER_STEP_PENALTY -0.001f // todo should these be made sweepable in the pathfinder.ini? #define PATHFINDER_NEW_WALL_PENALTY 0.0f -#define PATHFINDER_KNOWN_WALL_PENALTY -0.01f -#define PATHFINDER_KNOWN_WALL_DEATH_PENALTY -0.05f +#define PATHFINDER_KNOWN_WALL_PENALTY -0.01f // todo do we need this? I think we just kill if they hit an already KNOWN wall +#define PATHFINDER_KNOWN_WALL_DEATH_PENALTY -0.05f // todo prob make sweepable or -1 #define PATHFINDER_REPEAT_MOVE_DEATH_PENALTY -1.0f -#define PATHFINDER_NEW_CELL_REWARD 0.01f -#define PATHFINDER_REVISIT_PENALTY -0.01f -#define PATHFINDER_IMPOSSIBLE_PENALTY -0.01f +#define PATHFINDER_NEW_CELL_REWARD 0.01f // todo idk if I want to micromanage rewards +#define PATHFINDER_REVISIT_PENALTY -0.01f // todo make sweepable or maybe get rid of this, since we already have general move penalty +#define PATHFINDER_IMPOSSIBLE_PENALTY -0.01f // todo impossible move should be instant death and -1 #define PATHFINDER_GOAL_REWARD 1.0f typedef struct Log { @@ -56,6 +57,7 @@ typedef struct Log { float episode_return; float episode_length; float success; + float wins; float wall_hits; float revisits; float known_wall_deaths; @@ -65,7 +67,10 @@ typedef struct Log { float shortest_path_len; float agent_path_len; float curriculum_level; + float curriculum_min_solution_len; float curriculum_max_solution_len; + float curriculum_target_len; + float curriculum_next_target_len; float n; } Log; @@ -125,45 +130,56 @@ static inline int pathfinder_clamp_int(int value, int min_value, int max_value) return value; } -static inline int pathfinder_curriculum_base_solution_len(const Pathfinder* env) { +static inline int curriculum_base_solution_len(const Pathfinder* env) { if (env->max_solution_len <= 0) { return PATHFINDER_MAX_SOLUTION_LEN; } return pathfinder_clamp_int(env->max_solution_len, 1, PATHFINDER_MAX_SOLUTION_LEN); } -static inline int pathfinder_curriculum_max_solution_len(const Pathfinder* env) { - int max_len = pathfinder_curriculum_base_solution_len(env) + env->curriculum_level; +static inline int curriculum_max_solution_len(const Pathfinder* env) { + int max_len = curriculum_base_solution_len(env) + env->curriculum_level; return pathfinder_clamp_int(max_len, 1, PATHFINDER_MAX_SOLUTION_LEN); } -static inline int pathfinder_curriculum_min_solution_len(const Pathfinder* env) { - int max_len = pathfinder_curriculum_max_solution_len(env); - return pathfinder_clamp_int(max_len, 1, PATHFINDER_MAX_SOLUTION_LEN); +static inline int curriculum_min_solution_len(const Pathfinder* env) { // todo is this named correctly? + int base_max_solution_len = curriculum_base_solution_len(env); + int base_min_solution_len = pathfinder_clamp_int( + env->min_solution_len <= 0 ? 1 : env->min_solution_len, + 1, + base_max_solution_len + ); + int max_solution_len = pathfinder_clamp_int( + base_max_solution_len + env->curriculum_level, + 1, + PATHFINDER_MAX_SOLUTION_LEN + ); + int min_solution_len = base_min_solution_len + env->curriculum_level; + return pathfinder_clamp_int(min_solution_len, 1, max_solution_len); } -static inline int pathfinder_v_wall(int row, int edge_col) { +static inline int v_wall(int row, int edge_col) { return row * (PATHFINDER_COLS + 1) + edge_col; } -static inline int pathfinder_h_wall(int edge_row, int col) { +static inline int h_wall(int edge_row, int col) { return PATHFINDER_VERTICAL_WALLS + edge_row * PATHFINDER_COLS + col; } -static inline bool pathfinder_in_bounds(int row, int col) { +static inline bool in_bounds(int row, int col) { return row >= 0 && row < PATHFINDER_ROWS && col >= 0 && col < PATHFINDER_COLS; } -static inline unsigned int pathfinder_rand(Pathfinder* env) { +static inline unsigned int rand_u32(Pathfinder* env) { env->rng = 1664525u * env->rng + 1013904223u; return env->rng; } -static inline float pathfinder_rand_float(Pathfinder* env) { - return (float)(pathfinder_rand(env) >> 8) / 16777216.0f; +static inline float rand_float(Pathfinder* env) { + return (float)(rand_u32(env) >> 8) / 16777216.0f; } -static inline bool pathfinder_rand_chance_u8(Pathfinder* env, int threshold, +static inline bool rand_chance_u8(Pathfinder* env, int threshold, unsigned int* samples, int* remaining) { if (threshold <= 0) { return false; @@ -172,7 +188,7 @@ static inline bool pathfinder_rand_chance_u8(Pathfinder* env, int threshold, return true; } if (*remaining == 0) { - *samples = pathfinder_rand(env); + *samples = rand_u32(env); *remaining = 4; } unsigned int sample = *samples & 0xffu; @@ -181,50 +197,50 @@ static inline bool pathfinder_rand_chance_u8(Pathfinder* env, int threshold, return (int)sample < threshold; } -static inline int pathfinder_wall_between(int row, int col, int next_row, int next_col) { +static inline int wall_between(int row, int col, int next_row, int next_col) { if (next_row == row && next_col == col + 1) { - return pathfinder_v_wall(row, col + 1); + return v_wall(row, col + 1); } if (next_row == row && next_col == col - 1) { - return pathfinder_v_wall(row, col); + return v_wall(row, col); } if (next_col == col && next_row == row + 1) { - return pathfinder_h_wall(row + 1, col); + return h_wall(row + 1, col); } if (next_col == col && next_row == row - 1) { - return pathfinder_h_wall(row, col); + return h_wall(row, col); } return -1; } -static inline void pathfinder_open_edge(State* s, int row, int col, int next_row, int next_col) { - int wall = pathfinder_wall_between(row, col, next_row, next_col); +static inline void open_edge(State* s, int row, int col, int next_row, int next_col) { + int wall = wall_between(row, col, next_row, next_col); if (wall >= 0) { s->true_walls[wall] = 0; } } -static inline void pathfinder_mark_visited(State* s, int row, int col) { - if (!pathfinder_in_bounds(row, col) || s->visited[row][col]) { +static inline void mark_visited(State* s, int row, int col) { + if (!in_bounds(row, col) || s->visited[row][col]) { return; } s->visited[row][col] = 1; s->visited_count++; } -static inline void pathfinder_reset_move_history(State* s) { +static inline void reset_move_history(State* s) { s->recent_rows[0] = (unsigned char)s->agent_row; s->recent_cols[0] = (unsigned char)s->agent_col; s->recent_count = 1; } -static inline void pathfinder_ensure_move_history(State* s) { +static inline void ensure_move_history(State* s) { if (s->recent_count <= 0) { - pathfinder_reset_move_history(s); + reset_move_history(s); } } -static inline bool pathfinder_repeats_two_cell_cycle( +static inline bool repeats_two_cell_cycle( const State* s, int next_row, int next_col) { return s->recent_count >= 3 && s->recent_rows[0] == s->agent_row && @@ -233,7 +249,7 @@ static inline bool pathfinder_repeats_two_cell_cycle( s->recent_cols[1] == next_col; } -static inline void pathfinder_record_successful_move(State* s) { +static inline void record_successful_move(State* s) { if (s->recent_count < 3) { int idx = s->recent_count++; s->recent_rows[idx] = (unsigned char)s->agent_row; @@ -249,7 +265,7 @@ static inline void pathfinder_record_successful_move(State* s) { s->recent_cols[2] = (unsigned char)s->agent_col; } -static inline void pathfinder_action_delta(int action, int* d_row, int* d_col) { +static inline void action_delta(int action, int* d_row, int* d_col) { *d_row = 0; *d_col = 0; if (action == PATHFINDER_ACT_NORTH) { @@ -263,7 +279,7 @@ static inline void pathfinder_action_delta(int action, int* d_row, int* d_col) { } } -static int pathfinder_shortest_path(const State* s) { +static int shortest_path(const State* s) { int dist[PATHFINDER_ROWS][PATHFINDER_COLS]; int queue[PATHFINDER_ROWS * PATHFINDER_COLS]; memset(dist, -1, sizeof(dist)); @@ -286,10 +302,10 @@ static int pathfinder_shortest_path(const State* s) { for (int action = 0; action < PATHFINDER_NUM_ACTIONS; action++) { int nr = row + d_rows[action]; int nc = col + d_cols[action]; - if (!pathfinder_in_bounds(nr, nc)) { + if (!in_bounds(nr, nc)) { continue; } - int wall = pathfinder_wall_between(row, col, nr, nc); + int wall = wall_between(row, col, nr, nc); if (wall < 0 || s->true_walls[wall]) { continue; } @@ -304,11 +320,11 @@ static int pathfinder_shortest_path(const State* s) { return -1; } -static inline bool pathfinder_has_path_to_goal(const State* s) { - return pathfinder_shortest_path(s) >= 0; +static inline bool has_path_to_goal(const State* s) { + return shortest_path(s) >= 0; } -static void pathfinder_recount_known(State* s) { +static void recount_known(State* s) { s->known_wall_count = 0; s->known_open_count = 0; for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { @@ -320,7 +336,7 @@ static void pathfinder_recount_known(State* s) { } } -static void pathfinder_update_action_mask(Pathfinder* env) { +static void update_action_mask(Pathfinder* env) { if (env->action_mask == NULL) { return; } @@ -328,19 +344,19 @@ static void pathfinder_update_action_mask(Pathfinder* env) { for (int action = 0; action < PATHFINDER_NUM_ACTIONS; action++) { int d_row; int d_col; - pathfinder_action_delta(action, &d_row, &d_col); + action_delta(action, &d_row, &d_col); int next_row = env->state.agent_row + d_row; int next_col = env->state.agent_col + d_col; - int wall = pathfinder_wall_between( + int wall = wall_between( env->state.agent_row, env->state.agent_col, next_row, next_col); env->action_mask[action] = (wall >= 0 && env->state.known_walls[wall] == PATHFINDER_WALL) ? 0 : 1; } } -static void pathfinder_update_observations(Pathfinder* env) { - pathfinder_recount_known(&env->state); - pathfinder_update_action_mask(env); +static void update_observations(Pathfinder* env) { + recount_known(&env->state); + update_action_mask(env); if (env->observations == NULL) { return; } @@ -353,7 +369,7 @@ static void pathfinder_update_observations(Pathfinder* env) { (float)env->state.agent_row / (float)(PATHFINDER_ROWS - 1); } -static void pathfinder_reset_known(State* s) { +static void reset_known(State* s) { for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { s->known_walls[i] = PATHFINDER_UNKNOWN; } @@ -361,41 +377,175 @@ static void pathfinder_reset_known(State* s) { s->known_open_count = 0; } -static void pathfinder_init_walls(State* s) { +static void init_walls(State* s) { memset(s->true_walls, 1, sizeof(s->true_walls)); - pathfinder_reset_known(s); - s->true_walls[pathfinder_v_wall(0, 0)] = 0; + reset_known(s); + s->true_walls[v_wall(0, 0)] = 0; } -static void pathfinder_carve_solution(Pathfinder* env) { - State* s = &env->state; - int row = 0; - int col = 0; - - while (row != s->goal_row || col != s->goal_col) { - bool can_row = row != s->goal_row; - bool can_col = col != s->goal_col; - bool step_row = can_row && (!can_col || (pathfinder_rand(env) & 1u)); - - int next_row = row; - int next_col = col; - if (step_row) { - next_row += (s->goal_row > row) ? 1 : -1; - } else { - next_col += (s->goal_col > col) ? 1 : -1; +static inline int pathfinder_rand(Pathfinder* env) { + return (int)rand_u32(env); +} + +static inline void pathfinder_reset_known(State* s) { + reset_known(s); +} + +static inline void pathfinder_mark_visited(State* s, int row, int col) { + mark_visited(s, row, col); +} + +static inline void pathfinder_open_edge(State* s, int row, int col, int next_row, int next_col) { + open_edge(s, row, col, next_row, next_col); +} + +static inline int pathfinder_shortest_path(const State* s) { + return shortest_path(s); +} + +static inline int pathfinder_v_wall(int row, int edge_col) { + return v_wall(row, edge_col); +} + +static inline int pathfinder_h_wall(int edge_row, int col) { + return h_wall(edge_row, col); +} + +static inline int pathfinder_wall_between(int row, int col, int next_row, int next_col) { + return wall_between(row, col, next_row, next_col); +} + +static inline void pathfinder_action_delta(int action, int* d_row, int* d_col) { + action_delta(action, d_row, d_col); +} + +static inline bool pathfinder_has_path_to_goal(const State* s) { + return has_path_to_goal(s); +} + +static inline int pathfinder_curriculum_max_solution_len(const Pathfinder* env) { + return curriculum_max_solution_len(env); +} + +static inline int pathfinder_curriculum_min_solution_len(const Pathfinder* env) { + return curriculum_min_solution_len(env); +} + +static bool carve_solution_recursive( + Pathfinder* env, int row, int col, int remaining_steps, + int step, int visited_count, int* path_rows, int* path_cols, + unsigned char visited[PATHFINDER_ROWS][PATHFINDER_COLS]) { + if (remaining_steps == 0) { + return true; + } + + int max_possible_steps = PATHFINDER_MAX_PATH_CELLS - visited_count; + if (remaining_steps > max_possible_steps) { + return false; + } + + int next_rows[PATHFINDER_NUM_ACTIONS]; + int next_cols[PATHFINDER_NUM_ACTIONS]; + int option_count = 0; + + static const int d_rows[PATHFINDER_NUM_ACTIONS] = {-1, 0, 1, 0}; + static const int d_cols[PATHFINDER_NUM_ACTIONS] = {0, 1, 0, -1}; + for (int action = 0; action < PATHFINDER_NUM_ACTIONS; action++) { + int nr = row + d_rows[action]; + int nc = col + d_cols[action]; + if (!in_bounds(nr, nc) || visited[nr][nc]) { + continue; + } + next_rows[option_count] = nr; + next_cols[option_count] = nc; + option_count++; + } + + if (option_count == 0) { + return false; + } + + for (int i = 0; i < option_count - 1; i++) { + int swap_idx = i + (int)(rand_u32(env) % (unsigned int)(option_count - i)); + int tmp_row = next_rows[i]; + int tmp_col = next_cols[i]; + next_rows[i] = next_rows[swap_idx]; + next_cols[i] = next_cols[swap_idx]; + next_rows[swap_idx] = tmp_row; + next_cols[swap_idx] = tmp_col; + } + + for (int i = 0; i < option_count; i++) { + int nr = next_rows[i]; + int nc = next_cols[i]; + + visited[nr][nc] = 1; + path_rows[step + 1] = nr; + path_cols[step + 1] = nc; + + if (carve_solution_recursive(env, nr, nc, remaining_steps - 1, step + 1, visited_count + 1, + path_rows, path_cols, visited)) { + return true; } + visited[nr][nc] = 0; + } + return false; +} - pathfinder_open_edge(s, row, col, next_row, next_col); - row = next_row; - col = next_col; +static bool carve_solution(Pathfinder* env, int target_len) { + State* s = &env->state; + if (target_len <= 0) { + target_len = 1; } + if (target_len > PATHFINDER_MAX_SOLUTION_LEN) { + target_len = PATHFINDER_MAX_SOLUTION_LEN; + } + + unsigned char visited[PATHFINDER_ROWS][PATHFINDER_COLS] = {0}; + int path_rows[PATHFINDER_MAX_PATH_CELLS]; + int path_cols[PATHFINDER_MAX_PATH_CELLS]; + + visited[0][0] = 1; + path_rows[0] = 0; + path_cols[0] = 0; + + if (!carve_solution_recursive(env, 0, 0, target_len, 0, 1, + path_rows, path_cols, visited)) { + return false; + } + + for (int step = 0; step < target_len; step++) { + open_edge(s, path_rows[step], path_cols[step], path_rows[step + 1], path_cols[step + 1]); + } + + s->goal_row = path_rows[target_len]; + s->goal_col = path_cols[target_len]; + return true; +} + +static int biased_target_len(int min_len, int max_len, Pathfinder* env) { + int span = max_len - min_len + 1; + if (span <= 0) { + return min_len; + } + int roll_a = (int)(rand_u32(env) % (unsigned int)span); + int roll_b = (int)(rand_u32(env) % (unsigned int)span); + int roll_c = (int)(rand_u32(env) % (unsigned int)span); + int pick = roll_a; + if (roll_b > pick) { + pick = roll_b; + } + if (roll_c > pick) { + pick = roll_c; + } + return min_len + pick; } -static void pathfinder_open_random_edges(Pathfinder* env) { +static void open_random_edges(Pathfinder* env, int target_len) { State* s = &env->state; float open_prob = env->branch_prob + env->loop_prob; if (open_prob < 0.0f) open_prob = 0.0f; - if (open_prob > 0.95f) open_prob = 0.95f; + if (open_prob > 0.50f) open_prob = 0.50f; int open_threshold = (int)(open_prob * 256.0f); int entry_threshold = (int)(env->extra_entry_prob * 256.0f); unsigned int samples = 0; @@ -403,70 +553,74 @@ static void pathfinder_open_random_edges(Pathfinder* env) { for (int row = 0; row < PATHFINDER_ROWS; row++) { for (int col = 0; col < PATHFINDER_COLS - 1; col++) { - if (pathfinder_rand_chance_u8(env, open_threshold, &samples, &remaining)) { - s->true_walls[pathfinder_v_wall(row, col + 1)] = 0; + if (rand_chance_u8(env, open_threshold, &samples, &remaining)) { + int wall = v_wall(row, col + 1); + if (s->true_walls[wall] == 0) { + continue; + } + s->true_walls[wall] = 0; + if (shortest_path(s) < target_len) { + s->true_walls[wall] = 1; + } } } } for (int row = 0; row < PATHFINDER_ROWS - 1; row++) { for (int col = 0; col < PATHFINDER_COLS; col++) { - if (pathfinder_rand_chance_u8(env, open_threshold, &samples, &remaining)) { - s->true_walls[pathfinder_h_wall(row + 1, col)] = 0; + if (rand_chance_u8(env, open_threshold, &samples, &remaining)) { + int wall = h_wall(row + 1, col); + if (s->true_walls[wall] == 0) { + continue; + } + s->true_walls[wall] = 0; + if (shortest_path(s) < target_len) { + s->true_walls[wall] = 1; + } } } } for (int row = 1; row < PATHFINDER_ROWS; row++) { - if (pathfinder_rand_chance_u8(env, entry_threshold, &samples, &remaining)) { - s->true_walls[pathfinder_v_wall(row, 0)] = 0; - } - } -} - -static void pathfinder_choose_goal_at_distance(Pathfinder* env, int distance) { - State* s = &env->state; - int candidates[PATHFINDER_ROWS * PATHFINDER_COLS]; - int count = 0; - distance = pathfinder_clamp_int(distance, 1, PATHFINDER_MAX_SOLUTION_LEN); - - for (int row = 0; row < PATHFINDER_ROWS; row++) { - for (int col = 0; col < PATHFINDER_COLS; col++) { - if (row == 0 && col == 0) { + if (rand_chance_u8(env, entry_threshold, &samples, &remaining)) { + int wall = v_wall(row, 0); + if (s->true_walls[wall] == 0) { continue; } - if (row + col == distance) { - candidates[count++] = row * PATHFINDER_COLS + col; + s->true_walls[wall] = 0; + if (shortest_path(s) < target_len) { + s->true_walls[wall] = 1; } } } - - if (count == 0) { - s->goal_row = PATHFINDER_ROWS - 1; - s->goal_col = PATHFINDER_COLS - 1; - return; - } - - int cell = candidates[pathfinder_rand(env) % (unsigned int)count]; - s->goal_row = cell / PATHFINDER_COLS; - s->goal_col = cell % PATHFINDER_COLS; } -static void pathfinder_generate_maze(Pathfinder* env) { +static void generate_maze(Pathfinder* env) { State* s = &env->state; - int min_solution_len = pathfinder_curriculum_min_solution_len(env); - int max_solution_len = pathfinder_curriculum_max_solution_len(env); - int span = max_solution_len - min_solution_len + 1; - int target_len = min_solution_len + (int)(pathfinder_rand(env) % (unsigned int)span); + int min_solution_len = curriculum_min_solution_len(env); + int max_solution_len = curriculum_max_solution_len(env); + int target_len = biased_target_len(min_solution_len, max_solution_len, env); + + init_walls(s); + bool carved = carve_solution(env, target_len); + if (!carved) { + target_len = min_solution_len; + carved = carve_solution(env, target_len); + } + + if (!carved) { + target_len = 1; + carve_solution(env, target_len); + } - pathfinder_init_walls(s); - pathfinder_choose_goal_at_distance(env, target_len); - pathfinder_carve_solution(env); - pathfinder_open_random_edges(env); - s->shortest_path_len = s->goal_row + s->goal_col; + open_random_edges(env, target_len); + s->shortest_path_len = shortest_path(s); + if (s->shortest_path_len < 0) { + s->shortest_path_len = target_len; + } } -static void pathfinder_update_curriculum(Pathfinder* env, int success) { +static void update_curriculum(Pathfinder* env, int success) { env->curriculum_episodes++; - if (success && pathfinder_curriculum_max_solution_len(env) < PATHFINDER_MAX_SOLUTION_LEN) { + if (success && curriculum_max_solution_len(env) < PATHFINDER_MAX_SOLUTION_LEN) { env->curriculum_level++; } } @@ -474,6 +628,14 @@ static void pathfinder_update_curriculum(Pathfinder* env, int success) { void add_log(Pathfinder* env) { State* s = &env->state; float success = (float)s->success; + int current_curriculum_level = env->curriculum_level; + int current_curriculum_min_solution_len = curriculum_min_solution_len(env); + int current_curriculum_max_solution_len = curriculum_max_solution_len(env); + int next_curriculum_max_solution_len = pathfinder_clamp_int( + current_curriculum_max_solution_len + (s->success ? 1 : 0), + 1, + PATHFINDER_MAX_SOLUTION_LEN + ); float efficiency = 0.0f; if (s->success && s->agent_path_len > 0 && s->shortest_path_len > 0) { efficiency = (float)s->shortest_path_len / (float)s->agent_path_len; @@ -482,13 +644,14 @@ void add_log(Pathfinder* env) { } } - pathfinder_update_curriculum(env, s->success); + update_curriculum(env, s->success); env->log.perf += success; env->log.score += success * efficiency; env->log.episode_return += s->episode_return; env->log.episode_length += (float)s->tick; env->log.success += success; + env->log.wins += success; env->log.wall_hits += (float)s->wall_hits; env->log.revisits += (float)s->revisit_count; env->log.known_wall_deaths += (float)s->known_wall_death; @@ -497,13 +660,16 @@ void add_log(Pathfinder* env) { env->log.known_open_edges += (float)s->known_open_count; env->log.shortest_path_len += (float)s->shortest_path_len; env->log.agent_path_len += (float)s->agent_path_len; - env->log.curriculum_level += (float)env->curriculum_level; - env->log.curriculum_max_solution_len += (float)pathfinder_curriculum_max_solution_len(env); + env->log.curriculum_level += (float)current_curriculum_level; + env->log.curriculum_min_solution_len += (float)current_curriculum_min_solution_len; + env->log.curriculum_max_solution_len += (float)current_curriculum_max_solution_len; + env->log.curriculum_target_len += (float)current_curriculum_max_solution_len; + env->log.curriculum_next_target_len += (float)next_curriculum_max_solution_len; env->log.n += 1.0f; } void refresh_state(Pathfinder* env) { - pathfinder_update_observations(env); + update_observations(env); } void init(Pathfinder* env) { @@ -511,10 +677,10 @@ void init(Pathfinder* env) { env->num_agents = 1; } if (env->branch_prob == 0.0f) { - env->branch_prob = 0.35f; + env->branch_prob = 0.10f; } if (env->loop_prob == 0.0f) { - env->loop_prob = 0.10f; + env->loop_prob = 0.03f; } if (env->min_solution_len == 0) { env->min_solution_len = 1; @@ -529,13 +695,13 @@ void c_reset(Pathfinder* env) { memset(s, 0, sizeof(*s)); s->agent_row = 0; s->agent_col = 0; - pathfinder_generate_maze(env); - pathfinder_mark_visited(s, s->agent_row, s->agent_col); - pathfinder_reset_move_history(s); - pathfinder_update_observations(env); + generate_maze(env); + mark_visited(s, s->agent_row, s->agent_col); + reset_move_history(s); + update_observations(env); } -static void pathfinder_reset_attempt(Pathfinder* env) { +static void reset_attempt(Pathfinder* env) { State* s = &env->state; s->tick = 0; s->agent_row = 0; @@ -549,13 +715,13 @@ static void pathfinder_reset_attempt(Pathfinder* env) { s->success = 0; s->episode_return = 0.0f; memset(s->visited, 0, sizeof(s->visited)); - pathfinder_reset_known(s); - pathfinder_mark_visited(s, s->agent_row, s->agent_col); - pathfinder_reset_move_history(s); - pathfinder_update_observations(env); + reset_known(s); + mark_visited(s, s->agent_row, s->agent_col); + reset_move_history(s); + update_observations(env); } -static void pathfinder_reveal_wall(Pathfinder* env, int wall) { +static void reveal_wall(Pathfinder* env, int wall) { State* s = &env->state; if (s->known_walls[wall] != PATHFINDER_UNKNOWN) { return; @@ -576,16 +742,16 @@ void c_step(Pathfinder* env) { } else { int d_row; int d_col; - pathfinder_action_delta(action, &d_row, &d_col); + action_delta(action, &d_row, &d_col); int next_row = s->agent_row + d_row; int next_col = s->agent_col + d_col; - int wall = pathfinder_wall_between(s->agent_row, s->agent_col, next_row, next_col); + int wall = wall_between(s->agent_row, s->agent_col, next_row, next_col); if (wall < 0) { reward += PATHFINDER_IMPOSSIBLE_PENALTY; } else { bool was_known = s->known_walls[wall] != PATHFINDER_UNKNOWN; - pathfinder_reveal_wall(env, wall); + reveal_wall(env, wall); if (s->true_walls[wall]) { s->wall_hits++; @@ -595,11 +761,11 @@ void c_step(Pathfinder* env) { s->known_wall_death = 1; env->terminals[0] = 1.0f; } - } else if (!pathfinder_in_bounds(next_row, next_col)) { + } else if (!in_bounds(next_row, next_col)) { reward += PATHFINDER_IMPOSSIBLE_PENALTY; } else { - pathfinder_ensure_move_history(s); - if (pathfinder_repeats_two_cell_cycle(s, next_row, next_col)) { + ensure_move_history(s); + if (repeats_two_cell_cycle(s, next_row, next_col)) { reward += PATHFINDER_REPEAT_MOVE_DEATH_PENALTY; s->repeat_move_death = 1; env->terminals[0] = 1.0f; @@ -608,12 +774,12 @@ void c_step(Pathfinder* env) { s->agent_row = next_row; s->agent_col = next_col; s->agent_path_len++; - pathfinder_record_successful_move(s); + record_successful_move(s); if (revisited) { s->revisit_count++; reward += PATHFINDER_REVISIT_PENALTY; } else { - pathfinder_mark_visited(s, next_row, next_col); + mark_visited(s, next_row, next_col); reward += PATHFINDER_NEW_CELL_REWARD; } if (s->agent_row == s->goal_row && s->agent_col == s->goal_col) { @@ -632,7 +798,7 @@ void c_step(Pathfinder* env) { env->rewards[0] = reward; s->episode_return += reward; - pathfinder_update_observations(env); + update_observations(env); if (env->terminals[0]) { int solved = s->success; @@ -640,7 +806,7 @@ void c_step(Pathfinder* env) { if (solved) { c_reset(env); } else { - pathfinder_reset_attempt(env); + reset_attempt(env); } } } @@ -675,7 +841,7 @@ static const Color PATHFINDER_GOAL = {232, 184, 58, 255}; static const Color PATHFINDER_START = {118, 146, 150, 255}; static const Color PATHFINDER_VISITED = {0, 187, 187, 42}; -static PathfinderClient* pathfinder_make_client(void) { +static PathfinderClient* make_client(void) { PathfinderClient* client = (PathfinderClient*)calloc(1, sizeof(PathfinderClient)); client->show_truth = true; InitWindow(PATHFINDER_RENDER_WIDTH, PATHFINDER_RENDER_HEIGHT, "PufferLib Pathfinder"); @@ -683,22 +849,22 @@ static PathfinderClient* pathfinder_make_client(void) { return client; } -static inline int pathfinder_cell_x(int col) { +static inline int cell_x(int col) { return PATHFINDER_RENDER_BOARD_X + col * PATHFINDER_RENDER_TILE; } -static inline int pathfinder_cell_y(int row) { +static inline int cell_y(int row) { return PATHFINDER_RENDER_BOARD_Y + row * PATHFINDER_RENDER_TILE; } -static inline Vector2 pathfinder_cell_center(int row, int col) { +static inline Vector2 cell_center(int row, int col) { return (Vector2){ - (float)(pathfinder_cell_x(col) + PATHFINDER_RENDER_TILE / 2), - (float)(pathfinder_cell_y(row) + PATHFINDER_RENDER_TILE / 2) + (float)(cell_x(col) + PATHFINDER_RENDER_TILE / 2), + (float)(cell_y(row) + PATHFINDER_RENDER_TILE / 2) }; } -static const char* pathfinder_action_name(int action) { +static const char* action_name(int action) { if (action == PATHFINDER_ACT_NORTH) return "north"; if (action == PATHFINDER_ACT_EAST) return "east"; if (action == PATHFINDER_ACT_SOUTH) return "south"; @@ -706,13 +872,13 @@ static const char* pathfinder_action_name(int action) { return "invalid"; } -static void pathfinder_draw_centered_text(const char* text, int cx, int y, +static void draw_centered_text(const char* text, int cx, int y, int font_size, Color color) { int width = MeasureText(text, font_size); DrawText(text, cx - width / 2, y, font_size, color); } -static void pathfinder_draw_edge(Pathfinder* env, int wall, Vector2 start, Vector2 end) { +static void draw_edge(Pathfinder* env, int wall, Vector2 start, Vector2 end) { State* s = &env->state; float known = s->known_walls[wall]; @@ -728,16 +894,16 @@ static void pathfinder_draw_edge(Pathfinder* env, int wall, Vector2 start, Vecto } } -static void pathfinder_draw_board(Pathfinder* env) { +static void draw_board(Pathfinder* env) { State* s = &env->state; for (int row = 0; row < PATHFINDER_ROWS; row++) { for (int col = 0; col < PATHFINDER_COLS; col++) { Color cell_color = ((row + col) & 1) ? PATHFINDER_CELL_A : PATHFINDER_CELL_B; - DrawRectangle(pathfinder_cell_x(col), pathfinder_cell_y(row), + DrawRectangle(cell_x(col), cell_y(row), PATHFINDER_RENDER_TILE - 1, PATHFINDER_RENDER_TILE - 1, cell_color); if (s->visited[row][col]) { - DrawRectangle(pathfinder_cell_x(col) + 8, pathfinder_cell_y(row) + 8, + DrawRectangle(cell_x(col) + 8, cell_y(row) + 8, PATHFINDER_RENDER_TILE - 17, PATHFINDER_RENDER_TILE - 17, PATHFINDER_VISITED); } } @@ -752,66 +918,61 @@ static void pathfinder_draw_board(Pathfinder* env) { for (int col = 0; col < PATHFINDER_COLS; col++) { char label[2] = {(char)('A' + col), '\0'}; - pathfinder_draw_centered_text(label, - pathfinder_cell_x(col) + PATHFINDER_RENDER_TILE / 2, + draw_centered_text(label, + cell_x(col) + PATHFINDER_RENDER_TILE / 2, PATHFINDER_RENDER_BOARD_Y - 28, 20, PATHFINDER_TEXT); } for (int row = 0; row < PATHFINDER_ROWS; row++) { DrawText(TextFormat("%i", row + 1), PATHFINDER_RENDER_BOARD_X - 28, - pathfinder_cell_y(row) + PATHFINDER_RENDER_TILE / 2 - 10, + cell_y(row) + PATHFINDER_RENDER_TILE / 2 - 10, 20, PATHFINDER_TEXT); } DrawRectangleLinesEx((Rectangle){ - (float)pathfinder_cell_x(0) + 4.0f, - (float)pathfinder_cell_y(0) + 4.0f, + (float)cell_x(0) + 4.0f, + (float)cell_y(0) + 4.0f, (float)PATHFINDER_RENDER_TILE - 9.0f, (float)PATHFINDER_RENDER_TILE - 9.0f }, 2.0f, PATHFINDER_START); - pathfinder_draw_centered_text("A1", pathfinder_cell_x(0) + PATHFINDER_RENDER_TILE / 2, - pathfinder_cell_y(0) + PATHFINDER_RENDER_TILE - 24, 16, PATHFINDER_MUTED); + draw_centered_text("A1", cell_x(0) + PATHFINDER_RENDER_TILE / 2, + cell_y(0) + PATHFINDER_RENDER_TILE - 24, 16, PATHFINDER_MUTED); for (int row = 0; row < PATHFINDER_ROWS; row++) { for (int edge_col = 0; edge_col <= PATHFINDER_COLS; edge_col++) { - int wall = pathfinder_v_wall(row, edge_col); + int wall = v_wall(row, edge_col); float x = (float)(PATHFINDER_RENDER_BOARD_X + edge_col * PATHFINDER_RENDER_TILE); - float y0 = (float)(pathfinder_cell_y(row) + 7); - float y1 = (float)(pathfinder_cell_y(row + 1) - 7); - pathfinder_draw_edge(env, wall, (Vector2){x, y0}, (Vector2){x, y1}); + float y0 = (float)(cell_y(row) + 7); + float y1 = (float)(cell_y(row + 1) - 7); + draw_edge(env, wall, (Vector2){x, y0}, (Vector2){x, y1}); } } for (int edge_row = 0; edge_row <= PATHFINDER_ROWS; edge_row++) { for (int col = 0; col < PATHFINDER_COLS; col++) { - int wall = pathfinder_h_wall(edge_row, col); - float x0 = (float)(pathfinder_cell_x(col) + 7); - float x1 = (float)(pathfinder_cell_x(col + 1) - 7); + int wall = h_wall(edge_row, col); + float x0 = (float)(cell_x(col) + 7); + float x1 = (float)(cell_x(col + 1) - 7); float y = (float)(PATHFINDER_RENDER_BOARD_Y + edge_row * PATHFINDER_RENDER_TILE); - pathfinder_draw_edge(env, wall, (Vector2){x0, y}, (Vector2){x1, y}); + draw_edge(env, wall, (Vector2){x0, y}, (Vector2){x1, y}); } } if (env->client->show_truth) { - Vector2 goal = pathfinder_cell_center(s->goal_row, s->goal_col); + Vector2 goal = cell_center(s->goal_row, s->goal_col); DrawCircleV(goal, 19.0f, PATHFINDER_GOAL); - pathfinder_draw_centered_text("T", (int)goal.x, (int)goal.y - 10, 22, PATHFINDER_BG); + draw_centered_text("T", (int)goal.x, (int)goal.y - 10, 22, PATHFINDER_BG); } - Vector2 agent = pathfinder_cell_center(s->agent_row, s->agent_col); + Vector2 agent = cell_center(s->agent_row, s->agent_col); DrawCircleV(agent, 21.0f, PATHFINDER_AGENT); DrawCircleLines((int)agent.x, (int)agent.y, 22.0f, PATHFINDER_TEXT); - pathfinder_draw_centered_text("P", (int)agent.x, (int)agent.y - 11, 24, PATHFINDER_BG); + draw_centered_text("P", (int)agent.x, (int)agent.y - 11, 24, PATHFINDER_BG); } -static void pathfinder_draw_panel(Pathfinder* env) { +static void draw_panel(Pathfinder* env) { State* s = &env->state; int x = PATHFINDER_RENDER_BOARD_X + PATHFINDER_RENDER_BOARD_SIZE + 34; int y = PATHFINDER_RENDER_BOARD_Y; - int action = env->actions == NULL ? -1 : (int)env->actions[0]; - float reward = env->rewards == NULL ? 0.0f : env->rewards[0]; - float terminal = env->terminals == NULL ? 0.0f : env->terminals[0]; - int unknown = PATHFINDER_NUM_WALLS - s->known_wall_count - s->known_open_count; - DrawText("Pathfinder", x, y, 28, PATHFINDER_TEXT); y += 38; DrawText(env->client->show_truth ? "View: truth + observation" : "View: observation only", @@ -829,28 +990,9 @@ static void pathfinder_draw_panel(Pathfinder* env) { } y += 34; - DrawText(TextFormat("Tick: %i / %i", s->tick, env->max_steps), x, y, 18, PATHFINDER_TEXT); - y += 24; - DrawText(TextFormat("Action: %s", pathfinder_action_name(action)), x, y, 18, PATHFINDER_TEXT); - y += 24; - DrawText(TextFormat("Reward: %.3f", reward), x, y, 18, - reward >= 0.0f ? PATHFINDER_KNOWN_OPEN : PATHFINDER_KNOWN_WALL); - y += 24; - DrawText(TextFormat("Return: %.3f", s->episode_return), x, y, 18, PATHFINDER_TEXT); - y += 24; - DrawText(TextFormat("Terminal: %.0f", terminal), x, y, 18, PATHFINDER_TEXT); - y += 34; - - DrawText(TextFormat("Known walls: %i", s->known_wall_count), x, y, 18, PATHFINDER_KNOWN_WALL); - y += 24; - DrawText(TextFormat("Known open: %i", s->known_open_count), x, y, 18, PATHFINDER_KNOWN_OPEN); - y += 24; - DrawText(TextFormat("Unknown edges: %i", unknown), x, y, 18, PATHFINDER_MUTED); - y += 34; + y += 10; - DrawText(TextFormat("Wall hits: %i", s->wall_hits), x, y, 18, PATHFINDER_TEXT); - y += 24; - DrawText(TextFormat("Revisits: %i", s->revisit_count), x, y, 18, PATHFINDER_TEXT); + DrawText(TextFormat("Wall deaths: %i", s->wall_hits), x, y, 18, PATHFINDER_TEXT); y += 24; DrawText(TextFormat("Known-wall deaths: %.0f", env->log.known_wall_deaths), x, y, 18, PATHFINDER_KNOWN_WALL); @@ -858,18 +1000,8 @@ static void pathfinder_draw_panel(Pathfinder* env) { DrawText(TextFormat("Repeat-move deaths: %.0f", env->log.repeat_move_deaths), x, y, 18, PATHFINDER_KNOWN_WALL); y += 24; - DrawText(TextFormat("Shortest path: %i", s->shortest_path_len), x, y, 18, PATHFINDER_TEXT); - y += 24; - DrawText(TextFormat("Agent path: %i", s->agent_path_len), x, y, 18, PATHFINDER_TEXT); - y += 34; - - DrawText(TextFormat("Episodes: %.0f", env->log.n), x, y, 18, PATHFINDER_TEXT); - y += 24; - DrawText(TextFormat("Avg success: %.3f", env->log.n > 0.0f ? - env->log.success / env->log.n : 0.0f), x, y, 18, PATHFINDER_TEXT); - y += 24; - DrawText(TextFormat("Curriculum: %i / %i moves", env->curriculum_level, - pathfinder_curriculum_max_solution_len(env)), x, y, 18, PATHFINDER_GOAL); + DrawText(TextFormat("Wins: %.0f", env->log.wins), + x, y, 18, PATHFINDER_KNOWN_OPEN); DrawText("Arrows/WASD move | R reset", PATHFINDER_RENDER_BOARD_X, PATHFINDER_RENDER_HEIGHT - 30, 18, PATHFINDER_MUTED); @@ -880,7 +1012,7 @@ static void pathfinder_draw_panel(Pathfinder* env) { void c_render(Pathfinder* env) { if (!IsWindowReady()) { - env->client = pathfinder_make_client(); + env->client = make_client(); } else if (env->client == NULL) { env->client = (PathfinderClient*)calloc(1, sizeof(PathfinderClient)); env->client->show_truth = true; @@ -899,8 +1031,8 @@ void c_render(Pathfinder* env) { DrawText("Milton Bradley Pathfinder", PATHFINDER_RENDER_BOARD_X, 26, 30, PATHFINDER_TEXT); DrawText("Red = known wall, green = known open, gray = true hidden wall", PATHFINDER_RENDER_BOARD_X, 60, 18, PATHFINDER_MUTED); - pathfinder_draw_board(env); - pathfinder_draw_panel(env); + draw_board(env); + draw_panel(env); EndDrawing(); } #endif diff --git a/ocean/pathfinder/tests/benchmark_pathfinder.py b/ocean/pathfinder/tests/benchmark_pathfinder.py index b9ec7fafaa..4fe4eae223 100755 --- a/ocean/pathfinder/tests/benchmark_pathfinder.py +++ b/ocean/pathfinder/tests/benchmark_pathfinder.py @@ -64,6 +64,7 @@ def run_once(label, args, timesteps, eval_epochs_override): "total_wall_sps": actual_steps / elapsed if elapsed > 0 else 0.0, "reported_sps": flat.get("SPS", 0.0), "success": stats.get("env/success", 0.0), + "wins": stats.get("env/wins", 0.0), "score": stats.get("env/score", 0.0), "episode_return": stats.get("env/episode_return", 0.0), "episode_length": stats.get("env/episode_length", 0.0), @@ -74,7 +75,10 @@ def run_once(label, args, timesteps, eval_epochs_override): "shortest_path_len": stats.get("env/shortest_path_len", 0.0), "agent_path_len": stats.get("env/agent_path_len", 0.0), "curriculum_level": stats.get("env/curriculum_level", 0.0), + "curriculum_min_solution_len": stats.get("env/curriculum_min_solution_len", 0.0), "curriculum_max_solution_len": stats.get("env/curriculum_max_solution_len", 0.0), + "curriculum_target_len": stats.get("env/curriculum_target_len", 0.0), + "curriculum_next_target_len": stats.get("env/curriculum_next_target_len", 0.0), "perf_rollout_sec": flat.get("perf/rollout", 0.0), "perf_eval_env_sec": flat.get("perf/eval_env", 0.0), "perf_train_sec": flat.get("perf/train", 0.0), diff --git a/ocean/pathfinder/tests/test_pathfinder_core.c b/ocean/pathfinder/tests/test_pathfinder_core.c index bca9107a1b..4ac50ccea1 100644 --- a/ocean/pathfinder/tests/test_pathfinder_core.c +++ b/ocean/pathfinder/tests/test_pathfinder_core.c @@ -241,7 +241,6 @@ static void test_generated_shortest_path_matches_goal_distance(void) { for (int i = 0; i < 200; i++) { c_reset(&env); assert(env.state.shortest_path_len == pathfinder_shortest_path(&env.state)); - assert(env.state.shortest_path_len == env.state.goal_row + env.state.goal_col); } } @@ -576,7 +575,9 @@ static void test_generation_can_reach_board_max_distance(void) { for (int i = 0; i < 200; i++) { c_reset(&env); - assert(env.state.shortest_path_len == PATHFINDER_MAX_SOLUTION_LEN); + assert(env.state.shortest_path_len >= 1); + assert(env.state.shortest_path_len <= PATHFINDER_MAX_SOLUTION_LEN); + assert(pathfinder_has_path_to_goal(&env.state)); } } From 549b399e12219a48219040092c3aaafa9bc450e2 Mon Sep 17 00:00:00 2001 From: Kinvert Date: Fri, 5 Jun 2026 13:17:50 -0700 Subject: [PATCH 09/16] Tune pathfinder defaults to best path2 sweep run --- config/pathfinder.ini | 69 +++++++++------- ocean/pathfinder/binding.c | 9 +++ ocean/pathfinder/pathfinder.h | 57 ++++++++------ ocean/pathfinder/tests/test_pathfinder_core.c | 78 ++++++++++++++----- 4 files changed, 142 insertions(+), 71 deletions(-) diff --git a/config/pathfinder.ini b/config/pathfinder.ini index ee472b5599..a42d1a16fa 100644 --- a/config/pathfinder.ini +++ b/config/pathfinder.ini @@ -2,47 +2,56 @@ env_name = pathfinder [vec] -total_agents = 8192 -num_buffers = 2 +total_agents = 4096 +num_buffers = 8 num_threads = 0 [env] -branch_prob = 0.11 -loop_prob = 0.04 +branch_prob = 0.008231888077505314 +loop_prob = 0.009618103745667449 extra_entry_prob = 0.0 +step_penalty = -0.001 +new_wall_penalty = 0.0 +known_wall_penalty = -0.01 +known_wall_death_penalty = -0.05 +repeat_move_death_penalty = -1.0 +new_cell_reward = 0.01 +revisit_penalty = -0.01 +impossible_penalty = -1.0 +goal_reward = 1.0 min_solution_len = 1 max_solution_len = 4 max_steps = 128 [policy] -hidden_size = 128 +hidden_size = 64 num_layers = 2 expansion_factor = 1 [train] gpus = 1 seed = 42 -total_timesteps = 20_000_000 -learning_rate = 0.004385555933422184 +total_timesteps = 10000000 +learning_rate = 0.0050000000000000044 anneal_lr = 1 min_lr_ratio = 0 -gamma = 0.9156890319524347 -gae_lambda = 0.995 -replay_ratio = 1.841268465367614 -clip_coef = 0.5499249092401194 -vf_coef = 3.2377097432640958 -vf_clip_coef = 1.8780705428234663 -max_grad_norm = 1.6383931004852927 -ent_coef = 0.006542649506789298 +gamma = 0.9971535367510901 +gae_lambda = 0.817278800352197 +replay_ratio = 4 +clip_coef = 1 +vf_coef = 1.6878199475999294 +vf_clip_coef = 4.010603143252005 +max_grad_norm = 0.2693000896567146 +ent_coef = 0.20000000000000004 anneal_ent_coef = 0 min_ent_coef_ratio = 0.1 beta1 = 0.9 beta2 = 0.999 eps = 1e-8 minibatch_size = 8192 -horizon = 256 -vtrace_rho_clip = 5 -vtrace_c_clip = 2.3816034577895158 +horizon = 16 +vtrace_rho_clip = 1.9104954167345607 +vtrace_c_clip = 1.0591472721467328 prio_alpha = 1 prio_beta0 = 0.3976524344305938 anneal_prio_beta = 0 @@ -93,35 +102,35 @@ scale = time [sweep.policy.hidden_size] distribution = uniform_pow2 -min = 64 -mean = 256 -max = 512 +min = 32 +mean = 64 +max = 128 scale = auto [sweep.policy.num_layers] distribution = uniform min = 1 -mean = 2 -max = 4 +mean = 1 +max = 2 scale = auto [sweep.vec.total_agents] distribution = uniform_pow2 -min = 256 +min = 1024 mean = 1024 -max = 2048 +max = 8192 scale = auto [sweep.train.horizon] distribution = uniform_pow2 min = 16 -mean = 64 -max = 128 +mean = 32 +max = 64 scale = auto [sweep.train.minibatch_size] distribution = uniform_pow2 -min = 1024 -mean = 2048 -max = 8192 +min = 8192 +mean = 16384 +max = 16384 scale = auto diff --git a/ocean/pathfinder/binding.c b/ocean/pathfinder/binding.c index fc2d4aa9ff..bffd0bfa97 100644 --- a/ocean/pathfinder/binding.c +++ b/ocean/pathfinder/binding.c @@ -14,6 +14,15 @@ void my_init(Env* env, Dict* kwargs) { env->branch_prob = (float)dict_get(kwargs, "branch_prob")->value; env->loop_prob = (float)dict_get(kwargs, "loop_prob")->value; env->extra_entry_prob = (float)dict_get(kwargs, "extra_entry_prob")->value; + env->step_penalty = (float)dict_get(kwargs, "step_penalty")->value; + env->new_wall_penalty = (float)dict_get(kwargs, "new_wall_penalty")->value; + env->known_wall_penalty = (float)dict_get(kwargs, "known_wall_penalty")->value; + env->known_wall_death_penalty = (float)dict_get(kwargs, "known_wall_death_penalty")->value; + env->repeat_move_death_penalty = (float)dict_get(kwargs, "repeat_move_death_penalty")->value; + env->new_cell_reward = (float)dict_get(kwargs, "new_cell_reward")->value; + env->revisit_penalty = (float)dict_get(kwargs, "revisit_penalty")->value; + env->impossible_penalty = (float)dict_get(kwargs, "impossible_penalty")->value; + env->goal_reward = (float)dict_get(kwargs, "goal_reward")->value; env->min_solution_len = (int)dict_get(kwargs, "min_solution_len")->value; env->max_solution_len = (int)dict_get(kwargs, "max_solution_len")->value; env->max_steps = (int)dict_get(kwargs, "max_steps")->value; diff --git a/ocean/pathfinder/pathfinder.h b/ocean/pathfinder/pathfinder.h index b491fecf1e..b662fb9ee9 100644 --- a/ocean/pathfinder/pathfinder.h +++ b/ocean/pathfinder/pathfinder.h @@ -20,7 +20,8 @@ #define PATHFINDER_MAX_SOLUTION_LEN ((PATHFINDER_ROWS - 1) + (PATHFINDER_COLS - 1)) #define PATHFINDER_MAX_PATH_CELLS (PATHFINDER_ROWS * PATHFINDER_COLS) -#define PATHFINDER_RENDER_TILE 72 // todo should this be derived from the number of rows/cols? +#define PATHFINDER_RENDER_MAX_SIDE ((PATHFINDER_ROWS > PATHFINDER_COLS) ? PATHFINDER_ROWS : PATHFINDER_COLS) +#define PATHFINDER_RENDER_TILE (432 / PATHFINDER_RENDER_MAX_SIDE) #define PATHFINDER_RENDER_MARGIN 40 #define PATHFINDER_RENDER_BOARD_X PATHFINDER_RENDER_MARGIN #define PATHFINDER_RENDER_BOARD_Y 92 @@ -37,20 +38,10 @@ #define PATHFINDER_ACT_SOUTH 2 #define PATHFINDER_ACT_WEST 3 -#define PATHFINDER_UNKNOWN -1.0f // todo might it learn better if 0 is unknown, and wall is -1, and open is +1? +#define PATHFINDER_UNKNOWN -1.0f #define PATHFINDER_OPEN 0.0f #define PATHFINDER_WALL 1.0f -#define PATHFINDER_STEP_PENALTY -0.001f // todo should these be made sweepable in the pathfinder.ini? -#define PATHFINDER_NEW_WALL_PENALTY 0.0f -#define PATHFINDER_KNOWN_WALL_PENALTY -0.01f // todo do we need this? I think we just kill if they hit an already KNOWN wall -#define PATHFINDER_KNOWN_WALL_DEATH_PENALTY -0.05f // todo prob make sweepable or -1 -#define PATHFINDER_REPEAT_MOVE_DEATH_PENALTY -1.0f -#define PATHFINDER_NEW_CELL_REWARD 0.01f // todo idk if I want to micromanage rewards -#define PATHFINDER_REVISIT_PENALTY -0.01f // todo make sweepable or maybe get rid of this, since we already have general move penalty -#define PATHFINDER_IMPOSSIBLE_PENALTY -0.01f // todo impossible move should be instant death and -1 -#define PATHFINDER_GOAL_REWARD 1.0f - typedef struct Log { float perf; float score; @@ -116,6 +107,15 @@ typedef struct Pathfinder { float branch_prob; float loop_prob; float extra_entry_prob; + float step_penalty; + float new_wall_penalty; + float known_wall_penalty; + float known_wall_death_penalty; + float repeat_move_death_penalty; + float new_cell_reward; + float revisit_penalty; + float impossible_penalty; + float goal_reward; int min_solution_len; int max_solution_len; int max_steps; @@ -142,7 +142,7 @@ static inline int curriculum_max_solution_len(const Pathfinder* env) { return pathfinder_clamp_int(max_len, 1, PATHFINDER_MAX_SOLUTION_LEN); } -static inline int curriculum_min_solution_len(const Pathfinder* env) { // todo is this named correctly? +static inline int curriculum_min_solution_len(const Pathfinder* env) { int base_max_solution_len = curriculum_base_solution_len(env); int base_min_solution_len = pathfinder_clamp_int( env->min_solution_len <= 0 ? 1 : env->min_solution_len, @@ -685,6 +685,15 @@ void init(Pathfinder* env) { if (env->min_solution_len == 0) { env->min_solution_len = 1; } + env->step_penalty = env->step_penalty == 0.0f ? -0.001f : env->step_penalty; + env->new_wall_penalty = env->new_wall_penalty == 0.0f ? 0.0f : env->new_wall_penalty; + env->known_wall_penalty = env->known_wall_penalty == 0.0f ? -0.01f : env->known_wall_penalty; + env->known_wall_death_penalty = env->known_wall_death_penalty == 0.0f ? -0.05f : env->known_wall_death_penalty; + env->repeat_move_death_penalty = env->repeat_move_death_penalty == 0.0f ? -1.0f : env->repeat_move_death_penalty; + env->new_cell_reward = env->new_cell_reward == 0.0f ? 0.01f : env->new_cell_reward; + env->revisit_penalty = env->revisit_penalty == 0.0f ? -0.01f : env->revisit_penalty; + env->impossible_penalty = env->impossible_penalty == 0.0f ? -1.0f : env->impossible_penalty; + env->goal_reward = env->goal_reward == 0.0f ? 1.0f : env->goal_reward; if (env->max_steps == 0) { env->max_steps = 128; } @@ -735,10 +744,11 @@ void c_step(Pathfinder* env) { env->rewards[0] = 0.0f; s->tick++; - float reward = PATHFINDER_STEP_PENALTY; + float reward = env->step_penalty; int action = (int)env->actions[0]; if (action < 0 || action >= PATHFINDER_NUM_ACTIONS) { - reward += PATHFINDER_IMPOSSIBLE_PENALTY; + reward += env->impossible_penalty; + env->terminals[0] = 1.0f; } else { int d_row; int d_col; @@ -748,25 +758,26 @@ void c_step(Pathfinder* env) { int wall = wall_between(s->agent_row, s->agent_col, next_row, next_col); if (wall < 0) { - reward += PATHFINDER_IMPOSSIBLE_PENALTY; + reward += env->impossible_penalty; + env->terminals[0] = 1.0f; } else { bool was_known = s->known_walls[wall] != PATHFINDER_UNKNOWN; reveal_wall(env, wall); if (s->true_walls[wall]) { s->wall_hits++; - reward += was_known ? PATHFINDER_KNOWN_WALL_PENALTY : PATHFINDER_NEW_WALL_PENALTY; + reward += was_known ? env->known_wall_penalty : env->new_wall_penalty; if (was_known) { - reward += PATHFINDER_KNOWN_WALL_DEATH_PENALTY; + reward += env->known_wall_death_penalty; s->known_wall_death = 1; env->terminals[0] = 1.0f; } } else if (!in_bounds(next_row, next_col)) { - reward += PATHFINDER_IMPOSSIBLE_PENALTY; + reward += env->impossible_penalty; } else { ensure_move_history(s); if (repeats_two_cell_cycle(s, next_row, next_col)) { - reward += PATHFINDER_REPEAT_MOVE_DEATH_PENALTY; + reward += env->repeat_move_death_penalty; s->repeat_move_death = 1; env->terminals[0] = 1.0f; } else { @@ -777,14 +788,14 @@ void c_step(Pathfinder* env) { record_successful_move(s); if (revisited) { s->revisit_count++; - reward += PATHFINDER_REVISIT_PENALTY; + reward += env->revisit_penalty; } else { mark_visited(s, next_row, next_col); - reward += PATHFINDER_NEW_CELL_REWARD; + reward += env->new_cell_reward; } if (s->agent_row == s->goal_row && s->agent_col == s->goal_col) { s->success = 1; - reward += PATHFINDER_GOAL_REWARD; + reward += env->goal_reward; env->terminals[0] = 1.0f; } } diff --git a/ocean/pathfinder/tests/test_pathfinder_core.c b/ocean/pathfinder/tests/test_pathfinder_core.c index 4ac50ccea1..e9893beac9 100644 --- a/ocean/pathfinder/tests/test_pathfinder_core.c +++ b/ocean/pathfinder/tests/test_pathfinder_core.c @@ -209,7 +209,7 @@ static void test_position_observation_updates_after_move(void) { assert(fabsf(obs[PATHFINDER_NUM_WALLS] - 0.2f) < 1e-6f); assert(fabsf(obs[PATHFINDER_NUM_WALLS + 1] - 0.0f) < 1e-6f); assert(fabsf(rewards[0] - - (PATHFINDER_STEP_PENALTY + PATHFINDER_NEW_CELL_REWARD)) < 1e-6f); + (env.step_penalty + env.new_cell_reward)) < 1e-6f); assert(env.state.visited[0][1] == 1); assert(env.state.visited_count == 2); } @@ -262,7 +262,7 @@ static void test_open_edge_reveals_and_moves(void) { assert(env.state.agent_col == 1); assert(fabsf(obs[east_wall] - 0.0f) < 1e-6f); assert(fabsf(rewards[0] - - (PATHFINDER_STEP_PENALTY + PATHFINDER_NEW_CELL_REWARD)) < 1e-6f); + (env.step_penalty + env.new_cell_reward)) < 1e-6f); assert(terminals[0] == 0.0f); } @@ -283,7 +283,7 @@ static void test_blocked_edge_reveals_and_stays(void) { assert(env.state.agent_row == 0); assert(env.state.agent_col == 0); assert(fabsf(obs[east_wall] - 1.0f) < 1e-6f); - assert(fabsf(rewards[0] - PATHFINDER_STEP_PENALTY) < 1e-6f); + assert(fabsf(rewards[0] - env.step_penalty) < 1e-6f); assert(terminals[0] == 0.0f); assert(env.state.known_wall_death == 0); } @@ -300,20 +300,20 @@ static void test_known_wall_repeat_terminates_with_penalty(void) { actions[0] = PATHFINDER_ACT_EAST; c_step(&env); - assert(fabsf(rewards[0] - PATHFINDER_STEP_PENALTY) < 1e-6f); + assert(fabsf(rewards[0] - env.step_penalty) < 1e-6f); assert(terminals[0] == 0.0f); c_step(&env); assert(fabsf(rewards[0] - - (PATHFINDER_STEP_PENALTY + PATHFINDER_KNOWN_WALL_PENALTY + - PATHFINDER_KNOWN_WALL_DEATH_PENALTY)) < 1e-6f); + (env.step_penalty + env.known_wall_penalty + + env.known_wall_death_penalty)) < 1e-6f); assert(terminals[0] == 1.0f); assert(env.log.n >= 1.0f); assert(env.log.success == 0.0f); assert(env.log.known_wall_deaths == 1.0f); } -static void test_invalid_action_penalizes_without_terminating(void) { +static void test_invalid_action_penalizes_and_terminates(void) { Pathfinder env; float obs[PATHFINDER_OBS_SIZE]; float actions[1] = {0}; @@ -329,8 +329,48 @@ static void test_invalid_action_penalizes_without_terminating(void) { assert(env.state.agent_row == 0); assert(env.state.agent_col == 0); assert(fabsf(rewards[0] - - (PATHFINDER_STEP_PENALTY + PATHFINDER_IMPOSSIBLE_PENALTY)) < 1e-6f); - assert(terminals[0] == 0.0f); + (env.step_penalty + env.impossible_penalty)) < 1e-6f); + assert(terminals[0] == 1.0f); + assert(env.log.n == 1.0f); + assert(env.log.success == 0.0f); +} + +static void test_configured_reward_constants_are_used(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + setup_manual_state(&env, 0, 1); + open_manual_edge(&env, 0, 0, 0, 1); + env.step_penalty = -2.0f; + env.new_cell_reward = 0.5f; + env.goal_reward = 3.0f; + + actions[0] = PATHFINDER_ACT_EAST; + c_step(&env); + + assert(fabsf(rewards[0] - (env.step_penalty + env.new_cell_reward + env.goal_reward)) < 1e-6f); + assert(env.log.success == 1.0f); +} + +static void test_configured_impossible_penalty_is_used(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + setup_manual_state(&env, 5, 5); + env.impossible_penalty = -4.0f; + + actions[0] = 99; + c_step(&env); + + assert(terminals[0] == 1.0f); + assert(fabsf(rewards[0] - (env.step_penalty + env.impossible_penalty)) < 1e-6f); + assert(env.log.n == 1.0f); } static void test_boundary_action_reveals_wall(void) { @@ -351,7 +391,7 @@ static void test_boundary_action_reveals_wall(void) { assert(env.state.agent_row == 0); assert(env.state.agent_col == 0); assert(fabsf(obs[north_wall] - PATHFINDER_WALL) < 1e-6f); - assert(fabsf(rewards[0] - PATHFINDER_STEP_PENALTY) < 1e-6f); + assert(fabsf(rewards[0] - env.step_penalty) < 1e-6f); assert(terminals[0] == 0.0f); } @@ -368,12 +408,12 @@ static void test_revisiting_previously_left_square_has_penalty(void) { actions[0] = PATHFINDER_ACT_EAST; c_step(&env); assert(fabsf(rewards[0] - - (PATHFINDER_STEP_PENALTY + PATHFINDER_NEW_CELL_REWARD)) < 1e-6f); + (env.step_penalty + env.new_cell_reward)) < 1e-6f); actions[0] = PATHFINDER_ACT_WEST; c_step(&env); assert(fabsf(rewards[0] - - (PATHFINDER_STEP_PENALTY + PATHFINDER_REVISIT_PENALTY)) < 1e-6f); + (env.step_penalty + env.revisit_penalty)) < 1e-6f); assert(terminals[0] == 0.0f); assert(env.state.revisit_count == 1); } @@ -451,7 +491,7 @@ static void test_longer_backtrack_then_forward_move_is_allowed(void) { assert(env.state.agent_row == 0); assert(env.state.agent_col == 1); assert(fabsf(rewards[0] - - (PATHFINDER_STEP_PENALTY + PATHFINDER_REVISIT_PENALTY)) < 1e-6f); + (env.step_penalty + env.revisit_penalty)) < 1e-6f); } static void test_known_open_edge_to_new_square_has_no_extra_penalty(void) { @@ -476,7 +516,7 @@ static void test_known_open_edge_to_new_square_has_no_extra_penalty(void) { assert(env.state.agent_col == 2); assert(fabsf(rewards[0] - - (PATHFINDER_STEP_PENALTY + PATHFINDER_NEW_CELL_REWARD)) < 1e-6f); + (env.step_penalty + env.new_cell_reward)) < 1e-6f); assert(env.state.revisit_count == 0); assert(terminals[0] == 0.0f); } @@ -601,7 +641,7 @@ static void test_timeout_restarts_same_map_with_unknown_wall_memory(void) { assert(terminals[0] == 1.0f); assert(fabsf(rewards[0] - - (PATHFINDER_STEP_PENALTY + PATHFINDER_NEW_CELL_REWARD)) < 1e-6f); + (env.step_penalty + env.new_cell_reward)) < 1e-6f); assert(env.log.n == 1.0f); assert(env.log.success == 0.0f); assert(env.log.episode_length == 1.0f); @@ -633,8 +673,8 @@ static void test_reaching_goal_terminates(void) { assert(terminals[0] == 1.0f); assert(fabsf(rewards[0] - - (PATHFINDER_STEP_PENALTY + PATHFINDER_NEW_CELL_REWARD + - PATHFINDER_GOAL_REWARD)) < 1e-6f); + (env.step_penalty + env.new_cell_reward + + env.goal_reward)) < 1e-6f); assert(env.log.success >= 1.0f); assert(env.log.n >= 1.0f); } @@ -652,7 +692,9 @@ int main(void) { test_open_edge_reveals_and_moves(); test_blocked_edge_reveals_and_stays(); test_known_wall_repeat_terminates_with_penalty(); - test_invalid_action_penalizes_without_terminating(); + test_invalid_action_penalizes_and_terminates(); + test_configured_reward_constants_are_used(); + test_configured_impossible_penalty_is_used(); test_boundary_action_reveals_wall(); test_revisiting_previously_left_square_has_penalty(); test_repeating_directed_move_dies_and_restarts_same_map_blind(); From 23a36e7c581176ebcd686066ad5f65890c22de61 Mon Sep 17 00:00:00 2001 From: Kinvert Date: Fri, 5 Jun 2026 16:17:06 -0700 Subject: [PATCH 10/16] Clean up pathfinder naming and curriculum logic --- ocean/pathfinder/binding.c | 2 - ocean/pathfinder/pathfinder.h | 283 +++++------------- ocean/pathfinder/tests/bench_pathfinder.c | 56 ++-- ocean/pathfinder/tests/test_pathfinder_core.c | 198 +++++++++--- 4 files changed, 276 insertions(+), 263 deletions(-) diff --git a/ocean/pathfinder/binding.c b/ocean/pathfinder/binding.c index bffd0bfa97..c7f75a497d 100644 --- a/ocean/pathfinder/binding.c +++ b/ocean/pathfinder/binding.c @@ -40,8 +40,6 @@ void my_log(Log* log, Dict* out) { dict_set(out, "revisits", log->revisits); dict_set(out, "known_wall_deaths", log->known_wall_deaths); dict_set(out, "repeat_move_deaths", log->repeat_move_deaths); - dict_set(out, "known_walls", log->known_walls); - dict_set(out, "known_open_edges", log->known_open_edges); dict_set(out, "shortest_path_len", log->shortest_path_len); dict_set(out, "agent_path_len", log->agent_path_len); dict_set(out, "curriculum_level", log->curriculum_level); diff --git a/ocean/pathfinder/pathfinder.h b/ocean/pathfinder/pathfinder.h index b662fb9ee9..f1d34459a8 100644 --- a/ocean/pathfinder/pathfinder.h +++ b/ocean/pathfinder/pathfinder.h @@ -53,8 +53,6 @@ typedef struct Log { float revisits; float known_wall_deaths; float repeat_move_deaths; - float known_walls; - float known_open_edges; float shortest_path_len; float agent_path_len; float curriculum_level; @@ -78,8 +76,6 @@ typedef struct State { int known_wall_death; int repeat_move_death; int visited_count; - int known_wall_count; - int known_open_count; int success; float episode_return; unsigned char visited[PATHFINDER_ROWS][PATHFINDER_COLS]; @@ -118,51 +114,18 @@ typedef struct Pathfinder { float goal_reward; int min_solution_len; int max_solution_len; + int curriculum_min_solution_len; int max_steps; int curriculum_level; int curriculum_episodes; State state; } Pathfinder; -static inline int pathfinder_clamp_int(int value, int min_value, int max_value) { - if (value < min_value) return min_value; - if (value > max_value) return max_value; - return value; -} - -static inline int curriculum_base_solution_len(const Pathfinder* env) { - if (env->max_solution_len <= 0) { - return PATHFINDER_MAX_SOLUTION_LEN; - } - return pathfinder_clamp_int(env->max_solution_len, 1, PATHFINDER_MAX_SOLUTION_LEN); -} - -static inline int curriculum_max_solution_len(const Pathfinder* env) { - int max_len = curriculum_base_solution_len(env) + env->curriculum_level; - return pathfinder_clamp_int(max_len, 1, PATHFINDER_MAX_SOLUTION_LEN); -} - -static inline int curriculum_min_solution_len(const Pathfinder* env) { - int base_max_solution_len = curriculum_base_solution_len(env); - int base_min_solution_len = pathfinder_clamp_int( - env->min_solution_len <= 0 ? 1 : env->min_solution_len, - 1, - base_max_solution_len - ); - int max_solution_len = pathfinder_clamp_int( - base_max_solution_len + env->curriculum_level, - 1, - PATHFINDER_MAX_SOLUTION_LEN - ); - int min_solution_len = base_min_solution_len + env->curriculum_level; - return pathfinder_clamp_int(min_solution_len, 1, max_solution_len); -} - -static inline int v_wall(int row, int edge_col) { +static inline int v_wall_idx(int row, int edge_col) { return row * (PATHFINDER_COLS + 1) + edge_col; } -static inline int h_wall(int edge_row, int col) { +static inline int h_wall_idx(int edge_row, int col) { return PATHFINDER_VERTICAL_WALLS + edge_row * PATHFINDER_COLS + col; } @@ -197,31 +160,31 @@ static inline bool rand_chance_u8(Pathfinder* env, int threshold, return (int)sample < threshold; } -static inline int wall_between(int row, int col, int next_row, int next_col) { +static inline int wall_idx_between(int row, int col, int next_row, int next_col) { if (next_row == row && next_col == col + 1) { - return v_wall(row, col + 1); + return v_wall_idx(row, col + 1); } if (next_row == row && next_col == col - 1) { - return v_wall(row, col); + return v_wall_idx(row, col); } if (next_col == col && next_row == row + 1) { - return h_wall(row + 1, col); + return h_wall_idx(row + 1, col); } if (next_col == col && next_row == row - 1) { - return h_wall(row, col); + return h_wall_idx(row, col); } return -1; } static inline void open_edge(State* s, int row, int col, int next_row, int next_col) { - int wall = wall_between(row, col, next_row, next_col); - if (wall >= 0) { - s->true_walls[wall] = 0; + int wall_idx = wall_idx_between(row, col, next_row, next_col); + if (wall_idx >= 0) { + s->true_walls[wall_idx] = 0; } } static inline void mark_visited(State* s, int row, int col) { - if (!in_bounds(row, col) || s->visited[row][col]) { + if (s->visited[row][col]) { return; } s->visited[row][col] = 1; @@ -234,12 +197,6 @@ static inline void reset_move_history(State* s) { s->recent_count = 1; } -static inline void ensure_move_history(State* s) { - if (s->recent_count <= 0) { - reset_move_history(s); - } -} - static inline bool repeats_two_cell_cycle( const State* s, int next_row, int next_col) { return s->recent_count >= 3 && @@ -305,8 +262,8 @@ static int shortest_path(const State* s) { if (!in_bounds(nr, nc)) { continue; } - int wall = wall_between(row, col, nr, nc); - if (wall < 0 || s->true_walls[wall]) { + int wall_idx = wall_idx_between(row, col, nr, nc); + if (wall_idx < 0 || s->true_walls[wall_idx]) { continue; } if (dist[nr][nc] >= 0) { @@ -320,22 +277,6 @@ static int shortest_path(const State* s) { return -1; } -static inline bool has_path_to_goal(const State* s) { - return shortest_path(s) >= 0; -} - -static void recount_known(State* s) { - s->known_wall_count = 0; - s->known_open_count = 0; - for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { - if (s->known_walls[i] == PATHFINDER_WALL) { - s->known_wall_count++; - } else if (s->known_walls[i] == PATHFINDER_OPEN) { - s->known_open_count++; - } - } -} - static void update_action_mask(Pathfinder* env) { if (env->action_mask == NULL) { return; @@ -347,15 +288,14 @@ static void update_action_mask(Pathfinder* env) { action_delta(action, &d_row, &d_col); int next_row = env->state.agent_row + d_row; int next_col = env->state.agent_col + d_col; - int wall = wall_between( + int wall_idx = wall_idx_between( env->state.agent_row, env->state.agent_col, next_row, next_col); env->action_mask[action] = - (wall >= 0 && env->state.known_walls[wall] == PATHFINDER_WALL) ? 0 : 1; + (wall_idx >= 0 && env->state.known_walls[wall_idx] == PATHFINDER_WALL) ? 0 : 1; } } static void update_observations(Pathfinder* env) { - recount_known(&env->state); update_action_mask(env); if (env->observations == NULL) { return; @@ -369,68 +309,29 @@ static void update_observations(Pathfinder* env) { (float)env->state.agent_row / (float)(PATHFINDER_ROWS - 1); } -static void reset_known(State* s) { - for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { - s->known_walls[i] = PATHFINDER_UNKNOWN; - } - s->known_wall_count = 0; - s->known_open_count = 0; +static inline int configured_base_max_solution_len(const Pathfinder* env) { + return env->max_solution_len > 0 ? env->max_solution_len : PATHFINDER_MAX_SOLUTION_LEN; +} + +static inline bool curriculum_can_advance(const Pathfinder* env) { + // The environment advances by one each successful solve until the target + // distance cap is fully shifted to the board max. + return env->curriculum_level + configured_base_max_solution_len(env) < + PATHFINDER_MAX_SOLUTION_LEN; } static void init_walls(State* s) { memset(s->true_walls, 1, sizeof(s->true_walls)); - reset_known(s); - s->true_walls[v_wall(0, 0)] = 0; + for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { + s->known_walls[i] = PATHFINDER_UNKNOWN; + } + s->true_walls[v_wall_idx(0, 0)] = 0; } static inline int pathfinder_rand(Pathfinder* env) { return (int)rand_u32(env); } -static inline void pathfinder_reset_known(State* s) { - reset_known(s); -} - -static inline void pathfinder_mark_visited(State* s, int row, int col) { - mark_visited(s, row, col); -} - -static inline void pathfinder_open_edge(State* s, int row, int col, int next_row, int next_col) { - open_edge(s, row, col, next_row, next_col); -} - -static inline int pathfinder_shortest_path(const State* s) { - return shortest_path(s); -} - -static inline int pathfinder_v_wall(int row, int edge_col) { - return v_wall(row, edge_col); -} - -static inline int pathfinder_h_wall(int edge_row, int col) { - return h_wall(edge_row, col); -} - -static inline int pathfinder_wall_between(int row, int col, int next_row, int next_col) { - return wall_between(row, col, next_row, next_col); -} - -static inline void pathfinder_action_delta(int action, int* d_row, int* d_col) { - action_delta(action, d_row, d_col); -} - -static inline bool pathfinder_has_path_to_goal(const State* s) { - return has_path_to_goal(s); -} - -static inline int pathfinder_curriculum_max_solution_len(const Pathfinder* env) { - return curriculum_max_solution_len(env); -} - -static inline int pathfinder_curriculum_min_solution_len(const Pathfinder* env) { - return curriculum_min_solution_len(env); -} - static bool carve_solution_recursive( Pathfinder* env, int row, int col, int remaining_steps, int step, int visited_count, int* path_rows, int* path_cols, @@ -523,24 +424,6 @@ static bool carve_solution(Pathfinder* env, int target_len) { return true; } -static int biased_target_len(int min_len, int max_len, Pathfinder* env) { - int span = max_len - min_len + 1; - if (span <= 0) { - return min_len; - } - int roll_a = (int)(rand_u32(env) % (unsigned int)span); - int roll_b = (int)(rand_u32(env) % (unsigned int)span); - int roll_c = (int)(rand_u32(env) % (unsigned int)span); - int pick = roll_a; - if (roll_b > pick) { - pick = roll_b; - } - if (roll_c > pick) { - pick = roll_c; - } - return min_len + pick; -} - static void open_random_edges(Pathfinder* env, int target_len) { State* s = &env->state; float open_prob = env->branch_prob + env->loop_prob; @@ -554,13 +437,13 @@ static void open_random_edges(Pathfinder* env, int target_len) { for (int row = 0; row < PATHFINDER_ROWS; row++) { for (int col = 0; col < PATHFINDER_COLS - 1; col++) { if (rand_chance_u8(env, open_threshold, &samples, &remaining)) { - int wall = v_wall(row, col + 1); - if (s->true_walls[wall] == 0) { + int wall_idx = v_wall_idx(row, col + 1); + if (s->true_walls[wall_idx] == 0) { continue; } - s->true_walls[wall] = 0; + s->true_walls[wall_idx] = 0; if (shortest_path(s) < target_len) { - s->true_walls[wall] = 1; + s->true_walls[wall_idx] = 1; } } } @@ -568,26 +451,26 @@ static void open_random_edges(Pathfinder* env, int target_len) { for (int row = 0; row < PATHFINDER_ROWS - 1; row++) { for (int col = 0; col < PATHFINDER_COLS; col++) { if (rand_chance_u8(env, open_threshold, &samples, &remaining)) { - int wall = h_wall(row + 1, col); - if (s->true_walls[wall] == 0) { + int wall_idx = h_wall_idx(row + 1, col); + if (s->true_walls[wall_idx] == 0) { continue; } - s->true_walls[wall] = 0; + s->true_walls[wall_idx] = 0; if (shortest_path(s) < target_len) { - s->true_walls[wall] = 1; + s->true_walls[wall_idx] = 1; } } } } for (int row = 1; row < PATHFINDER_ROWS; row++) { if (rand_chance_u8(env, entry_threshold, &samples, &remaining)) { - int wall = v_wall(row, 0); - if (s->true_walls[wall] == 0) { + int wall_idx = v_wall_idx(row, 0); + if (s->true_walls[wall_idx] == 0) { continue; } - s->true_walls[wall] = 0; + s->true_walls[wall_idx] = 0; if (shortest_path(s) < target_len) { - s->true_walls[wall] = 1; + s->true_walls[wall_idx] = 1; } } } @@ -595,9 +478,9 @@ static void open_random_edges(Pathfinder* env, int target_len) { static void generate_maze(Pathfinder* env) { State* s = &env->state; - int min_solution_len = curriculum_min_solution_len(env); - int max_solution_len = curriculum_max_solution_len(env); - int target_len = biased_target_len(min_solution_len, max_solution_len, env); + env->curriculum_min_solution_len = env->min_solution_len + env->curriculum_level; + int min_solution_len = env->curriculum_min_solution_len; + int target_len = min_solution_len; init_walls(s); bool carved = carve_solution(env, target_len); @@ -620,22 +503,24 @@ static void generate_maze(Pathfinder* env) { static void update_curriculum(Pathfinder* env, int success) { env->curriculum_episodes++; - if (success && curriculum_max_solution_len(env) < PATHFINDER_MAX_SOLUTION_LEN) { - env->curriculum_level++; + if (!success || !curriculum_can_advance(env)) { + return; } + + env->curriculum_level++; + env->curriculum_min_solution_len++; } void add_log(Pathfinder* env) { State* s = &env->state; float success = (float)s->success; int current_curriculum_level = env->curriculum_level; - int current_curriculum_min_solution_len = curriculum_min_solution_len(env); - int current_curriculum_max_solution_len = curriculum_max_solution_len(env); - int next_curriculum_max_solution_len = pathfinder_clamp_int( - current_curriculum_max_solution_len + (s->success ? 1 : 0), - 1, - PATHFINDER_MAX_SOLUTION_LEN - ); + int current_curriculum_min_solution_len = env->curriculum_min_solution_len; + int current_curriculum_max_solution_len = env->max_solution_len + env->curriculum_level; + int next_curriculum_max_solution_len = current_curriculum_max_solution_len; + if (s->success && curriculum_can_advance(env)) { + next_curriculum_max_solution_len++; + } float efficiency = 0.0f; if (s->success && s->agent_path_len > 0 && s->shortest_path_len > 0) { efficiency = (float)s->shortest_path_len / (float)s->agent_path_len; @@ -656,8 +541,6 @@ void add_log(Pathfinder* env) { env->log.revisits += (float)s->revisit_count; env->log.known_wall_deaths += (float)s->known_wall_death; env->log.repeat_move_deaths += (float)s->repeat_move_death; - env->log.known_walls += (float)s->known_wall_count; - env->log.known_open_edges += (float)s->known_open_count; env->log.shortest_path_len += (float)s->shortest_path_len; env->log.agent_path_len += (float)s->agent_path_len; env->log.curriculum_level += (float)current_curriculum_level; @@ -685,6 +568,7 @@ void init(Pathfinder* env) { if (env->min_solution_len == 0) { env->min_solution_len = 1; } + env->curriculum_min_solution_len = env->min_solution_len + env->curriculum_level; env->step_penalty = env->step_penalty == 0.0f ? -0.001f : env->step_penalty; env->new_wall_penalty = env->new_wall_penalty == 0.0f ? 0.0f : env->new_wall_penalty; env->known_wall_penalty = env->known_wall_penalty == 0.0f ? -0.01f : env->known_wall_penalty; @@ -697,6 +581,9 @@ void init(Pathfinder* env) { if (env->max_steps == 0) { env->max_steps = 128; } + if (env->max_solution_len == 0) { + env->max_solution_len = PATHFINDER_MAX_SOLUTION_LEN; + } } void c_reset(Pathfinder* env) { @@ -724,18 +611,20 @@ static void reset_attempt(Pathfinder* env) { s->success = 0; s->episode_return = 0.0f; memset(s->visited, 0, sizeof(s->visited)); - reset_known(s); + for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { + s->known_walls[i] = PATHFINDER_UNKNOWN; + } mark_visited(s, s->agent_row, s->agent_col); reset_move_history(s); update_observations(env); } -static void reveal_wall(Pathfinder* env, int wall) { +static void reveal_wall(Pathfinder* env, int wall_idx) { State* s = &env->state; - if (s->known_walls[wall] != PATHFINDER_UNKNOWN) { + if (s->known_walls[wall_idx] != PATHFINDER_UNKNOWN) { return; } - s->known_walls[wall] = s->true_walls[wall] ? PATHFINDER_WALL : PATHFINDER_OPEN; + s->known_walls[wall_idx] = s->true_walls[wall_idx] ? PATHFINDER_WALL : PATHFINDER_OPEN; } void c_step(Pathfinder* env) { @@ -755,16 +644,21 @@ void c_step(Pathfinder* env) { action_delta(action, &d_row, &d_col); int next_row = s->agent_row + d_row; int next_col = s->agent_col + d_col; - int wall = wall_between(s->agent_row, s->agent_col, next_row, next_col); - - if (wall < 0) { + int wall_idx = wall_idx_between(s->agent_row, s->agent_col, next_row, next_col); + if (!in_bounds(next_row, next_col)) { + if (wall_idx >= 0) { + reveal_wall(env, wall_idx); + } + reward += env->impossible_penalty; + env->terminals[0] = 1.0f; + } else if (wall_idx < 0) { reward += env->impossible_penalty; env->terminals[0] = 1.0f; } else { - bool was_known = s->known_walls[wall] != PATHFINDER_UNKNOWN; - reveal_wall(env, wall); + bool was_known = s->known_walls[wall_idx] != PATHFINDER_UNKNOWN; + reveal_wall(env, wall_idx); - if (s->true_walls[wall]) { + if (s->true_walls[wall_idx]) { s->wall_hits++; reward += was_known ? env->known_wall_penalty : env->new_wall_penalty; if (was_known) { @@ -772,10 +666,7 @@ void c_step(Pathfinder* env) { s->known_wall_death = 1; env->terminals[0] = 1.0f; } - } else if (!in_bounds(next_row, next_col)) { - reward += env->impossible_penalty; } else { - ensure_move_history(s); if (repeats_two_cell_cycle(s, next_row, next_col)) { reward += env->repeat_move_death_penalty; s->repeat_move_death = 1; @@ -875,26 +766,18 @@ static inline Vector2 cell_center(int row, int col) { }; } -static const char* action_name(int action) { - if (action == PATHFINDER_ACT_NORTH) return "north"; - if (action == PATHFINDER_ACT_EAST) return "east"; - if (action == PATHFINDER_ACT_SOUTH) return "south"; - if (action == PATHFINDER_ACT_WEST) return "west"; - return "invalid"; -} - static void draw_centered_text(const char* text, int cx, int y, int font_size, Color color) { int width = MeasureText(text, font_size); DrawText(text, cx - width / 2, y, font_size, color); } -static void draw_edge(Pathfinder* env, int wall, Vector2 start, Vector2 end) { +static void draw_edge(Pathfinder* env, int wall_idx, Vector2 start, Vector2 end) { State* s = &env->state; - float known = s->known_walls[wall]; + float known = s->known_walls[wall_idx]; DrawLineEx(start, end, 2.0f, PATHFINDER_UNKNOWN_EDGE); - if (env->client->show_truth && s->true_walls[wall]) { + if (env->client->show_truth && s->true_walls[wall_idx]) { DrawLineEx(start, end, 6.0f, PATHFINDER_TRUE_WALL); } @@ -951,20 +834,20 @@ static void draw_board(Pathfinder* env) { for (int row = 0; row < PATHFINDER_ROWS; row++) { for (int edge_col = 0; edge_col <= PATHFINDER_COLS; edge_col++) { - int wall = v_wall(row, edge_col); + int wall_idx = v_wall_idx(row, edge_col); float x = (float)(PATHFINDER_RENDER_BOARD_X + edge_col * PATHFINDER_RENDER_TILE); float y0 = (float)(cell_y(row) + 7); float y1 = (float)(cell_y(row + 1) - 7); - draw_edge(env, wall, (Vector2){x, y0}, (Vector2){x, y1}); + draw_edge(env, wall_idx, (Vector2){x, y0}, (Vector2){x, y1}); } } for (int edge_row = 0; edge_row <= PATHFINDER_ROWS; edge_row++) { for (int col = 0; col < PATHFINDER_COLS; col++) { - int wall = h_wall(edge_row, col); + int wall_idx = h_wall_idx(edge_row, col); float x0 = (float)(cell_x(col) + 7); float x1 = (float)(cell_x(col + 1) - 7); float y = (float)(PATHFINDER_RENDER_BOARD_Y + edge_row * PATHFINDER_RENDER_TILE); - draw_edge(env, wall, (Vector2){x0, y}, (Vector2){x1, y}); + draw_edge(env, wall_idx, (Vector2){x0, y}, (Vector2){x1, y}); } } diff --git a/ocean/pathfinder/tests/bench_pathfinder.c b/ocean/pathfinder/tests/bench_pathfinder.c index 5d722faeff..e85512cd4b 100644 --- a/ocean/pathfinder/tests/bench_pathfinder.c +++ b/ocean/pathfinder/tests/bench_pathfinder.c @@ -73,7 +73,21 @@ static void bench_steps(Pathfinder* env, long steps) { steps, elapsed, step_sps, env->log.n, env->log.n > 0.0f ? env->log.success / env->log.n : 0.0f, env->log.repeat_move_deaths, reward_sum, env->curriculum_level, - pathfinder_curriculum_max_solution_len(env)); + env->max_solution_len + env->curriculum_level); +} + +static void bench_choose_goal_at_distance(Pathfinder* env, int target_len) { + for (int row = 0; row < PATHFINDER_ROWS; row++) { + for (int col = 0; col < PATHFINDER_COLS; col++) { + if (row + col == target_len) { + env->state.goal_row = row; + env->state.goal_col = col; + return; + } + } + } + env->state.goal_row = PATHFINDER_ROWS - 1; + env->state.goal_col = PATHFINDER_COLS - 1; } static void bench_reset_components(Pathfinder* env, long iters) { @@ -88,19 +102,19 @@ static void bench_reset_components(Pathfinder* env, long iters) { memset(&env->state, 0, sizeof(env->state)); env->state.agent_row = 0; env->state.agent_col = 0; - pathfinder_generate_maze(env); + generate_maze(env); } double gen_sec = now_seconds() - t0; t0 = now_seconds(); for (long i = 0; i < iters; i++) { - pathfinder_update_observations(env); + update_observations(env); } double obs_sec = now_seconds() - t0; t0 = now_seconds(); for (long i = 0; i < iters; i++) { - pathfinder_update_action_mask(env); + update_action_mask(env); } double mask_sec = now_seconds() - t0; @@ -113,34 +127,34 @@ static void bench_reset_components(Pathfinder* env, long iters) { t0 = now_seconds(); for (long i = 0; i < iters; i++) { - pathfinder_init_walls(&env->state); + init_walls(&env->state); } double init_walls_sec = now_seconds() - t0; t0 = now_seconds(); for (long i = 0; i < iters; i++) { - pathfinder_choose_goal_at_distance(env, 4); + bench_choose_goal_at_distance(env, 4); } double choose_goal_sec = now_seconds() - t0; - pathfinder_init_walls(&env->state); - pathfinder_choose_goal_at_distance(env, 4); + init_walls(&env->state); + bench_choose_goal_at_distance(env, 4); t0 = now_seconds(); for (long i = 0; i < iters; i++) { - pathfinder_init_walls(&env->state); + init_walls(&env->state); env->state.goal_row = 2; env->state.goal_col = 2; - pathfinder_carve_solution(env); + carve_solution(env, 4); } double carve_sec = now_seconds() - t0; - pathfinder_init_walls(&env->state); + init_walls(&env->state); env->state.goal_row = 2; env->state.goal_col = 2; - pathfinder_carve_solution(env); + carve_solution(env, 4); t0 = now_seconds(); for (long i = 0; i < iters; i++) { - pathfinder_open_random_edges(env); + open_random_edges(env, 4); } double random_edges_sec = now_seconds() - t0; @@ -156,16 +170,18 @@ static void setup_open_line(Pathfinder* env) { State* s = &env->state; memset(s, 0, sizeof(*s)); memset(s->true_walls, 1, sizeof(s->true_walls)); - pathfinder_reset_known(s); + for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { + s->known_walls[i] = PATHFINDER_UNKNOWN; + } s->agent_row = 0; s->agent_col = 0; s->goal_row = 5; s->goal_col = 5; for (int col = 0; col < PATHFINDER_COLS - 1; col++) { - pathfinder_open_edge(s, 0, col, 0, col + 1); + open_edge(s, 0, col, 0, col + 1); } - pathfinder_mark_visited(s, 0, 0); - pathfinder_update_observations(env); + mark_visited(s, 0, 0); + update_observations(env); } static void bench_forced_steps(Pathfinder* env, long iters) { @@ -182,9 +198,9 @@ static void bench_forced_steps(Pathfinder* env, long iters) { memset(&env->log, 0, sizeof(env->log)); setup_open_line(env); - int east_wall = pathfinder_wall_between(0, 0, 0, 1); + int east_wall = wall_idx_between(0, 0, 0, 1); env->state.true_walls[east_wall] = 1; - pathfinder_update_observations(env); + update_observations(env); t0 = now_seconds(); for (long i = 0; i < iters; i++) { env->actions[0] = PATHFINDER_ACT_EAST; @@ -194,7 +210,7 @@ static void bench_forced_steps(Pathfinder* env, long iters) { } setup_open_line(env); env->state.true_walls[east_wall] = 1; - pathfinder_update_observations(env); + update_observations(env); } double known_wall_death_sec = now_seconds() - t0; diff --git a/ocean/pathfinder/tests/test_pathfinder_core.c b/ocean/pathfinder/tests/test_pathfinder_core.c index e9893beac9..fb09d0956e 100644 --- a/ocean/pathfinder/tests/test_pathfinder_core.c +++ b/ocean/pathfinder/tests/test_pathfinder_core.c @@ -27,23 +27,24 @@ static void setup_manual_state(Pathfinder* env, int goal_row, int goal_col) { State* s = &env->state; memset(s, 0, sizeof(*s)); memset(s->true_walls, 1, sizeof(s->true_walls)); - pathfinder_reset_known(s); + for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { + s->known_walls[i] = PATHFINDER_UNKNOWN; + } s->agent_row = 0; s->agent_col = 0; s->goal_row = goal_row; s->goal_col = goal_col; - pathfinder_mark_visited(s, 0, 0); + reset_move_history(s); + mark_visited(s, 0, 0); } static void open_manual_edge(Pathfinder* env, int row, int col, int next_row, int next_col) { - pathfinder_open_edge(&env->state, row, col, next_row, next_col); - env->state.shortest_path_len = pathfinder_shortest_path(&env->state); + open_edge(&env->state, row, col, next_row, next_col); + env->state.shortest_path_len = shortest_path(&env->state); refresh_state(env); } static void assert_wall_observations_unknown(Pathfinder* env, float* obs) { - assert(env->state.known_wall_count == 0); - assert(env->state.known_open_count == 0); for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { assert(fabsf(env->state.known_walls[i] - PATHFINDER_UNKNOWN) < 1e-6f); assert(fabsf(obs[i] - PATHFINDER_UNKNOWN) < 1e-6f); @@ -61,28 +62,73 @@ static void test_constants(void) { } static void test_wall_indexing_and_action_deltas(void) { - assert(pathfinder_v_wall(0, 0) == 0); - assert(pathfinder_v_wall(5, 6) == PATHFINDER_VERTICAL_WALLS - 1); - assert(pathfinder_h_wall(0, 0) == PATHFINDER_VERTICAL_WALLS); - assert(pathfinder_h_wall(6, 5) == PATHFINDER_NUM_WALLS - 1); - assert(pathfinder_wall_between(0, 0, 0, 1) == pathfinder_v_wall(0, 1)); - assert(pathfinder_wall_between(0, 1, 0, 0) == pathfinder_v_wall(0, 1)); - assert(pathfinder_wall_between(0, 0, 1, 0) == pathfinder_h_wall(1, 0)); - assert(pathfinder_wall_between(1, 0, 0, 0) == pathfinder_h_wall(1, 0)); - assert(pathfinder_wall_between(0, 0, 1, 1) == -1); + assert(v_wall_idx(0, 0) == 0); + assert(v_wall_idx(5, 6) == PATHFINDER_VERTICAL_WALLS - 1); + assert(h_wall_idx(0, 0) == PATHFINDER_VERTICAL_WALLS); + assert(h_wall_idx(6, 5) == PATHFINDER_NUM_WALLS - 1); + assert(wall_idx_between(0, 0, 0, 1) == v_wall_idx(0, 1)); + assert(wall_idx_between(0, 1, 0, 0) == v_wall_idx(0, 1)); + assert(wall_idx_between(0, 0, 1, 0) == h_wall_idx(1, 0)); + assert(wall_idx_between(1, 0, 0, 0) == h_wall_idx(1, 0)); + assert(wall_idx_between(0, 0, 1, 1) == -1); int d_row; int d_col; - pathfinder_action_delta(PATHFINDER_ACT_NORTH, &d_row, &d_col); + action_delta(PATHFINDER_ACT_NORTH, &d_row, &d_col); assert(d_row == -1 && d_col == 0); - pathfinder_action_delta(PATHFINDER_ACT_EAST, &d_row, &d_col); + action_delta(PATHFINDER_ACT_EAST, &d_row, &d_col); assert(d_row == 0 && d_col == 1); - pathfinder_action_delta(PATHFINDER_ACT_SOUTH, &d_row, &d_col); + action_delta(PATHFINDER_ACT_SOUTH, &d_row, &d_col); assert(d_row == 1 && d_col == 0); - pathfinder_action_delta(PATHFINDER_ACT_WEST, &d_row, &d_col); + action_delta(PATHFINDER_ACT_WEST, &d_row, &d_col); assert(d_row == 0 && d_col == -1); } +static void test_wall_between_bidirectional_vertical_edges(void) { + for (int row = 0; row < PATHFINDER_ROWS; row++) { + for (int col = 0; col < PATHFINDER_COLS - 1; col++) { + int wall_idx = v_wall_idx(row, col + 1); + assert(wall_idx_between(row, col, row, col + 1) == wall_idx); + assert(wall_idx_between(row, col + 1, row, col) == wall_idx); + } + } +} + +static void test_wall_between_bidirectional_horizontal_edges(void) { + for (int row = 0; row < PATHFINDER_ROWS - 1; row++) { + for (int col = 0; col < PATHFINDER_COLS; col++) { + int wall_idx = h_wall_idx(row + 1, col); + assert(wall_idx_between(row, col, row + 1, col) == wall_idx); + assert(wall_idx_between(row + 1, col, row, col) == wall_idx); + } + } +} + +static void test_wall_between_boundary_steps_to_outer_edges(void) { + for (int row = 0; row < PATHFINDER_ROWS; row++) { + for (int col = 0; col < PATHFINDER_COLS; col++) { + assert(wall_idx_between(row, 0, row, -1) == v_wall_idx(row, 0)); + assert(wall_idx_between(row, PATHFINDER_COLS - 1, row, PATHFINDER_COLS) == v_wall_idx(row, PATHFINDER_COLS)); + } + } + + for (int col = 0; col < PATHFINDER_COLS; col++) { + assert(wall_idx_between(0, col, -1, col) == h_wall_idx(0, col)); + assert(wall_idx_between(PATHFINDER_ROWS - 1, col, PATHFINDER_ROWS, col) == h_wall_idx(PATHFINDER_ROWS, col)); + } +} + +static void test_wall_between_rejects_non_adjacent_positions(void) { + assert(wall_idx_between(0, 0, 1, 1) == -1); + assert(wall_idx_between(0, 0, 2, 0) == -1); + assert(wall_idx_between(0, 0, 0, 2) == -1); + assert(wall_idx_between(2, 2, 0, 2) == -1); + assert(wall_idx_between(2, 2, 5, 2) == -1); + assert(wall_idx_between(0, 0, -2, 0) == -1); + assert(wall_idx_between(5, 5, 7, 5) == -1); + assert(wall_idx_between(3, 3, 3, 8) == -1); +} + static void test_reset_initializes_a1_and_unknown_walls(void) { Pathfinder env; float obs[PATHFINDER_OBS_SIZE]; @@ -165,7 +211,7 @@ static void test_known_wall_death_restarts_same_map_with_unknown_wall_memory(voi setup_manual_state(&env, 5, 5); refresh_state(&env); - int east_wall = pathfinder_wall_between(0, 0, 0, 1); + int east_wall = wall_idx_between(0, 0, 0, 1); memcpy(true_walls, env.state.true_walls, sizeof(true_walls)); actions[0] = PATHFINDER_ACT_EAST; @@ -224,7 +270,7 @@ static void test_generated_mazes_connect_a1_to_goal(void) { for (int i = 0; i < 100; i++) { c_reset(&env); - assert(pathfinder_has_path_to_goal(&env.state)); + assert(shortest_path(&env.state) >= 0); assert(env.state.shortest_path_len >= 0); } } @@ -240,10 +286,69 @@ static void test_generated_shortest_path_matches_goal_distance(void) { for (int i = 0; i < 200; i++) { c_reset(&env); - assert(env.state.shortest_path_len == pathfinder_shortest_path(&env.state)); + assert(env.state.shortest_path_len == shortest_path(&env.state)); } } +static void test_shortest_path_start_equals_goal(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + setup_manual_state(&env, 0, 0); + + assert(shortest_path(&env.state) == 0); +} + +static void test_shortest_path_no_available_path_returns_minus_one(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + setup_manual_state(&env, 5, 5); + + assert(shortest_path(&env.state) == -1); +} + +static void test_shortest_path_one_step_path(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + setup_manual_state(&env, 0, 1); + open_manual_edge(&env, 0, 0, 0, 1); + + assert(shortest_path(&env.state) == 1); + assert(env.state.shortest_path_len == 1); +} + +static void test_shortest_path_prefers_shorter_of_multiple_routes(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + setup_manual_state(&env, 0, 2); + + open_manual_edge(&env, 0, 0, 0, 1); + open_manual_edge(&env, 0, 1, 0, 2); + + open_manual_edge(&env, 0, 0, 1, 0); + open_manual_edge(&env, 1, 0, 1, 1); + open_manual_edge(&env, 1, 1, 1, 2); + open_manual_edge(&env, 1, 2, 0, 2); + + assert(shortest_path(&env.state) == 2); + assert(env.state.shortest_path_len == 2); +} + static void test_open_edge_reveals_and_moves(void) { Pathfinder env; float obs[PATHFINDER_OBS_SIZE]; @@ -252,7 +357,7 @@ static void test_open_edge_reveals_and_moves(void) { float terminals[1] = {0}; setup_env(&env, obs, actions, rewards, terminals); setup_manual_state(&env, 5, 5); - int east_wall = pathfinder_wall_between(0, 0, 0, 1); + int east_wall = wall_idx_between(0, 0, 0, 1); open_manual_edge(&env, 0, 0, 0, 1); actions[0] = PATHFINDER_ACT_EAST; @@ -274,7 +379,7 @@ static void test_blocked_edge_reveals_and_stays(void) { float terminals[1] = {0}; setup_env(&env, obs, actions, rewards, terminals); setup_manual_state(&env, 5, 5); - int east_wall = pathfinder_wall_between(0, 0, 0, 1); + int east_wall = wall_idx_between(0, 0, 0, 1); refresh_state(&env); actions[0] = PATHFINDER_ACT_EAST; @@ -373,7 +478,7 @@ static void test_configured_impossible_penalty_is_used(void) { assert(env.log.n == 1.0f); } -static void test_boundary_action_reveals_wall(void) { +static void test_boundary_action_terminates_with_impossible_penalty(void) { Pathfinder env; float obs[PATHFINDER_OBS_SIZE]; float actions[1] = {0}; @@ -386,13 +491,12 @@ static void test_boundary_action_reveals_wall(void) { actions[0] = PATHFINDER_ACT_NORTH; c_step(&env); - int north_wall = pathfinder_wall_between(0, 0, -1, 0); - assert(north_wall == pathfinder_h_wall(0, 0)); assert(env.state.agent_row == 0); assert(env.state.agent_col == 0); - assert(fabsf(obs[north_wall] - PATHFINDER_WALL) < 1e-6f); - assert(fabsf(rewards[0] - env.step_penalty) < 1e-6f); - assert(terminals[0] == 0.0f); + assert(fabsf(rewards[0] - (env.step_penalty + env.impossible_penalty)) < 1e-6f); + assert(terminals[0] == 1.0f); + assert(fabsf(obs[PATHFINDER_NUM_WALLS] - 0.0f) < 1e-6f); + assert(fabsf(obs[PATHFINDER_NUM_WALLS + 1] - 0.0f) < 1e-6f); } static void test_revisiting_previously_left_square_has_penalty(void) { @@ -428,7 +532,7 @@ static void test_repeating_directed_move_dies_and_restarts_same_map_blind(void) setup_env(&env, obs, actions, rewards, terminals); setup_manual_state(&env, 5, 5); open_manual_edge(&env, 0, 0, 0, 1); - int east_wall = pathfinder_wall_between(0, 0, 0, 1); + int east_wall = wall_idx_between(0, 0, 0, 1); memcpy(true_walls, env.state.true_walls, sizeof(true_walls)); actions[0] = PATHFINDER_ACT_EAST; @@ -507,7 +611,7 @@ static void test_known_open_edge_to_new_square_has_no_extra_penalty(void) { actions[0] = PATHFINDER_ACT_EAST; c_step(&env); - int second_wall = pathfinder_wall_between(0, 1, 0, 2); + int second_wall = wall_idx_between(0, 1, 0, 2); env.state.known_walls[second_wall] = PATHFINDER_OPEN; refresh_state(&env); @@ -528,12 +632,13 @@ static void test_max_solution_len_limits_curriculum_distance(void) { float rewards[1] = {0}; float terminals[1] = {0}; setup_env(&env, obs, actions, rewards, terminals); + env.min_solution_len = 2; env.max_solution_len = 2; for (int i = 0; i < 100; i++) { c_reset(&env); - assert(pathfinder_has_path_to_goal(&env.state)); - assert(env.state.shortest_path_len == 2); + assert(shortest_path(&env.state) >= 0); + assert(env.state.shortest_path_len == env.min_solution_len); } } @@ -555,7 +660,7 @@ static void test_success_generates_next_map_one_step_farther(void) { assert(terminals[0] == 1.0f); assert(env.log.success == 1.0f); - assert(pathfinder_curriculum_max_solution_len(&env) == 2); + assert(env.max_solution_len + env.curriculum_level == 2); assert(env.state.agent_row == 0); assert(env.state.agent_col == 0); assert(env.state.shortest_path_len == 2); @@ -584,7 +689,7 @@ static void test_failure_retry_does_not_graduate_curriculum(void) { assert(terminals[0] == 1.0f); assert(env.curriculum_level == 0); - assert(pathfinder_curriculum_max_solution_len(&env) == 4); + assert(env.max_solution_len + env.curriculum_level == 4); assert(memcmp(env.state.true_walls, true_walls, sizeof(true_walls)) == 0); } @@ -596,10 +701,12 @@ static void test_curriculum_caps_at_board_max(void) { float terminals[1] = {0}; setup_env(&env, obs, actions, rewards, terminals); env.max_solution_len = 4; - env.curriculum_level = PATHFINDER_MAX_SOLUTION_LEN; + env.min_solution_len = 4; + env.curriculum_level = PATHFINDER_MAX_SOLUTION_LEN - env.max_solution_len; + env.curriculum_min_solution_len = env.min_solution_len + env.curriculum_level; - assert(pathfinder_curriculum_max_solution_len(&env) == PATHFINDER_MAX_SOLUTION_LEN); - assert(pathfinder_curriculum_min_solution_len(&env) == PATHFINDER_MAX_SOLUTION_LEN); + assert(env.max_solution_len + env.curriculum_level == PATHFINDER_MAX_SOLUTION_LEN); + assert(env.curriculum_min_solution_len == PATHFINDER_MAX_SOLUTION_LEN); } static void test_generation_can_reach_board_max_distance(void) { @@ -617,7 +724,7 @@ static void test_generation_can_reach_board_max_distance(void) { c_reset(&env); assert(env.state.shortest_path_len >= 1); assert(env.state.shortest_path_len <= PATHFINDER_MAX_SOLUTION_LEN); - assert(pathfinder_has_path_to_goal(&env.state)); + assert(shortest_path(&env.state) >= 0); } } @@ -677,11 +784,16 @@ static void test_reaching_goal_terminates(void) { env.goal_reward)) < 1e-6f); assert(env.log.success >= 1.0f); assert(env.log.n >= 1.0f); + assert(shortest_path(&env.state) >= 0); } int main(void) { test_constants(); test_wall_indexing_and_action_deltas(); + test_wall_between_bidirectional_vertical_edges(); + test_wall_between_bidirectional_horizontal_edges(); + test_wall_between_boundary_steps_to_outer_edges(); + test_wall_between_rejects_non_adjacent_positions(); test_reset_initializes_a1_and_unknown_walls(); test_action_mask_allows_unknown_edges_on_reset(); test_action_mask_blocks_known_wall_but_forced_hit_still_dies(); @@ -689,13 +801,17 @@ int main(void) { test_position_observation_updates_after_move(); test_generated_mazes_connect_a1_to_goal(); test_generated_shortest_path_matches_goal_distance(); + test_shortest_path_start_equals_goal(); + test_shortest_path_no_available_path_returns_minus_one(); + test_shortest_path_one_step_path(); + test_shortest_path_prefers_shorter_of_multiple_routes(); test_open_edge_reveals_and_moves(); test_blocked_edge_reveals_and_stays(); test_known_wall_repeat_terminates_with_penalty(); test_invalid_action_penalizes_and_terminates(); test_configured_reward_constants_are_used(); test_configured_impossible_penalty_is_used(); - test_boundary_action_reveals_wall(); + test_boundary_action_terminates_with_impossible_penalty(); test_revisiting_previously_left_square_has_penalty(); test_repeating_directed_move_dies_and_restarts_same_map_blind(); test_longer_backtrack_then_forward_move_is_allowed(); From 73ede901e0a4cc49a47d5989458e10cea2edb49b Mon Sep 17 00:00:00 2001 From: Kinvert Date: Fri, 5 Jun 2026 16:45:26 -0700 Subject: [PATCH 11/16] Simplify known-wall handling and enforce deterministic test/build order --- AGENTS.md | 46 +++++++- config/pathfinder.ini | 3 +- ocean/pathfinder/binding.c | 1 - ocean/pathfinder/pathfinder.h | 105 +++++++----------- ocean/pathfinder/tests/test_pathfinder_core.c | 3 +- 5 files changed, 87 insertions(+), 71 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 9542bf00fe..12e03a7aeb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -124,6 +124,13 @@ The Codex sandbox may not expose GPU devices. That can make PyTorch report valid GPU is visible. Native extension builds can still compile in the sandbox. GPU training/sweeps may need to run outside the sandbox. +If `python -m pufferlib.pufferl train pathfinder --train.gpus 1` fails inside +Codex with `Assertion 'device_count > 0 && "CUDA is not available"' failed`, +do not switch to CPU, rewrite CUDA setup, or debug the venv as the first +response. This is the managed sandbox hiding GPU devices. Re-run the same +training command with escalated/outside-sandbox execution so it can see +`/dev/nvidia*`. + ## Verified Build Baseline The fresh workspace has already been prepared with: @@ -213,14 +220,26 @@ Use test-driven development for Pathfinder behavior: 1. Add focused tests for wall indexing, maze solvability, reset observation, movement/wall reveal semantics, and terminal success. 2. Make the narrow tests pass. -3. Build native: +3. Run the Pathfinder core tests. The script compiles its own scoped C test + binary from the current `ocean/pathfinder/` source before executing it; it + does not use an existing `pufferlib/_C*.so` and is not testing stale native + extension code: + +```bash +source .venv/bin/activate +bash ocean/pathfinder/tests/run_all.sh +``` + +4. Build the native PufferLib extension. Do this before any Python-level + Pathfinder smoke test, training run, or eval, because those paths import the + built `pufferlib/_C*.so` artifact: ```bash source .venv/bin/activate ./build.sh pathfinder ``` -4. Run a short training smoke only after build and behavior tests pass: +5. Run a short training smoke only after build and behavior tests pass: ```bash source .venv/bin/activate @@ -230,6 +249,29 @@ python -m pufferlib.pufferl train pathfinder --train.total-timesteps 2097152 If tests require helper binaries, put them under `ocean/pathfinder/tests/` and keep them scoped to Pathfinder. +When the human asks to "run all tests" for Pathfinder, do not manually iterate +the repo-root `tests/test_*.py` files. Those files are stale upstream or +experimental tests for older PufferLib APIs and optional dependencies, not the +current Pathfinder acceptance suite. Known expected failures there include +missing `pufferlib.emulation`, missing `pufferl.make_parser`, missing +`pufferlib/src/models.cu`, optional packages such as `heavyball`, `pandas`, and +`pyximport`, and CUDA visibility failures inside the Codex sandbox. + +For Pathfinder, the supported baseline is: + +```bash +source .venv/bin/activate +bash ocean/pathfinder/tests/run_all.sh +./build.sh pathfinder +``` + +If running any Python-level Pathfinder check after source edits, rebuild with +`./build.sh pathfinder` first so Python does not import an old native extension. + +If the human explicitly asks for the stale repo-root Python tests anyway, state +that they are not the Pathfinder baseline before running them, and do not report +their expected failures as a Pathfinder regression. + ## Quick Ops Notes - The eval command supports `--load-model-path latest` to automatically pick the diff --git a/config/pathfinder.ini b/config/pathfinder.ini index a42d1a16fa..8b7056c0c7 100644 --- a/config/pathfinder.ini +++ b/config/pathfinder.ini @@ -12,8 +12,7 @@ loop_prob = 0.009618103745667449 extra_entry_prob = 0.0 step_penalty = -0.001 new_wall_penalty = 0.0 -known_wall_penalty = -0.01 -known_wall_death_penalty = -0.05 +known_wall_death_penalty = -1.0 repeat_move_death_penalty = -1.0 new_cell_reward = 0.01 revisit_penalty = -0.01 diff --git a/ocean/pathfinder/binding.c b/ocean/pathfinder/binding.c index c7f75a497d..32026680c9 100644 --- a/ocean/pathfinder/binding.c +++ b/ocean/pathfinder/binding.c @@ -16,7 +16,6 @@ void my_init(Env* env, Dict* kwargs) { env->extra_entry_prob = (float)dict_get(kwargs, "extra_entry_prob")->value; env->step_penalty = (float)dict_get(kwargs, "step_penalty")->value; env->new_wall_penalty = (float)dict_get(kwargs, "new_wall_penalty")->value; - env->known_wall_penalty = (float)dict_get(kwargs, "known_wall_penalty")->value; env->known_wall_death_penalty = (float)dict_get(kwargs, "known_wall_death_penalty")->value; env->repeat_move_death_penalty = (float)dict_get(kwargs, "repeat_move_death_penalty")->value; env->new_cell_reward = (float)dict_get(kwargs, "new_cell_reward")->value; diff --git a/ocean/pathfinder/pathfinder.h b/ocean/pathfinder/pathfinder.h index f1d34459a8..9a7a1cd867 100644 --- a/ocean/pathfinder/pathfinder.h +++ b/ocean/pathfinder/pathfinder.h @@ -105,7 +105,6 @@ typedef struct Pathfinder { float extra_entry_prob; float step_penalty; float new_wall_penalty; - float known_wall_penalty; float known_wall_death_penalty; float repeat_move_death_penalty; float new_cell_reward; @@ -233,6 +232,11 @@ static inline void action_delta(int action, int* d_row, int* d_col) { *d_row = 1; } else if (action == PATHFINDER_ACT_WEST) { *d_col = -1; + } else { + // Invalid actions are treated as an impossible move and handled by + // the bounds check in c_step. + *d_row = PATHFINDER_ROWS; + *d_col = PATHFINDER_ROWS; } } @@ -325,7 +329,6 @@ static void init_walls(State* s) { for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { s->known_walls[i] = PATHFINDER_UNKNOWN; } - s->true_walls[v_wall_idx(0, 0)] = 0; } static inline int pathfinder_rand(Pathfinder* env) { @@ -430,7 +433,6 @@ static void open_random_edges(Pathfinder* env, int target_len) { if (open_prob < 0.0f) open_prob = 0.0f; if (open_prob > 0.50f) open_prob = 0.50f; int open_threshold = (int)(open_prob * 256.0f); - int entry_threshold = (int)(env->extra_entry_prob * 256.0f); unsigned int samples = 0; int remaining = 0; @@ -462,18 +464,6 @@ static void open_random_edges(Pathfinder* env, int target_len) { } } } - for (int row = 1; row < PATHFINDER_ROWS; row++) { - if (rand_chance_u8(env, entry_threshold, &samples, &remaining)) { - int wall_idx = v_wall_idx(row, 0); - if (s->true_walls[wall_idx] == 0) { - continue; - } - s->true_walls[wall_idx] = 0; - if (shortest_path(s) < target_len) { - s->true_walls[wall_idx] = 1; - } - } - } } static void generate_maze(Pathfinder* env) { @@ -571,8 +561,7 @@ void init(Pathfinder* env) { env->curriculum_min_solution_len = env->min_solution_len + env->curriculum_level; env->step_penalty = env->step_penalty == 0.0f ? -0.001f : env->step_penalty; env->new_wall_penalty = env->new_wall_penalty == 0.0f ? 0.0f : env->new_wall_penalty; - env->known_wall_penalty = env->known_wall_penalty == 0.0f ? -0.01f : env->known_wall_penalty; - env->known_wall_death_penalty = env->known_wall_death_penalty == 0.0f ? -0.05f : env->known_wall_death_penalty; + env->known_wall_death_penalty = env->known_wall_death_penalty == 0.0f ? -1.0f : env->known_wall_death_penalty; env->repeat_move_death_penalty = env->repeat_move_death_penalty == 0.0f ? -1.0f : env->repeat_move_death_penalty; env->new_cell_reward = env->new_cell_reward == 0.0f ? 0.01f : env->new_cell_reward; env->revisit_penalty = env->revisit_penalty == 0.0f ? -0.01f : env->revisit_penalty; @@ -635,60 +624,48 @@ void c_step(Pathfinder* env) { float reward = env->step_penalty; int action = (int)env->actions[0]; - if (action < 0 || action >= PATHFINDER_NUM_ACTIONS) { + int d_row; + int d_col; + action_delta(action, &d_row, &d_col); + int next_row = s->agent_row + d_row; + int next_col = s->agent_col + d_col; + int wall_idx = wall_idx_between(s->agent_row, s->agent_col, next_row, next_col); + if (!in_bounds(next_row, next_col)) { reward += env->impossible_penalty; env->terminals[0] = 1.0f; } else { - int d_row; - int d_col; - action_delta(action, &d_row, &d_col); - int next_row = s->agent_row + d_row; - int next_col = s->agent_col + d_col; - int wall_idx = wall_idx_between(s->agent_row, s->agent_col, next_row, next_col); - if (!in_bounds(next_row, next_col)) { - if (wall_idx >= 0) { - reveal_wall(env, wall_idx); + bool was_known = s->known_walls[wall_idx] != PATHFINDER_UNKNOWN; + reveal_wall(env, wall_idx); + + if (s->true_walls[wall_idx]) { + s->wall_hits++; + reward += was_known ? env->known_wall_death_penalty : env->new_wall_penalty; + if (was_known) { + s->known_wall_death = 1; + env->terminals[0] = 1.0f; } - reward += env->impossible_penalty; - env->terminals[0] = 1.0f; - } else if (wall_idx < 0) { - reward += env->impossible_penalty; - env->terminals[0] = 1.0f; } else { - bool was_known = s->known_walls[wall_idx] != PATHFINDER_UNKNOWN; - reveal_wall(env, wall_idx); - - if (s->true_walls[wall_idx]) { - s->wall_hits++; - reward += was_known ? env->known_wall_penalty : env->new_wall_penalty; - if (was_known) { - reward += env->known_wall_death_penalty; - s->known_wall_death = 1; - env->terminals[0] = 1.0f; - } + if (repeats_two_cell_cycle(s, next_row, next_col)) { + reward += env->repeat_move_death_penalty; + s->repeat_move_death = 1; + env->terminals[0] = 1.0f; } else { - if (repeats_two_cell_cycle(s, next_row, next_col)) { - reward += env->repeat_move_death_penalty; - s->repeat_move_death = 1; - env->terminals[0] = 1.0f; + bool revisited = s->visited[next_row][next_col] != 0; + s->agent_row = next_row; + s->agent_col = next_col; + s->agent_path_len++; + record_successful_move(s); + if (revisited) { + s->revisit_count++; + reward += env->revisit_penalty; } else { - bool revisited = s->visited[next_row][next_col] != 0; - s->agent_row = next_row; - s->agent_col = next_col; - s->agent_path_len++; - record_successful_move(s); - if (revisited) { - s->revisit_count++; - reward += env->revisit_penalty; - } else { - mark_visited(s, next_row, next_col); - reward += env->new_cell_reward; - } - if (s->agent_row == s->goal_row && s->agent_col == s->goal_col) { - s->success = 1; - reward += env->goal_reward; - env->terminals[0] = 1.0f; - } + mark_visited(s, next_row, next_col); + reward += env->new_cell_reward; + } + if (s->agent_row == s->goal_row && s->agent_col == s->goal_col) { + s->success = 1; + reward += env->goal_reward; + env->terminals[0] = 1.0f; } } } diff --git a/ocean/pathfinder/tests/test_pathfinder_core.c b/ocean/pathfinder/tests/test_pathfinder_core.c index fb09d0956e..ea9dca0bd2 100644 --- a/ocean/pathfinder/tests/test_pathfinder_core.c +++ b/ocean/pathfinder/tests/test_pathfinder_core.c @@ -410,8 +410,7 @@ static void test_known_wall_repeat_terminates_with_penalty(void) { c_step(&env); assert(fabsf(rewards[0] - - (env.step_penalty + env.known_wall_penalty + - env.known_wall_death_penalty)) < 1e-6f); + (env.step_penalty + env.known_wall_death_penalty)) < 1e-6f); assert(terminals[0] == 1.0f); assert(env.log.n >= 1.0f); assert(env.log.success == 0.0f); From 5c836780fc55e44515e64ecf500921589f2e78ca Mon Sep 17 00:00:00 2001 From: Kinvert Date: Fri, 5 Jun 2026 17:06:45 -0700 Subject: [PATCH 12/16] Refactor pathfinder state refresh naming and remove init defaults --- ocean/pathfinder/binding.c | 1 - ocean/pathfinder/pathfinder.h | 31 ++----------------- ocean/pathfinder/tests/test_pathfinder_core.c | 26 ++++++++++------ 3 files changed, 19 insertions(+), 39 deletions(-) diff --git a/ocean/pathfinder/binding.c b/ocean/pathfinder/binding.c index 32026680c9..1db1a3149e 100644 --- a/ocean/pathfinder/binding.c +++ b/ocean/pathfinder/binding.c @@ -6,7 +6,6 @@ #define MY_ACTION_MASK PATHFINDER_NUM_ACTIONS #define Env Pathfinder -static inline void puffer_state_refresh(Pathfinder* env) { refresh_state(env); } #include "vecenv.h" void my_init(Env* env, Dict* kwargs) { diff --git a/ocean/pathfinder/pathfinder.h b/ocean/pathfinder/pathfinder.h index 9a7a1cd867..e014e4a4a8 100644 --- a/ocean/pathfinder/pathfinder.h +++ b/ocean/pathfinder/pathfinder.h @@ -233,8 +233,6 @@ static inline void action_delta(int action, int* d_row, int* d_col) { } else if (action == PATHFINDER_ACT_WEST) { *d_col = -1; } else { - // Invalid actions are treated as an impossible move and handled by - // the bounds check in c_step. *d_row = PATHFINDER_ROWS; *d_col = PATHFINDER_ROWS; } @@ -314,12 +312,10 @@ static void update_observations(Pathfinder* env) { } static inline int configured_base_max_solution_len(const Pathfinder* env) { - return env->max_solution_len > 0 ? env->max_solution_len : PATHFINDER_MAX_SOLUTION_LEN; + return env->max_solution_len; } static inline bool curriculum_can_advance(const Pathfinder* env) { - // The environment advances by one each successful solve until the target - // distance cap is fully shifted to the board max. return env->curriculum_level + configured_base_max_solution_len(env) < PATHFINDER_MAX_SOLUTION_LEN; } @@ -541,7 +537,7 @@ void add_log(Pathfinder* env) { env->log.n += 1.0f; } -void refresh_state(Pathfinder* env) { +void puffer_state_refresh(Pathfinder* env) { update_observations(env); } @@ -549,30 +545,7 @@ void init(Pathfinder* env) { if (env->num_agents == 0) { env->num_agents = 1; } - if (env->branch_prob == 0.0f) { - env->branch_prob = 0.10f; - } - if (env->loop_prob == 0.0f) { - env->loop_prob = 0.03f; - } - if (env->min_solution_len == 0) { - env->min_solution_len = 1; - } env->curriculum_min_solution_len = env->min_solution_len + env->curriculum_level; - env->step_penalty = env->step_penalty == 0.0f ? -0.001f : env->step_penalty; - env->new_wall_penalty = env->new_wall_penalty == 0.0f ? 0.0f : env->new_wall_penalty; - env->known_wall_death_penalty = env->known_wall_death_penalty == 0.0f ? -1.0f : env->known_wall_death_penalty; - env->repeat_move_death_penalty = env->repeat_move_death_penalty == 0.0f ? -1.0f : env->repeat_move_death_penalty; - env->new_cell_reward = env->new_cell_reward == 0.0f ? 0.01f : env->new_cell_reward; - env->revisit_penalty = env->revisit_penalty == 0.0f ? -0.01f : env->revisit_penalty; - env->impossible_penalty = env->impossible_penalty == 0.0f ? -1.0f : env->impossible_penalty; - env->goal_reward = env->goal_reward == 0.0f ? 1.0f : env->goal_reward; - if (env->max_steps == 0) { - env->max_steps = 128; - } - if (env->max_solution_len == 0) { - env->max_solution_len = PATHFINDER_MAX_SOLUTION_LEN; - } } void c_reset(Pathfinder* env) { diff --git a/ocean/pathfinder/tests/test_pathfinder_core.c b/ocean/pathfinder/tests/test_pathfinder_core.c index ea9dca0bd2..898a2767f2 100644 --- a/ocean/pathfinder/tests/test_pathfinder_core.c +++ b/ocean/pathfinder/tests/test_pathfinder_core.c @@ -19,6 +19,14 @@ static void setup_env(Pathfinder* env, float* obs, float* actions, env->min_solution_len = 1; env->max_solution_len = 0; env->max_steps = 128; + env->step_penalty = -0.001f; + env->new_wall_penalty = 0.0f; + env->known_wall_death_penalty = -1.0f; + env->repeat_move_death_penalty = -1.0f; + env->new_cell_reward = 0.01f; + env->revisit_penalty = -0.01f; + env->impossible_penalty = -1.0f; + env->goal_reward = 1.0f; env->rng = 7; init(env); } @@ -41,7 +49,7 @@ static void setup_manual_state(Pathfinder* env, int goal_row, int goal_col) { static void open_manual_edge(Pathfinder* env, int row, int col, int next_row, int next_col) { open_edge(&env->state, row, col, next_row, next_col); env->state.shortest_path_len = shortest_path(&env->state); - refresh_state(env); + puffer_state_refresh(env); } static void assert_wall_observations_unknown(Pathfinder* env, float* obs) { @@ -177,7 +185,7 @@ static void test_action_mask_blocks_known_wall_but_forced_hit_still_dies(void) { setup_env(&env, obs, actions, rewards, terminals); env.action_mask = action_mask; setup_manual_state(&env, 5, 5); - refresh_state(&env); + puffer_state_refresh(&env); for (int i = 0; i < PATHFINDER_NUM_ACTIONS; i++) { assert(action_mask[i] == 1); @@ -209,7 +217,7 @@ static void test_known_wall_death_restarts_same_map_with_unknown_wall_memory(voi setup_env(&env, obs, actions, rewards, terminals); env.action_mask = action_mask; setup_manual_state(&env, 5, 5); - refresh_state(&env); + puffer_state_refresh(&env); int east_wall = wall_idx_between(0, 0, 0, 1); memcpy(true_walls, env.state.true_walls, sizeof(true_walls)); @@ -380,7 +388,7 @@ static void test_blocked_edge_reveals_and_stays(void) { setup_env(&env, obs, actions, rewards, terminals); setup_manual_state(&env, 5, 5); int east_wall = wall_idx_between(0, 0, 0, 1); - refresh_state(&env); + puffer_state_refresh(&env); actions[0] = PATHFINDER_ACT_EAST; c_step(&env); @@ -401,7 +409,7 @@ static void test_known_wall_repeat_terminates_with_penalty(void) { float terminals[1] = {0}; setup_env(&env, obs, actions, rewards, terminals); setup_manual_state(&env, 5, 5); - refresh_state(&env); + puffer_state_refresh(&env); actions[0] = PATHFINDER_ACT_EAST; c_step(&env); @@ -425,7 +433,7 @@ static void test_invalid_action_penalizes_and_terminates(void) { float terminals[1] = {0}; setup_env(&env, obs, actions, rewards, terminals); setup_manual_state(&env, 5, 5); - refresh_state(&env); + puffer_state_refresh(&env); actions[0] = 99; c_step(&env); @@ -485,7 +493,7 @@ static void test_boundary_action_terminates_with_impossible_penalty(void) { float terminals[1] = {0}; setup_env(&env, obs, actions, rewards, terminals); setup_manual_state(&env, 5, 5); - refresh_state(&env); + puffer_state_refresh(&env); actions[0] = PATHFINDER_ACT_NORTH; c_step(&env); @@ -612,7 +620,7 @@ static void test_known_open_edge_to_new_square_has_no_extra_penalty(void) { c_step(&env); int second_wall = wall_idx_between(0, 1, 0, 2); env.state.known_walls[second_wall] = PATHFINDER_OPEN; - refresh_state(&env); + puffer_state_refresh(&env); actions[0] = PATHFINDER_ACT_EAST; c_step(&env); @@ -679,7 +687,7 @@ static void test_failure_retry_does_not_graduate_curriculum(void) { setup_env(&env, obs, actions, rewards, terminals); env.max_solution_len = 4; setup_manual_state(&env, 5, 5); - refresh_state(&env); + puffer_state_refresh(&env); memcpy(true_walls, env.state.true_walls, sizeof(true_walls)); actions[0] = PATHFINDER_ACT_EAST; From 634940ad5ca296f431e7e23af2f03a1b9c09c58b Mon Sep 17 00:00:00 2001 From: Kinvert Date: Fri, 5 Jun 2026 17:55:26 -0700 Subject: [PATCH 13/16] Preparing for PR --- AGENTS.md | 7 +- PATHFINDER_SPEC.md | 66 ++++----- config/pathfinder.ini | 5 +- ocean/pathfinder/binding.c | 8 +- ocean/pathfinder/pathfinder.c | 13 +- ocean/pathfinder/pathfinder.h | 78 ++++------ ocean/pathfinder/tests/bench_pathfinder.c | 30 ++-- .../pathfinder/tests/benchmark_pathfinder.py | 2 - ocean/pathfinder/tests/test_pathfinder_core.c | 140 ++++++++---------- 9 files changed, 153 insertions(+), 196 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 12e03a7aeb..5cdf85ff41 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -186,10 +186,11 @@ it is not the best state-memory reference on this branch. - Spawn: `A1`. - Hidden pawn: generated at the current curriculum distance from `A1`, and the maze must connect `A1` to it. -- Initial curriculum: `env.max_solution_len = 4` starts targets exactly 4 moves +- Initial curriculum: `env.start_solution_len = 4` starts targets exactly 4 moves from `A1`; each solve advances the next generated puzzle by 1 move, capped at - the board maximum. Set `max_solution_len = 0` only when you want to start at the board maximum. +- Set `env.curriculum_enabled = 0` to disable curriculum and start every + generated map at the board maximum difficulty immediately. - Actions: four discrete moves. - Observation: - 84 wall slots as floats: @@ -243,7 +244,7 @@ source .venv/bin/activate ```bash source .venv/bin/activate -python -m pufferlib.pufferl train pathfinder --train.total-timesteps 2097152 +python -m pufferlib.pufferl train pathfinder --train.gpus 1 ``` If tests require helper binaries, put them under `ocean/pathfinder/tests/` and diff --git a/PATHFINDER_SPEC.md b/PATHFINDER_SPEC.md index 9226897a48..c254cafe5e 100644 --- a/PATHFINDER_SPEC.md +++ b/PATHFINDER_SPEC.md @@ -74,10 +74,9 @@ Total wall observation slots: `6*7 + 7*6 = 84`. Boundary behavior: -- Left boundary edges are real Pathfinder entry/exit edges and may be open or - blocked by maze generation. -- Top, right, and bottom boundary edges are hard board boundaries. Their truth - value is `1` and the agent can discover them by attempting to move off-board. +- All off-board movement is impossible in v1. Asking to move off the board + applies `impossible_penalty`, terminates the attempt, and resets the agent to + `A1` on the same map. ## Maze Generation @@ -85,34 +84,32 @@ The generator must produce legal, solvable layouts without hand-authored maps. Puzzle generation: -1. Compute the curriculum span (`min_solution_len` to `max_solution_len`). -2. Sample a target path length with a bias toward the upper end of the span. -3. Initialize all wall slots to blocked. -4. Open the left entry edge for `A1`. -5. Carve one orthogonal randomized path from `A1` to a hidden pawn that is - exactly the sampled target length (including turns and winding moves). -6. Store `A1` as the agent spawn for the episode. -7. Add false branches and optional loops while preserving that the sampled - target length remains a valid shortest path lower bound. -8. Optionally open additional column-1 entries under the same shortest-path - guard. -9. Validate with BFS that `A1` reaches the pawn. +1. Choose the target solution length from the curriculum state. +2. Initialize all wall slots to blocked. +3. Carve one randomized orthogonal path from `A1` to a hidden pawn that is + exactly the target length. +4. Store `A1` as the agent spawn for the episode. +5. Open additional random internal edges while preserving the target length as + the shortest path. +6. Validate with BFS that `A1` reaches the pawn. Default generator style: -- Choose a target length from the curriculum span with an upper-biased random - sample. -- Carve a randomized, non-monotonic solution path of exact length to that target. +- Use deterministic curriculum difficulty: target length starts at + `start_solution_len` and advances by one after each solve. +- Carve a randomized, non-monotonic solution path of exact length to the current + target. - Open additional random internal edges as branch/loop density knobs rather than trying to exactly copy human barricade layouts. Curriculum: -- `max_solution_len` is the starting curriculum distance. The default training +- `start_solution_len` is the starting curriculum distance. The default training value is `4`. -- `max_solution_len = 0` starts at the board maximum distance. +- `curriculum_enabled = 0` disables the schedule and starts every generated map + at `PATHFINDER_MAX_SOLUTION_LEN`. - Each successful solve increments the next generated puzzle distance by one, - capped at `PATHFINDER_MAX_SOLUTION_LEN`. + capped at `PATHFINDER_MAX_SOLUTION_LEN`, when `curriculum_enabled = 1`. - Failed attempts do not advance curriculum. - Failed attempts restart the agent at `A1` on the same true map, but clear discovered wall/open observations back to `-1.0`, so map generation happens @@ -122,10 +119,9 @@ Config knobs: - `branch_prob`: probability of adding false branches from the main route. - `loop_prob`: probability of opening extra internal edges after carving. -- `extra_entry_prob`: probability each non-`A1` column-1 entrance is open. -- `min_solution_len`: reserved for future variants; the current curriculum uses - an exact generated distance. -- `max_solution_len`: starting exact solution distance. +- `start_solution_len`: starting exact solution distance. +- `curriculum_enabled`: `1` for deterministic solve-based curriculum, `0` for + max-difficulty maps from the first reset. - `max_steps`: timeout. - `seed`: inherited from vector env config. @@ -184,14 +180,13 @@ Default reward model: - `+0.01` for first entering a cell in the current attempt. - `0.0` extra penalty for hitting a newly discovered wall; the agent paid the step cost but gained information. -- `-0.01` for hitting a wall that was already known. -- `-0.05` extra penalty and terminal attempt reset for hitting a known wall. +- `-1.0` extra penalty and terminal attempt reset for hitting a wall that was + already known. - `-0.01` for revisiting a square that was previously left in the current attempt. - `-1.0` extra penalty and terminal attempt reset for immediate two-cell oscillation such as `A1 -> B1 -> A1 -> B1`. -- `-0.01` for impossible movement, such as attempting to exit through the - left edge in v1. +- `-1.0` for impossible movement, such as attempting to move off the board. Termination: @@ -227,7 +222,8 @@ Logged metrics: - `shortest_path_len` - `agent_path_len` - `curriculum_level` -- `curriculum_max_solution_len` +- `curriculum_target_len` +- `curriculum_next_target_len` - `n` ## PufferLib Integration @@ -259,7 +255,6 @@ The env should follow current PufferLib 5 Ocean patterns: - `NUM_ATNS 1` - `ACT_SIZES {4}` - `OBS_TENSOR_T FloatTensor` - - `MY_ACTION_MASK PATHFINDER_NUM_ACTIONS` - `Env Pathfinder` - `puffer_state_refresh(Pathfinder* env)` to rebuild observations from restored state. @@ -306,7 +301,7 @@ Verification commands after implementation: ```bash source .venv/bin/activate ./build.sh pathfinder -python -m pufferlib.pufferl train pathfinder --train.total-timesteps 2097152 +python -m pufferlib.pufferl train pathfinder --train.gpus 1 ``` Native GPU builds are the expected path for this workspace. @@ -317,10 +312,9 @@ Start conservative: - `vec.total_agents = 8192` or `16384` - `vec.num_buffers = 2` -- `env.max_solution_len = 4` for early closer-target curriculum +- `env.start_solution_len = 4` for early closer-target curriculum +- `env.curriculum_enabled = 1` - `train.gpus = 1` -- `train.total_timesteps = 100M` for real runs, at least `2,097,152` for smoke - tests with the initial `8192 x 128` rollout geometry. - `train.horizon = 128` - `train.minibatch_size = 32768` - `train.learning_rate = 0.001` diff --git a/config/pathfinder.ini b/config/pathfinder.ini index 8b7056c0c7..16473c8b05 100644 --- a/config/pathfinder.ini +++ b/config/pathfinder.ini @@ -9,7 +9,6 @@ num_threads = 0 [env] branch_prob = 0.008231888077505314 loop_prob = 0.009618103745667449 -extra_entry_prob = 0.0 step_penalty = -0.001 new_wall_penalty = 0.0 known_wall_death_penalty = -1.0 @@ -18,8 +17,8 @@ new_cell_reward = 0.01 revisit_penalty = -0.01 impossible_penalty = -1.0 goal_reward = 1.0 -min_solution_len = 1 -max_solution_len = 4 +start_solution_len = 4 +curriculum_enabled = 1 max_steps = 128 [policy] diff --git a/ocean/pathfinder/binding.c b/ocean/pathfinder/binding.c index 1db1a3149e..fa01df3796 100644 --- a/ocean/pathfinder/binding.c +++ b/ocean/pathfinder/binding.c @@ -3,7 +3,6 @@ #define NUM_ATNS 1 #define ACT_SIZES {4} #define OBS_TENSOR_T FloatTensor -#define MY_ACTION_MASK PATHFINDER_NUM_ACTIONS #define Env Pathfinder #include "vecenv.h" @@ -12,7 +11,6 @@ void my_init(Env* env, Dict* kwargs) { env->num_agents = 1; env->branch_prob = (float)dict_get(kwargs, "branch_prob")->value; env->loop_prob = (float)dict_get(kwargs, "loop_prob")->value; - env->extra_entry_prob = (float)dict_get(kwargs, "extra_entry_prob")->value; env->step_penalty = (float)dict_get(kwargs, "step_penalty")->value; env->new_wall_penalty = (float)dict_get(kwargs, "new_wall_penalty")->value; env->known_wall_death_penalty = (float)dict_get(kwargs, "known_wall_death_penalty")->value; @@ -21,8 +19,8 @@ void my_init(Env* env, Dict* kwargs) { env->revisit_penalty = (float)dict_get(kwargs, "revisit_penalty")->value; env->impossible_penalty = (float)dict_get(kwargs, "impossible_penalty")->value; env->goal_reward = (float)dict_get(kwargs, "goal_reward")->value; - env->min_solution_len = (int)dict_get(kwargs, "min_solution_len")->value; - env->max_solution_len = (int)dict_get(kwargs, "max_solution_len")->value; + env->start_solution_len = (int)dict_get(kwargs, "start_solution_len")->value; + env->curriculum_enabled = (int)dict_get(kwargs, "curriculum_enabled")->value; env->max_steps = (int)dict_get(kwargs, "max_steps")->value; init(env); } @@ -41,8 +39,6 @@ void my_log(Log* log, Dict* out) { dict_set(out, "shortest_path_len", log->shortest_path_len); dict_set(out, "agent_path_len", log->agent_path_len); dict_set(out, "curriculum_level", log->curriculum_level); - dict_set(out, "curriculum_min_solution_len", log->curriculum_min_solution_len); - dict_set(out, "curriculum_max_solution_len", log->curriculum_max_solution_len); dict_set(out, "curriculum_target_len", log->curriculum_target_len); dict_set(out, "curriculum_next_target_len", log->curriculum_next_target_len); } diff --git a/ocean/pathfinder/pathfinder.c b/ocean/pathfinder/pathfinder.c index dfe143a062..84f9ca04a8 100644 --- a/ocean/pathfinder/pathfinder.c +++ b/ocean/pathfinder/pathfinder.c @@ -26,10 +26,17 @@ int main(void) { env.rng = (unsigned int)time(NULL); env.branch_prob = 0.35f; env.loop_prob = 0.10f; - env.extra_entry_prob = 0.0f; - env.min_solution_len = 1; - env.max_solution_len = 4; + env.start_solution_len = 4; + env.curriculum_enabled = 1; env.max_steps = 128; + env.step_penalty = -0.001f; + env.new_wall_penalty = 0.0f; + env.known_wall_death_penalty = -1.0f; + env.repeat_move_death_penalty = -1.0f; + env.new_cell_reward = 0.01f; + env.revisit_penalty = -0.01f; + env.impossible_penalty = -1.0f; + env.goal_reward = 1.0f; init(&env); c_reset(&env); diff --git a/ocean/pathfinder/pathfinder.h b/ocean/pathfinder/pathfinder.h index e014e4a4a8..37a3c6ac11 100644 --- a/ocean/pathfinder/pathfinder.h +++ b/ocean/pathfinder/pathfinder.h @@ -56,8 +56,6 @@ typedef struct Log { float shortest_path_len; float agent_path_len; float curriculum_level; - float curriculum_min_solution_len; - float curriculum_max_solution_len; float curriculum_target_len; float curriculum_next_target_len; float n; @@ -97,12 +95,10 @@ typedef struct Pathfinder { float* actions; float* rewards; float* terminals; - unsigned char* action_mask; int num_agents; unsigned int rng; float branch_prob; float loop_prob; - float extra_entry_prob; float step_penalty; float new_wall_penalty; float known_wall_death_penalty; @@ -111,9 +107,8 @@ typedef struct Pathfinder { float revisit_penalty; float impossible_penalty; float goal_reward; - int min_solution_len; - int max_solution_len; - int curriculum_min_solution_len; + int start_solution_len; + int curriculum_enabled; int max_steps; int curriculum_level; int curriculum_episodes; @@ -137,10 +132,6 @@ static inline unsigned int rand_u32(Pathfinder* env) { return env->rng; } -static inline float rand_float(Pathfinder* env) { - return (float)(rand_u32(env) >> 8) / 16777216.0f; -} - static inline bool rand_chance_u8(Pathfinder* env, int threshold, unsigned int* samples, int* remaining) { if (threshold <= 0) { @@ -248,8 +239,8 @@ static int shortest_path(const State* s) { dist[0][0] = 0; queue[tail++] = 0; - static const int d_rows[4] = {-1, 0, 1, 0}; - static const int d_cols[4] = {0, 1, 0, -1}; + static const int d_rows[PATHFINDER_NUM_ACTIONS] = {-1, 0, 1, 0}; + static const int d_cols[PATHFINDER_NUM_ACTIONS] = {0, 1, 0, -1}; while (head < tail) { int cell = queue[head++]; int row = cell / PATHFINDER_COLS; @@ -279,26 +270,7 @@ static int shortest_path(const State* s) { return -1; } -static void update_action_mask(Pathfinder* env) { - if (env->action_mask == NULL) { - return; - } - - for (int action = 0; action < PATHFINDER_NUM_ACTIONS; action++) { - int d_row; - int d_col; - action_delta(action, &d_row, &d_col); - int next_row = env->state.agent_row + d_row; - int next_col = env->state.agent_col + d_col; - int wall_idx = wall_idx_between( - env->state.agent_row, env->state.agent_col, next_row, next_col); - env->action_mask[action] = - (wall_idx >= 0 && env->state.known_walls[wall_idx] == PATHFINDER_WALL) ? 0 : 1; - } -} - static void update_observations(Pathfinder* env) { - update_action_mask(env); if (env->observations == NULL) { return; } @@ -311,13 +283,26 @@ static void update_observations(Pathfinder* env) { (float)env->state.agent_row / (float)(PATHFINDER_ROWS - 1); } -static inline int configured_base_max_solution_len(const Pathfinder* env) { - return env->max_solution_len; +static inline int clamp_solution_len(int solution_len) { + if (solution_len < 1) { + return 1; + } + if (solution_len > PATHFINDER_MAX_SOLUTION_LEN) { + return PATHFINDER_MAX_SOLUTION_LEN; + } + return solution_len; +} + +static inline int current_target_solution_len(const Pathfinder* env) { + if (!env->curriculum_enabled) { + return PATHFINDER_MAX_SOLUTION_LEN; + } + return clamp_solution_len(env->start_solution_len + env->curriculum_level); } static inline bool curriculum_can_advance(const Pathfinder* env) { - return env->curriculum_level + configured_base_max_solution_len(env) < - PATHFINDER_MAX_SOLUTION_LEN; + return env->curriculum_enabled && + current_target_solution_len(env) < PATHFINDER_MAX_SOLUTION_LEN; } static void init_walls(State* s) { @@ -464,14 +449,11 @@ static void open_random_edges(Pathfinder* env, int target_len) { static void generate_maze(Pathfinder* env) { State* s = &env->state; - env->curriculum_min_solution_len = env->min_solution_len + env->curriculum_level; - int min_solution_len = env->curriculum_min_solution_len; - int target_len = min_solution_len; + int target_len = current_target_solution_len(env); init_walls(s); bool carved = carve_solution(env, target_len); if (!carved) { - target_len = min_solution_len; carved = carve_solution(env, target_len); } @@ -494,18 +476,17 @@ static void update_curriculum(Pathfinder* env, int success) { } env->curriculum_level++; - env->curriculum_min_solution_len++; } void add_log(Pathfinder* env) { State* s = &env->state; float success = (float)s->success; int current_curriculum_level = env->curriculum_level; - int current_curriculum_min_solution_len = env->curriculum_min_solution_len; - int current_curriculum_max_solution_len = env->max_solution_len + env->curriculum_level; - int next_curriculum_max_solution_len = current_curriculum_max_solution_len; + int current_target_len = current_target_solution_len(env); + int next_target_len = current_target_len; if (s->success && curriculum_can_advance(env)) { - next_curriculum_max_solution_len++; + next_target_len = clamp_solution_len( + env->start_solution_len + env->curriculum_level + 1); } float efficiency = 0.0f; if (s->success && s->agent_path_len > 0 && s->shortest_path_len > 0) { @@ -530,10 +511,8 @@ void add_log(Pathfinder* env) { env->log.shortest_path_len += (float)s->shortest_path_len; env->log.agent_path_len += (float)s->agent_path_len; env->log.curriculum_level += (float)current_curriculum_level; - env->log.curriculum_min_solution_len += (float)current_curriculum_min_solution_len; - env->log.curriculum_max_solution_len += (float)current_curriculum_max_solution_len; - env->log.curriculum_target_len += (float)current_curriculum_max_solution_len; - env->log.curriculum_next_target_len += (float)next_curriculum_max_solution_len; + env->log.curriculum_target_len += (float)current_target_len; + env->log.curriculum_next_target_len += (float)next_target_len; env->log.n += 1.0f; } @@ -545,7 +524,6 @@ void init(Pathfinder* env) { if (env->num_agents == 0) { env->num_agents = 1; } - env->curriculum_min_solution_len = env->min_solution_len + env->curriculum_level; } void c_reset(Pathfinder* env) { diff --git a/ocean/pathfinder/tests/bench_pathfinder.c b/ocean/pathfinder/tests/bench_pathfinder.c index e85512cd4b..50181d7019 100644 --- a/ocean/pathfinder/tests/bench_pathfinder.c +++ b/ocean/pathfinder/tests/bench_pathfinder.c @@ -21,10 +21,17 @@ static void setup_env(Pathfinder* env, float* obs, float* actions, env->num_agents = 1; env->branch_prob = 0.35f; env->loop_prob = 0.10f; - env->extra_entry_prob = 0.0f; - env->min_solution_len = 1; - env->max_solution_len = 4; + env->start_solution_len = 4; + env->curriculum_enabled = 1; env->max_steps = 128; + env->step_penalty = -0.001f; + env->new_wall_penalty = 0.0f; + env->known_wall_death_penalty = -1.0f; + env->repeat_move_death_penalty = -1.0f; + env->new_cell_reward = 0.01f; + env->revisit_penalty = -0.01f; + env->impossible_penalty = -1.0f; + env->goal_reward = 1.0f; env->rng = 12345; init(env); } @@ -69,11 +76,11 @@ static void bench_steps(Pathfinder* env, long steps) { } double elapsed = now_seconds() - t0; double step_sps = (double)steps / elapsed; - printf("step_bench steps=%ld seconds=%.6f step_sps=%.2f episodes=%.0f success=%.6f repeat_move_deaths=%.0f reward_sum=%.3f curriculum_level=%d curriculum_max_solution_len=%d\n", + printf("step_bench steps=%ld seconds=%.6f step_sps=%.2f episodes=%.0f success=%.6f repeat_move_deaths=%.0f reward_sum=%.3f curriculum_level=%d curriculum_target_len=%d\n", steps, elapsed, step_sps, env->log.n, env->log.n > 0.0f ? env->log.success / env->log.n : 0.0f, env->log.repeat_move_deaths, reward_sum, env->curriculum_level, - env->max_solution_len + env->curriculum_level); + current_target_solution_len(env)); } static void bench_choose_goal_at_distance(Pathfinder* env, int target_len) { @@ -112,18 +119,11 @@ static void bench_reset_components(Pathfinder* env, long iters) { } double obs_sec = now_seconds() - t0; - t0 = now_seconds(); - for (long i = 0; i < iters; i++) { - update_action_mask(env); - } - double mask_sec = now_seconds() - t0; - - printf("reset_components iters=%ld memset_ns=%.2f gen_plus_memset_ns=%.2f obs_ns=%.2f mask_ns=%.2f\n", + printf("reset_components iters=%ld memset_ns=%.2f gen_plus_memset_ns=%.2f obs_ns=%.2f\n", iters, 1e9 * memset_sec / (double)iters, 1e9 * gen_sec / (double)iters, - 1e9 * obs_sec / (double)iters, - 1e9 * mask_sec / (double)iters); + 1e9 * obs_sec / (double)iters); t0 = now_seconds(); for (long i = 0; i < iters; i++) { @@ -239,9 +239,7 @@ int main(int argc, char** argv) { float actions[1] = {0}; float rewards[1] = {0}; float terminals[1] = {0}; - unsigned char action_mask[PATHFINDER_NUM_ACTIONS] = {0}; setup_env(&env, obs, actions, rewards, terminals); - env.action_mask = action_mask; bench_resets(&env, resets, 0); bench_resets(&env, resets, PATHFINDER_MAX_SOLUTION_LEN); diff --git a/ocean/pathfinder/tests/benchmark_pathfinder.py b/ocean/pathfinder/tests/benchmark_pathfinder.py index 4fe4eae223..84c7367514 100755 --- a/ocean/pathfinder/tests/benchmark_pathfinder.py +++ b/ocean/pathfinder/tests/benchmark_pathfinder.py @@ -75,8 +75,6 @@ def run_once(label, args, timesteps, eval_epochs_override): "shortest_path_len": stats.get("env/shortest_path_len", 0.0), "agent_path_len": stats.get("env/agent_path_len", 0.0), "curriculum_level": stats.get("env/curriculum_level", 0.0), - "curriculum_min_solution_len": stats.get("env/curriculum_min_solution_len", 0.0), - "curriculum_max_solution_len": stats.get("env/curriculum_max_solution_len", 0.0), "curriculum_target_len": stats.get("env/curriculum_target_len", 0.0), "curriculum_next_target_len": stats.get("env/curriculum_next_target_len", 0.0), "perf_rollout_sec": flat.get("perf/rollout", 0.0), diff --git a/ocean/pathfinder/tests/test_pathfinder_core.c b/ocean/pathfinder/tests/test_pathfinder_core.c index 898a2767f2..6004b47249 100644 --- a/ocean/pathfinder/tests/test_pathfinder_core.c +++ b/ocean/pathfinder/tests/test_pathfinder_core.c @@ -15,9 +15,8 @@ static void setup_env(Pathfinder* env, float* obs, float* actions, env->num_agents = 1; env->branch_prob = 0.35f; env->loop_prob = 0.10f; - env->extra_entry_prob = 0.0f; - env->min_solution_len = 1; - env->max_solution_len = 0; + env->start_solution_len = 1; + env->curriculum_enabled = 1; env->max_steps = 128; env->step_penalty = -0.001f; env->new_wall_penalty = 0.0f; @@ -158,64 +157,14 @@ static void test_reset_initializes_a1_and_unknown_walls(void) { } } -static void test_action_mask_allows_unknown_edges_on_reset(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - unsigned char action_mask[PATHFINDER_NUM_ACTIONS] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - env.action_mask = action_mask; - - c_reset(&env); - - for (int i = 0; i < PATHFINDER_NUM_ACTIONS; i++) { - assert(action_mask[i] == 1); - } -} - -static void test_action_mask_blocks_known_wall_but_forced_hit_still_dies(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - unsigned char action_mask[PATHFINDER_NUM_ACTIONS] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - env.action_mask = action_mask; - setup_manual_state(&env, 5, 5); - puffer_state_refresh(&env); - - for (int i = 0; i < PATHFINDER_NUM_ACTIONS; i++) { - assert(action_mask[i] == 1); - } - - actions[0] = PATHFINDER_ACT_EAST; - c_step(&env); - - assert(terminals[0] == 0.0f); - assert(action_mask[PATHFINDER_ACT_EAST] == 0); - assert(action_mask[PATHFINDER_ACT_NORTH] == 1); - assert(action_mask[PATHFINDER_ACT_SOUTH] == 1); - assert(action_mask[PATHFINDER_ACT_WEST] == 1); - - c_step(&env); - - assert(terminals[0] == 1.0f); - assert(env.log.known_wall_deaths == 1.0f); -} - static void test_known_wall_death_restarts_same_map_with_unknown_wall_memory(void) { Pathfinder env; float obs[PATHFINDER_OBS_SIZE]; float actions[1] = {0}; float rewards[1] = {0}; float terminals[1] = {0}; - unsigned char action_mask[PATHFINDER_NUM_ACTIONS] = {0}; unsigned char true_walls[PATHFINDER_NUM_WALLS]; setup_env(&env, obs, actions, rewards, terminals); - env.action_mask = action_mask; setup_manual_state(&env, 5, 5); puffer_state_refresh(&env); @@ -227,7 +176,6 @@ static void test_known_wall_death_restarts_same_map_with_unknown_wall_memory(voi assert(terminals[0] == 0.0f); assert(fabsf(obs[east_wall] - PATHFINDER_WALL) < 1e-6f); - assert(action_mask[PATHFINDER_ACT_EAST] == 0); c_step(&env); @@ -244,7 +192,6 @@ static void test_known_wall_death_restarts_same_map_with_unknown_wall_memory(voi assert(env.state.visited[0][0] == 1); assert(memcmp(env.state.true_walls, true_walls, sizeof(true_walls)) == 0); assert_wall_observations_unknown(&env, obs); - assert(action_mask[PATHFINDER_ACT_EAST] == 1); } static void test_position_observation_updates_after_move(void) { @@ -290,7 +237,7 @@ static void test_generated_shortest_path_matches_goal_distance(void) { float rewards[1] = {0}; float terminals[1] = {0}; setup_env(&env, obs, actions, rewards, terminals); - env.max_solution_len = PATHFINDER_MAX_SOLUTION_LEN; + env.start_solution_len = PATHFINDER_MAX_SOLUTION_LEN; for (int i = 0; i < 200; i++) { c_reset(&env); @@ -632,23 +579,67 @@ static void test_known_open_edge_to_new_square_has_no_extra_penalty(void) { assert(terminals[0] == 0.0f); } -static void test_max_solution_len_limits_curriculum_distance(void) { +static void test_start_solution_len_sets_curriculum_distance(void) { Pathfinder env; float obs[PATHFINDER_OBS_SIZE]; float actions[1] = {0}; float rewards[1] = {0}; float terminals[1] = {0}; setup_env(&env, obs, actions, rewards, terminals); - env.min_solution_len = 2; - env.max_solution_len = 2; + env.start_solution_len = 2; for (int i = 0; i < 100; i++) { c_reset(&env); assert(shortest_path(&env.state) >= 0); - assert(env.state.shortest_path_len == env.min_solution_len); + assert(env.state.shortest_path_len == env.start_solution_len); + } +} + +static void test_curriculum_uses_configured_start_solution_len(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + env.branch_prob = 0.0f; + env.loop_prob = 0.0f; + env.start_solution_len = 4; + + for (int i = 0; i < 100; i++) { + c_reset(&env); + assert(env.state.shortest_path_len == env.start_solution_len); } } +static void test_curriculum_disabled_starts_at_board_max_and_does_not_advance(void) { + Pathfinder env; + float obs[PATHFINDER_OBS_SIZE]; + float actions[1] = {0}; + float rewards[1] = {0}; + float terminals[1] = {0}; + setup_env(&env, obs, actions, rewards, terminals); + env.branch_prob = 0.0f; + env.loop_prob = 0.0f; + env.start_solution_len = 4; + env.curriculum_enabled = 0; + + c_reset(&env); + assert(env.state.shortest_path_len == PATHFINDER_MAX_SOLUTION_LEN); + + setup_manual_state(&env, 0, 1); + open_manual_edge(&env, 0, 0, 0, 1); + actions[0] = PATHFINDER_ACT_EAST; + c_step(&env); + + assert(terminals[0] == 1.0f); + assert(fabsf(rewards[0] - + (env.step_penalty + env.new_cell_reward + env.goal_reward)) < 1e-6f); + assert(env.log.success == 1.0f); + assert(env.curriculum_level == 0); + assert(env.state.shortest_path_len == PATHFINDER_MAX_SOLUTION_LEN); +} + static void test_success_generates_next_map_one_step_farther(void) { Pathfinder env; float obs[PATHFINDER_OBS_SIZE]; @@ -658,7 +649,7 @@ static void test_success_generates_next_map_one_step_farther(void) { setup_env(&env, obs, actions, rewards, terminals); env.branch_prob = 0.0f; env.loop_prob = 0.0f; - env.max_solution_len = 1; + env.start_solution_len = 1; setup_manual_state(&env, 0, 1); open_manual_edge(&env, 0, 0, 0, 1); @@ -667,7 +658,7 @@ static void test_success_generates_next_map_one_step_farther(void) { assert(terminals[0] == 1.0f); assert(env.log.success == 1.0f); - assert(env.max_solution_len + env.curriculum_level == 2); + assert(env.start_solution_len + env.curriculum_level == 2); assert(env.state.agent_row == 0); assert(env.state.agent_col == 0); assert(env.state.shortest_path_len == 2); @@ -685,7 +676,7 @@ static void test_failure_retry_does_not_graduate_curriculum(void) { float terminals[1] = {0}; unsigned char true_walls[PATHFINDER_NUM_WALLS]; setup_env(&env, obs, actions, rewards, terminals); - env.max_solution_len = 4; + env.start_solution_len = 4; setup_manual_state(&env, 5, 5); puffer_state_refresh(&env); memcpy(true_walls, env.state.true_walls, sizeof(true_walls)); @@ -696,7 +687,7 @@ static void test_failure_retry_does_not_graduate_curriculum(void) { assert(terminals[0] == 1.0f); assert(env.curriculum_level == 0); - assert(env.max_solution_len + env.curriculum_level == 4); + assert(env.start_solution_len + env.curriculum_level == 4); assert(memcmp(env.state.true_walls, true_walls, sizeof(true_walls)) == 0); } @@ -707,13 +698,11 @@ static void test_curriculum_caps_at_board_max(void) { float rewards[1] = {0}; float terminals[1] = {0}; setup_env(&env, obs, actions, rewards, terminals); - env.max_solution_len = 4; - env.min_solution_len = 4; - env.curriculum_level = PATHFINDER_MAX_SOLUTION_LEN - env.max_solution_len; - env.curriculum_min_solution_len = env.min_solution_len + env.curriculum_level; + env.start_solution_len = 4; + env.curriculum_level = PATHFINDER_MAX_SOLUTION_LEN - env.start_solution_len; - assert(env.max_solution_len + env.curriculum_level == PATHFINDER_MAX_SOLUTION_LEN); - assert(env.curriculum_min_solution_len == PATHFINDER_MAX_SOLUTION_LEN); + assert(env.start_solution_len + env.curriculum_level == PATHFINDER_MAX_SOLUTION_LEN); + assert(current_target_solution_len(&env) == PATHFINDER_MAX_SOLUTION_LEN); } static void test_generation_can_reach_board_max_distance(void) { @@ -725,7 +714,7 @@ static void test_generation_can_reach_board_max_distance(void) { setup_env(&env, obs, actions, rewards, terminals); env.branch_prob = 0.0f; env.loop_prob = 0.0f; - env.max_solution_len = PATHFINDER_MAX_SOLUTION_LEN; + env.start_solution_len = PATHFINDER_MAX_SOLUTION_LEN; for (int i = 0; i < 200; i++) { c_reset(&env); @@ -741,10 +730,8 @@ static void test_timeout_restarts_same_map_with_unknown_wall_memory(void) { float actions[1] = {0}; float rewards[1] = {0}; float terminals[1] = {0}; - unsigned char action_mask[PATHFINDER_NUM_ACTIONS] = {0}; unsigned char true_walls[PATHFINDER_NUM_WALLS]; setup_env(&env, obs, actions, rewards, terminals); - env.action_mask = action_mask; env.max_steps = 1; setup_manual_state(&env, 5, 5); open_manual_edge(&env, 0, 0, 0, 1); @@ -769,7 +756,6 @@ static void test_timeout_restarts_same_map_with_unknown_wall_memory(void) { assert(env.state.visited[0][1] == 0); assert(memcmp(env.state.true_walls, true_walls, sizeof(true_walls)) == 0); assert_wall_observations_unknown(&env, obs); - assert(action_mask[PATHFINDER_ACT_EAST] == 1); } static void test_reaching_goal_terminates(void) { @@ -802,8 +788,6 @@ int main(void) { test_wall_between_boundary_steps_to_outer_edges(); test_wall_between_rejects_non_adjacent_positions(); test_reset_initializes_a1_and_unknown_walls(); - test_action_mask_allows_unknown_edges_on_reset(); - test_action_mask_blocks_known_wall_but_forced_hit_still_dies(); test_known_wall_death_restarts_same_map_with_unknown_wall_memory(); test_position_observation_updates_after_move(); test_generated_mazes_connect_a1_to_goal(); @@ -823,7 +807,9 @@ int main(void) { test_repeating_directed_move_dies_and_restarts_same_map_blind(); test_longer_backtrack_then_forward_move_is_allowed(); test_known_open_edge_to_new_square_has_no_extra_penalty(); - test_max_solution_len_limits_curriculum_distance(); + test_start_solution_len_sets_curriculum_distance(); + test_curriculum_uses_configured_start_solution_len(); + test_curriculum_disabled_starts_at_board_max_and_does_not_advance(); test_success_generates_next_map_one_step_farther(); test_failure_retry_does_not_graduate_curriculum(); test_curriculum_caps_at_board_max(); From 44757dba97649cddabd97674505204b04aed8870 Mon Sep 17 00:00:00 2001 From: Kinvert Date: Fri, 5 Jun 2026 20:14:20 -0700 Subject: [PATCH 14/16] Update pathfinder defaults and player-mode rendering behavior --- TODO.md | 53 +++++++++++++++++++++++++++++++++++ config/pathfinder.ini | 36 ++++++++++++------------ ocean/pathfinder/pathfinder.c | 1 + ocean/pathfinder/pathfinder.h | 22 ++++++++++----- 4 files changed, 87 insertions(+), 25 deletions(-) create mode 100644 TODO.md diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000000..3b80a9c6b5 --- /dev/null +++ b/TODO.md @@ -0,0 +1,53 @@ +# Pathfinder Bloat Reduction TODO + +Track simplifications to reduce unnecessary complexity and keep the env lean. + +## High-priority refactors + +- [ ] Fix random action generation in player mode + - `pathfinder_rand` returns an `int` from `rand_u32`, which can be negative when cast. + - `pathfinder_rand(&env) % PATHFINDER_NUM_ACTIONS` can yield negative values and create invalid actions. + - Replace with unsigned-safe modulo path (`(int)(rand_u32(&env) & 0xFFu) % PATHFINDER_NUM_ACTIONS`) or direct `pathfinder_rand(&env)` modulo on `unsigned int` before cast. + +- [ ] Remove curriculum helper asymmetry in `pathfinder.h` + - [curriculum_min_solution_len](ocean/pathfinder/pathfinder.h:139) still does fallback/clamp sequencing while max length logic is already direct. + - [next_curriculum_max_solution_len](ocean/pathfinder/pathfinder.h:582) duplicates curriculum clamping logic. + - Simplify to direct env-invariant behavior where possible. + +- [ ] Remove clamp/safety fallback patterns that are no longer needed + - [pathfinder_clamp_int](ocean/pathfinder/pathfinder.h:15) and related call sites were added for defensive fault handling. + - Decide if we trust validated INI/env invariants and drop this extra layer where possible. + +- [ ] Simplify config initialization default fallback chains + - [init defaults](ocean/pathfinder/pathfinder.h:627) has many `value == 0 ? default : value` branches for reward and tuning parameters. + - Make defaults explicit and avoid repeated defensive defaulting. + +- [ ] Simplify maze generation fallback flow + - [generate_maze](ocean/pathfinder/pathfinder.h:544) includes multi-step rollback behavior (try A, then B, then hard reset). + - Collapse to a single, deterministic path length selection approach. + +- [ ] Reduce duplication in random edge opening + - [open_random_edges](ocean/pathfinder/pathfinder.h:492) has duplicated horizontal/vertical loops with repeated shortest-path checks. + - Consolidate edge-try logic into a shared path. + +- [ ] Refactor `c_step` control flow + - [c_step](ocean/pathfinder/pathfinder.h:689) has deep nested branching for action validity, bounds, wall checks, movement, penalties, and terminal handling. + - Flatten into early-return branches for readability and lower cognitive load. + +- [ ] Consolidate reset logic + - [c_reset](ocean/pathfinder/pathfinder.h:650) and [reset_attempt](ocean/pathfinder/pathfinder.h:665) overlap heavily. + - Extract a shared reset initializer with a small mode flag for map regeneration vs attempt retry. + +## Medium-priority cleanup + +- [ ] Reduce overlong/duplicated rendering code blocks in `pathfinder.h` + - Render path in the same file is dense and mixed with gameplay-specific logic; split into clearer helpers. + +- [ ] Reconcile thin “micro-wrapper” helpers + - Some tiny wrappers (wall access/action helpers) remain after earlier cleanup; remove ones that don’t add meaningful abstraction. + +## Test file cleanup + +- [ ] Review `test_pathfinder_core.c` for repetitive setup/assert patterns + - [test file is large](ocean/pathfinder/tests/test_pathfinder_core.c:1). + - Consider shared maze builders/helpers to reduce repeated scenario boilerplate and keep tests dense but readable. diff --git a/config/pathfinder.ini b/config/pathfinder.ini index 16473c8b05..4853d0779c 100644 --- a/config/pathfinder.ini +++ b/config/pathfinder.ini @@ -3,12 +3,12 @@ env_name = pathfinder [vec] total_agents = 4096 -num_buffers = 8 +num_buffers = 1 num_threads = 0 [env] -branch_prob = 0.008231888077505314 -loop_prob = 0.009618103745667449 +branch_prob = 0 +loop_prob = 0 step_penalty = -0.001 new_wall_penalty = 0.0 known_wall_death_penalty = -1.0 @@ -22,7 +22,7 @@ curriculum_enabled = 1 max_steps = 128 [policy] -hidden_size = 64 +hidden_size = 128 num_layers = 2 expansion_factor = 1 @@ -33,25 +33,25 @@ total_timesteps = 10000000 learning_rate = 0.0050000000000000044 anneal_lr = 1 min_lr_ratio = 0 -gamma = 0.9971535367510901 -gae_lambda = 0.817278800352197 -replay_ratio = 4 -clip_coef = 1 -vf_coef = 1.6878199475999294 -vf_clip_coef = 4.010603143252005 -max_grad_norm = 0.2693000896567146 -ent_coef = 0.20000000000000004 +gamma = 0.9323750048012285 +gae_lambda = 0.6447272834389924 +replay_ratio = 3.9541522542643888 +clip_coef = 0.04758361994585345 +vf_coef = 2.5078946504421316 +vf_clip_coef = 3.904257054821052 +max_grad_norm = 0.31773469745800403 +ent_coef = 0.007301801357163463 anneal_ent_coef = 0 min_ent_coef_ratio = 0.1 beta1 = 0.9 beta2 = 0.999 -eps = 1e-8 +eps = 2.17069274192998e-12 minibatch_size = 8192 -horizon = 16 -vtrace_rho_clip = 1.9104954167345607 -vtrace_c_clip = 1.0591472721467328 -prio_alpha = 1 -prio_beta0 = 0.3976524344305938 +horizon = 32 +vtrace_rho_clip = 2.1610638009706147 +vtrace_c_clip = 4.02621810163764 +prio_alpha = 0.3225588903414761 +prio_beta0 = 1.0 anneal_prio_beta = 0 state_buffer_size = 0 cl_frac = 0 diff --git a/ocean/pathfinder/pathfinder.c b/ocean/pathfinder/pathfinder.c index 84f9ca04a8..b42fd33279 100644 --- a/ocean/pathfinder/pathfinder.c +++ b/ocean/pathfinder/pathfinder.c @@ -23,6 +23,7 @@ int main(void) { env.rewards = rewards; env.terminals = terminals; env.num_agents = 1; + env.player_mode = true; env.rng = (unsigned int)time(NULL); env.branch_prob = 0.35f; env.loop_prob = 0.10f; diff --git a/ocean/pathfinder/pathfinder.h b/ocean/pathfinder/pathfinder.h index 37a3c6ac11..a5a5d72add 100644 --- a/ocean/pathfinder/pathfinder.h +++ b/ocean/pathfinder/pathfinder.h @@ -90,6 +90,7 @@ typedef struct PathfinderClient { typedef struct Pathfinder { PathfinderClient* client; + bool player_mode; Log log; float* observations; float* actions; @@ -671,9 +672,9 @@ static const Color PATHFINDER_GOAL = {232, 184, 58, 255}; static const Color PATHFINDER_START = {118, 146, 150, 255}; static const Color PATHFINDER_VISITED = {0, 187, 187, 42}; -static PathfinderClient* make_client(void) { +static PathfinderClient* make_client(Pathfinder* env) { PathfinderClient* client = (PathfinderClient*)calloc(1, sizeof(PathfinderClient)); - client->show_truth = true; + client->show_truth = !env->player_mode; InitWindow(PATHFINDER_RENDER_WIDTH, PATHFINDER_RENDER_HEIGHT, "PufferLib Pathfinder"); SetTargetFPS(30); return client; @@ -827,31 +828,38 @@ static void draw_panel(Pathfinder* env) { DrawText("Arrows/WASD move | R reset", PATHFINDER_RENDER_BOARD_X, PATHFINDER_RENDER_HEIGHT - 30, 18, PATHFINDER_MUTED); - DrawText("TAB view | SPACE random | ESC quit", + DrawText(env->player_mode + ? "SPACE random | TAB locked off | ESC quit" + : "TAB view | SPACE random | ESC quit", PATHFINDER_RENDER_BOARD_X + 310, PATHFINDER_RENDER_HEIGHT - 30, 18, PATHFINDER_MUTED); } void c_render(Pathfinder* env) { if (!IsWindowReady()) { - env->client = make_client(); + env->client = make_client(env); } else if (env->client == NULL) { env->client = (PathfinderClient*)calloc(1, sizeof(PathfinderClient)); - env->client->show_truth = true; + env->client->show_truth = !env->player_mode; + } + if (env->player_mode) { + env->client->show_truth = false; } if (IsKeyDown(KEY_ESCAPE)) { c_close(env); exit(0); } - if (IsKeyPressed(KEY_TAB)) { + if (IsKeyPressed(KEY_TAB) && !env->player_mode) { env->client->show_truth = !env->client->show_truth; } BeginDrawing(); ClearBackground(PATHFINDER_BG); DrawText("Milton Bradley Pathfinder", PATHFINDER_RENDER_BOARD_X, 26, 30, PATHFINDER_TEXT); - DrawText("Red = known wall, green = known open, gray = true hidden wall", + DrawText(env->player_mode + ? "Red = known wall, green = known open" + : "Red = known wall, green = known open, gray = true hidden wall", PATHFINDER_RENDER_BOARD_X, 60, 18, PATHFINDER_MUTED); draw_board(env); draw_panel(env); From 8018d4d19412a61755aaf50c17b029d76aa6a409 Mon Sep 17 00:00:00 2001 From: Kinvert Date: Fri, 5 Jun 2026 20:14:48 -0700 Subject: [PATCH 15/16] Use unsigned RNG for player-mode action sampling --- TODO.md | 7 +++---- ocean/pathfinder/pathfinder.h | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/TODO.md b/TODO.md index 3b80a9c6b5..141df4242f 100644 --- a/TODO.md +++ b/TODO.md @@ -4,10 +4,9 @@ Track simplifications to reduce unnecessary complexity and keep the env lean. ## High-priority refactors -- [ ] Fix random action generation in player mode - - `pathfinder_rand` returns an `int` from `rand_u32`, which can be negative when cast. - - `pathfinder_rand(&env) % PATHFINDER_NUM_ACTIONS` can yield negative values and create invalid actions. - - Replace with unsigned-safe modulo path (`(int)(rand_u32(&env) & 0xFFu) % PATHFINDER_NUM_ACTIONS`) or direct `pathfinder_rand(&env)` modulo on `unsigned int` before cast. +- [x] Fix random action generation in player mode + - `pathfinder_rand` now returns `unsigned int`, and action sampling uses unsigned modulo. + - This removes potential negative casts before indexing actions. - [ ] Remove curriculum helper asymmetry in `pathfinder.h` - [curriculum_min_solution_len](ocean/pathfinder/pathfinder.h:139) still does fallback/clamp sequencing while max length logic is already direct. diff --git a/ocean/pathfinder/pathfinder.h b/ocean/pathfinder/pathfinder.h index a5a5d72add..65710873db 100644 --- a/ocean/pathfinder/pathfinder.h +++ b/ocean/pathfinder/pathfinder.h @@ -313,8 +313,8 @@ static void init_walls(State* s) { } } -static inline int pathfinder_rand(Pathfinder* env) { - return (int)rand_u32(env); +static inline unsigned int pathfinder_rand(Pathfinder* env) { + return rand_u32(env); } static bool carve_solution_recursive( From 3a33deb19762f1a1a8566d152ed1f772487a69c2 Mon Sep 17 00:00:00 2001 From: Kinvert Date: Fri, 5 Jun 2026 21:29:21 -0700 Subject: [PATCH 16/16] Drop workspace-only files from pathfinder PR surface --- AGENTS.md | 284 ---- PATHFINDER_SPEC.md | 343 ---- TODO.md | 52 - build.sh | 20 +- .../plans/2026-06-04-pathfinder-env.md | 178 -- ocean/pathfinder/tests/bench_pathfinder.c | 255 --- .../pathfinder/tests/benchmark_pathfinder.py | 163 -- .../pathfinder/tests/benchmark_pathfinder.sh | 22 - ocean/pathfinder/tests/run_all.sh | 15 - ocean/pathfinder/tests/test_pathfinder_core.c | 821 ---------- uv.lock | 1456 ----------------- 11 files changed, 2 insertions(+), 3607 deletions(-) delete mode 100644 AGENTS.md delete mode 100644 PATHFINDER_SPEC.md delete mode 100644 TODO.md delete mode 100644 docs/superpowers/plans/2026-06-04-pathfinder-env.md delete mode 100644 ocean/pathfinder/tests/bench_pathfinder.c delete mode 100755 ocean/pathfinder/tests/benchmark_pathfinder.py delete mode 100755 ocean/pathfinder/tests/benchmark_pathfinder.sh delete mode 100644 ocean/pathfinder/tests/run_all.sh delete mode 100644 ocean/pathfinder/tests/test_pathfinder_core.c delete mode 100644 uv.lock diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 5cdf85ff41..0000000000 --- a/AGENTS.md +++ /dev/null @@ -1,284 +0,0 @@ -# AGENTS.md - -Guidance for autonomous agents working on the Pathfinder PufferLib env. - -## Current Workspace - -This machine is `g240`. - -Writable working repo: - -- `/home/claude/pathfinder` -- Branch: `pathfinder` -- Base: official PufferLib `5.0` -- Remote: `origin https://github.com/PufferAI/PufferLib.git` - -This workspace is for a new PufferLib 5 Ocean environment named -`pathfinder`, based on Milton Bradley's 1977 Pathfinder board game. - -Reference repos: - -- `/home/claude/dogfight5` - - PufferLib 5 Dogfight port workspace. - - Use as the local reference for uv setup, CUDA 12.8 activation, native build - behavior, and PufferLib 5 state-memory conventions. - - Do not modify it unless the human explicitly redirects work there. -- `/home/claude/PufferLib` - - Dirty Dogfight 4 control workspace. - - Do not use as the implementation target for Pathfinder. - -## Goal - -Build a single-agent Pathfinder maze-solving environment first. - -The env should generate a hidden 6x6 barricade maze, hide a pawn, start the -agent at `A1`, and train the agent to discover walls and find a path to the -hidden pawn using only four movement actions. - -The current design source is: - -- `/home/claude/pathfinder/PATHFINDER_SPEC.md` - -Update that spec deliberately when requirements change. Do not let -implementation drift away from it silently. - -## Rule Source - -The board-game rules were read from: - -- User-provided rules scan: - `http://www.transformertoys.co.uk/images/instruction-scans/hasbro/Pathfinder.pdf` -- User-provided video transcript. - -Important game facts: - -- The board is 6x6 with rows `A-F` and columns `1-6`. -- Barriers live in slots around and between squares. -- A hidden short pawn sits on one square. -- There must be at least one route from a column-1 entrance to the hidden pawn. -- Movement is orthogonal only. -- Asking for a blocked edge reveals a wall and leaves the pawn in place. -- Asking for an open edge moves the pawn. -- Moving into the hidden pawn square wins. - -For v1, simplify the board-game entry/re-entry rule: - -- The agent always starts at `A1`. -- The generator must guarantee a route from `A1` to the hidden pawn. -- Action space is four moves: north, east, south, west. - -## Safety Rules - -- Do implementation work only in `/home/claude/pathfinder`. -- Keep Pathfinder implementation under `ocean/pathfinder/` whenever possible. -- Allowed repo-level additions for this env: - - `config/pathfinder.ini` - - root planning docs such as `PATHFINDER_SPEC.md` and `AGENTS.md` - - minimal build/integration fixes already needed for this PufferLib 5 branch. -- Do not modify `/home/claude/dogfight5`, `/home/claude/PufferLib`, - `/home/claude/dogfight3`, or `/home/claude/dogfight4`. -- Avoid destructive Git operations. Do not run `git reset --hard`, mass - deletes, or history rewrites unless the human explicitly asks. -- Do not add CPU fallback work unless explicitly requested. Native CUDA is the - expected path on this machine. - -## Environment Setup - -This repo uses `uv` and an editable install: - -```bash -cd /home/claude/pathfinder -uv venv --python 3.12 .venv -uv sync -uv pip install -e . -source .venv/bin/activate -python --version -``` - -The current venv is verified with: - -- Python `3.12.3` -- Torch `2.10.0+cu128` -- CUDA runtime `12.8` -- `CUDA_HOME=/usr/local/cuda-12.8` -- `CC=clang` - -The venv activation script has the same CUDA 12.8 hook pattern as -`dogfight5`: - -```bash -source .venv/bin/activate -echo "$CUDA_HOME" -which nvcc -``` - -Expected: - -```text -/usr/local/cuda-12.8 -/usr/local/cuda-12.8/bin/nvcc -``` - -The Codex sandbox may not expose GPU devices. That can make PyTorch report -`torch.cuda.is_available() == False` and make `nvcc -arch=native` warn that no -valid GPU is visible. Native extension builds can still compile in the sandbox. -GPU training/sweeps may need to run outside the sandbox. - -If `python -m pufferlib.pufferl train pathfinder --train.gpus 1` fails inside -Codex with `Assertion 'device_count > 0 && "CUDA is not available"' failed`, -do not switch to CPU, rewrite CUDA setup, or debug the venv as the first -response. This is the managed sandbox hiding GPU devices. Re-run the same -training command with escalated/outside-sandbox execution so it can see -`/dev/nvidia*`. - -## Verified Build Baseline - -The fresh workspace has already been prepared with: - -- `uv sync` -- `uv pip install -e .` -- local `raylib-5.5_linux_amd64` copied from `/home/claude/dogfight5` -- the `dogfight5` `build.sh` fix that links versioned cuDNN/NCCL wheel - libraries correctly. - -Verified native CUDA extension builds: - -```bash -source .venv/bin/activate -./build.sh g2048 -./build.sh breakout -``` - -Both produced: - -```text -pufferlib/_C.cpython-312-x86_64-linux-gnu.so -``` - -Do not use `./build.sh ENV --cpu` as the default smoke path for Pathfinder. - -## Implementation Target - -Expected files: - -```text -ocean/pathfinder/pathfinder.h -ocean/pathfinder/pathfinder.c -ocean/pathfinder/binding.c -ocean/pathfinder/tests/ -config/pathfinder.ini -``` - -Follow PufferLib 5 state-aware env patterns from: - -```text -ocean/g2048/ -ocean/boxoban/ -ocean/craftax/ -``` - -`ocean/maze/` is useful for navigation ideas but should not be copied blindly; -it is not the best state-memory reference on this branch. - -## Current Design Defaults - -- Fixed board size: 6x6. -- Spawn: `A1`. -- Hidden pawn: generated at the current curriculum distance from `A1`, and the - maze must connect `A1` to it. -- Initial curriculum: `env.start_solution_len = 4` starts targets exactly 4 moves - from `A1`; each solve advances the next generated puzzle by 1 move, capped at - the board maximum. -- Set `env.curriculum_enabled = 0` to disable curriculum and start every - generated map at the board maximum difficulty immediately. -- Actions: four discrete moves. -- Observation: - - 84 wall slots as floats: - - `-1.0` unobserved - - `0.0` observed open - - `1.0` observed wall - - normalized current x - - normalized current y - - total `OBS_SIZE = 86` -- Rewards: - - first-time wall discovery has no extra penalty beyond step cost - - repeated known-wall hits get a small penalty - - repeated known-wall hits terminate the attempt - - first visits get a small discovery reward - - revisits get a small penalty - - immediate two-cell oscillation like `A1 -> B1 -> A1 -> B1` terminates with - a large penalty -- Failed attempts reset the agent to `A1` on the same true map but clear all - discovered wall/open observations back to `-1.0`. Solves generate the next - map and advance curriculum. -- First implementation should keep `train.state_buffer_size = 0` until - deterministic state roundtrip tests exist. - -## Test Expectations - -Use test-driven development for Pathfinder behavior: - -1. Add focused tests for wall indexing, maze solvability, reset observation, - movement/wall reveal semantics, and terminal success. -2. Make the narrow tests pass. -3. Run the Pathfinder core tests. The script compiles its own scoped C test - binary from the current `ocean/pathfinder/` source before executing it; it - does not use an existing `pufferlib/_C*.so` and is not testing stale native - extension code: - -```bash -source .venv/bin/activate -bash ocean/pathfinder/tests/run_all.sh -``` - -4. Build the native PufferLib extension. Do this before any Python-level - Pathfinder smoke test, training run, or eval, because those paths import the - built `pufferlib/_C*.so` artifact: - -```bash -source .venv/bin/activate -./build.sh pathfinder -``` - -5. Run a short training smoke only after build and behavior tests pass: - -```bash -source .venv/bin/activate -python -m pufferlib.pufferl train pathfinder --train.gpus 1 -``` - -If tests require helper binaries, put them under `ocean/pathfinder/tests/` and -keep them scoped to Pathfinder. - -When the human asks to "run all tests" for Pathfinder, do not manually iterate -the repo-root `tests/test_*.py` files. Those files are stale upstream or -experimental tests for older PufferLib APIs and optional dependencies, not the -current Pathfinder acceptance suite. Known expected failures there include -missing `pufferlib.emulation`, missing `pufferl.make_parser`, missing -`pufferlib/src/models.cu`, optional packages such as `heavyball`, `pandas`, and -`pyximport`, and CUDA visibility failures inside the Codex sandbox. - -For Pathfinder, the supported baseline is: - -```bash -source .venv/bin/activate -bash ocean/pathfinder/tests/run_all.sh -./build.sh pathfinder -``` - -If running any Python-level Pathfinder check after source edits, rebuild with -`./build.sh pathfinder` first so Python does not import an old native extension. - -If the human explicitly asks for the stale repo-root Python tests anyway, state -that they are not the Pathfinder baseline before running them, and do not report -their expected failures as a Pathfinder regression. - -## Quick Ops Notes - -- The eval command supports `--load-model-path latest` to automatically pick the - latest available checkpoint in the workspace. -- Verified working invocation: - - ```bash - source .venv/bin/activate && DISPLAY=:0 python -m pufferlib.pufferl eval pathfinder --load-model-path latest - ``` diff --git a/PATHFINDER_SPEC.md b/PATHFINDER_SPEC.md deleted file mode 100644 index c254cafe5e..0000000000 --- a/PATHFINDER_SPEC.md +++ /dev/null @@ -1,343 +0,0 @@ -# Pathfinder Environment Spec - -Status: implemented baseline; update as behavior changes - -Workspace: `/home/claude/pathfinder` - -Target env name: `pathfinder` - -## Intent - -Build a PufferLib 5 Ocean environment inspired by Milton Bradley's 1977 -Pathfinder board game. The first version is a single-agent maze-solving task: -the environment generates a hidden, traversible barricade layout and a hidden -pawn, and the agent learns to discover walls while finding a path to the pawn. - -The full two-player race is intentionally out of scope for the first version. -The board-game rules remain the source model for the grid, hidden pawn, -barriers, entry from column 1, adjacent movement, wall discovery, and terminal -goal detection. - -## Rule Summary - -Rules source: - -- Official scanned rules PDF supplied by the user: - `http://www.transformertoys.co.uk/images/instruction-scans/hasbro/Pathfinder.pdf` -- User-supplied video transcript describing play. - -Board-game facts that matter for the env: - -- The board is a 6x6 grid with rows `A-F` and columns `1-6`. -- Each player hides a short pawn on any square of their home grid. -- Each player secretly places barrier chips in slots around and between grid - squares. -- The barricades must leave at least one route from a column-1 entrance square - to the hidden pawn. -- Some column-1 entrances may be blocked. Some open column-1 entrances may be - dead ends. -- A searching player starts by asking whether a column-1 square can be entered. -- After entering, the player asks to move to adjacent orthogonal squares. - Diagonal movement is not allowed. -- If the requested edge has no barrier, the tall pawn moves and the turn - continues. If the requested edge has a barrier, the pawn stays put, the wall - is marked on the tracking grid, and turn control would pass in the board game. -- Backtracking is legal and uses the same adjacent-square calls. -- If an unblocked requested square contains the hidden pawn, the searcher wins. - -The single-agent env treats every action as one RL step. It does not model -board-game turn passing or the pre-entry choice, because there is no opponent -policy in the first version. - -## Board Model - -Constants: - -- `PF_ROWS = 6` -- `PF_COLS = 6` -- Cells use zero-based `(row, col)` internally. -- Row names map `A-F -> 0-5`. -- Column names map `1-6 -> 0-5`. - -Wall slots are represented as graph edges: - -- Vertical slots: `V[row][edge_col]`, shape `6 x 7`. - - `edge_col = 0` is the left entry/exit edge for column 1. - - `edge_col = 1..5` are internal east/west edges. - - `edge_col = 6` is the right board boundary. -- Horizontal slots: `H[edge_row][col]`, shape `7 x 6`. - - `edge_row = 0` is the top board boundary. - - `edge_row = 1..5` are internal north/south edges. - - `edge_row = 6` is the bottom board boundary. - -Total wall observation slots: `6*7 + 7*6 = 84`. - -Boundary behavior: - -- All off-board movement is impossible in v1. Asking to move off the board - applies `impossible_penalty`, terminates the attempt, and resets the agent to - `A1` on the same map. - -## Maze Generation - -The generator must produce legal, solvable layouts without hand-authored maps. - -Puzzle generation: - -1. Choose the target solution length from the curriculum state. -2. Initialize all wall slots to blocked. -3. Carve one randomized orthogonal path from `A1` to a hidden pawn that is - exactly the target length. -4. Store `A1` as the agent spawn for the episode. -5. Open additional random internal edges while preserving the target length as - the shortest path. -6. Validate with BFS that `A1` reaches the pawn. - -Default generator style: - -- Use deterministic curriculum difficulty: target length starts at - `start_solution_len` and advances by one after each solve. -- Carve a randomized, non-monotonic solution path of exact length to the current - target. -- Open additional random internal edges as branch/loop density knobs rather than - trying to exactly copy human barricade layouts. - -Curriculum: - -- `start_solution_len` is the starting curriculum distance. The default training - value is `4`. -- `curriculum_enabled = 0` disables the schedule and starts every generated map - at `PATHFINDER_MAX_SOLUTION_LEN`. -- Each successful solve increments the next generated puzzle distance by one, - capped at `PATHFINDER_MAX_SOLUTION_LEN`, when `curriculum_enabled = 1`. -- Failed attempts do not advance curriculum. -- Failed attempts restart the agent at `A1` on the same true map, but clear - discovered wall/open observations back to `-1.0`, so map generation happens - only after a solve or external reset. - -Config knobs: - -- `branch_prob`: probability of adding false branches from the main route. -- `loop_prob`: probability of opening extra internal edges after carving. -- `start_solution_len`: starting exact solution distance. -- `curriculum_enabled`: `1` for deterministic solve-based curriculum, `0` for - max-difficulty maps from the first reset. -- `max_steps`: timeout. -- `seed`: inherited from vector env config. - -The first implementation should keep board size fixed at 6x6. Larger synthetic -boards can be a later variant after the baseline trains. - -## Action Space - -Use one categorical action head. - -Action count: `4` - -- `0`: move north. -- `1`: move east. -- `2`: move south. -- `3`: move west. - -Action semantics: - -- Reset starts the agent at `A1`. -- Cardinal moves test the corresponding wall slot. -- If the tested slot is blocked, reveal it and keep the same position. -- If the tested slot is open, reveal it and move to the destination cell. -- Moving west from column 1 through an open left edge is treated as an exit - attempt and leaves the agent in place in v1. This keeps action space simple - and avoids an outside-grid state. Full retreat/re-entry can be added later. -- Attempts to move through top, right, or bottom board boundaries reveal a - blocked boundary slot and keep the agent in place. - -## Observation Space - -Use a float observation tensor. - -Observation layout: - -1. `wall_obs[84]` - - `-1.0`: unobserved wall slot. - - `0.0`: observed open slot. - - `1.0`: observed wall slot. -2. `pos_x` - - Current column normalized to `[0, 1]`. -3. `pos_y` - - Current row normalized to `[0, 1]`. - -Observation size: `86`. - -The hidden pawn location is not directly observed. It is only discovered when -the agent enters or moves into that square through an open edge. - -## Rewards and Termination - -Default reward model: - -- `+1.0` for reaching the hidden pawn. -- `-0.001` per step. -- `+0.01` for first entering a cell in the current attempt. -- `0.0` extra penalty for hitting a newly discovered wall; the agent paid the - step cost but gained information. -- `-1.0` extra penalty and terminal attempt reset for hitting a wall that was - already known. -- `-0.01` for revisiting a square that was previously left in the current - attempt. -- `-1.0` extra penalty and terminal attempt reset for immediate two-cell - oscillation such as `A1 -> B1 -> A1 -> B1`. -- `-1.0` for impossible movement, such as attempting to move off the board. - -Termination: - -- Success: agent reaches the hidden pawn. -- Timeout: `tick >= max_steps`. -- Known-wall death: agent tries to move through a wall that is already observed - as blocked. -- Repeat-move death: agent repeats an immediate two-cell oscillation. For - example, `A1 -> B1 -> A1` is legal, but the next `A1 -> B1` dies. - -Reset after terminal: - -- Success logs the episode, advances curriculum by one move when not capped, - and generates a new map. -- Timeout, known-wall death, and repeat-move death log the episode, then reset - only the attempt state: position returns to `A1`, tick/path/revisit/move - counters clear, the same true map remains, and all wall observations return - to `-1.0`. - -Logged metrics: - -- `perf`: `1.0` on success, `0.0` on timeout. -- `score`: success reward adjusted by path efficiency. -- `episode_return` -- `episode_length` -- `success` -- `wall_hits` -- `revisits` -- `known_wall_deaths` -- `repeat_move_deaths` -- `known_walls` -- `known_open_edges` -- `shortest_path_len` -- `agent_path_len` -- `curriculum_level` -- `curriculum_target_len` -- `curriculum_next_target_len` -- `n` - -## PufferLib Integration - -Expected files: - -- `ocean/pathfinder/pathfinder.h` -- `ocean/pathfinder/pathfinder.c` -- `ocean/pathfinder/binding.c` -- `ocean/pathfinder/tests/` -- `config/pathfinder.ini` - -The env should follow current PufferLib 5 Ocean patterns: - -- Define `Log`. -- Define `State`. -- Keep rollout-local future-affecting fields inside `State`. -- Define env struct `Pathfinder` with required pointers: - - `float* observations` - - `float* actions` - - `float* rewards` - - `float* terminals` - - `int num_agents` - - `Log log` - - `State state` - - `unsigned int rng` -- In `binding.c`, set: - - `OBS_SIZE 86` - - `NUM_ATNS 1` - - `ACT_SIZES {4}` - - `OBS_TENSOR_T FloatTensor` - - `Env Pathfinder` - - `puffer_state_refresh(Pathfinder* env)` to rebuild observations from - restored state. -- Keep `state_buffer_size = 0` initially unless state-memory tests exist. - -`g2048`, `boxoban`, and `craftax` are the nearest 5.0 state-aware references. -`maze` is useful for navigation ideas but is not state-memory complete on this -branch. - -## Testing - -Add focused tests before training: - -- Wall index mapping: - - Every cardinal move maps to the expected `V` or `H` slot. -- Observation initialization: - - All wall observations start at `-1.0`. - - Position is `A1` after reset. -- Maze legality: - - Every generated maze has at least one open column-1 entrance. - - BFS from open column-1 entrances reaches the hidden pawn. - - `A1` is connected to the hidden pawn. - - Generated truth walls never allow top, right, or bottom off-board movement. -- Step semantics: - - Open edge reveals `0.0` and moves. - - Closed edge reveals `1.0` and does not move. - - Repeated known wall hit terminates the attempt and retries the same true - map with wall observations reset to `-1.0`. - - Repeating an immediate two-cell oscillation, such as - `A1 -> B1 -> A1 -> B1`, terminates with a large penalty and retries the - same true map blind. - - Timeout retries the same true map with wall observations reset to `-1.0`. - - Success advances the next map by one solution step. - - West move from column 1 through an open left edge reveals the edge but does - not move in v1. - - Reaching the hidden pawn sets terminal and success log. -- State roundtrip: - - Save state after partial exploration. - - Restore into a fresh env. - - Verify observations and scripted future steps match. - -Verification commands after implementation: - -```bash -source .venv/bin/activate -./build.sh pathfinder -python -m pufferlib.pufferl train pathfinder --train.gpus 1 -``` - -Native GPU builds are the expected path for this workspace. - -## Initial Training Config - -Start conservative: - -- `vec.total_agents = 8192` or `16384` -- `vec.num_buffers = 2` -- `env.start_solution_len = 4` for early closer-target curriculum -- `env.curriculum_enabled = 1` -- `train.gpus = 1` -- `train.horizon = 128` -- `train.minibatch_size = 32768` -- `train.learning_rate = 0.001` -- `train.gamma = 0.995` -- `train.use_rnn = 1` -- `train.state_buffer_size = 0` -- `train.cl_frac = 0` - -Reasoning: the task is partially observable and memory-dependent. Recurrent -policy support is likely useful because the agent must remember discovered -walls and path context, even though the observation also contains discovered -wall memory. - -## Later Extensions - -After the single-agent solver works: - -- Add curriculum over branch density, loop density, and solution length. -- Add larger synthetic boards while retaining a 6x6 rules mode. -- Add direct coordinate-call action variants for closer board-game fidelity. -- Add explicit outside-grid entry/re-entry actions for closer board-game - fidelity. -- Add two-player self-play where each side generates or chooses a maze and - races to find the opposing pawn. -- Add render mode showing true hidden maze, observed tracking grid, current - pawn, and discovered path. diff --git a/TODO.md b/TODO.md deleted file mode 100644 index 141df4242f..0000000000 --- a/TODO.md +++ /dev/null @@ -1,52 +0,0 @@ -# Pathfinder Bloat Reduction TODO - -Track simplifications to reduce unnecessary complexity and keep the env lean. - -## High-priority refactors - -- [x] Fix random action generation in player mode - - `pathfinder_rand` now returns `unsigned int`, and action sampling uses unsigned modulo. - - This removes potential negative casts before indexing actions. - -- [ ] Remove curriculum helper asymmetry in `pathfinder.h` - - [curriculum_min_solution_len](ocean/pathfinder/pathfinder.h:139) still does fallback/clamp sequencing while max length logic is already direct. - - [next_curriculum_max_solution_len](ocean/pathfinder/pathfinder.h:582) duplicates curriculum clamping logic. - - Simplify to direct env-invariant behavior where possible. - -- [ ] Remove clamp/safety fallback patterns that are no longer needed - - [pathfinder_clamp_int](ocean/pathfinder/pathfinder.h:15) and related call sites were added for defensive fault handling. - - Decide if we trust validated INI/env invariants and drop this extra layer where possible. - -- [ ] Simplify config initialization default fallback chains - - [init defaults](ocean/pathfinder/pathfinder.h:627) has many `value == 0 ? default : value` branches for reward and tuning parameters. - - Make defaults explicit and avoid repeated defensive defaulting. - -- [ ] Simplify maze generation fallback flow - - [generate_maze](ocean/pathfinder/pathfinder.h:544) includes multi-step rollback behavior (try A, then B, then hard reset). - - Collapse to a single, deterministic path length selection approach. - -- [ ] Reduce duplication in random edge opening - - [open_random_edges](ocean/pathfinder/pathfinder.h:492) has duplicated horizontal/vertical loops with repeated shortest-path checks. - - Consolidate edge-try logic into a shared path. - -- [ ] Refactor `c_step` control flow - - [c_step](ocean/pathfinder/pathfinder.h:689) has deep nested branching for action validity, bounds, wall checks, movement, penalties, and terminal handling. - - Flatten into early-return branches for readability and lower cognitive load. - -- [ ] Consolidate reset logic - - [c_reset](ocean/pathfinder/pathfinder.h:650) and [reset_attempt](ocean/pathfinder/pathfinder.h:665) overlap heavily. - - Extract a shared reset initializer with a small mode flag for map regeneration vs attempt retry. - -## Medium-priority cleanup - -- [ ] Reduce overlong/duplicated rendering code blocks in `pathfinder.h` - - Render path in the same file is dense and mixed with gameplay-specific logic; split into clearer helpers. - -- [ ] Reconcile thin “micro-wrapper” helpers - - Some tiny wrappers (wall access/action helpers) remain after earlier cleanup; remove ones that don’t add meaningful abstraction. - -## Test file cleanup - -- [ ] Review `test_pathfinder_core.c` for repetitive setup/assert patterns - - [test file is large](ocean/pathfinder/tests/test_pathfinder_core.c:1). - - Consider shared maze builders/helpers to reduce repeated scenario boilerplate and keep tests dense but readable. diff --git a/build.sh b/build.sh index 101891644f..99a898fc6f 100755 --- a/build.sh +++ b/build.sh @@ -252,22 +252,6 @@ for lib_flag in "$CUDNN_LFLAG" "$NCCL_LFLAG"; do fi done -resolve_lib_arg() { - local lib_flag=$1 lib_name=$2 - local default_arg="-l$lib_name" - [[ "$lib_flag" == -L* ]] || { echo "$default_arg"; return; } - local lib_dir="${lib_flag#-L}" - [ -f "$lib_dir/lib$lib_name.so" ] && { echo "$default_arg"; return; } - local candidate - for candidate in "$lib_dir/lib$lib_name.so".*; do - [ -f "$candidate" ] && { echo "$candidate"; return; } - done - echo "$default_arg" -} - -CUDNN_LIB_ARG="$(resolve_lib_arg "$CUDNN_LFLAG" cudnn)" -NCCL_LIB_ARG="$(resolve_lib_arg "$NCCL_LFLAG" nccl)" - export CCACHE_DIR="${CCACHE_DIR:-$HOME/.ccache}" export CCACHE_BASEDIR="$(pwd)" export CCACHE_COMPILERCHECK=content @@ -328,7 +312,7 @@ if [ -z "$MODE" ]; then build/bindings.o "$RAYLIB_A" -L$CUDA_HOME/lib64 $CUDNN_LFLAG $NCCL_LFLAG "${WHEEL_RPATH_FLAGS[@]}" - -lcudart "$NCCL_LIB_ARG" -lnvidia-ml -lcublas -lcusolver -lcurand "$CUDNN_LIB_ARG" + -lcudart -lnccl -lnvidia-ml -lcublas -lcusolver -lcurand -lcudnn $OMP_LIB $LINK_OPT "${SHARED_LDFLAGS[@]}" -o "$OUTPUT" @@ -371,7 +355,7 @@ elif [ "$MODE" = "profile" ]; then -Xcompiler=-fopenmp \ tests/profile_kernels.cu vendor/ini.c \ "$RAYLIB_A" \ - "$NCCL_LIB_ARG" -lnvidia-ml -lcublas -lcurand "$CUDNN_LIB_ARG" \ + -lnccl -lnvidia-ml -lcublas -lcurand -lcudnn \ -lGL -lm -lpthread $OMP_LIB \ -o profile echo "Built: ./profile" diff --git a/docs/superpowers/plans/2026-06-04-pathfinder-env.md b/docs/superpowers/plans/2026-06-04-pathfinder-env.md deleted file mode 100644 index 0e149a638b..0000000000 --- a/docs/superpowers/plans/2026-06-04-pathfinder-env.md +++ /dev/null @@ -1,178 +0,0 @@ -# Pathfinder Environment Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Build the first PufferLib 5 `pathfinder` Ocean environment: a generated 6x6 hidden-wall maze, fixed `A1` spawn, hidden pawn target, four movement actions, and wall-memory observations. - -**Architecture:** Implement the environment as a compact C env under `ocean/pathfinder/`, following `g2048` for state ownership and `boxoban`/`maze` for navigation style. Put deterministic helpers in `pathfinder.h` so focused C tests can include them directly before the PufferLib CUDA binding is built. - -**Tech Stack:** C, PufferLib 5 Ocean static vec binding, `uv` editable install, native CUDA build via `./build.sh pathfinder`. - ---- - -### Task 1: Core Pathfinder Model and Tests - -**Files:** -- Create: `ocean/pathfinder/pathfinder.h` -- Create: `ocean/pathfinder/tests/test_pathfinder_core.c` -- Create: `ocean/pathfinder/tests/run_all.sh` - -- [ ] **Step 1: Write the failing core test** - -Create `ocean/pathfinder/tests/test_pathfinder_core.c` with tests that include `../pathfinder.h` and check: - -- `PATHFINDER_OBS_SIZE == 86` -- `PATHFINDER_NUM_WALLS == 84` -- reset starts at row `0`, col `0` -- all wall observations start at `-1.0f` -- generated mazes connect `A1` to the hidden pawn -- moving through an open edge reveals `0.0f` and moves -- moving into a blocked edge reveals `1.0f` and does not move - -- [ ] **Step 2: Run the core test to verify RED** - -Run: - -```bash -cd /home/claude/pathfinder -bash ocean/pathfinder/tests/run_all.sh -``` - -Expected: FAIL because `ocean/pathfinder/pathfinder.h` does not exist yet. - -- [ ] **Step 3: Implement minimal core model** - -Create `ocean/pathfinder/pathfinder.h` with: - -- constants for 6x6 cells, 84 wall slots, 86 obs size, 4 actions -- `Log`, `State`, and `Pathfinder` structs -- wall indexing helpers for vertical and horizontal slots -- deterministic `pathfinder_generate_maze` -- `pathfinder_update_observations` -- `pathfinder_reset` -- `pathfinder_step` -- `refresh_state`, `init`, `c_reset`, `c_step`, `c_close` - -- [ ] **Step 4: Run core test to verify GREEN** - -Run: - -```bash -cd /home/claude/pathfinder -bash ocean/pathfinder/tests/run_all.sh -``` - -Expected: PASS for core model tests. - -### Task 2: PufferLib Binding and Config - -**Files:** -- Create: `ocean/pathfinder/binding.c` -- Create: `ocean/pathfinder/pathfinder.c` -- Create: `config/pathfinder.ini` - -- [ ] **Step 1: Write the failing build expectation** - -Run: - -```bash -cd /home/claude/pathfinder -source .venv/bin/activate -./build.sh pathfinder -``` - -Expected: FAIL because `ocean/pathfinder/binding.c` does not exist. - -- [ ] **Step 2: Add binding** - -Create `ocean/pathfinder/binding.c` using the `g2048`/`boxoban` pattern: - -- `#include "pathfinder.h"` -- `#define OBS_SIZE PATHFINDER_OBS_SIZE` -- `#define NUM_ATNS 1` -- `#define ACT_SIZES {4}` -- `#define OBS_TENSOR_T PrecisionTensor` -- `#define Env Pathfinder` -- `static inline void puffer_state_refresh(Pathfinder* env) { refresh_state(env); }` -- `#include "vecenv.h"` -- `my_init` reads `branch_prob`, `loop_prob`, `extra_entry_prob`, `min_solution_len`, and `max_steps` -- `my_log` exports `perf`, `score`, `episode_return`, `episode_length`, `success`, `wall_hits`, `known_walls`, `known_open_edges`, `shortest_path_len`, `agent_path_len` - -- [ ] **Step 3: Add local demo/standalone file** - -Create `ocean/pathfinder/pathfinder.c` as a small random-action local runner matching the simple env style used by `g2048.c` and `boxoban.c`. - -- [ ] **Step 4: Add config** - -Create `config/pathfinder.ini` with: - -- `[base] env_name = pathfinder` -- `[vec] total_agents = 8192`, `num_buffers = 2`, `num_threads = 0` -- `[env] branch_prob`, `loop_prob`, `extra_entry_prob`, `min_solution_len`, `max_steps` -- `[policy] hidden_size = 512`, `num_layers = 2`, `expansion_factor = 1` -- `[train] gpus = 1`, `total_timesteps = 100000000`, `horizon = 128`, `minibatch_size = 32768`, `learning_rate = 0.001`, `gamma = 0.995`, `use_rnn = 1`, `state_buffer_size = 0`, `cl_frac = 0` - -- [ ] **Step 5: Run native build to verify GREEN** - -Run: - -```bash -cd /home/claude/pathfinder -source .venv/bin/activate -./build.sh pathfinder -``` - -Expected: PASS and build `pufferlib/_C.cpython-312-x86_64-linux-gnu.so`. - -### Task 3: Training Smoke and Documentation Check - -**Files:** -- Modify: `PATHFINDER_SPEC.md` only if implementation exposes a deliberate spec correction. -- Modify: `AGENTS.md` only if commands or defaults changed. - -- [ ] **Step 1: Run core tests** - -Run: - -```bash -cd /home/claude/pathfinder -bash ocean/pathfinder/tests/run_all.sh -``` - -Expected: PASS. - -- [ ] **Step 2: Run native build** - -Run: - -```bash -cd /home/claude/pathfinder -source .venv/bin/activate -./build.sh pathfinder -``` - -Expected: PASS. - -- [ ] **Step 3: Run short train smoke if GPU access permits** - -Run: - -```bash -cd /home/claude/pathfinder -source .venv/bin/activate -python -m pufferlib.pufferl train pathfinder --train.total-timesteps 2097152 -``` - -Expected: The run starts, validates the compiled env name, and completes at least one rollout/update. If sandbox GPU access blocks training, record the exact failure and do not treat it as an env logic failure. - -- [ ] **Step 4: Review docs and status** - -Run: - -```bash -cd /home/claude/pathfinder -rg -n "Action count|Observation size|OBS_SIZE|ACT_SIZES|A1|state_buffer_size" PATHFINDER_SPEC.md AGENTS.md -git status --short --branch -``` - -Expected: docs still say 4 actions, `OBS_SIZE = 86`, fixed `A1` spawn, and initial state memory off. diff --git a/ocean/pathfinder/tests/bench_pathfinder.c b/ocean/pathfinder/tests/bench_pathfinder.c deleted file mode 100644 index 50181d7019..0000000000 --- a/ocean/pathfinder/tests/bench_pathfinder.c +++ /dev/null @@ -1,255 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#define PATHFINDER_NO_RENDER - -#include -#include -#include "../pathfinder.h" - -static double now_seconds(void) { - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return (double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0; -} - -static void setup_env(Pathfinder* env, float* obs, float* actions, - float* rewards, float* terminals) { - memset(env, 0, sizeof(*env)); - env->observations = obs; - env->actions = actions; - env->rewards = rewards; - env->terminals = terminals; - env->num_agents = 1; - env->branch_prob = 0.35f; - env->loop_prob = 0.10f; - env->start_solution_len = 4; - env->curriculum_enabled = 1; - env->max_steps = 128; - env->step_penalty = -0.001f; - env->new_wall_penalty = 0.0f; - env->known_wall_death_penalty = -1.0f; - env->repeat_move_death_penalty = -1.0f; - env->new_cell_reward = 0.01f; - env->revisit_penalty = -0.01f; - env->impossible_penalty = -1.0f; - env->goal_reward = 1.0f; - env->rng = 12345; - init(env); -} - -static long parse_long(const char* value, long fallback) { - char* end = NULL; - long parsed = strtol(value, &end, 10); - if (end == value || parsed <= 0) { - return fallback; - } - return parsed; -} - -static void bench_resets(Pathfinder* env, long resets, int curriculum_level) { - env->curriculum_level = curriculum_level; - double t0 = now_seconds(); - long shortest_sum = 0; - int min_shortest = PATHFINDER_MAX_SOLUTION_LEN; - int max_shortest = 0; - for (long i = 0; i < resets; i++) { - c_reset(env); - int sp = env->state.shortest_path_len; - shortest_sum += sp; - if (sp < min_shortest) min_shortest = sp; - if (sp > max_shortest) max_shortest = sp; - } - double elapsed = now_seconds() - t0; - double reset_sps = (double)resets / elapsed; - double avg_shortest = (double)shortest_sum / (double)resets; - printf("reset_bench curriculum_level=%d resets=%ld seconds=%.6f reset_sps=%.2f avg_shortest=%.3f min_shortest=%d max_shortest=%d\n", - curriculum_level, resets, elapsed, reset_sps, avg_shortest, min_shortest, max_shortest); -} - -static void bench_steps(Pathfinder* env, long steps) { - c_reset(env); - double t0 = now_seconds(); - double reward_sum = 0.0; - for (long i = 0; i < steps; i++) { - env->actions[0] = (float)(pathfinder_rand(env) % PATHFINDER_NUM_ACTIONS); - c_step(env); - reward_sum += env->rewards[0]; - } - double elapsed = now_seconds() - t0; - double step_sps = (double)steps / elapsed; - printf("step_bench steps=%ld seconds=%.6f step_sps=%.2f episodes=%.0f success=%.6f repeat_move_deaths=%.0f reward_sum=%.3f curriculum_level=%d curriculum_target_len=%d\n", - steps, elapsed, step_sps, env->log.n, - env->log.n > 0.0f ? env->log.success / env->log.n : 0.0f, - env->log.repeat_move_deaths, reward_sum, env->curriculum_level, - current_target_solution_len(env)); -} - -static void bench_choose_goal_at_distance(Pathfinder* env, int target_len) { - for (int row = 0; row < PATHFINDER_ROWS; row++) { - for (int col = 0; col < PATHFINDER_COLS; col++) { - if (row + col == target_len) { - env->state.goal_row = row; - env->state.goal_col = col; - return; - } - } - } - env->state.goal_row = PATHFINDER_ROWS - 1; - env->state.goal_col = PATHFINDER_COLS - 1; -} - -static void bench_reset_components(Pathfinder* env, long iters) { - double t0 = now_seconds(); - for (long i = 0; i < iters; i++) { - memset(&env->state, 0, sizeof(env->state)); - } - double memset_sec = now_seconds() - t0; - - t0 = now_seconds(); - for (long i = 0; i < iters; i++) { - memset(&env->state, 0, sizeof(env->state)); - env->state.agent_row = 0; - env->state.agent_col = 0; - generate_maze(env); - } - double gen_sec = now_seconds() - t0; - - t0 = now_seconds(); - for (long i = 0; i < iters; i++) { - update_observations(env); - } - double obs_sec = now_seconds() - t0; - - printf("reset_components iters=%ld memset_ns=%.2f gen_plus_memset_ns=%.2f obs_ns=%.2f\n", - iters, - 1e9 * memset_sec / (double)iters, - 1e9 * gen_sec / (double)iters, - 1e9 * obs_sec / (double)iters); - - t0 = now_seconds(); - for (long i = 0; i < iters; i++) { - init_walls(&env->state); - } - double init_walls_sec = now_seconds() - t0; - - t0 = now_seconds(); - for (long i = 0; i < iters; i++) { - bench_choose_goal_at_distance(env, 4); - } - double choose_goal_sec = now_seconds() - t0; - - init_walls(&env->state); - bench_choose_goal_at_distance(env, 4); - t0 = now_seconds(); - for (long i = 0; i < iters; i++) { - init_walls(&env->state); - env->state.goal_row = 2; - env->state.goal_col = 2; - carve_solution(env, 4); - } - double carve_sec = now_seconds() - t0; - - init_walls(&env->state); - env->state.goal_row = 2; - env->state.goal_col = 2; - carve_solution(env, 4); - t0 = now_seconds(); - for (long i = 0; i < iters; i++) { - open_random_edges(env, 4); - } - double random_edges_sec = now_seconds() - t0; - - printf("generation_components iters=%ld init_walls_ns=%.2f choose_goal_ns=%.2f init_plus_carve_ns=%.2f random_edges_ns=%.2f\n", - iters, - 1e9 * init_walls_sec / (double)iters, - 1e9 * choose_goal_sec / (double)iters, - 1e9 * carve_sec / (double)iters, - 1e9 * random_edges_sec / (double)iters); -} - -static void setup_open_line(Pathfinder* env) { - State* s = &env->state; - memset(s, 0, sizeof(*s)); - memset(s->true_walls, 1, sizeof(s->true_walls)); - for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { - s->known_walls[i] = PATHFINDER_UNKNOWN; - } - s->agent_row = 0; - s->agent_col = 0; - s->goal_row = 5; - s->goal_col = 5; - for (int col = 0; col < PATHFINDER_COLS - 1; col++) { - open_edge(s, 0, col, 0, col + 1); - } - mark_visited(s, 0, 0); - update_observations(env); -} - -static void bench_forced_steps(Pathfinder* env, long iters) { - setup_open_line(env); - double t0 = now_seconds(); - for (long i = 0; i < iters; i++) { - if (env->state.agent_col == PATHFINDER_COLS - 1) { - setup_open_line(env); - } - env->actions[0] = PATHFINDER_ACT_EAST; - c_step(env); - } - double move_sec = now_seconds() - t0; - - memset(&env->log, 0, sizeof(env->log)); - setup_open_line(env); - int east_wall = wall_idx_between(0, 0, 0, 1); - env->state.true_walls[east_wall] = 1; - update_observations(env); - t0 = now_seconds(); - for (long i = 0; i < iters; i++) { - env->actions[0] = PATHFINDER_ACT_EAST; - c_step(env); - if (env->terminals[0] == 0.0f) { - c_step(env); - } - setup_open_line(env); - env->state.true_walls[east_wall] = 1; - update_observations(env); - } - double known_wall_death_sec = now_seconds() - t0; - - printf("forced_step_components iters=%ld open_move_ns=%.2f known_wall_death_cycle_ns=%.2f\n", - iters, - 1e9 * move_sec / (double)iters, - 1e9 * known_wall_death_sec / (double)iters); -} - -int main(int argc, char** argv) { - long resets = 200000; - long steps = 5000000; - for (int i = 1; i < argc; i++) { - if (strcmp(argv[i], "--resets") == 0 && i + 1 < argc) { - resets = parse_long(argv[++i], resets); - } else if (strcmp(argv[i], "--steps") == 0 && i + 1 < argc) { - steps = parse_long(argv[++i], steps); - } else { - fprintf(stderr, "Usage: %s [--resets N] [--steps N]\n", argv[0]); - return 1; - } - } - - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE] = {0}; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - - bench_resets(&env, resets, 0); - bench_resets(&env, resets, PATHFINDER_MAX_SOLUTION_LEN); - bench_reset_components(&env, resets); - bench_forced_steps(&env, resets); - - memset(&env.log, 0, sizeof(env.log)); - env.curriculum_level = 0; - env.curriculum_episodes = 0; - bench_steps(&env, steps); - c_close(&env); - return 0; -} diff --git a/ocean/pathfinder/tests/benchmark_pathfinder.py b/ocean/pathfinder/tests/benchmark_pathfinder.py deleted file mode 100755 index 84c7367514..0000000000 --- a/ocean/pathfinder/tests/benchmark_pathfinder.py +++ /dev/null @@ -1,163 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import json -import sys -import time -from copy import deepcopy - -from pufferlib.pufferl import _resolve_backend, load_config, unroll_nested_dict - - -def base_args(): - saved_argv = sys.argv - try: - sys.argv = [saved_argv[0]] - args = load_config("pathfinder") - finally: - sys.argv = saved_argv - args["wandb"] = False - args["render_mode"] = "None" - args["checkpoint_interval"] = 10**12 - return args - - -def run_once(label, args, timesteps, eval_epochs_override): - cfg = deepcopy(args) - cfg["train"]["total_timesteps"] = timesteps - cfg["train"]["gpus"] = 1 - cfg["nccl_id"] = b"" - backend = _resolve_backend(cfg) - pufferl = backend.create_pufferl(cfg) - train_epochs = max(1, timesteps // (cfg["vec"]["total_agents"] * cfg["train"]["horizon"])) - flat = {} - eval_flat = {} - t0 = time.perf_counter() - train_t0 = t0 - try: - for _ in range(train_epochs): - backend.rollouts(pufferl) - backend.train(pufferl) - flat = {**flat, **dict(unroll_nested_dict(backend.log(pufferl)))} - train_elapsed = time.perf_counter() - train_t0 - train_steps = int(pufferl.global_step) - - eval_epochs = train_epochs // 2 if eval_epochs_override < 0 else eval_epochs_override - for _ in range(eval_epochs): - backend.rollouts(pufferl) - eval_flat = {**eval_flat, **dict(unroll_nested_dict(backend.eval_log(pufferl)))} - finally: - actual_steps = int(pufferl.global_step) - backend.close(pufferl) - - elapsed = time.perf_counter() - t0 - stats = eval_flat if eval_flat else flat - summary = { - "label": label, - "timesteps_requested": timesteps, - "train_timesteps_actual": train_steps, - "timesteps_actual": actual_steps, - "train_epochs": train_epochs, - "eval_epochs": train_epochs // 2 if eval_epochs_override < 0 else eval_epochs_override, - "train_seconds": train_elapsed, - "seconds": elapsed, - "wall_sps": train_steps / train_elapsed if train_elapsed > 0 else 0.0, - "total_wall_sps": actual_steps / elapsed if elapsed > 0 else 0.0, - "reported_sps": flat.get("SPS", 0.0), - "success": stats.get("env/success", 0.0), - "wins": stats.get("env/wins", 0.0), - "score": stats.get("env/score", 0.0), - "episode_return": stats.get("env/episode_return", 0.0), - "episode_length": stats.get("env/episode_length", 0.0), - "wall_hits": stats.get("env/wall_hits", 0.0), - "known_wall_deaths": stats.get("env/known_wall_deaths", 0.0), - "repeat_move_deaths": stats.get("env/repeat_move_deaths", 0.0), - "revisits": stats.get("env/revisits", 0.0), - "shortest_path_len": stats.get("env/shortest_path_len", 0.0), - "agent_path_len": stats.get("env/agent_path_len", 0.0), - "curriculum_level": stats.get("env/curriculum_level", 0.0), - "curriculum_target_len": stats.get("env/curriculum_target_len", 0.0), - "curriculum_next_target_len": stats.get("env/curriculum_next_target_len", 0.0), - "perf_rollout_sec": flat.get("perf/rollout", 0.0), - "perf_eval_env_sec": flat.get("perf/eval_env", 0.0), - "perf_train_sec": flat.get("perf/train", 0.0), - "policy_hidden_size": cfg["policy"]["hidden_size"], - "policy_num_layers": cfg["policy"]["num_layers"], - "total_agents": cfg["vec"]["total_agents"], - "horizon": cfg["train"]["horizon"], - "minibatch_size": cfg["train"]["minibatch_size"], - } - return summary - - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument("--timesteps", type=int, default=2097152) - parser.add_argument("--eval-epochs", type=int, default=-1) - parser.add_argument("--hidden-size", type=int, default=None) - parser.add_argument("--num-layers", type=int, default=None) - parser.add_argument("--total-agents", type=int, default=None) - parser.add_argument("--horizon", type=int, default=None) - parser.add_argument("--minibatch-size", type=int, default=None) - parser.add_argument("--compare-hidden-size", type=int, default=None) - parser.add_argument("--min-wall-sps", type=float, default=0.0) - parser.add_argument("--min-success", type=float, default=0.0) - parser.add_argument("--require-sps-gain", type=float, default=0.0) - parser.add_argument("--require-success-ratio", type=float, default=0.0) - opts = parser.parse_args() - - args = base_args() - if opts.hidden_size is not None: - args["policy"]["hidden_size"] = opts.hidden_size - if opts.num_layers is not None: - args["policy"]["num_layers"] = opts.num_layers - if opts.total_agents is not None: - args["vec"]["total_agents"] = opts.total_agents - if opts.horizon is not None: - args["train"]["horizon"] = opts.horizon - if opts.minibatch_size is not None: - args["train"]["minibatch_size"] = opts.minibatch_size - - if opts.compare_hidden_size is None: - single = run_once("single", args, opts.timesteps, opts.eval_epochs) - result = { - "runs": [single], - "checks": { - "passes_min_wall_sps": single["wall_sps"] >= opts.min_wall_sps, - "passes_min_success": single["success"] >= opts.min_success, - }, - } - if not result["checks"]["passes_min_wall_sps"] or not result["checks"]["passes_min_success"]: - print(json.dumps(result, indent=2, sort_keys=True)) - return 2 - else: - baseline = run_once("baseline", args, opts.timesteps, opts.eval_epochs) - candidate_args = deepcopy(args) - candidate_args["policy"]["hidden_size"] = opts.compare_hidden_size - candidate = run_once("candidate", candidate_args, opts.timesteps, opts.eval_epochs) - sps_gain = ( - candidate["wall_sps"] / baseline["wall_sps"] - 1.0 - if baseline["wall_sps"] > 0.0 else 0.0 - ) - success_ratio = ( - candidate["success"] / baseline["success"] - if baseline["success"] > 0.0 else 1.0 - ) - result = { - "runs": [baseline, candidate], - "comparison": { - "sps_gain": sps_gain, - "success_ratio": success_ratio, - "passes_sps_gain": sps_gain >= opts.require_sps_gain, - "passes_success_ratio": success_ratio >= opts.require_success_ratio, - }, - } - if not result["comparison"]["passes_sps_gain"] or not result["comparison"]["passes_success_ratio"]: - print(json.dumps(result, indent=2, sort_keys=True)) - return 2 - - print(json.dumps(result, indent=2, sort_keys=True)) - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/ocean/pathfinder/tests/benchmark_pathfinder.sh b/ocean/pathfinder/tests/benchmark_pathfinder.sh deleted file mode 100755 index d2180636e0..0000000000 --- a/ocean/pathfinder/tests/benchmark_pathfinder.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -cd "$(dirname "$0")/../../.." - -mkdir -p build/pathfinder-tests - -${CC:-clang} -std=c11 -O3 -DNDEBUG -Wall -Wextra -Werror \ - -I. -Iocean/pathfinder -Ivendor -Iraylib-5.5_linux_amd64/include \ - ocean/pathfinder/tests/bench_pathfinder.c \ - -lm \ - -o build/pathfinder-tests/bench_pathfinder - -build/pathfinder-tests/bench_pathfinder "$@" - -if [[ "${PATHFINDER_TRAIN_BENCH:-0}" != "0" ]]; then - source .venv/bin/activate - ./build.sh pathfinder - python ocean/pathfinder/tests/benchmark_pathfinder.py \ - --timesteps "${PATHFINDER_TRAIN_TIMESTEPS:-2097152}" \ - ${PATHFINDER_TRAIN_BENCH_ARGS:-} -fi diff --git a/ocean/pathfinder/tests/run_all.sh b/ocean/pathfinder/tests/run_all.sh deleted file mode 100644 index 3196fd0a64..0000000000 --- a/ocean/pathfinder/tests/run_all.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -cd "$(dirname "$0")/../../.." - -mkdir -p build/pathfinder-tests - -${CC:-clang} -std=c11 -Wall -Wextra -Werror \ - -I. -Iocean/pathfinder -Ivendor -Iraylib-5.5_linux_amd64/include \ - -DPATHFINDER_NO_RENDER \ - ocean/pathfinder/tests/test_pathfinder_core.c \ - -lm \ - -o build/pathfinder-tests/test_pathfinder_core - -build/pathfinder-tests/test_pathfinder_core diff --git a/ocean/pathfinder/tests/test_pathfinder_core.c b/ocean/pathfinder/tests/test_pathfinder_core.c deleted file mode 100644 index 6004b47249..0000000000 --- a/ocean/pathfinder/tests/test_pathfinder_core.c +++ /dev/null @@ -1,821 +0,0 @@ -#include -#include -#include -#include - -#include "../pathfinder.h" - -static void setup_env(Pathfinder* env, float* obs, float* actions, - float* rewards, float* terminals) { - memset(env, 0, sizeof(*env)); - env->observations = obs; - env->actions = actions; - env->rewards = rewards; - env->terminals = terminals; - env->num_agents = 1; - env->branch_prob = 0.35f; - env->loop_prob = 0.10f; - env->start_solution_len = 1; - env->curriculum_enabled = 1; - env->max_steps = 128; - env->step_penalty = -0.001f; - env->new_wall_penalty = 0.0f; - env->known_wall_death_penalty = -1.0f; - env->repeat_move_death_penalty = -1.0f; - env->new_cell_reward = 0.01f; - env->revisit_penalty = -0.01f; - env->impossible_penalty = -1.0f; - env->goal_reward = 1.0f; - env->rng = 7; - init(env); -} - -static void setup_manual_state(Pathfinder* env, int goal_row, int goal_col) { - State* s = &env->state; - memset(s, 0, sizeof(*s)); - memset(s->true_walls, 1, sizeof(s->true_walls)); - for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { - s->known_walls[i] = PATHFINDER_UNKNOWN; - } - s->agent_row = 0; - s->agent_col = 0; - s->goal_row = goal_row; - s->goal_col = goal_col; - reset_move_history(s); - mark_visited(s, 0, 0); -} - -static void open_manual_edge(Pathfinder* env, int row, int col, int next_row, int next_col) { - open_edge(&env->state, row, col, next_row, next_col); - env->state.shortest_path_len = shortest_path(&env->state); - puffer_state_refresh(env); -} - -static void assert_wall_observations_unknown(Pathfinder* env, float* obs) { - for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { - assert(fabsf(env->state.known_walls[i] - PATHFINDER_UNKNOWN) < 1e-6f); - assert(fabsf(obs[i] - PATHFINDER_UNKNOWN) < 1e-6f); - } -} - -static void test_constants(void) { - assert(PATHFINDER_ROWS == 6); - assert(PATHFINDER_COLS == 6); - assert(PATHFINDER_NUM_WALLS == 84); - assert(PATHFINDER_OBS_SIZE == 86); - assert(PATHFINDER_NUM_ACTIONS == 4); - assert(PATHFINDER_RENDER_WIDTH > PATHFINDER_RENDER_BOARD_X + PATHFINDER_RENDER_BOARD_SIZE); - assert(PATHFINDER_RENDER_HEIGHT > PATHFINDER_RENDER_BOARD_Y + PATHFINDER_RENDER_BOARD_SIZE); -} - -static void test_wall_indexing_and_action_deltas(void) { - assert(v_wall_idx(0, 0) == 0); - assert(v_wall_idx(5, 6) == PATHFINDER_VERTICAL_WALLS - 1); - assert(h_wall_idx(0, 0) == PATHFINDER_VERTICAL_WALLS); - assert(h_wall_idx(6, 5) == PATHFINDER_NUM_WALLS - 1); - assert(wall_idx_between(0, 0, 0, 1) == v_wall_idx(0, 1)); - assert(wall_idx_between(0, 1, 0, 0) == v_wall_idx(0, 1)); - assert(wall_idx_between(0, 0, 1, 0) == h_wall_idx(1, 0)); - assert(wall_idx_between(1, 0, 0, 0) == h_wall_idx(1, 0)); - assert(wall_idx_between(0, 0, 1, 1) == -1); - - int d_row; - int d_col; - action_delta(PATHFINDER_ACT_NORTH, &d_row, &d_col); - assert(d_row == -1 && d_col == 0); - action_delta(PATHFINDER_ACT_EAST, &d_row, &d_col); - assert(d_row == 0 && d_col == 1); - action_delta(PATHFINDER_ACT_SOUTH, &d_row, &d_col); - assert(d_row == 1 && d_col == 0); - action_delta(PATHFINDER_ACT_WEST, &d_row, &d_col); - assert(d_row == 0 && d_col == -1); -} - -static void test_wall_between_bidirectional_vertical_edges(void) { - for (int row = 0; row < PATHFINDER_ROWS; row++) { - for (int col = 0; col < PATHFINDER_COLS - 1; col++) { - int wall_idx = v_wall_idx(row, col + 1); - assert(wall_idx_between(row, col, row, col + 1) == wall_idx); - assert(wall_idx_between(row, col + 1, row, col) == wall_idx); - } - } -} - -static void test_wall_between_bidirectional_horizontal_edges(void) { - for (int row = 0; row < PATHFINDER_ROWS - 1; row++) { - for (int col = 0; col < PATHFINDER_COLS; col++) { - int wall_idx = h_wall_idx(row + 1, col); - assert(wall_idx_between(row, col, row + 1, col) == wall_idx); - assert(wall_idx_between(row + 1, col, row, col) == wall_idx); - } - } -} - -static void test_wall_between_boundary_steps_to_outer_edges(void) { - for (int row = 0; row < PATHFINDER_ROWS; row++) { - for (int col = 0; col < PATHFINDER_COLS; col++) { - assert(wall_idx_between(row, 0, row, -1) == v_wall_idx(row, 0)); - assert(wall_idx_between(row, PATHFINDER_COLS - 1, row, PATHFINDER_COLS) == v_wall_idx(row, PATHFINDER_COLS)); - } - } - - for (int col = 0; col < PATHFINDER_COLS; col++) { - assert(wall_idx_between(0, col, -1, col) == h_wall_idx(0, col)); - assert(wall_idx_between(PATHFINDER_ROWS - 1, col, PATHFINDER_ROWS, col) == h_wall_idx(PATHFINDER_ROWS, col)); - } -} - -static void test_wall_between_rejects_non_adjacent_positions(void) { - assert(wall_idx_between(0, 0, 1, 1) == -1); - assert(wall_idx_between(0, 0, 2, 0) == -1); - assert(wall_idx_between(0, 0, 0, 2) == -1); - assert(wall_idx_between(2, 2, 0, 2) == -1); - assert(wall_idx_between(2, 2, 5, 2) == -1); - assert(wall_idx_between(0, 0, -2, 0) == -1); - assert(wall_idx_between(5, 5, 7, 5) == -1); - assert(wall_idx_between(3, 3, 3, 8) == -1); -} - -static void test_reset_initializes_a1_and_unknown_walls(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - - c_reset(&env); - - assert(env.state.agent_row == 0); - assert(env.state.agent_col == 0); - assert(env.state.visited[0][0] == 1); - assert(env.state.visited_count == 1); - assert(fabsf(obs[PATHFINDER_NUM_WALLS] - 0.0f) < 1e-6f); - assert(fabsf(obs[PATHFINDER_NUM_WALLS + 1] - 0.0f) < 1e-6f); - for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { - assert(fabsf(obs[i] + 1.0f) < 1e-6f); - } -} - -static void test_known_wall_death_restarts_same_map_with_unknown_wall_memory(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - unsigned char true_walls[PATHFINDER_NUM_WALLS]; - setup_env(&env, obs, actions, rewards, terminals); - setup_manual_state(&env, 5, 5); - puffer_state_refresh(&env); - - int east_wall = wall_idx_between(0, 0, 0, 1); - memcpy(true_walls, env.state.true_walls, sizeof(true_walls)); - - actions[0] = PATHFINDER_ACT_EAST; - c_step(&env); - - assert(terminals[0] == 0.0f); - assert(fabsf(obs[east_wall] - PATHFINDER_WALL) < 1e-6f); - - c_step(&env); - - assert(terminals[0] == 1.0f); - assert(env.log.n == 1.0f); - assert(env.log.success == 0.0f); - assert(env.log.known_wall_deaths == 1.0f); - assert(env.state.tick == 0); - assert(env.state.agent_row == 0); - assert(env.state.agent_col == 0); - assert(env.state.goal_row == 5); - assert(env.state.goal_col == 5); - assert(env.state.visited_count == 1); - assert(env.state.visited[0][0] == 1); - assert(memcmp(env.state.true_walls, true_walls, sizeof(true_walls)) == 0); - assert_wall_observations_unknown(&env, obs); -} - -static void test_position_observation_updates_after_move(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - setup_manual_state(&env, 5, 5); - open_manual_edge(&env, 0, 0, 0, 1); - - actions[0] = PATHFINDER_ACT_EAST; - c_step(&env); - - assert(fabsf(obs[PATHFINDER_NUM_WALLS] - 0.2f) < 1e-6f); - assert(fabsf(obs[PATHFINDER_NUM_WALLS + 1] - 0.0f) < 1e-6f); - assert(fabsf(rewards[0] - - (env.step_penalty + env.new_cell_reward)) < 1e-6f); - assert(env.state.visited[0][1] == 1); - assert(env.state.visited_count == 2); -} - -static void test_generated_mazes_connect_a1_to_goal(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - - for (int i = 0; i < 100; i++) { - c_reset(&env); - assert(shortest_path(&env.state) >= 0); - assert(env.state.shortest_path_len >= 0); - } -} - -static void test_generated_shortest_path_matches_goal_distance(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - env.start_solution_len = PATHFINDER_MAX_SOLUTION_LEN; - - for (int i = 0; i < 200; i++) { - c_reset(&env); - assert(env.state.shortest_path_len == shortest_path(&env.state)); - } -} - -static void test_shortest_path_start_equals_goal(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - setup_manual_state(&env, 0, 0); - - assert(shortest_path(&env.state) == 0); -} - -static void test_shortest_path_no_available_path_returns_minus_one(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - setup_manual_state(&env, 5, 5); - - assert(shortest_path(&env.state) == -1); -} - -static void test_shortest_path_one_step_path(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - setup_manual_state(&env, 0, 1); - open_manual_edge(&env, 0, 0, 0, 1); - - assert(shortest_path(&env.state) == 1); - assert(env.state.shortest_path_len == 1); -} - -static void test_shortest_path_prefers_shorter_of_multiple_routes(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - setup_manual_state(&env, 0, 2); - - open_manual_edge(&env, 0, 0, 0, 1); - open_manual_edge(&env, 0, 1, 0, 2); - - open_manual_edge(&env, 0, 0, 1, 0); - open_manual_edge(&env, 1, 0, 1, 1); - open_manual_edge(&env, 1, 1, 1, 2); - open_manual_edge(&env, 1, 2, 0, 2); - - assert(shortest_path(&env.state) == 2); - assert(env.state.shortest_path_len == 2); -} - -static void test_open_edge_reveals_and_moves(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - setup_manual_state(&env, 5, 5); - int east_wall = wall_idx_between(0, 0, 0, 1); - open_manual_edge(&env, 0, 0, 0, 1); - - actions[0] = PATHFINDER_ACT_EAST; - c_step(&env); - - assert(env.state.agent_row == 0); - assert(env.state.agent_col == 1); - assert(fabsf(obs[east_wall] - 0.0f) < 1e-6f); - assert(fabsf(rewards[0] - - (env.step_penalty + env.new_cell_reward)) < 1e-6f); - assert(terminals[0] == 0.0f); -} - -static void test_blocked_edge_reveals_and_stays(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - setup_manual_state(&env, 5, 5); - int east_wall = wall_idx_between(0, 0, 0, 1); - puffer_state_refresh(&env); - - actions[0] = PATHFINDER_ACT_EAST; - c_step(&env); - - assert(env.state.agent_row == 0); - assert(env.state.agent_col == 0); - assert(fabsf(obs[east_wall] - 1.0f) < 1e-6f); - assert(fabsf(rewards[0] - env.step_penalty) < 1e-6f); - assert(terminals[0] == 0.0f); - assert(env.state.known_wall_death == 0); -} - -static void test_known_wall_repeat_terminates_with_penalty(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - setup_manual_state(&env, 5, 5); - puffer_state_refresh(&env); - - actions[0] = PATHFINDER_ACT_EAST; - c_step(&env); - assert(fabsf(rewards[0] - env.step_penalty) < 1e-6f); - assert(terminals[0] == 0.0f); - - c_step(&env); - assert(fabsf(rewards[0] - - (env.step_penalty + env.known_wall_death_penalty)) < 1e-6f); - assert(terminals[0] == 1.0f); - assert(env.log.n >= 1.0f); - assert(env.log.success == 0.0f); - assert(env.log.known_wall_deaths == 1.0f); -} - -static void test_invalid_action_penalizes_and_terminates(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - setup_manual_state(&env, 5, 5); - puffer_state_refresh(&env); - - actions[0] = 99; - c_step(&env); - - assert(env.state.agent_row == 0); - assert(env.state.agent_col == 0); - assert(fabsf(rewards[0] - - (env.step_penalty + env.impossible_penalty)) < 1e-6f); - assert(terminals[0] == 1.0f); - assert(env.log.n == 1.0f); - assert(env.log.success == 0.0f); -} - -static void test_configured_reward_constants_are_used(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - setup_manual_state(&env, 0, 1); - open_manual_edge(&env, 0, 0, 0, 1); - env.step_penalty = -2.0f; - env.new_cell_reward = 0.5f; - env.goal_reward = 3.0f; - - actions[0] = PATHFINDER_ACT_EAST; - c_step(&env); - - assert(fabsf(rewards[0] - (env.step_penalty + env.new_cell_reward + env.goal_reward)) < 1e-6f); - assert(env.log.success == 1.0f); -} - -static void test_configured_impossible_penalty_is_used(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - setup_manual_state(&env, 5, 5); - env.impossible_penalty = -4.0f; - - actions[0] = 99; - c_step(&env); - - assert(terminals[0] == 1.0f); - assert(fabsf(rewards[0] - (env.step_penalty + env.impossible_penalty)) < 1e-6f); - assert(env.log.n == 1.0f); -} - -static void test_boundary_action_terminates_with_impossible_penalty(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - setup_manual_state(&env, 5, 5); - puffer_state_refresh(&env); - - actions[0] = PATHFINDER_ACT_NORTH; - c_step(&env); - - assert(env.state.agent_row == 0); - assert(env.state.agent_col == 0); - assert(fabsf(rewards[0] - (env.step_penalty + env.impossible_penalty)) < 1e-6f); - assert(terminals[0] == 1.0f); - assert(fabsf(obs[PATHFINDER_NUM_WALLS] - 0.0f) < 1e-6f); - assert(fabsf(obs[PATHFINDER_NUM_WALLS + 1] - 0.0f) < 1e-6f); -} - -static void test_revisiting_previously_left_square_has_penalty(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - setup_manual_state(&env, 5, 5); - open_manual_edge(&env, 0, 0, 0, 1); - - actions[0] = PATHFINDER_ACT_EAST; - c_step(&env); - assert(fabsf(rewards[0] - - (env.step_penalty + env.new_cell_reward)) < 1e-6f); - - actions[0] = PATHFINDER_ACT_WEST; - c_step(&env); - assert(fabsf(rewards[0] - - (env.step_penalty + env.revisit_penalty)) < 1e-6f); - assert(terminals[0] == 0.0f); - assert(env.state.revisit_count == 1); -} - -static void test_repeating_directed_move_dies_and_restarts_same_map_blind(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - unsigned char true_walls[PATHFINDER_NUM_WALLS]; - setup_env(&env, obs, actions, rewards, terminals); - setup_manual_state(&env, 5, 5); - open_manual_edge(&env, 0, 0, 0, 1); - int east_wall = wall_idx_between(0, 0, 0, 1); - memcpy(true_walls, env.state.true_walls, sizeof(true_walls)); - - actions[0] = PATHFINDER_ACT_EAST; - c_step(&env); - assert(terminals[0] == 0.0f); - assert(env.state.agent_col == 1); - assert(fabsf(obs[east_wall] - PATHFINDER_OPEN) < 1e-6f); - - actions[0] = PATHFINDER_ACT_WEST; - c_step(&env); - assert(terminals[0] == 0.0f); - assert(env.state.agent_col == 0); - assert(env.state.revisit_count == 1); - - actions[0] = PATHFINDER_ACT_EAST; - c_step(&env); - - assert(terminals[0] == 1.0f); - assert(rewards[0] <= -1.0f); - assert(env.log.n == 1.0f); - assert(env.log.success == 0.0f); - assert(env.log.repeat_move_deaths == 1.0f); - assert(env.state.tick == 0); - assert(env.state.agent_row == 0); - assert(env.state.agent_col == 0); - assert(env.state.goal_row == 5); - assert(env.state.goal_col == 5); - assert(memcmp(env.state.true_walls, true_walls, sizeof(true_walls)) == 0); - assert_wall_observations_unknown(&env, obs); -} - -static void test_longer_backtrack_then_forward_move_is_allowed(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - setup_manual_state(&env, 5, 5); - open_manual_edge(&env, 0, 0, 0, 1); - open_manual_edge(&env, 0, 1, 0, 2); - - actions[0] = PATHFINDER_ACT_EAST; - c_step(&env); - assert(terminals[0] == 0.0f); - actions[0] = PATHFINDER_ACT_EAST; - c_step(&env); - assert(terminals[0] == 0.0f); - actions[0] = PATHFINDER_ACT_WEST; - c_step(&env); - assert(terminals[0] == 0.0f); - actions[0] = PATHFINDER_ACT_WEST; - c_step(&env); - assert(terminals[0] == 0.0f); - - actions[0] = PATHFINDER_ACT_EAST; - c_step(&env); - - assert(terminals[0] == 0.0f); - assert(env.state.agent_row == 0); - assert(env.state.agent_col == 1); - assert(fabsf(rewards[0] - - (env.step_penalty + env.revisit_penalty)) < 1e-6f); -} - -static void test_known_open_edge_to_new_square_has_no_extra_penalty(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - setup_manual_state(&env, 5, 5); - open_manual_edge(&env, 0, 0, 0, 1); - open_manual_edge(&env, 0, 1, 0, 2); - - actions[0] = PATHFINDER_ACT_EAST; - c_step(&env); - int second_wall = wall_idx_between(0, 1, 0, 2); - env.state.known_walls[second_wall] = PATHFINDER_OPEN; - puffer_state_refresh(&env); - - actions[0] = PATHFINDER_ACT_EAST; - c_step(&env); - - assert(env.state.agent_col == 2); - assert(fabsf(rewards[0] - - (env.step_penalty + env.new_cell_reward)) < 1e-6f); - assert(env.state.revisit_count == 0); - assert(terminals[0] == 0.0f); -} - -static void test_start_solution_len_sets_curriculum_distance(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - env.start_solution_len = 2; - - for (int i = 0; i < 100; i++) { - c_reset(&env); - assert(shortest_path(&env.state) >= 0); - assert(env.state.shortest_path_len == env.start_solution_len); - } -} - -static void test_curriculum_uses_configured_start_solution_len(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - env.branch_prob = 0.0f; - env.loop_prob = 0.0f; - env.start_solution_len = 4; - - for (int i = 0; i < 100; i++) { - c_reset(&env); - assert(env.state.shortest_path_len == env.start_solution_len); - } -} - -static void test_curriculum_disabled_starts_at_board_max_and_does_not_advance(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - env.branch_prob = 0.0f; - env.loop_prob = 0.0f; - env.start_solution_len = 4; - env.curriculum_enabled = 0; - - c_reset(&env); - assert(env.state.shortest_path_len == PATHFINDER_MAX_SOLUTION_LEN); - - setup_manual_state(&env, 0, 1); - open_manual_edge(&env, 0, 0, 0, 1); - actions[0] = PATHFINDER_ACT_EAST; - c_step(&env); - - assert(terminals[0] == 1.0f); - assert(fabsf(rewards[0] - - (env.step_penalty + env.new_cell_reward + env.goal_reward)) < 1e-6f); - assert(env.log.success == 1.0f); - assert(env.curriculum_level == 0); - assert(env.state.shortest_path_len == PATHFINDER_MAX_SOLUTION_LEN); -} - -static void test_success_generates_next_map_one_step_farther(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - env.branch_prob = 0.0f; - env.loop_prob = 0.0f; - env.start_solution_len = 1; - setup_manual_state(&env, 0, 1); - open_manual_edge(&env, 0, 0, 0, 1); - - actions[0] = PATHFINDER_ACT_EAST; - c_step(&env); - - assert(terminals[0] == 1.0f); - assert(env.log.success == 1.0f); - assert(env.start_solution_len + env.curriculum_level == 2); - assert(env.state.agent_row == 0); - assert(env.state.agent_col == 0); - assert(env.state.shortest_path_len == 2); - assert(env.state.goal_row + env.state.goal_col == 2); - for (int i = 0; i < PATHFINDER_NUM_WALLS; i++) { - assert(fabsf(env.state.known_walls[i] - PATHFINDER_UNKNOWN) < 1e-6f); - } -} - -static void test_failure_retry_does_not_graduate_curriculum(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - unsigned char true_walls[PATHFINDER_NUM_WALLS]; - setup_env(&env, obs, actions, rewards, terminals); - env.start_solution_len = 4; - setup_manual_state(&env, 5, 5); - puffer_state_refresh(&env); - memcpy(true_walls, env.state.true_walls, sizeof(true_walls)); - - actions[0] = PATHFINDER_ACT_EAST; - c_step(&env); - c_step(&env); - - assert(terminals[0] == 1.0f); - assert(env.curriculum_level == 0); - assert(env.start_solution_len + env.curriculum_level == 4); - assert(memcmp(env.state.true_walls, true_walls, sizeof(true_walls)) == 0); -} - -static void test_curriculum_caps_at_board_max(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - env.start_solution_len = 4; - env.curriculum_level = PATHFINDER_MAX_SOLUTION_LEN - env.start_solution_len; - - assert(env.start_solution_len + env.curriculum_level == PATHFINDER_MAX_SOLUTION_LEN); - assert(current_target_solution_len(&env) == PATHFINDER_MAX_SOLUTION_LEN); -} - -static void test_generation_can_reach_board_max_distance(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - env.branch_prob = 0.0f; - env.loop_prob = 0.0f; - env.start_solution_len = PATHFINDER_MAX_SOLUTION_LEN; - - for (int i = 0; i < 200; i++) { - c_reset(&env); - assert(env.state.shortest_path_len >= 1); - assert(env.state.shortest_path_len <= PATHFINDER_MAX_SOLUTION_LEN); - assert(shortest_path(&env.state) >= 0); - } -} - -static void test_timeout_restarts_same_map_with_unknown_wall_memory(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - unsigned char true_walls[PATHFINDER_NUM_WALLS]; - setup_env(&env, obs, actions, rewards, terminals); - env.max_steps = 1; - setup_manual_state(&env, 5, 5); - open_manual_edge(&env, 0, 0, 0, 1); - memcpy(true_walls, env.state.true_walls, sizeof(true_walls)); - - actions[0] = PATHFINDER_ACT_EAST; - c_step(&env); - - assert(terminals[0] == 1.0f); - assert(fabsf(rewards[0] - - (env.step_penalty + env.new_cell_reward)) < 1e-6f); - assert(env.log.n == 1.0f); - assert(env.log.success == 0.0f); - assert(env.log.episode_length == 1.0f); - assert(env.state.tick == 0); - assert(env.state.agent_row == 0); - assert(env.state.agent_col == 0); - assert(env.state.goal_row == 5); - assert(env.state.goal_col == 5); - assert(env.state.visited_count == 1); - assert(env.state.visited[0][0] == 1); - assert(env.state.visited[0][1] == 0); - assert(memcmp(env.state.true_walls, true_walls, sizeof(true_walls)) == 0); - assert_wall_observations_unknown(&env, obs); -} - -static void test_reaching_goal_terminates(void) { - Pathfinder env; - float obs[PATHFINDER_OBS_SIZE]; - float actions[1] = {0}; - float rewards[1] = {0}; - float terminals[1] = {0}; - setup_env(&env, obs, actions, rewards, terminals); - setup_manual_state(&env, 0, 1); - open_manual_edge(&env, 0, 0, 0, 1); - - actions[0] = PATHFINDER_ACT_EAST; - c_step(&env); - - assert(terminals[0] == 1.0f); - assert(fabsf(rewards[0] - - (env.step_penalty + env.new_cell_reward + - env.goal_reward)) < 1e-6f); - assert(env.log.success >= 1.0f); - assert(env.log.n >= 1.0f); - assert(shortest_path(&env.state) >= 0); -} - -int main(void) { - test_constants(); - test_wall_indexing_and_action_deltas(); - test_wall_between_bidirectional_vertical_edges(); - test_wall_between_bidirectional_horizontal_edges(); - test_wall_between_boundary_steps_to_outer_edges(); - test_wall_between_rejects_non_adjacent_positions(); - test_reset_initializes_a1_and_unknown_walls(); - test_known_wall_death_restarts_same_map_with_unknown_wall_memory(); - test_position_observation_updates_after_move(); - test_generated_mazes_connect_a1_to_goal(); - test_generated_shortest_path_matches_goal_distance(); - test_shortest_path_start_equals_goal(); - test_shortest_path_no_available_path_returns_minus_one(); - test_shortest_path_one_step_path(); - test_shortest_path_prefers_shorter_of_multiple_routes(); - test_open_edge_reveals_and_moves(); - test_blocked_edge_reveals_and_stays(); - test_known_wall_repeat_terminates_with_penalty(); - test_invalid_action_penalizes_and_terminates(); - test_configured_reward_constants_are_used(); - test_configured_impossible_penalty_is_used(); - test_boundary_action_terminates_with_impossible_penalty(); - test_revisiting_previously_left_square_has_penalty(); - test_repeating_directed_move_dies_and_restarts_same_map_blind(); - test_longer_backtrack_then_forward_move_is_allowed(); - test_known_open_edge_to_new_square_has_no_extra_penalty(); - test_start_solution_len_sets_curriculum_distance(); - test_curriculum_uses_configured_start_solution_len(); - test_curriculum_disabled_starts_at_board_max_and_does_not_advance(); - test_success_generates_next_map_one_step_farther(); - test_failure_retry_does_not_graduate_curriculum(); - test_curriculum_caps_at_board_max(); - test_generation_can_reach_board_max_distance(); - test_timeout_restarts_same_map_with_unknown_wall_memory(); - test_reaching_goal_terminates(); - printf("pathfinder core tests passed\n"); - return 0; -} diff --git a/uv.lock b/uv.lock deleted file mode 100644 index 940189f1b7..0000000000 --- a/uv.lock +++ /dev/null @@ -1,1456 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.10" -resolution-markers = [ - "python_full_version >= '3.12'", - "python_full_version == '3.11.*'", - "python_full_version < '3.11'", -] - -[[package]] -name = "annotated-types" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, -] - -[[package]] -name = "certifi" -version = "2026.5.20" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/08/0f303cb0b529e456bb116f2d50565a482694fbb94340bf56d44677e7ed03/charset_normalizer-3.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d", size = 315182, upload-time = "2026-04-02T09:25:40.673Z" }, - { url = "https://files.pythonhosted.org/packages/24/47/b192933e94b546f1b1fe4df9cc1f84fcdbf2359f8d1081d46dd029b50207/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8", size = 209329, upload-time = "2026-04-02T09:25:42.354Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b4/01fa81c5ca6141024d89a8fc15968002b71da7f825dd14113207113fabbd/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790", size = 231230, upload-time = "2026-04-02T09:25:44.281Z" }, - { url = "https://files.pythonhosted.org/packages/20/f7/7b991776844dfa058017e600e6e55ff01984a063290ca5622c0b63162f68/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc", size = 225890, upload-time = "2026-04-02T09:25:45.475Z" }, - { url = "https://files.pythonhosted.org/packages/20/e7/bed0024a0f4ab0c8a9c64d4445f39b30c99bd1acd228291959e3de664247/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393", size = 216930, upload-time = "2026-04-02T09:25:46.58Z" }, - { url = "https://files.pythonhosted.org/packages/e2/ab/b18f0ab31cdd7b3ddb8bb76c4a414aeb8160c9810fdf1bc62f269a539d87/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153", size = 202109, upload-time = "2026-04-02T09:25:48.031Z" }, - { url = "https://files.pythonhosted.org/packages/82/e5/7e9440768a06dfb3075936490cb82dbf0ee20a133bf0dd8551fa096914ec/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af", size = 214684, upload-time = "2026-04-02T09:25:49.245Z" }, - { url = "https://files.pythonhosted.org/packages/71/94/8c61d8da9f062fdf457c80acfa25060ec22bf1d34bbeaca4350f13bcfd07/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34", size = 212785, upload-time = "2026-04-02T09:25:50.671Z" }, - { url = "https://files.pythonhosted.org/packages/66/cd/6e9889c648e72c0ab2e5967528bb83508f354d706637bc7097190c874e13/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1", size = 203055, upload-time = "2026-04-02T09:25:51.802Z" }, - { url = "https://files.pythonhosted.org/packages/92/2e/7a951d6a08aefb7eb8e1b54cdfb580b1365afdd9dd484dc4bee9e5d8f258/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752", size = 232502, upload-time = "2026-04-02T09:25:53.388Z" }, - { url = "https://files.pythonhosted.org/packages/58/d5/abcf2d83bf8e0a1286df55cd0dc1d49af0da4282aa77e986df343e7de124/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53", size = 214295, upload-time = "2026-04-02T09:25:54.765Z" }, - { url = "https://files.pythonhosted.org/packages/47/3a/7d4cd7ed54be99973a0dc176032cba5cb1f258082c31fa6df35cff46acfc/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616", size = 227145, upload-time = "2026-04-02T09:25:55.904Z" }, - { url = "https://files.pythonhosted.org/packages/1d/98/3a45bf8247889cf28262ebd3d0872edff11565b2a1e3064ccb132db3fbb0/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a", size = 218884, upload-time = "2026-04-02T09:25:57.074Z" }, - { url = "https://files.pythonhosted.org/packages/ad/80/2e8b7f8915ed5c9ef13aa828d82738e33888c485b65ebf744d615040c7ea/charset_normalizer-3.4.7-cp310-cp310-win32.whl", hash = "sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374", size = 148343, upload-time = "2026-04-02T09:25:58.199Z" }, - { url = "https://files.pythonhosted.org/packages/35/1b/3b8c8c77184af465ee9ad88b5aea46ea6b2e1f7b9dc9502891e37af21e30/charset_normalizer-3.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943", size = 159174, upload-time = "2026-04-02T09:25:59.322Z" }, - { url = "https://files.pythonhosted.org/packages/be/c1/feb40dca40dbb21e0a908801782d9288c64fc8d8e562c2098e9994c8c21b/charset_normalizer-3.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008", size = 147805, upload-time = "2026-04-02T09:26:00.756Z" }, - { url = "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", size = 309705, upload-time = "2026-04-02T09:26:02.191Z" }, - { url = "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", size = 206419, upload-time = "2026-04-02T09:26:03.583Z" }, - { url = "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", size = 227901, upload-time = "2026-04-02T09:26:04.738Z" }, - { url = "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", size = 222742, upload-time = "2026-04-02T09:26:06.36Z" }, - { url = "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", size = 214061, upload-time = "2026-04-02T09:26:08.347Z" }, - { url = "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", size = 199239, upload-time = "2026-04-02T09:26:09.823Z" }, - { url = "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", size = 210173, upload-time = "2026-04-02T09:26:10.953Z" }, - { url = "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", size = 209841, upload-time = "2026-04-02T09:26:12.142Z" }, - { url = "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", size = 200304, upload-time = "2026-04-02T09:26:13.711Z" }, - { url = "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", size = 229455, upload-time = "2026-04-02T09:26:14.941Z" }, - { url = "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", size = 210036, upload-time = "2026-04-02T09:26:16.478Z" }, - { url = "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", size = 224739, upload-time = "2026-04-02T09:26:17.751Z" }, - { url = "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", size = 216277, upload-time = "2026-04-02T09:26:18.981Z" }, - { url = "https://files.pythonhosted.org/packages/06/6d/3be70e827977f20db77c12a97e6a9f973631a45b8d186c084527e53e77a4/charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad", size = 147819, upload-time = "2026-04-02T09:26:20.295Z" }, - { url = "https://files.pythonhosted.org/packages/20/d9/5f67790f06b735d7c7637171bbfd89882ad67201891b7275e51116ed8207/charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00", size = 159281, upload-time = "2026-04-02T09:26:21.74Z" }, - { url = "https://files.pythonhosted.org/packages/ca/83/6413f36c5a34afead88ce6f66684d943d91f233d76dd083798f9602b75ae/charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1", size = 147843, upload-time = "2026-04-02T09:26:22.901Z" }, - { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, - { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, - { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, - { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, - { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, - { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, - { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, - { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, - { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, - { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, - { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, - { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, - { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, - { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, - { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, - { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, - { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, - { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, - { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, - { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, - { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, - { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, - { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, - { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, - { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, - { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, - { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, - { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, - { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, - { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, - { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, - { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, - { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, - { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, - { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, - { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, - { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, - { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, - { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, - { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, - { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, - { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, - { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, - { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, - { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, - { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, - { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, - { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, - { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, - { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, - { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, - { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, - { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, - { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, - { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, - { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, - { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, - { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, - { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, - { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, -] - -[[package]] -name = "click" -version = "8.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9b/98/518d8e5081007684232226f475082b30087d0f585e8457db087298259f49/click-8.4.1.tar.gz", hash = "sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96", size = 353007, upload-time = "2026-05-22T04:08:37.769Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/0d/67e5b4109ea4a837e80daa87c2c696711955e40449a97e8926672534def2/click-8.4.1-py3-none-any.whl", hash = "sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2", size = 116639, upload-time = "2026-05-22T04:08:35.26Z" }, -] - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } -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 = "cuda-bindings" -version = "12.9.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cuda-pathfinder" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/d8/b546104b8da3f562c1ff8ab36d130c8fe1dd6a045ced80b4f6ad74f7d4e1/cuda_bindings-12.9.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d3c842c2a4303b2a580fe955018e31aea30278be19795ae05226235268032e5", size = 12148218, upload-time = "2025-10-21T14:51:28.855Z" }, - { url = "https://files.pythonhosted.org/packages/45/e7/b47792cc2d01c7e1d37c32402182524774dadd2d26339bd224e0e913832e/cuda_bindings-12.9.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c912a3d9e6b6651853eed8eed96d6800d69c08e94052c292fec3f282c5a817c9", size = 12210593, upload-time = "2025-10-21T14:51:36.574Z" }, - { url = "https://files.pythonhosted.org/packages/a9/c1/dabe88f52c3e3760d861401bb994df08f672ec893b8f7592dc91626adcf3/cuda_bindings-12.9.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fda147a344e8eaeca0c6ff113d2851ffca8f7dfc0a6c932374ee5c47caa649c8", size = 12151019, upload-time = "2025-10-21T14:51:43.167Z" }, - { url = "https://files.pythonhosted.org/packages/63/56/e465c31dc9111be3441a9ba7df1941fe98f4aa6e71e8788a3fb4534ce24d/cuda_bindings-12.9.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:32bdc5a76906be4c61eb98f546a6786c5773a881f3b166486449b5d141e4a39f", size = 11906628, upload-time = "2025-10-21T14:51:49.905Z" }, - { url = "https://files.pythonhosted.org/packages/a3/84/1e6be415e37478070aeeee5884c2022713c1ecc735e6d82d744de0252eee/cuda_bindings-12.9.4-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56e0043c457a99ac473ddc926fe0dc4046694d99caef633e92601ab52cbe17eb", size = 11925991, upload-time = "2025-10-21T14:51:56.535Z" }, - { url = "https://files.pythonhosted.org/packages/d1/af/6dfd8f2ed90b1d4719bc053ff8940e494640fe4212dc3dd72f383e4992da/cuda_bindings-12.9.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8b72ee72a9cc1b531db31eebaaee5c69a8ec3500e32c6933f2d3b15297b53686", size = 11922703, upload-time = "2025-10-21T14:52:03.585Z" }, - { url = "https://files.pythonhosted.org/packages/6c/19/90ac264acc00f6df8a49378eedec9fd2db3061bf9263bf9f39fd3d8377c3/cuda_bindings-12.9.4-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d80bffc357df9988dca279734bc9674c3934a654cab10cadeed27ce17d8635ee", size = 11924658, upload-time = "2025-10-21T14:52:10.411Z" }, -] - -[[package]] -name = "cuda-pathfinder" -version = "1.5.5" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/11/c8/26f2e4aae92f11522a96043892ba39a90eac610d5242523aa863212bc1c7/cuda_pathfinder-1.5.5-py3-none-any.whl", hash = "sha256:0228c023f95d1480f143ef5c8922d27a2ab052087a942e81dc289c9eb8f91689", size = 51671, upload-time = "2026-05-27T01:21:25.413Z" }, -] - -[[package]] -name = "filelock" -version = "3.29.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1f/f9/f38573ed5844586db374d085911740a501ccfa373b455fc9413f09f85237/filelock-3.29.1.tar.gz", hash = "sha256:d97e6b1b9757569626c58caa07dc4beb1613f4a2938b1e8cc81afca398906c9e", size = 59335, upload-time = "2026-06-03T15:19:04.053Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4c/a0/614c5fe402fd88951df45f4dda2fa3b4e17a99ecd92340771929169b3b95/filelock-3.29.1-py3-none-any.whl", hash = "sha256:85199dfd706869641b72b2e8955d5416a4b2b7dc4b0e8e6d97b4cc1299a6983b", size = 40750, upload-time = "2026-06-03T15:19:02.959Z" }, -] - -[[package]] -name = "fsspec" -version = "2026.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d5/8d/1c51c094345df128ca4a990d633fe1a0ff28726c9e6b3c41ba65087bba1d/fsspec-2026.4.0.tar.gz", hash = "sha256:301d8ac70ae90ef3ad05dcf94d6c3754a097f9b5fe4667d2787aa359ec7df7e4", size = 312760, upload-time = "2026-04-29T20:42:38.635Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/0c/043d5e551459da400957a1395e0febbf771446ff34291afcbe3d8be2a279/fsspec-2026.4.0-py3-none-any.whl", hash = "sha256:11ef7bb35dab8a394fde6e608221d5cf3e8499401c249bebaeaad760a1a8dec2", size = 203402, upload-time = "2026-04-29T20:42:36.842Z" }, -] - -[[package]] -name = "gitdb" -version = "4.0.12" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "smmap" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" }, -] - -[[package]] -name = "gitpython" -version = "3.1.50" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "gitdb" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/33/f6/354ae6491228b5eb40e10d89c4d13c651fe1cf7556e35ebdded50cff57ce/gitpython-3.1.50.tar.gz", hash = "sha256:80da2d12504d52e1f998772dc5baf6e553f8d2fcfe1fcc226c9d9a2ee3372dcc", size = 219798, upload-time = "2026-05-06T04:01:26.571Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/7a/1c6e3562dfd8950adbb11ffbc65d21e7c89d01a6e4f137fa981056de25c5/gitpython-3.1.50-py3-none-any.whl", hash = "sha256:d352abe2908d07355014abdd21ddf798c2a961469239afec4962e9da884858f9", size = 212507, upload-time = "2026-05-06T04:01:23.799Z" }, -] - -[[package]] -name = "gpytorch" -version = "1.15.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "linear-operator" }, - { name = "mpmath" }, - { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scikit-learn", version = "1.9.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.11'" }, - { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/23/00/cac3e309b2f66518d937ce4d9fa821f167d9070d004fcb070b1780cd1d94/gpytorch-1.15.2.tar.gz", hash = "sha256:380625e93f851b85f772b25c5fb0a6c6d2e3eb2ef667f1e566ab4f95b8775361", size = 2781831, upload-time = "2026-02-28T01:12:55.172Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/70/c419a361b09ccb618b5165daa919003ccdd9c148356f7ff71a710db6bcf0/gpytorch-1.15.2-py3-none-any.whl", hash = "sha256:2112fdc7c0c0bf56a7f2444663cfc80fdfc3e19724399d6303a83d8efdd71e9e", size = 291209, upload-time = "2026-02-28T01:12:52.587Z" }, -] - -[[package]] -name = "idna" -version = "3.18" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" }, -] - -[[package]] -name = "jinja2" -version = "3.1.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, -] - -[[package]] -name = "joblib" -version = "1.5.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603, upload-time = "2025-12-15T08:41:46.427Z" } -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 = "linear-operator" -version = "0.6.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "torch" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/02/1a/0d6d0269cf7327e31a2fe80b274d5bf5001dc4462acbef26240da6ac5dfe/linear_operator-0.6.1.tar.gz", hash = "sha256:3fba49a8080d16f822a5d870f462279cd6afbcf4ed670f4511b38fad96f61831", size = 181898, upload-time = "2026-02-27T23:43:18.716Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/34/ee74708a93704baca5c1ef439817c4cb649f73382c518380ccc5ab335603/linear_operator-0.6.1-py3-none-any.whl", hash = "sha256:a5981c1fcda08df3a210dffb6e8019b4751f4afaf3ffc822c24eaaf56b11eed9", size = 174770, upload-time = "2026-02-27T23:43:17.074Z" }, -] - -[[package]] -name = "markdown-it-py" -version = "4.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mdurl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, -] - -[[package]] -name = "markupsafe" -version = "3.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631, upload-time = "2025-09-27T18:36:05.558Z" }, - { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057, upload-time = "2025-09-27T18:36:07.165Z" }, - { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050, upload-time = "2025-09-27T18:36:08.005Z" }, - { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681, upload-time = "2025-09-27T18:36:08.881Z" }, - { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705, upload-time = "2025-09-27T18:36:10.131Z" }, - { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524, upload-time = "2025-09-27T18:36:11.324Z" }, - { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282, upload-time = "2025-09-27T18:36:12.573Z" }, - { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745, upload-time = "2025-09-27T18:36:13.504Z" }, - { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571, upload-time = "2025-09-27T18:36:14.779Z" }, - { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056, upload-time = "2025-09-27T18:36:16.125Z" }, - { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932, upload-time = "2025-09-27T18:36:17.311Z" }, - { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" }, - { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" }, - { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" }, - { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" }, - { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" }, - { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" }, - { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" }, - { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" }, - { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" }, - { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" }, - { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" }, - { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, - { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, - { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, - { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, - { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, - { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, - { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, - { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, - { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, - { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, - { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, - { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, - { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, - { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, - { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, - { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, - { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, - { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, - { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, - { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, - { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, - { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, - { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, - { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, - { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, - { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, - { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, - { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, - { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, - { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, - { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, - { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, - { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, - { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, - { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, - { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, - { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, - { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, - { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, - { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, - { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, - { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, - { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, - { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, - { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, - { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, - { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, - { 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" }, -] - -[[package]] -name = "mdurl" -version = "0.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, -] - -[[package]] -name = "mpmath" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, -] - -[[package]] -name = "narwhals" -version = "2.22.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9c/1c/c80cb7719721a44846c6301ef118434bae30a423924bfad3a47f16bdc064/narwhals-2.22.0.tar.gz", hash = "sha256:6486282bb7e4b4ab55963efbd8be1451b764cc4874b74d1fd625eba9dc60b86f", size = 417565, upload-time = "2026-06-01T13:34:36.249Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/b6/e7cdde7b8e90d5dff25b622f95833ef26567ad184c977278b93a1cbd5717/narwhals-2.22.0-py3-none-any.whl", hash = "sha256:1421797ede01789cc1537619dbc3f36f840737240f748fdb24a60a0225fc80be", size = 453815, upload-time = "2026-06-01T13:34:34.127Z" }, -] - -[[package]] -name = "networkx" -version = "3.4.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "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 = [ - { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263, upload-time = "2024-10-21T12:39:36.247Z" }, -] - -[[package]] -name = "networkx" -version = "3.6.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.12'", - "python_full_version == '3.11.*'", -] -sdist = { url = "https://files.pythonhosted.org/packages/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.2.6" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "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 = [ - { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, - { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, - { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, - { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, - { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, - { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, - { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, - { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, - { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" }, - { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" }, - { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" }, - { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" }, - { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" }, - { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" }, - { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" }, - { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" }, - { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" }, - { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" }, - { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" }, - { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" }, - { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, - { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, - { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, - { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, - { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, - { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, - { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, - { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, - { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, - { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, - { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, - { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, - { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, - { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, - { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, - { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, - { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, - { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, - { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" }, - { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" }, - { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, - { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, - { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, - { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, - { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, - { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, - { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, - { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" }, - { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, - { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, - { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, - { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" }, -] - -[[package]] -name = "numpy" -version = "2.4.6" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.12'", - "python_full_version == '3.11.*'", -] -sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/49/ec46835a70be8fa6446c495126ac84fdb28cb2558e1620ffb87a10c8b64c/numpy-2.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0280e0356c0829a18d9de1cb7eee50ec22ca639878d7240307ca0943d73cd2c4", size = 16969194, upload-time = "2026-05-18T23:33:13.503Z" }, - { url = "https://files.pythonhosted.org/packages/0e/0d/f5957185c0ee2f3e12f78715aa9e3b353fd83633316c8532b38faa37e3f6/numpy-2.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110f8b71aacb688ec69062bb7f6938a0f8acb01b7c1c4beb453c65b6d234584d", size = 14964111, upload-time = "2026-05-18T23:33:17.795Z" }, - { url = "https://files.pythonhosted.org/packages/ad/40/40a40ee0ddf7ceb782c49af278894b686e586d65d8c1889c8b5da01a3d7d/numpy-2.4.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4cfe66903cc32a9921a6733d96b19bb6abf310397581bbad89c228f5abaf0ee8", size = 5469159, upload-time = "2026-05-18T23:33:20.654Z" }, - { url = "https://files.pythonhosted.org/packages/63/13/f9a8046535cb21deae82f8d03de9617e08882d274fad2539630761888228/numpy-2.4.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8155154c7c691289fe18f510b5d4657c68c67989f293f0535a91360392ff6538", size = 6798936, upload-time = "2026-05-18T23:33:22.987Z" }, - { url = "https://files.pythonhosted.org/packages/33/a8/6fa8c1a345a8c85dbb21932c447bee07c30a2c2a3f31e369c0a84b300147/numpy-2.4.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab0a9c4ffb1a6d95ef519fe4247dba8eb6b18ad93999f76b7f657039acabd47", size = 15966692, upload-time = "2026-05-18T23:33:26.62Z" }, - { url = "https://files.pythonhosted.org/packages/02/03/74fe2a4cb3817d94d86402f2506554130a2f01414e299b5a843e5a8a957f/numpy-2.4.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cd468399cfd2504718f0ba50e410dca55a170b61a02ad92bb18c8a65186e93", size = 16918164, upload-time = "2026-05-18T23:33:29.955Z" }, - { url = "https://files.pythonhosted.org/packages/c5/80/3615be3313f7e7696609bc194b9f0101da809df79e859bdb84e0cd043f46/numpy-2.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2d37ab77531417474168eb79d6d80b14f821a966818505d03013d0833edb7a8", size = 17322877, upload-time = "2026-05-18T23:33:34.724Z" }, - { url = "https://files.pythonhosted.org/packages/ca/ac/a691e0fe2675e370d0e08ff905adc49a1c8830e8cae03efe4477e92cd55d/numpy-2.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f407cb6b8e9d6d8c626bc73c945db1706035af8fd632295547bf1c9e46d092d6", size = 18651487, upload-time = "2026-05-18T23:33:38.217Z" }, - { url = "https://files.pythonhosted.org/packages/15/a7/9bc1cd626d7bf6869bfedf27b91b6ab5dd607758bf8e959d6fa80c6a59cb/numpy-2.4.6-cp311-cp311-win32.whl", hash = "sha256:ddea102b48f9e339f3948bf22040944184627a30fdf7f858667673b9c5f033c8", size = 6233945, upload-time = "2026-05-18T23:33:41.331Z" }, - { url = "https://files.pythonhosted.org/packages/c5/31/7fc6239c12bce7e931463251cca4426c465e1876ba3cc785402ef4dd8f4e/numpy-2.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:1e254a00cdf42b1e4d5b3d68d33af63268d41340d8885df2ab6470f2e1500147", size = 12608406, upload-time = "2026-05-18T23:33:44.131Z" }, - { url = "https://files.pythonhosted.org/packages/27/83/140f85a466595a16382996a1bf06b2b54bcd597488921b0c9daaeeda72af/numpy-2.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:ed9749eef4cbd126da3dc1d6bcb3a57f5eb7ac6a6484146bdbf743f552dfc577", size = 10479528, upload-time = "2026-05-18T23:33:50.725Z" }, - { url = "https://files.pythonhosted.org/packages/95/2a/3d7b5ac8aac24feaf9ad7ed58f45b0bbc06d37e4338ae84c9f2298b570f9/numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1", size = 16689119, upload-time = "2026-05-18T23:33:54.065Z" }, - { url = "https://files.pythonhosted.org/packages/ea/12/92c4c131527599e8288d6918e888d88726f84d805d784b771f32408aeaef/numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb", size = 14699246, upload-time = "2026-05-18T23:33:57.621Z" }, - { url = "https://files.pythonhosted.org/packages/ad/fe/c0a6b7b2ca128a8fb228575147073b660656734b8ebe4d76c8fd748dcc79/numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41", size = 5204410, upload-time = "2026-05-18T23:34:00.302Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d4/9770d14ba719432bb90a421bfd443872ed0f70f7264b64bec12ea363d5fd/numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698", size = 6551240, upload-time = "2026-05-18T23:34:02.852Z" }, - { url = "https://files.pythonhosted.org/packages/c9/c6/50a46a6205feba2343f1d6d17438107c5dc491ed1c736e6ea68689fd906b/numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f", size = 15671012, upload-time = "2026-05-18T23:34:05.485Z" }, - { url = "https://files.pythonhosted.org/packages/99/60/14115e6364fa676c5397c2ad3004e527e9aa487abf5d0706ec81bbd08529/numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853", size = 16645538, upload-time = "2026-05-18T23:34:09.265Z" }, - { url = "https://files.pythonhosted.org/packages/ae/c5/693cbe59e57db94d2231fa519ca3978dc9e19da5a8f088588f5c6e947ff2/numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a", size = 17020706, upload-time = "2026-05-18T23:34:13.053Z" }, - { url = "https://files.pythonhosted.org/packages/ef/fc/85b7c4eff9b4966ade25c2273cf7e7012e92366c032058653934b37de044/numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2", size = 18368541, upload-time = "2026-05-18T23:34:17.024Z" }, - { url = "https://files.pythonhosted.org/packages/f6/81/e1b27545deedce7f4a0b348618c6b62d74e36a4dc9ccd42f3eb2f85eee32/numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45", size = 5962825, upload-time = "2026-05-18T23:34:20.3Z" }, - { url = "https://files.pythonhosted.org/packages/ab/ca/feab00bd44aa5fe1ad2c18f08b4d3bb92e26484b0b1d1443897809ed528c/numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751", size = 12321687, upload-time = "2026-05-18T23:34:23.095Z" }, - { url = "https://files.pythonhosted.org/packages/63/cf/5a6d34850a39d1093558564f77ee8e8e0bee5061151b8f05a55711001ec7/numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8", size = 10221482, upload-time = "2026-05-18T23:34:25.876Z" }, - { url = "https://files.pythonhosted.org/packages/fb/82/bdab26d7438c6791ca31b7c024ca37c1eab8b726ba236129005cd4a06e45/numpy-2.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:511dbaf848decaaaf4b4ca48032619fb3138710c4bf7da7617765edad1ef96b0", size = 16684648, upload-time = "2026-05-18T23:34:29.41Z" }, - { url = "https://files.pythonhosted.org/packages/1b/30/a80189bcc7f5e4258b3fbc3968d909d1756f54d023299ecc39ad6fdb9ef8/numpy-2.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf162abab1c1a736333192707cef898e735a5ca00f38f27eeedf44b39d9e85eb", size = 14693902, upload-time = "2026-05-18T23:34:33.013Z" }, - { url = "https://files.pythonhosted.org/packages/97/12/70b5d0d7c15e1ebb8a6a84a8caa1d19e181d84fb58bb6d70aca29099dec1/numpy-2.4.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:043191bfa8eab18c776647b62723ac9dddece59743b13f49b2016094129c2b3f", size = 5198992, upload-time = "2026-05-18T23:34:36.132Z" }, - { url = "https://files.pythonhosted.org/packages/ba/8c/ebd2a8f8a83541f8d38cc5667e8c2b69cecfd30da6e45693e8158857d44b/numpy-2.4.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6180d8b35af935aed8ece3a85e0a43f87393ae0ac87c8d2c8bd2c993f7270ef3", size = 6546944, upload-time = "2026-05-18T23:34:38.484Z" }, - { url = "https://files.pythonhosted.org/packages/bb/c5/7b863a97a91671a0338f4253bd3b5a3d3852f0692dae91711c9f4a10e787/numpy-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72fbe16c6fac95aedf5937fa873445cec2110be35d8a4e9433d7501fd98dae6b", size = 15669392, upload-time = "2026-05-18T23:34:41.257Z" }, - { url = "https://files.pythonhosted.org/packages/a5/9d/3584b9984ca4c047aea75214ce1a4c4c73d849bd71b604264b7f5653f8a8/numpy-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7830bab239b79cda9c08c2da014761cafb48da6150e1da17ac06283f43b6089", size = 16633220, upload-time = "2026-05-18T23:34:45.075Z" }, - { url = "https://files.pythonhosted.org/packages/05/ae/7c67fba23bd98caec7c99261f3a16072ade14813486b0282cb29846de832/numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef4aea96ce4d3b074422cb4f2f64e216bf9e213004bb58ecfdf50ea02ea8eb9a", size = 17020800, upload-time = "2026-05-18T23:34:49.065Z" }, - { url = "https://files.pythonhosted.org/packages/d9/5d/3b6725cb31d983c5e66916f5d36f6d7e5521129e4c4404d64f918292a5b6/numpy-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfa20cc6ca228e6b155b11da03825975ce66aea520985dbbddf0f2a5a495c605", size = 18357600, upload-time = "2026-05-18T23:34:52.709Z" }, - { url = "https://files.pythonhosted.org/packages/f7/da/2ccc6c2fe8898dee01d90c75c5f5f914a23daf99e3e0f59516a08760c8b5/numpy-2.4.6-cp313-cp313-win32.whl", hash = "sha256:56b39e5e0622a09a25bf5baf62f4bcf0cb8a41ae6e2819cf49bbc5a74c083f91", size = 5961134, upload-time = "2026-05-18T23:34:55.618Z" }, - { url = "https://files.pythonhosted.org/packages/b5/cd/9cc4dc876fb065d5c220aae4d5e14826b2715331bb7618ce1fb07a679d99/numpy-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:c4fc99836233ea196540b17ab0983aff60ed07941751930f5f4d05bc3b3b7359", size = 12318598, upload-time = "2026-05-18T23:34:58.928Z" }, - { url = "https://files.pythonhosted.org/packages/39/1e/c0bcba1f8694116485fe28fd1be698c278fcda4141c5b0e53a2aed8b12a8/numpy-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a7c711e21628b52034bb5ab8d1bce291f752fcc5e92accc615778acee1ff4778", size = 10222272, upload-time = "2026-05-18T23:35:02.167Z" }, - { url = "https://files.pythonhosted.org/packages/63/6d/cc5619247c8f4204e507f5883528372e4ac4bb189e579fb859a12e480b1f/numpy-2.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:112b06a867b235ef466ed3508ddf0238050df9c727cafb5301ac385b899189a1", size = 14821197, upload-time = "2026-05-18T23:35:05.468Z" }, - { url = "https://files.pythonhosted.org/packages/00/58/f1c39161c87d9e9bed660f1ed4bafc0e403d5ec9650b6dd77aead07d489b/numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:eaf7fa2de5c0be8ae6ff8e9bea2ccd725e980541244521d8d4b5f3354a27babe", size = 5326287, upload-time = "2026-05-18T23:35:08.693Z" }, - { url = "https://files.pythonhosted.org/packages/af/57/3917ab0fd97f271a8694513581b8a36c655f111c446852c302f04ccdb6fc/numpy-2.4.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7265a2f3d436e54ef9f2b52b5c937e6be778781bd97a590319d7348f1c1ca997", size = 6646763, upload-time = "2026-05-18T23:35:11.459Z" }, - { url = "https://files.pythonhosted.org/packages/eb/0f/037e64c494b67581ae18193d770adef354c41f3f2c8ebf865602d949bf8f/numpy-2.4.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20", size = 15728070, upload-time = "2026-05-18T23:35:14.79Z" }, - { url = "https://files.pythonhosted.org/packages/21/a6/5d2bae9c9542eb4df16dc9c46dc79c186e9bad53805dfa5399a6023c6db0/numpy-2.4.6-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ede83e07a75dd06bc501566c1eca2afc0d61677c1472ac9ad93fdee6e638a48d", size = 16681752, upload-time = "2026-05-18T23:35:18.836Z" }, - { url = "https://files.pythonhosted.org/packages/92/14/23d1dfb410ae362cd59ce53e936b1513d545eb40db3949ced632e19a459e/numpy-2.4.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:68bb27509ac1b9a3443094260f6326150663b06abe40b73a2f81160623da5b67", size = 17086024, upload-time = "2026-05-18T23:35:22.52Z" }, - { url = "https://files.pythonhosted.org/packages/4b/6e/23595a2c642cdf3bc567877064bdd7f91c8b0038a4453cf2daf7248eafe9/numpy-2.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a0df0043bdb289bde1f62da130d20df23d58b45429f752bc7a8fc5325a225ecd", size = 18403398, upload-time = "2026-05-18T23:35:26.398Z" }, - { url = "https://files.pythonhosted.org/packages/8a/90/0ac3bc947217e66dec77e7cbc6a1979d1af70b6461b82f620d3bccd5e4c8/numpy-2.4.6-cp313-cp313t-win32.whl", hash = "sha256:29a287e0cf63ff528da061de6b9f64a4618da591ca1046aafc54062e40ca7eab", size = 6084971, upload-time = "2026-05-18T23:35:29.387Z" }, - { url = "https://files.pythonhosted.org/packages/77/71/5673e351671a1d2bd6063b91b44f70c0affea7d1516fa7a6572941ba4aa1/numpy-2.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:25c692919ac5a01f170a3bfcd62d745b24fd095c353d50812637d6fcab442e75", size = 12458532, upload-time = "2026-05-18T23:35:32.175Z" }, - { url = "https://files.pythonhosted.org/packages/3f/88/19d3503c5046e688f049274b27a3ef3d771152fa80d3ba3d01a3dff61abe/numpy-2.4.6-cp313-cp313t-win_arm64.whl", hash = "sha256:1e978ec1e8bd0e0e4de6bb75de9d30cbb74db6b6a2bb727618613703ca0167dd", size = 10291881, upload-time = "2026-05-18T23:35:35.465Z" }, - { url = "https://files.pythonhosted.org/packages/f8/91/3ab2044d05fd16d343c5ac2e69b127f1b2854040dd20b193257c78028bd3/numpy-2.4.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06ca2f61ec4385a07a6977c55ba998a4466c123642b4a32694d3128fce18c079", size = 16683458, upload-time = "2026-05-18T23:35:38.353Z" }, - { url = "https://files.pythonhosted.org/packages/8e/62/764ce66fa4147ae6d73071a3abf804ffe606f174618697c571acdf26a7c9/numpy-2.4.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:38efbc8de75c7a0fc1ac190162d892787f3f47b57cc291231aafee36b80982b7", size = 14704559, upload-time = "2026-05-18T23:35:42.14Z" }, - { url = "https://files.pythonhosted.org/packages/60/61/23f27c172f022e04025b7dc2367f4d63c1a398120607ec896228649a6f48/numpy-2.4.6-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:d581b735e177fdcdce6fed8e7e8880a3fb6ee4e3653a3ac6af01c6f4c03effc5", size = 5209716, upload-time = "2026-05-18T23:35:45.377Z" }, - { url = "https://files.pythonhosted.org/packages/03/71/21cf70dc6ea3e3acb95fc53a265b2fc248b981f0194ceb5b475271b8809d/numpy-2.4.6-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:0a041d3d761dc3c35cc56ce0351506a02bcbc25f7b169f652435141a17db9096", size = 6543947, upload-time = "2026-05-18T23:35:47.926Z" }, - { url = "https://files.pythonhosted.org/packages/d5/91/64288395ee1799bd2e0b04a305dce9666da90c961e1f3fe982a05ee1c036/numpy-2.4.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40fdc1ae7125e518ea98e53e69a4ebc27e1fd50510c47b7ea130cf21e5e1d42b", size = 15685197, upload-time = "2026-05-18T23:35:50.863Z" }, - { url = "https://files.pythonhosted.org/packages/f3/eb/ebffaa97dc55502df69584a8f0dcf07f69a3e0b3e2323670a2722db9aa39/numpy-2.4.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c306dea656c12c68f51f4cea133cbe78ca7435eb28c735eac1d3ebe73be6e8", size = 16638245, upload-time = "2026-05-18T23:35:54.752Z" }, - { url = "https://files.pythonhosted.org/packages/b8/0b/54f9da33128d7e350fab89c7455902eeae70349ee52bddb448dc4a576f45/numpy-2.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:33111801a01c12a8a1e3721f0a9232f8cfc8ae2c6b7098167e6f623c6073f402", size = 17036587, upload-time = "2026-05-18T23:35:58.355Z" }, - { url = "https://files.pythonhosted.org/packages/b6/f0/fdebc1052db1cc37c64beb22072d67cd6d1c71adca1299f53dec2b5e20d3/numpy-2.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae506e6902902557576a26ff33eda8695e7ecb3cb36c3b573a0765dee114ebdb", size = 18363226, upload-time = "2026-05-18T23:36:02.845Z" }, - { url = "https://files.pythonhosted.org/packages/aa/b4/298628d98c72b57e57f7165ae6a481a1deaf6f3c28262a6e4c739c275930/numpy-2.4.6-cp314-cp314-win32.whl", hash = "sha256:aaf159caa35993cb1f56fb9b8e4610d35758e7ca005412eb1daa856a78c9c4b1", size = 6010196, upload-time = "2026-05-18T23:36:05.92Z" }, - { url = "https://files.pythonhosted.org/packages/df/ac/46de6dda46478f7942f839e094970be2d4a861e005c4b3bf07c92e291a09/numpy-2.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:b507f5c4c1d508876d1819b6bf9a49d365b96320b5d4993426b33a23ca4b8261", size = 12450334, upload-time = "2026-05-18T23:36:09.107Z" }, - { url = "https://files.pythonhosted.org/packages/78/92/b8b798ac784102c0da830d2257d59358e3d3d90d1e2b3f2575dad976c5cf/numpy-2.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:6f41ae150c4e32db4f3310cdaf64b1593a03dbabe29eec77fc9b50fe64061df6", size = 10495678, upload-time = "2026-05-18T23:36:12.766Z" }, - { url = "https://files.pythonhosted.org/packages/30/34/ec28d1aa8115971537c01469ab2011ee96827930f0a124de1000cc2a7ed7/numpy-2.4.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ece3d2cfe132e7d51f44a832b303895e6f2d499c5e74dfbdb06ee246147a304a", size = 14823672, upload-time = "2026-05-18T23:36:16.473Z" }, - { url = "https://files.pythonhosted.org/packages/16/bd/f6d1fede4e54e8042a7ff97bb495510f3c220f94bcd9e8b228e87c92cc0d/numpy-2.4.6-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e3e5193ef5a3dc73bceee50f7fdc2c90dbb76c42df8d8fae3d1067a583df579e", size = 5328731, upload-time = "2026-05-18T23:36:19.767Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f0/e105b9e2fd728a9910103884decd6951d9dd73896b914a98d9a231de02ee/numpy-2.4.6-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:17f9ade344e7d9b464a084d69bcf18fc691cb1db67c62ed80820bf4926d78f0e", size = 6649805, upload-time = "2026-05-18T23:36:22.266Z" }, - { url = "https://files.pythonhosted.org/packages/82/dd/1206a7ca6ab15e3f02069707ca96222e202af681bb73756da7527f3cb837/numpy-2.4.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd5ffd25db4e7ba6a375693b3fc0fc1791ec636c17db3720da19bde7180ec43", size = 15730496, upload-time = "2026-05-18T23:36:25.713Z" }, - { url = "https://files.pythonhosted.org/packages/51/e7/38d3ea825dcab85a591734decb2f6c67caa7c8367d374df1a1c3842f9b07/numpy-2.4.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d92c3819208a60205a12a245c91ad70cb0a85336659b19b834205573ac8456e", size = 16679616, upload-time = "2026-05-18T23:36:29.652Z" }, - { url = "https://files.pythonhosted.org/packages/93/b7/caabfdf53edf663e0b4eb74d7d405d83baef09eb5e83bcd32d601d72b93e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e85b752a1e912b70eaad4fafbd4d1238007ab221de2009b9a2f5ae7461239895", size = 17085145, upload-time = "2026-05-18T23:36:33.449Z" }, - { url = "https://files.pythonhosted.org/packages/f9/45/68d7c33a6bcf3e5aa3bdbd57a367e6f615286dfd6482f97e8ffeb734306e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:29cb7f67d10b479ff07c17d33e39f78c07f71c40ef30d63c153d340e96cd3fb4", size = 18403813, upload-time = "2026-05-18T23:36:37.369Z" }, - { url = "https://files.pythonhosted.org/packages/9c/50/0753655aa844c99cd9e018aacf76f130f1bd81d881bb74bc0aef5d73a8ba/numpy-2.4.6-cp314-cp314t-win32.whl", hash = "sha256:260a5d70215b61ab4fadf5c7baacd64821842975eea312125ed3c39a6391b063", size = 6156982, upload-time = "2026-05-18T23:36:40.817Z" }, - { url = "https://files.pythonhosted.org/packages/b2/d4/7c67becf668f973cb490cec3e98dfd799d866f9c989a54d355672cfa0db6/numpy-2.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:81a1cca95ed5bb92aa8b10dd2cdc9a0d3853a50fad926c28b5d7e8ea54389627", size = 12638908, upload-time = "2026-05-18T23:36:43.996Z" }, - { url = "https://files.pythonhosted.org/packages/43/bb/e1c71a4295b1b1d1393d50dbb4f2a36283c6859d9d3892e84f00ec5a91d5/numpy-2.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:0c9136e14ed34a9e343a31c533d78a9813a69a3148332bce5e9821cb2f996e66", size = 10565867, upload-time = "2026-05-18T23:36:47.114Z" }, - { url = "https://files.pythonhosted.org/packages/de/12/b422cc84439adc0d00de605bf4a308890ae5c26f2c71fbd73e5d08fbb0dd/numpy-2.4.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:55cced7c52e981362f708ad635198e97a752dfba412cc03c23bbf3bd8d5cd662", size = 16847511, upload-time = "2026-05-18T23:36:50.673Z" }, - { url = "https://files.pythonhosted.org/packages/44/53/f481bef68011740f8849418d82db07230e825013f31f4eef5ba5b805316a/numpy-2.4.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d6da64deb6b8ed903e7560180a92f2d804ee1ba5eeb849ac2748b8c1aba1f6d7", size = 14889064, upload-time = "2026-05-18T23:36:53.879Z" }, - { url = "https://files.pythonhosted.org/packages/7f/57/42ed575c10ced8af951d426bc4e1f8aff16fd851db33f067036215a7f860/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:68a5124b13fa6cc2086764a20005d30bc0548146f7f5322f02fce212ca14317f", size = 5394157, upload-time = "2026-05-18T23:36:57.194Z" }, - { url = "https://files.pythonhosted.org/packages/6a/ef/f66cc724fcc36c1e364c67f51ae9146090b8b584f27d58b97fdae3edd737/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:948424b06129ce883307e8cff868c31396d8dc7630a59c61d70d98dbe70f222c", size = 6708728, upload-time = "2026-05-18T23:36:59.575Z" }, - { url = "https://files.pythonhosted.org/packages/1a/9c/c531f2293b91265d8b48e9b329f54fdd7ffae73cb4134ea10cca4237e9cc/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbbdb29840ca3d91ee0fece42fc29278886d908280bfec0a5846c6f901a3eb0", size = 15798374, upload-time = "2026-05-18T23:37:02.674Z" }, - { url = "https://files.pythonhosted.org/packages/1a/b0/413077f6b1153ed3cba361401c6783bbad6114804a000cc22eb71c13e190/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ad03c0965fb3c692200e74d458ca28c1dbb4ce96f9a479a8aa041ad5fabca02", size = 16747286, upload-time = "2026-05-18T23:37:06.327Z" }, - { url = "https://files.pythonhosted.org/packages/15/ce/e5ec180bc41812edcd8daeb8639d205622c0e8c02259d8ab25a0201b3c2a/numpy-2.4.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2803abfebfc990042cd494d8ce2d5f82e9d847af6d35ec486923aa19dbad5e73", size = 12504263, upload-time = "2026-05-18T23:37:09.715Z" }, -] - -[[package]] -name = "nvidia-cublas-cu12" -version = "12.8.4.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" }, -] - -[[package]] -name = "nvidia-cuda-cupti-cu12" -version = "12.8.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" }, -] - -[[package]] -name = "nvidia-cuda-nvrtc-cu12" -version = "12.8.93" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" }, -] - -[[package]] -name = "nvidia-cuda-runtime-cu12" -version = "12.8.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" }, -] - -[[package]] -name = "nvidia-cudnn-cu12" -version = "9.10.2.21" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, -] - -[[package]] -name = "nvidia-cufft-cu12" -version = "11.3.3.83" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, -] - -[[package]] -name = "nvidia-cufile-cu12" -version = "1.13.1.3" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" }, -] - -[[package]] -name = "nvidia-curand-cu12" -version = "10.3.9.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" }, -] - -[[package]] -name = "nvidia-cusolver-cu12" -version = "11.7.3.90" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas-cu12" }, - { name = "nvidia-cusparse-cu12" }, - { name = "nvidia-nvjitlink-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, -] - -[[package]] -name = "nvidia-cusparse-cu12" -version = "12.5.8.93" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, -] - -[[package]] -name = "nvidia-cusparselt-cu12" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" }, -] - -[[package]] -name = "nvidia-nccl-cu12" -version = "2.27.5" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229, upload-time = "2025-06-26T04:11:28.385Z" }, -] - -[[package]] -name = "nvidia-nvjitlink-cu12" -version = "12.8.93" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" }, -] - -[[package]] -name = "nvidia-nvshmem-cu12" -version = "3.4.5" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/09/6ea3ea725f82e1e76684f0708bbedd871fc96da89945adeba65c3835a64c/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd", size = 139103095, upload-time = "2025-09-06T00:32:31.266Z" }, -] - -[[package]] -name = "nvidia-nvtx-cu12" -version = "12.8.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" }, -] - -[[package]] -name = "packaging" -version = "26.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, -] - -[[package]] -name = "platformdirs" -version = "4.10.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/47/e4501f49c178ae1d9f4a75073fda4204f52647993f075a9db4d14930e0c5/platformdirs-4.10.0.tar.gz", hash = "sha256:31e761a6a0ca04faf7353ea759bdba55652be214725111e5aac52dfa29d4bef7", size = 31224, upload-time = "2026-05-28T03:32:53.587Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/e6/cd9575ac904136b3cbf7aa7ee819ef86eedb7274e46f230e94ea4342e729/platformdirs-4.10.0-py3-none-any.whl", hash = "sha256:fb516cdb12eb0d857d0cd85a7c57cea4d060bee4578d6cf5a14dfdf8cbf8784a", size = 22743, upload-time = "2026-05-28T03:32:52.175Z" }, -] - -[[package]] -name = "protobuf" -version = "7.35.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/60/fd/5b1491d9e4b586d621c54f4c36b888714164b6875f8d6afa3f9072906a51/protobuf-7.35.0.tar.gz", hash = "sha256:a2efd84605f41e559f1881b0912b44099d0a2ac9bf46b3474823f10fb393b0e6", size = 458677, upload-time = "2026-05-19T23:02:29.197Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/83/ee/93d06e358a4aa32280b00e722d3ea0a1f25fc3cc5778d80581c9cca2c10e/protobuf-7.35.0-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:66be6c513931c794fa92c080ffee41671390da3d79da219cf9c0c0907f035dda", size = 433225, upload-time = "2026-05-19T23:02:19.884Z" }, - { url = "https://files.pythonhosted.org/packages/8b/39/1c76c2da93f3c507e958e0aecee2391cc44d4625de6c728bbc555195b5a8/protobuf-7.35.0-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:fcbe42a4ac09d3ec9c987ddfcd956afd0b15f1ff613bd8371bde9405ffd5c8e5", size = 328847, upload-time = "2026-05-19T23:02:22.3Z" }, - { url = "https://files.pythonhosted.org/packages/91/1a/39f7ce90a238c1a987a4d81ec26379e02ca0aff367de68e4a1fa474215b9/protobuf-7.35.0-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:4cbf5cc286130e06a6c9bbefac442431173906dfcc979712183d4adcc01b37ee", size = 344030, upload-time = "2026-05-19T23:02:23.591Z" }, - { url = "https://files.pythonhosted.org/packages/70/5b/6baf9008817964454055ff3fe65f1de0b5f1e26c80c82f7fb108b7cd4ea3/protobuf-7.35.0-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:6c0f98f10c8a05ea30f8993dfef2de093d27b490fdae78bb60c8343795d55011", size = 327130, upload-time = "2026-05-19T23:02:24.637Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e5/e46adb0badc388bfb84877a5f9f026aff63f60e611016cf64dbe77e05446/protobuf-7.35.0-cp310-abi3-win32.whl", hash = "sha256:4c4617b83ade0e279d1d2bfe04025a1adb87f9ed657de038620dc0ff959357f6", size = 428946, upload-time = "2026-05-19T23:02:25.741Z" }, - { url = "https://files.pythonhosted.org/packages/a7/ab/547fbd9e16d879dd13c167478f8ae0a83a428008ca07a5e06acdc23ad473/protobuf-7.35.0-cp310-abi3-win_amd64.whl", hash = "sha256:f05bcadf9a2a6b8dda047007075135fb7d08c73d9177aabc067e1be46881a201", size = 439996, upload-time = "2026-05-19T23:02:26.808Z" }, - { url = "https://files.pythonhosted.org/packages/b8/ef/50433d346c56657a70d27f156c7b349ac59a068b01de4eb796e747eecc43/protobuf-7.35.0-py3-none-any.whl", hash = "sha256:c13f325cf242bad135c350629eeb5d54b24228eb472fb3e2e9ebbd4c5dc20ca0", size = 171659, upload-time = "2026-05-19T23:02:27.842Z" }, -] - -[[package]] -name = "pufferlib" -version = "4.0.0" -source = { editable = "." } -dependencies = [ - { name = "gpytorch" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "pybind11" }, - { name = "rich" }, - { name = "rich-argparse" }, - { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scikit-learn", version = "1.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "setuptools" }, - { name = "torch" }, - { name = "wandb" }, -] - -[package.metadata] -requires-dist = [ - { name = "gpytorch" }, - { name = "numpy" }, - { name = "pybind11" }, - { name = "rich" }, - { name = "rich-argparse" }, - { name = "scikit-learn" }, - { name = "setuptools" }, - { name = "torch", specifier = ">=2.9" }, - { name = "wandb" }, -] - -[[package]] -name = "pybind11" -version = "3.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cc/f0/35145a3c3baffeef55d4b8324caa33abaa8fa56ab345ecd4b2211d09163e/pybind11-3.0.4.tar.gz", hash = "sha256:3286b59c8a774b9ee650169302dd5a4eedc30a8617905a0560dd8ee44775130c", size = 589533, upload-time = "2026-04-19T03:08:15.925Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/06/c3a23c9a0263b136c519f033a58d4641e73065fefc7754e9667ec206d992/pybind11-3.0.4-py3-none-any.whl", hash = "sha256:961720ee652da51d531b7b2451a6bd2bc042b0106e6d9baa48ecb7d58034ce63", size = 314166, upload-time = "2026-04-19T03:08:14.091Z" }, -] - -[[package]] -name = "pydantic" -version = "2.13.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-types" }, - { name = "pydantic-core" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" }, -] - -[[package]] -name = "pydantic-core" -version = "2.46.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/08/f1ba952f1c8ae5581c70fa9c6da89f247b83e3dd8c09c035d5d7931fc23d/pydantic_core-2.46.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4", size = 2113146, upload-time = "2026-05-06T13:37:36.537Z" }, - { url = "https://files.pythonhosted.org/packages/56/c6/65f646c7ff09bd257f660434adb45c4dfcbbcebcc030562fecf6f5bf887d/pydantic_core-2.46.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5", size = 1949769, upload-time = "2026-05-06T13:37:46.365Z" }, - { url = "https://files.pythonhosted.org/packages/64/ba/bfb1d928fd5b49e1258935ff104ae356e9fd89384a55bf9f847e9193ad40/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb63e0198ca18aad131c089b9204c23079c3afa95487e561f4c522d519e55aba", size = 1974958, upload-time = "2026-05-06T13:37:28.611Z" }, - { url = "https://files.pythonhosted.org/packages/4e/74/76223bfb117b64af743c9b6670d1364516f5c0604f96b48f3272f6af6cc6/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f47286a97f0bc9b8859519809077b91b2cefe4ae47fcbf5e466a009c1c5d742b", size = 2042118, upload-time = "2026-05-06T13:36:55.216Z" }, - { url = "https://files.pythonhosted.org/packages/cb/7b/848732968bc8f48f3187542f08358b9d842db564147b256669426ebb1652/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:905a0ed8ea6f2d61c1738835f99b699348d7857379083e5fc497fa0c967a407c", size = 2222876, upload-time = "2026-05-06T13:38:25.455Z" }, - { url = "https://files.pythonhosted.org/packages/b5/2f/e90b63ee2e14bd8d3db8f705a6d75d64e6ee1b7c2c8833747ce706e1e0ce/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea793e075b70290d89d8142074262885d3f7da19634845135751bd6344f73b50", size = 2286703, upload-time = "2026-05-06T13:37:53.304Z" }, - { url = "https://files.pythonhosted.org/packages/ba/1e/acc4d70f88a0a277e4a1fa77ebb985ceabaf900430f875bf9338e11c9420/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395aebd9183f9d112f569aeb5b2214d1a10a33bec8456447f7fbdfa51d38d4cd", size = 2092042, upload-time = "2026-05-06T13:38:46.981Z" }, - { url = "https://files.pythonhosted.org/packages/a9/da/0a422b57bf8504102bf3c4ccea9c41bab5a5cee6a54650acf8faf67f5a24/pydantic_core-2.46.4-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:b078afbc25f3a1436c7a1d2cd3e322497ee99615ba97c563566fdf46aff1ee01", size = 2117231, upload-time = "2026-05-06T13:39:23.146Z" }, - { url = "https://files.pythonhosted.org/packages/bd/2a/2ac13c3af305843e23c5078c53d135656b3f05a2fd78cb7bbbb12e97b473/pydantic_core-2.46.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f747929cf940cddb5b3668a390056ddd5ba2e5010615ea2dcf4f9c4f3ab8791d", size = 2168388, upload-time = "2026-05-06T13:40:08.06Z" }, - { url = "https://files.pythonhosted.org/packages/72/04/2beacf7e1607e93eefe4aed1b4709f079b905fb77530179d4f7c71745f22/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:daa27d92c36f24388fe3ad306b174781c747627f134452e4f128ea00ce1fe8c4", size = 2184769, upload-time = "2026-05-06T13:38:13.901Z" }, - { url = "https://files.pythonhosted.org/packages/9e/29/d2b9fd9f539133548eaf622c06a4ce176cb46ac59f32d0359c4abc0de047/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:19e51f073cd3df251856a8a4189fbdf1de4012c3ebacfb1884f94f1eb406079f", size = 2319312, upload-time = "2026-05-06T13:39:08.24Z" }, - { url = "https://files.pythonhosted.org/packages/7c/af/0f7a5b85fec6075bea96e3ef9187de38fccced0de92c1e7feda8d5cc7bb9/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1747f85cee84c26985853c6f3d9bd3e75da5212912443fa111c113b9c246f39", size = 2361817, upload-time = "2026-05-06T13:38:43.2Z" }, - { url = "https://files.pythonhosted.org/packages/25/a4/73363fec545fd3ec025490bdda2743c56d0dd5b6266b1a53bbe9e4265375/pydantic_core-2.46.4-cp310-cp310-win32.whl", hash = "sha256:2f84c03c8607173d16b5a854ec68a2f9079ae03237a54fb506d13af47e1d018d", size = 1987085, upload-time = "2026-05-06T13:39:25.497Z" }, - { url = "https://files.pythonhosted.org/packages/01/aa/62f082da2c91fac1c234bc9ee0066257ce83f0604abd72e4c9d5991f2d84/pydantic_core-2.46.4-cp310-cp310-win_amd64.whl", hash = "sha256:8358a950c8909158e3df31538a7e4edc2d7265a7c54b47f0864d9e5bae9dcebf", size = 2074311, upload-time = "2026-05-06T13:39:59.922Z" }, - { url = "https://files.pythonhosted.org/packages/5c/fa/6d7708d2cfc1a832acb6aeb0cd16e801902df8a0f583bb3b4b527fde022e/pydantic_core-2.46.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594", size = 2111872, upload-time = "2026-05-06T13:40:27.596Z" }, - { url = "https://files.pythonhosted.org/packages/ae/6f/aa064a3e74b5745afbdf250594f38e7ead05e2d651bcb35994b9417a0d4d/pydantic_core-2.46.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c", size = 1948255, upload-time = "2026-05-06T13:39:12.574Z" }, - { url = "https://files.pythonhosted.org/packages/43/3a/41114a9f7569b84b4d84e7a018c57c56347dac30c0d4a872946ec4e36c46/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826", size = 1972827, upload-time = "2026-05-06T13:38:19.841Z" }, - { url = "https://files.pythonhosted.org/packages/ef/25/1ab42e8048fe551934d9884e8d64daa7e990ad386f310a15981aeb6a5b08/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04", size = 2041051, upload-time = "2026-05-06T13:38:10.447Z" }, - { url = "https://files.pythonhosted.org/packages/94/c2/1a934597ddf08da410385b3b7aae91956a5a76c635effef456074fad7e88/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e", size = 2221314, upload-time = "2026-05-06T13:40:13.089Z" }, - { url = "https://files.pythonhosted.org/packages/02/6d/9e8ad178c9c4df27ad3c8f25d1fe2a7ab0d2ba0559fad4aee5d3d1f16771/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3", size = 2285146, upload-time = "2026-05-06T13:38:59.224Z" }, - { url = "https://files.pythonhosted.org/packages/80/50/540cd3aeefc041beb111125c4bff779831a2111fc6b15a9138cda277d32c/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4", size = 2089685, upload-time = "2026-05-06T13:38:17.762Z" }, - { url = "https://files.pythonhosted.org/packages/6b/a4/b440ad35f05f6a38f89fa0f149accb3f0e02be94ca5e15f3c449a61b4bc9/pydantic_core-2.46.4-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398", size = 2115420, upload-time = "2026-05-06T13:37:58.195Z" }, - { url = "https://files.pythonhosted.org/packages/99/61/de4f55db8dfd57bfdfa9a12ec90fe1b57c4f41062f7ca86f08586b3e0ac0/pydantic_core-2.46.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3", size = 2165122, upload-time = "2026-05-06T13:37:01.167Z" }, - { url = "https://files.pythonhosted.org/packages/f7/52/7c529d7bdb2d1068bd52f51fe32572c8301f9a4febf1948f10639f1436f5/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848", size = 2182573, upload-time = "2026-05-06T13:38:45.04Z" }, - { url = "https://files.pythonhosted.org/packages/37/b3/7c40325848ba78247f2812dcf9c7274e38cd801820ca6dd9fe63bcfb0eb4/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3", size = 2317139, upload-time = "2026-05-06T13:37:15.539Z" }, - { url = "https://files.pythonhosted.org/packages/d9/37/f913f81a657c865b75da6c0dbed79876073c2a43b5bd9edbe8da785e4d49/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109", size = 2360433, upload-time = "2026-05-06T13:37:30.099Z" }, - { url = "https://files.pythonhosted.org/packages/c4/67/6acaa1be2567f9256b056d8477158cac7240813956ce86e49deae8e173b4/pydantic_core-2.46.4-cp311-cp311-win32.whl", hash = "sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda", size = 1985513, upload-time = "2026-05-06T13:38:15.669Z" }, - { url = "https://files.pythonhosted.org/packages/aa/e6/c505f83dfeda9a2e5c995cfd872949e4d05e12f7feb3dca72f633daefa94/pydantic_core-2.46.4-cp311-cp311-win_amd64.whl", hash = "sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33", size = 2071114, upload-time = "2026-05-06T13:40:35.416Z" }, - { url = "https://files.pythonhosted.org/packages/0f/da/7a263a96d965d9d0df5e8de8a475f33495451117035b09acb110288c381f/pydantic_core-2.46.4-cp311-cp311-win_arm64.whl", hash = "sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d", size = 2044298, upload-time = "2026-05-06T13:38:29.754Z" }, - { url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" }, - { url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" }, - { url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" }, - { url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" }, - { url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" }, - { url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" }, - { url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" }, - { url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" }, - { url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" }, - { url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" }, - { url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" }, - { url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" }, - { url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" }, - { url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" }, - { url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" }, - { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" }, - { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" }, - { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" }, - { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" }, - { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" }, - { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" }, - { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" }, - { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" }, - { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" }, - { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" }, - { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" }, - { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" }, - { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" }, - { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" }, - { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" }, - { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" }, - { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" }, - { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" }, - { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" }, - { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" }, - { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" }, - { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" }, - { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" }, - { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" }, - { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" }, - { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" }, - { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" }, - { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" }, - { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" }, - { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" }, - { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" }, - { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" }, - { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" }, - { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" }, - { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" }, - { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" }, - { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" }, - { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" }, - { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" }, - { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" }, - { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" }, - { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" }, - { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" }, - { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" }, - { url = "https://files.pythonhosted.org/packages/ee/a4/73995fd4ebbb46ba0ee51e6fa049b8f02c40daebb762208feda8a6b7894d/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c", size = 2111589, upload-time = "2026-05-06T13:37:10.817Z" }, - { url = "https://files.pythonhosted.org/packages/fb/7f/f37d3a5e8bfcc2e403f5c57a730f2d815693fb42119e8ea48b3789335af1/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b", size = 1944552, upload-time = "2026-05-06T13:36:56.717Z" }, - { url = "https://files.pythonhosted.org/packages/15/3c/d7eb777b3ff43e8433a4efb39a17aa8fd98a4ee8561a24a67ef5db07b2d6/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b", size = 1982984, upload-time = "2026-05-06T13:39:06.207Z" }, - { url = "https://files.pythonhosted.org/packages/63/87/70b9f40170a81afd55ca26c9b2acb25c20d64bcfbf888fafecb3ba077d4c/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea", size = 2138417, upload-time = "2026-05-06T13:39:45.476Z" }, - { url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" }, - { url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" }, - { url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" }, - { url = "https://files.pythonhosted.org/packages/11/cb/428de0385b6c8d44b716feba566abfacfbd23ee3c4439faa789a1456242f/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0", size = 2112782, upload-time = "2026-05-06T13:37:04.016Z" }, - { url = "https://files.pythonhosted.org/packages/0b/b5/6a17bdadd0fc1f170adfd05a20d37c832f52b117b4d9131da1f41bb097ce/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7", size = 1952146, upload-time = "2026-05-06T13:39:43.092Z" }, - { url = "https://files.pythonhosted.org/packages/2a/dc/03734d80e362cd43ef65428e9de77c730ce7f2f11c60d2b1e1b39f0fbf99/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2", size = 2134492, upload-time = "2026-05-06T13:36:58.124Z" }, - { url = "https://files.pythonhosted.org/packages/de/df/5e5ffc085ed07cc22d298134d3d911c63e91f6a0eb91fe646750a3209910/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9", size = 2156604, upload-time = "2026-05-06T13:37:49.88Z" }, - { url = "https://files.pythonhosted.org/packages/81/44/6e112a4253e56f5705467cbab7ab5e91ee7398ba3d56d358635958893d3e/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf", size = 2183828, upload-time = "2026-05-06T13:37:43.053Z" }, - { url = "https://files.pythonhosted.org/packages/ac/ad/5565071e937d8e752842ac241463944c9eb14c87e2d269f2658a5bd05e98/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30", size = 2310000, upload-time = "2026-05-06T13:37:56.694Z" }, - { url = "https://files.pythonhosted.org/packages/4f/c3/66883a5cec183e7fba4d024b4cbbe61851a63750ef606b0afecc46d1f2bf/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc", size = 2361286, upload-time = "2026-05-06T13:40:05.667Z" }, - { url = "https://files.pythonhosted.org/packages/4b/2d/69abac8f838090bbecd5df894befb2c2619e7996a98ddb949db9f3b93225/pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983", size = 2193071, upload-time = "2026-05-06T13:38:08.682Z" }, -] - -[[package]] -name = "pygments" -version = "2.20.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, - { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, - { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, - { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, - { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, - { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, - { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, - { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, - { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, - { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, - { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, - { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, - { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, - { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, - { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, - { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, - { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, - { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { 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" }, -] - -[[package]] -name = "requests" -version = "2.34.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "idna" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, -] - -[[package]] -name = "rich" -version = "15.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, -] - -[[package]] -name = "rich-argparse" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "rich" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6a/e5/1064c43203a357d668cd42435f7a15fe6af51512d85b2104fecb937aa861/rich_argparse-1.8.0.tar.gz", hash = "sha256:679df3d832fa94ad6e4bdb07ded088cd7ea2dddc58ae9b2b46346a40b06cbc0c", size = 38940, upload-time = "2026-05-01T15:18:43.604Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/35/1cceccc5fcb50fa2ed53e2aa278cd032f3902682a73e763fb1ac3be8e6fa/rich_argparse-1.8.0-py3-none-any.whl", hash = "sha256:d2a3ce7854654e2253c578763ab0a32f05016f23a55fadba7b9a91b6c0e92142", size = 25616, upload-time = "2026-05-01T15:18:42.395Z" }, -] - -[[package]] -name = "scikit-learn" -version = "1.7.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { 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 = [ - { url = "https://files.pythonhosted.org/packages/ba/3e/daed796fd69cce768b8788401cc464ea90b306fb196ae1ffed0b98182859/scikit_learn-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b33579c10a3081d076ab403df4a4190da4f4432d443521674637677dc91e61f", size = 9336221, upload-time = "2025-09-09T08:20:19.328Z" }, - { url = "https://files.pythonhosted.org/packages/1c/ce/af9d99533b24c55ff4e18d9b7b4d9919bbc6cd8f22fe7a7be01519a347d5/scikit_learn-1.7.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:36749fb62b3d961b1ce4fedf08fa57a1986cd409eff2d783bca5d4b9b5fce51c", size = 8653834, upload-time = "2025-09-09T08:20:22.073Z" }, - { url = "https://files.pythonhosted.org/packages/58/0e/8c2a03d518fb6bd0b6b0d4b114c63d5f1db01ff0f9925d8eb10960d01c01/scikit_learn-1.7.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7a58814265dfc52b3295b1900cfb5701589d30a8bb026c7540f1e9d3499d5ec8", size = 9660938, upload-time = "2025-09-09T08:20:24.327Z" }, - { url = "https://files.pythonhosted.org/packages/2b/75/4311605069b5d220e7cf5adabb38535bd96f0079313cdbb04b291479b22a/scikit_learn-1.7.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a847fea807e278f821a0406ca01e387f97653e284ecbd9750e3ee7c90347f18", size = 9477818, upload-time = "2025-09-09T08:20:26.845Z" }, - { url = "https://files.pythonhosted.org/packages/7f/9b/87961813c34adbca21a6b3f6b2bea344c43b30217a6d24cc437c6147f3e8/scikit_learn-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:ca250e6836d10e6f402436d6463d6c0e4d8e0234cfb6a9a47835bd392b852ce5", size = 8886969, upload-time = "2025-09-09T08:20:29.329Z" }, - { url = "https://files.pythonhosted.org/packages/43/83/564e141eef908a5863a54da8ca342a137f45a0bfb71d1d79704c9894c9d1/scikit_learn-1.7.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7509693451651cd7361d30ce4e86a1347493554f172b1c72a39300fa2aea79e", size = 9331967, upload-time = "2025-09-09T08:20:32.421Z" }, - { url = "https://files.pythonhosted.org/packages/18/d6/ba863a4171ac9d7314c4d3fc251f015704a2caeee41ced89f321c049ed83/scikit_learn-1.7.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:0486c8f827c2e7b64837c731c8feff72c0bd2b998067a8a9cbc10643c31f0fe1", size = 8648645, upload-time = "2025-09-09T08:20:34.436Z" }, - { url = "https://files.pythonhosted.org/packages/ef/0e/97dbca66347b8cf0ea8b529e6bb9367e337ba2e8be0ef5c1a545232abfde/scikit_learn-1.7.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89877e19a80c7b11a2891a27c21c4894fb18e2c2e077815bcade10d34287b20d", size = 9715424, upload-time = "2025-09-09T08:20:36.776Z" }, - { url = "https://files.pythonhosted.org/packages/f7/32/1f3b22e3207e1d2c883a7e09abb956362e7d1bd2f14458c7de258a26ac15/scikit_learn-1.7.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8da8bf89d4d79aaec192d2bda62f9b56ae4e5b4ef93b6a56b5de4977e375c1f1", size = 9509234, upload-time = "2025-09-09T08:20:38.957Z" }, - { url = "https://files.pythonhosted.org/packages/9f/71/34ddbd21f1da67c7a768146968b4d0220ee6831e4bcbad3e03dd3eae88b6/scikit_learn-1.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:9b7ed8d58725030568523e937c43e56bc01cadb478fc43c042a9aca1dacb3ba1", size = 8894244, upload-time = "2025-09-09T08:20:41.166Z" }, - { url = "https://files.pythonhosted.org/packages/a7/aa/3996e2196075689afb9fce0410ebdb4a09099d7964d061d7213700204409/scikit_learn-1.7.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8d91a97fa2b706943822398ab943cde71858a50245e31bc71dba62aab1d60a96", size = 9259818, upload-time = "2025-09-09T08:20:43.19Z" }, - { url = "https://files.pythonhosted.org/packages/43/5d/779320063e88af9c4a7c2cf463ff11c21ac9c8bd730c4a294b0000b666c9/scikit_learn-1.7.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:acbc0f5fd2edd3432a22c69bed78e837c70cf896cd7993d71d51ba6708507476", size = 8636997, upload-time = "2025-09-09T08:20:45.468Z" }, - { url = "https://files.pythonhosted.org/packages/5c/d0/0c577d9325b05594fdd33aa970bf53fb673f051a45496842caee13cfd7fe/scikit_learn-1.7.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e5bf3d930aee75a65478df91ac1225ff89cd28e9ac7bd1196853a9229b6adb0b", size = 9478381, upload-time = "2025-09-09T08:20:47.982Z" }, - { url = "https://files.pythonhosted.org/packages/82/70/8bf44b933837ba8494ca0fc9a9ab60f1c13b062ad0197f60a56e2fc4c43e/scikit_learn-1.7.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4d6e9deed1a47aca9fe2f267ab8e8fe82ee20b4526b2c0cd9e135cea10feb44", size = 9300296, upload-time = "2025-09-09T08:20:50.366Z" }, - { url = "https://files.pythonhosted.org/packages/c6/99/ed35197a158f1fdc2fe7c3680e9c70d0128f662e1fee4ed495f4b5e13db0/scikit_learn-1.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:6088aa475f0785e01bcf8529f55280a3d7d298679f50c0bb70a2364a82d0b290", size = 8731256, upload-time = "2025-09-09T08:20:52.627Z" }, - { url = "https://files.pythonhosted.org/packages/ae/93/a3038cb0293037fd335f77f31fe053b89c72f17b1c8908c576c29d953e84/scikit_learn-1.7.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0b7dacaa05e5d76759fb071558a8b5130f4845166d88654a0f9bdf3eb57851b7", size = 9212382, upload-time = "2025-09-09T08:20:54.731Z" }, - { url = "https://files.pythonhosted.org/packages/40/dd/9a88879b0c1104259136146e4742026b52df8540c39fec21a6383f8292c7/scikit_learn-1.7.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:abebbd61ad9e1deed54cca45caea8ad5f79e1b93173dece40bb8e0c658dbe6fe", size = 8592042, upload-time = "2025-09-09T08:20:57.313Z" }, - { url = "https://files.pythonhosted.org/packages/46/af/c5e286471b7d10871b811b72ae794ac5fe2989c0a2df07f0ec723030f5f5/scikit_learn-1.7.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:502c18e39849c0ea1a5d681af1dbcf15f6cce601aebb657aabbfe84133c1907f", size = 9434180, upload-time = "2025-09-09T08:20:59.671Z" }, - { url = "https://files.pythonhosted.org/packages/f1/fd/df59faa53312d585023b2da27e866524ffb8faf87a68516c23896c718320/scikit_learn-1.7.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a4c328a71785382fe3fe676a9ecf2c86189249beff90bf85e22bdb7efaf9ae0", size = 9283660, upload-time = "2025-09-09T08:21:01.71Z" }, - { url = "https://files.pythonhosted.org/packages/a7/c7/03000262759d7b6f38c836ff9d512f438a70d8a8ddae68ee80de72dcfb63/scikit_learn-1.7.2-cp313-cp313-win_amd64.whl", hash = "sha256:63a9afd6f7b229aad94618c01c252ce9e6fa97918c5ca19c9a17a087d819440c", size = 8702057, upload-time = "2025-09-09T08:21:04.234Z" }, - { url = "https://files.pythonhosted.org/packages/55/87/ef5eb1f267084532c8e4aef98a28b6ffe7425acbfd64b5e2f2e066bc29b3/scikit_learn-1.7.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9acb6c5e867447b4e1390930e3944a005e2cb115922e693c08a323421a6966e8", size = 9558731, upload-time = "2025-09-09T08:21:06.381Z" }, - { url = "https://files.pythonhosted.org/packages/93/f8/6c1e3fc14b10118068d7938878a9f3f4e6d7b74a8ddb1e5bed65159ccda8/scikit_learn-1.7.2-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:2a41e2a0ef45063e654152ec9d8bcfc39f7afce35b08902bfe290c2498a67a6a", size = 9038852, upload-time = "2025-09-09T08:21:08.628Z" }, - { url = "https://files.pythonhosted.org/packages/83/87/066cafc896ee540c34becf95d30375fe5cbe93c3b75a0ee9aa852cd60021/scikit_learn-1.7.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:98335fb98509b73385b3ab2bd0639b1f610541d3988ee675c670371d6a87aa7c", size = 9527094, upload-time = "2025-09-09T08:21:11.486Z" }, - { url = "https://files.pythonhosted.org/packages/9c/2b/4903e1ccafa1f6453b1ab78413938c8800633988c838aa0be386cbb33072/scikit_learn-1.7.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:191e5550980d45449126e23ed1d5e9e24b2c68329ee1f691a3987476e115e09c", size = 9367436, upload-time = "2025-09-09T08:21:13.602Z" }, - { url = "https://files.pythonhosted.org/packages/b5/aa/8444be3cfb10451617ff9d177b3c190288f4563e6c50ff02728be67ad094/scikit_learn-1.7.2-cp313-cp313t-win_amd64.whl", hash = "sha256:57dc4deb1d3762c75d685507fbd0bc17160144b2f2ba4ccea5dc285ab0d0e973", size = 9275749, upload-time = "2025-09-09T08:21:15.96Z" }, - { url = "https://files.pythonhosted.org/packages/d9/82/dee5acf66837852e8e68df6d8d3a6cb22d3df997b733b032f513d95205b7/scikit_learn-1.7.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fa8f63940e29c82d1e67a45d5297bdebbcb585f5a5a50c4914cc2e852ab77f33", size = 9208906, upload-time = "2025-09-09T08:21:18.557Z" }, - { url = "https://files.pythonhosted.org/packages/3c/30/9029e54e17b87cb7d50d51a5926429c683d5b4c1732f0507a6c3bed9bf65/scikit_learn-1.7.2-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:f95dc55b7902b91331fa4e5845dd5bde0580c9cd9612b1b2791b7e80c3d32615", size = 8627836, upload-time = "2025-09-09T08:21:20.695Z" }, - { url = "https://files.pythonhosted.org/packages/60/18/4a52c635c71b536879f4b971c2cedf32c35ee78f48367885ed8025d1f7ee/scikit_learn-1.7.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9656e4a53e54578ad10a434dc1f993330568cfee176dff07112b8785fb413106", size = 9426236, upload-time = "2025-09-09T08:21:22.645Z" }, - { url = "https://files.pythonhosted.org/packages/99/7e/290362f6ab582128c53445458a5befd471ed1ea37953d5bcf80604619250/scikit_learn-1.7.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96dc05a854add0e50d3f47a1ef21a10a595016da5b007c7d9cd9d0bffd1fcc61", size = 9312593, upload-time = "2025-09-09T08:21:24.65Z" }, - { url = "https://files.pythonhosted.org/packages/8e/87/24f541b6d62b1794939ae6422f8023703bbf6900378b2b34e0b4384dfefd/scikit_learn-1.7.2-cp314-cp314-win_amd64.whl", hash = "sha256:bb24510ed3f9f61476181e4db51ce801e2ba37541def12dc9333b946fc7a9cf8", size = 8820007, upload-time = "2025-09-09T08:21:26.713Z" }, -] - -[[package]] -name = "scikit-learn" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.12'", - "python_full_version == '3.11.*'", -] -dependencies = [ - { name = "joblib", marker = "python_full_version >= '3.11'" }, - { name = "narwhals", marker = "python_full_version >= '3.11'" }, - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "scipy", version = "1.17.1", 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/fa/6f/37092bdb25f712817231799fc5674d8e704066a8a70c1d2d40517e18b4ab/scikit_learn-1.9.0.tar.gz", hash = "sha256:8833266989d3a5110178a9fae30783675460724d0e1efb13b14901d2c660c557", size = 7750767, upload-time = "2026-06-02T11:54:32.706Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/be/e844fd9586e66540a15b71924d17a6cbc1bb749e81ddd0a796bcdba4c055/scikit_learn-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9db6f4d34e68c8899e4cab27fdf8eafe6ed21f2ba52ceb25ea250cd237f8e47b", size = 8789686, upload-time = "2026-06-02T11:53:05.439Z" }, - { url = "https://files.pythonhosted.org/packages/42/e2/ff880f62677a17d035817d543cb0fc8727d01eccbee81c5f7fc733a9d856/scikit_learn-1.9.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f401448645a3e7bc115aa3c094097865155b34bff1cba8101857d9104e99074c", size = 8256782, upload-time = "2026-06-02T11:53:08.904Z" }, - { url = "https://files.pythonhosted.org/packages/25/64/eb40435e1a508ab1b4e284ce43ae80f6a162e5be5e38ed5a6fab467a9ea4/scikit_learn-1.9.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fd3a8ef0c758555a3b23c03adaa858af32f7736785ded50ad5991f59c4ed03fa", size = 8992419, upload-time = "2026-06-02T11:53:11.551Z" }, - { url = "https://files.pythonhosted.org/packages/8d/da/4810a28e473185429e45a57eebcc91fc991b33d889cc0676063e671db03d/scikit_learn-1.9.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7e254636164090da847715a27f8e5478feb98c40a9e0ee90cbd277de9e5ceb8", size = 9281411, upload-time = "2026-06-02T11:53:15.063Z" }, - { url = "https://files.pythonhosted.org/packages/3b/67/be3d369f40d8178ba3bd86635d132e08cb5329b023e4669d9426d84bc007/scikit_learn-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:5dc1818c77575d149e25fce9ef82dd7b7263ae372f03494158668ad632a69759", size = 8272736, upload-time = "2026-06-02T11:53:18.108Z" }, - { url = "https://files.pythonhosted.org/packages/37/79/a733f02dc2118da7e77a134b34f39f40201a353311b011d20859d2db3556/scikit_learn-1.9.0-cp311-cp311-win_arm64.whl", hash = "sha256:366652351f092b219c248f1e72821e841960a63d8f358f1dcfd54dc1cbdbbc28", size = 7919564, upload-time = "2026-06-02T11:53:21.2Z" }, - { url = "https://files.pythonhosted.org/packages/ac/20/75f915ff375d6249e6550ac740fdbbd66159a068fd3af1400ff62036b07a/scikit_learn-1.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2bd41b0d201bc81575531b96b713d3eb5e5f50fb0b82101ff0f92294fdc236ac", size = 8741122, upload-time = "2026-06-02T11:53:24.08Z" }, - { url = "https://files.pythonhosted.org/packages/cc/d5/2b5148f2279196775e1db2aeb85d14b70ac80e7e32b3b28e7ebeafb0901d/scikit_learn-1.9.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5be45aa4a42a68a533913a6ed736cf309de2226411c79ef8d609a5456f1939b1", size = 8261512, upload-time = "2026-06-02T11:53:27.183Z" }, - { url = "https://files.pythonhosted.org/packages/a0/ee/5adbc77656b71f9456a2f5a7a9fdb4bcf9207a6b962889f1c2f9323afa4e/scikit_learn-1.9.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e50ed4da51974e86e940690e9a3d82e729b62b5a49f7c9bac534d515d39d86f", size = 8837603, upload-time = "2026-06-02T11:53:30.328Z" }, - { url = "https://files.pythonhosted.org/packages/6c/c2/63fdda36c56437eeb44aaf9493c8bcd62ce230ab1598924fc626ffbfa943/scikit_learn-1.9.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:056c92bb67ad4c28463c2f2653d9701449201e7e7a9e94e321be0f71c4fef2b8", size = 9132097, upload-time = "2026-06-02T11:53:33.456Z" }, - { url = "https://files.pythonhosted.org/packages/83/a4/c8e67227c680e2259c8864ae72ff48b06e16a6f51253a22167aa02a8aa4e/scikit_learn-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:4306775fad04cc4b472a1b15af1ae9cede1540fbfcc17fbce3767cd8dc7ae283", size = 8211173, upload-time = "2026-06-02T11:53:36.602Z" }, - { url = "https://files.pythonhosted.org/packages/cf/fd/3c0863792e98e67e9184aa4029288a175935eb65443afcd30d4f143450cf/scikit_learn-1.9.0-cp312-cp312-win_arm64.whl", hash = "sha256:26e22435f63bcdcf396b574273f29f13dd531f5ea035801f5be10ba1540a4e60", size = 7867451, upload-time = "2026-06-02T11:53:39.075Z" }, - { url = "https://files.pythonhosted.org/packages/3c/01/cf3310626b6d48d3e9be69a1223f9180360b5e6edb045f50fade723ce494/scikit_learn-1.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:80746d63bd4b6eaca54d36fe5feaf4d28bb38dc6f9470f81c7cad7c40155f119", size = 8705188, upload-time = "2026-06-02T11:53:41.964Z" }, - { url = "https://files.pythonhosted.org/packages/3e/04/5acd7ae280c5f93b6ac5ef6cdec14eef4c8d1cd91d85b3292989c94d96b1/scikit_learn-1.9.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:5b934c45c252844a91d69fda3a34cff5e7307e1db10d77cb10a3980312c74713", size = 8228299, upload-time = "2026-06-02T11:53:44.817Z" }, - { url = "https://files.pythonhosted.org/packages/0c/39/ffe829a5b8ecb40a518724a997794657fdc354ada5e8fe8e64d998c0bac9/scikit_learn-1.9.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:38c3dcb9a1ffb85505ec53d54c7b4aea0cff70050425a7760c2af661ac85df05", size = 8789690, upload-time = "2026-06-02T11:53:47.461Z" }, - { url = "https://files.pythonhosted.org/packages/1f/88/8dab5de10c638c083772a6be83a3d8106ced492f74a928c8693638e5bb50/scikit_learn-1.9.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da76d09304a4706db7cc1e3ebaa3b6b98a67365cc11d2996c4f1e58ba47df714", size = 9087723, upload-time = "2026-06-02T11:53:50.702Z" }, - { url = "https://files.pythonhosted.org/packages/20/3f/7917ca72464038f6240ec70c29f94862d08a34a74291ae4d4ec5eb8186a0/scikit_learn-1.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:5808d98f15c6bf6d9d96d2348c1997392a5888ce7097e664105f930c4bca1277", size = 8184330, upload-time = "2026-06-02T11:53:53.396Z" }, - { url = "https://files.pythonhosted.org/packages/78/c7/15739eb2f61fda3c54639e9942414e5a19ad8a8d1f5a3266afad7cb7df80/scikit_learn-1.9.0-cp313-cp313-win_arm64.whl", hash = "sha256:d77f54c017633791bc0225a43e2f8d03745fdcfe4880268fcc4df15f505dec2e", size = 7840653, upload-time = "2026-06-02T11:53:56.035Z" }, - { url = "https://files.pythonhosted.org/packages/f4/7d/c9a35cf59b20a86fec24d306f1547b78dec194b08d367ce2a3e4854169d9/scikit_learn-1.9.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9656acd4e93f74e0b66c8a36c88830a99252dfa900044d36bc2212ae89a47162", size = 8713289, upload-time = "2026-06-02T11:53:58.788Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a7/552a7821597c632b907f7bfe8f36f9f572777af8ef8a48353041cf8e091a/scikit_learn-1.9.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:24360002ae845e7866522b0a5bbf690802e7bc388cac8663502e78aa98598aa2", size = 8245141, upload-time = "2026-06-02T11:54:01.694Z" }, - { url = "https://files.pythonhosted.org/packages/7d/79/f4a0c4fe9711154cddabf913471153af79056382ddc612cfe5ee0ff4b72e/scikit_learn-1.9.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5162ad10a418c8a282dde04c9aa06965de3e9a65f33c1440c0ae69bb1a09d913", size = 8847671, upload-time = "2026-06-02T11:54:04.448Z" }, - { url = "https://files.pythonhosted.org/packages/f0/af/4d72d9e475ac83719160c662619e4bf7b95c19507cd582e7d0167a3c3dae/scikit_learn-1.9.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fea2cc5677ab49d6f5bade978c866da44957b712d92e9635e8b4f723013c3cb", size = 9118104, upload-time = "2026-06-02T11:54:07.205Z" }, - { url = "https://files.pythonhosted.org/packages/a2/d5/6a58eea2cb9abbb9b3f2bb8b2cfb3243d1152d69f442d256c7af71304769/scikit_learn-1.9.0-cp314-cp314-win_amd64.whl", hash = "sha256:64fa347efc1c839c487433e40c5144d38c336e8a2b59c81aa8660373945c2673", size = 8290674, upload-time = "2026-06-02T11:54:10.087Z" }, - { url = "https://files.pythonhosted.org/packages/65/5b/d4c879cf358f1187141cf90ced473f087183489090244f50c124a2ee478b/scikit_learn-1.9.0-cp314-cp314-win_arm64.whl", hash = "sha256:1b944b6db288f6b926e3650026ddafb988929de95d11fc2cc5fa117773c9ba42", size = 7978807, upload-time = "2026-06-02T11:54:12.769Z" }, - { url = "https://files.pythonhosted.org/packages/8a/43/bfae3121ec67ae09150d453c442c7c1cc166e9aefe056e6ab3b7728a5cfc/scikit_learn-1.9.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:4ccacf04ca5f4b492158a5f28afe0ace43f81b2571e4b9a66d34848b46128949", size = 9031941, upload-time = "2026-06-02T11:54:15.436Z" }, - { url = "https://files.pythonhosted.org/packages/75/b0/20a4546eb17f3b25d3c66df15810411c14ed5065bcfab50b53c96fb627b2/scikit_learn-1.9.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:ee1a8db2c18c08e34c7412d4b10be1cac214cd4ea7dc9715a6a327eb49a37c96", size = 8613528, upload-time = "2026-06-02T11:54:18.842Z" }, - { url = "https://files.pythonhosted.org/packages/18/3c/e440e039bb82cd19004edaaad00acbde0fb9b461083c3ecf37941c557312/scikit_learn-1.9.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:147e9329ef0e39f75d4cffa02b2aa48d827832684926cd5210d9a2cb5c57246b", size = 8855050, upload-time = "2026-06-02T11:54:21.699Z" }, - { url = "https://files.pythonhosted.org/packages/43/26/b341b8dab5998da6270a3a42c2152c578501354d36f944b5856757035ef8/scikit_learn-1.9.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bad8f8b9950321b54c965fdcbac6c6c55e79e16646b49977bcf3668d3870a1a", size = 9097190, upload-time = "2026-06-02T11:54:24.454Z" }, - { url = "https://files.pythonhosted.org/packages/fb/de/b650b4d69b84468cfa2e28a3ff7b8103743029e6446ce1a97fe060ef688c/scikit_learn-1.9.0-cp314-cp314t-win_amd64.whl", hash = "sha256:78fc56eafd4edb9575d2d8950d1dd152061abb573341a1cb7e099fc40f6c6666", size = 8963204, upload-time = "2026-06-02T11:54:27.428Z" }, - { url = "https://files.pythonhosted.org/packages/ee/f3/ff83d76d7418112e5a61326443cdda87be3545dd8d6599c95b2481a4419e/scikit_learn-1.9.0-cp314-cp314t-win_arm64.whl", hash = "sha256:051075bda8b7aab87b1906ab3d4740a1e1224a19d7b3781a576736edc94e76aa", size = 8222661, upload-time = "2026-06-02T11:54:30.192Z" }, -] - -[[package]] -name = "scipy" -version = "1.15.3" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { 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 = [ - { url = "https://files.pythonhosted.org/packages/78/2f/4966032c5f8cc7e6a60f1b2e0ad686293b9474b65246b0c642e3ef3badd0/scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c", size = 38702770, upload-time = "2025-05-08T16:04:20.849Z" }, - { url = "https://files.pythonhosted.org/packages/a0/6e/0c3bf90fae0e910c274db43304ebe25a6b391327f3f10b5dcc638c090795/scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253", size = 30094511, upload-time = "2025-05-08T16:04:27.103Z" }, - { url = "https://files.pythonhosted.org/packages/ea/b1/4deb37252311c1acff7f101f6453f0440794f51b6eacb1aad4459a134081/scipy-1.15.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:aef683a9ae6eb00728a542b796f52a5477b78252edede72b8327a886ab63293f", size = 22368151, upload-time = "2025-05-08T16:04:31.731Z" }, - { url = "https://files.pythonhosted.org/packages/38/7d/f457626e3cd3c29b3a49ca115a304cebb8cc6f31b04678f03b216899d3c6/scipy-1.15.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:1c832e1bd78dea67d5c16f786681b28dd695a8cb1fb90af2e27580d3d0967e92", size = 25121732, upload-time = "2025-05-08T16:04:36.596Z" }, - { url = "https://files.pythonhosted.org/packages/db/0a/92b1de4a7adc7a15dcf5bddc6e191f6f29ee663b30511ce20467ef9b82e4/scipy-1.15.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:263961f658ce2165bbd7b99fa5135195c3a12d9bef045345016b8b50c315cb82", size = 35547617, upload-time = "2025-05-08T16:04:43.546Z" }, - { url = "https://files.pythonhosted.org/packages/8e/6d/41991e503e51fc1134502694c5fa7a1671501a17ffa12716a4a9151af3df/scipy-1.15.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2abc762b0811e09a0d3258abee2d98e0c703eee49464ce0069590846f31d40", size = 37662964, upload-time = "2025-05-08T16:04:49.431Z" }, - { url = "https://files.pythonhosted.org/packages/25/e1/3df8f83cb15f3500478c889be8fb18700813b95e9e087328230b98d547ff/scipy-1.15.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed7284b21a7a0c8f1b6e5977ac05396c0d008b89e05498c8b7e8f4a1423bba0e", size = 37238749, upload-time = "2025-05-08T16:04:55.215Z" }, - { url = "https://files.pythonhosted.org/packages/93/3e/b3257cf446f2a3533ed7809757039016b74cd6f38271de91682aa844cfc5/scipy-1.15.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5380741e53df2c566f4d234b100a484b420af85deb39ea35a1cc1be84ff53a5c", size = 40022383, upload-time = "2025-05-08T16:05:01.914Z" }, - { url = "https://files.pythonhosted.org/packages/d1/84/55bc4881973d3f79b479a5a2e2df61c8c9a04fcb986a213ac9c02cfb659b/scipy-1.15.3-cp310-cp310-win_amd64.whl", hash = "sha256:9d61e97b186a57350f6d6fd72640f9e99d5a4a2b8fbf4b9ee9a841eab327dc13", size = 41259201, upload-time = "2025-05-08T16:05:08.166Z" }, - { url = "https://files.pythonhosted.org/packages/96/ab/5cc9f80f28f6a7dff646c5756e559823614a42b1939d86dd0ed550470210/scipy-1.15.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:993439ce220d25e3696d1b23b233dd010169b62f6456488567e830654ee37a6b", size = 38714255, upload-time = "2025-05-08T16:05:14.596Z" }, - { url = "https://files.pythonhosted.org/packages/4a/4a/66ba30abe5ad1a3ad15bfb0b59d22174012e8056ff448cb1644deccbfed2/scipy-1.15.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:34716e281f181a02341ddeaad584205bd2fd3c242063bd3423d61ac259ca7eba", size = 30111035, upload-time = "2025-05-08T16:05:20.152Z" }, - { url = "https://files.pythonhosted.org/packages/4b/fa/a7e5b95afd80d24313307f03624acc65801846fa75599034f8ceb9e2cbf6/scipy-1.15.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3b0334816afb8b91dab859281b1b9786934392aa3d527cd847e41bb6f45bee65", size = 22384499, upload-time = "2025-05-08T16:05:24.494Z" }, - { url = "https://files.pythonhosted.org/packages/17/99/f3aaddccf3588bb4aea70ba35328c204cadd89517a1612ecfda5b2dd9d7a/scipy-1.15.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:6db907c7368e3092e24919b5e31c76998b0ce1684d51a90943cb0ed1b4ffd6c1", size = 25152602, upload-time = "2025-05-08T16:05:29.313Z" }, - { url = "https://files.pythonhosted.org/packages/56/c5/1032cdb565f146109212153339f9cb8b993701e9fe56b1c97699eee12586/scipy-1.15.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721d6b4ef5dc82ca8968c25b111e307083d7ca9091bc38163fb89243e85e3889", size = 35503415, upload-time = "2025-05-08T16:05:34.699Z" }, - { url = "https://files.pythonhosted.org/packages/bd/37/89f19c8c05505d0601ed5650156e50eb881ae3918786c8fd7262b4ee66d3/scipy-1.15.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39cb9c62e471b1bb3750066ecc3a3f3052b37751c7c3dfd0fd7e48900ed52982", size = 37652622, upload-time = "2025-05-08T16:05:40.762Z" }, - { url = "https://files.pythonhosted.org/packages/7e/31/be59513aa9695519b18e1851bb9e487de66f2d31f835201f1b42f5d4d475/scipy-1.15.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:795c46999bae845966368a3c013e0e00947932d68e235702b5c3f6ea799aa8c9", size = 37244796, upload-time = "2025-05-08T16:05:48.119Z" }, - { url = "https://files.pythonhosted.org/packages/10/c0/4f5f3eeccc235632aab79b27a74a9130c6c35df358129f7ac8b29f562ac7/scipy-1.15.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:18aaacb735ab38b38db42cb01f6b92a2d0d4b6aabefeb07f02849e47f8fb3594", size = 40047684, upload-time = "2025-05-08T16:05:54.22Z" }, - { url = "https://files.pythonhosted.org/packages/ab/a7/0ddaf514ce8a8714f6ed243a2b391b41dbb65251affe21ee3077ec45ea9a/scipy-1.15.3-cp311-cp311-win_amd64.whl", hash = "sha256:ae48a786a28412d744c62fd7816a4118ef97e5be0bee968ce8f0a2fba7acf3bb", size = 41246504, upload-time = "2025-05-08T16:06:00.437Z" }, - { url = "https://files.pythonhosted.org/packages/37/4b/683aa044c4162e10ed7a7ea30527f2cbd92e6999c10a8ed8edb253836e9c/scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019", size = 38766735, upload-time = "2025-05-08T16:06:06.471Z" }, - { url = "https://files.pythonhosted.org/packages/7b/7e/f30be3d03de07f25dc0ec926d1681fed5c732d759ac8f51079708c79e680/scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6", size = 30173284, upload-time = "2025-05-08T16:06:11.686Z" }, - { url = "https://files.pythonhosted.org/packages/07/9c/0ddb0d0abdabe0d181c1793db51f02cd59e4901da6f9f7848e1f96759f0d/scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477", size = 22446958, upload-time = "2025-05-08T16:06:15.97Z" }, - { url = "https://files.pythonhosted.org/packages/af/43/0bce905a965f36c58ff80d8bea33f1f9351b05fad4beaad4eae34699b7a1/scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c", size = 25242454, upload-time = "2025-05-08T16:06:20.394Z" }, - { url = "https://files.pythonhosted.org/packages/56/30/a6f08f84ee5b7b28b4c597aca4cbe545535c39fe911845a96414700b64ba/scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45", size = 35210199, upload-time = "2025-05-08T16:06:26.159Z" }, - { url = "https://files.pythonhosted.org/packages/0b/1f/03f52c282437a168ee2c7c14a1a0d0781a9a4a8962d84ac05c06b4c5b555/scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49", size = 37309455, upload-time = "2025-05-08T16:06:32.778Z" }, - { url = "https://files.pythonhosted.org/packages/89/b1/fbb53137f42c4bf630b1ffdfc2151a62d1d1b903b249f030d2b1c0280af8/scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e", size = 36885140, upload-time = "2025-05-08T16:06:39.249Z" }, - { url = "https://files.pythonhosted.org/packages/2e/2e/025e39e339f5090df1ff266d021892694dbb7e63568edcfe43f892fa381d/scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539", size = 39710549, upload-time = "2025-05-08T16:06:45.729Z" }, - { url = "https://files.pythonhosted.org/packages/e6/eb/3bf6ea8ab7f1503dca3a10df2e4b9c3f6b3316df07f6c0ded94b281c7101/scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed", size = 40966184, upload-time = "2025-05-08T16:06:52.623Z" }, - { url = "https://files.pythonhosted.org/packages/73/18/ec27848c9baae6e0d6573eda6e01a602e5649ee72c27c3a8aad673ebecfd/scipy-1.15.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759", size = 38728256, upload-time = "2025-05-08T16:06:58.696Z" }, - { url = "https://files.pythonhosted.org/packages/74/cd/1aef2184948728b4b6e21267d53b3339762c285a46a274ebb7863c9e4742/scipy-1.15.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62", size = 30109540, upload-time = "2025-05-08T16:07:04.209Z" }, - { url = "https://files.pythonhosted.org/packages/5b/d8/59e452c0a255ec352bd0a833537a3bc1bfb679944c4938ab375b0a6b3a3e/scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb", size = 22383115, upload-time = "2025-05-08T16:07:08.998Z" }, - { url = "https://files.pythonhosted.org/packages/08/f5/456f56bbbfccf696263b47095291040655e3cbaf05d063bdc7c7517f32ac/scipy-1.15.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730", size = 25163884, upload-time = "2025-05-08T16:07:14.091Z" }, - { url = "https://files.pythonhosted.org/packages/a2/66/a9618b6a435a0f0c0b8a6d0a2efb32d4ec5a85f023c2b79d39512040355b/scipy-1.15.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825", size = 35174018, upload-time = "2025-05-08T16:07:19.427Z" }, - { url = "https://files.pythonhosted.org/packages/b5/09/c5b6734a50ad4882432b6bb7c02baf757f5b2f256041da5df242e2d7e6b6/scipy-1.15.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7", size = 37269716, upload-time = "2025-05-08T16:07:25.712Z" }, - { url = "https://files.pythonhosted.org/packages/77/0a/eac00ff741f23bcabd352731ed9b8995a0a60ef57f5fd788d611d43d69a1/scipy-1.15.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11", size = 36872342, upload-time = "2025-05-08T16:07:31.468Z" }, - { url = "https://files.pythonhosted.org/packages/fe/54/4379be86dd74b6ad81551689107360d9a3e18f24d20767a2d5b9253a3f0a/scipy-1.15.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126", size = 39670869, upload-time = "2025-05-08T16:07:38.002Z" }, - { url = "https://files.pythonhosted.org/packages/87/2e/892ad2862ba54f084ffe8cc4a22667eaf9c2bcec6d2bff1d15713c6c0703/scipy-1.15.3-cp313-cp313-win_amd64.whl", hash = "sha256:b90ab29d0c37ec9bf55424c064312930ca5f4bde15ee8619ee44e69319aab163", size = 40988851, upload-time = "2025-05-08T16:08:33.671Z" }, - { url = "https://files.pythonhosted.org/packages/1b/e9/7a879c137f7e55b30d75d90ce3eb468197646bc7b443ac036ae3fe109055/scipy-1.15.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8", size = 38863011, upload-time = "2025-05-08T16:07:44.039Z" }, - { url = "https://files.pythonhosted.org/packages/51/d1/226a806bbd69f62ce5ef5f3ffadc35286e9fbc802f606a07eb83bf2359de/scipy-1.15.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5", size = 30266407, upload-time = "2025-05-08T16:07:49.891Z" }, - { url = "https://files.pythonhosted.org/packages/e5/9b/f32d1d6093ab9eeabbd839b0f7619c62e46cc4b7b6dbf05b6e615bbd4400/scipy-1.15.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e", size = 22540030, upload-time = "2025-05-08T16:07:54.121Z" }, - { url = "https://files.pythonhosted.org/packages/e7/29/c278f699b095c1a884f29fda126340fcc201461ee8bfea5c8bdb1c7c958b/scipy-1.15.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb", size = 25218709, upload-time = "2025-05-08T16:07:58.506Z" }, - { url = "https://files.pythonhosted.org/packages/24/18/9e5374b617aba742a990581373cd6b68a2945d65cc588482749ef2e64467/scipy-1.15.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723", size = 34809045, upload-time = "2025-05-08T16:08:03.929Z" }, - { url = "https://files.pythonhosted.org/packages/e1/fe/9c4361e7ba2927074360856db6135ef4904d505e9b3afbbcb073c4008328/scipy-1.15.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb", size = 36703062, upload-time = "2025-05-08T16:08:09.558Z" }, - { url = "https://files.pythonhosted.org/packages/b7/8e/038ccfe29d272b30086b25a4960f757f97122cb2ec42e62b460d02fe98e9/scipy-1.15.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4", size = 36393132, upload-time = "2025-05-08T16:08:15.34Z" }, - { url = "https://files.pythonhosted.org/packages/10/7e/5c12285452970be5bdbe8352c619250b97ebf7917d7a9a9e96b8a8140f17/scipy-1.15.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5", size = 38979503, upload-time = "2025-05-08T16:08:21.513Z" }, - { url = "https://files.pythonhosted.org/packages/81/06/0a5e5349474e1cbc5757975b21bd4fad0e72ebf138c5592f191646154e06/scipy-1.15.3-cp313-cp313t-win_amd64.whl", hash = "sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca", size = 40308097, upload-time = "2025-05-08T16:08:27.627Z" }, -] - -[[package]] -name = "scipy" -version = "1.17.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.12'", - "python_full_version == '3.11.*'", -] -dependencies = [ - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/75/b4ce781849931fef6fd529afa6b63711d5a733065722d0c3e2724af9e40a/scipy-1.17.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1f95b894f13729334fb990162e911c9e5dc1ab390c58aa6cbecb389c5b5e28ec", size = 31613675, upload-time = "2026-02-23T00:16:00.13Z" }, - { url = "https://files.pythonhosted.org/packages/f7/58/bccc2861b305abdd1b8663d6130c0b3d7cc22e8d86663edbc8401bfd40d4/scipy-1.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e18f12c6b0bc5a592ed23d3f7b891f68fd7f8241d69b7883769eb5d5dfb52696", size = 28162057, upload-time = "2026-02-23T00:16:09.456Z" }, - { url = "https://files.pythonhosted.org/packages/6d/ee/18146b7757ed4976276b9c9819108adbc73c5aad636e5353e20746b73069/scipy-1.17.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a3472cfbca0a54177d0faa68f697d8ba4c80bbdc19908c3465556d9f7efce9ee", size = 20334032, upload-time = "2026-02-23T00:16:17.358Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e6/cef1cf3557f0c54954198554a10016b6a03b2ec9e22a4e1df734936bd99c/scipy-1.17.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:766e0dc5a616d026a3a1cffa379af959671729083882f50307e18175797b3dfd", size = 22709533, upload-time = "2026-02-23T00:16:25.791Z" }, - { url = "https://files.pythonhosted.org/packages/4d/60/8804678875fc59362b0fb759ab3ecce1f09c10a735680318ac30da8cd76b/scipy-1.17.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:744b2bf3640d907b79f3fd7874efe432d1cf171ee721243e350f55234b4cec4c", size = 33062057, upload-time = "2026-02-23T00:16:36.931Z" }, - { url = "https://files.pythonhosted.org/packages/09/7d/af933f0f6e0767995b4e2d705a0665e454d1c19402aa7e895de3951ebb04/scipy-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43af8d1f3bea642559019edfe64e9b11192a8978efbd1539d7bc2aaa23d92de4", size = 35349300, upload-time = "2026-02-23T00:16:49.108Z" }, - { url = "https://files.pythonhosted.org/packages/b4/3d/7ccbbdcbb54c8fdc20d3b6930137c782a163fa626f0aef920349873421ba/scipy-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd96a1898c0a47be4520327e01f874acfd61fb48a9420f8aa9f6483412ffa444", size = 35127333, upload-time = "2026-02-23T00:17:01.293Z" }, - { url = "https://files.pythonhosted.org/packages/e8/19/f926cb11c42b15ba08e3a71e376d816ac08614f769b4f47e06c3580c836a/scipy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4eb6c25dd62ee8d5edf68a8e1c171dd71c292fdae95d8aeb3dd7d7de4c364082", size = 37741314, upload-time = "2026-02-23T00:17:12.576Z" }, - { url = "https://files.pythonhosted.org/packages/95/da/0d1df507cf574b3f224ccc3d45244c9a1d732c81dcb26b1e8a766ae271a8/scipy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:d30e57c72013c2a4fe441c2fcb8e77b14e152ad48b5464858e07e2ad9fbfceff", size = 36607512, upload-time = "2026-02-23T00:17:23.424Z" }, - { url = "https://files.pythonhosted.org/packages/68/7f/bdd79ceaad24b671543ffe0ef61ed8e659440eb683b66f033454dcee90eb/scipy-1.17.1-cp311-cp311-win_arm64.whl", hash = "sha256:9ecb4efb1cd6e8c4afea0daa91a87fbddbce1b99d2895d151596716c0b2e859d", size = 24599248, upload-time = "2026-02-23T00:17:34.561Z" }, - { url = "https://files.pythonhosted.org/packages/35/48/b992b488d6f299dbe3f11a20b24d3dda3d46f1a635ede1c46b5b17a7b163/scipy-1.17.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:35c3a56d2ef83efc372eaec584314bd0ef2e2f0d2adb21c55e6ad5b344c0dcb8", size = 31610954, upload-time = "2026-02-23T00:17:49.855Z" }, - { url = "https://files.pythonhosted.org/packages/b2/02/cf107b01494c19dc100f1d0b7ac3cc08666e96ba2d64db7626066cee895e/scipy-1.17.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fcb310ddb270a06114bb64bbe53c94926b943f5b7f0842194d585c65eb4edd76", size = 28172662, upload-time = "2026-02-23T00:18:01.64Z" }, - { url = "https://files.pythonhosted.org/packages/cf/a9/599c28631bad314d219cf9ffd40e985b24d603fc8a2f4ccc5ae8419a535b/scipy-1.17.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cc90d2e9c7e5c7f1a482c9875007c095c3194b1cfedca3c2f3291cdc2bc7c086", size = 20344366, upload-time = "2026-02-23T00:18:12.015Z" }, - { url = "https://files.pythonhosted.org/packages/35/f5/906eda513271c8deb5af284e5ef0206d17a96239af79f9fa0aebfe0e36b4/scipy-1.17.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:c80be5ede8f3f8eded4eff73cc99a25c388ce98e555b17d31da05287015ffa5b", size = 22704017, upload-time = "2026-02-23T00:18:21.502Z" }, - { url = "https://files.pythonhosted.org/packages/da/34/16f10e3042d2f1d6b66e0428308ab52224b6a23049cb2f5c1756f713815f/scipy-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e19ebea31758fac5893a2ac360fedd00116cbb7628e650842a6691ba7ca28a21", size = 32927842, upload-time = "2026-02-23T00:18:35.367Z" }, - { url = "https://files.pythonhosted.org/packages/01/8e/1e35281b8ab6d5d72ebe9911edcdffa3f36b04ed9d51dec6dd140396e220/scipy-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02ae3b274fde71c5e92ac4d54bc06c42d80e399fec704383dcd99b301df37458", size = 35235890, upload-time = "2026-02-23T00:18:49.188Z" }, - { url = "https://files.pythonhosted.org/packages/c5/5c/9d7f4c88bea6e0d5a4f1bc0506a53a00e9fcb198de372bfe4d3652cef482/scipy-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a604bae87c6195d8b1045eddece0514d041604b14f2727bbc2b3020172045eb", size = 35003557, upload-time = "2026-02-23T00:18:54.74Z" }, - { url = "https://files.pythonhosted.org/packages/65/94/7698add8f276dbab7a9de9fb6b0e02fc13ee61d51c7c3f85ac28b65e1239/scipy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f590cd684941912d10becc07325a3eeb77886fe981415660d9265c4c418d0bea", size = 37625856, upload-time = "2026-02-23T00:19:00.307Z" }, - { url = "https://files.pythonhosted.org/packages/a2/84/dc08d77fbf3d87d3ee27f6a0c6dcce1de5829a64f2eae85a0ecc1f0daa73/scipy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:41b71f4a3a4cab9d366cd9065b288efc4d4f3c0b37a91a8e0947fb5bd7f31d87", size = 36549682, upload-time = "2026-02-23T00:19:07.67Z" }, - { url = "https://files.pythonhosted.org/packages/bc/98/fe9ae9ffb3b54b62559f52dedaebe204b408db8109a8c66fdd04869e6424/scipy-1.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:f4115102802df98b2b0db3cce5cb9b92572633a1197c77b7553e5203f284a5b3", size = 24547340, upload-time = "2026-02-23T00:19:12.024Z" }, - { url = "https://files.pythonhosted.org/packages/76/27/07ee1b57b65e92645f219b37148a7e7928b82e2b5dbeccecb4dff7c64f0b/scipy-1.17.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5e3c5c011904115f88a39308379c17f91546f77c1667cea98739fe0fccea804c", size = 31590199, upload-time = "2026-02-23T00:19:17.192Z" }, - { url = "https://files.pythonhosted.org/packages/ec/ae/db19f8ab842e9b724bf5dbb7db29302a91f1e55bc4d04b1025d6d605a2c5/scipy-1.17.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6fac755ca3d2c3edcb22f479fceaa241704111414831ddd3bc6056e18516892f", size = 28154001, upload-time = "2026-02-23T00:19:22.241Z" }, - { url = "https://files.pythonhosted.org/packages/5b/58/3ce96251560107b381cbd6e8413c483bbb1228a6b919fa8652b0d4090e7f/scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:7ff200bf9d24f2e4d5dc6ee8c3ac64d739d3a89e2326ba68aaf6c4a2b838fd7d", size = 20325719, upload-time = "2026-02-23T00:19:26.329Z" }, - { url = "https://files.pythonhosted.org/packages/b2/83/15087d945e0e4d48ce2377498abf5ad171ae013232ae31d06f336e64c999/scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4b400bdc6f79fa02a4d86640310dde87a21fba0c979efff5248908c6f15fad1b", size = 22683595, upload-time = "2026-02-23T00:19:30.304Z" }, - { url = "https://files.pythonhosted.org/packages/b4/e0/e58fbde4a1a594c8be8114eb4aac1a55bcd6587047efc18a61eb1f5c0d30/scipy-1.17.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b64ca7d4aee0102a97f3ba22124052b4bd2152522355073580bf4845e2550b6", size = 32896429, upload-time = "2026-02-23T00:19:35.536Z" }, - { url = "https://files.pythonhosted.org/packages/f5/5f/f17563f28ff03c7b6799c50d01d5d856a1d55f2676f537ca8d28c7f627cd/scipy-1.17.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:581b2264fc0aa555f3f435a5944da7504ea3a065d7029ad60e7c3d1ae09c5464", size = 35203952, upload-time = "2026-02-23T00:19:42.259Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a5/9afd17de24f657fdfe4df9a3f1ea049b39aef7c06000c13db1530d81ccca/scipy-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:beeda3d4ae615106d7094f7e7cef6218392e4465cc95d25f900bebabfded0950", size = 34979063, upload-time = "2026-02-23T00:19:47.547Z" }, - { url = "https://files.pythonhosted.org/packages/8b/13/88b1d2384b424bf7c924f2038c1c409f8d88bb2a8d49d097861dd64a57b2/scipy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6609bc224e9568f65064cfa72edc0f24ee6655b47575954ec6339534b2798369", size = 37598449, upload-time = "2026-02-23T00:19:53.238Z" }, - { url = "https://files.pythonhosted.org/packages/35/e5/d6d0e51fc888f692a35134336866341c08655d92614f492c6860dc45bb2c/scipy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:37425bc9175607b0268f493d79a292c39f9d001a357bebb6b88fdfaff13f6448", size = 36510943, upload-time = "2026-02-23T00:20:50.89Z" }, - { url = "https://files.pythonhosted.org/packages/2a/fd/3be73c564e2a01e690e19cc618811540ba5354c67c8680dce3281123fb79/scipy-1.17.1-cp313-cp313-win_arm64.whl", hash = "sha256:5cf36e801231b6a2059bf354720274b7558746f3b1a4efb43fcf557ccd484a87", size = 24545621, upload-time = "2026-02-23T00:20:55.871Z" }, - { url = "https://files.pythonhosted.org/packages/6f/6b/17787db8b8114933a66f9dcc479a8272e4b4da75fe03b0c282f7b0ade8cd/scipy-1.17.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:d59c30000a16d8edc7e64152e30220bfbd724c9bbb08368c054e24c651314f0a", size = 31936708, upload-time = "2026-02-23T00:19:58.694Z" }, - { url = "https://files.pythonhosted.org/packages/38/2e/524405c2b6392765ab1e2b722a41d5da33dc5c7b7278184a8ad29b6cb206/scipy-1.17.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:010f4333c96c9bb1a4516269e33cb5917b08ef2166d5556ca2fd9f082a9e6ea0", size = 28570135, upload-time = "2026-02-23T00:20:03.934Z" }, - { url = "https://files.pythonhosted.org/packages/fd/c3/5bd7199f4ea8556c0c8e39f04ccb014ac37d1468e6cfa6a95c6b3562b76e/scipy-1.17.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2ceb2d3e01c5f1d83c4189737a42d9cb2fc38a6eeed225e7515eef71ad301dce", size = 20741977, upload-time = "2026-02-23T00:20:07.935Z" }, - { url = "https://files.pythonhosted.org/packages/d9/b8/8ccd9b766ad14c78386599708eb745f6b44f08400a5fd0ade7cf89b6fc93/scipy-1.17.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:844e165636711ef41f80b4103ed234181646b98a53c8f05da12ca5ca289134f6", size = 23029601, upload-time = "2026-02-23T00:20:12.161Z" }, - { url = "https://files.pythonhosted.org/packages/6d/a0/3cb6f4d2fb3e17428ad2880333cac878909ad1a89f678527b5328b93c1d4/scipy-1.17.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:158dd96d2207e21c966063e1635b1063cd7787b627b6f07305315dd73d9c679e", size = 33019667, upload-time = "2026-02-23T00:20:17.208Z" }, - { url = "https://files.pythonhosted.org/packages/f3/c3/2d834a5ac7bf3a0c806ad1508efc02dda3c8c61472a56132d7894c312dea/scipy-1.17.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74cbb80d93260fe2ffa334efa24cb8f2f0f622a9b9febf8b483c0b865bfb3475", size = 35264159, upload-time = "2026-02-23T00:20:23.087Z" }, - { url = "https://files.pythonhosted.org/packages/4d/77/d3ed4becfdbd217c52062fafe35a72388d1bd82c2d0ba5ca19d6fcc93e11/scipy-1.17.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dbc12c9f3d185f5c737d801da555fb74b3dcfa1a50b66a1a93e09190f41fab50", size = 35102771, upload-time = "2026-02-23T00:20:28.636Z" }, - { url = "https://files.pythonhosted.org/packages/bd/12/d19da97efde68ca1ee5538bb261d5d2c062f0c055575128f11a2730e3ac1/scipy-1.17.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94055a11dfebe37c656e70317e1996dc197e1a15bbcc351bcdd4610e128fe1ca", size = 37665910, upload-time = "2026-02-23T00:20:34.743Z" }, - { url = "https://files.pythonhosted.org/packages/06/1c/1172a88d507a4baaf72c5a09bb6c018fe2ae0ab622e5830b703a46cc9e44/scipy-1.17.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e30bdeaa5deed6bc27b4cc490823cd0347d7dae09119b8803ae576ea0ce52e4c", size = 36562980, upload-time = "2026-02-23T00:20:40.575Z" }, - { url = "https://files.pythonhosted.org/packages/70/b0/eb757336e5a76dfa7911f63252e3b7d1de00935d7705cf772db5b45ec238/scipy-1.17.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a720477885a9d2411f94a93d16f9d89bad0f28ca23c3f8daa521e2dcc3f44d49", size = 24856543, upload-time = "2026-02-23T00:20:45.313Z" }, - { url = "https://files.pythonhosted.org/packages/cf/83/333afb452af6f0fd70414dc04f898647ee1423979ce02efa75c3b0f2c28e/scipy-1.17.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:a48a72c77a310327f6a3a920092fa2b8fd03d7deaa60f093038f22d98e096717", size = 31584510, upload-time = "2026-02-23T00:21:01.015Z" }, - { url = "https://files.pythonhosted.org/packages/ed/a6/d05a85fd51daeb2e4ea71d102f15b34fedca8e931af02594193ae4fd25f7/scipy-1.17.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:45abad819184f07240d8a696117a7aacd39787af9e0b719d00285549ed19a1e9", size = 28170131, upload-time = "2026-02-23T00:21:05.888Z" }, - { url = "https://files.pythonhosted.org/packages/db/7b/8624a203326675d7746a254083a187398090a179335b2e4a20e2ddc46e83/scipy-1.17.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:3fd1fcdab3ea951b610dc4cef356d416d5802991e7e32b5254828d342f7b7e0b", size = 20342032, upload-time = "2026-02-23T00:21:09.904Z" }, - { url = "https://files.pythonhosted.org/packages/c9/35/2c342897c00775d688d8ff3987aced3426858fd89d5a0e26e020b660b301/scipy-1.17.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7bdf2da170b67fdf10bca777614b1c7d96ae3ca5794fd9587dce41eb2966e866", size = 22678766, upload-time = "2026-02-23T00:21:14.313Z" }, - { url = "https://files.pythonhosted.org/packages/ef/f2/7cdb8eb308a1a6ae1e19f945913c82c23c0c442a462a46480ce487fdc0ac/scipy-1.17.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:adb2642e060a6549c343603a3851ba76ef0b74cc8c079a9a58121c7ec9fe2350", size = 32957007, upload-time = "2026-02-23T00:21:19.663Z" }, - { url = "https://files.pythonhosted.org/packages/0b/2e/7eea398450457ecb54e18e9d10110993fa65561c4f3add5e8eccd2b9cd41/scipy-1.17.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eee2cfda04c00a857206a4330f0c5e3e56535494e30ca445eb19ec624ae75118", size = 35221333, upload-time = "2026-02-23T00:21:25.278Z" }, - { url = "https://files.pythonhosted.org/packages/d9/77/5b8509d03b77f093a0d52e606d3c4f79e8b06d1d38c441dacb1e26cacf46/scipy-1.17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d2650c1fb97e184d12d8ba010493ee7b322864f7d3d00d3f9bb97d9c21de4068", size = 35042066, upload-time = "2026-02-23T00:21:31.358Z" }, - { url = "https://files.pythonhosted.org/packages/f9/df/18f80fb99df40b4070328d5ae5c596f2f00fffb50167e31439e932f29e7d/scipy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:08b900519463543aa604a06bec02461558a6e1cef8fdbb8098f77a48a83c8118", size = 37612763, upload-time = "2026-02-23T00:21:37.247Z" }, - { url = "https://files.pythonhosted.org/packages/4b/39/f0e8ea762a764a9dc52aa7dabcfad51a354819de1f0d4652b6a1122424d6/scipy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:3877ac408e14da24a6196de0ddcace62092bfc12a83823e92e49e40747e52c19", size = 37290984, upload-time = "2026-02-23T00:22:35.023Z" }, - { url = "https://files.pythonhosted.org/packages/7c/56/fe201e3b0f93d1a8bcf75d3379affd228a63d7e2d80ab45467a74b494947/scipy-1.17.1-cp314-cp314-win_arm64.whl", hash = "sha256:f8885db0bc2bffa59d5c1b72fad7a6a92d3e80e7257f967dd81abb553a90d293", size = 25192877, upload-time = "2026-02-23T00:22:39.798Z" }, - { url = "https://files.pythonhosted.org/packages/96/ad/f8c414e121f82e02d76f310f16db9899c4fcde36710329502a6b2a3c0392/scipy-1.17.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:1cc682cea2ae55524432f3cdff9e9a3be743d52a7443d0cba9017c23c87ae2f6", size = 31949750, upload-time = "2026-02-23T00:21:42.289Z" }, - { url = "https://files.pythonhosted.org/packages/7c/b0/c741e8865d61b67c81e255f4f0a832846c064e426636cd7de84e74d209be/scipy-1.17.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:2040ad4d1795a0ae89bfc7e8429677f365d45aa9fd5e4587cf1ea737f927b4a1", size = 28585858, upload-time = "2026-02-23T00:21:47.706Z" }, - { url = "https://files.pythonhosted.org/packages/ed/1b/3985219c6177866628fa7c2595bfd23f193ceebbe472c98a08824b9466ff/scipy-1.17.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:131f5aaea57602008f9822e2115029b55d4b5f7c070287699fe45c661d051e39", size = 20757723, upload-time = "2026-02-23T00:21:52.039Z" }, - { url = "https://files.pythonhosted.org/packages/c0/19/2a04aa25050d656d6f7b9e7b685cc83d6957fb101665bfd9369ca6534563/scipy-1.17.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9cdc1a2fcfd5c52cfb3045feb399f7b3ce822abdde3a193a6b9a60b3cb5854ca", size = 23043098, upload-time = "2026-02-23T00:21:56.185Z" }, - { url = "https://files.pythonhosted.org/packages/86/f1/3383beb9b5d0dbddd030335bf8a8b32d4317185efe495374f134d8be6cce/scipy-1.17.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e3dcd57ab780c741fde8dc68619de988b966db759a3c3152e8e9142c26295ad", size = 33030397, upload-time = "2026-02-23T00:22:01.404Z" }, - { url = "https://files.pythonhosted.org/packages/41/68/8f21e8a65a5a03f25a79165ec9d2b28c00e66dc80546cf5eb803aeeff35b/scipy-1.17.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9956e4d4f4a301ebf6cde39850333a6b6110799d470dbbb1e25326ac447f52a", size = 35281163, upload-time = "2026-02-23T00:22:07.024Z" }, - { url = "https://files.pythonhosted.org/packages/84/8d/c8a5e19479554007a5632ed7529e665c315ae7492b4f946b0deb39870e39/scipy-1.17.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a4328d245944d09fd639771de275701ccadf5f781ba0ff092ad141e017eccda4", size = 35116291, upload-time = "2026-02-23T00:22:12.585Z" }, - { url = "https://files.pythonhosted.org/packages/52/52/e57eceff0e342a1f50e274264ed47497b59e6a4e3118808ee58ddda7b74a/scipy-1.17.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a77cbd07b940d326d39a1d1b37817e2ee4d79cb30e7338f3d0cddffae70fcaa2", size = 37682317, upload-time = "2026-02-23T00:22:18.513Z" }, - { url = "https://files.pythonhosted.org/packages/11/2f/b29eafe4a3fbc3d6de9662b36e028d5f039e72d345e05c250e121a230dd4/scipy-1.17.1-cp314-cp314t-win_amd64.whl", hash = "sha256:eb092099205ef62cd1782b006658db09e2fed75bffcae7cc0d44052d8aa0f484", size = 37345327, upload-time = "2026-02-23T00:22:24.442Z" }, - { url = "https://files.pythonhosted.org/packages/07/39/338d9219c4e87f3e708f18857ecd24d22a0c3094752393319553096b98af/scipy-1.17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:200e1050faffacc162be6a486a984a0497866ec54149a01270adc8a59b7c7d21", size = 25489165, upload-time = "2026-02-23T00:22:29.563Z" }, -] - -[[package]] -name = "sentry-sdk" -version = "2.61.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/63/3b/4bc6b348bbd331daa14d4babe9f2b99bc854f4da41560eefb9488d78481d/sentry_sdk-2.61.1.tar.gz", hash = "sha256:9c6adccb3feefa9ba032c8d295ca477575c2f11896046a2b0ad686c47c4af555", size = 459429, upload-time = "2026-06-01T07:24:18.875Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/54/c9218db183846e08efaf68534889ef42e499dde432778881104a42f7071b/sentry_sdk-2.61.1-py3-none-any.whl", hash = "sha256:fa36eaf4b8ad708f718500d4bdcc1532637526a22beb874d88cbc0a46458b5ae", size = 483735, upload-time = "2026-06-01T07:24:17.027Z" }, -] - -[[package]] -name = "setuptools" -version = "82.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" }, -] - -[[package]] -name = "smmap" -version = "5.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1f/ea/49c993d6dfdd7338c9b1000a0f36817ed7ec84577ae2e52f890d1a4ff909/smmap-5.0.3.tar.gz", hash = "sha256:4d9debb8b99007ae47165abc08670bd74cb74b5227dda7f643eccc4e9eb5642c", size = 22506, upload-time = "2026-03-09T03:43:26.1Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl", hash = "sha256:c106e05d5a61449cf6ba9a1e650227ecfb141590d2a98412103ff35d89fc7b2f", size = 24390, upload-time = "2026-03-09T03:43:24.361Z" }, -] - -[[package]] -name = "sympy" -version = "1.14.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mpmath" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, -] - -[[package]] -name = "threadpoolctl" -version = "3.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, -] - -[[package]] -name = "torch" -version = "2.10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cuda-bindings", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "filelock" }, - { name = "fsspec" }, - { name = "jinja2" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "setuptools", marker = "python_full_version >= '3.12'" }, - { name = "sympy" }, - { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "typing-extensions" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/30/bfebdd8ec77db9a79775121789992d6b3b75ee5494971294d7b4b7c999bc/torch-2.10.0-2-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:2b980edd8d7c0a68c4e951ee1856334a43193f98730d97408fbd148c1a933313", size = 79411457, upload-time = "2026-02-10T21:44:59.189Z" }, - { url = "https://files.pythonhosted.org/packages/0f/8b/4b61d6e13f7108f36910df9ab4b58fd389cc2520d54d81b88660804aad99/torch-2.10.0-2-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:418997cb02d0a0f1497cf6a09f63166f9f5df9f3e16c8a716ab76a72127c714f", size = 79423467, upload-time = "2026-02-10T21:44:48.711Z" }, - { url = "https://files.pythonhosted.org/packages/d3/54/a2ba279afcca44bbd320d4e73675b282fcee3d81400ea1b53934efca6462/torch-2.10.0-2-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:13ec4add8c3faaed8d13e0574f5cd4a323c11655546f91fbe6afa77b57423574", size = 79498202, upload-time = "2026-02-10T21:44:52.603Z" }, - { url = "https://files.pythonhosted.org/packages/ec/23/2c9fe0c9c27f7f6cb865abcea8a4568f29f00acaeadfc6a37f6801f84cb4/torch-2.10.0-2-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:e521c9f030a3774ed770a9c011751fb47c4d12029a3d6522116e48431f2ff89e", size = 79498254, upload-time = "2026-02-10T21:44:44.095Z" }, - { url = "https://files.pythonhosted.org/packages/16/ee/efbd56687be60ef9af0c9c0ebe106964c07400eade5b0af8902a1d8cd58c/torch-2.10.0-3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a1ff626b884f8c4e897c4c33782bdacdff842a165fee79817b1dd549fdda1321", size = 915510070, upload-time = "2026-03-11T14:16:39.386Z" }, - { url = "https://files.pythonhosted.org/packages/36/ab/7b562f1808d3f65414cd80a4f7d4bb00979d9355616c034c171249e1a303/torch-2.10.0-3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ac5bdcbb074384c66fa160c15b1ead77839e3fe7ed117d667249afce0acabfac", size = 915518691, upload-time = "2026-03-11T14:15:43.147Z" }, - { url = "https://files.pythonhosted.org/packages/b3/7a/abada41517ce0011775f0f4eacc79659bc9bc6c361e6bfe6f7052a6b9363/torch-2.10.0-3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:98c01b8bb5e3240426dcde1446eed6f40c778091c8544767ef1168fc663a05a6", size = 915622781, upload-time = "2026-03-11T14:17:11.354Z" }, - { url = "https://files.pythonhosted.org/packages/ab/c6/4dfe238342ffdcec5aef1c96c457548762d33c40b45a1ab7033bb26d2ff2/torch-2.10.0-3-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:80b1b5bfe38eb0e9f5ff09f206dcac0a87aadd084230d4a36eea5ec5232c115b", size = 915627275, upload-time = "2026-03-11T14:16:11.325Z" }, - { url = "https://files.pythonhosted.org/packages/d8/f0/72bf18847f58f877a6a8acf60614b14935e2f156d942483af1ffc081aea0/torch-2.10.0-3-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:46b3574d93a2a8134b3f5475cfb98e2eb46771794c57015f6ad1fb795ec25e49", size = 915523474, upload-time = "2026-03-11T14:17:44.422Z" }, - { url = "https://files.pythonhosted.org/packages/f4/39/590742415c3030551944edc2ddc273ea1fdfe8ffb2780992e824f1ebee98/torch-2.10.0-3-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:b1d5e2aba4eb7f8e87fbe04f86442887f9167a35f092afe4c237dfcaaef6e328", size = 915632474, upload-time = "2026-03-11T14:15:13.666Z" }, - { url = "https://files.pythonhosted.org/packages/b6/8e/34949484f764dde5b222b7fe3fede43e4a6f0da9d7f8c370bb617d629ee2/torch-2.10.0-3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:0228d20b06701c05a8f978357f657817a4a63984b0c90745def81c18aedfa591", size = 915523882, upload-time = "2026-03-11T14:14:46.311Z" }, - { url = "https://files.pythonhosted.org/packages/0c/1a/c61f36cfd446170ec27b3a4984f072fd06dab6b5d7ce27e11adb35d6c838/torch-2.10.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5276fa790a666ee8becaffff8acb711922252521b28fbce5db7db5cf9cb2026d", size = 145992962, upload-time = "2026-01-21T16:24:14.04Z" }, - { url = "https://files.pythonhosted.org/packages/b5/60/6662535354191e2d1555296045b63e4279e5a9dbad49acf55a5d38655a39/torch-2.10.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:aaf663927bcd490ae971469a624c322202a2a1e68936eb952535ca4cd3b90444", size = 915599237, upload-time = "2026-01-21T16:23:25.497Z" }, - { url = "https://files.pythonhosted.org/packages/40/b8/66bbe96f0d79be2b5c697b2e0b187ed792a15c6c4b8904613454651db848/torch-2.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:a4be6a2a190b32ff5c8002a0977a25ea60e64f7ba46b1be37093c141d9c49aeb", size = 113720931, upload-time = "2026-01-21T16:24:23.743Z" }, - { url = "https://files.pythonhosted.org/packages/76/bb/d820f90e69cda6c8169b32a0c6a3ab7b17bf7990b8f2c680077c24a3c14c/torch-2.10.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:35e407430795c8d3edb07a1d711c41cc1f9eaddc8b2f1cc0a165a6767a8fb73d", size = 79411450, upload-time = "2026-01-21T16:25:30.692Z" }, - { url = "https://files.pythonhosted.org/packages/78/89/f5554b13ebd71e05c0b002f95148033e730d3f7067f67423026cc9c69410/torch-2.10.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:3282d9febd1e4e476630a099692b44fdc214ee9bf8ee5377732d9d9dfe5712e4", size = 145992610, upload-time = "2026-01-21T16:25:26.327Z" }, - { url = "https://files.pythonhosted.org/packages/ae/30/a3a2120621bf9c17779b169fc17e3dc29b230c29d0f8222f499f5e159aa8/torch-2.10.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a2f9edd8dbc99f62bc4dfb78af7bf89499bca3d753423ac1b4e06592e467b763", size = 915607863, upload-time = "2026-01-21T16:25:06.696Z" }, - { url = "https://files.pythonhosted.org/packages/6f/3d/c87b33c5f260a2a8ad68da7147e105f05868c281c63d65ed85aa4da98c66/torch-2.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:29b7009dba4b7a1c960260fc8ac85022c784250af43af9fb0ebafc9883782ebd", size = 113723116, upload-time = "2026-01-21T16:25:21.916Z" }, - { url = "https://files.pythonhosted.org/packages/61/d8/15b9d9d3a6b0c01b883787bd056acbe5cc321090d4b216d3ea89a8fcfdf3/torch-2.10.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:b7bd80f3477b830dd166c707c5b0b82a898e7b16f59a7d9d42778dd058272e8b", size = 79423461, upload-time = "2026-01-21T16:24:50.266Z" }, - { url = "https://files.pythonhosted.org/packages/cc/af/758e242e9102e9988969b5e621d41f36b8f258bb4a099109b7a4b4b50ea4/torch-2.10.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:5fd4117d89ffd47e3dcc71e71a22efac24828ad781c7e46aaaf56bf7f2796acf", size = 145996088, upload-time = "2026-01-21T16:24:44.171Z" }, - { url = "https://files.pythonhosted.org/packages/23/8e/3c74db5e53bff7ed9e34c8123e6a8bfef718b2450c35eefab85bb4a7e270/torch-2.10.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:787124e7db3b379d4f1ed54dd12ae7c741c16a4d29b49c0226a89bea50923ffb", size = 915711952, upload-time = "2026-01-21T16:23:53.503Z" }, - { url = "https://files.pythonhosted.org/packages/6e/01/624c4324ca01f66ae4c7cd1b74eb16fb52596dce66dbe51eff95ef9e7a4c/torch-2.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:2c66c61f44c5f903046cc696d088e21062644cbe541c7f1c4eaae88b2ad23547", size = 113757972, upload-time = "2026-01-21T16:24:39.516Z" }, - { url = "https://files.pythonhosted.org/packages/c9/5c/dee910b87c4d5c0fcb41b50839ae04df87c1cfc663cf1b5fca7ea565eeaa/torch-2.10.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:6d3707a61863d1c4d6ebba7be4ca320f42b869ee657e9b2c21c736bf17000294", size = 79498198, upload-time = "2026-01-21T16:24:34.704Z" }, - { url = "https://files.pythonhosted.org/packages/c9/6f/f2e91e34e3fcba2e3fc8d8f74e7d6c22e74e480bbd1db7bc8900fdf3e95c/torch-2.10.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:5c4d217b14741e40776dd7074d9006fd28b8a97ef5654db959d8635b2fe5f29b", size = 146004247, upload-time = "2026-01-21T16:24:29.335Z" }, - { url = "https://files.pythonhosted.org/packages/98/fb/5160261aeb5e1ee12ee95fe599d0541f7c976c3701d607d8fc29e623229f/torch-2.10.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6b71486353fce0f9714ca0c9ef1c850a2ae766b409808acd58e9678a3edb7738", size = 915716445, upload-time = "2026-01-21T16:22:45.353Z" }, - { url = "https://files.pythonhosted.org/packages/6a/16/502fb1b41e6d868e8deb5b0e3ae926bbb36dab8ceb0d1b769b266ad7b0c3/torch-2.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:c2ee399c644dc92ef7bc0d4f7e74b5360c37cdbe7c5ba11318dda49ffac2bc57", size = 113757050, upload-time = "2026-01-21T16:24:19.204Z" }, - { url = "https://files.pythonhosted.org/packages/1a/0b/39929b148f4824bc3ad6f9f72a29d4ad865bcf7ebfc2fa67584773e083d2/torch-2.10.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:3202429f58309b9fa96a614885eace4b7995729f44beb54d3e4a47773649d382", size = 79851305, upload-time = "2026-01-21T16:24:09.209Z" }, - { url = "https://files.pythonhosted.org/packages/d8/14/21fbce63bc452381ba5f74a2c0a959fdf5ad5803ccc0c654e752e0dbe91a/torch-2.10.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:aae1b29cd68e50a9397f5ee897b9c24742e9e306f88a807a27d617f07adb3bd8", size = 146005472, upload-time = "2026-01-21T16:22:29.022Z" }, - { url = "https://files.pythonhosted.org/packages/54/fd/b207d1c525cb570ef47f3e9f836b154685011fce11a2f444ba8a4084d042/torch-2.10.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:6021db85958db2f07ec94e1bc77212721ba4920c12a18dc552d2ae36a3eb163f", size = 915612644, upload-time = "2026-01-21T16:21:47.019Z" }, - { url = "https://files.pythonhosted.org/packages/36/53/0197f868c75f1050b199fe58f9bf3bf3aecac9b4e85cc9c964383d745403/torch-2.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff43db38af76fda183156153983c9a096fc4c78d0cd1e07b14a2314c7f01c2c8", size = 113997015, upload-time = "2026-01-21T16:23:00.767Z" }, - { url = "https://files.pythonhosted.org/packages/0e/13/e76b4d9c160e89fff48bf16b449ea324bda84745d2ab30294c37c2434c0d/torch-2.10.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:cdf2a523d699b70d613243211ecaac14fe9c5df8a0b0a9c02add60fb2a413e0f", size = 79498248, upload-time = "2026-01-21T16:23:09.315Z" }, - { url = "https://files.pythonhosted.org/packages/4f/93/716b5ac0155f1be70ed81bacc21269c3ece8dba0c249b9994094110bfc51/torch-2.10.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:bf0d9ff448b0218e0433aeb198805192346c4fd659c852370d5cc245f602a06a", size = 79464992, upload-time = "2026-01-21T16:23:05.162Z" }, - { url = "https://files.pythonhosted.org/packages/69/2b/51e663ff190c9d16d4a8271203b71bc73a16aa7619b9f271a69b9d4a936b/torch-2.10.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:233aed0659a2503b831d8a67e9da66a62c996204c0bba4f4c442ccc0c68a3f60", size = 146018567, upload-time = "2026-01-21T16:22:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/5e/cd/4b95ef7f293b927c283db0b136c42be91c8ec6845c44de0238c8c23bdc80/torch-2.10.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:682497e16bdfa6efeec8cde66531bc8d1fbbbb4d8788ec6173c089ed3cc2bfe5", size = 915721646, upload-time = "2026-01-21T16:21:16.983Z" }, - { url = "https://files.pythonhosted.org/packages/56/97/078a007208f8056d88ae43198833469e61a0a355abc0b070edd2c085eb9a/torch-2.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:6528f13d2a8593a1a412ea07a99812495bec07e9224c28b2a25c0a30c7da025c", size = 113752373, upload-time = "2026-01-21T16:22:13.471Z" }, - { url = "https://files.pythonhosted.org/packages/d8/94/71994e7d0d5238393df9732fdab607e37e2b56d26a746cb59fdb415f8966/torch-2.10.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:f5ab4ba32383061be0fb74bda772d470140a12c1c3b58a0cfbf3dae94d164c28", size = 79850324, upload-time = "2026-01-21T16:22:09.494Z" }, - { url = "https://files.pythonhosted.org/packages/e2/65/1a05346b418ea8ccd10360eef4b3e0ce688fba544e76edec26913a8d0ee0/torch-2.10.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:716b01a176c2a5659c98f6b01bf868244abdd896526f1c692712ab36dbaf9b63", size = 146006482, upload-time = "2026-01-21T16:22:18.42Z" }, - { url = "https://files.pythonhosted.org/packages/1d/b9/5f6f9d9e859fc3235f60578fa64f52c9c6e9b4327f0fe0defb6de5c0de31/torch-2.10.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:d8f5912ba938233f86361e891789595ff35ca4b4e2ac8fe3670895e5976731d6", size = 915613050, upload-time = "2026-01-21T16:20:49.035Z" }, - { url = "https://files.pythonhosted.org/packages/66/4d/35352043ee0eaffdeff154fad67cd4a31dbed7ff8e3be1cc4549717d6d51/torch-2.10.0-cp314-cp314t-win_amd64.whl", hash = "sha256:71283a373f0ee2c89e0f0d5f446039bdabe8dbc3c9ccf35f0f784908b0acd185", size = 113995816, upload-time = "2026-01-21T16:22:05.312Z" }, -] - -[[package]] -name = "triton" -version = "3.6.0" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/f7/f1c9d3424ab199ac53c2da567b859bcddbb9c9e7154805119f8bd95ec36f/triton-3.6.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6550fae429e0667e397e5de64b332d1e5695b73650ee75a6146e2e902770bea", size = 188105201, upload-time = "2026-01-20T16:00:29.272Z" }, - { url = "https://files.pythonhosted.org/packages/e0/12/b05ba554d2c623bffa59922b94b0775673de251f468a9609bc9e45de95e9/triton-3.6.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8e323d608e3a9bfcc2d9efcc90ceefb764a82b99dea12a86d643c72539ad5d3", size = 188214640, upload-time = "2026-01-20T16:00:35.869Z" }, - { url = "https://files.pythonhosted.org/packages/ab/a8/cdf8b3e4c98132f965f88c2313a4b493266832ad47fb52f23d14d4f86bb5/triton-3.6.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74caf5e34b66d9f3a429af689c1c7128daba1d8208df60e81106b115c00d6fca", size = 188266850, upload-time = "2026-01-20T16:00:43.041Z" }, - { url = "https://files.pythonhosted.org/packages/f9/0b/37d991d8c130ce81a8728ae3c25b6e60935838e9be1b58791f5997b24a54/triton-3.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10c7f76c6e72d2ef08df639e3d0d30729112f47a56b0c81672edc05ee5116ac9", size = 188289450, upload-time = "2026-01-20T16:00:49.136Z" }, - { url = "https://files.pythonhosted.org/packages/35/f8/9c66bfc55361ec6d0e4040a0337fb5924ceb23de4648b8a81ae9d33b2b38/triton-3.6.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d002e07d7180fd65e622134fbd980c9a3d4211fb85224b56a0a0efbd422ab72f", size = 188400296, upload-time = "2026-01-20T16:00:56.042Z" }, - { url = "https://files.pythonhosted.org/packages/df/3d/9e7eee57b37c80cec63322c0231bb6da3cfe535a91d7a4d64896fcb89357/triton-3.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a17a5d5985f0ac494ed8a8e54568f092f7057ef60e1b0fa09d3fd1512064e803", size = 188273063, upload-time = "2026-01-20T16:01:07.278Z" }, - { url = "https://files.pythonhosted.org/packages/f6/56/6113c23ff46c00aae423333eb58b3e60bdfe9179d542781955a5e1514cb3/triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7", size = 188397994, upload-time = "2026-01-20T16:01:14.236Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, -] - -[[package]] -name = "typing-inspection" -version = "0.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, -] - -[[package]] -name = "urllib3" -version = "2.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, -] - -[[package]] -name = "wandb" -version = "0.27.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "gitpython" }, - { name = "packaging" }, - { name = "platformdirs" }, - { name = "protobuf" }, - { name = "pydantic" }, - { name = "pyyaml" }, - { name = "requests" }, - { name = "sentry-sdk" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fa/a4/35a6e297ca22e0e2cbe77cbd21d33462874b8f508b41f0fae2f4c292ffea/wandb-0.27.1.tar.gz", hash = "sha256:214b3430c1e76e5eb55192c1bb4184a084f969e0c75cb15a709324c069efe10e", size = 40298729, upload-time = "2026-06-04T04:28:23.42Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/e2/3fa03632b11cbaa8b078a2c50fd985b6009a3ffcc566c3898d430c987cd1/wandb-0.27.1-py3-none-macosx_12_0_arm64.whl", hash = "sha256:abd80f9f74a4c28890c2a38d356fb0bceadfce497311ff13431ff387f4f23698", size = 23985424, upload-time = "2026-06-04T04:28:01.685Z" }, - { url = "https://files.pythonhosted.org/packages/91/4a/4dfa139ccce0f686eef44e3b8a3efc535333e4275e8fddcb68ff03b4f60d/wandb-0.27.1-py3-none-macosx_12_0_x86_64.whl", hash = "sha256:2fb189f186f2a41df9c559918a3308da22069dca5bc6f021bfface197a9aa1ff", size = 25164981, upload-time = "2026-06-04T04:28:04.593Z" }, - { url = "https://files.pythonhosted.org/packages/16/4e/872bd057b83a6c5da5c4e8c482c41df9338898a3c34d26172eb306388b8c/wandb-0.27.1-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:1c62f0daae156b1f56b2ac78e3dab25a19c8e4ef73b83547c0c877c77d433e9a", size = 24551609, upload-time = "2026-06-04T04:28:06.786Z" }, - { url = "https://files.pythonhosted.org/packages/02/cd/0cd5ef8723581cef8d6ecb9de29623a3da89c969b04f32809c1f22985ba2/wandb-0.27.1-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:77ccc56582d07671a867b78ced686096ca53c021d79fed38e5aaf4cebbd0450b", size = 26378746, upload-time = "2026-06-04T04:28:09.015Z" }, - { url = "https://files.pythonhosted.org/packages/46/3e/9fbaac13e1ffcecb3265dd08b8a76a15c21361ccbc0c65ab745847268240/wandb-0.27.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:af91e99c0dc02de03997169d218698e1175fca1cd06d5af5ef68a855c0cf6968", size = 24724687, upload-time = "2026-06-04T04:28:11.651Z" }, - { url = "https://files.pythonhosted.org/packages/3d/2b/f9c5bffd1f858c2c0a6dd41a8b5dea3d9337d4282dd247e603e1bea98d80/wandb-0.27.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a79f899de9861543bf20f273cdfe513c85d6841e88568aa1de05ce4a0a5a547b", size = 26690886, upload-time = "2026-06-04T04:28:14.364Z" }, - { url = "https://files.pythonhosted.org/packages/fa/14/91283eb2a17063091858f9f2b822437b16d75e53d742e295db1b479dcca4/wandb-0.27.1-py3-none-win32.whl", hash = "sha256:38dd902b56188789d3b17c03f8f77fef97a0b1c5aecee94c5bd70836e667b2cf", size = 24149484, upload-time = "2026-06-04T04:28:16.712Z" }, - { url = "https://files.pythonhosted.org/packages/7a/a9/7d25ba1386fe6b4dea65342aba7c82a9ac3294cb591b449622b31d75e0c5/wandb-0.27.1-py3-none-win_amd64.whl", hash = "sha256:2d7e4bb60a28756a0a6c78884dbad60bcd07e7cf840cdd8e3a3873bdf2af62af", size = 24149492, upload-time = "2026-06-04T04:28:18.85Z" }, - { url = "https://files.pythonhosted.org/packages/5d/b4/ed3e3410d0a86b9542c4cd3839f47bd72493063240759e7120f38637a9c0/wandb-0.27.1-py3-none-win_arm64.whl", hash = "sha256:356eda1fbce41101286935f5291e7db5ddfd062b23ddbbdd1d45ed03763ba2c4", size = 22059130, upload-time = "2026-06-04T04:28:20.985Z" }, -]