diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml new file mode 100644 index 0000000..697ac93 --- /dev/null +++ b/.github/workflows/documentation.yml @@ -0,0 +1,173 @@ +name: Example documentation +on: + pull_request: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + +env: + SEQUINCE_LOCK_READY: "1" + +jobs: + manifest: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.manifest.outputs.matrix }} + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.sha }} + persist-credentials: false + - uses: prefix-dev/setup-pixi@v0.9.3 + with: + pixi-version: v0.81.0 + manifest-path: pyproject.toml + run-install: false + cache: false + - name: Resolve current Sequince main once for this workflow + run: pixi update sequince + - uses: actions/upload-artifact@v4 + with: + name: execution-lock + path: pixi.lock + retention-days: 14 + if-no-files-found: error + - id: manifest + run: echo "matrix=$(python3 scripts/docs_pipeline.py matrix)" >> "$GITHUB_OUTPUT" + + examples: + needs: manifest + runs-on: ubuntu-latest + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: ${{ fromJSON(needs.manifest.outputs.matrix) }} + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.sha }} + persist-credentials: false + - uses: actions/download-artifact@v4 + with: + name: execution-lock + path: . + - uses: prefix-dev/setup-pixi@v0.9.3 + with: + pixi-version: v0.81.0 + manifest-path: pyproject.toml + environments: ${{ matrix.environment }} + cache: true + locked: true + - run: pixi run --locked -e '${{ matrix.environment }}' example '${{ matrix.id }}' + - uses: actions/upload-artifact@v4 + with: + name: example-${{ matrix.id }} + path: build-docs/outputs/${{ matrix.id }}/ + if-no-files-found: error + retention-days: 14 + - uses: actions/upload-artifact@v4 + if: always() + with: + name: log-${{ matrix.id }} + path: build-docs/*.log + retention-days: 7 + + documentation: + needs: examples + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.sha }} + persist-credentials: false + - uses: actions/download-artifact@v4 + with: + name: execution-lock + path: . + - uses: prefix-dev/setup-pixi@v0.9.3 + with: + pixi-version: v0.81.0 + manifest-path: pyproject.toml + environments: docs check + cache: true + locked: true + # No run-id: download-artifact takes artifacts from this workflow run. + - uses: actions/download-artifact@v4 + with: + pattern: example-* + path: build-docs/downloads + - name: Collect example artifacts from this workflow + run: | + python3 - <<'PYTHON' + import json + import shutil + from pathlib import Path + destination = Path('build-docs/outputs') + destination.mkdir(parents=True) + for example in json.loads(Path('docs/examples.json').read_text())['examples']: + shutil.move(Path('build-docs/downloads') / ('example-' + example['id']), destination / example['id']) + PYTHON + - run: pixi run --locked -e check test + - run: pixi run --locked -e check lint + - run: pixi run --locked -e check format-check + - run: pixi run --locked -e docs docs + - run: pixi run --locked -e docs bundle + - uses: actions/upload-artifact@v4 + with: + name: validated-bundle + path: | + build-docs/docs-outputs-*.tar.gz + build-docs/docs-outputs-*.tar.gz.sha256 + if-no-files-found: error + retention-days: 14 + - uses: actions/upload-artifact@v4 + with: + name: documentation-html + path: docs/_build/html/ + if-no-files-found: error + retention-days: 14 + + publish: + needs: documentation + if: github.repository == 'sequince-dev/sequince-examples' && github.ref == 'refs/heads/main' && github.event_name != 'pull_request' + runs-on: ubuntu-latest + timeout-minutes: 55 + concurrency: + group: examples-rtd-latest + cancel-in-progress: false + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.sha }} + persist-credentials: false + - uses: actions/download-artifact@v4 + with: + name: execution-lock + path: . + - uses: actions/download-artifact@v4 + with: + name: validated-bundle + path: build-docs + - name: Verify same-run bundle before publication + run: | + python3 - <<'PY' + import sys + from pathlib import Path + sys.path.insert(0, 'scripts') + from docs_pipeline import ROOT, OUTPUT, digest, git, unpack + sha = git('rev-parse', 'HEAD') + path = ROOT / 'build-docs' / f'docs-outputs-{sha}.tar.gz' + assert Path(str(path) + '.sha256').read_text().split() == [digest(path), path.name] + unpack(path, sha, OUTPUT) + PY + - name: Publish durable assets and wait for RTD + run: python3 scripts/publish_docs.py + env: + GH_TOKEN: ${{ github.token }} + RTD_TOKEN: ${{ secrets.RTD_TOKEN }} + RTD_PROJECT: ${{ vars.RTD_PROJECT }} diff --git a/.gitignore b/.gitignore index 0f77cd0..96d806a 100644 --- a/.gitignore +++ b/.gitignore @@ -225,3 +225,7 @@ outdir*/ # Checkpoint files *.pickle *.npz + +# Documentation execution, bundles and generated Sphinx sources +build-docs/ +docs/_generated/ diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..418df24 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,16 @@ +version: 2 +build: + os: ubuntu-24.04 + tools: + python: "3.13" + jobs: + pre_build: + - python scripts/docs_pipeline.py fetch + post_build: + - python scripts/docs_pipeline.py check-latest +python: + install: + - requirements: docs/requirements.txt +sphinx: + configuration: docs/conf.py + fail_on_warning: true diff --git a/MAINTENANCE.rst b/MAINTENANCE.rst new file mode 100644 index 0000000..7fb8818 --- /dev/null +++ b/MAINTENANCE.rst @@ -0,0 +1,284 @@ +Setup and maintenance +===================== + +Local execution +--------------- + +Install Pixi 0.81.0 or newer, then run from the repository root:: + + pixi install --locked -e default -e docs -e check + pixi run --locked example gaussian + pixi run --locked example rosenbrock + pixi run --locked example adaptive-mutation + pixi run --locked -e docs docs + pixi run --locked -e check test + pixi run --locked -e check lint + pixi run --locked -e check format-check + pixi run --locked -e check pre-commit run --all-files + +The Makefile in ``docs/`` wraps these Pixi tasks. From the repository root:: + + make -C docs setup + make -C docs check + make -C docs example EXAMPLE=rosenbrock + make -C docs examples + make -C docs docs + make -C docs docs-only + make -C docs validate + +``make -C docs docs`` checks existing output completeness and provenance, then builds +the full Sphinx site. It reruns examples only if outputs are missing or stale. +``make -C docs validate`` runs checks, explicitly reruns all examples, then builds the +site. Both preserve execution order with ``make -C docs -j``. +You can also change into ``docs/`` and run these targets without ``-C docs``. ``make -C docs docs-only`` never +executes examples and fails if outputs are missing or stale. Use this target +when editing documentation on a machine with limited memory. Use ``make -C docs example EXAMPLE= ENV=`` for +a single example requiring another backend. ``make -C docs hooks`` runs all pre-commit +hooks and may edit files. ``make -C docs help`` lists the available targets. The +``SEQUINCE_INVENTORY`` environment override described below also works with make. + +``pixi run --locked examples`` runs every manifest entry using its declared +environment. Open ``docs/_build/html/index.html`` after a successful build. +The maintained narratives remain beside the scripts under ``examples/``. +The pipeline copies them and script sources into ``docs/_generated`` so relative ``literalinclude`` +paths still resolve. Both that directory and ``build-docs`` are ignored. + +The runner imports each script and overrides only its output-directory global. +It preserves the original working directory, parameters, sampler, seed and RNG +calls. For CLI examples the runner passes the script defaults without the +pipeline's own arguments. The three additional scripts write concise JSON +statistics only when the runner sets ``documentation_output``; ordinary script +execution keeps its existing output behavior. Matplotlib uses Agg. Raw particles and HDF5 results live in a temporary +run directory and are removed after extracting plots and weighted summaries. +Logs remain in ``build-docs``. The covariance in the summary is the weighted +population covariance; it does not apply a finite-sample correction. No target, +weighting, evidence convention or mutation setting is changed for publication. + +Dependencies and new examples +----------------------------- + +During development, Sequince follows the latest ``main`` branch in +``pyproject.toml``. Normal example commands refresh that dependency before +execution. ``make -C docs setup`` and ``make -C docs docs`` refresh it too; +``make -C docs docs-only`` uses the existing snapshot and never runs sampling. +Use ``make -C docs refresh`` to resolve main without executing examples. +Direct standalone Python script execution uses whichever Sequince is installed; +use the runner commands for automatic refresh. + +Actions resolves main once in the manifest job, shares the resulting +``pixi.lock`` with every matrix job, and uses locked installs afterward. This +keeps one workflow internally consistent even if main advances during the run. +``SEQUINCE_LOCK_READY=1`` is reserved for such already-resolved batches or +intentional reproduction of an archived execution; it suppresses refresh. + +``pixi.lock`` remains versioned as a bootstrap snapshot. Metadata verifies and +records the installed version and exact resolved Git commit. Each output bundle +includes ``execution-pixi.lock`` with checksums. RTD restores that archived lock +before validating outputs; it does not fetch today's main. The publishing job +allows this intentional lock-file change but rejects other tracked edits. + +Separate executions of the same example source commit can now produce different +results as Sequince main changes. Existing immutable-by-policy bundles still +cannot be overwritten with different bytes: publish a new example source commit +for a new dependency snapshot. To reproduce an old run, use its archived lock +and ``SEQUINCE_LOCK_READY=1``. This development policy supersedes the original +requirement that dependency resolution be identical for every run of a source +commit. When Sequince releases are available, replace the branch dependency with +the latest compatible release and revise the refresh command at the same time. + +The docs environment contains no sampler or optional backend. The separate +``flow`` environment uses CPU-only PyTorch and pins ``sequince-density`` to a +full Git commit in its repository (currently named ``sequince-flows``). Both Git +dependency commits and installed versions are verified and recorded for flow +runs. The separate ``jax`` environment is a CPU template for future examples. + +The Sequince repository is currently private and there is no public PyPI +release. Existing SSH access was used to resolve the local lock. To use existing +SSH access locally without changing Git configuration permanently:: + + GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=url.git@github.com:.insteadOf \ + GIT_CONFIG_VALUE_0=https://github.com/ pixi install --locked + +Before enabling this workflow for public/fork PRs, make the dependency +publicly readable (or replace it with a compatible exact public release and +regenerate the lock). Never add a private dependency token to PR execution. +RTD only installs Sphinx and reads public output assets, so it needs no Sequince +repository access or sampling environment. The Sequince documentation inventory +at ``https://sequince.readthedocs.io/en/latest/objects.inv`` must also be publicly +readable; current probes returned HTTP 403/404 in this environment. For local +validation against an inventory generated by the main Sequince Sphinx build, +set ``SEQUINCE_INVENTORY=/absolute/path/to/objects.inv``. This overrides only the +inventory location; generated API links still point to the main Sequince docs. + +Place new example directories under ``examples/`` and add a record to +``docs/examples.json`` with a unique ID, script, working +directory, environment, timeout in seconds, narrative, parameter globals, +required raw outputs and captioned figures. Scripts must expose ``main()`` and +``outdir``, save weighted particles in ``posterior.npz`` with ``particles``, +``weights`` and scalar ``log_evidence``, and list every required output. +Seed must be a declared parameter. Examples can provide a raw ``summary.json`` +instead of ``posterior.npz``; the runner validates and packages it without +rendering it in the tutorial. CLI examples expose ``build_parser()`` so metadata +records their actual defaults. Optional Git dependencies list their feature +names under ``dependency_features`` in the execution manifest. Add backend features/environments to +``pyproject.toml`` and regenerate the lock as needed. The execution matrix comes from the manifest; no workflow edit is needed for +another entry. Put figure directives, captions, and summary includes directly +in the example README using ``/_generated/results//`` paths. The staging +step copies the narrative, script, and validated outputs without creating a +separate result page. Link its staged README from ``docs/index.rst`` in reading +order. Narrative-only tutorials keep their ``README.rst`` beside the script in +``examples/``. Add their directory and source filename to the staging list in +``prepare()`` and link the staged README from ``docs/index.rst``. This preserves +relative source includes without executing optional backends or adding an +execution-manifest entry. Keep contributor and hosting instructions +in this repository document rather than the tutorial navigation. + +Provenance and durable bundles +------------------------------ + +Every example records the checked-out source SHA, hashes of its script and +narrative, manifest, runner and Pixi inputs, installed Sequince version/resolved main commit, +Python version, environment, parameters and seed. Per-example checksums are +validated before Sphinx. Local edited inputs are fingerprinted, but publication +requires a clean tracked checkout except for the resolved execution lock. Actions checks out the same SHA in each job +(including the synthetic merge commit on PRs) and downloads only same-run artifacts. + +Trusted main runs create ``docs-outputs-.tar.gz`` and a companion +``.sha256`` asset on the dedicated prerelease ``docs-output-`` in +**sequince-examples**, never the library repository. The archive includes plots, +concise summaries, provenance, checksums, ``execution-pixi.lock`` and +``bundle-manifest.json``. It excludes +raw chains, checkpoints and HDF5 files. Tar and gzip headers are deterministic. +Existing assets are downloaded and compared before reuse; different bytes fail +rather than replacing archived results. Draft releases allow recovery from a +partial upload. Do not move these tags or edit release assets. + +Retain release assets for as long as any corresponding RTD version may be +rebuilt. Actions artifacts expire after 14 days (logs after 7) and serve only +intermediate/PR validation. To regenerate, check out the exact source revision, +restore the bundle execution lock and set ``SEQUINCE_LOCK_READY=1`` before +executing examples, building docs and creating the bundle. A rerun +with different results intentionally fails publication: investigate the mismatch, +then publish a new source commit instead of replacing history. Hardware/BLAS +variation can change numerical or figure bytes despite a fixed seed. + +Read the Docs Community setup +----------------------------- + +These are manual maintainer steps; no external settings or credentials are +created by this change. + +#. Make sequince-examples and its output release assets public. Import the repo + at ``app.readthedocs.org`` as a separate Community project, with main as the + default branch and ``.readthedocs.yaml`` as its configuration. +#. Keep ``latest`` active/default. Disable automatic push/tag builds and PR + builds for this project. For a GitHub App integration, use automation rules + with an enabled **Trigger build for version** rule that never matches real + refs (for example a reserved ``^disabled-auto-build$`` ref), and remove other + build-trigger rules. Such rules gate webhook builds; explicit API builds + remain available. For legacy webhook integrations, disable automatic builds + on the integration. Verify no ordinary push starts a build before enabling + publication. Do not enable rules that activate ``docs-output-*`` tags. +#. In the main Sequince project's **Subprojects** settings attach this imported + project with alias ``examples``. Select the examples default version as + latest. Its URL is ``https://sequince.readthedocs.io/projects/examples/en/latest/``. + The main Sequince build system remains unchanged. A parent navigation link to + this URL can be added when the subproject is live. +#. Set GitHub Actions variable ``RTD_PROJECT`` to the imported project's slug + and secret ``RTD_TOKEN`` to an RTD Community API token. Tokens inherit their + user's permissions: use a dedicated maintainer account with access only to + this project where possible. RTD needs no GitHub publishing token. The workflow + uses the automatic GitHub token with ``contents: read`` for validation and + ``contents: write`` only for the trusted publishing job. Allow Actions to + create releases in the examples repository. No secrets enter PR jobs. +#. Merge the files, then run the workflow on main once dependency access is + ready. Inspect the release assets and monitored RTD result. Initial import + builds can fail until the first matching bundle is uploaded; rerun afterward. + +RTD runs standard Sphinx on Community, downloading and verifying the exact bundle +for ``git rev-parse HEAD`` in ``pre_build``. It never falls back to an older +bundle. A trigger for ``latest`` does not pin a commit: the actual checkout is +verified and must have its own matching bundle. Main freshness is checked before +Sphinx and in ``post_build``, before RTD's upload. + +The publishing job holds a non-cancelling concurrency lock until RTD finishes, +waits for any existing RTD builds, and checks current main before triggering. +Stale workflow runs skip triggering. A newer push during an older RTD build +causes the freshness hook to fail; its workflow then releases the lock and the +newer run can build. This prevents older workflow builds from overwriting newer +successful docs. Do not run concurrent manual builds or enable automatic builds: +RTD has no customizable upload hook or API commit-pinning guarantee. After a +monitor timeout or manual cancellation, inspect/drain RTD builds before retrying; +the next publishing job also waits for active builds. + +Tagged versions and stable +-------------------------- + +Initially publish only main/latest. Do not automatically activate artifact tags +or a stable alias. For a curated example release, choose an immutable ``vX.Y.Z`` +tag on a main commit whose output bundle already exists; sync and activate that +version in RTD, then manually build it. The hook downloads the bundle for the +tag's actual checkout. Configure stable to the newest curated release only +when adopting this policy. Never retarget a release tag; artifact prereleases +are not example releases. The current workflow intentionally does not trigger +tag/stable builds or create semantic version tags. + +PRs and troubleshooting +----------------------- + +PRs execute the full matrix and build Sphinx, with downloadable HTML artifacts. +They do not publish releases, receive RTD credentials or create RTD previews. +Use the HTML artifact for review. RTD PR previews would require a separate +trusted bundle/authorization policy and are disabled here. + +For failed examples, inspect the named job and its log artifact, reproduce with +``pixi run --locked example ``, and check required filenames and the timeout. +Do not reduce particle counts or change seeds just to pass CI. For a missing +bundle, verify the full RTD checkout SHA has a successful publishing workflow +and matching release assets; execute that revision instead of selecting another +commit's outputs. A checksum/provenance failure means corruption or mixed inputs: +re-download or reproduce the exact locked revision. Missing figures fail before +Sphinx. Warnings (including missing Intersphinx inventories) fail the build. + +For RTD API failures check the project slug, account permissions, token, rate +limits and build logs. A main-advanced error is expected during rapid pushes; +check that the newest workflow eventually succeeds. A private dependency error +in Actions requires public dependency access, not credentials in PR jobs. + +Official integration references +------------------------------- + +* `Pixi setup action and cache/locked inputs `_ +* `GitHub release and asset API `_ +* `RTD build hooks `_ +* `RTD API v3, builds and authentication `_ +* `RTD webhook automation rules `_ +* `RTD subprojects `_ + +Prepared plot integrations +-------------------------- + +The flow preconditioning, persistent sampling, and waste-free examples are now +configured for the matrix and durable bundles, with inline figure directives. +Their new integrations have not been executed or numerically validated locally. +The standalone scripts retain their particle counts, dimensions, seeds, fitting +budgets, and mutation settings. Some constants have moved to module scope so +the runner can record them. Documentation-only summary writes do not draw RNG. +Flow preconditioning uses its existing four-dimensional, 1,000-particle setup; +persistent sampling uses the parser defaults (four dimensions, 256 active +particles); waste-free SMC keeps 100 chains of length 100 and seed 123. + +Do not treat pre-existing loose PNG files as verified documentation outputs. +A full build requires fresh, matching provenance for all seven execution entries. +In particular, ``make -C docs docs`` may launch sampling when outputs are stale +or missing; ``make -C docs docs-only`` never launches it. The runner has timeouts +but no enforced memory ceiling yet. Validate the new runs on suitable hardware +or after adding memory limits. Dependency resolution and mocked worker tests +do not validate numerical behavior or plot quality. + +Particle filtering is also configured for automated execution, with its existing +defaults of 1,000 particles, 20 observations, seed 2026, and resampling after +every observation. The runner isolates its plot and captures accuracy, ESS, +and evidence diagnostics in the bundle without rendering numerical summaries. +This integration has mocked worker and layout checks; no filtering run was +performed while preparing it. diff --git a/README.md b/README.md index 56257bf..61ebadb 100644 --- a/README.md +++ b/README.md @@ -1 +1,32 @@ -# sequince-examples \ No newline at end of file +# Sequince examples + +Example scripts and their narrative tutorials live together under `examples/`. +Start with the [basic examples](examples/basic_examples/README.rst), then explore +[adaptive mutation](examples/adaptive_mutation_steps/README.rst), +[flow preconditioning](examples/preconditioning/README.rst), and +[persistent sampling](examples/persistent_sampling/README.rst). The documentation +also covers waste-free SMC and particle filtering. + +The documentation pipeline is configured to execute Gaussian, Rosenbrock, +adaptive mutation, flow preconditioning, persistent sampling, and waste-free +SMC and particle-filtering examples using the latest Sequince main, resolved +once per execution batch. Their plots appear directly in the tutorial pages. + +```bash +pixi install --locked -e default -e docs +pixi run --locked examples +pixi run --locked -e docs docs +``` + +Use `make -C docs setup` followed by `make -C docs docs` to build the full documentation. +Valid example outputs are reused; missing or stale outputs trigger example +execution. `make -C docs docs-only` never executes examples. Use `make -C docs validate` to run +checks, rerun all examples, and build the documentation. +Run `make -C docs check` for tests, lint, and formatting checks, or `make -C docs help` for +individual targets. New example runs refresh Sequince main; all jobs in a batch +use the same resolved lock. Later, the dependency will switch to releases. + +See [setup and maintenance](MAINTENANCE.rst) for dependency access, adding +examples, publication and Read the Docs Community subproject setup. Generated +results are ignored. Environments, dependencies, and tasks are defined in +`pyproject.toml`; `pixi.lock` is intentionally tracked for reproducibility. diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..e13c724 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,66 @@ +.DEFAULT_GOAL := help + +DOCS_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) +REPO_ROOT := $(abspath $(DOCS_DIR)/..) + +PIXI ?= pixi +EXAMPLE ?= gaussian +ENV ?= default + +.PHONY: help setup test lint format-check check hooks example examples refresh docs docs-only validate + +help: + @printf '%s\n' \ + 'make setup Refresh Sequince main and install environments' \ + 'make refresh Resolve current Sequince main without sampling' \ + 'make check Run tests, lint and formatting checks' \ + 'make test Run pipeline tests' \ + 'make lint Run Ruff lint checks' \ + 'make format-check Check Python formatting' \ + 'make hooks Run all pre-commit hooks (may edit files)' \ + 'make example EXAMPLE=gaussian [ENV=default]' \ + ' Run one example in the selected environment' \ + 'make examples Run all documentation examples in their environments' \ + 'make docs Reuse valid outputs or run examples, then build docs' \ + 'make docs-only Build docs using existing validated outputs' \ + 'make validate Run checks, all examples, then build docs' + +refresh: + cd "$(REPO_ROOT)" && $(PIXI) update sequince + +setup: refresh + cd "$(REPO_ROOT)" && $(PIXI) install --locked -e default -e docs -e check + +test: + cd "$(REPO_ROOT)" && $(PIXI) run --locked -e check test + +lint: + cd "$(REPO_ROOT)" && $(PIXI) run --locked -e check lint + +format-check: + cd "$(REPO_ROOT)" && $(PIXI) run --locked -e check format-check + +check: test lint format-check + +hooks: + cd "$(REPO_ROOT)" && $(PIXI) run --locked -e check pre-commit run --all-files + +example: + cd "$(REPO_ROOT)" && $(PIXI) run --locked -e $(ENV) example $(EXAMPLE) + +examples: + cd "$(REPO_ROOT)" && $(PIXI) run --locked examples + +docs: refresh + @if ! (cd "$(REPO_ROOT)" && $(PIXI) run --locked -e docs validate-outputs); then \ + $(MAKE) -C "$(DOCS_DIR)" examples || exit $$?; \ + fi + $(MAKE) -C "$(DOCS_DIR)" docs-only + +docs-only: + cd "$(REPO_ROOT)" && $(PIXI) run --locked -e docs docs + +# Keep execution before Sphinx even when invoked with make -j. +validate: check + $(MAKE) -C "$(DOCS_DIR)" examples + $(MAKE) -C "$(DOCS_DIR)" docs-only diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..0e893bc --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,25 @@ +"""Sphinx configuration; generated outputs are mandatory.""" + +import os +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts")) +from docs_pipeline import validate # noqa: E402 + +validate() +project = "Sequince examples" +author = "Sequince contributors" +extensions = ["sphinx.ext.intersphinx"] +html_theme = "sphinx_book_theme" +html_theme_options = { + "repository_url": "https://github.com/sequince-dev/sequince-examples", + "use_repository_button": True, +} +exclude_patterns = ["_build"] +intersphinx_mapping = { + "sequince": ( + "https://sequince.readthedocs.io/en/latest/", + os.environ.get("SEQUINCE_INVENTORY"), + ), +} diff --git a/docs/examples.json b/docs/examples.json new file mode 100644 index 0000000..008cb6b --- /dev/null +++ b/docs/examples.json @@ -0,0 +1,192 @@ +{ + "schema": 1, + "examples": [ + { + "id": "gaussian", + "title": "Gaussian posterior", + "script": "examples/basic_examples/gaussian.py", + "working_directory": "examples/basic_examples", + "environment": "default", + "timeout": 600, + "narrative": "examples/basic_examples/gaussian.rst", + "parameters": [ + "n_dimensions", + "n_particles", + "lower_bound", + "upper_bound", + "mean", + "sigma", + "seed" + ], + "figures": { + "corner.png": "Weighted marginal and joint posterior distributions for the Gaussian target.", + "history.png": "Adaptive temperature and accumulated log evidence at each SMC stage." + }, + "required_outputs": [ + "corner.png", + "history.png", + "posterior.npz", + "result.h5" + ] + }, + { + "id": "rosenbrock", + "title": "Rosenbrock posterior", + "script": "examples/basic_examples/rosenbrock.py", + "working_directory": "examples/basic_examples", + "environment": "default", + "timeout": 600, + "narrative": "examples/basic_examples/rosenbrock.rst", + "parameters": [ + "n_dimensions", + "n_particles", + "lower_bound", + "upper_bound", + "a", + "b", + "seed" + ], + "figures": { + "corner.png": "Weighted posterior samples along the curved Rosenbrock valley.", + "history.png": "Temperature and log evidence for the unnormalized Rosenbrock likelihood." + }, + "required_outputs": [ + "corner.png", + "history.png", + "posterior.npz", + "result.h5" + ] + }, + { + "id": "adaptive-mutation", + "title": "Adaptive mutation steps", + "script": "examples/adaptive_mutation_steps/adaptive_mutation_steps.py", + "working_directory": "examples/adaptive_mutation_steps", + "environment": "default", + "timeout": 1200, + "narrative": "examples/adaptive_mutation_steps/README.rst", + "parameters": [ + "n_dimensions", + "n_particles", + "lower_bound", + "upper_bound", + "rosenbrock_scale", + "seed", + "target_steps", + "initial_steps", + "min_steps", + "max_steps" + ], + "figures": { + "adaptive_mutation_history.png": "Executed mutation budgets, acceptance rates, and expected accepted moves across SMC stages.", + "corner.png": "Weighted marginal and joint distributions for the eight-dimensional Rosenbrock posterior.", + "history.png": "Temperature and log evidence through the adaptive mutation run." + }, + "required_outputs": [ + "adaptive_mutation_history.png", + "corner.png", + "history.png", + "posterior.npz", + "result.h5" + ] + }, + { + "id": "flow-preconditioning", + "title": "Flow preconditioning", + "script": "examples/preconditioning/flow_preconditioning.py", + "working_directory": "examples/preconditioning", + "environment": "flow", + "timeout": 2400, + "narrative": "examples/preconditioning/README.rst", + "parameters": [ + "dims", + "n_particles", + "rosenbrock_scale", + "seed" + ], + "dependency_features": [ + "flow" + ], + "figures": { + "rosenbrock_corner.png": "Posterior marginals and joint distributions for the four-dimensional Rosenbrock target sampled with flow preconditioning." + }, + "required_outputs": [ + "rosenbrock_corner.png", + "summary.json" + ] + }, + { + "id": "persistent-sampling", + "title": "Persistent sampling", + "script": "examples/persistent_sampling/rosenbrock.py", + "working_directory": "examples/persistent_sampling", + "environment": "default", + "timeout": 1200, + "narrative": "examples/persistent_sampling/README.rst", + "parameters": [ + "dimensions", + "n_particles", + "target_ess", + "target_total_ess", + "mutation_steps", + "flow", + "bound", + "rosenbrock_scale", + "seed" + ], + "figures": { + "corner.png": "Posterior marginals and joint distributions from the persistent Rosenbrock run, using the script defaults." + }, + "required_outputs": [ + "corner.png", + "summary.json" + ] + }, + { + "id": "waste-free", + "title": "Waste-free SMC", + "script": "examples/waste_free_smc/run_waste_free_smc.py", + "working_directory": "examples/waste_free_smc", + "environment": "default", + "timeout": 1200, + "narrative": "examples/waste_free_smc/README.rst", + "parameters": [ + "dims", + "bounds", + "correlation", + "n_chains", + "chain_length", + "seed" + ], + "figures": { + "waste_free_history.png": "Adaptive tempering schedules for standard and waste-free SMC. Retained sample counts do not measure the number of independent samples." + }, + "required_outputs": [ + "waste_free_history.png", + "summary.json" + ] + }, + { + "id": "particle-filtering", + "title": "Particle filtering", + "script": "examples/particle_filtering/particle_filter.py", + "working_directory": "examples/particle_filtering", + "environment": "default", + "timeout": 600, + "narrative": "examples/particle_filtering/README.rst", + "parameters": [ + "particles", + "observations", + "seed", + "ess_threshold" + ], + "figures": { + "particle_filter.png": "Bootstrap and locally optimal particle filters compared with the exact Kalman filter, including effective sample size before resampling." + }, + "required_outputs": [ + "particle_filter.png", + "summary.json" + ] + } + ] +} diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..f80dd6a --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,49 @@ +Sequince examples +================= + +Start with the basic examples to learn how to define a prior and likelihood, +initialize particles, run Sequential Monte Carlo (SMC), and interpret weighted +posterior samples and evidence estimates. Work through the Gaussian target +first, then try the curved Rosenbrock posterior. + +.. toctree:: + :maxdepth: 1 + :caption: Getting started + + _generated/examples/basic_examples/gaussian + _generated/examples/basic_examples/rosenbrock + +Once you are comfortable with that workflow, explore ways to adapt mutation, +change the coordinates used by the sampler, and reuse particle populations. +Each example has its own tutorial page with explanations and script source. +Example plots appear directly on that page. + +.. toctree:: + :maxdepth: 1 + :caption: Sampling methods + + _generated/examples/adaptive_mutation_steps/README + _generated/examples/preconditioning/README + _generated/examples/persistent_sampling/README + _generated/examples/waste_free_smc/README + +For sequential observations, the particle-filtering example compares bootstrap +and observation-conditioned proposals with a Kalman-filter reference. + +.. toctree:: + :maxdepth: 1 + :caption: Sequential data + + _generated/examples/particle_filtering/README + +The basic, adaptive mutation, flow preconditioning, persistent sampling, and +waste-free SMC, and particle-filtering tutorials include plots alongside their +explanations. Their +run metadata identifies the source revision, parameters, seed, and pinned +dependencies. + +For background and the full interface reference, see Sequince's +`user guide `_ +and `API reference `_. +The examples use :external:py:class:`sequince.ParticleState` to represent the +weighted particle population. diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..085b8f5 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,3 @@ +# Match the direct Sphinx dependencies in the Pixi docs environment. +Sphinx==8.2.3 +sphinx-book-theme==1.1.4 diff --git a/adaptive_mutation_steps/README.rst b/examples/adaptive_mutation_steps/README.rst similarity index 85% rename from adaptive_mutation_steps/README.rst rename to examples/adaptive_mutation_steps/README.rst index 8ff6aca..139f47f 100644 --- a/adaptive_mutation_steps/README.rst +++ b/examples/adaptive_mutation_steps/README.rst @@ -117,6 +117,28 @@ Try lowering ``max_steps`` to see the budget saturate, or increasing ``min_steps <= initial_steps <= max_steps`` and ``n_dimensions >= 2``. Retain weights when loading ``posterior.npz`` for further analysis. +Example results +~~~~~~~~~~~~~~~ + +:download:`Download script `. + +.. figure:: /_generated/results/adaptive-mutation/adaptive_mutation_history.png + :alt: Executed mutation budgets, acceptance rates, and expected accepted moves across SMC stages. + + Executed mutation budgets, acceptance rates, and expected accepted moves across SMC stages. + +.. figure:: /_generated/results/adaptive-mutation/corner.png + :alt: Weighted marginal and joint distributions for the eight-dimensional Rosenbrock posterior. + + Weighted marginal and joint distributions for the eight-dimensional Rosenbrock posterior. + +.. figure:: /_generated/results/adaptive-mutation/history.png + :alt: Temperature and log evidence through the adaptive mutation run. + + Temperature and log evidence through the adaptive mutation run. + +:download:`Run provenance `. + The full sampler setup is shown below: .. literalinclude:: adaptive_mutation_steps.py diff --git a/adaptive_mutation_steps/adaptive_mutation_steps.py b/examples/adaptive_mutation_steps/adaptive_mutation_steps.py similarity index 100% rename from adaptive_mutation_steps/adaptive_mutation_steps.py rename to examples/adaptive_mutation_steps/adaptive_mutation_steps.py diff --git a/examples/basic_examples/README.rst b/examples/basic_examples/README.rst new file mode 100644 index 0000000..f80aa5a --- /dev/null +++ b/examples/basic_examples/README.rst @@ -0,0 +1,6 @@ +Basic examples +============== + +Start with `Gaussian posterior `_ to learn the SMC workflow, +then follow `Rosenbrock posterior `_ to explore a curved target +and conditional resampling. Both tutorials include their plots and script source. diff --git a/basic_examples/gaussian.py b/examples/basic_examples/gaussian.py similarity index 100% rename from basic_examples/gaussian.py rename to examples/basic_examples/gaussian.py diff --git a/basic_examples/README.rst b/examples/basic_examples/gaussian.rst similarity index 68% rename from basic_examples/README.rst rename to examples/basic_examples/gaussian.rst index 491235a..b941a7b 100644 --- a/basic_examples/README.rst +++ b/examples/basic_examples/gaussian.rst @@ -1,5 +1,5 @@ -Basic examples -============== +Gaussian posterior +================== These examples walk through Bayesian inference with Sequince's standard Sequential Monte Carlo (SMC) sampler. Start with ``gaussian.py`` to learn the @@ -19,13 +19,11 @@ HDF5 dependencies:: For installation options, including using a development checkout, see "Getting started" in the main Sequince documentation. -Run the scripts from this directory:: +Run the script from this directory:: python gaussian.py - python rosenbrock.py -The scripts write to ``outdir/gaussian`` and ``outdir/rosenbrock`` within this -directory. +The script writes to ``outdir/gaussian`` within this directory. Running a script again replaces its previous outputs. 1. Define the target distribution @@ -192,72 +190,27 @@ The arrays can be loaded with ``numpy.load``. Retain the weights when analyzing the particles. See the main API reference for ``SMCResult`` for posterior sample extraction, diagnostics, plotting, and result persistence. -6. Explore a curved posterior ------------------------------- +Gaussian results +~~~~~~~~~~~~~~~~ -``rosenbrock.py`` follows the same workflow with a Rosenbrock log likelihood: +:download:`Download script `. -.. math:: +.. figure:: /_generated/results/gaussian/corner.png + :alt: Weighted marginal and joint posterior distributions for the Gaussian target. - \log L(\boldsymbol{\theta}) = -\sum_{j=1}^{d-1} - \left[b\left(\theta_{j+1} - \theta_j^2\right)^2 - + \left(a - \theta_j\right)^2\right]. + Weighted marginal and joint posterior distributions for the Gaussian target. -The likelihood reads :math:`a` and :math:`b` from ``context.shared``. With the -default :math:`a=1` and :math:`b=100`, the two-dimensional likelihood concentrates -around a narrow curved valley near :math:`\theta_2 = \theta_1^2`, with a maximum -at :math:`(\theta_1, \theta_2) = (1, 1)`. Inspect the corner plot for this shape -and the history plot for how tempering progresses. This likelihood is -unnormalized: the reported evidence uses exactly the convention above, and the -script provides no analytic reference value. +.. figure:: /_generated/results/gaussian/history.png + :alt: Adaptive temperature and accumulated log evidence at each SMC stage. -Configure mutation and resampling -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Adaptive temperature and accumulated log evidence at each SMC stage. -The Rosenbrock example also demonstrates more options in the prebuilt helper: +:download:`Run provenance `. -.. code-block:: python +.. literalinclude:: gaussian.py + :language: python + :linenos: - smc, posterior, metadata = make_standard_bayesian_smc( - log_prior=log_prior, - log_likelihood=log_likelihood, - weighting_criterion="cess", - target_ess_ratio=0.8, - resampling="stratified", - always_resample=False, - resampling_ess_threshold=0.5, - mutation="differential-evolution", - mutation_n_steps=32, - mutation_kwargs={"gamma_one_probability": 0.1}, - ) -``mutation="differential-evolution"`` proposes moves using scaled differences -between other particles in the ensemble. ``mutation_kwargs`` passes options to -the mutation implementation: here ``gamma_one_probability=0.1`` gives each -proposal a 10% chance of using a unit scale instead of the usual -dimension-dependent scale. Each mutation stage takes 32 steps. - -``resampling="stratified"`` changes the resampling scheme from the Gaussian -example's default systematic scheme. With ``always_resample=False``, resampling -occurs when the population ESS falls below :math:`0.5N`. By default, -``resample_at_end=True`` also resamples at the final posterior, even if that -threshold has not been crossed. When omitted, ``always_mutate`` follows the -resolved ``always_resample`` value, so mutation runs only on iterations that -resample here. Set ``always_mutate=True`` to run mutation every iteration. - -Use ``weighting_criterion="cess"`` with conditional resampling. Conditional ESS -(CESS) measures the effect of a proposed temperature increment given the -current weighted population; ``target_ess_ratio=0.8`` controls that increment. -The separate ``resampling_ess_threshold=0.5`` controls when to resample using the -population ESS. Sequince's ordinary ``"ess"`` tempering criterion requires -resampling every iteration, so simply setting ``always_resample=False`` with -that criterion is invalid. - -See the main API reference for ``make_standard_bayesian_smc`` and -``DifferentialEvolutionMutation`` for the available options. - -Try changing ``n_particles``, ``mutation_n_steps``, and the seed to assess the -stability of the posterior summaries. Increasing ``target_ess_ratio`` usually -produces smaller tempering increments and more iterations. Increasing particle -count or mutation steps increases computational cost. Compare several runs -before judging whether a setting explores the posterior adequately. +Continue with :doc:`rosenbrock` to explore a curved posterior and conditional +resampling. diff --git a/basic_examples/rosenbrock.py b/examples/basic_examples/rosenbrock.py similarity index 100% rename from basic_examples/rosenbrock.py rename to examples/basic_examples/rosenbrock.py diff --git a/examples/basic_examples/rosenbrock.rst b/examples/basic_examples/rosenbrock.rst new file mode 100644 index 0000000..f034fda --- /dev/null +++ b/examples/basic_examples/rosenbrock.rst @@ -0,0 +1,104 @@ +Rosenbrock posterior +==================== + +Start with :doc:`gaussian` for the prior, particle initialization, and standard +SMC workflow. This example keeps the same uniform prior and uses a curved +likelihood with differential-evolution mutation and conditional resampling. + +Run from this directory with the same dependencies as the Gaussian example:: + + python rosenbrock.py + +Outputs are written to ``outdir/rosenbrock``. + +Define the likelihood +--------------------- + + +``rosenbrock.py`` follows the same workflow with a Rosenbrock log likelihood: + +.. math:: + + \log L(\boldsymbol{\theta}) = -\sum_{j=1}^{d-1} + \left[b\left(\theta_{j+1} - \theta_j^2\right)^2 + + \left(a - \theta_j\right)^2\right]. + +The likelihood reads :math:`a` and :math:`b` from ``context.shared``. With the +default :math:`a=1` and :math:`b=100`, the two-dimensional likelihood concentrates +around a narrow curved valley near :math:`\theta_2 = \theta_1^2`, with a maximum +at :math:`(\theta_1, \theta_2) = (1, 1)`. Inspect the corner plot for this shape +and the history plot for how tempering progresses. This likelihood is +unnormalized: the reported evidence uses exactly the convention above, and the +script provides no analytic reference value. + +Configure mutation and resampling +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Rosenbrock example also demonstrates more options in the prebuilt helper: + +.. code-block:: python + + smc, posterior, metadata = make_standard_bayesian_smc( + log_prior=log_prior, + log_likelihood=log_likelihood, + weighting_criterion="cess", + target_ess_ratio=0.8, + resampling="stratified", + always_resample=False, + resampling_ess_threshold=0.5, + mutation="differential-evolution", + mutation_n_steps=32, + mutation_kwargs={"gamma_one_probability": 0.1}, + ) + +``mutation="differential-evolution"`` proposes moves using scaled differences +between other particles in the ensemble. ``mutation_kwargs`` passes options to +the mutation implementation: here ``gamma_one_probability=0.1`` gives each +proposal a 10% chance of using a unit scale instead of the usual +dimension-dependent scale. Each mutation stage takes 32 steps. + +``resampling="stratified"`` changes the resampling scheme from the Gaussian +example's default systematic scheme. With ``always_resample=False``, resampling +occurs when the population ESS falls below :math:`0.5N`. By default, +``resample_at_end=True`` also resamples at the final posterior, even if that +threshold has not been crossed. When omitted, ``always_mutate`` follows the +resolved ``always_resample`` value, so mutation runs only on iterations that +resample here. Set ``always_mutate=True`` to run mutation every iteration. + +Use ``weighting_criterion="cess"`` with conditional resampling. Conditional ESS +(CESS) measures the effect of a proposed temperature increment given the +current weighted population; ``target_ess_ratio=0.8`` controls that increment. +The separate ``resampling_ess_threshold=0.5`` controls when to resample using the +population ESS. Sequince's ordinary ``"ess"`` tempering criterion requires +resampling every iteration, so simply setting ``always_resample=False`` with +that criterion is invalid. + +See the main API reference for ``make_standard_bayesian_smc`` and +``DifferentialEvolutionMutation`` for the available options. + +Try changing ``n_particles``, ``mutation_n_steps``, and the seed to assess the +stability of the posterior summaries. Increasing ``target_ess_ratio`` usually +produces smaller tempering increments and more iterations. Increasing particle +count or mutation steps increases computational cost. Compare several runs +before judging whether a setting explores the posterior adequately. + +Rosenbrock results +~~~~~~~~~~~~~~~~~~ + +:download:`Download script `. + +.. figure:: /_generated/results/rosenbrock/corner.png + :alt: Weighted posterior samples along the curved Rosenbrock valley. + + Weighted posterior samples along the curved Rosenbrock valley. + +.. figure:: /_generated/results/rosenbrock/history.png + :alt: Temperature and log evidence for the unnormalized Rosenbrock likelihood. + + Temperature and log evidence for the unnormalized Rosenbrock likelihood. + +:download:`Run provenance `. + +.. literalinclude:: rosenbrock.py + :language: python + :linenos: diff --git a/gpu_sampling/cupy_examples/run_minipcn_cupy.py b/examples/gpu_sampling/cupy_examples/run_minipcn_cupy.py similarity index 82% rename from gpu_sampling/cupy_examples/run_minipcn_cupy.py rename to examples/gpu_sampling/cupy_examples/run_minipcn_cupy.py index ede43d8..881f830 100644 --- a/gpu_sampling/cupy_examples/run_minipcn_cupy.py +++ b/examples/gpu_sampling/cupy_examples/run_minipcn_cupy.py @@ -1,11 +1,9 @@ -import matplotlib.pyplot as plt import cupy as cp - +import matplotlib.pyplot as plt +from orng import ArrayRNG from sequince import ParticleState from sequince.logging_utils import configure_logging -from sequince.posterior import draw_additional_samples from sequince.prebuilt_samplers import make_standard_bayesian_smc -from orng import ArrayRNG # Number of dimensions in the multivariate normal model dims = 4 @@ -20,10 +18,9 @@ def log_likelihood(particles: cp.ndarray, context) -> cp.ndarray: """Log-likelihood for a multivariate standard normal observation.""" - return ( - -0.5 * cp.sum(cp.square(particles - mu) / sigma**2, axis=-1) - - (dims / 2) * cp.log(2 * cp.pi * sigma**2) - ) + return -0.5 * cp.sum(cp.square(particles - mu) / sigma**2, axis=-1) - ( + dims / 2 + ) * cp.log(2 * cp.pi * sigma**2) def log_prior(particles: cp.ndarray, context) -> cp.ndarray: @@ -37,7 +34,7 @@ def log_prior(particles: cp.ndarray, context) -> cp.ndarray: def main() -> None: # Create a CuPy RNG - rng = ArrayRNG(backend='cupy', seed=42) + rng = ArrayRNG(backend="cupy", seed=42) # Enable logging configure_logging() @@ -45,12 +42,15 @@ def main() -> None: # Initialize particles from the prior lower_bound, upper_bound = bounds n_particles = 1000 - initial_particles = rng.uniform(low=lower_bound, high=upper_bound, size=(n_particles, dims)) - initial_log_weights = cp.full((n_particles,), -cp.log(cp.array(n_particles))) + initial_particles = rng.uniform( + low=lower_bound, high=upper_bound, size=(n_particles, dims) + ) + initial_log_weights = cp.full( + (n_particles,), -cp.log(cp.array(n_particles)) + ) initial_state = ParticleState( - particles=initial_particles, - log_weights=initial_log_weights + particles=initial_particles, log_weights=initial_log_weights ) # Use the prebuilt SMC with MiniPCN mutation diff --git a/ns-smc/.gitignore b/examples/ns-smc/.gitignore similarity index 100% rename from ns-smc/.gitignore rename to examples/ns-smc/.gitignore diff --git a/ns-smc/README.rst b/examples/ns-smc/README.rst similarity index 100% rename from ns-smc/README.rst rename to examples/ns-smc/README.rst diff --git a/ns-smc/example.py b/examples/ns-smc/example.py similarity index 100% rename from ns-smc/example.py rename to examples/ns-smc/example.py diff --git a/ns-smc/gaussian.rst b/examples/ns-smc/gaussian.rst similarity index 100% rename from ns-smc/gaussian.rst rename to examples/ns-smc/gaussian.rst diff --git a/ns-smc/run_spike_and_slab.py b/examples/ns-smc/run_spike_and_slab.py similarity index 100% rename from ns-smc/run_spike_and_slab.py rename to examples/ns-smc/run_spike_and_slab.py diff --git a/ns-smc/spike_and_slab.rst b/examples/ns-smc/spike_and_slab.rst similarity index 100% rename from ns-smc/spike_and_slab.rst rename to examples/ns-smc/spike_and_slab.rst diff --git a/examples/particle_filtering/README.rst b/examples/particle_filtering/README.rst new file mode 100644 index 0000000..c925436 --- /dev/null +++ b/examples/particle_filtering/README.rst @@ -0,0 +1,159 @@ +Particle filtering with Sequince +================================ + +This example shows how to implement particle filters in Sequince. + +It compares two particle filters for a scalar linear-Gaussian +state-space model: + +- a bootstrap filter, which proposes from the transition model; +- a locally optimal filter, which proposes conditional on the current + observation. + +Both filters process the same simulated observations. Their filtering means, +variances, and log evidence are compared with the exact Kalman-filter result. + +Running the example +------------------- + +.. code-block:: bash + + python particle_filter.py + + +The script prints accuracy, effective-sample-size, and evidence diagnostics, +then writes ``particle_filter.png`` in this directory. + +Useful options include: + +.. code-block:: bash + + python particle_filter.py --particles 10000 --observations 30 + python particle_filter.py --ess-threshold 0.5 + python particle_filter.py --output /tmp/particle-filter.png + + +By default both filters resample after every observation. Passing +``--ess-threshold FRACTION`` instead resamples only when the effective sample +size divided by the particle count falls below that fraction. + +Model +----- + +The example uses + +.. code-block:: text + + x_0 ~ Normal(0, sigma_0^2) + x_t | x_(t-1) ~ Normal(a x_(t-1), sigma_x^2) + y_t | x_t ~ Normal(x_t, sigma_y^2). + + +The model is linear and Gaussian, so a Kalman filter supplies exact filtering +moments and log evidence for comparison. + +Why the filters use different steps +----------------------------------- + +For a proposal distribution ``q``, particle filtering applies the incremental +weight + +.. code-block:: text + + p(y_t | x_t) p(x_t | x_(t-1)) / q(x_t | x_(t-1), y_t). + + +In the bootstrap filter, ``q`` is the transition distribution itself. The +transition and proposal terms therefore cancel, leaving only the observation +likelihood. A regular propagation step followed by ``IncrementalWeighting`` is +enough: after propagation, the weighting step only needs the new particles. + +The locally optimal proposal is not the transition distribution, so its +weights also need the transition-to-proposal correction. That correction +depends on both the parent particles and their proposed children. A standalone +propagation step replaces the particles before the next step runs, so an +ordinary ``IncrementalWeighting`` step would no longer have both values. +``ImportanceTransition`` instead samples the children and computes the complete +correction while parent and child particles are simultaneously available. + +The remaining steps are identical. Both filters record the weighted state and +then resample; only proposal generation and its associated weighting differ. + +Bootstrap filter +---------------- + +The bootstrap filter is assembled from this Sequince pipeline: + +.. code-block:: text + + PropagationStep + -> IncrementalWeighting + -> RecordFilteringState + -> SystematicResampling + + +``PropagationStep`` samples from ``p(x_t | x_(t-1))``, and +``IncrementalWeighting`` applies ``log p(y_t | x_t)``. The local +``RecordFilteringState`` step saves a detached copy of the weighted population +before resampling. These pre-resampling populations give the filtering +moments; ``SMCResult.states`` contains the post-resampling populations passed to +the next iteration. + +Locally optimal proposal filter +------------------------------- + +The second filter samples from +``q(x_t | x_(t-1), y_t) = p(x_t | x_(t-1), y_t)``. The proposal parameters are + +.. code-block:: text + + V_q = (1 / sigma_x^2 + 1 / sigma_y^2)^-1 + m_q = V_q (a x_(t-1) / sigma_x^2 + y_t / sigma_y^2). + + +Its pipeline is + +.. code-block:: text + + ImportanceTransition(OptimalProposalKernel) + -> RecordFilteringState + -> SystematicResampling + + +The proposal kernel sees parent and child particles together and returns the +complete importance increment + +.. code-block:: text + + log p(x_t | x_(t-1)) - log q(x_t | x_(t-1), y_t) + + log p(y_t | x_t). + + +``ImportanceTransition`` applies that increment, normalizes the weights, and +updates the log evidence. Keeping the proposal and its correction together +avoids storing an extra parent population in each state. + +The lower panel of the output figure compares effective sample size before +resampling. Conditioning on the observation generally gives the locally +optimal proposal a higher ESS than the bootstrap proposal. + +Example plot +------------ + +.. figure:: /_generated/results/particle-filtering/particle_filter.png + :alt: Bootstrap and locally optimal particle filters compared with the exact Kalman filter, including effective sample size before resampling. + + Bootstrap and locally optimal particle filters compared with the exact + Kalman filter. The lower panel shows effective sample size before resampling; + read it alongside filtering accuracy and the evidence diagnostics. + +:download:`Run provenance `. + +Complete script +--------------- + +:download:`Download the script `. + +.. literalinclude:: particle_filter.py + :language: python + :linenos: diff --git a/particle_filtering/particle_filter.py b/examples/particle_filtering/particle_filter.py similarity index 92% rename from particle_filtering/particle_filter.py rename to examples/particle_filtering/particle_filter.py index b1cb970..ca4fd3b 100644 --- a/particle_filtering/particle_filter.py +++ b/examples/particle_filtering/particle_filter.py @@ -8,6 +8,7 @@ from __future__ import annotations import argparse +import json from dataclasses import dataclass from pathlib import Path from typing import TYPE_CHECKING @@ -26,6 +27,9 @@ from orng import RandomGenerator +documentation_output: Path | None = None + + @dataclass(frozen=True) class LinearGaussianModel: """Parameters of the scalar state-space model.""" @@ -553,7 +557,8 @@ def plot_comparison(comparison: ProposalComparison, output: Path) -> None: plt.close(figure) -def main() -> None: +def build_parser() -> argparse.ArgumentParser: + """Build the CLI parser used for execution and documentation metadata.""" parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--particles", type=int, default=1_000) parser.add_argument("--observations", type=int, default=20) @@ -569,7 +574,11 @@ def main() -> None: type=Path, default=Path(__file__).with_name("particle_filter.png"), ) - args = parser.parse_args() + return parser + + +def main() -> None: + args = build_parser().parse_args() comparison = run_comparison( n_observations=args.observations, n_particles=args.particles, @@ -601,6 +610,38 @@ def main() -> None: f"{comparison.bootstrap_log_evidence:.4f} / " f"{comparison.kalman_log_evidence:.4f}" ) + if documentation_output is not None: + args.output = documentation_output / "particle_filter.png" + summary = { + "mean_rmse": { + "optimal": rmse( + comparison.proposal_means, comparison.kalman_means + ), + "bootstrap": rmse( + comparison.bootstrap_means, comparison.kalman_means + ), + }, + "variance_rmse": { + "optimal": rmse( + comparison.proposal_variances, comparison.kalman_variances + ), + "bootstrap": rmse( + comparison.bootstrap_variances, comparison.kalman_variances + ), + }, + "mean_ess_before_resampling": { + "optimal": float(np.mean(comparison.proposal_ess)), + "bootstrap": float(np.mean(comparison.bootstrap_ess)), + }, + "log_evidence": { + "optimal": comparison.proposal_log_evidence, + "bootstrap": comparison.bootstrap_log_evidence, + "kalman": comparison.kalman_log_evidence, + }, + } + (documentation_output / "summary.json").write_text( + json.dumps(summary, allow_nan=False) + "\n" + ) plot_comparison(comparison, args.output) print(f"wrote {args.output}") diff --git a/examples/persistent_sampling/README.rst b/examples/persistent_sampling/README.rst new file mode 100644 index 0000000..b70a8c2 --- /dev/null +++ b/examples/persistent_sampling/README.rst @@ -0,0 +1,77 @@ +Persistent sampling +=================== + +This example demonstrates how to use persistent sampling in ``sequince`` to +sample an n-dimensional Rosenbrock target. + +Persistent sampling is a variation of SMC proposed by `Karamanis 2025`_. + +Run the example from the repository root: + +.. code-block:: bash + + python persistent_sampling/rosenbrock.py + + +For a larger run: + +.. code-block:: bash + + python persistent_sampling/rosenbrock.py \ + --dimensions 16 \ + --n-particles 1024 \ + --target-ess 2048 \ + --target-total-ess 8192 \ + --mutation-steps 32 + + +The script prints diagnostics from the final weighted persistent state, +including its archive size, effective sample size, weighted mean, best +Rosenbrock potential, and log evidence. Unless ``--no-plot`` is supplied, it also +saves a weighted corner plot as ``examples/persistent_sampling/rosenbrock_d.png``. +Use ``--output PATH`` to choose a different location. + +Run the following for all available options: + +.. code-block:: bash + + python persistent_sampling/rosenbrock.py --help + + +Preconditioning +--------------- + +First read :doc:`../preconditioning/README` for an explicit flow-transform +setup. This example also shows how to use the normalizing-flow-based preconditioning +described in `Karamanis 2022`_ and `Karamanis 2025`_. + +The ``--flow`` option enables learned normalizing-flow preconditioning: + +.. code-block:: bash + + python persistent_sampling/rosenbrock.py --dimensions 6 --flow + + +The flow option requires ``sequince-density`` and its flow dependencies. + +.. _Karamanis 2022: https://arxiv.org/abs/2207.05660 +.. _Karamanis 2025: https://arxiv.org/abs/2407.20722 + +Example plots +------------- + +.. figure:: /_generated/results/persistent-sampling/corner.png + :alt: Posterior marginals and joint distributions from the persistent Rosenbrock run, using the script defaults. + + Posterior marginals and joint distributions from the persistent Rosenbrock run, using the script defaults. + +:download:`Run provenance `. + +Complete script +--------------- + +:download:`Download the script `. + +.. literalinclude:: rosenbrock.py + :language: python + :linenos: diff --git a/persistent_sampling/rosenbrock.py b/examples/persistent_sampling/rosenbrock.py similarity index 91% rename from persistent_sampling/rosenbrock.py rename to examples/persistent_sampling/rosenbrock.py index 83746cc..1fc6938 100644 --- a/persistent_sampling/rosenbrock.py +++ b/examples/persistent_sampling/rosenbrock.py @@ -13,6 +13,7 @@ from __future__ import annotations import argparse +import json from pathlib import Path import numpy as np @@ -26,6 +27,8 @@ from sequince.steps import StepContext from sequince.transforms import BoundedToUnboundedTransform +documentation_output: Path | None = None + def rosenbrock(particles: np.ndarray) -> np.ndarray: """Return the Rosenbrock potential for each particle.""" @@ -213,6 +216,22 @@ def main() -> None: print(f"best particle: {np.array2string(best, precision=3)}") print(f"final log evidence: {float(final_state.log_evidence):+.6f}") + if documentation_output is not None: + summary = { + "log_evidence": float(final_state.log_evidence), + "weighted_mean": weighted_mean.tolist(), + "archive_size": int(final_state.n_particles), + "effective_sample_size": float( + final_state.effective_sample_size() + ), + "weighted_mean_potential": float(weighted_mean_potential), + } + (documentation_output / "summary.json").write_text( + json.dumps(summary, allow_nan=False) + "\n" + ) + if args.output is None: + args.output = documentation_output / "corner.png" + if not args.no_plot: output = args.output or Path(__file__).with_name( f"rosenbrock_{args.dimensions}d.png" diff --git a/preconditioning/README.rst b/examples/preconditioning/README.rst similarity index 68% rename from preconditioning/README.rst rename to examples/preconditioning/README.rst index c01fdb4..c5293b1 100644 --- a/preconditioning/README.rst +++ b/examples/preconditioning/README.rst @@ -10,10 +10,6 @@ kernel can explore more efficiently. particle transform. The flow must implement the ``sequince.flows.NormalizingFlow`` protocol: -.. autoclass:: sequince.flows.NormalizingFlow - :members: - :undoc-members: - Construct the transform by passing it a compatible flow and, optionally, the keyword arguments used to fit that flow: @@ -33,11 +29,11 @@ The resulting object can be used anywhere a particle transform is accepted by Fitting the flow ---------------- -The fitting of the transform is handle by the mutation step. The frequency +The fitting of the transform is handled by the mutation step. The frequency of fitting can be controlled by the ``transform_refit`` argument. Current options are: -- ``"always"``: fit the every time the mutation step is called (default) +- ``"always"``: fit the flow every time the mutation step is called (default) - ``"once"``: fit the first time the mutation step is called, then reuse - ``"per-context"``: fit once per unique context, then reuse for that context @@ -48,10 +44,13 @@ Prebuilt flows -------------- ``sequince-density`` provides various normalizing flows that can be used for -preconditioning via the ``zuko` and `flowjax` packages. See the +preconditioning via the ``zuko`` and ``flowjax`` packages. See the ``sequince_density.flows.zuko`` and ``sequince_density.flows.flowjax`` modules for more information. +Continue with :doc:`../persistent_sampling/README` to combine this approach +with persistent sampling. + Example ------- @@ -59,6 +58,18 @@ The following example uses a Zuko masked autoregressive flow to precondition a four-dimensional Rosenbrock target. It requires ``sequince-density`` with its Zuko backend and PyTorch support. +Example plots +------------- + +.. figure:: /_generated/results/flow-preconditioning/rosenbrock_corner.png + :alt: Posterior marginals and joint distributions for the four-dimensional Rosenbrock target sampled with flow preconditioning. + + Posterior marginals and joint distributions for the four-dimensional Rosenbrock target sampled with flow preconditioning. + +:download:`Run provenance `. + +:download:`Download the script `. + .. literalinclude:: flow_preconditioning.py :language: python :linenos: diff --git a/preconditioning/flow_preconditioning.py b/examples/preconditioning/flow_preconditioning.py similarity index 78% rename from preconditioning/flow_preconditioning.py rename to examples/preconditioning/flow_preconditioning.py index 74501f5..0ff1b38 100644 --- a/preconditioning/flow_preconditioning.py +++ b/examples/preconditioning/flow_preconditioning.py @@ -6,19 +6,30 @@ from __future__ import annotations +import json import logging from pathlib import Path +import numpy as np import torch from orng import RandomGenerator +from sequince.array_api import to_numpy from sequince.callbacks import IntermediateParticlesPlot, ParticleTransformPlot from sequince.log import configure_logging from sequince.plot import plot_corner from sequince.prebuilt_samplers import make_standard_bayesian_smc +from sequince.result import SMCResult from sequince.state import ParticleState from sequince.transforms import AffineTransform, FlowTransform from sequince_density.flows.zuko import ZukoFlow +seed = 1234 +n_particles = 1000 +dims = 4 +rosenbrock_scale = 1.0 +outdir = Path("./") +documentation_output: Path | None = None + def rosenbrock(x: torch.Tensor) -> torch.Tensor: return torch.sum( @@ -43,19 +54,31 @@ def log_likelihood(particles: torch.Tensor, context) -> torch.Tensor | float: return values +def _documentation_summary(result: SMCResult) -> dict[str, object]: + """Summarize the weighted empirical measure on the host for JSON output.""" + state = result.final_state + particles = to_numpy(state.particles) + weights = to_numpy(state.weights) + mean = np.sum(particles * weights[:, None], axis=0) / weights.sum() + centered = particles - mean + # The pinned Sequince revision uses a NumPy-only transpose signature in + # weighted_covariance. Host arrays keep this documentation step portable. + covariance = (centered * weights[:, None]).T @ centered / weights.sum() + return { + "log_evidence": float(state.log_evidence), + "weighted_mean": mean.tolist(), + "weighted_population_covariance": covariance.tolist(), + "n_particles": len(particles), + } + + def main() -> None: logger = configure_logging(level=logging.INFO) - seed = 1234 - rng = RandomGenerator(backend="torch", seed=seed) - n_particles = 1000 - dims = 4 - rosenbrock_scale = 1.0 bounds = torch.tensor([[-5.0, 5.0]]).repeat(dims, 1) labels = [f"x{i + 1}" for i in range(dims)] - outdir = Path("./") # Define the normalizing flow to use for preconditioning. # We use a Masked Autoregressive Flow (MAF) with 2 transforms and 2 hidden @@ -119,6 +142,12 @@ def main() -> None: shared_context=shared_context, ) + if documentation_output is not None: + summary = _documentation_summary(result) + (documentation_output / "summary.json").write_text( + json.dumps(summary, allow_nan=False) + "\n" + ) + # Compute the log evidence and its standard deviation from the SMC result. log_evidence = result.log_evidence() log_evidence_std = result.log_evidence_error() diff --git a/waste_free_smc/README.rst b/examples/waste_free_smc/README.rst similarity index 82% rename from waste_free_smc/README.rst rename to examples/waste_free_smc/README.rst index c8d84d4..29ad57c 100644 --- a/waste_free_smc/README.rst +++ b/examples/waste_free_smc/README.rst @@ -1,7 +1,7 @@ Waste-free sequential Monte Carlo -================================ +================================= -`run_waste_free_smc.py `_ demonstrates waste-free +:download:`run_waste_free_smc.py ` demonstrates waste-free sequential Monte Carlo (SMC), as described by `Dau & Chopin`_, alongside a standard resample-move SMC run. Both sample a four-dimensional correlated Gaussian likelihood with a uniform prior on ``[-10, 10]^4``. @@ -63,6 +63,19 @@ zero and marginal variances should approach one. The evidence reference is ``-4 * log(20)`` (approximately -11.983), neglecting the tiny Gaussian mass outside the prior box. Both densities include their normalization constants. +Example plots +------------- + +.. figure:: /_generated/results/waste-free/waste_free_history.png + :alt: Adaptive tempering schedules for standard and waste-free SMC. Retained sample counts do not measure the number of independent samples. + + Adaptive tempering schedules for standard and waste-free SMC. Retained sample counts do not measure the number of independent samples. + +:download:`Run provenance `. + +Complete script +--------------- + The target densities and complete sampler setup are shown below: .. literalinclude:: run_waste_free_smc.py diff --git a/waste_free_smc/run_waste_free_smc.py b/examples/waste_free_smc/run_waste_free_smc.py similarity index 81% rename from waste_free_smc/run_waste_free_smc.py rename to examples/waste_free_smc/run_waste_free_smc.py index f5edd81..1bd9e84 100644 --- a/waste_free_smc/run_waste_free_smc.py +++ b/examples/waste_free_smc/run_waste_free_smc.py @@ -1,5 +1,8 @@ """Waste-free SMC for a correlated Gaussian likelihood and uniform prior.""" +import json +from pathlib import Path + import matplotlib.pyplot as plt import numpy as np from sequince import ParticleState, SequentialMonteCarlo, TemperedPosterior @@ -16,6 +19,14 @@ MetropolisHastingsMutation, ) +dims = 4 +bounds = (-10.0, 10.0) +correlation = 0.8 +n_chains = 100 +chain_length = 100 # Includes the resampled starting state. +seed = 123 +documentation_output: Path | None = None + def log_likelihood(particles: np.ndarray, context: StepContext) -> np.ndarray: """Return normalized zero-mean Gaussian log densities.""" @@ -34,17 +45,12 @@ def log_prior(particles: np.ndarray, context: StepContext) -> np.ndarray: def main() -> None: logger = configure_logging() - dims = 4 - bounds = (-10.0, 10.0) - correlation = 0.8 indices = np.arange(dims) covariance = correlation ** np.abs(indices[:, None] - indices[None, :]) cholesky = np.linalg.cholesky(covariance) log_norm = ( -0.5 * dims * np.log(2 * np.pi) - np.log(np.diag(cholesky)).sum() ) - n_chains = 100 - chain_length = 100 # Includes the resampled starting state. posterior = TemperedPosterior(log_prior, log_likelihood) # Truncation at +/-10 has negligible effect on the evidence reference. reference_log_evidence = -dims * np.log(bounds[1] - bounds[0]) @@ -53,6 +59,8 @@ def main() -> None: comparison_rows: list[str] = [] posterior_counts: dict[str, int] = {} + summaries: dict[str, dict] = {} + for name in ("Standard", "Waste-free"): mutation = MetropolisHastingsMutation( posterior, @@ -69,7 +77,7 @@ def main() -> None: moves = [MultinomialResampling(always_resample=True), mutation] logger.info("%s SMC: %d particles", name, n_particles) - rng = np.random.default_rng(123) + rng = np.random.default_rng(seed) initial = ParticleState(rng.uniform(*bounds, size=(n_particles, dims))) smc = SequentialMonteCarlo( steps=[ @@ -104,6 +112,16 @@ def main() -> None: name, particles.var(axis=0), ) + if documentation_output is not None: + summaries[name] = { + "log_evidence": float(results.final_state.log_evidence), + "weighted_mean": results.weighted_mean().tolist(), + "weighted_population_covariance": results.weighted_covariance().tolist(), + "n_particles": len(particles), + "likelihood_evaluations": int( + results.context.evaluation_counts.log_likelihood_evaluations + ), + } posterior_counts[name] = len(particles) comparison_rows.append( f"{name:<12} {len(particles):>10,d} {len(results.outputs):>8d} " @@ -142,7 +160,12 @@ def main() -> None: ax.spines[["top", "right"]].set_visible(False) ax.grid(alpha=0.2) ax.legend() - fig.savefig("waste_free_history.png", dpi=180) + output_directory = documentation_output or Path(".") + fig.savefig(output_directory / "waste_free_history.png", dpi=180) + if documentation_output is not None: + (documentation_output / "summary.json").write_text( + json.dumps(summaries, allow_nan=False) + "\n" + ) plt.close(fig) logger.info("Saved waste_free_history.png") diff --git a/particle_filtering/README.md b/particle_filtering/README.md deleted file mode 100644 index 80c39da..0000000 --- a/particle_filtering/README.md +++ /dev/null @@ -1,124 +0,0 @@ -# Particle filtering with Sequince - -This example shows how to implement particle filters in Sequince. - -It compares two particle filters for a scalar linear-Gaussian -state-space model: - -- a bootstrap filter, which proposes from the transition model; -- a locally optimal filter, which proposes conditional on the current - observation. - -Both filters process the same simulated observations. Their filtering means, -variances, and log evidence are compared with the exact Kalman-filter result. - -## Running the example - -```bash -python particle_filter.py -``` - -The script prints accuracy, effective-sample-size, and evidence diagnostics, -then writes `particle_filter.png` in this directory. - -Useful options include: - -```bash -python particle_filter.py --particles 10000 --observations 30 -python particle_filter.py --ess-threshold 0.5 -python particle_filter.py --output /tmp/particle-filter.png -``` - -By default both filters resample after every observation. Passing -`--ess-threshold FRACTION` instead resamples only when the effective sample -size divided by the particle count falls below that fraction. - -## Model - -The example uses - -```text -x_0 ~ Normal(0, sigma_0^2) -x_t | x_(t-1) ~ Normal(a x_(t-1), sigma_x^2) -y_t | x_t ~ Normal(x_t, sigma_y^2). -``` - -The model is linear and Gaussian, so a Kalman filter supplies exact filtering -moments and log evidence for comparison. - -## Why the filters use different steps - -For a proposal distribution `q`, particle filtering applies the incremental -weight - -```text -p(y_t | x_t) p(x_t | x_(t-1)) / q(x_t | x_(t-1), y_t). -``` - -In the bootstrap filter, `q` is the transition distribution itself. The -transition and proposal terms therefore cancel, leaving only the observation -likelihood. A regular propagation step followed by `IncrementalWeighting` is -enough: after propagation, the weighting step only needs the new particles. - -The locally optimal proposal is not the transition distribution, so its -weights also need the transition-to-proposal correction. That correction -depends on both the parent particles and their proposed children. A standalone -propagation step replaces the particles before the next step runs, so an -ordinary `IncrementalWeighting` step would no longer have both values. -`ImportanceTransition` instead samples the children and computes the complete -correction while parent and child particles are simultaneously available. - -The remaining steps are identical. Both filters record the weighted state and -then resample; only proposal generation and its associated weighting differ. - -## Bootstrap filter - -The bootstrap filter is assembled from this Sequince pipeline: - -```text -PropagationStep --> IncrementalWeighting --> RecordFilteringState --> SystematicResampling -``` - -`PropagationStep` samples from `p(x_t | x_(t-1))`, and -`IncrementalWeighting` applies `log p(y_t | x_t)`. The local -`RecordFilteringState` step saves a detached copy of the weighted population -before resampling. These pre-resampling populations give the filtering -moments; `SMCResult.states` contains the post-resampling populations passed to -the next iteration. - -## Locally optimal proposal filter - -The second filter samples from -`q(x_t | x_(t-1), y_t) = p(x_t | x_(t-1), y_t)`. The proposal parameters are - -```text -V_q = (1 / sigma_x^2 + 1 / sigma_y^2)^-1 -m_q = V_q (a x_(t-1) / sigma_x^2 + y_t / sigma_y^2). -``` - -Its pipeline is - -```text -ImportanceTransition(OptimalProposalKernel) --> RecordFilteringState --> SystematicResampling -``` - -The proposal kernel sees parent and child particles together and returns the -complete importance increment - -```text -log p(x_t | x_(t-1)) - log q(x_t | x_(t-1), y_t) -+ log p(y_t | x_t). -``` - -`ImportanceTransition` applies that increment, normalizes the weights, and -updates the log evidence. Keeping the proposal and its correction together -avoids storing an extra parent population in each state. - -The lower panel of the output figure compares effective sample size before -resampling. Conditioning on the observation generally gives the locally -optimal proposal a higher ESS than the bootstrap proposal. diff --git a/persistent_sampling/README.md b/persistent_sampling/README.md deleted file mode 100644 index 5beaac4..0000000 --- a/persistent_sampling/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# Persistent sampling - -This example demonstrates how to use persistent sampling in `sequince` to -sample an n-dimensional Rosenbrock target. - -Persistent sampling is a variation of SMC proposed by [Karamanis 2025]. - -Run the example from the repository root: - -```bash -python persistent_sampling/rosenbrock.py -``` - -For a larger run: - -```bash -python persistent_sampling/rosenbrock.py \ - --dimensions 16 \ - --n-particles 1024 \ - --target-ess 2048 \ - --target-total-ess 8192 \ - --mutation-steps 32 -``` - -The script prints diagnostics from the final weighted persistent state, -including its archive size, effective sample size, weighted mean, best -Rosenbrock potential, and log evidence. Unless `--no-plot` is supplied, it also -saves a weighted corner plot as `persistent_sampling/rosenbrock_d.png`. -Use `--output PATH` to choose a different location. - -Run the following for all available options: - -```bash -python persistent_sampling/rosenbrock.py --help -``` - -## Preconditioning - -This example also shows how to use the normalizing-flow-based preconditioning -described in [Karamanis 2022] and [Karamanis 2025]. - -The `--flow` option enables learned normalizing-flow preconditioning: - -```bash -python persistent_sampling/rosenbrock.py --dimensions 6 --flow -``` - -The flow option requires `sequince-density` and its flow dependencies. - -[Karamanis 2022]: https://arxiv.org/abs/2207.05660 -[Karamanis 2025]: https://arxiv.org/abs/2407.20722 diff --git a/pixi.lock b/pixi.lock new file mode 100644 index 0000000..98c9df7 --- /dev/null +++ b/pixi.lock @@ -0,0 +1,3888 @@ +version: 7 +platforms: +- name: linux-64 + virtual-packages: + - __unix=0=0 + - __linux=4.18 + - __glibc=2.28 + - __archspec=0=x86_64 +- name: osx-arm64 + virtual-packages: + - __unix=0=0 + - __osx=13.0 + - __archspec=0=m1 +environments: + check: + channels: + - url: https://conda.anaconda.org/conda-forge/ + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.1.1-py313ha52865f_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.5-hd2095e1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h81df57d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.13.16-hdc7f604_101_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.54.0-h0737f62_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.4-hcfc3c73_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.5-h781a0a9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.13.16-hf47f18c_101_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py313h3dea7bd_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.14.14-h40fa522_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h1df4ec4_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.1.0-py313h7037e92_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-hebe6cf0_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.3-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-4.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.20-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.10.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.13.0-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.6.2-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.11-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.21.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.1.1-pyhc364b38_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-discovery-1.6.2-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-9_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.5.0-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-21.14.7-pyh5ded981_0.conda + osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.3-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-4.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.20-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.10.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.13.0-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.6.2-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.11-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.21.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.1.1-pyhc364b38_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-discovery-1.6.2-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-9_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.5.0-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-21.14.7-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.1.1-py313h056b358_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.3-h55c6f16_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.5-h6111c0a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-h47dc5ef_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.13.16-hb350d79_101_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.54.0-h34a968b_101.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.5-h55eecbc_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.16-hb59dee6_101_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py313h65a2061_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h8b90a29_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.14.14-h279115b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hbeba79b_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ukkonen-1.1.0-py313h5c29297_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h74c22ad_3.conda + default: + channels: + - url: https://conda.anaconda.org/conda-forge/ + indexes: + - https://pypi.org/simple + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.5-hd2095e1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h81df57d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.13.16-hdc7f604_101_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.54.0-h0737f62_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.4-hcfc3c73_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.5-h781a0a9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.13.16-hf47f18c_101_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h1df4ec4_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-9_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - pypi: git+https://github.com/sequince-dev/sequince.git?branch=main#9968074e158e13760a9af5d5a1be0c28e554d036 + - pypi: https://files.pythonhosted.org/packages/07/ef/9b77fc80fc9bf28978d5649a3c71c0e77efe9e1966e5f608e7c3d5ba86b4/acai_control-0.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/1e/cf/83c3377e7018ee3c455153d9560c6a972b0a407075868f5e58979fc640a2/minipcn-0.3.0a1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/38/bb/d215ee7c73b61497b28a5503f9f53523f294fcc936762b7caf90e0c1c2b5/pyparsing-3.3.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/41/48/6450ed9243315322bbc19ac57b9b70d66a20bf1d38d124c96bc4bf6af9ea/scipy-1.18.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4c/1c/6ec22372362b03350fe3da7bf33491a07cc9a553a36dd2383b76ec1741eb/fonttools-4.66.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/68/77/8f93ecbde1a4d11dfd460cd19a0c159f5487352e751fa87f5d78cc9db08e/contourpy-1.4.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/76/3e/0fa5690a3a8303d821a46fd35691b1b6f8a9c06b97b41847ca41aed72e90/orng-0.2.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/86/16/1a8fd2b19544b84575cf84ef7aa3ad4c173b756d5f087c91f85d1b295777/array_api_compat-1.15.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/93/b2/696029cd979f5429ec25c12767734bf0e13175d3341a2aeac59127d00ca0/optree-0.20.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/a7/03/921a3d3c75785aca9ebfbfcabfbc3a1be12e2ab5265deb026d55a5a3f83e/tqdm-4.70.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a9/df/e7c10292f348cf55178a5b3c0092a38ea68891a3945f199ae8b7b8ac9dc1/matplotlib-3.11.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/ab/43/86aacc027959108b4c66eeae8b73cedb057dfa6eb3a335d05ad65197081c/kiwisolver-1.5.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bd/98/ef2b6fe2903e377cbe870c3b2800d62552f1e3dbe81ce49e1923c53d1c5c/h5py-3.16.0-cp313-cp313-manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/c7/b3/fcf70b2ed935f26fc15dfd81f72f781d395b09e302af5c86ec285d63524e/array_api_extra-0.11.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d6/cb/17a130e548ee1777f2a66e09240c531a9bf79454add7ef1198fe1e41c412/corner-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/da/24/3bd070f3269dc609d8f26b2643f62ef91bb415841c0b294805aaf7fe06da/numpy-2.5.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f7/62/de5bdd77d935331f4f802edc11e4d82950f642caad6cb2f949837b8560e2/pillow-12.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-9_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.3-h55c6f16_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.5-h6111c0a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-h47dc5ef_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.13.16-hb350d79_101_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.54.0-h34a968b_101.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.5-h55eecbc_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.16-hb59dee6_101_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h8b90a29_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hbeba79b_4.conda + - pypi: git+https://github.com/sequince-dev/sequince.git?branch=main#9968074e158e13760a9af5d5a1be0c28e554d036 + - pypi: https://files.pythonhosted.org/packages/07/ef/9b77fc80fc9bf28978d5649a3c71c0e77efe9e1966e5f608e7c3d5ba86b4/acai_control-0.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/10/76/8803c13605b763d33d156c4678fc77f8443389c0c51c8aef707bb02015f4/pillow-12.3.0-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/1e/cf/83c3377e7018ee3c455153d9560c6a972b0a407075868f5e58979fc640a2/minipcn-0.3.0a1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2a/f5/769f36d14922b8071a43e95d24d18b6bdafad10d7f5cf647867e1ac052bc/scipy-1.18.1-cp313-cp313-macosx_12_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/38/bb/d215ee7c73b61497b28a5503f9f53523f294fcc936762b7caf90e0c1c2b5/pyparsing-3.3.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3f/2f/103cdc5b7077cd569989d3ddb99ee3d4e96ee31bde8df2a6e426b2dc6195/contourpy-1.4.0-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/47/8b/40d33ffd2f378094ed462e9a9a0907e59d4de9845e65a59561272da350d4/kiwisolver-1.5.1-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/76/3e/0fa5690a3a8303d821a46fd35691b1b6f8a9c06b97b41847ca41aed72e90/orng-0.2.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/83/8c/b0ea9477fb1f0d4484bbc5cba21678cc9969704d8d7f3f158d1db35f8e14/numpy-2.5.4-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/86/16/1a8fd2b19544b84575cf84ef7aa3ad4c173b756d5f087c91f85d1b295777/array_api_compat-1.15.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/8d/c0/85fa4951ed8fcfc54ea10367e20617b60292780999835f636a6bdbf01c34/optree-0.20.0-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/a7/03/921a3d3c75785aca9ebfbfcabfbc3a1be12e2ab5265deb026d55a5a3f83e/tqdm-4.70.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a9/29/2a47b700545aca050e0ed73c89ba21e08407e11f5f42a567421b220de6c1/matplotlib-3.11.2-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/b0/65/5e088a45d0f43cd814bc5bec521c051d42005a472e804b1a36c48dada09b/h5py-3.16.0-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/b3/fcf70b2ed935f26fc15dfd81f72f781d395b09e302af5c86ec285d63524e/array_api_extra-0.11.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/cb/f4/e410b8c913da5b3fdbb4d16db0f2d2a0952f59c4db8d52dcf2d421d82044/fonttools-4.66.1-cp313-cp313-macosx_10_13_universal2.whl + - pypi: https://files.pythonhosted.org/packages/d6/cb/17a130e548ee1777f2a66e09240c531a9bf79454add7ef1198fe1e41c412/corner-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl + docs: + channels: + - url: https://conda.anaconda.org/conda-forge/ + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.8.0-py313h8f72f4d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py313h2fc2bef_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/charset-normalizer-3.5.2-h1114479_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.5-hd2095e1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h81df57d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.13.16-hdc7f604_101_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.54.0-h0737f62_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.4-hcfc3c73_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.4-py313h2af15c8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.5-h781a0a9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.13.16-hf47f18c_101_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h1df4ec4_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/accessible-pygments-0.0.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/alabaster-1.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.18.0-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.13.16-py313hd8ed1ab_101.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.20-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-2.0.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.15.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.21.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.13.16-h4df99d1_101.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-9_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/roman-numerals-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/roman-numerals-py-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-3.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-3.0.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-8.2.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-book-theme-1.1.4-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-applehelp-2.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-devhelp-2.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-2.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-2.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.8.0-pyhd8ed1ab_0.conda + osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/noarch/accessible-pygments-0.0.5-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/alabaster-1.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.18.0-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.20-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-2.0.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.15.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.21.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-9_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/roman-numerals-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/roman-numerals-py-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-3.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-3.0.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-8.2.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-book-theme-1.1.4-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-applehelp-2.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-devhelp-2.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-2.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-2.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.8.0-py313h8c7ad59_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py313h3c37e74_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.3-h55c6f16_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.5-h6111c0a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-h47dc5ef_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.13.16-hb350d79_101_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.54.0-h34a968b_101.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.4-py313h996ddf1_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.5-h55eecbc_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.16-hb59dee6_101_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h8b90a29_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hbeba79b_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hf451053_7.conda + flow: + channels: + - url: https://conda.anaconda.org/conda-forge/ + indexes: + - https://pypi.org/simple + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-8_kmp_llvm.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-h76c4fd7_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gmp-6.3.0-hfd2156b_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gmpy2-2.3.2-py313ha4a6282_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260526.0-cxx17_h0dc7533_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-11_h5875eb1_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-11_hfef963f_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.5-hd2095e1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h81df57d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.15.0-default_hd7c4d0c_1001.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h0cb94f2_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-11_h5e43f62_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-7.35.1-h622638d_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.13.16-hd51c5da_102_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.54.0-h0737f62_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtorch-2.14.1-cpu_mkl_hc3c961d_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.4-hcfc3c73_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.52.1-h280c20c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.4-hbdfff7e_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.4-ha7a5771_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-23.1.3-h7148c6a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.4-py313h2af15c8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mkl-2026.1.0-hd2095e1_245.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mpc-1.4.0-ha2cb11d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mpfr-4.2.2-ha2cb11d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.4-py313hb5f73ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/onednn-3.13.4-omp_h64ed1e9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.5-h781a0a9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.20.0-py313h6bdb861_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.13.16-hf47f18c_102_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.14.1-cpu_mkl_py313_h7b4a8e5_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-cpu-2.14.1-cpu_mkl_hd61e0f4_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sleef-3.9.0-ha0421bc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tbb-2023.1.0-haa8c3ee_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h1df4ec4_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.13.16-py313hd8ed1ab_102.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-4.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.9.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mpmath-1.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.7-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.4-pyh293190f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-11-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-global-3.0.4-pyh648e204_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-9_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/scikit-build-core-1.1.1-pyhcb39dcd_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sympy-1.14.0-pyh2585a3b_106.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.5.0-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - pypi: git+https://github.com/sequince-dev/sequince-flows.git?rev=cbe32939713f17be2c5deb8f813ba1ec4550ea7e#cbe32939713f17be2c5deb8f813ba1ec4550ea7e + - pypi: git+https://github.com/sequince-dev/sequince.git?branch=main#9968074e158e13760a9af5d5a1be0c28e554d036 + - pypi: https://files.pythonhosted.org/packages/07/ef/9b77fc80fc9bf28978d5649a3c71c0e77efe9e1966e5f608e7c3d5ba86b4/acai_control-0.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/1e/cf/83c3377e7018ee3c455153d9560c6a972b0a407075868f5e58979fc640a2/minipcn-0.3.0a1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/38/bb/d215ee7c73b61497b28a5503f9f53523f294fcc936762b7caf90e0c1c2b5/pyparsing-3.3.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/41/48/6450ed9243315322bbc19ac57b9b70d66a20bf1d38d124c96bc4bf6af9ea/scipy-1.18.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/4c/1c/6ec22372362b03350fe3da7bf33491a07cc9a553a36dd2383b76ec1741eb/fonttools-4.66.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/68/77/8f93ecbde1a4d11dfd460cd19a0c159f5487352e751fa87f5d78cc9db08e/contourpy-1.4.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/76/3e/0fa5690a3a8303d821a46fd35691b1b6f8a9c06b97b41847ca41aed72e90/orng-0.2.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/86/16/1a8fd2b19544b84575cf84ef7aa3ad4c173b756d5f087c91f85d1b295777/array_api_compat-1.15.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/95/10/ff159867f522cd98e039e748c5e9777446e8b797a79be691c6b730676094/zuko-1.6.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a7/03/921a3d3c75785aca9ebfbfcabfbc3a1be12e2ab5265deb026d55a5a3f83e/tqdm-4.70.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a9/df/e7c10292f348cf55178a5b3c0092a38ea68891a3945f199ae8b7b8ac9dc1/matplotlib-3.11.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/ab/43/86aacc027959108b4c66eeae8b73cedb057dfa6eb3a335d05ad65197081c/kiwisolver-1.5.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bd/98/ef2b6fe2903e377cbe870c3b2800d62552f1e3dbe81ce49e1923c53d1c5c/h5py-3.16.0-cp313-cp313-manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/c7/b3/fcf70b2ed935f26fc15dfd81f72f781d395b09e302af5c86ec285d63524e/array_api_extra-0.11.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d6/cb/17a130e548ee1777f2a66e09240c531a9bf79454add7ef1198fe1e41c412/corner-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f7/62/de5bdd77d935331f4f802edc11e4d82950f642caad6cb2f949837b8560e2/pillow-12.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.13.16-py313hd8ed1ab_102.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-4.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.9.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mpmath-1.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.7-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nomkl-1.0-h5ca1d4c_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.4-pyh293190f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-11-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-global-3.0.4-pyh648e204_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-9_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/scikit-build-core-1.1.1-pyhcb39dcd_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sympy-1.14.0-pyh2585a3b_106.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.5.0-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-8_kmp_llvm.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fmt-12.1.0-h99e4e11_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gmp-6.3.0-hf5e55b1_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gmpy2-2.3.2-py313h0091f1d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260526.0-cxx17_h4c27e2a_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-11_h51639a9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-11_hb0561ab_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.3-h55c6f16_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.5-h6111c0a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-h47dc5ef_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.2.0-h3cf6597_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.2.0-h07b0088_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.2.0-hdb7a957_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-11_hd9741b5_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.34-openmp_h5e6e99c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-7.35.1-h391e224_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.13.16-h0af5753_102_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.54.0-h34a968b_101.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtorch-2.14.1-cpu_generic_h8539c7d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.52.1-h1a92334_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-23.1.3-hdb3d66b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.4-py313h996ddf1_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/mpc-1.4.0-h071a35a_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/mpfr-4.2.2-h72846b0_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.4-py313h24c19cf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/onednn-3.13.4-omp_h3fb3e40_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.5-h55eecbc_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/optree-0.20.0-py313h5c89e09_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.16-hb59dee6_102_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pytorch-2.14.1-cpu_generic_py313_hf1850e0_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pytorch-cpu-2.14.1-cpu_generic_hcc7c195_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h8b90a29_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sleef-3.9.0-hb028509_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hbeba79b_4.conda + - pypi: git+https://github.com/sequince-dev/sequince-flows.git?rev=cbe32939713f17be2c5deb8f813ba1ec4550ea7e#cbe32939713f17be2c5deb8f813ba1ec4550ea7e + - pypi: git+https://github.com/sequince-dev/sequince.git?branch=main#9968074e158e13760a9af5d5a1be0c28e554d036 + - pypi: https://files.pythonhosted.org/packages/07/ef/9b77fc80fc9bf28978d5649a3c71c0e77efe9e1966e5f608e7c3d5ba86b4/acai_control-0.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/10/76/8803c13605b763d33d156c4678fc77f8443389c0c51c8aef707bb02015f4/pillow-12.3.0-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/1e/cf/83c3377e7018ee3c455153d9560c6a972b0a407075868f5e58979fc640a2/minipcn-0.3.0a1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2a/f5/769f36d14922b8071a43e95d24d18b6bdafad10d7f5cf647867e1ac052bc/scipy-1.18.1-cp313-cp313-macosx_12_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/38/bb/d215ee7c73b61497b28a5503f9f53523f294fcc936762b7caf90e0c1c2b5/pyparsing-3.3.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3f/2f/103cdc5b7077cd569989d3ddb99ee3d4e96ee31bde8df2a6e426b2dc6195/contourpy-1.4.0-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/47/8b/40d33ffd2f378094ed462e9a9a0907e59d4de9845e65a59561272da350d4/kiwisolver-1.5.1-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/76/3e/0fa5690a3a8303d821a46fd35691b1b6f8a9c06b97b41847ca41aed72e90/orng-0.2.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/86/16/1a8fd2b19544b84575cf84ef7aa3ad4c173b756d5f087c91f85d1b295777/array_api_compat-1.15.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/95/10/ff159867f522cd98e039e748c5e9777446e8b797a79be691c6b730676094/zuko-1.6.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a7/03/921a3d3c75785aca9ebfbfcabfbc3a1be12e2ab5265deb026d55a5a3f83e/tqdm-4.70.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a9/29/2a47b700545aca050e0ed73c89ba21e08407e11f5f42a567421b220de6c1/matplotlib-3.11.2-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/b0/65/5e088a45d0f43cd814bc5bec521c051d42005a472e804b1a36c48dada09b/h5py-3.16.0-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/b3/fcf70b2ed935f26fc15dfd81f72f781d395b09e302af5c86ec285d63524e/array_api_extra-0.11.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/cb/f4/e410b8c913da5b3fdbb4d16db0f2d2a0952f59c4db8d52dcf2d421d82044/fonttools-4.66.1-cp313-cp313-macosx_10_13_universal2.whl + - pypi: https://files.pythonhosted.org/packages/d6/cb/17a130e548ee1777f2a66e09240c531a9bf79454add7ef1198fe1e41c412/corner-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl + jax: + channels: + - url: https://conda.anaconda.org/conda-forge/ + indexes: + - https://pypi.org/simple + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.5-hd2095e1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h81df57d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.13.16-hdc7f604_101_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.54.0-h0737f62_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.4-hcfc3c73_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.5-h781a0a9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.13.16-hf47f18c_101_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h1df4ec4_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-9_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - pypi: git+https://github.com/sequince-dev/sequince.git?branch=main#9968074e158e13760a9af5d5a1be0c28e554d036 + - pypi: https://files.pythonhosted.org/packages/07/ef/9b77fc80fc9bf28978d5649a3c71c0e77efe9e1966e5f608e7c3d5ba86b4/acai_control-0.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/1e/cf/83c3377e7018ee3c455153d9560c6a972b0a407075868f5e58979fc640a2/minipcn-0.3.0a1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/23/cd/066e86230ae37ed0be70aae89aabf03ca8d9f39c8aea0dec8029455b5540/opt_einsum-3.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/24/ec/014b428c3a05837643768d08aeb8fb1289d119d3f9f6fba479dffae05dbc/jaxlib-0.11.2-cp313-cp313-manylinux_2_27_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/38/bb/d215ee7c73b61497b28a5503f9f53523f294fcc936762b7caf90e0c1c2b5/pyparsing-3.3.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/41/48/6450ed9243315322bbc19ac57b9b70d66a20bf1d38d124c96bc4bf6af9ea/scipy-1.18.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4c/1c/6ec22372362b03350fe3da7bf33491a07cc9a553a36dd2383b76ec1741eb/fonttools-4.66.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/68/77/8f93ecbde1a4d11dfd460cd19a0c159f5487352e751fa87f5d78cc9db08e/contourpy-1.4.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/76/3e/0fa5690a3a8303d821a46fd35691b1b6f8a9c06b97b41847ca41aed72e90/orng-0.2.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/86/16/1a8fd2b19544b84575cf84ef7aa3ad4c173b756d5f087c91f85d1b295777/array_api_compat-1.15.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/89/a5/da8ae6c6f1babe4b68e3e55d43d39b529e29774f10e0910671a6b8c86eb8/ml_dtypes-0.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/93/b2/696029cd979f5429ec25c12767734bf0e13175d3341a2aeac59127d00ca0/optree-0.20.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/a7/03/921a3d3c75785aca9ebfbfcabfbc3a1be12e2ab5265deb026d55a5a3f83e/tqdm-4.70.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a9/df/e7c10292f348cf55178a5b3c0092a38ea68891a3945f199ae8b7b8ac9dc1/matplotlib-3.11.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/ab/43/86aacc027959108b4c66eeae8b73cedb057dfa6eb3a335d05ad65197081c/kiwisolver-1.5.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bd/98/ef2b6fe2903e377cbe870c3b2800d62552f1e3dbe81ce49e1923c53d1c5c/h5py-3.16.0-cp313-cp313-manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/c7/b3/fcf70b2ed935f26fc15dfd81f72f781d395b09e302af5c86ec285d63524e/array_api_extra-0.11.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ca/87/0c0ca8433e1135f6d1acd355cff21f31c3d433d937d6547aa545c8b060b8/jax-0.11.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d6/cb/17a130e548ee1777f2a66e09240c531a9bf79454add7ef1198fe1e41c412/corner-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/da/24/3bd070f3269dc609d8f26b2643f62ef91bb415841c0b294805aaf7fe06da/numpy-2.5.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f7/62/de5bdd77d935331f4f802edc11e4d82950f642caad6cb2f949837b8560e2/pillow-12.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-9_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.3-h55c6f16_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.5-h6111c0a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-h47dc5ef_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.13.16-hb350d79_101_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.54.0-h34a968b_101.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.5-h55eecbc_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.16-hb59dee6_101_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h8b90a29_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hbeba79b_4.conda + - pypi: git+https://github.com/sequince-dev/sequince.git?branch=main#9968074e158e13760a9af5d5a1be0c28e554d036 + - pypi: https://files.pythonhosted.org/packages/07/ef/9b77fc80fc9bf28978d5649a3c71c0e77efe9e1966e5f608e7c3d5ba86b4/acai_control-0.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/10/76/8803c13605b763d33d156c4678fc77f8443389c0c51c8aef707bb02015f4/pillow-12.3.0-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/1e/cf/83c3377e7018ee3c455153d9560c6a972b0a407075868f5e58979fc640a2/minipcn-0.3.0a1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/23/cd/066e86230ae37ed0be70aae89aabf03ca8d9f39c8aea0dec8029455b5540/opt_einsum-3.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2a/f5/769f36d14922b8071a43e95d24d18b6bdafad10d7f5cf647867e1ac052bc/scipy-1.18.1-cp313-cp313-macosx_12_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/38/bb/d215ee7c73b61497b28a5503f9f53523f294fcc936762b7caf90e0c1c2b5/pyparsing-3.3.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3f/2f/103cdc5b7077cd569989d3ddb99ee3d4e96ee31bde8df2a6e426b2dc6195/contourpy-1.4.0-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/47/7f/f0d33a414e2d84fca39438a30f0588c32f7365746e903293cadad9d52b0d/jaxlib-0.11.2-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/47/8b/40d33ffd2f378094ed462e9a9a0907e59d4de9845e65a59561272da350d4/kiwisolver-1.5.1-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/50/51/fd1582b8f5ed8a9e7be0e161a6ea0dff70cb280479a12178df0b3a72700e/ml_dtypes-0.6.0-cp313-cp313-macosx_10_13_universal2.whl + - pypi: https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/76/3e/0fa5690a3a8303d821a46fd35691b1b6f8a9c06b97b41847ca41aed72e90/orng-0.2.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/83/8c/b0ea9477fb1f0d4484bbc5cba21678cc9969704d8d7f3f158d1db35f8e14/numpy-2.5.4-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/86/16/1a8fd2b19544b84575cf84ef7aa3ad4c173b756d5f087c91f85d1b295777/array_api_compat-1.15.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/8d/c0/85fa4951ed8fcfc54ea10367e20617b60292780999835f636a6bdbf01c34/optree-0.20.0-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/a7/03/921a3d3c75785aca9ebfbfcabfbc3a1be12e2ab5265deb026d55a5a3f83e/tqdm-4.70.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a9/29/2a47b700545aca050e0ed73c89ba21e08407e11f5f42a567421b220de6c1/matplotlib-3.11.2-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/b0/65/5e088a45d0f43cd814bc5bec521c051d42005a472e804b1a36c48dada09b/h5py-3.16.0-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/b3/fcf70b2ed935f26fc15dfd81f72f781d395b09e302af5c86ec285d63524e/array_api_extra-0.11.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ca/87/0c0ca8433e1135f6d1acd355cff21f31c3d433d937d6547aa545c8b060b8/jax-0.11.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/cb/f4/e410b8c913da5b3fdbb4d16db0f2d2a0952f59c4db8d52dcf2d421d82044/fonttools-4.66.1-cp313-cp313-macosx_10_13_universal2.whl + - pypi: https://files.pythonhosted.org/packages/d6/cb/17a130e548ee1777f2a66e09240c531a9bf79454add7ef1198fe1e41c412/corner-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl +packages: +- conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda + build_number: 20 + sha256: 1dd3fffd892081df9726d7eb7e0dea6198962ba775bd88842135a4ddb4deb3c9 + md5: a9f577daf3de00bca7c3c76c0ecbd1de + depends: + - __glibc >=2.17,<3.0.a0 + - libgomp >=7.5.0 + constrains: + - openmp_impl <0.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + strong: + - _openmp_mutex >=4.5 + size: 28948 + timestamp: 1770939786096 +- conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-8_kmp_llvm.conda + build_number: 8 + sha256: b8de006376660eb12c7fc40803c97e1480bf4bc1253f0d59694b12f81b52fe14 + md5: b4d1b93dd1dab4eea23395fd5e5b357d + depends: + - llvm-openmp >=9.0.1 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - _openmp_mutex >=4.5 + size: 8050 + timestamp: 1788046427177 +- conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.8.0-py313h8f72f4d_0.conda + sha256: f174b8570aea9c80de141fe43e0cf3353df233e77aa396f945be4bb242a31649 + md5: b2b55be09675e65d54fb1709208bd250 + depends: + - python + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + - zstd >=1.5.7,<1.6.0a0 + - python_abi 3.13.* *_cp313 + license: BSD-3-Clause AND MIT AND EPL-2.0 + run_exports: {} + size: 244807 + timestamp: 1791654836417 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py313h2fc2bef_4.conda + sha256: 3621a39631aec2d6f4b8ac0cc9ee6d46cee1ae684ba9d2ed6f84b9993e14fbc4 + md5: 8beb0b96d9c701006ff5511a3a92329f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - libstdcxx >=15 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + constrains: + - libbrotlicommon 1.2.0 h39a168f_4 + license: MIT + license_family: MIT + run_exports: {} + size: 367791 + timestamp: 1788480600802 +- conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda + sha256: 1a0d382c515ebf55f8ee1f38c8b81bc95af5c2acc42ad53b66bc5df932032f96 + md5: e675fabcf81499adc7edf58124fb1e01 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: bzip2-1.0.6 + license_family: BSD + purls: [] + run_exports: + weak: + - bzip2 >=1.0.8,<2.0a0 + size: 257808 + timestamp: 1785906269155 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.1.1-py313ha52865f_3.conda + sha256: e1968f1efd8491e32a84416a4994cea98bbf51aebd8c7c04c783e121088fec63 + md5: 2b17c17965f71b59158030dce83832e1 + depends: + - __glibc >=2.17,<3.0.a0 + - libffi >=3.7.0,<3.8.0a0 + - libgcc >=15 + - pycparser + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + license: MIT + license_family: MIT + run_exports: {} + size: 304628 + timestamp: 1788485299863 +- conda: https://conda.anaconda.org/conda-forge/linux-64/charset-normalizer-3.5.2-h1114479_0.conda + noarch: python + sha256: e4f04b45d68716fcc47c808aeacbf165f6f295fb91bd20b91aa38942b8d2cdc6 + md5: 78d5c6112c9320a1a797935cecfec7c2 + depends: + - python + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + - _python_abi3_support 1.* + - cpython >=3.9 + license: MIT + license_family: MIT + run_exports: {} + size: 217200 + timestamp: 1790766086110 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-h76c4fd7_1.conda + sha256: 25d14a197601fdd616f2e501431b52887e69b31e7defbc1679381d718357ceb7 + md5: a70bb6d42ad121aef456e0007e92bed6 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - fmt >=12.1.0,<12.2.0a0 + size: 199072 + timestamp: 1785915412102 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gmp-6.3.0-hfd2156b_3.conda + sha256: f36c336efc874f346a53a9011f67e997f9faac505562cc18c13b6d399cfe61c7 + md5: 576e32739f323438bf69ab006c21be7b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: GPL-2.0-or-later OR LGPL-3.0-or-later + purls: [] + run_exports: + weak: + - gmp >=6.3.0,<7.0a0 + size: 493498 + timestamp: 1786629164954 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gmpy2-2.3.2-py313ha4a6282_0.conda + sha256: 0883e75a80869d2eda1c78e3154eaac3a60cbcfdde56583e2c0dfc9eefb50bfb + md5: 2d829f59f3506bf5660fb6f0c4cc77a3 + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - mpfr >=4.2.2,<5.0a0 + - python_abi 3.13.* *_cp313 + - mpc >=1.4.0,<2.0a0 + - gmp >=6.3.0,<7.0a0 + license: LGPL-3.0-or-later + license_family: LGPL + purls: + - pkg:pypi/gmpy2?source=compressed-mapping + run_exports: {} + size: 327168 + timestamp: 1791166953052 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_103.conda + sha256: 901649b1edbe841fd4ee7e2d491a05184ed540eb01cdae506872f344babc1d28 + md5: 2362c70e7e79827c32db35547d9c179b + depends: + - __glibc >=2.17,<3.0.a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - binutils_impl_linux-64 2.46.1 + license: GPL-3.0-only + license_family: GPL + purls: [] + run_exports: {} + size: 742109 + timestamp: 1791565468810 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260526.0-cxx17_h0dc7533_3.conda + sha256: 24708450d80f4d542fc357c941909446e0b6caffb8baca478a0da1f74820e688 + md5: d224f3a53f55029366c73161f15461fb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + constrains: + - abseil-cpp =20260526.0 + - libabseil-static =20260526.0=cxx17* + license: Apache-2.0 + license_family: Apache + purls: [] + run_exports: + weak: + - libabseil >=20260526.0,<20260527.0a0 + - libabseil =*=cxx17* + size: 1440678 + timestamp: 1791681043356 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-11_h5875eb1_mkl.conda + build_number: 11 + sha256: 3b9862430914d13ae264398340117f80b85720de308f6a80ad463588abe10358 + md5: 8db4ae3941966215baa885eb004aded5 + depends: + - mkl >=2026.1.0,<2027.0a0 + constrains: + - blas 2.311 mkl + - libcblas 3.11.0 11*_mkl + - liblapack 3.11.0 11*_mkl + - liblapacke 3.11.0 11*_mkl + track_features: + - blas_mkl + - blas_mkl_2 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - libblas >=3.11.0,<4.0a0 + size: 18622 + timestamp: 1789061051585 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-11_hfef963f_mkl.conda + build_number: 11 + sha256: 6ebc9042e96050ddb4d57b666415677164561b8229905c52e6f84defbc884b0f + md5: 5100a1707606a71eebcb00ace3b23339 + depends: + - libblas 3.11.0 11_h5875eb1_mkl + constrains: + - blas 2.311 mkl + - liblapack 3.11.0 11*_mkl + - liblapacke 3.11.0 11*_mkl + track_features: + - blas_mkl + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - libcblas >=3.11.0,<4.0a0 + size: 18250 + timestamp: 1789061057597 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.5-hd2095e1_0.conda + sha256: 6de73033a7c5f712c9c5a54124e4581e025691029835e5bceaf10a333a7e7bc0 + md5: 72cfd2b95b0540247220b42f58a148d7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + constrains: + - expat 2.8.5.* + license: MIT + license_family: MIT + purls: [] + run_exports: {} + size: 77893 + timestamp: 1790346247149 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h81df57d_1.conda + sha256: c8c7583ef063bc3c430f1d48298e5ad24f796a35c22ed5b14183325825a61106 + md5: 6525a0b06aa4fd390795f0740636a9dd + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - libffi >=3.7.0,<3.8.0a0 + size: 68004 + timestamp: 1787753412298 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_7.conda + sha256: e031634c3a928f9594eba868bc46ef5230d6571a364a0c63215b06c4563bfcea + md5: 735629b400b70eeab21a2ba8f5b1c2ce + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + constrains: + - libgcc-ng ==16.2.0=*_7 + - libgomp 16.2.0 he0feb66_7 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + run_exports: {} + size: 1058444 + timestamp: 1790303109576 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_7.conda + sha256: 92a0bb350c7be6a4eedcb4e111c60aefd176018e6b1c009a6e025dbe9bbcd758 + md5: d904c7ddd95e026254b4b80808ee9e12 + depends: + - __glibc >=2.17,<3.0.a0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + run_exports: + strong: + - _openmp_mutex >=4.5 + size: 640566 + timestamp: 1790303046034 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.15.0-default_hd7c4d0c_1001.conda + sha256: a140f8b94caad6ea27e9deba66a74ae2566976591db8a867677c652add6ee5a0 + md5: 66c755b3bd9ec82308fa6ec7674f473f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - libstdcxx >=15 + - libxml2 + - libxml2-16 >=2.15.4 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - libhwloc >=2.15.0,<2.15.1.0a0 + size: 2456090 + timestamp: 1790243647895 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h0cb94f2_3.conda + sha256: f943117edb9cd4d9c61cc972eee5a34291dc55ea7a6e9e38da104995841cbcb6 + md5: f92233bf33e24a25668bb2119e2c51f9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + license: LGPL-2.1-only + purls: [] + run_exports: + weak: + - libiconv >=1.18,<2.0a0 + size: 789471 + timestamp: 1787033836207 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-11_h5e43f62_mkl.conda + build_number: 11 + sha256: 7b80fc195419463533262b9d327c75559dd820251902e3fa41cce02a3b23bb5b + md5: 7dee7c97a08b93f9d11f04d974e65724 + depends: + - libblas 3.11.0 11_h5875eb1_mkl + constrains: + - blas 2.311 mkl + - libcblas 3.11.0 11*_mkl + - liblapacke 3.11.0 11*_mkl + track_features: + - blas_mkl + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - liblapack >=3.11.0,<3.12.0a0 + size: 18295 + timestamp: 1789061064514 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda + sha256: 9787df8c22a59c9a70d3e5a10db9ad663485e75e9ccc3f09bd092cb7b95e0dab + md5: 1390b7c5ac0b1d8e447bc5efa6d3c8c2 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - xz 5.8.3.* + license: 0BSD + purls: [] + run_exports: + weak: + - liblzma >=5.8.3,<6.0a0 + size: 112995 + timestamp: 1786348617826 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_2.conda + sha256: 46820b4a835e175940ae20bec00fdddaff804cda27a1408ab1b95e77a2196437 + md5: fcfed1dc5053eb1901b66e7b1fc32588 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: BSD-2-Clause + license_family: BSD + purls: [] + run_exports: {} + size: 92759 + timestamp: 1786650399772 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-7.35.1-h622638d_3.conda + sha256: 37c51fbc936a6bb8bf5f67c8f056edacaaa41e8603738bc8a4dee186292bb114 + md5: fd307b61cceb36fdca38e1718bdb2893 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libgcc >=15 + - libstdcxx >=15 + - libzlib >=1.3.2,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - libprotobuf >=7.35.1,<7.35.2.0a0 + size: 3808338 + timestamp: 1787657986197 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.13.16-hd51c5da_102_cp313.conda + build_number: 102 + sha256: 87aa891c1e06aee56aabadf3ac0a1fd07432aa7f3834c24f3baccffcebe9f8bb + md5: de5467b4cae23d95c711f84b2033341d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + license: Python-2.0 + purls: [] + run_exports: + weak: + - libpython >=3.13,<4.0a0 + size: 9718549 + timestamp: 1791911455904 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.13.16-hdc7f604_101_cp313.conda + build_number: 101 + sha256: 7a01347dfc0d99c6592010fc278500ab12fc852d902f35beca69608c5f1282d5 + md5: 6c9b3aaaa24997d37c5e193cfdca3938 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - libstdcxx >=15 + license: Python-2.0 + purls: [] + run_exports: + weak: + - libpython >=3.13,<4.0a0 + size: 9718885 + timestamp: 1791334276485 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.54.0-h0737f62_101.conda + sha256: b4d5d4428bd1931eede11d2d69389c5b9d396a7f3c8df9d7c39c9efd80761eb3 + md5: a8b899d3c5fc1034b209045653b86ed2 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - libzlib >=1.3.2,<2.0a0 + constrains: + - icu <0.0a0 + license: blessing + purls: [] + run_exports: + weak: + - libsqlite >=3.54.0,<4.0a0 + size: 979215 + timestamp: 1791584361438 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_7.conda + sha256: fa7018298629fc429971fffd6ecba3d27a7b02e4e890d6a93ea01ffebe8cb745 + md5: 9b6707e0caca917fa6ea9ee473f37a5d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc 16.2.0 ha9f2e26_7 + constrains: + - libstdcxx-ng ==16.2.0=*_7 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + run_exports: {} + size: 6615436 + timestamp: 1790303132935 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libtorch-2.14.1-cpu_mkl_hc3c961d_100.conda + sha256: 771a151465e667c6165eace7d61f1eee94c1bb9cbeceaeca55bf14450ef511aa + md5: 0d4407c5f1fcbf718363cd1b0579d25d + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex * *_llvm + - _openmp_mutex >=4.5 + - fmt >=12.1.0,<12.2.0a0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libblas * *mkl + - libcblas >=3.11.0,<4.0a0 + - libgcc >=15 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libstdcxx >=15 + - libuv >=1.52.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - llvm-openmp >=23.1.3 + - mkl >=2026.1.0,<2027.0a0 + - onednn >=3.13.4,<4.0a0 + - pybind11-abi 11 + - sleef >=3.9.0,<4.0a0 + constrains: + - pytorch 2.14.1 cpu_mkl_*_100 + - pytorch-cpu 2.14.1 + - pytorch-gpu <0.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - libtorch >=2.14.1,<2.15.0a0 + size: 62087888 + timestamp: 1791586025454 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.4-hcfc3c73_0.conda + sha256: f34b8d1de9a2bf62476d8608057d9b8a918d97444236dc53a47128f2fae79041 + md5: 54a948337e66706380ff24d6b5fde63f + depends: + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - libuuid >=2.42.4,<3.0a0 + size: 34442 + timestamp: 1790121449161 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.52.1-h280c20c_1.conda + sha256: 67761d0206f84140047a367eaf9befe03a7e157a29ee25aee0047b40801cc6b6 + md5: 01c4ed87826af55996768af6dbc936b4 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - libuv >=1.52.1,<2.0a0 + size: 420040 + timestamp: 1785914567661 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.4-hbdfff7e_1.conda + sha256: 642d5d650a5f877a13ca60b720ae88643a145a30fb7a1065fd96c370595a5e2c + md5: 36e109ff64ea3bc459fdf0bfad9dd21b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.3,<6.0a0 + - libzlib >=1.3.2,<2.0a0 + constrains: + - icu <0.0a0 + - libxml2 2.15.4 + license: MIT + license_family: MIT + purls: [] + run_exports: {} + size: 566161 + timestamp: 1791465627683 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.4-ha7a5771_1.conda + sha256: ef0f51a53d5f5d8a0aa8a71115a36b1bb3cd3ac95d65ced2cbfec7f1f08229b0 + md5: 7c2fba53903a27aeb56a410a9510774b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.3,<6.0a0 + - libxml2-16 2.15.4 hbdfff7e_1 + - libzlib >=1.3.2,<2.0a0 + constrains: + - icu <0.0a0 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - libxml2 + - libxml2-16 >=2.15.4 + size: 46856 + timestamp: 1791465632103 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda + sha256: eb8a0db0aa570124f7d2a93d7c7f596e3390df5e047818d873baad32985fc736 + md5: 0de0122d9570a8ab637c6b73db268389 + depends: + - __glibc >=2.17,<3.0.a0 + constrains: + - zlib 1.3.2 *_3 + license: Zlib + license_family: Other + purls: [] + run_exports: + weak: + - libzlib >=1.3.2,<2.0a0 + size: 63713 + timestamp: 1785362952714 +- conda: https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-23.1.3-h7148c6a_1.conda + sha256: 250d9e8413c3e45a97626e03125fcce2c172b41bcb8e6226717c467d6e9327c9 + md5: fd3aa819acbe2a94a3c332cfdab0799a + depends: + - __glibc >=2.17,<3.0.a0 + constrains: + - intel-openmp <0.0a0 + - openmp 23.1.3|23.1.3.* + license: Apache-2.0 WITH LLVM-exception + purls: [] + run_exports: + strong: + - llvm-openmp >=23.1.3 + - _openmp_mutex >=4.5 + - _openmp_mutex * *_llvm + size: 6121536 + timestamp: 1791950734069 +- conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.4-py313h2af15c8_1.conda + sha256: 74e611bb15ffe1065c14e9007a6f04f15a53f350524c3cd1d033620ef9c3c035 + md5: d15255d7f81d9188edecde1e024a1649 + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - python_abi 3.13.* *_cp313 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/markupsafe?source=compressed-mapping + run_exports: {} + size: 27785 + timestamp: 1791548971554 +- conda: https://conda.anaconda.org/conda-forge/linux-64/mkl-2026.1.0-hd2095e1_245.conda + sha256: a2699b4db5f93ae7211249f5b8df4981c22765c392cc6d34c18d76994fe3b327 + md5: 5c8b5643ac9b96cd1c5b65a17337ce5c + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex * *_llvm + - _openmp_mutex >=4.5 + - libgcc >=15 + - libstdcxx >=15 + - llvm-openmp >=23.1.0 + - tbb >=2023.0.0 + license: LicenseRef-IntelSimplifiedSoftwareOct2022 + license_family: Proprietary + purls: [] + run_exports: {} + size: 142335878 + timestamp: 1787725704439 +- conda: https://conda.anaconda.org/conda-forge/linux-64/mpc-1.4.0-ha2cb11d_1.conda + sha256: 3d56733fe0f44159787ad086d5e7bdab8b69e6a5a9b6b873a8beb3519f3d8d07 + md5: f0f6c8691a9ed4099d1f287a444e251a + depends: + - __glibc >=2.17,<3.0.a0 + - gmp >=6.3.0,<7.0a0 + - libgcc >=15 + - mpfr >=4.2.2,<5.0a0 + license: LGPL-3.0-or-later + license_family: LGPL + purls: [] + run_exports: + weak: + - mpc >=1.4.0,<2.0a0 + size: 101074 + timestamp: 1787668090174 +- conda: https://conda.anaconda.org/conda-forge/linux-64/mpfr-4.2.2-ha2cb11d_1.conda + sha256: 0be79ef2ee536e0c5d3ddb4a52bbae9f26d08560623299f33db2f4c4a17c525a + md5: 7dcc10f6c0bc3596d5519a710a84dfd4 + depends: + - __glibc >=2.17,<3.0.a0 + - gmp >=6.3.0,<7.0a0 + - libgcc >=15 + license: LGPL-3.0-only + license_family: LGPL + purls: [] + run_exports: + weak: + - mpfr >=4.2.2,<5.0a0 + size: 730409 + timestamp: 1787236218159 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + sha256: 5d46557214ed184381dafe835b7c94a474a1c3b307a08a250b1ea4779b44ffb3 + md5: ee6c0cd80a60961a1f48aa3e0b91f986 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: X11 AND BSD-3-Clause + purls: [] + run_exports: + weak: + - ncurses >=6.6,<7.0a0 + size: 911196 + timestamp: 1786355078102 +- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.4-py313hb5f73ae_0.conda + sha256: e8ffae51383b57192b018bdeeb95c84ec617aa1428656825b1d4a326b81410dd + md5: 1ddee4b1dc2f784b4137a43789be3816 + depends: + - python + - libstdcxx >=15 + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + - libcblas >=3.9.0,<4.0a0 + - libblas >=3.9.0,<4.0a0 + - liblapack >=3.9.0,<4.0a0 + - python_abi 3.13.* *_cp313 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/numpy?source=compressed-mapping + run_exports: + weak: + - numpy >=1.25,<3 + size: 9339599 + timestamp: 1791675323834 +- conda: https://conda.anaconda.org/conda-forge/linux-64/onednn-3.13.4-omp_h64ed1e9_0.conda + sha256: 9fd0ca36c2983d40bc07b14eecfaca6f9fafdf078f0ef5c45f6491d2cf1140d7 + md5: 474f6631d8b9c50f26ee383d6a155a7b + depends: + - libstdcxx >=15 + - libgcc >=15 + - __glibc >=2.28,<3.0.a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - onednn >=3.13.4,<4.0a0 + size: 13798149 + timestamp: 1791401101750 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.5-h781a0a9_0.conda + sha256: 9c1533f1cf784f0e250919c0c9762d0b0e6bc727a79290ffb7cea1ceca1d038c + md5: 5a51a2ea653f120240b1a9bf76e0aae9 + depends: + - __glibc >=2.17,<3.0.a0 + - ca-certificates + - libgcc >=15 + license: Apache-2.0 + license_family: Apache + purls: [] + run_exports: + weak: + - openssl >=3.6.5,<4.0a0 + size: 3195299 + timestamp: 1791098052301 +- conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.20.0-py313h6bdb861_1.conda + sha256: f72bf4359c00908875c20eefc3328c994111837e33206f5c34c7c029dd770a94 + md5: fc3bae04198dde717798c6dc25044aa6 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - libstdcxx >=15 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + - typing-extensions >=4.12 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/optree?source=hash-mapping + run_exports: {} + size: 586381 + timestamp: 1788504569784 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.13.16-hf47f18c_101_cp313.conda + build_number: 101 + sha256: 914ce0022ee08951329ce85d8a35f55fa11e74239568bca6102375d630ca9e61 + md5: 85450b9eb16e2b58d2cae71600e6fbde + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-64 >=2.36.1 + - libexpat >=2.8.5,<3.0a0 + - libffi >=3.7.0,<3.8.0a0 + - libgcc >=15 + - liblzma >=5.8.3,<6.0a0 + - libmpdec >=4.0.0,<5.0a0 + - libpython 3.13.16 hdc7f604_101_cp313 + - libsqlite >=3.53.4,<4.0a0 + - libuuid >=2.42.4,<3.0a0 + - libzlib >=1.3.2,<2.0a0 + - ncurses >=6.6,<7.0a0 + - openssl >=3.5.9,<4.0a0 + - python_abi 3.13.* *_cp313 + - readline >=8.3,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + license: Python-2.0 + purls: [] + run_exports: + weak: + - python_abi 3.13.* *_cp313 + noarch: + - python + size: 24430407 + timestamp: 1791334320130 + python_site_packages_path: lib/python3.13/site-packages +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.13.16-hf47f18c_102_cp313.conda + build_number: 102 + sha256: 38d9b47548304992a6bcdc9bc5472730a77178047ecee142381acf626984c5d2 + md5: d406e245f44390c9c3adcd86cda21899 + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-64 >=2.36.1 + - libexpat >=2.8.5,<3.0a0 + - libffi >=3.7.0,<3.8.0a0 + - libgcc >=15 + - liblzma >=5.8.3,<6.0a0 + - libmpdec >=4.0.0,<5.0a0 + - libpython 3.13.16 hd51c5da_102_cp313 + - libsqlite >=3.54.0,<4.0a0 + - libuuid >=2.42.4,<3.0a0 + - libzlib >=1.3.2,<2.0a0 + - ncurses >=6.6,<7.0a0 + - openssl >=3.5.9,<4.0a0 + - python_abi 3.13.* *_cp313 + - readline >=8.3,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + license: Python-2.0 + purls: [] + run_exports: + weak: + - python_abi 3.13.* *_cp313 + noarch: + - python + size: 24298220 + timestamp: 1791911488431 + python_site_packages_path: lib/python3.13/site-packages +- conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.14.1-cpu_mkl_py313_h7b4a8e5_100.conda + sha256: 154b2357b04753651586dac84fe2cdb673eb26aacf1c72d3eaa20620c2549cce + md5: 29ab64128c75eac81d31e6f35eab85ca + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex * *_llvm + - _openmp_mutex >=4.5 + - filelock + - fmt >=12.1.0,<12.2.0a0 + - fsspec >=0.8.5 + - jinja2 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libblas * *mkl + - libcblas >=3.11.0,<4.0a0 + - libgcc >=15 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libstdcxx >=15 + - libtorch 2.14.1 cpu_mkl_hc3c961d_100 + - libuv >=1.52.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - llvm-openmp >=23.1.3 + - mkl >=2026.1.0,<2027.0a0 + - networkx >=2.5.1 + - numpy >=1.25,<3 + - onednn >=3.13.4,<4.0a0 + - optree >=0.13.0 + - pybind11 + - pybind11-abi 11 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + - scikit-build-core + - setuptools >=77.0.3 + - sleef >=3.9.0,<4.0a0 + - sympy >=1.13.3 + - typing_extensions >=4.13.0 + constrains: + - pytorch-cpu 2.14.1 + - pytorch-gpu <0.0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/torch?source=hash-mapping + run_exports: + weak: + - pytorch >=2.14.1,<2.15.0a0 + - libtorch >=2.14.1,<2.15.0a0 + size: 29275859 + timestamp: 1791586803158 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-cpu-2.14.1-cpu_mkl_hd61e0f4_100.conda + sha256: c8a8b42cd076c41a7547c96ec9af52bb9d46f03465d83328913658ab57ab58d5 + md5: e3d9aa9875bf8cfc0a0f4eb6141d35f4 + depends: + - pytorch 2.14.1 cpu_mkl*100 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: {} + size: 56075 + timestamp: 1791587589299 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py313h3dea7bd_1.conda + sha256: ef7df29b38ef04ec67a8888a4aa039973eaa377e8c4b59a7be0a1c50cd7e4ac6 + md5: f256753e840c3cd3766488c9437a8f8b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + run_exports: {} + size: 201616 + timestamp: 1770223543730 +- conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda + sha256: 01b3fe073a66e321970d09e04b388708f8cbdca5cdfbfcb7c9eeb470ad10383d + md5: 69c01c781e7c8190bbdaf79e3848c5ca + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - ncurses >=6.6,<7.0a0 + license: GPL-3.0-only + license_family: GPL + purls: [] + run_exports: + weak: + - readline >=8.3,<9.0a0 + size: 348899 + timestamp: 1787033801506 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.14.14-h40fa522_1.conda + noarch: python + sha256: 0c6c9825ff88195fd13936d63872213d6c88c1fe795d136881c0753c3037c5ff + md5: d3e1d08b141529c7fce6a13b4d670605 + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + run_exports: {} + size: 9131490 + timestamp: 1769520999080 +- conda: https://conda.anaconda.org/conda-forge/linux-64/sleef-3.9.0-ha0421bc_0.conda + sha256: 57afc2ab5bdb24cf979964018dddbc5dfaee130b415e6863765e45aed2175ee4 + md5: e8a0b4f5e82ecacffaa5e805020473cb + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + - libgcc >=14 + - libstdcxx >=14 + license: BSL-1.0 + purls: [] + run_exports: + weak: + - sleef >=3.9.0,<4.0a0 + size: 1951720 + timestamp: 1756274576844 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tbb-2023.1.0-haa8c3ee_2.conda + sha256: 5e47c01ea6c2bea041b78b4e45b3135b80bfc015533ec44e4c2d2326a755f8ba + md5: f805909f478c179e230baf7bd5878e26 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - libhwloc >=2.15.0,<2.15.1.0a0 + - libstdcxx >=15 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: {} + size: 186862 + timestamp: 1790591931727 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h1df4ec4_4.conda + build_number: 104 + sha256: a1a241d172c1ccab067ba245206dd048bc3c2d1b84504b53c9468e99adfc16a1 + md5: 676a2f9b1c4fcf57b70aa5c65f6c60d0 + depends: + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + - libzlib >=1.3.2,<2.0a0 + constrains: + - xorg-libx11 >=1.8.13,<2.0a0 + license: TCL + purls: [] + run_exports: + weak: + - tk >=8.6.13,<8.7.0a0 + size: 3566806 + timestamp: 1787272857910 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.1.0-py313h7037e92_0.conda + sha256: 7f2e4f38e57c17858c644259a1be868d6e98780239fd93bfa057cb5cfc24a928 + md5: cb423e0853b3dde2b3738db4dedf5ba2 + depends: + - __glibc >=2.17,<3.0.a0 + - cffi + - libgcc >=14 + - libstdcxx >=14 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + license: MIT + license_family: MIT + run_exports: {} + size: 14910 + timestamp: 1769438729201 +- conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-hebe6cf0_3.conda + sha256: d164dfa75ecd538f6fd68765defcc06aa875bc697b9b215362d79a2a73125dd0 + md5: e741576fb8f89821ac7c1c537322a33d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + license: MIT + license_family: MIT + run_exports: + weak: + - yaml >=0.2.5,<0.3.0a0 + size: 84936 + timestamp: 1787228426393 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda + sha256: 47d682b9f6d6ec9eb1a6e6c3e75ea6273e899e78fb7fc59f81d39745009fbc60 + md5: aa459086047c0e5e27023ab19f8cb86a + depends: + - __glibc >=2.17,<3.0.a0 + - libzlib >=1.3.2,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - zstd >=1.5.7,<1.6.0a0 + size: 601301 + timestamp: 1786599621503 +- conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_3.conda + sha256: 2a7204314663eeda5dec482a956f0e2eaf289bd5b9953eaaaad0e81aa64638f2 + md5: 3845f3d75991bae0fb90884662f4327c + depends: + - cpython + - python-gil + license: MIT + license_family: MIT + run_exports: {} + size: 8144 + timestamp: 1784221492234 +- conda: https://conda.anaconda.org/conda-forge/noarch/accessible-pygments-0.0.5-pyhd8ed1ab_1.conda + sha256: 1307719f0d8ee694fc923579a39c0621c23fdaa14ccdf9278a5aac5665ac58e9 + md5: 74ac5069774cdbc53910ec4d631a3999 + depends: + - pygments + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 1326096 + timestamp: 1734956217254 +- conda: https://conda.anaconda.org/conda-forge/noarch/alabaster-1.0.0-pyhd8ed1ab_1.conda + sha256: 6c4456a138919dae9edd3ac1a74b6fbe5fd66c05675f54df2f8ab8c8d0cc6cea + md5: 1fd9696649f65fd6611fcdb4ffec738a + depends: + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 18684 + timestamp: 1733750512696 +- conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.18.0-pyhcf101f3_1.conda + sha256: a14a9ad02101aab25570543a59c5193043b73dc311a25650134ed9e6cb691770 + md5: f1976ce927373500cc19d3c0b2c85177 + depends: + - python >=3.10 + - python + constrains: + - pytz >=2015.7 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 7684321 + timestamp: 1772555330347 +- conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda + sha256: aed4b9dcf68ec2a75e5645fed14d77fd884d38d2e52bfa6ef4b278d90cd88781 + md5: 3b261da3fe9b4168738712832410b022 + depends: + - python >=3.10 + - soupsieve >=1.2 + - typing-extensions + license: MIT + license_family: MIT + run_exports: {} + size: 92704 + timestamp: 1780853175566 +- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + sha256: 0a0544cf95f64394fe4959286f5c71f5444ad58feb0602e53becb27448d24da6 + md5: 0f51e2391ade309db462a55611263e9c + depends: + - __unix + license: ISC + purls: [] + run_exports: {} + size: 131780 + timestamp: 1784754889428 +- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda + sha256: fb167de4388e64e52aa3907ed099afab944c1fa6e5f74b281a312dae1bcf7f3b + md5: 37e13edbe3b48f1095a9d085ef9cd83b + depends: + - python >=3.10 + license: ISC + run_exports: {} + size: 137015 + timestamp: 1784717699092 +- conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.5.0-pyhd8ed1ab_0.conda + sha256: aa589352e61bb221351a79e5946d56916e3c595783994884accdb3b97fe9d449 + md5: 381bd45fb7aa032691f3063aff47e3a1 + depends: + - python >=3.10 + license: MIT + license_family: MIT + run_exports: {} + size: 13589 + timestamp: 1763607964133 +- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.5.1-pyhd8ed1ab_0.conda + sha256: cb60ef3e0631c8bacb4f7057196dee4496091a22baa3bb4b9bccb12c7e1c921b + md5: e0ac3accc64e23e40969d660e5f58ac8 + depends: + - python >=3.10 + license: MIT + license_family: MIT + run_exports: {} + size: 64487 + timestamp: 1786835648298 +- conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + sha256: ab29d57dc70786c1269633ba3dff20288b81664d3ff8d21af995742e2bb03287 + md5: 962b9857ee8e7018c22f2776ffa0b2d7 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 27011 + timestamp: 1733218222191 +- conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.13.16-py313hd8ed1ab_101.conda + noarch: generic + sha256: b50695cff97a76a961fe9837666c2f44c1a9048834bfc899b7945b5867af8ca1 + md5: 55a50f3a95f8622b6a2cf0373860fc53 + depends: + - python >=3.13,<3.14.0a0 + - python_abi * *_cp313 + license: Python-2.0 + run_exports: {} + size: 49892 + timestamp: 1791332598898 +- conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.13.16-py313hd8ed1ab_102.conda + noarch: generic + sha256: 21e80bd39d647ccdeac2a6ab042d4f9cf66963b4696c6bb9ba052f445a6100e4 + md5: f06b4e55916af895989a48ea4c2732c1 + depends: + - python >=3.13,<3.14.0a0 + - python_abi * *_cp313 + license: Python-2.0 + purls: [] + run_exports: {} + size: 49980 + timestamp: 1791910441528 +- conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.3-pyhcf101f3_0.conda + sha256: e2753997b8bd34205f42be01b8bab8037423dc30c02a1ec12de23e5b4c0b0a2e + md5: 58638f77697c4f6726753eb8be34818b + depends: + - python >=3.10 + - python + license: Apache-2.0 + license_family: APACHE + run_exports: {} + size: 303705 + timestamp: 1781320269259 +- conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_1.conda + sha256: fa5966bb1718bbf6967a85075e30e4547901410cc7cb7b16daf68942e9a94823 + md5: 24c1ca34138ee57de72a943237cde4cc + depends: + - python >=3.9 + license: CC-PDDC AND BSD-3-Clause AND BSD-2-Clause AND ZPL-2.1 + run_exports: {} + size: 402700 + timestamp: 1733217860944 +- conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + sha256: ee6cf346d017d954255bbcbdb424cddea4d14e4ed7e9813e429db1d795d01144 + md5: 8e662bd460bda79b1ea39194e3c4c9ab + depends: + - python >=3.10 + - typing_extensions >=4.6.0 + license: MIT and PSF-2.0 + purls: + - pkg:pypi/exceptiongroup?source=hash-mapping + run_exports: {} + size: 21333 + timestamp: 1763918099466 +- conda: https://conda.anaconda.org/conda-forge/noarch/filelock-4.1.1-pyhd8ed1ab_0.conda + sha256: 744790edc27f622ffa20af6029b8f628e8b7e0993ca1dbdafcf015de6c1fad6f + md5: fbff0cf6c30bdd9b38d0448fdadfe72d + depends: + - python >=3.11 + license: MIT + license_family: MIT + purls: + - pkg:pypi/filelock?source=compressed-mapping + run_exports: {} + size: 102017 + timestamp: 1791739638282 +- conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.9.0-pyhd8ed1ab_0.conda + sha256: 7d17272012e7479ba58f8a72b522d3e88e6837a04a111a278ff417aa82364255 + md5: 42de5cd170647599a84f468a42fabb89 + depends: + - python >=3.11 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/fsspec?source=compressed-mapping + run_exports: {} + size: 163722 + timestamp: 1790256848815 +- conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda + sha256: 307dd6ec90140c3cf4171071b0e5e870abec314f4565c1edd5bc433e942cdcc0 + md5: e652ac7756069c456d0da2a922cd7df5 + depends: + - python >=3.10 + - hyperframe >=6.1,<7 + - hpack >=4.2,<5 + - python + license: MIT + license_family: MIT + run_exports: {} + size: 100789 + timestamp: 1785796355216 +- conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda + sha256: fdcea5d7cb314485d3907192ef024c704311548c5b0cbeb390cd1951051e29d2 + md5: b395909221b9bd1df066e5930e18855b + depends: + - python >=3.10 + license: MIT + license_family: MIT + run_exports: {} + size: 32884 + timestamp: 1782283986153 +- conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + sha256: 77af6f5fe8b62ca07d09ac60127a30d9069fdc3c68d6b256754d0ffb1f7779f8 + md5: 8e6923fc12f1fe8f8c4e5c9f343256ac + depends: + - python >=3.9 + license: MIT + license_family: MIT + run_exports: {} + size: 17397 + timestamp: 1737618427549 +- conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.20-pyhd8ed1ab_0.conda + sha256: 6375ae2e28de130843ab7ca98132bf83d988f7fd7034e3dbe646308dbeaca2fd + md5: be9ea4f030b4b2bc121b2a548b066c0b + depends: + - python >=3.11 + - ukkonen + license: MIT + license_family: MIT + run_exports: {} + size: 79783 + timestamp: 1790540591548 +- conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.20-pyh5ded981_0.conda + sha256: e044b1ec829e2e24f971fa3f3408eb26f1db836e521a58f08b0fd9a03fee47be + md5: ee73be987f9de9ea5c2fdaeefa7b6491 + depends: + - python >=3.11 + - python + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 178572 + timestamp: 1790020377150 +- conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-2.0.1-pyhd8ed1ab_0.conda + sha256: 4e787f9ccc31053ccea56d98ecd284a4f788988a3f9c4627db72fdd0b127529b + md5: ebc56022e4ef7e74a829be94c53797ca + depends: + - python >=3.10 + license: MIT + license_family: MIT + run_exports: {} + size: 21467 + timestamp: 1787649248672 +- conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.1-pyhd8ed1ab_0.conda + sha256: 8f53876d0fb3525235e39635dd55633abe0a4307b1b3ab1415c80a9e0bbdf663 + md5: c0a803d97dd09da7e2c8b0fdfaa5739b + depends: + - python >=3.11 + license: MIT + license_family: MIT + run_exports: {} + size: 14485 + timestamp: 1791588553866 +- conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + sha256: fc9ca7348a4f25fed2079f2153ecdcf5f9cf2a0bc36c4172420ca09e1849df7b + md5: 04558c96691bed63104678757beb4f8d + depends: + - markupsafe >=2.0 + - python >=3.10 + - python + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/jinja2?source=hash-mapping + run_exports: {} + size: 120685 + timestamp: 1764517220861 +- conda: https://conda.anaconda.org/conda-forge/noarch/mpmath-1.4.1-pyhd8ed1ab_0.conda + sha256: 5bbf2f8179ec43d34d67ca8e4989d216c1bdb4b749fe6cb40e86ebf88c1b5300 + md5: 2e81b32b805f406d23ba61938a184081 + depends: + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/mpmath?source=hash-mapping + run_exports: {} + size: 464918 + timestamp: 1773662068273 +- conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.7-pyhc364b38_0.conda + sha256: 9458e13767779d5db48c035ba27a1af843fde5187d34c17b60076e18835e8637 + md5: ddc679d85e7c9c3738efd303b6580c69 + depends: + - python >=3.12 + - python + constrains: + - numpy >=1.25 + - scipy >=1.11.2 + - matplotlib-base >=3.8 + - pandas >=2.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/networkx?source=compressed-mapping + run_exports: {} + size: 1642541 + timestamp: 1790068597221 +- conda: https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.10.0-pyhd8ed1ab_0.conda + sha256: 4fa40e3e13fc6ea0a93f67dfc76c96190afd7ea4ffc1bac2612d954b42cdc3ee + md5: eb52d14a901e23c39e9e7b4a1a5c015f + depends: + - python >=3.10 + - setuptools + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 40866 + timestamp: 1766261270149 +- conda: https://conda.anaconda.org/conda-forge/noarch/nomkl-1.0-h5ca1d4c_0.tar.bz2 + sha256: d38542a151a90417065c1a234866f97fd1ea82a81de75ecb725955ab78f88b4b + md5: 9a66894dfd07c4510beb6b3f9672ccc0 + constrains: + - mkl <0.a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: {} + size: 3843 + timestamp: 1582593857545 +- conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda + sha256: c432626b16768b8dab228bfb706f7060c2d462a21c516d240f68f2f902b5a044 + md5: 936687ed80f295a1f5dbcf8bd34c252c + depends: + - python >=3.9 + - python + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/packaging?source=hash-mapping + run_exports: {} + size: 116363 + timestamp: 1785888127370 +- conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda + sha256: 6eaee417d33f298db79bc7185ab1208604c0e6cf51dade34cd513c6f9db9c6f3 + md5: 11adc78451c998c0fd162584abfa3559 + depends: + - python >=3.10 + license: MPL-2.0 + license_family: MOZILLA + purls: + - pkg:pypi/pathspec?source=hash-mapping + run_exports: {} + size: 56559 + timestamp: 1777271601895 +- conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.13.0-pyh5ded981_0.conda + sha256: e75e5e85e0039c77f8b707baa862f029f25a582ff158bcae36cf361a409eaf59 + md5: 35cc2a176fc6f886d6efa41ecb5f1de0 + depends: + - python >=3.11 + - python + license: MIT + license_family: MIT + run_exports: {} + size: 34027 + timestamp: 1791727231159 +- conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda + sha256: e14aafa63efa0528ca99ba568eaf506eb55a0371d12e6250aaaa61718d2eb62e + md5: d7585b6550ad04c8c5e21097ada2888e + depends: + - python >=3.9 + - python + license: MIT + license_family: MIT + run_exports: {} + size: 25877 + timestamp: 1764896838868 +- conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.6.2-pyha770c72_0.conda + sha256: fc0a0620a8f829c46787a9a13ddbae8b6049f2e77da353a8a2adaa01af0da7e2 + md5: c36b88db560b4e74f294380613f1a239 + depends: + - cfgv >=2.0.0 + - identify >=1.0.0 + - nodeenv >=0.11.1 + - python >=3.10 + - pyyaml >=5.1 + - virtualenv >=20.10.0 + license: MIT + license_family: MIT + run_exports: {} + size: 201879 + timestamp: 1786408353117 +- conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.4-pyh293190f_0.conda + sha256: f213f735f0c2b9bfcc1358c8bdeb0dfcf3614bf2c47aad68227dabea99b0e0d7 + md5: 2e57132f130bffcb5a8b17092b2871bc + depends: + - python >=3.8 + - pybind11-global ==3.0.4 *_0 + - python + constrains: + - pybind11-abi ==11 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pybind11?source=compressed-mapping + run_exports: {} + size: 251088 + timestamp: 1786199539429 +- conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-11-hc364b38_1.conda + sha256: 9e7fe12f727acd2787fb5816b2049cef4604b7a00ad3e408c5e709c298ce8bf1 + md5: f0599959a2447c1e544e216bddf393fa + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - pybind11-abi ==11 + size: 14671 + timestamp: 1752769938071 +- conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-global-3.0.4-pyh648e204_0.conda + sha256: 25e887672e68188a5fa899efdc116acb01d689989493342aac05781ec1f09b81 + md5: e594cb485091100204e0b77cb5709896 + depends: + - python >=3.8 + - __unix + - python + constrains: + - pybind11-abi ==11 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pybind11-global?source=compressed-mapping + run_exports: {} + size: 244363 + timestamp: 1786199539429 +- conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.11-pyh5ded981_0.conda + sha256: 99af7c6061d7aa73cfcef615500ddf3e9f4a58c844646b7d7ce58147fb5758d7 + md5: 3cd164745122ab557cc30879390089e8 + depends: + - python >=3.11 + - python + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 58624 + timestamp: 1791592610066 +- conda: https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.15.4-pyhd8ed1ab_0.conda + sha256: 5ec877142ded763061e114e787a4e201c2fb3f0b1db2f04ace610a1187bb34ae + md5: c7c50dd5192caa58a05e6a4248a27acb + depends: + - accessible-pygments + - babel + - beautifulsoup4 + - docutils !=0.17.0 + - packaging + - pygments >=2.7 + - python >=3.9 + - sphinx >=5.0 + - typing_extensions + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 1393462 + timestamp: 1719344980505 +- conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.21.0-pyhcf101f3_0.conda + sha256: f5f015ff1bc3e1b7fc08eee096b1865234189ae0b82bebdb86a5369116e42fa0 + md5: 2882dee445dfa45b0c5afce3ffb7730a + depends: + - python >=3.10 + - python + license: BSD-2-Clause + license_family: BSD + run_exports: {} + size: 959376 + timestamp: 1786995678795 +- conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + sha256: ba3b032fa52709ce0d9fd388f63d330a026754587a2f461117cac9ab73d8d0d8 + md5: 461219d1a5bd61342293efa2c0c90eac + depends: + - __unix + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 21085 + timestamp: 1733217331982 +- conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.1.1-pyhc364b38_2.conda + sha256: 430051d80765207a7d782b2b188230ba1489d35c6e75fd9903f76cb9fda4af16 + md5: 64c98a12c4e23eb238bf66bbecafdf3c + depends: + - colorama + - pygments >=2.7.2 + - python >=3.10 + - iniconfig >=1.0.1 + - packaging >=22 + - pluggy >=1.5,<2 + - tomli >=1 + - exceptiongroup >=1 + - python + constrains: + - pytest-faulthandler >=2 + license: MIT + license_family: MIT + run_exports: {} + size: 306724 + timestamp: 1782127176429 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-discovery-1.6.2-pyh5ded981_0.conda + sha256: 0d5fa564eae1e90cde8fbf71216f08f13e82791e02e38ade728ad2e8a01b88d8 + md5: 17e6c8b2fc2d021a217d317fe4f89fc4 + depends: + - python >=3.11 + - filelock >=3.16.1 + - python + license: MIT + license_family: MIT + run_exports: {} + size: 39339 + timestamp: 1791631961919 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.13.16-h4df99d1_101.conda + sha256: f8916e4d2625b894cb25dac41396ab315b6f8fefa45c89172d8a962c784c1598 + md5: 432007147854d0bcda8ba02040666756 + depends: + - cpython 3.13.16.* + - python_abi * *_cp313 + license: Python-2.0 + run_exports: {} + size: 49925 + timestamp: 1791332629849 +- conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-9_cp313.conda + build_number: 9 + sha256: 9dc0cb6a20ebc7e9bcd8c6868f2bec083c114b0ba20b7118405c036533d5c522 + md5: c5abc97af551bc12d71305852392f75c + constrains: + - python 3.13.* *_cp313 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: {} + size: 6769 + timestamp: 1788302828005 +- conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda + sha256: 1715246b19c9f85ee022933b4845f2fc14ac9184981b7b7d9b728bec8e9588da + md5: 4a85203c1d80c1059086ae860836ffb9 + depends: + - python >=3.10 + - certifi >=2023.5.7 + - charset-normalizer >=2,<4 + - idna >=2.5,<4 + - urllib3 >=1.26,<3 + - python + constrains: + - chardet >=3.0.2,<8 + license: Apache-2.0 + license_family: APACHE + run_exports: {} + size: 68709 + timestamp: 1778851103479 +- conda: https://conda.anaconda.org/conda-forge/noarch/roman-numerals-4.1.0-pyhd8ed1ab_0.conda + sha256: 30f3c04fcfb64c44d821d392a4a0b8915650dbd900c8befc20ade8fde8ec6aa2 + md5: 0dc48b4b570931adc8641e55c6c17fe4 + depends: + - python >=3.10 + license: 0BSD OR CC0-1.0 + run_exports: {} + size: 13814 + timestamp: 1766003022813 +- conda: https://conda.anaconda.org/conda-forge/noarch/roman-numerals-py-4.1.0-pyhd8ed1ab_0.conda + sha256: ce21b50a412b87b388db9e8dfbf8eb16fc436c23750b29bf612ee1a74dd0beb2 + md5: 28687768633154993d521aecfa4a56ac + depends: + - python >=3.10 + - roman-numerals 4.1.0 + license: 0BSD OR CC0-1.0 + run_exports: {} + size: 11074 + timestamp: 1766025162370 +- conda: https://conda.anaconda.org/conda-forge/noarch/scikit-build-core-1.1.1-pyhcb39dcd_0.conda + sha256: bb3edbe12bdaf6f3283d6567ae180225afb89e85d80f75cc77f5e44dc09d9b8c + md5: 43b4de2a8d4a9c164697d6a44ebfb94c + depends: + - python >=3.9 + - exceptiongroup >=1.0 + - packaging >=23.2 + - pathspec >=0.12.0 + - tomli >=1.2.2 + - typing_extensions >=4 + - python + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/scikit-build-core?source=compressed-mapping + run_exports: {} + size: 365250 + timestamp: 1790997173721 +- conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-84.0.0-pyh332efcf_0.conda + sha256: 9e200ee5f9ff19a4d94e4b51c4856d53dec849f91032f345cf0c6bc3d51a7183 + md5: 62ac906f1cd582c6c264c95625cb9d6f + depends: + - python >=3.10 + license: MIT + license_family: MIT + purls: + - pkg:pypi/setuptools?source=compressed-mapping + run_exports: {} + size: 524488 + timestamp: 1786282924579 +- conda: https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-3.1.1-pyhd8ed1ab_0.conda + sha256: ad89284ea94821c20ff87e64b948e4afc690cf5202d14c009355b0594cf23aea + md5: 46b6abe31482f6bca064b965696ae807 + depends: + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 74456 + timestamp: 1780468201547 +- conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-3.0.3-pyhd8ed1ab_0.conda + sha256: 858ae626017ccba80201e7431db6b517e53d6322b32f00fd21b64159592fa7cd + md5: bd2ea090567b9d0a53240bd1367c87ad + depends: + - python >=3.11 + license: MIT + run_exports: {} + size: 43039 + timestamp: 1791821664697 +- conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-8.2.3-pyhd8ed1ab_0.conda + sha256: 995f58c662db0197d681fa345522fd9e7ac5f05330d3dff095ab2f102e260ab0 + md5: f7af826063ed569bb13f7207d6f949b0 + depends: + - alabaster >=0.7.14 + - babel >=2.13 + - colorama >=0.4.6 + - docutils >=0.20,<0.22 + - imagesize >=1.3 + - jinja2 >=3.1 + - packaging >=23.0 + - pygments >=2.17 + - python >=3.11 + - requests >=2.30.0 + - roman-numerals-py >=1.0.0 + - snowballstemmer >=2.2 + - sphinxcontrib-applehelp >=1.0.7 + - sphinxcontrib-devhelp >=1.0.6 + - sphinxcontrib-htmlhelp >=2.0.6 + - sphinxcontrib-jsmath >=1.0.1 + - sphinxcontrib-qthelp >=1.0.6 + - sphinxcontrib-serializinghtml >=1.1.9 + license: BSD-2-Clause + license_family: BSD + run_exports: {} + size: 1424416 + timestamp: 1740956642838 +- conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-book-theme-1.1.4-pyh29332c3_0.conda + sha256: 78581f1ba538186fc4129191a8db4ee7798382b6b4a1a0c55dedb437da1a9fd8 + md5: f3d3f4e7e2c9198e88cd524633665081 + depends: + - pydata-sphinx-theme ==0.15.4 + - python >=3.9 + - sphinx >=6.1 + - python + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 255445 + timestamp: 1740145414720 +- conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-applehelp-2.0.0-pyhd8ed1ab_1.conda + sha256: d7433a344a9ad32a680b881c81b0034bc61618d12c39dd6e3309abeffa9577ba + md5: 16e3f039c0aa6446513e94ab18a8784b + depends: + - python >=3.9 + - sphinx >=5 + license: BSD-2-Clause + license_family: BSD + run_exports: {} + size: 29752 + timestamp: 1733754216334 +- conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-devhelp-2.0.0-pyhd8ed1ab_1.conda + sha256: 55d5076005d20b84b20bee7844e686b7e60eb9f683af04492e598a622b12d53d + md5: 910f28a05c178feba832f842155cbfff + depends: + - python >=3.9 + - sphinx >=5 + license: BSD-2-Clause + license_family: BSD + run_exports: {} + size: 24536 + timestamp: 1733754232002 +- conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.1.0-pyhd8ed1ab_1.conda + sha256: c1492c0262ccf16694bdcd3bb62aa4627878ea8782d5cd3876614ffeb62b3996 + md5: e9fb3fe8a5b758b4aff187d434f94f03 + depends: + - python >=3.9 + - sphinx >=5 + license: BSD-2-Clause + license_family: BSD + run_exports: {} + size: 32895 + timestamp: 1733754385092 +- conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-pyhd8ed1ab_1.conda + sha256: 578bef5ec630e5b2b8810d898bbbf79b9ae66d49b7938bcc3efc364e679f2a62 + md5: fa839b5ff59e192f411ccc7dae6588bb + depends: + - python >=3.9 + license: BSD-2-Clause + license_family: BSD + run_exports: {} + size: 10462 + timestamp: 1733753857224 +- conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-2.0.0-pyhd8ed1ab_1.conda + sha256: c664fefae4acdb5fae973bdde25836faf451f41d04342b64a358f9a7753c92ca + md5: 00534ebcc0375929b45c3039b5ba7636 + depends: + - python >=3.9 + - sphinx >=5 + license: BSD-2-Clause + license_family: BSD + run_exports: {} + size: 26959 + timestamp: 1733753505008 +- conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-2.0.0-pyhd8ed1ab_0.conda + sha256: 20b49741065fd7d3fabf98caf6d19b6436badb06b6d41f66b58f1fc2b52f37a1 + md5: f77df1fcf9af03b7287342638befca77 + depends: + - python >=3.10 + - sphinx >=5 + license: BSD-2-Clause + license_family: BSD + run_exports: {} + size: 30640 + timestamp: 1781260357443 +- conda: https://conda.anaconda.org/conda-forge/noarch/sympy-1.14.0-pyh2585a3b_106.conda + sha256: 1c8057e6875eba958aa8b3c1a072dc9a75d013f209c26fd8125a5ebd3abbec0c + md5: 32d866e43b25275f61566b9391ccb7b5 + depends: + - __unix + - cpython + - gmpy2 >=2.0.8 + - mpmath >=1.1.0,<1.5 + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/sympy?source=hash-mapping + run_exports: {} + size: 4661767 + timestamp: 1771952371059 +- conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.5.0-pyh5ded981_0.conda + sha256: 93b6f517fb7adff00b62be02b798d384a1b34d0e368fdb576fb248bcb3bb9ec7 + md5: 36d6a162326c52d6339c8b606f43932d + depends: + - python >=3.11 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/tomli?source=compressed-mapping + run_exports: {} + size: 21687 + timestamp: 1791394576814 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda + sha256: b141933ece3518f6d7b75dfb59451e2f26b405a44c18e2518a83e9a02e09315c + md5: c680b5747e8c4c8f23dca0bb7042a8fc + depends: + - typing_extensions ==4.16.0 pyhcf101f3_0 + license: PSF-2.0 + license_family: PSF + purls: [] + run_exports: {} + size: 94080 + timestamp: 1783002732887 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda + sha256: 2d888f90af0686044882c74193ec80a90ec1943145d94a7b1b048958acda1848 + md5: c70ad746c22219b9700931707482992c + depends: + - python >=3.10 + - python + license: PSF-2.0 + license_family: PSF + purls: + - pkg:pypi/typing-extensions?source=hash-mapping + run_exports: {} + size: 52631 + timestamp: 1783002732887 +- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + sha256: b928c30ddcb0e3f544c6eade8352737e6e610e263276b90232db6a578ef899d8 + md5: fcb489df604d100968b737f2cb6076c6 + license: LicenseRef-Public-Domain + purls: [] + run_exports: {} + size: 118849 + timestamp: 1784250406640 +- conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.8.0-pyhd8ed1ab_0.conda + sha256: c5511c190ab55168ca412f3c54592fbf677ae6250784cb72b227a7cf3f538f7d + md5: 407a3d3778570bae8e8fcf554e803e43 + depends: + - backports.zstd >=1.0.0 + - brotli-python >=1.2.0 + - h2 >=4,<5 + - pysocks >=1.5.6,<2.0,!=1.5.7 + - python >=3.11 + license: MIT + license_family: MIT + run_exports: {} + size: 107485 + timestamp: 1789566710244 +- conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-21.14.7-pyh5ded981_0.conda + sha256: 74caaf8d70f0858d99a1f480e5658839b09f86ee947d9907f676683f84062abe + md5: 1635534aa129eb9ab6ce5e01929d86ff + depends: + - python >=3.11 + - distlib >=0.4.3,<1 + - filelock >=3.24.2,<5 + - packaging >=26.3 + - platformdirs >=4.4,<5 + - python-discovery >=1.6.1 + - python + license: MIT + license_family: MIT + run_exports: {} + size: 3995083 + timestamp: 1791784412547 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-8_kmp_llvm.conda + build_number: 8 + sha256: c7c98ce74f6a7ff25aaa4706d06fa41d63a333add4d697b73aa4d8d2d7ad7651 + md5: a2d706b4a7d603524133037c34f7fb76 + depends: + - llvm-openmp >=9.0.1 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - _openmp_mutex >=4.5 + size: 8016 + timestamp: 1788046437162 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.8.0-py313h8c7ad59_0.conda + sha256: 527968bdd299cf54f49dd88f6543470b1502cbc53a09ff128b8f75867154f436 + md5: adec8816f4cda3cd23f02f8f49964461 + depends: + - python + - __osx >=11.0 + - zstd >=1.5.7,<1.6.0a0 + - python_abi 3.13.* *_cp313 + license: BSD-3-Clause AND MIT AND EPL-2.0 + run_exports: {} + size: 242793 + timestamp: 1791654845115 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py313h3c37e74_4.conda + sha256: 9f0d62630225a681b618685c29fd073436654717693344a3b2ce36d1cecebcb8 + md5: 1d46a945b575d98e60f2259e14140c3c + depends: + - __osx >=11.0 + - libcxx >=21 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + constrains: + - libbrotlicommon 1.2.0 h1dcdb26_4 + license: MIT + license_family: MIT + run_exports: {} + size: 365453 + timestamp: 1788480413282 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda + sha256: 8ec22f0ba25cbfc2e64d70cf29459eccd7ffdf6436f6a6ff15bbfef799f7d4f6 + md5: b50612e7d190b8061ab4e7dc119cf4d5 + depends: + - __osx >=11.0 + license: bzip2-1.0.6 + license_family: BSD + purls: [] + run_exports: + weak: + - bzip2 >=1.0.8,<2.0a0 + size: 124965 + timestamp: 1785906749812 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.1.1-py313h056b358_3.conda + sha256: be442fc43c5e77e4ff78c443c4026cf404a956fb90febfad89156d054eebd444 + md5: db79aff26a4e63a605a91b76d91573b7 + depends: + - __osx >=11.0 + - libffi >=3.7.0,<3.8.0a0 + - pycparser + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + license: MIT + license_family: MIT + run_exports: {} + size: 290137 + timestamp: 1788485303346 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fmt-12.1.0-h99e4e11_1.conda + sha256: ca42427b99ff92228e907bed5f10a1df868180997571d29c230faad2c38e35fd + md5: 891b1202d7b835f215e26a4db685ec7c + depends: + - __osx >=11.0 + - libcxx >=19 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - fmt >=12.1.0,<12.2.0a0 + size: 181008 + timestamp: 1785915450316 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gmp-6.3.0-hf5e55b1_3.conda + sha256: 980cb1bbc3656d6f923d44297a2c4ab75f20482b8930c70537fd7f1ef2378894 + md5: bbfa5bd261b68536ddcda39e03d3407f + depends: + - libcxx >=19 + - __osx >=11.0 + license: GPL-2.0-or-later OR LGPL-3.0-or-later + purls: [] + run_exports: + weak: + - gmp >=6.3.0,<7.0a0 + size: 399307 + timestamp: 1786629210135 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gmpy2-2.3.2-py313h0091f1d_0.conda + sha256: a979a7088dfd128f9e6418767ab1d1a34910bb0824219a5895150af586a4da0b + md5: fd6b154752e536062cc670f445847974 + depends: + - python + - __osx >=11.0 + - mpc >=1.4.0,<2.0a0 + - python_abi 3.13.* *_cp313 + - mpfr >=4.2.2,<5.0a0 + - gmp >=6.3.0,<7.0a0 + license: LGPL-3.0-or-later + license_family: LGPL + purls: + - pkg:pypi/gmpy2?source=compressed-mapping + run_exports: {} + size: 253145 + timestamp: 1791167016923 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260526.0-cxx17_h4c27e2a_3.conda + sha256: 27859d85276442961b01bcfc488dc85426109330ffb157d3224772a7eaa72a75 + md5: 009ec10961e8426182e9d4c33e31db55 + depends: + - __osx >=11.0 + - libcxx >=19 + constrains: + - abseil-cpp =20260526.0 + - libabseil-static =20260526.0=cxx17* + license: Apache-2.0 + license_family: Apache + purls: [] + run_exports: + weak: + - libabseil >=20260526.0,<20260527.0a0 + - libabseil =*=cxx17* + size: 1277648 + timestamp: 1791681045738 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-11_h51639a9_openblas.conda + build_number: 11 + sha256: 718c3e1a16ad946caeeba041520dc18b7af461226bd8c28faef78b181920dbb4 + md5: 364639b771dbb4ede335d965f55a23ac + depends: + - libopenblas >=0.3.34,<0.3.35.0a0 + - libopenblas >=0.3.34,<1.0a0 + constrains: + - blas 2.311 openblas + - libcblas 3.11.0 11*_openblas + - liblapack 3.11.0 11*_openblas + - liblapacke 3.11.0 11*_openblas + - mkl <2027 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - libblas >=3.11.0,<4.0a0 + size: 18300 + timestamp: 1789061064192 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-11_hb0561ab_openblas.conda + build_number: 11 + sha256: 3cebd672d8bd7c776a381422ea3fc48131343aeda2879974734715ac6a05c292 + md5: 73bde7d713e790b3168707e02102e87b + depends: + - libblas 3.11.0 11_h51639a9_openblas + constrains: + - blas 2.311 openblas + - liblapack 3.11.0 11*_openblas + - liblapacke 3.11.0 11*_openblas + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - libcblas >=3.11.0,<4.0a0 + size: 18324 + timestamp: 1789061068736 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.3-h55c6f16_0.conda + sha256: 2d9d31172d7919bd276a8d1be338de567dd5afdd181e8d3d3d8c2d8695e09de2 + md5: 424521e52e65ef7f6cf957c4966e6386 + depends: + - __osx >=11.0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + run_exports: {} + size: 577501 + timestamp: 1791337239735 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.5-h6111c0a_0.conda + sha256: 50aa761b83fc7a116ca88fe0cb4cefce5abababb10a926bf8afbafd5c298d102 + md5: 5383c550b785bdaafcc4eaf638071a7c + depends: + - __osx >=11.0 + constrains: + - expat 2.8.5.* + license: MIT + license_family: MIT + purls: [] + run_exports: {} + size: 69565 + timestamp: 1790370026495 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-h47dc5ef_1.conda + sha256: 2783389a7b9dda04c62cc54c6bbeb03dd3cef24b3fc642715d02a3fa19464a9a + md5: 216bbcc23c11e9695b3db4a8771d76eb + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - libffi >=3.7.0,<3.8.0a0 + size: 43637 + timestamp: 1787753403688 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.2.0-h3cf6597_7.conda + sha256: deacf62bd6cbbe05eb085dd69a2ca120053597c37066d53c8f7cf6815f26e9eb + md5: f100c8baeb0c816052b0d43948555bf6 + depends: + - _openmp_mutex + constrains: + - libgcc-ng ==16.2.0=*_7 + - libgomp 16.2.0 7 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + run_exports: {} + size: 365973 + timestamp: 1790301995634 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.2.0-h07b0088_7.conda + sha256: a5b4b63d0ef4c7c59c37225b9f0307b5cd2a3a8499b2a40b6ddd2b9f072712a8 + md5: 2efa9518e126c501a7c7d638b2fa3a21 + depends: + - libgfortran5 16.2.0 hdb7a957_7 + constrains: + - libgfortran-ng ==16.2.0=*_7 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + run_exports: {} + size: 99745 + timestamp: 1790302083840 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.2.0-hdb7a957_7.conda + sha256: 19297e5cf39de976cdd1a46499a4e15fdf54d249d3231f3b2a9d43af3be0bc66 + md5: c9d80dd8775b8fe76e825d63d8ccaf1b + depends: + - libgcc >=16.2.0 + constrains: + - libgfortran 16.2.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + run_exports: {} + size: 554793 + timestamp: 1790302001166 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-11_hd9741b5_openblas.conda + build_number: 11 + sha256: e7697df761eeb243272801a8c883dc1de6018a7f738746b443ef1396f0a78075 + md5: d67ef1462e77d20cd3e0d27c876c3c9a + depends: + - libblas 3.11.0 11_h51639a9_openblas + constrains: + - blas 2.311 openblas + - libcblas 3.11.0 11*_openblas + - liblapacke 3.11.0 11*_openblas + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - liblapack >=3.11.0,<3.12.0a0 + size: 18284 + timestamp: 1789061072707 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_1.conda + sha256: 23d0630046a3e8b164d8f80f2b74ed2605af2e7050ab9913018056402fae4311 + md5: 8ab10323068b107661a4b9a4af84f3b5 + depends: + - __osx >=11.0 + constrains: + - xz 5.8.3.* + license: 0BSD + purls: [] + run_exports: + weak: + - liblzma >=5.8.3,<6.0a0 + size: 91720 + timestamp: 1786348695846 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_2.conda + sha256: 04cc136c5a956a73aa14e0a160b5822b0b29714e67b299fa1b1fd16dbcba5366 + md5: ff33a4dbd93abc8a798cc4e0e7c8136d + depends: + - __osx >=11.0 + license: BSD-2-Clause + license_family: BSD + purls: [] + run_exports: {} + size: 73289 + timestamp: 1786651074391 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.34-openmp_h5e6e99c_1.conda + sha256: 18f167fdaa26f67a123c59766f4730b41866d2c0920b5c29d74a93d3a2e92a96 + md5: 9c874eb9d4441a658ef3acf6ac56dc3e + depends: + - __osx >=11.0 + - libgcc >=15 + - libgfortran + - libgfortran5 >=15.3.0 + - llvm-openmp >=21.1.8 + constrains: + - openblas >=0.3.34,<0.3.35.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - libopenblas >=0.3.34,<1.0a0 + size: 4249236 + timestamp: 1789023664380 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-7.35.1-h391e224_3.conda + sha256: 4129c13739e30dfbab8c20594acd8c182de2008f4dfc94d54f52b556eb39050c + md5: 7666c238f885d0c7927607ebfc163f97 + depends: + - __osx >=12.0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libcxx >=21 + - libzlib >=1.3.2,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - libprotobuf >=7.35.1,<7.35.2.0a0 + size: 2877232 + timestamp: 1787657252942 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.13.16-h0af5753_102_cp313.conda + build_number: 102 + sha256: 245a479d398db406d8391081e7da897e4f161718b2c7cc81cd9eb177ff6ccd98 + md5: cd21726321310e0ed0261edf5c1129ee + depends: + - __osx >=11.0 + license: Python-2.0 + purls: [] + run_exports: + weak: + - libpython >=3.13,<4.0a0 + size: 1814014 + timestamp: 1791910695132 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.13.16-hb350d79_101_cp313.conda + build_number: 101 + sha256: dbae2e688e271f140bf5678257c7f671f06b76045b90418f086427e4dc7c2eca + md5: bd71eafbdb134f540ae4aacd52ecf977 + depends: + - __osx >=11.0 + - libcxx >=21 + license: Python-2.0 + purls: [] + run_exports: + weak: + - libpython >=3.13,<4.0a0 + size: 1816892 + timestamp: 1791332817737 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.54.0-h34a968b_101.conda + sha256: 7f0f2e4ca2eb6e5059d9593ffe88088debe453b146e2794641d94242b901d85c + md5: 42b7a7607b4c9838cc2e72a232af6809 + depends: + - __osx >=11.0 + - libzlib >=1.3.2,<2.0a0 + constrains: + - icu <0.0a0 + license: blessing + purls: [] + run_exports: + weak: + - libsqlite >=3.54.0,<4.0a0 + size: 949146 + timestamp: 1791584347793 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtorch-2.14.1-cpu_generic_h8539c7d_0.conda + sha256: 611136e49805a77e6c37888a9f9615b9649bff7344adfc4b25df84628b128e4f + md5: 5e8ed7a3ce0988cb2730c298c8f16435 + depends: + - __osx >=12.0 + - fmt >=12.1.0,<12.2.0a0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libcxx >=21 + - liblapack >=3.9.0,<4.0a0 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libuv >=1.52.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - llvm-openmp >=21.1.8 + - onednn >=3.13.4,<4.0a0 + - pybind11-abi 11 + - sleef >=3.9.0,<4.0a0 + constrains: + - libopenblas * openmp_* + - openblas * openmp_* + - pytorch 2.14.1 cpu_generic_*_0 + - pytorch-cpu 2.14.1 + - pytorch-gpu <0.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - libtorch >=2.14.1,<2.15.0a0 + size: 35580021 + timestamp: 1791586159933 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.52.1-h1a92334_1.conda + sha256: 4f47de9de1990efd998edbbd6793f89c8f02ffde987ae8120b1c006acefd2a04 + md5: de09bd0f175611e94f21b28f8c708e80 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - libuv >=1.52.1,<2.0a0 + size: 122729 + timestamp: 1785914645797 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda + sha256: a18fa5d5bac452401459f966cf0d872224e8080c4ff93c77e168d43ab42ef9d7 + md5: f39288f0ea63ae962e1a2e4f355a0d75 + depends: + - __osx >=11.0 + constrains: + - zlib 1.3.2 *_3 + license: Zlib + license_family: Other + purls: [] + run_exports: + weak: + - libzlib >=1.3.2,<2.0a0 + size: 47822 + timestamp: 1785277049190 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-23.1.3-hdb3d66b_1.conda + sha256: eec66f3d0a0c428411e22a59c4726069ccef81a46864876b6e270c8c254d4045 + md5: 5b7efd98534e6722de8d223b8654185b + depends: + - __osx >=11.0 + constrains: + - intel-openmp <0.0a0 + - openmp 23.1.3|23.1.3.* + license: Apache-2.0 WITH LLVM-exception + purls: [] + run_exports: + strong: + - llvm-openmp >=23.1.3 + size: 287944 + timestamp: 1791950643503 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.4-py313h996ddf1_1.conda + sha256: 9d47cb555572c552de411bb6d140a19cf6fee173dbc712aef4d3a1e36e028363 + md5: cf0b179746e4ff1ddf1440355d5f3914 + depends: + - python + - __osx >=11.0 + - python_abi 3.13.* *_cp313 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/markupsafe?source=hash-mapping + run_exports: {} + size: 28234 + timestamp: 1791548956889 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/mpc-1.4.0-h071a35a_1.conda + sha256: 6238c857d6b6e575bcf26c79367f2faa22573a79ca67ee7f69694634fd865f5e + md5: 9d932443c0f21214dfa9821dc18881e1 + depends: + - __osx >=11.0 + - gmp >=6.3.0,<7.0a0 + - mpfr >=4.2.2,<5.0a0 + license: LGPL-3.0-or-later + license_family: LGPL + purls: [] + run_exports: + weak: + - mpc >=1.4.0,<2.0a0 + size: 85436 + timestamp: 1787668129803 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/mpfr-4.2.2-h72846b0_1.conda + sha256: 6a86fe9c09d708d71508c601dec53fb7a4fbfb23e6f57a590ef832afdca738c4 + md5: 6c990c07502330c3876dfc67d7bb6917 + depends: + - __osx >=11.0 + - gmp >=6.3.0,<7.0a0 + license: LGPL-3.0-only + license_family: LGPL + purls: [] + run_exports: + weak: + - mpfr >=4.2.2,<5.0a0 + size: 350536 + timestamp: 1787236978744 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda + sha256: 7024a48c8c0d0114ed4ab53c76bf9275d50e91ba7cea367a9aead638d3c29c68 + md5: 3dfa0d0316dc246cd44937a557de4501 + depends: + - __osx >=11.0 + license: X11 AND BSD-3-Clause + purls: [] + run_exports: + weak: + - ncurses >=6.6,<7.0a0 + size: 804298 + timestamp: 1786355189145 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.4-py313h24c19cf_0.conda + sha256: f971a672568cf797cdca18f5644b4c08e88a7b5acf84bebb5b929c93e43971d6 + md5: 96efd3f5436f47a2ed3d1ade1b05c323 + depends: + - python + - __osx >=11.0 + - libcxx >=21 + - libcblas >=3.9.0,<4.0a0 + - liblapack >=3.9.0,<4.0a0 + - python_abi 3.13.* *_cp313 + - libblas >=3.9.0,<4.0a0 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/numpy?source=compressed-mapping + run_exports: + weak: + - numpy >=1.25,<3 + size: 7216969 + timestamp: 1791675324621 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/onednn-3.13.4-omp_h3fb3e40_0.conda + sha256: bf04fb73059bdf3189abeac938d224eace2a469a78dfe904a1c311b01ef49130 + md5: ca729a6795c531eb691801bc97e49933 + depends: + - llvm-openmp >=21.1.8 + - libcxx >=21 + - __osx >=11.0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - onednn >=3.13.4,<4.0a0 + size: 5498840 + timestamp: 1791401174841 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.5-h55eecbc_0.conda + sha256: 44fbc526456571a9fb8fa18157f6f269d4d4bd40eef20183a93b9c9296f94497 + md5: 45d9179cdf6e53d88aec034246f0dad6 + depends: + - __osx >=11.0 + - ca-certificates + license: Apache-2.0 + license_family: Apache + purls: [] + run_exports: + weak: + - openssl >=3.6.5,<4.0a0 + size: 3111573 + timestamp: 1791097959641 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/optree-0.20.0-py313h5c89e09_1.conda + sha256: ab4022ab54ae3f36b182a46d24394f47ed449bc44e5f586639904e897d1e511e + md5: 2cdcb4aa251566f505cdb31e1007a977 + depends: + - __osx >=11.0 + - libcxx >=21 + - python >=3.13,<3.14.0a0 + - python >=3.13,<3.14.0a0 *_cp313 + - python_abi 3.13.* *_cp313 + - typing-extensions >=4.12 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/optree?source=hash-mapping + run_exports: {} + size: 507081 + timestamp: 1788505310500 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.16-hb59dee6_101_cp313.conda + build_number: 101 + sha256: 456913ec9e3cb021b52c7055addd5ce7f7768905e9080de97ccf4aa9a6fa34d5 + md5: 77177bbe88744e60747483716fa30f90 + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.8.5,<3.0a0 + - libffi >=3.7.0,<3.8.0a0 + - liblzma >=5.8.3,<6.0a0 + - libmpdec >=4.0.0,<5.0a0 + - libpython 3.13.16 hb350d79_101_cp313 + - libsqlite >=3.53.4,<4.0a0 + - libzlib >=1.3.2,<2.0a0 + - ncurses >=6.6,<7.0a0 + - openssl >=3.5.9,<4.0a0 + - python_abi 3.13.* *_cp313 + - readline >=8.3,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + license: Python-2.0 + purls: [] + run_exports: + weak: + - python_abi 3.13.* *_cp313 + noarch: + - python + size: 11892664 + timestamp: 1791332850865 + python_site_packages_path: lib/python3.13/site-packages +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.16-hb59dee6_102_cp313.conda + build_number: 102 + sha256: 613c550dca35cfd16ff0f38c6e66ff72ab146df0aae83aa2be81c9b091b044ac + md5: 00ac7aa9499b4b74aad9f8e92d44dd0a + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.8.5,<3.0a0 + - libffi >=3.7.0,<3.8.0a0 + - liblzma >=5.8.3,<6.0a0 + - libmpdec >=4.0.0,<5.0a0 + - libpython 3.13.16 h0af5753_102_cp313 + - libsqlite >=3.54.0,<4.0a0 + - libzlib >=1.3.2,<2.0a0 + - ncurses >=6.6,<7.0a0 + - openssl >=3.5.9,<4.0a0 + - python_abi 3.13.* *_cp313 + - readline >=8.3,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + license: Python-2.0 + purls: [] + run_exports: + weak: + - python_abi 3.13.* *_cp313 + noarch: + - python + size: 11933817 + timestamp: 1791910728707 + python_site_packages_path: lib/python3.13/site-packages +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pytorch-2.14.1-cpu_generic_py313_hf1850e0_0.conda + sha256: 0ae47b5ea2a73cd5f32cdb7ff068fa6a1be1ad45fc9cb9623c1fd1ce507793df + md5: e4e25350ef3367708d502085bf19c7ae + depends: + - __osx >=12.0 + - filelock + - fmt >=12.1.0,<12.2.0a0 + - fsspec >=0.8.5 + - jinja2 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libcblas >=3.9.0,<4.0a0 + - libcxx >=21 + - liblapack >=3.9.0,<4.0a0 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libtorch 2.14.1 cpu_generic_h8539c7d_0 + - libuv >=1.52.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - llvm-openmp >=21.1.8 + - networkx >=2.5.1 + - nomkl + - numpy >=1.25,<3 + - onednn >=3.13.4,<4.0a0 + - optree >=0.13.0 + - pybind11 + - pybind11-abi 11 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + - scikit-build-core + - setuptools >=77.0.3 + - sleef >=3.9.0,<4.0a0 + - sympy >=1.13.3 + - typing_extensions >=4.13.0 + constrains: + - pytorch-cpu 2.14.1 + - pytorch-gpu <0.0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/torch?source=hash-mapping + run_exports: + weak: + - pytorch >=2.14.1,<2.15.0a0 + - libtorch >=2.14.1,<2.15.0a0 + size: 27663073 + timestamp: 1791587577341 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pytorch-cpu-2.14.1-cpu_generic_hcc7c195_0.conda + sha256: eb7529735a1e681efe37255fc6031743233b0351c3de7f26d809e3f1385e60e3 + md5: ba2b1a8ecf18af2746731ca973f97194 + depends: + - pytorch 2.14.1 cpu_generic*0 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: {} + size: 56363 + timestamp: 1791587630647 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py313h65a2061_1.conda + sha256: 950725516f67c9691d81bb8dde8419581c5332c5da3da10c9ba8cbb1698b825d + md5: 5d0c8b92128c93027632ca8f8dc1190f + depends: + - __osx >=11.0 + - python >=3.13,<3.14.0a0 + - python >=3.13,<3.14.0a0 *_cp313 + - python_abi 3.13.* *_cp313 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + run_exports: {} + size: 188763 + timestamp: 1770224094408 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h8b90a29_1.conda + sha256: d6782b430e6dce4fe8c7ac118cd988d5a193eb4a7f60741edfaede58016b6110 + md5: 9347fd56a002e6b58946831d48fe62f6 + depends: + - __osx >=11.0 + - ncurses >=6.6,<7.0a0 + license: GPL-3.0-only + license_family: GPL + purls: [] + run_exports: + weak: + - readline >=8.3,<9.0a0 + size: 314395 + timestamp: 1787033878899 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.14.14-h279115b_1.conda + noarch: python + sha256: 20f93b70375e6ad43ec507611cf28814277be17a2794a2a94e2df13a0b34f8d3 + md5: bcc5ef166c4de9a225c9ca9cb4fa631e + depends: + - python + - __osx >=11.0 + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + run_exports: {} + size: 8337249 + timestamp: 1769521105071 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/sleef-3.9.0-hb028509_0.conda + sha256: 799d0578369e67b6d0d6ecdacada411c259629fc4a500b99703c5e85d0a68686 + md5: 68f833178f171cfffdd18854c0e9b7f9 + depends: + - __osx >=11.0 + - libcxx >=19 + - llvm-openmp >=19.1.7 + license: BSL-1.0 + purls: [] + run_exports: + weak: + - sleef >=3.9.0,<4.0a0 + size: 587027 + timestamp: 1756274982526 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hbeba79b_4.conda + sha256: 857d89087ae7f2c328cf256728affcb7343031a148b4af847334d248a9cf564c + md5: 90a01bf394d1c594e0eb9258f967d828 + depends: + - __osx >=11.0 + - libzlib >=1.3.2,<2.0a0 + license: TCL + purls: [] + run_exports: + weak: + - tk >=8.6.13,<8.7.0a0 + size: 3342183 + timestamp: 1787272852357 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ukkonen-1.1.0-py313h5c29297_0.conda + sha256: d28d0242d3fa23784630c775d5b628ce25e2d45f5d3f1cfcdc3815bc954073fa + md5: 43b1eb729bd1cd9ea595548eb8100b65 + depends: + - __osx >=11.0 + - cffi + - libcxx >=19 + - python >=3.13,<3.14.0a0 + - python >=3.13,<3.14.0a0 *_cp313 + - python_abi 3.13.* *_cp313 + license: MIT + license_family: MIT + run_exports: {} + size: 14773 + timestamp: 1769439197815 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h74c22ad_3.conda + sha256: 3e78c43207502418fc5fb2317bbbefe5df8b6fc1051d90bdcd1c881c76bb4193 + md5: 31cf6dcc138abe673c9440cd6fe6c6fe + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + run_exports: + weak: + - yaml >=0.2.5,<0.3.0a0 + size: 77530 + timestamp: 1787228556857 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hf451053_7.conda + sha256: da867f5092eb0cb746d353694f0098031fd9817a4ce7d5743121209ae0f406ca + md5: 4ec2684c73812cc2c3d78379384a39cc + depends: + - __osx >=11.0 + - libzlib >=1.3.2,<2.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - zstd >=1.5.7,<1.6.0a0 + size: 433687 + timestamp: 1786599629846 +- pypi: git+https://github.com/sequince-dev/sequince-flows.git?rev=cbe32939713f17be2c5deb8f813ba1ec4550ea7e#cbe32939713f17be2c5deb8f813ba1ec4550ea7e + name: sequince-density + version: 0.1.dev30+gcbe329397 + requires_dist: + - array-api-compat>=1.5 + - numpy>=1.25.2 + - orng>=0.2.0a2 + - sequince + - scikit-learn>=1.4 ; extra == 'gmm' + - torch>=2 ; extra == 'zuko' + - zuko>=1.6 ; extra == 'zuko' + - jax>0.5.3 ; extra == 'flowjax' + - flowjax ; extra == 'flowjax' + - equinox ; extra == 'flowjax' + - h5py>=3 ; extra == 'flowjax' + requires_python: '>=3.11' +- pypi: git+https://github.com/sequince-dev/sequince.git?branch=main#9968074e158e13760a9af5d5a1be0c28e554d036 + name: sequince + version: 0.1.dev170+g9968074e1 + requires_dist: + - acai-control>=0.1.0 + - scipy>=1.16 + - matplotlib>=3.5 + - numpy>=1.25.2 + - minipcn>=0.3.0a1 + - array-api-compat>=1.13 + - array-api-extra>=0.10 + - optree>=0.19 + - orng>=0.2.1 + - jax>0.5.3 ; extra == 'jax' + - jaxlib ; extra == 'jax' + - blackjax>=1.2 ; extra == 'blackjax' + - emcee>=3.0 ; extra == 'emcee' + - eryn>=1.2.6 ; extra == 'eryn' + - numpy<2.4 ; extra == 'eryn' + - corner ; extra == 'corner' + - h5py>=3 ; extra == 'hdf5' + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/07/ef/9b77fc80fc9bf28978d5649a3c71c0e77efe9e1966e5f608e7c3d5ba86b4/acai_control-0.1.0-py3-none-any.whl + name: acai-control + version: 0.1.0 + sha256: b61bc1aa7f50944175ea563c178ef52b066f4c3e8306c037d7120fdf6df6421a + requires_dist: + - array-api-compat>=1.9 + - optree>=0.14 + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/10/76/8803c13605b763d33d156c4678fc77f8443389c0c51c8aef707bb02015f4/pillow-12.3.0-cp313-cp313-macosx_11_0_arm64.whl + name: pillow + version: 12.3.0 + sha256: d69141514cc30b774ceea5e3ed3a6635c8d8a96edf664689b890f4089111fb35 + requires_dist: + - furo ; extra == 'docs' + - olefile ; extra == 'docs' + - sphinx>=8.2 ; extra == 'docs' + - sphinx-autobuild ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - sphinx-inline-tabs ; extra == 'docs' + - sphinxext-opengraph ; extra == 'docs' + - olefile ; extra == 'fpx' + - olefile ; extra == 'mic' + - arro3-compute ; extra == 'test-arrow' + - arro3-core ; extra == 'test-arrow' + - nanoarrow ; extra == 'test-arrow' + - pyarrow ; extra == 'test-arrow' + - coverage>=7.4.2 ; extra == 'tests' + - defusedxml ; extra == 'tests' + - markdown2 ; extra == 'tests' + - olefile ; extra == 'tests' + - packaging ; extra == 'tests' + - pytest ; extra == 'tests' + - pytest-cov ; extra == 'tests' + - pytest-timeout ; extra == 'tests' + - pytest-xdist ; extra == 'tests' + - setuptools ; extra == 'tests' + - trove-classifiers>=2024.10.12 ; extra == 'tests' + - defusedxml ; extra == 'xmp' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/1e/cf/83c3377e7018ee3c455153d9560c6a972b0a407075868f5e58979fc640a2/minipcn-0.3.0a1-py3-none-any.whl + name: minipcn + version: 0.3.0a1 + sha256: 4bc483602761d4ff95b3e1de32a8626d2d5f7ec034bc8a7be9290b76f9a90c3b + requires_dist: + - matplotlib + - numpy + - scipy + - tqdm + - array-api-compat + - orng>=0.1.0 + - acai-control>=0.1.0 ; extra == 'scan' + - jax>0.4.35 ; extra == 'jax' + - jaxlib ; extra == 'jax' + - torch ; extra == 'torch' + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/23/cd/066e86230ae37ed0be70aae89aabf03ca8d9f39c8aea0dec8029455b5540/opt_einsum-3.4.0-py3-none-any.whl + name: opt-einsum + version: 3.4.0 + sha256: 69bb92469f86a1565195ece4ac0323943e83477171b91d24c35afe028a90d7cd + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/24/ec/014b428c3a05837643768d08aeb8fb1289d119d3f9f6fba479dffae05dbc/jaxlib-0.11.2-cp313-cp313-manylinux_2_27_x86_64.whl + name: jaxlib + version: 0.11.2 + sha256: 5f3cac8d7030c1f80a182025d7171ea3b1245162a710f6e58d25ee3fe1748aac + requires_dist: + - scipy>=1.14 + - numpy>=2.0 + - ml-dtypes>=0.5.0 + requires_python: '>=3.12' +- pypi: https://files.pythonhosted.org/packages/2a/f5/769f36d14922b8071a43e95d24d18b6bdafad10d7f5cf647867e1ac052bc/scipy-1.18.1-cp313-cp313-macosx_12_0_arm64.whl + name: scipy + version: 1.18.1 + sha256: e6fb6a55cc0ba97b59a1f288fb86dc6fce8bdfc0fffcbfd015e3a954bf2a2d93 + requires_dist: + - numpy>=2.0.0,<2.8 + - pytest>=8.0.0 ; extra == 'test' + - pytest-cov ; extra == 'test' + - pytest-timeout ; extra == 'test' + - pytest-xdist ; extra == 'test' + - asv ; extra == 'test' + - mpmath ; extra == 'test' + - gmpy2 ; extra == 'test' + - threadpoolctl ; extra == 'test' + - scikit-umfpack ; extra == 'test' + - pooch ; extra == 'test' + - hypothesis>=6.30 ; extra == 'test' + - array-api-strict>=2.3.1 ; extra == 'test' + - cython ; extra == 'test' + - meson ; extra == 'test' + - ninja ; sys_platform != 'emscripten' and extra == 'test' + - scipy-doctest>=2.0.0 ; extra == 'test' + - sphinx>=5.0.0,<8.2.0 ; extra == 'doc' + - intersphinx-registry ; extra == 'doc' + - pydata-sphinx-theme>=0.15.2 ; extra == 'doc' + - sphinx-copybutton ; extra == 'doc' + - sphinx-design>=0.4.0 ; extra == 'doc' + - matplotlib>=3.5 ; extra == 'doc' + - numpydoc ; extra == 'doc' + - jupytext ; extra == 'doc' + - myst-nb>=1.2.0 ; extra == 'doc' + - pooch ; extra == 'doc' + - jupyterlite-sphinx>=0.19.1 ; extra == 'doc' + - jupyterlite-pyodide-kernel ; extra == 'doc' + - linkify-it-py ; extra == 'doc' + - tabulate ; extra == 'doc' + - click<8.3.0 ; extra == 'dev' + - spin ; extra == 'dev' + - mypy==1.19.1 ; extra == 'dev' + - pyrefly==0.63.0 ; extra == 'dev' + - typing-extensions ; extra == 'dev' + - types-psutil ; extra == 'dev' + - pycodestyle ; extra == 'dev' + - ruff>=0.12.0 ; extra == 'dev' + - cython-lint>=0.12.2 ; extra == 'dev' + requires_python: '>=3.12' +- pypi: https://files.pythonhosted.org/packages/38/bb/d215ee7c73b61497b28a5503f9f53523f294fcc936762b7caf90e0c1c2b5/pyparsing-3.3.3-py3-none-any.whl + name: pyparsing + version: 3.3.3 + sha256: ece8c00a69cf01b45d0b1dedabb469c90d8caf996d4fda40f147627a122849a4 + requires_dist: + - railroad-diagrams ; extra == 'diagrams' + - jinja2 ; extra == 'diagrams' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/3f/2f/103cdc5b7077cd569989d3ddb99ee3d4e96ee31bde8df2a6e426b2dc6195/contourpy-1.4.0-cp313-cp313-macosx_11_0_arm64.whl + name: contourpy + version: 1.4.0 + sha256: 196759a3e4db60e1546167e361090ebb6d199aa3aafc9e34ea17a5ac3b23e814 + requires_dist: + - numpy>=2.0 + - myst-parser ; extra == 'docs' + - sphinx ; extra == 'docs' + - sphinx-book-theme ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - bokeh ; extra == 'bokeh' + - selenium ; extra == 'bokeh' + - contourpy[bokeh,docs] ; extra == 'mypy' + - bokeh ; extra == 'mypy' + - docutils-stubs ; extra == 'mypy' + - mypy==2.1.0 ; extra == 'mypy' + - types-pillow ; extra == 'mypy' + - contourpy[test-no-images] ; extra == 'test' + - matplotlib ; extra == 'test' + - pre-commit ; extra == 'test' + - pillow ; extra == 'test' + - pytest ; extra == 'test-no-images' + - pytest-cov ; extra == 'test-no-images' + - pytest-rerunfailures<16 ; extra == 'test-no-images' + - pytest-xdist ; extra == 'test-no-images' + - wurlitzer ; extra == 'test-no-images' + requires_python: '>=3.12' +- pypi: https://files.pythonhosted.org/packages/41/48/6450ed9243315322bbc19ac57b9b70d66a20bf1d38d124c96bc4bf6af9ea/scipy-1.18.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + name: scipy + version: 1.18.1 + sha256: fdaf5ea890a6183d0565f51a61799d67081bd5b1cf03c5f4b3fd3732108625c9 + requires_dist: + - numpy>=2.0.0,<2.8 + - pytest>=8.0.0 ; extra == 'test' + - pytest-cov ; extra == 'test' + - pytest-timeout ; extra == 'test' + - pytest-xdist ; extra == 'test' + - asv ; extra == 'test' + - mpmath ; extra == 'test' + - gmpy2 ; extra == 'test' + - threadpoolctl ; extra == 'test' + - scikit-umfpack ; extra == 'test' + - pooch ; extra == 'test' + - hypothesis>=6.30 ; extra == 'test' + - array-api-strict>=2.3.1 ; extra == 'test' + - cython ; extra == 'test' + - meson ; extra == 'test' + - ninja ; sys_platform != 'emscripten' and extra == 'test' + - scipy-doctest>=2.0.0 ; extra == 'test' + - sphinx>=5.0.0,<8.2.0 ; extra == 'doc' + - intersphinx-registry ; extra == 'doc' + - pydata-sphinx-theme>=0.15.2 ; extra == 'doc' + - sphinx-copybutton ; extra == 'doc' + - sphinx-design>=0.4.0 ; extra == 'doc' + - matplotlib>=3.5 ; extra == 'doc' + - numpydoc ; extra == 'doc' + - jupytext ; extra == 'doc' + - myst-nb>=1.2.0 ; extra == 'doc' + - pooch ; extra == 'doc' + - jupyterlite-sphinx>=0.19.1 ; extra == 'doc' + - jupyterlite-pyodide-kernel ; extra == 'doc' + - linkify-it-py ; extra == 'doc' + - tabulate ; extra == 'doc' + - click<8.3.0 ; extra == 'dev' + - spin ; extra == 'dev' + - mypy==1.19.1 ; extra == 'dev' + - pyrefly==0.63.0 ; extra == 'dev' + - typing-extensions ; extra == 'dev' + - types-psutil ; extra == 'dev' + - pycodestyle ; extra == 'dev' + - ruff>=0.12.0 ; extra == 'dev' + - cython-lint>=0.12.2 ; extra == 'dev' + requires_python: '>=3.12' +- pypi: https://files.pythonhosted.org/packages/47/7f/f0d33a414e2d84fca39438a30f0588c32f7365746e903293cadad9d52b0d/jaxlib-0.11.2-cp313-cp313-macosx_11_0_arm64.whl + name: jaxlib + version: 0.11.2 + sha256: 766bd90e27a0ff53b87bb9d70c73565ec80b9b1165320ccb8f72de46b85510df + requires_dist: + - scipy>=1.14 + - numpy>=2.0 + - ml-dtypes>=0.5.0 + requires_python: '>=3.12' +- pypi: https://files.pythonhosted.org/packages/47/8b/40d33ffd2f378094ed462e9a9a0907e59d4de9845e65a59561272da350d4/kiwisolver-1.5.1-cp313-cp313-macosx_11_0_arm64.whl + name: kiwisolver + version: 1.5.1 + sha256: 77a4c8187a5948d7f8795adb765a3c7b553d07d86d88e43038fc32fc1fb9a3f3 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl + name: typing-extensions + version: 4.16.0 + sha256: 481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/4c/1c/6ec22372362b03350fe3da7bf33491a07cc9a553a36dd2383b76ec1741eb/fonttools-4.66.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + name: fonttools + version: 4.66.1 + sha256: 83572afe48733bad7a4a9c11721d3a726c2e976d82b063fc9bdd049d76955abd + requires_dist: + - lxml>=4.0 ; extra == 'lxml' + - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff' + - brotlicffi>=0.8.0 ; platform_python_implementation != 'CPython' and extra == 'woff' + - zopfli>=0.1.4 ; extra == 'woff' + - unicodedata2>=18.0.0 ; python_full_version < '3.16' and extra == 'unicode' + - lz4>=1.7.4.2 ; extra == 'graphite' + - scipy ; platform_python_implementation != 'PyPy' and extra == 'interpolatable' + - munkres ; platform_python_implementation == 'PyPy' and extra == 'interpolatable' + - pycairo ; extra == 'interpolatable' + - matplotlib ; extra == 'plot' + - sympy ; extra == 'symfont' + - xattr ; sys_platform == 'darwin' and extra == 'type1' + - skia-pathops>=0.5.0 ; extra == 'pathops' + - uharfbuzz>=0.45.0 ; extra == 'repacker' + - lxml>=4.0 ; extra == 'all' + - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'all' + - brotlicffi>=0.8.0 ; platform_python_implementation != 'CPython' and extra == 'all' + - zopfli>=0.1.4 ; extra == 'all' + - unicodedata2>=18.0.0 ; python_full_version < '3.16' and extra == 'all' + - lz4>=1.7.4.2 ; extra == 'all' + - scipy ; platform_python_implementation != 'PyPy' and extra == 'all' + - munkres ; platform_python_implementation == 'PyPy' and extra == 'all' + - pycairo ; extra == 'all' + - matplotlib ; extra == 'all' + - sympy ; extra == 'all' + - xattr ; sys_platform == 'darwin' and extra == 'all' + - skia-pathops>=0.5.0 ; extra == 'all' + - uharfbuzz>=0.45.0 ; extra == 'all' + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/50/51/fd1582b8f5ed8a9e7be0e161a6ea0dff70cb280479a12178df0b3a72700e/ml_dtypes-0.6.0-cp313-cp313-macosx_10_13_universal2.whl + name: ml-dtypes + version: 0.6.0 + sha256: 084dfe51a7ad58b171f05115f8226ed4233a454a1611371947e806e76f0c638d + requires_dist: + - numpy>=2.0.0 + - numpy>=2.1.0 ; python_full_version >= '3.13' + - numpy>=2.3.0 ; python_full_version >= '3.14' + - absl-py ; extra == 'dev' + - pytest ; extra == 'dev' + - pytest-xdist ; extra == 'dev' + - pylint>=2.6.0 ; extra == 'dev' + - pyink ; extra == 'dev' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl + name: packaging + version: '26.3' + sha256: d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/68/77/8f93ecbde1a4d11dfd460cd19a0c159f5487352e751fa87f5d78cc9db08e/contourpy-1.4.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + name: contourpy + version: 1.4.0 + sha256: f1219a8898523cba821085f2da8a1b962cc696325763f09d7f93538ba43b2d70 + requires_dist: + - numpy>=2.0 + - myst-parser ; extra == 'docs' + - sphinx ; extra == 'docs' + - sphinx-book-theme ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - bokeh ; extra == 'bokeh' + - selenium ; extra == 'bokeh' + - contourpy[bokeh,docs] ; extra == 'mypy' + - bokeh ; extra == 'mypy' + - docutils-stubs ; extra == 'mypy' + - mypy==2.1.0 ; extra == 'mypy' + - types-pillow ; extra == 'mypy' + - contourpy[test-no-images] ; extra == 'test' + - matplotlib ; extra == 'test' + - pre-commit ; extra == 'test' + - pillow ; extra == 'test' + - pytest ; extra == 'test-no-images' + - pytest-cov ; extra == 'test-no-images' + - pytest-rerunfailures<16 ; extra == 'test-no-images' + - pytest-xdist ; extra == 'test-no-images' + - wurlitzer ; extra == 'test-no-images' + requires_python: '>=3.12' +- pypi: https://files.pythonhosted.org/packages/76/3e/0fa5690a3a8303d821a46fd35691b1b6f8a9c06b97b41847ca41aed72e90/orng-0.2.1-py3-none-any.whl + name: orng + version: 0.2.1 + sha256: f8d98fc058ae7e8a516405235590c95f1dd3916c39f28893cc769bf0adb9f90f + requires_dist: + - array-api-compat + - numpy>=1.22 ; extra == 'numpy' + - torch>=2.0 ; extra == 'torch' + - cupy>=12.0 ; extra == 'cupy' + - jax>=0.4 ; extra == 'jax' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/83/8c/b0ea9477fb1f0d4484bbc5cba21678cc9969704d8d7f3f158d1db35f8e14/numpy-2.5.4-cp313-cp313-macosx_11_0_arm64.whl + name: numpy + version: 2.5.4 + sha256: 7415db95818b39ec475a5eea54d9e3b6bc83e3912158e46da3438cdce399804d + requires_python: '>=3.12' +- pypi: https://files.pythonhosted.org/packages/86/16/1a8fd2b19544b84575cf84ef7aa3ad4c173b756d5f087c91f85d1b295777/array_api_compat-1.15.0-py3-none-any.whl + name: array-api-compat + version: 1.15.0 + sha256: 7b1b9c53269061403fd5f45a8de349f16e7887653328bfa0c5f2d45299ff0a8e + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/89/a5/da8ae6c6f1babe4b68e3e55d43d39b529e29774f10e0910671a6b8c86eb8/ml_dtypes-0.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + name: ml-dtypes + version: 0.6.0 + sha256: 26b1f1fa4f0435a2946859823f6e2bf06796f1e9f10f5a05b08a5e3c8f46ff69 + requires_dist: + - numpy>=2.0.0 + - numpy>=2.1.0 ; python_full_version >= '3.13' + - numpy>=2.3.0 ; python_full_version >= '3.14' + - absl-py ; extra == 'dev' + - pytest ; extra == 'dev' + - pytest-xdist ; extra == 'dev' + - pylint>=2.6.0 ; extra == 'dev' + - pyink ; extra == 'dev' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/8d/c0/85fa4951ed8fcfc54ea10367e20617b60292780999835f636a6bdbf01c34/optree-0.20.0-cp313-cp313-macosx_11_0_arm64.whl + name: optree + version: 0.20.0 + sha256: eefb6f5cedc3ded670a79bcde0985d92e99e0c807787427c310adafafe1b9671 + requires_dist: + - typing-extensions>=4.10.0 + - typing-extensions>=4.12.0 ; python_full_version >= '3.13' + - typing-extensions>=4.14.0 ; python_full_version >= '3.15' + - attrs ; extra == 'attrs' + - jax ; extra == 'jax' + - numpy ; extra == 'numpy' + - torch ; extra == 'torch' + - cpplint ; extra == 'lint' + - doc8 ; extra == 'lint' + - mypy ; extra == 'lint' + - pre-commit ; extra == 'lint' + - pyenchant ; extra == 'lint' + - pylint[spelling] ; extra == 'lint' + - ruff ; extra == 'lint' + - xdoctest ; extra == 'lint' + - pytest ; extra == 'test' + - pytest-cov ; extra == 'test' + - covdefaults ; extra == 'test' + - rich ; extra == 'test' + - typing-extensions==4.10.0 ; python_full_version < '3.13' and sys_platform == 'linux' and extra == 'test' + - typing-extensions==4.10.0 ; python_full_version < '3.13' and sys_platform == 'darwin' and extra == 'test' + - typing-extensions==4.10.0 ; python_full_version < '3.13' and sys_platform == 'win32' and extra == 'test' + - typing-extensions==4.12.0 ; python_full_version >= '3.13' and python_full_version < '3.15' and sys_platform == 'linux' and extra == 'test' + - typing-extensions==4.12.0 ; python_full_version >= '3.13' and python_full_version < '3.15' and sys_platform == 'darwin' and extra == 'test' + - typing-extensions==4.12.0 ; python_full_version >= '3.13' and python_full_version < '3.15' and sys_platform == 'win32' and extra == 'test' + - typing-extensions==4.14.0 ; python_full_version >= '3.15' and sys_platform == 'linux' and extra == 'test' + - typing-extensions==4.14.0 ; python_full_version >= '3.15' and sys_platform == 'darwin' and extra == 'test' + - typing-extensions==4.14.0 ; python_full_version >= '3.15' and sys_platform == 'win32' and extra == 'test' + - sphinx~=8.0 ; extra == 'docs' + - sphinx-autoapi ; extra == 'docs' + - sphinx-autobuild ; extra == 'docs' + - sphinx-autodoc-typehints ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - sphinx-rtd-theme ; extra == 'docs' + - sphinxcontrib-bibtex ; extra == 'docs' + - docutils ; extra == 'docs' + - attrs ; extra == 'docs' + - jax[cpu] ; extra == 'docs' + - numpy ; extra == 'docs' + - torch ; extra == 'docs' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/93/b2/696029cd979f5429ec25c12767734bf0e13175d3341a2aeac59127d00ca0/optree-0.20.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + name: optree + version: 0.20.0 + sha256: d1034c2cc02ec12ed413727664f015a681e2eea551e05640951b05b06c07f084 + requires_dist: + - typing-extensions>=4.10.0 + - typing-extensions>=4.12.0 ; python_full_version >= '3.13' + - typing-extensions>=4.14.0 ; python_full_version >= '3.15' + - attrs ; extra == 'attrs' + - jax ; extra == 'jax' + - numpy ; extra == 'numpy' + - torch ; extra == 'torch' + - cpplint ; extra == 'lint' + - doc8 ; extra == 'lint' + - mypy ; extra == 'lint' + - pre-commit ; extra == 'lint' + - pyenchant ; extra == 'lint' + - pylint[spelling] ; extra == 'lint' + - ruff ; extra == 'lint' + - xdoctest ; extra == 'lint' + - pytest ; extra == 'test' + - pytest-cov ; extra == 'test' + - covdefaults ; extra == 'test' + - rich ; extra == 'test' + - typing-extensions==4.10.0 ; python_full_version < '3.13' and sys_platform == 'linux' and extra == 'test' + - typing-extensions==4.10.0 ; python_full_version < '3.13' and sys_platform == 'darwin' and extra == 'test' + - typing-extensions==4.10.0 ; python_full_version < '3.13' and sys_platform == 'win32' and extra == 'test' + - typing-extensions==4.12.0 ; python_full_version >= '3.13' and python_full_version < '3.15' and sys_platform == 'linux' and extra == 'test' + - typing-extensions==4.12.0 ; python_full_version >= '3.13' and python_full_version < '3.15' and sys_platform == 'darwin' and extra == 'test' + - typing-extensions==4.12.0 ; python_full_version >= '3.13' and python_full_version < '3.15' and sys_platform == 'win32' and extra == 'test' + - typing-extensions==4.14.0 ; python_full_version >= '3.15' and sys_platform == 'linux' and extra == 'test' + - typing-extensions==4.14.0 ; python_full_version >= '3.15' and sys_platform == 'darwin' and extra == 'test' + - typing-extensions==4.14.0 ; python_full_version >= '3.15' and sys_platform == 'win32' and extra == 'test' + - sphinx~=8.0 ; extra == 'docs' + - sphinx-autoapi ; extra == 'docs' + - sphinx-autobuild ; extra == 'docs' + - sphinx-autodoc-typehints ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - sphinx-rtd-theme ; extra == 'docs' + - sphinxcontrib-bibtex ; extra == 'docs' + - docutils ; extra == 'docs' + - attrs ; extra == 'docs' + - jax[cpu] ; extra == 'docs' + - numpy ; extra == 'docs' + - torch ; extra == 'docs' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/95/10/ff159867f522cd98e039e748c5e9777446e8b797a79be691c6b730676094/zuko-1.6.0-py3-none-any.whl + name: zuko + version: 1.6.0 + sha256: 5c073b613a84a7cd65470ddb94855169020ac49432f73b85f25207e377248a4a + requires_dist: + - numpy>=1.20.0 + - torch>=1.12.0 + - ipykernel>=6.29.0 ; extra == 'dev' + - matplotlib>=3.8.0 ; extra == 'dev' + - pre-commit>=3.7.0 ; extra == 'dev' + - ruff==0.9.9 ; extra == 'dev' + - docutils==0.19 ; extra == 'docs' + - furo==2024.5.6 ; extra == 'docs' + - myst-nb==1.0.0 ; extra == 'docs' + - sphinx==7.3.7 ; extra == 'docs' + - pytest>=8.0.0 ; extra == 'test' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/a7/03/921a3d3c75785aca9ebfbfcabfbc3a1be12e2ab5265deb026d55a5a3f83e/tqdm-4.70.1-py3-none-any.whl + name: tqdm + version: 4.70.1 + sha256: c293e525e6fef9c20e8728fd4612df02a0aa31bb5fe91ecd93e123b1b7bffa73 + requires_dist: + - colorama ; sys_platform == 'win32' + - requests ; extra == 'discord' + - envwrap ; extra == 'discord' + - slack-sdk ; extra == 'slack' + - envwrap ; extra == 'slack' + - requests ; extra == 'telegram' + - envwrap ; extra == 'telegram' + - ipywidgets>=6 ; extra == 'notebook' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/a9/29/2a47b700545aca050e0ed73c89ba21e08407e11f5f42a567421b220de6c1/matplotlib-3.11.2-cp313-cp313-macosx_11_0_arm64.whl + name: matplotlib + version: 3.11.2 + sha256: 2a8285cea8ef4d92aa041d1c33788bcae82248503300f93f1ca2136b9049452f + requires_dist: + - contourpy>=1.0.1 + - cycler>=0.10 + - fonttools>=4.28.2 + - kiwisolver>=1.3.1 + - numpy>=1.25 + - packaging>=20.0 + - pillow>=9 + - pyparsing>=3 + - python-dateutil>=2.7 + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/a9/df/e7c10292f348cf55178a5b3c0092a38ea68891a3945f199ae8b7b8ac9dc1/matplotlib-3.11.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + name: matplotlib + version: 3.11.2 + sha256: 1944895967f87c84c9b4bad29a707b31f5b36df4a3a2339ea1f8ea3ce5105539 + requires_dist: + - contourpy>=1.0.1 + - cycler>=0.10 + - fonttools>=4.28.2 + - kiwisolver>=1.3.1 + - numpy>=1.25 + - packaging>=20.0 + - pillow>=9 + - pyparsing>=3 + - python-dateutil>=2.7 + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/ab/43/86aacc027959108b4c66eeae8b73cedb057dfa6eb3a335d05ad65197081c/kiwisolver-1.5.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + name: kiwisolver + version: 1.5.1 + sha256: 74ad5c3dad54a4641b4c28cd15ded70899d04459c6c7aeacafea716be97cce6d + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/b0/65/5e088a45d0f43cd814bc5bec521c051d42005a472e804b1a36c48dada09b/h5py-3.16.0-cp313-cp313-macosx_11_0_arm64.whl + name: h5py + version: 3.16.0 + sha256: 42108e93326c50c2810025aade9eac9d6827524cdccc7d4b75a546e5ab308edb + requires_dist: + - numpy>=1.21.2 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + name: six + version: 1.17.0 + sha256: 4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 + requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*' +- pypi: https://files.pythonhosted.org/packages/bd/98/ef2b6fe2903e377cbe870c3b2800d62552f1e3dbe81ce49e1923c53d1c5c/h5py-3.16.0-cp313-cp313-manylinux_2_28_x86_64.whl + name: h5py + version: 3.16.0 + sha256: 9300ad32dea9dfc5171f94d5f6948e159ed93e4701280b0f508773b3f582f402 + requires_dist: + - numpy>=1.21.2 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/c7/b3/fcf70b2ed935f26fc15dfd81f72f781d395b09e302af5c86ec285d63524e/array_api_extra-0.11.4-py3-none-any.whl + name: array-api-extra + version: 0.11.4 + sha256: b2d5bd31b3072249fd3f90a2b1f7b398c78e3ccb7575e04e9e3348ae894da60f + requires_dist: + - array-api-compat>=1.15.0,<2 + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/ca/87/0c0ca8433e1135f6d1acd355cff21f31c3d433d937d6547aa545c8b060b8/jax-0.11.2-py3-none-any.whl + name: jax + version: 0.11.2 + sha256: 59e7ed9bd9ace049f4856752fe580c890e0164e1c023f40fadac386408daad66 + requires_dist: + - jaxlib<=0.11.2,>=0.11.2 + - ml-dtypes>=0.5.0 + - numpy>=2.1 + - opt-einsum + - scipy>=1.15 + - jaxlib==0.11.2 ; extra == 'minimum-jaxlib' + - jaxlib==0.11.1 ; extra == 'ci' + - jaxlib<=0.11.2,>=0.11.2 ; extra == 'tpu' + - libtpu==0.0.48.* ; extra == 'tpu' + - requests ; extra == 'tpu' + - jaxlib<=0.11.2,>=0.11.2 ; extra == 'cuda' + - jax-cuda12-plugin[with-cuda]<=0.11.2,>=0.11.2 ; extra == 'cuda' + - jaxlib<=0.11.2,>=0.11.2 ; extra == 'cuda12' + - jax-cuda12-plugin[with-cuda]<=0.11.2,>=0.11.2 ; extra == 'cuda12' + - jaxlib<=0.11.2,>=0.11.2 ; extra == 'cuda13' + - jax-cuda13-plugin[with-cuda]<=0.11.2,>=0.11.2 ; extra == 'cuda13' + - jaxlib<=0.11.2,>=0.11.2 ; extra == 'cuda12-local' + - jax-cuda12-plugin<=0.11.2,>=0.11.2 ; extra == 'cuda12-local' + - jaxlib<=0.11.2,>=0.11.2 ; extra == 'cuda13-local' + - jax-cuda13-plugin<=0.11.2,>=0.11.2 ; extra == 'cuda13-local' + - jaxlib<=0.11.2,>=0.11.2 ; extra == 'rocm7-local' + - jax-rocm7-plugin==0.11.2.* ; extra == 'rocm7-local' + - jaxlib<=0.11.2,>=0.11.2 ; extra == 'oneapi' + - jax-oneapi-plugin[with-oneapi]<=0.11.2,>=0.11.2 ; extra == 'oneapi' + - kubernetes ; extra == 'k8s' + - xprof ; extra == 'xprof' + requires_python: '>=3.12' +- pypi: https://files.pythonhosted.org/packages/cb/f4/e410b8c913da5b3fdbb4d16db0f2d2a0952f59c4db8d52dcf2d421d82044/fonttools-4.66.1-cp313-cp313-macosx_10_13_universal2.whl + name: fonttools + version: 4.66.1 + sha256: 53e5854ea8003efec34adc0863c18ce91da923018354d27366f7fee7db928d7a + requires_dist: + - lxml>=4.0 ; extra == 'lxml' + - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff' + - brotlicffi>=0.8.0 ; platform_python_implementation != 'CPython' and extra == 'woff' + - zopfli>=0.1.4 ; extra == 'woff' + - unicodedata2>=18.0.0 ; python_full_version < '3.16' and extra == 'unicode' + - lz4>=1.7.4.2 ; extra == 'graphite' + - scipy ; platform_python_implementation != 'PyPy' and extra == 'interpolatable' + - munkres ; platform_python_implementation == 'PyPy' and extra == 'interpolatable' + - pycairo ; extra == 'interpolatable' + - matplotlib ; extra == 'plot' + - sympy ; extra == 'symfont' + - xattr ; sys_platform == 'darwin' and extra == 'type1' + - skia-pathops>=0.5.0 ; extra == 'pathops' + - uharfbuzz>=0.45.0 ; extra == 'repacker' + - lxml>=4.0 ; extra == 'all' + - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'all' + - brotlicffi>=0.8.0 ; platform_python_implementation != 'CPython' and extra == 'all' + - zopfli>=0.1.4 ; extra == 'all' + - unicodedata2>=18.0.0 ; python_full_version < '3.16' and extra == 'all' + - lz4>=1.7.4.2 ; extra == 'all' + - scipy ; platform_python_implementation != 'PyPy' and extra == 'all' + - munkres ; platform_python_implementation == 'PyPy' and extra == 'all' + - pycairo ; extra == 'all' + - matplotlib ; extra == 'all' + - sympy ; extra == 'all' + - xattr ; sys_platform == 'darwin' and extra == 'all' + - skia-pathops>=0.5.0 ; extra == 'all' + - uharfbuzz>=0.45.0 ; extra == 'all' + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/d6/cb/17a130e548ee1777f2a66e09240c531a9bf79454add7ef1198fe1e41c412/corner-2.3.0-py3-none-any.whl + name: corner + version: 2.3.0 + sha256: f93f0a4d77ff6852d6ddc80a6a99038117be64e3b5b49d96a82c9f5974cce126 + requires_dist: + - matplotlib>=2.1 + - arviz-base ; extra == 'arviz' + - arviz>=0.9 ; extra == 'arviz' + - arviz-base ; extra == 'docs' + - arviz>=0.9 ; extra == 'docs' + - ipython ; extra == 'docs' + - myst-nb ; extra == 'docs' + - pandoc ; extra == 'docs' + - sphinx-book-theme ; extra == 'docs' + - sphinx>=1.7.5 ; extra == 'docs' + - arviz-base ; extra == 'test' + - arviz>=0.9 ; extra == 'test' + - pytest ; extra == 'test' + - scipy ; extra == 'test' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/da/24/3bd070f3269dc609d8f26b2643f62ef91bb415841c0b294805aaf7fe06da/numpy-2.5.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + name: numpy + version: 2.5.4 + sha256: 6ffa07666f8da0eef81d149934a626d0d95fbd6838432a33e66245423a9062c0 + requires_python: '>=3.12' +- pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl + name: cycler + version: 0.12.1 + sha256: 85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30 + requires_dist: + - ipython ; extra == 'docs' + - matplotlib ; extra == 'docs' + - numpydoc ; extra == 'docs' + - sphinx ; extra == 'docs' + - pytest ; extra == 'tests' + - pytest-cov ; extra == 'tests' + - pytest-xdist ; extra == 'tests' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl + name: python-dateutil + version: 2.9.0.post0 + sha256: a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 + requires_dist: + - six>=1.5 + requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*' +- pypi: https://files.pythonhosted.org/packages/f7/62/de5bdd77d935331f4f802edc11e4d82950f642caad6cb2f949837b8560e2/pillow-12.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + name: pillow + version: 12.3.0 + sha256: 0847a763afefb695bc912d7c131e7e0632d4edc1d8698f58ddabec8e46b8b6d3 + requires_dist: + - furo ; extra == 'docs' + - olefile ; extra == 'docs' + - sphinx>=8.2 ; extra == 'docs' + - sphinx-autobuild ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - sphinx-inline-tabs ; extra == 'docs' + - sphinxext-opengraph ; extra == 'docs' + - olefile ; extra == 'fpx' + - olefile ; extra == 'mic' + - arro3-compute ; extra == 'test-arrow' + - arro3-core ; extra == 'test-arrow' + - nanoarrow ; extra == 'test-arrow' + - pyarrow ; extra == 'test-arrow' + - coverage>=7.4.2 ; extra == 'tests' + - defusedxml ; extra == 'tests' + - markdown2 ; extra == 'tests' + - olefile ; extra == 'tests' + - packaging ; extra == 'tests' + - pytest ; extra == 'tests' + - pytest-cov ; extra == 'tests' + - pytest-timeout ; extra == 'tests' + - pytest-xdist ; extra == 'tests' + - setuptools ; extra == 'tests' + - trove-classifiers>=2024.10.12 ; extra == 'tests' + - defusedxml ; extra == 'xmp' + requires_python: '>=3.10' diff --git a/pyproject.toml b/pyproject.toml index 704d496..32ea428 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,3 +7,58 @@ target-version = "py39" # Allow fix for all enabled rules (when `--fix`) is provided. fixable = ["ALL"] extend-select = ["I"] + +[tool.pixi.workspace] +name = "sequince-examples" +channels = ["conda-forge"] +platforms = ["linux-64", "osx-arm64"] +requires-pixi = ">=0.81.0" + +[tool.pixi.dependencies] +python = "3.13.*" + +[tool.pixi.feature.cpu.pypi-dependencies] +sequince = { git = "https://github.com/sequince-dev/sequince.git", branch = "main", extras = ["corner", "hdf5"] } + +[tool.pixi.feature.jax.pypi-dependencies] +jax = ">0.5.3" + +[tool.pixi.feature.jax.activation.env] +JAX_PLATFORMS = "cpu" + +[tool.pixi.feature.flow.dependencies] +pytorch-cpu = "*" + +[tool.pixi.feature.flow.pypi-dependencies] +sequince-density = { git = "https://github.com/sequince-dev/sequince-flows.git", rev = "cbe32939713f17be2c5deb8f813ba1ec4550ea7e", extras = ["zuko"] } + +[tool.pixi.feature.docs.dependencies] +sphinx = "8.2.*" +sphinx-book-theme = "1.1.*" + +[tool.pixi.feature.check.dependencies] +ruff = "0.14.*" +pre-commit = "*" +pytest = "*" + +[tool.pixi.environments] +default = { features = ["cpu"], solve-group = "cpu" } +jax = { features = ["cpu", "jax"], solve-group = "cpu" } +flow = ["cpu", "flow"] +docs = ["docs"] +check = ["check"] + +[tool.pixi.tasks] +example = "python scripts/docs_pipeline.py run" +examples = "python scripts/docs_pipeline.py run-all" +validate-outputs = "python scripts/docs_pipeline.py validate" +prepare-docs = "python scripts/docs_pipeline.py prepare" +bundle = "python scripts/docs_pipeline.py bundle" + +[tool.pixi.feature.docs.tasks] +docs = { cmd = "sphinx-build -W --keep-going -b html docs docs/_build/html", depends-on = ["prepare-docs"] } + +[tool.pixi.feature.check.tasks] +test = "pytest tests" +lint = "ruff check scripts tests docs/conf.py" +format-check = "ruff format --check scripts tests docs/conf.py" diff --git a/scripts/docs_pipeline.py b/scripts/docs_pipeline.py new file mode 100644 index 0000000..29b3a97 --- /dev/null +++ b/scripts/docs_pipeline.py @@ -0,0 +1,555 @@ +"""Execute, validate and transport commit-specific example documentation.""" + +from __future__ import annotations + +import argparse +import gzip +import hashlib +import importlib.metadata +import importlib.util +import io +import json +import os +import re +import shutil +import subprocess +import sys +import tarfile +import tempfile +import urllib.request +from pathlib import Path + +import tomllib + +ROOT = Path(__file__).resolve().parents[1] +BUNDLES = ROOT / "build-docs" +OUTPUT = BUNDLES / "outputs" +GENERATED = ROOT / "docs" / "_generated" +REPOSITORY = "sequince-dev/sequince-examples" + + +def git(*args: str) -> str: + """Run a read-only Git command in the source checkout.""" + return subprocess.check_output(["git", *args], cwd=ROOT, text=True).strip() + + +def digest(path: Path) -> str: + """Hash file contents.""" + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def write_json(path: Path, value: object) -> None: + """Write stable, readable JSON and reject nonfinite numerical values.""" + path.write_text( + json.dumps(value, indent=2, sort_keys=True, allow_nan=False) + "\n" + ) + + +def examples() -> list[dict]: + """Read the explicit publication manifest.""" + return json.loads((ROOT / "docs/examples.json").read_text())["examples"] + + +def entry(name: str) -> dict: + """Find an example or fail with a useful error.""" + for example in examples(): + if example["id"] == name: + return example + raise ValueError(f"Unknown example: {name}") + + +def dependency_commit() -> str: + """Read Sequince's resolved commit from the execution lock.""" + return git_dependencies({})["sequince"] + + +def git_dependencies(example: dict) -> dict[str, str]: + """Read configured Git dependencies' exact resolved lock revisions.""" + config = tomllib.loads((ROOT / "pyproject.toml").read_text())["tool"][ + "pixi" + ] + lock = (ROOT / "pixi.lock").read_text() + revisions = {} + for feature in ["cpu", *example.get("dependency_features", [])]: + for name, specification in ( + config["feature"][feature].get("pypi-dependencies", {}).items() + ): + if ( + not isinstance(specification, dict) + or "git" not in specification + ): + continue + pattern = ( + re.escape("git+" + specification["git"]) + + r"\?[^#\s]*#([0-9a-f]{40})" + ) + matches = set(re.findall(pattern, lock)) + if len(matches) != 1: + raise ValueError( + f"Missing or inconsistent locked Git commit: {name}" + ) + revision = matches.pop() + if "rev" in specification and specification["rev"] != revision: + raise ValueError( + f"Configured and locked revisions differ: {name}" + ) + revisions[name] = revision + return revisions + + +def refresh() -> None: + """Resolve current Sequince main once before a new execution batch.""" + subprocess.run(["pixi", "update", "sequince"], cwd=ROOT, check=True) + + +def source_hashes(example: dict) -> dict[str, str]: + """Fingerprint execution inputs as well as the checkout commit.""" + paths = [ + example["script"], + example["narrative"], + "pyproject.toml", + "pixi.lock", + "docs/examples.json", + "scripts/docs_pipeline.py", + ] + return {path: digest(ROOT / path) for path in paths} + + +def worker(name: str, raw: Path, destination: Path) -> None: + """Run a script with only its output destination changed.""" + import numpy as np + + example = entry(name) + revisions = git_dependencies(example) + versions = {} + for package, revision in revisions.items(): + distribution = importlib.metadata.distribution(package) + direct = json.loads(distribution.read_text("direct_url.json") or "{}") + resolved = direct.get("vcs_info", {}).get("commit_id") + if resolved != revision: + raise ValueError( + f"Installed {package} commit {resolved} != pinned {revision}" + ) + versions[package] = distribution.version + spec = importlib.util.spec_from_file_location( + "documentation_example", ROOT / example["script"] + ) + if spec is None or spec.loader is None: + raise ValueError(f"Cannot load {example['script']}") + module = importlib.util.module_from_spec(spec) + # Dataclasses with postponed annotations resolve their module by name. + sys.modules[spec.name] = module + spec.loader.exec_module(module) + defaults = ( + vars(module.build_parser().parse_args([])) + if hasattr(module, "build_parser") + else vars(module) + ) + parameters = {key: defaults[key] for key in example["parameters"]} + # Keep the script's main function, sampler, RNG and constants intact. + module.outdir = raw + module.documentation_output = raw + raw.mkdir(parents=True, exist_ok=True) + original_argv = sys.argv + try: + sys.argv = [str(ROOT / example["script"])] + module.main() + finally: + sys.argv = original_argv + for name in example["required_outputs"]: + path = raw / name + if not path.is_file() or not path.stat().st_size: + raise ValueError(f"Missing required output: {path}") + if (raw / "summary.json").is_file(): + summary = json.loads((raw / "summary.json").read_text()) + else: + with np.load(raw / "posterior.npz", allow_pickle=False) as posterior: + particles = posterior["particles"] + weights = posterior["weights"] + if not np.all(np.isfinite(particles)): + raise ValueError("Nonfinite posterior particles") + if ( + not np.all(np.isfinite(weights)) + or np.any(weights < 0) + or not np.isclose(weights.sum(), 1.0) + ): + raise ValueError("Invalid posterior weights") + mean = np.sum(particles * weights[:, None], axis=0) + centered = particles - mean + # This is the population covariance of the weighted empirical measure. + covariance = (centered * weights[:, None]).T @ centered + summary = { + "log_evidence": float(posterior["log_evidence"]), + "weighted_mean": mean.tolist(), + "weighted_population_covariance": covariance.tolist(), + "effective_sample_size": float(1 / np.sum(weights**2)), + "n_particles": len(particles), + } + destination.mkdir(parents=True, exist_ok=False) + for name in example["figures"]: + shutil.copyfile(raw / name, destination / name) + write_json(destination / "summary.json", summary) + write_json( + destination / "metadata.json", + { + "schema": 1, + "example": example["id"], + "source_commit": git("rev-parse", "HEAD"), + "source_hashes": source_hashes(example), + "sequince_version": importlib.metadata.version("sequince"), + "sequince_commit": revisions["sequince"], + "git_dependencies": revisions, + "dependency_versions": versions, + "python_version": sys.version.split()[0], + "environment": example["environment"], + "parameters": parameters, + "seed": parameters["seed"], + }, + ) + write_json( + destination / "checksums.json", + {path.name: digest(path) for path in sorted(destination.iterdir())}, + ) + + +def run(name: str) -> None: + """Execute with a hard timeout, a noninteractive backend and fresh outputs.""" + example = entry(name) + OUTPUT.mkdir(parents=True, exist_ok=True) + target = OUTPUT / name + if target.exists(): + shutil.rmtree(target) + log = ROOT / "build-docs" / f"{name}.log" + with tempfile.TemporaryDirectory(prefix=f"sequince-{name}-") as tmp: + env = os.environ | {"MPLBACKEND": "Agg", "MPLCONFIGDIR": tmp + "/mpl"} + try: + with log.open("w") as stream: + subprocess.run( + [ + sys.executable, + str(Path(__file__).resolve()), + "worker", + name, + "--raw", + tmp + "/raw", + "--destination", + str(target), + ], + cwd=ROOT / example["working_directory"], + env=env, + stdout=stream, + stderr=subprocess.STDOUT, + timeout=example["timeout"], + check=True, + ) + except (subprocess.CalledProcessError, subprocess.TimeoutExpired): + print(log.read_text(), file=sys.stderr) + raise + print(f"Completed {name}: {target}; execution log: {log}") + + +def validate(base: Path | None = None) -> None: + """Reject incomplete, corrupt, mixed-commit or incompatible outputs.""" + base = OUTPUT if base is None else base + expected = {example["id"] for example in examples()} + actual = {path.name for path in base.iterdir()} if base.is_dir() else set() + missing = expected - {name for name in actual if (base / name).is_dir()} + unexpected = actual - expected + if missing or unexpected: + details = [] + if missing: + details.append( + "missing example outputs: " + ", ".join(sorted(missing)) + ) + if unexpected: + details.append( + "unexpected entries: " + ", ".join(sorted(unexpected)) + ) + raise ValueError( + f"Invalid documentation outputs in {base}: " + + "; ".join(details) + + ". Generate or restore outputs for this source revision " + "before building documentation." + ) + for example in examples(): + directory = base / example["id"] + required = {*example["figures"], "summary.json", "metadata.json"} + checksums = json.loads((directory / "checksums.json").read_text()) + if set(checksums) != required: + raise ValueError(f"Incomplete checksum manifest: {directory}") + if {p.name for p in directory.iterdir()} != required | { + "checksums.json" + }: + raise ValueError(f"Unexpected files: {directory}") + for name, expected in checksums.items(): + path = directory / name + if not path.is_file() or not path.stat().st_size: + raise ValueError(f"Missing or empty output: {path}") + if digest(path) != expected: + raise ValueError(f"Checksum mismatch: {path}") + metadata = json.loads((directory / "metadata.json").read_text()) + expected_metadata = { + "schema": 1, + "example": example["id"], + "source_commit": git("rev-parse", "HEAD"), + "source_hashes": source_hashes(example), + "sequince_commit": dependency_commit(), + "git_dependencies": git_dependencies(example), + "environment": example["environment"], + } + for key, value in expected_metadata.items(): + if metadata.get(key) != value: + raise ValueError(f"Provenance mismatch: {directory}: {key}") + if set(metadata["parameters"]) != set(example["parameters"]): + raise ValueError(f"Missing parameters: {directory}") + if metadata["seed"] != metadata["parameters"]["seed"]: + raise ValueError(f"Seed mismatch: {directory}") + versions = metadata.get("dependency_versions", {}) + if set(versions) != set(git_dependencies(example)) or not all( + versions.values() + ): + raise ValueError( + f"Missing installed dependency versions: {directory}" + ) + if not metadata.get("sequince_version"): + raise ValueError(f"Missing installed version: {directory}") + json.loads((directory / "summary.json").read_text()) + print(f"Validated all outputs for {git('rev-parse', 'HEAD')}") + + +def prepare() -> None: + """Stage narrative, script source and validated results separately.""" + validate() + if GENERATED.exists(): + shutil.rmtree(GENERATED) + GENERATED.mkdir(parents=True) + narratives: set[str] = set() + for example in examples(): + narrative = example["narrative"] + folder = GENERATED / str(Path(narrative).parent) + folder.mkdir(parents=True, exist_ok=True) + shutil.copyfile( + ROOT / example["script"], folder / Path(example["script"]).name + ) + if narrative not in narratives: + shutil.copyfile(ROOT / narrative, GENERATED / narrative) + narratives.add(narrative) + target = GENERATED / "results" / example["id"] + shutil.copytree(OUTPUT / example["id"], target) + + +def bundle() -> Path: + """Create a deterministic archive containing only validated public outputs.""" + validate() + sha = git("rev-parse", "HEAD") + path = BUNDLES / f"docs-outputs-{sha}.tar.gz" + files = { + str(p.relative_to(OUTPUT)): p.read_bytes() + for p in sorted(OUTPUT.rglob("*")) + if p.is_file() + } + manifest = { + "schema": 1, + "source_commit": sha, + "files": { + name: hashlib.sha256(data).hexdigest() + for name, data in files.items() + }, + } + files["execution-pixi.lock"] = (ROOT / "pixi.lock").read_bytes() + manifest["files"]["execution-pixi.lock"] = hashlib.sha256( + files["execution-pixi.lock"] + ).hexdigest() + files["bundle-manifest.json"] = ( + json.dumps(manifest, indent=2, sort_keys=True) + "\n" + ).encode() + buffer = io.BytesIO() + with gzip.GzipFile( + fileobj=buffer, mode="wb", mtime=0, filename="" + ) as zipped: + with tarfile.open(fileobj=zipped, mode="w") as archive: + for name, data in sorted(files.items()): + info = tarfile.TarInfo(name) + info.size = len(data) + info.mode = 0o644 + archive.addfile(info, io.BytesIO(data)) + if path.exists() and path.read_bytes() != buffer.getvalue(): + raise ValueError( + f"Refusing to replace different existing bundle: {path}" + ) + path.write_bytes(buffer.getvalue()) + path.with_suffix(path.suffix + ".sha256").write_text( + f"{digest(path)} {path.name}\n" + ) + print(path) + return path + + +def unpack( + path: Path, + expected_sha: str, + destination: Path, + *, + restore_lock: bool = False, +) -> None: + """Validate archive paths, completeness and checksums before extraction.""" + with tarfile.open(path) as archive: + members = archive.getmembers() + names = [m.name for m in members] + if len(names) != len(set(names)): + raise ValueError("Duplicate archive members") + files = {} + for member in members: + name = Path(member.name) + if ( + not member.isfile() + or name.is_absolute() + or ".." in name.parts + or str(name) != member.name + ): + raise ValueError(f"Unsafe archive member: {member.name}") + stream = archive.extractfile(member) + if stream is None: + raise ValueError(f"Cannot read {member.name}") + files[member.name] = stream.read() + manifest = json.loads(files.pop("bundle-manifest.json")) + if manifest["schema"] != 1 or manifest["source_commit"] != expected_sha: + raise ValueError("Bundle source commit mismatch") + hashes = { + name: hashlib.sha256(data).hexdigest() for name, data in files.items() + } + if manifest["files"] != hashes: + raise ValueError("Bundle completeness/checksum mismatch") + if destination.exists(): + raise ValueError(f"Destination already exists: {destination}") + execution_lock = files.pop("execution-pixi.lock") + if restore_lock: + (ROOT / "pixi.lock").write_bytes(execution_lock) + elif execution_lock != (ROOT / "pixi.lock").read_bytes(): + raise ValueError("Bundle execution lock differs from the active lock") + for name, data in files.items(): + target = destination / name + target.parent.mkdir(parents=True, exist_ok=True) + target.write_bytes(data) + validate(destination) + + +def check_latest() -> None: + """Stop stale latest builds both before Sphinx and before RTD upload.""" + if os.environ.get("READTHEDOCS_VERSION") == "latest": + remote = git("ls-remote", "origin", "refs/heads/main").split()[0] + if git("rev-parse", "HEAD") != remote: + raise ValueError( + "Stale latest checkout: main advanced; rebuild latest" + ) + + +def fetch() -> None: + """Fetch only the durable bundle for RTD's actual checked-out commit.""" + check_latest() + sha = git("rev-parse", "HEAD") + # No credentials: RTD Community and these assets must be publicly readable. + name = f"docs-outputs-{sha}.tar.gz" + base = ( + f"https://github.com/{REPOSITORY}/releases/download/docs-output-{sha}" + ) + with tempfile.TemporaryDirectory() as tmp: + path = Path(tmp) / name + for suffix in ("", ".sha256"): + with urllib.request.urlopen( + base + "/" + name + suffix, timeout=60 + ) as response: + Path(str(path) + suffix).write_bytes(response.read()) + expected = Path(str(path) + ".sha256").read_text().split() + if expected != [digest(path), name]: + raise ValueError("Downloaded bundle checksum mismatch") + if OUTPUT.exists(): + shutil.rmtree(OUTPUT) + unpack(path, sha, OUTPUT, restore_lock=True) + prepare() + + +def main() -> None: + """Dispatch local, Actions and RTD commands.""" + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "command", + choices=[ + "matrix", + "run", + "run-all", + "worker", + "validate", + "prepare", + "bundle", + "fetch", + "check-latest", + "refresh", + ], + ) + parser.add_argument("name", nargs="?") + parser.add_argument("--raw", type=Path) + parser.add_argument("--destination", type=Path) + args = parser.parse_args() + if args.command == "matrix": + print( + json.dumps( + { + "include": [ + {"id": e["id"], "environment": e["environment"]} + for e in examples() + ] + } + ) + ) + elif args.command == "worker": + worker(args.name, args.raw, args.destination) + elif args.command == "run": + if os.environ.get("SEQUINCE_LOCK_READY") != "1": + refresh() + subprocess.run( + [ + "pixi", + "install", + "--locked", + "-e", + entry(args.name)["environment"], + ], + cwd=ROOT, + check=True, + ) + run(args.name) + elif args.command == "run-all": + if os.environ.get("SEQUINCE_LOCK_READY") != "1": + refresh() + for example in examples(): + subprocess.run( + [ + "pixi", + "run", + "--locked", + "-e", + example["environment"], + "example", + example["id"], + ], + cwd=ROOT, + env=os.environ | {"SEQUINCE_LOCK_READY": "1"}, + check=True, + ) + else: + { + "refresh": refresh, + "validate": validate, + "prepare": prepare, + "bundle": bundle, + "fetch": fetch, + "check-latest": check_latest, + }[args.command]() + + +if __name__ == "__main__": + try: + main() + except (ValueError, FileNotFoundError) as error: + raise SystemExit(f"Documentation pipeline error: {error}") from None diff --git a/scripts/publish_docs.py b/scripts/publish_docs.py new file mode 100644 index 0000000..17bdf21 --- /dev/null +++ b/scripts/publish_docs.py @@ -0,0 +1,128 @@ +"""Trusted publication only: immutable-by-policy assets, then serialized RTD.""" + +from __future__ import annotations + +import json +import os +import subprocess +import time +import urllib.parse +import urllib.request +from pathlib import Path + +from docs_pipeline import ROOT, bundle, digest, git + + +def gh(*args: str) -> str: + """Call the GitHub CLI, failing on API or permission errors.""" + return subprocess.check_output(["gh", *args], cwd=ROOT, text=True).strip() + + +def rtd(path: str, method: str = "GET") -> dict: + """Call the documented Community API without logging credentials.""" + request = urllib.request.Request( + "https://app.readthedocs.org/api/v3/" + path, + headers={"Authorization": "Token " + os.environ["RTD_TOKEN"]}, + method=method, + ) + with urllib.request.urlopen(request, timeout=60) as response: + return json.load(response) + + +def wait_idle(project: str, deadline: float) -> None: + """Wait through the RTD upload before allowing another publication.""" + while rtd(f"projects/{project}/builds/?running=true")["count"]: + if time.monotonic() > deadline: + raise TimeoutError( + "RTD still running; inspect builds before retrying" + ) + time.sleep(20) + + +def main() -> None: + """Publish only main, verify assets, and monitor the triggered build.""" + sha = git("rev-parse", "HEAD") + if git("ls-remote", "origin", "refs/heads/main").split()[0] != sha: + print("Main has advanced; skipping stale publication") + return + if set(git("diff", "--name-only", "HEAD").splitlines()) - {"pixi.lock"}: + raise ValueError("Refusing publication from a modified checkout") + path = bundle() + tag = f"docs-output-{sha}" + # Dedicated prereleases in the examples repository cannot become a library + # release or GitHub's latest software release. + view = subprocess.run( + ["gh", "release", "view", tag, "--json", "assets,isDraft"], + cwd=ROOT, + text=True, + capture_output=True, + ) + if view.returncode: + gh( + "release", + "create", + tag, + "--target", + sha, + "--title", + tag, + "--notes", + f"Validated example documentation outputs for {sha}.", + "--prerelease", + "--latest=false", + "--draft", + ) + release = {"assets": [], "isDraft": True} + else: + release = json.loads(view.stdout) + names = {asset["name"] for asset in release["assets"]} + for asset in (path, Path(str(path) + ".sha256")): + if asset.name in names: + existing = ROOT / "build-docs" / "existing" + existing.mkdir(exist_ok=True) + gh( + "release", + "download", + tag, + "--pattern", + asset.name, + "--dir", + str(existing), + "--clobber", + ) + if digest(existing / asset.name) != digest(asset): + raise ValueError( + f"Refusing to replace different asset: {asset.name}" + ) + else: + gh("release", "upload", tag, str(asset)) + if release["isDraft"]: + gh("release", "edit", tag, "--draft=false", "--latest=false") + # The workflow's non-cancelling concurrency lock covers this whole wait. + project = urllib.parse.quote(os.environ["RTD_PROJECT"], safe="") + deadline = time.monotonic() + 2400 + wait_idle(project, deadline) + if git("ls-remote", "origin", "refs/heads/main").split()[0] != sha: + print("Bundle uploaded; main advanced, skipping latest trigger") + return + result = rtd(f"projects/{project}/versions/latest/builds/", "POST") + build_id = result["build"]["id"] + while True: + build = rtd(f"projects/{project}/builds/{build_id}/") + if build["state"]["code"] in {"finished", "cancelled"}: + break + if time.monotonic() > deadline: + raise TimeoutError( + "RTD build timed out; inspect it before retrying" + ) + time.sleep(20) + if not build["success"] or build["commit"] != sha: + raise ValueError( + f"RTD build {build_id} failed or checked out another commit; " + "inspect RTD and rerun the workflow for current main" + ) + print(f"RTD build {build_id} succeeded for {sha}") + + +if __name__ == "__main__": + main() diff --git a/tests/test_docs_pipeline.py b/tests/test_docs_pipeline.py new file mode 100644 index 0000000..c283211 --- /dev/null +++ b/tests/test_docs_pipeline.py @@ -0,0 +1,292 @@ +"""Exercise provenance and transport failures independently of sampling.""" + +from __future__ import annotations + +import io +import json +import sys +import tarfile +from pathlib import Path + +import pytest + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts")) +import docs_pipeline as pipeline # noqa: E402 + + +@pytest.fixture +def outputs(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path: + """Make a complete, checksummed output set for the current source.""" + monkeypatch.setattr(pipeline, "OUTPUT", tmp_path / "outputs") + monkeypatch.setattr(pipeline, "git", lambda *args: "a" * 40) + for example in pipeline.examples(): + directory = pipeline.OUTPUT / example["id"] + directory.mkdir(parents=True) + for name in example["figures"]: + (directory / name).write_bytes(b"plot") + pipeline.write_json(directory / "summary.json", {"log_evidence": -1.0}) + pipeline.write_json( + directory / "metadata.json", + { + "schema": 1, + "example": example["id"], + "source_commit": "a" * 40, + "source_hashes": pipeline.source_hashes(example), + "sequince_version": "0.1.dev1", + "sequince_commit": pipeline.dependency_commit(), + "git_dependencies": pipeline.git_dependencies(example), + "dependency_versions": { + name: "test-version" + for name in pipeline.git_dependencies(example) + }, + "environment": example["environment"], + "seed": 42, + "parameters": {key: 42 for key in example["parameters"]}, + }, + ) + pipeline.write_json( + directory / "checksums.json", + {path.name: pipeline.digest(path) for path in directory.iterdir()}, + ) + return pipeline.OUTPUT + + +def test_complete_outputs(outputs: Path) -> None: + pipeline.validate(outputs) + + +@pytest.mark.parametrize("failure", ["missing", "corrupt", "commit", "input"]) +def test_invalid_outputs_fail(outputs: Path, failure: str) -> None: + directory = outputs / "gaussian" + if failure == "missing": + (directory / "corner.png").unlink() + elif failure == "corrupt": + (directory / "corner.png").write_bytes(b"corrupt") + else: + path = directory / "metadata.json" + metadata = json.loads(path.read_text()) + if failure == "commit": + metadata["source_commit"] = "b" * 40 + else: + metadata["source_hashes"]["pixi.lock"] = "wrong" + pipeline.write_json(path, metadata) + checksums = json.loads((directory / "checksums.json").read_text()) + checksums[path.name] = pipeline.digest(path) + pipeline.write_json(directory / "checksums.json", checksums) + with pytest.raises((ValueError, FileNotFoundError)): + pipeline.validate(outputs) + + +def test_bundle_roundtrip( + outputs: Path, tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + monkeypatch.setattr(pipeline, "BUNDLES", tmp_path) + path = pipeline.bundle() + checksum = pipeline.digest(path) + assert pipeline.digest(pipeline.bundle()) == checksum + destination = tmp_path / "restored" + pipeline.unpack(path, "a" * 40, destination) + assert (destination / "gaussian/summary.json").read_bytes() == ( + outputs / "gaussian/summary.json" + ).read_bytes() + with pytest.raises(ValueError, match="commit mismatch"): + pipeline.unpack(path, "b" * 40, tmp_path / "wrong") + path.write_bytes(b"existing different bundle") + with pytest.raises(ValueError, match="Refusing to replace"): + pipeline.bundle() + + +def test_unsafe_archive_rejected(tmp_path: Path) -> None: + path = tmp_path / "unsafe.tar" + with tarfile.open(path, "w") as archive: + info = tarfile.TarInfo("../escape") + info.size = 3 + archive.addfile(info, io.BytesIO(b"bad")) + with pytest.raises(ValueError, match="Unsafe archive"): + pipeline.unpack(path, "a" * 40, tmp_path / "output") + assert not (tmp_path / "escape").exists() + + +def test_latest_checkout_guard(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setenv("READTHEDOCS_VERSION", "latest") + monkeypatch.setattr( + pipeline, + "git", + lambda *args: ( + "b" * 40 + "\trefs/heads/main" + if args[0] == "ls-remote" + else "a" * 40 + ), + ) + with pytest.raises(ValueError, match="Stale latest checkout"): + pipeline.check_latest() + + +def test_missing_output_root_is_actionable(tmp_path: Path) -> None: + with pytest.raises(ValueError, match="missing example outputs:.*gaussian"): + pipeline.validate(tmp_path / "absent") + + +def test_missing_example_is_named(outputs: Path) -> None: + import shutil + + shutil.rmtree(outputs / "adaptive-mutation") + with pytest.raises( + ValueError, match="missing example outputs: adaptive-mutation" + ): + pipeline.validate(outputs) + + +def test_unexpected_output_is_named(outputs: Path) -> None: + (outputs / "stray.txt").write_text("unexpected") + with pytest.raises(ValueError, match="unexpected entries: stray.txt"): + pipeline.validate(outputs) + + +def test_prepare_preserves_narrative_and_source_paths( + outputs: Path, tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + generated = tmp_path / "generated" + monkeypatch.setattr(pipeline, "GENERATED", generated) + pipeline.prepare() + for example in pipeline.examples(): + for key in ("script", "narrative"): + relative = example[key] + assert (generated / relative).read_bytes() == ( + pipeline.ROOT / relative + ).read_bytes() + assert ( + generated / "results" / example["id"] / "summary.json" + ).is_file() + assert not (generated / "index.rst").exists() + for example in pipeline.examples(): + narrative = (generated / example["narrative"]).read_text() + for name in example["figures"]: + assert ( + f".. figure:: /_generated/results/{example['id']}/{name}" + in narrative + ) + assert "Numerical summary" not in narrative + assert "/summary.json" not in narrative + + +@pytest.mark.parametrize( + "name", + [ + "flow-preconditioning", + "persistent-sampling", + "waste-free", + "particle-filtering", + ], +) +def test_prepared_worker_without_sampling( + name: str, tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + """Exercise adapters and provenance using a fake main, never a sampler.""" + from types import SimpleNamespace + + example = pipeline.entry(name) + parameters = {key: 42 for key in example["parameters"]} + module = SimpleNamespace(**parameters) + if name in {"persistent-sampling", "particle-filtering"}: + module.build_parser = lambda: SimpleNamespace( + parse_args=lambda argv: SimpleNamespace(**parameters) + ) + original_argv = sys.argv + + def main() -> None: + assert sys.argv == [str(pipeline.ROOT / example["script"])] + for figure in example["figures"]: + (module.documentation_output / figure).write_bytes(b"mock plot") + pipeline.write_json( + module.documentation_output / "summary.json", + { + "log_evidence": -1.0, + }, + ) + + module.main = main + monkeypatch.setitem(sys.modules, "documentation_example", None) + spec = SimpleNamespace( + name="documentation_example", + loader=SimpleNamespace(exec_module=lambda module: None), + ) + monkeypatch.setattr( + pipeline.importlib.util, "spec_from_file_location", lambda *args: spec + ) + monkeypatch.setattr( + pipeline.importlib.util, "module_from_spec", lambda spec: module + ) + revisions = pipeline.git_dependencies(example) + monkeypatch.setattr( + pipeline.importlib.metadata, + "distribution", + lambda package: SimpleNamespace( + version="test-version", + read_text=lambda filename: json.dumps( + {"vcs_info": {"commit_id": revisions[package]}} + ), + ), + ) + monkeypatch.setattr(pipeline, "git", lambda *args: "a" * 40) + monkeypatch.setitem(sys.modules, "numpy", SimpleNamespace()) + destination = tmp_path / "outputs" + pipeline.worker(name, tmp_path / "raw", destination) + assert sys.argv is original_argv + metadata = json.loads((destination / "metadata.json").read_text()) + assert metadata["parameters"] == parameters + assert metadata["git_dependencies"] == revisions + for figure in example["figures"]: + assert (destination / figure).read_bytes() == b"mock plot" + assert ( + json.loads((destination / "summary.json").read_text())["log_evidence"] + == -1.0 + ) + + +def test_bundle_restores_execution_lock( + outputs: Path, tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + """RTD restores the execution snapshot even when its checkout lock differs.""" + import shutil + + monkeypatch.setattr(pipeline, "BUNDLES", tmp_path) + archive = pipeline.bundle() + checkout = tmp_path / "checkout" + source_paths = set() + for example in pipeline.examples(): + source_paths.update(pipeline.source_hashes(example)) + for relative in source_paths: + target = checkout / relative + target.parent.mkdir(parents=True, exist_ok=True) + shutil.copyfile(pipeline.ROOT / relative, target) + archived_lock = (checkout / "pixi.lock").read_bytes() + (checkout / "pixi.lock").write_bytes( + archived_lock + b"\n# Different checkout lock\n" + ) + monkeypatch.setattr(pipeline, "ROOT", checkout) + with pytest.raises(ValueError, match="execution lock differs"): + pipeline.unpack(archive, "a" * 40, tmp_path / "refused") + pipeline.unpack( + archive, "a" * 40, tmp_path / "restored", restore_lock=True + ) + assert (checkout / "pixi.lock").read_bytes() == archived_lock + + +def test_execution_batch_refreshes_once( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """All child executions share one main resolution instead of racing updates.""" + calls = [] + monkeypatch.delenv("SEQUINCE_LOCK_READY", raising=False) + monkeypatch.setattr(sys, "argv", ["docs_pipeline.py", "run-all"]) + monkeypatch.setattr(pipeline, "refresh", lambda: calls.append("refresh")) + monkeypatch.setattr( + pipeline.subprocess, + "run", + lambda command, **kwargs: calls.append(kwargs), + ) + pipeline.main() + assert calls[0] == "refresh" + assert len(calls) == len(pipeline.examples()) + 1 + assert all(call["env"]["SEQUINCE_LOCK_READY"] == "1" for call in calls[1:]) diff --git a/tests/test_flow_summary.py b/tests/test_flow_summary.py new file mode 100644 index 0000000..057cc77 --- /dev/null +++ b/tests/test_flow_summary.py @@ -0,0 +1,43 @@ +"""Check the flow example's host summary without constructing a sampler.""" + +from __future__ import annotations + +import importlib.util +from pathlib import Path + +import pytest + + +@pytest.mark.parametrize("backend", ["numpy", "torch"]) +def test_flow_documentation_summary(backend: str) -> None: + np = pytest.importorskip("numpy") + torch = pytest.importorskip("torch") + pytest.importorskip("sequince_density") + from sequince import ParticleState + from sequince.result import SMCResult + from sequince.steps import StepContext + + path = ( + Path(__file__).resolve().parents[1] + / "examples/preconditioning/flow_preconditioning.py" + ) + spec = importlib.util.spec_from_file_location("flow_example", path) + assert spec is not None and spec.loader is not None + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + particles = np.array([[0.0, 0.0], [2.0, 0.0], [0.0, 4.0]]) + log_weights = np.log([0.5, 0.25, 0.25]) + if backend == "torch": + particles = torch.as_tensor(particles) + log_weights = torch.as_tensor(log_weights) + state = ParticleState(particles, log_weights=log_weights) + result = SMCResult([state], StepContext(t=0)) + + summary = module._documentation_summary(result) + + np.testing.assert_allclose(summary["weighted_mean"], [0.5, 1.0]) + np.testing.assert_allclose( + summary["weighted_population_covariance"], + [[0.75, -0.5], [-0.5, 3.0]], + ) + assert summary["n_particles"] == 3 diff --git a/tests/test_particle_filter_docs.py b/tests/test_particle_filter_docs.py new file mode 100644 index 0000000..591f268 --- /dev/null +++ b/tests/test_particle_filter_docs.py @@ -0,0 +1,74 @@ +"""Check filtering documentation exports without running either filter.""" + +from __future__ import annotations + +import importlib.util +import json +import sys +from pathlib import Path +from types import SimpleNamespace + +import pytest + + +@pytest.mark.parametrize("documentation", [False, True]) +def test_particle_filter_outputs_without_sampling( + documentation: bool, tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + np = pytest.importorskip("numpy") + pytest.importorskip("sequince") + source = ( + Path(__file__).resolve().parents[1] + / "examples/particle_filtering/particle_filter.py" + ) + spec = importlib.util.spec_from_file_location("filter_example", source) + assert spec is not None and spec.loader is not None + module = importlib.util.module_from_spec(spec) + monkeypatch.setitem(sys.modules, spec.name, module) + spec.loader.exec_module(module) + calls = [] + comparison = SimpleNamespace( + proposal_means=np.array([1.0, 2.0]), + bootstrap_means=np.array([0.0, 3.0]), + kalman_means=np.array([1.0, 1.0]), + proposal_variances=np.array([1.0, 1.0]), + bootstrap_variances=np.array([1.0, 2.0]), + kalman_variances=np.array([1.0, 1.0]), + proposal_ess=np.array([10.0, 20.0]), + bootstrap_ess=np.array([5.0, 10.0]), + proposal_log_evidence=-1.0, + bootstrap_log_evidence=-2.0, + kalman_log_evidence=-0.5, + ) + monkeypatch.setattr( + module, + "run_comparison", + lambda **kwargs: (calls.append(kwargs) or comparison), + ) + monkeypatch.setattr( + module, + "plot_comparison", + lambda result, output: (output.write_bytes(b"mock plot")), + ) + requested = tmp_path / "custom.png" + monkeypatch.setattr(sys, "argv", [str(source), "--output", str(requested)]) + if documentation: + module.documentation_output = tmp_path + module.main() + assert calls == [ + { + "n_observations": 20, + "n_particles": 1000, + "seed": 2026, + "always_resample": True, + "ess_threshold": 0.5, + } + ] + if documentation: + assert (tmp_path / "particle_filter.png").is_file() + summary = json.loads((tmp_path / "summary.json").read_text()) + assert summary["log_evidence"]["kalman"] == -0.5 + assert summary["mean_ess_before_resampling"]["optimal"] == 15.0 + else: + assert requested.is_file() + assert not (tmp_path / "summary.json").exists() diff --git a/tests/test_publish_docs.py b/tests/test_publish_docs.py new file mode 100644 index 0000000..204d07c --- /dev/null +++ b/tests/test_publish_docs.py @@ -0,0 +1,122 @@ +"""Check publication ordering and refusal paths without external writes.""" + +from __future__ import annotations + +import json +import sys +from pathlib import Path +from types import SimpleNamespace + +import pytest + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts")) +import publish_docs as publisher # noqa: E402 + + +@pytest.fixture +def publication( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> list[tuple]: + """Replace GitHub and RTD calls with a recorded successful publication.""" + calls: list[tuple] = [] + sha = "a" * 40 + monkeypatch.setattr(publisher, "ROOT", tmp_path) + monkeypatch.setattr( + publisher, "git", lambda *args: ("" if args[0] == "diff" else sha) + ) + bundle = tmp_path / "bundle.tar.gz" + bundle.write_bytes(b"validated outputs") + Path(str(bundle) + ".sha256").write_text("checksum") + monkeypatch.setattr(publisher, "bundle", lambda: bundle) + monkeypatch.setattr( + publisher.subprocess, + "run", + lambda *args, **kwargs: (SimpleNamespace(returncode=1, stdout="")), + ) + monkeypatch.setattr( + publisher, "gh", lambda *args: calls.append(args) or "" + ) + monkeypatch.setenv("RTD_PROJECT", "examples") + + def api(path: str, method: str = "GET") -> dict: + calls.append((method, path)) + if "running=true" in path: + return {"count": 0} + if method == "POST": + return {"build": {"id": 123}} + return {"state": {"code": "finished"}, "success": True, "commit": sha} + + monkeypatch.setattr(publisher, "rtd", api) + return calls + + +def test_publish_before_trigger(publication: list[tuple]) -> None: + publisher.main() + uploaded = publication.index( + ( + "release", + "edit", + "docs-output-" + "a" * 40, + "--draft=false", + "--latest=false", + ) + ) + triggered = publication.index( + ( + "POST", + "projects/examples/versions/latest/builds/", + ) + ) + assert uploaded < triggered + assert publication[-1] == ("GET", "projects/examples/builds/123/") + + +def test_stale_run_does_not_publish( + publication: list[tuple], monkeypatch: pytest.MonkeyPatch +) -> None: + monkeypatch.setattr( + publisher, + "git", + lambda *args: ("b" * 40 if args[0] == "ls-remote" else "a" * 40), + ) + publisher.main() + assert not publication + + +def test_different_existing_asset_fails( + publication: list[tuple], tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + monkeypatch.setattr( + publisher.subprocess, + "run", + lambda *args, **kwargs: ( + SimpleNamespace( + returncode=0, + stdout=json.dumps( + { + "assets": [{"name": "bundle.tar.gz"}], + "isDraft": False, + } + ), + ) + ), + ) + (tmp_path / "build-docs").mkdir() + + def download(*args: str) -> str: + publication.append(args) + target = Path(args[args.index("--dir") + 1]) / "bundle.tar.gz" + target.write_bytes(b"different uploaded content") + return "" + + monkeypatch.setattr(publisher, "gh", download) + with pytest.raises(ValueError, match="Refusing to replace"): + publisher.main() + assert not any(call[0] == "POST" for call in publication) + + +def test_wait_idle_times_out(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr(publisher, "rtd", lambda path: {"count": 1}) + monkeypatch.setattr(publisher.time, "monotonic", lambda: 10.0) + with pytest.raises(TimeoutError, match="RTD still running"): + publisher.wait_idle("examples", 0.0)