diff --git a/.githooks/pre-commit b/.githooks/pre-commit index 4f5ee79..c73937a 100755 --- a/.githooks/pre-commit +++ b/.githooks/pre-commit @@ -12,6 +12,7 @@ # * any file larger than 512 KB # * filenames that are not snake_case # * camelCase / PascalCase identifiers in R code +# * ruff check and ruff format on Python, when ruff is on PATH # # Install: pixi run install-hooks (sets core.hooksPath to .githooks) # Lint all: pixi run lint (same checks across the whole repo, read-only) @@ -140,6 +141,27 @@ while IFS= read -r file; do rm -f "$scratch" fi + # --- Python lint and format ------------------------------------------------------------------ + # ruff, not a bespoke regex. The R check below exists because nothing else was going to + # enforce R style; Python has a linter that already knows the rules, and the same one the + # project's `pixi run format` task uses, so the hook and the task cannot disagree. + # + # Read-only here even under --fix: ruff format rewrites whole files, and a hook that silently + # reformats what you are committing makes `git diff --cached` stop describing your change. + case "$file" in + *.py) + if command -v ruff >/dev/null 2>&1; then + if ! ruff check --quiet "$file"; then + red "REJECT $file fails ruff check" + failures=$((failures + 1)) + elif ! ruff format --check --quiet "$file" >/dev/null 2>&1; then + red "REJECT $file is not ruff-formatted; run 'pixi run -e dev format'" + failures=$((failures + 1)) + fi + fi + ;; + esac + # --- R identifier convention --------------------------------------------------------------- # Flags camelCase and PascalCase assignments. Deliberately does not demand strict snake_case: # SCREAMING_SNAKE constants (PERT_LEVELS) and dotted S3 methods (print.sim_input) are correct diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..a3c5ca8 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,48 @@ +# The tests that need neither R nor a real screen, which is all of them by default: the two that +# do are behind `-m realdata` and are skipped here. +# +# Not a pixi environment. pixi resolves pysceptre from git and would rebuild the whole conda +# environment on every push; uv installs the same dependencies from pyproject.toml in seconds. What +# that gives up is checking that pixi.lock still resolves, which is what the container build does +# and which does not need to happen on every commit. + +name: tests + +on: + push: + branches: [main, 'feat/**'] + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + + - name: Install + run: | + uv venv --python 3.12 + # pysceptre is not on PyPI; the pin matches pixi.toml's and has to move with it. + uv pip install "pysceptre @ git+https://github.com/broadinstitute/pysceptre.git@v0.2.0" + uv pip install -e ".[dev]" + + - name: Lint + run: | + uv run ruff check src tests workflow + uv run ruff format --check src tests workflow + + - name: Test + run: uv run pytest -q + + - name: Nextflow DAG + # Catches a rewiring mistake without running anything. The stub needs the synthetic + # dataset, which is generated rather than committed. + run: | + curl -fsSL https://get.nextflow.io | bash + uv run watteg-make-test-data --out tests/data/synthetic.h5mu + ./nextflow run . -profile standard -params-file config/test.yml -stub-run \ + --samplesheet assets/samplesheet_synthetic.csv --outdir /tmp/stub diff --git a/.gitignore b/.gitignore index b499dae..a163eab 100644 --- a/.gitignore +++ b/.gitignore @@ -88,3 +88,7 @@ tests/data/ # Created by tests/config/run.sbatch -- sparse files whose SIZE is the fixture, 3.3 GB of nothing. tests/config/inputs/ +.venv/ +__pycache__/ +*.egg-info/ +tests/data/*.h5mu diff --git a/README.md b/README.md index 279cc05..a2de79c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # WattEG **W**atts for **E**lement–**G**ene pairs: power analysis for element–gene pairs in single-cell -CRISPR screens, built on [sceptre](https://katsevich-lab.github.io/sceptre/). +CRISPR screens, built on [pysceptre](https://github.com/broadinstitute/pysceptre) — a Python port +of [sceptre](https://katsevich-lab.github.io/sceptre/)'s statistical engine. **📖 [Documentation](https://engreitzlab.github.io/WattEG/)** @@ -22,67 +23,84 @@ written for one specific analysis. This repository generalises it. ```sh pixi install -pixi run setup # installs sceptre from a pinned commit -pixi run check-api # verifies that pin against the internals the pipeline uses ``` -sceptre is not on conda-forge or bioconda, so it is installed from a pinned commit rather than -captured in `pixi.lock`. The pipeline reads several of its unexported S4 slots, which is why the -version is pinned and checked. +That is the whole of it. The environment is one Python package plus Nextflow: no R, no pinned +sceptre commit, no patch applied to it, and no separate install step. pysceptre enters as a git +dependency pinned to a commit, because a sweep has to be re-runnable against the engine that +produced it. + +**The R implementation has not been deleted.** It is the reference the Python path was validated +against and what the methods paper describes, and it still lives in `src/*.R` and `lib/*.R`. What +is gone is the environment that ran it; to run it, use the **`r-implementation`** branch. + +That branch is **maintained, not frozen**. Two bugs found during the port change results, and both +were fixed in R as well as in Python rather than being quarantined on a snapshot -- nothing was +published, so there were no numbers owed a bit-for-bit reproduction, and freezing the branch would +only have preserved the bugs. Sweeps produced before 2026-09-21 predate both fixes. +(`legacy` is something else again -- the Snakemake implementation that preceded both.) ## Input -**One file: a sceptre object** (`.rds`) on which `assign_grnas()` and `run_qc()` have been called, -using `grna_integration_strategy = "union"`. +**One file: a `.h5mu` dataset** exported from a sceptre object on which `assign_grnas()` and +`run_qc()` have been called, using `grna_integration_strategy = "union"`. Everything else is derived from it — the discovery pairs, the gRNA-to-target mapping and the -significance threshold all already live inside the object. +significance threshold all travel with the export. + +Making one is a one-off step per dataset, run wherever R and sceptre are available. It is the only +place R appears at all, and it is not part of the pipeline: + +```sh +Rscript pysceptre/scripts/export_sceptre_dataset.R \ + --sceptre-object results/sample1/sceptre_object.rds \ + --out-dir export/ --all-genes --all-cells +python pysceptre/scripts/make_h5mu.py export/ +``` -If the object's response matrix is odm-backed (out-of-core), pass `--response-odm` on -`prepare_sim_input.R` with the path to the backing `.odm` file — see [Usage](https://engreitzlab.github.io/WattEG/usage/). +**`--all-cells` is required, not optional.** DESeq2 "poscounts" size factors are a per-cell +reduction against a per-gene geometric mean taken over every cell in the object, so an export +restricted to the QC-passing cells gives different size factors for the cells that remain. The +export handles odm-backed (out-of-core) matrices itself, which is why there is no longer an +`--response-odm` flag anywhere in the pipeline. List your samples in a CSV (see `assets/samplesheet.csv`): ```csv -sample,sceptre_object -sample1,results/sample1/sceptre_object.rds +sample,dataset +sample1,export/dataset.h5mu ``` -Add an optional `response_odm` column (see `assets/samplesheet_seqera_test.csv`) for any sample whose -sceptre object's response matrix is odm-backed — same file `prepare_sim_input.R --response-odm` takes -standalone. Leave it blank, or omit the column entirely, for in-memory-backed objects. - ## Quickstart -Each step is a standalone executable in `src/` with `--help`. +```sh +nextflow run . -profile sherlock -params-file config/config.yml +``` + +Each step is also a console script with `--help`, so a sweep can be driven by hand or by another +runner: ```sh # derive the simulation inputs (once per sample) -Rscript src/prepare_sim_input.R \ - --sceptre-object results/sample1/sceptre_object.rds \ - --outdir prepared/ +watteg-prepare-sim-input --dataset export/dataset.h5mu --outdir prepared/ # split targets into per-task chunks -Rscript src/split_pairs.R --pairs prepared/pairs.tsv --n-splits 280 --outdir splits/ +watteg-split-pairs --pairs prepared/pairs.tsv --n-splits 280 --outdir splits/ # simulate (once per split x effect size) -Rscript src/run_power_simulation.R \ - --sim-input prepared/sim_input.rds \ - --sceptre-template prepared/sceptre_template.rds \ - --pairs splits/split_001.tsv \ - --grna-targets prepared/grna_targets.tsv \ +watteg-run-power-simulation \ + --prepared prepared/ --pairs splits/split_001.tsv \ --effect-size 0.15 --reps 100 --seed 20250812 \ --out sim/split_001_es0.15.tsv # power per pair, then one table across effect sizes -Rscript src/compute_power.R \ - --simulations "$(ls sim/*_es0.15.tsv | paste -sd, -)" \ - --threshold-file prepared/discovery_threshold.txt \ +watteg-compute-power \ + --simulations sim/ --threshold-file prepared/discovery_threshold.txt \ --out power_es0.15.tsv -Rscript src/summarize_power.R \ - --power power_es0.15.tsv,power_es0.2.tsv \ - --out power_summary.tsv +watteg-summarize-power \ + --power power_es0.15.tsv power_es0.2.tsv \ + --sim-input prepared/sim_input.h5 --out power_summary.tsv ``` Parameters live in `config/config.yml`. diff --git a/assets/samplesheet.csv b/assets/samplesheet.csv index f4f5e82..481c4a4 100644 --- a/assets/samplesheet.csv +++ b/assets/samplesheet.csv @@ -1,2 +1,2 @@ -sample,sceptre_object -day0_grna20_no_shuffle,results/day0_grna20_no_shuffle/sceptre_object.rds +sample,dataset +day0_grna20_no_shuffle,results/day0_grna20_no_shuffle/dataset.h5mu diff --git a/assets/samplesheet_all_samples.csv b/assets/samplesheet_all_samples.csv index c077980..7840d3a 100644 --- a/assets/samplesheet_all_samples.csv +++ b/assets/samplesheet_all_samples.csv @@ -1,5 +1,5 @@ -sample,sceptre_object -day0_grna20,results/day0_grna20/sceptre_object.rds -day0_grna20_no_shuffle,results/day0_grna20_no_shuffle/sceptre_object.rds -day2_grna20,results/day2_grna20/sceptre_object.rds -day4_grna20,results/day4_grna20/sceptre_object.rds +sample,dataset +day0_grna20,results/day0_grna20/dataset.h5mu +day0_grna20_no_shuffle,results/day0_grna20_no_shuffle/dataset.h5mu +day2_grna20,results/day2_grna20/dataset.h5mu +day4_grna20,results/day4_grna20/dataset.h5mu diff --git a/assets/samplesheet_day0_grna20.csv b/assets/samplesheet_day0_grna20.csv index a55d1ac..e5b70c7 100644 --- a/assets/samplesheet_day0_grna20.csv +++ b/assets/samplesheet_day0_grna20.csv @@ -1,2 +1,2 @@ -sample,sceptre_object -day0_grna20,results/day0_grna20/sceptre_object.rds +sample,dataset +day0_grna20,results/day0_grna20/dataset.h5mu diff --git a/assets/samplesheet_moi5_cis.csv b/assets/samplesheet_moi5_cis.csv index a5555ee..6597638 100644 --- a/assets/samplesheet_moi5_cis.csv +++ b/assets/samplesheet_moi5_cis.csv @@ -1,2 +1,2 @@ -sample,sceptre_object,response_odm -moi5,gs://hgrm-seqera/element-gene-power-analysis-union/moi5/sceptre_object.rds,gs://hgrm-seqera/element-gene-power-analysis-union/moi5/response.odm +sample,dataset +moi5,gs://hgrm-seqera/element-gene-power-analysis-union/moi5/dataset.h5mu diff --git a/assets/samplesheet_moi5_trans.csv b/assets/samplesheet_moi5_trans.csv index 3bdf5f9..9b52bf1 100644 --- a/assets/samplesheet_moi5_trans.csv +++ b/assets/samplesheet_moi5_trans.csv @@ -1,2 +1,2 @@ -sample,sceptre_object,response_odm -moi5_trans_discovery,gs://hgrm-seqera/sceptre-nf-ondisc-test/moi5-trans-output/sceptre_object.rds,gs://hgrm-seqera/20260508-wtc11-sceptre/grna-single-target-moi5-ondisc/gene.odm +sample,dataset +moi5_trans_discovery,gs://hgrm-seqera/sceptre-nf-ondisc-test/moi5-trans-output/dataset.h5mu diff --git a/assets/samplesheet_seqera_test.csv b/assets/samplesheet_seqera_test.csv index fe6402c..5cab6ef 100644 --- a/assets/samplesheet_seqera_test.csv +++ b/assets/samplesheet_seqera_test.csv @@ -1,2 +1,2 @@ -sample,sceptre_object,response_odm -wtc11_union_odm,gs://hgrm-seqera/element-gene-power-analysis-test/sceptre_object.rds,gs://hgrm-seqera/element-gene-power-analysis-test/response.odm +sample,dataset +wtc11_union_odm,gs://hgrm-seqera/element-gene-power-analysis-test/dataset.h5mu diff --git a/assets/samplesheet_synthetic.csv b/assets/samplesheet_synthetic.csv index 0aa92c0..547a9de 100644 --- a/assets/samplesheet_synthetic.csv +++ b/assets/samplesheet_synthetic.csv @@ -1,2 +1,2 @@ -sample,sceptre_object -synthetic,tests/data/sceptre_object_high_default.rds +sample,dataset +synthetic,tests/data/synthetic.h5mu diff --git a/conf/base.config b/conf/base.config index f50dc85..2afa566 100644 --- a/conf/base.config +++ b/conf/base.config @@ -5,8 +5,8 @@ // numbers -- see docs/status.md, "Reference numbers". Do not raise them speculatively; the whole // point of having measured them is that a request is a claim about behaviour. // -// cpus is 1 everywhere on purpose: sceptre is called with parallel = FALSE and every bit of -// parallelism comes from the fan-out. Asking for more cores would idle them. +// cpus is 1 everywhere except POWER_SIMULATION, which runs one worker per CPU (see its block). +// The other steps are single-threaded, so more cores would idle. process { cpus = 1 @@ -45,95 +45,65 @@ process { time = { 30.min * task.attempt } } - withName: FIT_NULL_MODELS { - // ~20 min per simulation, covering every gene. Peak 7.6 GB measured, so 10 GB is only 1.3x - // headroom -- do not lower it. Running this at the bare 4 GB default OOM-killed 788 tasks - // on 2026-09-03; see the array_size note in conf/sherlock.config for how that happened. - memory = { 10.GB * task.attempt } - time = { 2.h * task.attempt } - array = params.array_size - } - - withName: MERGE_NULL_MODELS { - memory = { 8.GB * task.attempt } - time = { 20.min * task.attempt } - } - withName: POWER_SIMULATION { - // Median 36 min, max 65 min over ~6,500 tasks at 100 simulations and ~35 pairs per split - // (jobs 38887744 and the 2026-09-03 paper sweeps). Peak RSS median 2.27 GB, max 3.27 GB. - // Cost model: 1.140s per target + 0.5561s per pair, per replicate. + // FAST CONFIGURATION, MEASURED 2026-09-25 (docs/pysceptre-backend.md, section 13, item 4): + // a 330-pair moi5 cis task (29 targets, 100 simulations, fits from FIT_NULL_MODELS, per-pair + // counts only) at 8 workers peaked at 3.38 GB on Linux (cgroup memory.peak, forked workers, + // arm64 container) in 278 s. On macOS, where the workers are spawned and each loads its own + // inputs, the same task's process tree peaked at 9.7 GB -- a local-run number, not a cloud + // one. 8 GB is 2.4x the Linux peak and is also the smallest predefined 8-vCPU shape, so + // asking for less would buy no cheaper machine. trans tasks hold ~250 genes per target and + // have not been measured under this configuration. // - // 4 GB / 2 h, cut from 6 GB / 4 h on 2026-09-03. That is 1.2x the worst peak RSS and 1.8x - // the worst wall clock ever observed, and the reduction is a SCHEDULING fix, not a - // tightening for its own sake. Measured over 3,100 tasks on the same cluster in the same - // hours: + // Earlier, the engine: across 20 cis tasks, 0.29 s fixed per (target, simulation) call + // against 0.55 s per pair, and ~2 s of startup in a ~2,000 s task. A 1-worker cis task + // peaked at 1.1-1.3 GB. // - // | request | median submit -> start wait | - // |--------------|-----------------------------| - // | 6 GB / 4 h | 52.3 min (n=2,800) | - // | 4 GB / 2 h | 4.8 min (n=308) | + // 8 WORKERS, because the task parallelises over (target, replicate) units + // (watteg-run-power-simulation --n-jobs); 8 workers ran 5.6x faster than 1 on a laptop, + // byte-identical. At 1 CPU the request was 8 GB, so every task paid for an e2-standard-2 + // and used one of its two vCPUs. // - // An 11x reduction, because both numbers were fighting the scheduler: `owners` allocates - // 4 GB per core by default, so asking 6 GB for 1 core needs a node with spare MEMORY - // rather than a spare core -- sh_part showed 8,347 idle cores on owners while our tasks - // queued -- and Slurm backfills short jobs into gaps ahead of large reservations, so a 4 h - // request is invisible to every gap under 4 h. + // 8 GB, MEASURED: pysceptre-paper results.md, day0 (567,690 cells, 34,886 pairs) on an + // 8-core Linux VM with the core count pinned: permutations peak 5.13 GB at 8 cores against + // 2.46 GB at 1; the CRT 7.24 GB (10.2-10.3 GB in two earlier runs). moi5 has 131,055 cells, + // 4.3x fewer. 4 GB is below the 8-core permutation peak, and saves nothing: no predefined + // 8-vCPU machine has under 8 GB, and Nextflow picks the cheapest predefined shape that fits + // (1 CPU / 8 GB ran on e2-standard-2, 1 CPU / 48 GB on e2-highmem-8). e2 vCPUs are + // hyperthreads: 8 is 4 physical cores. // - // The margin is thinner than it was on purpose: the errorStrategy above retries a task - // that exceeds its predicted memory, and `* task.attempt` scales it up further. Failing 1 - // task in 3,500 and retrying it is far cheaper than making all 18,000 queue for a bigger - // slot upfront. + // An OOM (137) doubles the request on the retry. An infrastructure retry on gcb keeps it: + // google.batch.maxSpotAttempts already absorbs plain preemption, so a retry Nextflow does + // see has usually failed with a Google Batch reserved code -- 50001 (spot preemption), + // 50002 ("VM became unresponsive", workflow 9QT0dpkW7IWgO), 50003-50005, 50006 (a VM + // recreation) -- which says nothing about memory. // - // ON GCB, no machine type is pinned; Google Batch chooses a legal shape for the requested - // memory, while on Sherlock a bigger request just needs a node with spare RAM. A standard - // OOM exit (137) doubles the 4/8 GB baseline on the retry, allowing 8/16 GB requests. - // -- unlike Sherlock, where a bigger request just needs a node with spare RAM. Escalating - // for every retry is therefore wasted spend when the retry was never about memory in the - // first place: google.batch.maxSpotAttempts already retries plain spot preemption before - // Nextflow ever sees a failure, so a retry Nextflow DOES see on gcb has usually failed for - // one of Google Batch's reserved infrastructure codes -- 50001 (documented spot - // preemption), 50002 ("VM became unresponsive... host event or crash", observed directly - // 2026-09-10, workflow 9QT0dpkW7IWgO), 50003-50005 (same reserved family), 50006 ("Google - // Batch reserved error", a VM recreation, also observed 2026-09-10) -- which say nothing - // about how much memory the task asked for. An infra-only retry on gcb keeps the same - // 4 GB; anything else, on either platform, escalates as above. - // - // COERCE THE PARAMS BEFORE DOING ARITHMETIC ON THEM. A params file carries JSON, which has - // no MemoryUnit, so Seqera sends `power_simulation_memory_floor` as the String '4 GB' -- - // and `-params-file` takes precedence over these config defaults (docs/status.md, trap 3). - // `(split.size() / 1MiB) * '62.5 GB'` is Number.multiply(String), which throws - // MissingMethodException while Nextflow is submitting the first task. That aborted run - // `shrivelled_bhabha` (zqCjFyEUvTFEE, 2026-09-16) immediately after MERGE_NULL_MODELS - // without creating one POWER_SIMULATION task, and it is invisible in the task records - // because no task ever existed to fail. `mighty_euler` survived the same commit only - // because its params file did not mention these two keys, so they stayed MemoryUnit. - // Coercing through MemoryUnit(x.toString()) accepts either form, because MemoryUnit's own - // toString() round-trips. Do not remove it because "the config says 4.GB" -- what the - // config says is not what a launch sends. (This was a nested asMem() helper until - // Nextflow 26.04, whose stricter config parser rejects a closure defined inside one.) + // COERCE THE PARAM. A params file carries JSON, which has no MemoryUnit, so Seqera sends + // `power_simulation_memory` as the String '8 GB', and arithmetic on it throws while the + // first task is being submitted (run shrivelled_bhabha, zqCjFyEUvTFEE, 2026-09-16, died + // that way with no task record). MemoryUnit(x.toString()) accepts either form. + cpus = { params.power_simulation_cpus as int } memory = { - def floorMem = new nextflow.util.MemoryUnit(params.power_simulation_memory_floor.toString()) - def slopeMem = new nextflow.util.MemoryUnit(params.power_simulation_memory_slope_per_mb.toString()) + def base = new nextflow.util.MemoryUnit(params.power_simulation_memory.toString()) def infra = [50001,50002,50003,50004,50005,50006] def isInfraRetry = workflow.profile.tokenize(',').contains('gcb') && task.attempt > 1 && task.exitStatus in infra - def mib = split.size() / (1024 * 1024) - def predicted = new nextflow.util.MemoryUnit( - (floorMem.toBytes() + mib * slopeMem.toBytes()) as long) - // The step to 8 GB is what 5,000 trans tasks actually validated (peaks 4.45-5.58 GiB), - // so it stays as a minimum rather than being replaced by `predicted` -- at trans scale - // predicted is 5.85 GiB, only 1.05x the observed peak, which is too thin for the tail. - // Taking the max means the slope only ever RAISES the request, for a dataset whose - // splits are big enough to need it, and never lowers it below what has been measured. - def baseline = predicted <= floorMem ? 4.GB : [8.GB, predicted].max() def oomRetry = task.attempt > 1 && task.exitStatus == 137 - isInfraRetry ? baseline : (oomRetry ? baseline * 2 : baseline) + isInfraRetry ? base : (oomRetry ? base * 2 : base) } time = { 2.h * task.attempt } array = params.array_size } + withName: FIT_NULL_MODELS { + // MEASURED 2026-09-25 on moi5 cis (244 genes, 131,055 cells): 100 simulations took 208 s on + // 8 workers (macOS). Peak memory at 8 workers: 3.04 GB on Linux (cgroup, forked workers); + // 6.9 GB on macOS, where each spawned worker loads its own inputs. 8 GB covers both. + cpus = { params.fit_null_models_cpus as int } + memory = { 8.GB * task.attempt } + time = { 1.h * task.attempt } + } + withName: CONSOLIDATE_REPLICATES { // Holds one effect size's rows in a data frame before writing the Parquet, so its peak is // a function of the input it is handed -- NOT a constant. A flat 16 GB was measured on @@ -142,9 +112,8 @@ process { // third attempt at 48 GB, using 31.8 GiB (run mighty_euler, 2026-09-16). Two wasted // attempts and 47 minutes, for a quantity that was predictable from the staged input size. // - // See params.consolidate_memory_* in nextflow.config for the calibration. The coercion - // through MemoryUnit is because a params file sends these as Strings -- same reason as - // POWER_SIMULATION above. + // See params.consolidate_memory_* in nextflow.config for the calibration. asMem() coerces + // because a params file sends these as Strings -- same reason as POWER_SIMULATION above. memory = { def staged = simulations instanceof Collection ? simulations : [simulations] def bytes = (staged.sum { it.size() } ?: 0L) as long diff --git a/config/config.yml b/config/config.yml index 62e9709..b075924 100644 --- a/config/config.yml +++ b/config/config.yml @@ -42,14 +42,30 @@ num_replicates: 100 # three CRISPRi screens (docs/methods.md). Replaces guide_sd, which the pipeline now refuses. guide_spread_c: 0.65 +# What the power is power for (docs/methods.md, "What simulated power means"). 'fixed': an element +# whose effect IS the effect size, the guides' cell-weighted mean pinned to it in every simulation. +# 'random': an element whose effect is the effect size on average (the question PerturbPlan asks +# with fold_change_sd = c * es * (1 - es)). Identical at effect size 0. Written into every output. +estimand: fixed + +# The simulation's configuration: the fast one, the default since 2026-09-25, each part measured +# before it was adopted (docs/pysceptre-backend.md, section 13; docs/methods.md). +# permutations per-target: one permutation set per target, drawn from the seed (per-replicate: +# a fresh set per simulation, engine only) +# nulls sparse: pysceptre's draw-matrix route, identical results (scan: its default) +# driver fast: a target's simulations tested together, byte-identical to the engine +# null_fits reuse: each gene's null model fitted once per simulation (FIT_NULL_MODELS) and +# shared across targets (refit: per target, exactly; what driver engine needs) +# The fast driver runs the permutation test only: a CRT screen needs driver engine, null_fits refit. +permutations: per-target +nulls: sparse +driver: fast +null_fits: reuse + # Parallelisation only; does not affect results. Seeds derive from # (seed, target, rep, effect_size), so output is invariant to the split layout. n_splits: 1000 -# Simulations per null-model task (step 02b). One fit covers every gene on one simulation in ~20 -# minutes, so 1 per task keeps the whole set at ~20 minutes rather than ~33 hours serial. -reps_per_null_chunk: 1 - # Simulations per simulation task. Set equal to num_replicates for no chunking, which is what every # run so far has done and what the measured cost model assumes. # @@ -59,6 +75,13 @@ reps_per_null_chunk: 1 # prefer raising n_splits over lowering this. See docs/status.md, "Decisions already made". reps_per_chunk: 100 +# Keep one row per (pair, simulation), published as a Parquet per effect size. Off by default: +# each simulation task holds all simulations of its pairs and writes per-pair counts, and +# COMPUTE_POWER builds the same power table from them (docs/output.md). Turn it on for a sweep you +# will want to re-analyse by simulation (subsampling, bootstrapping, a new threshold). Implied when +# reps_per_chunk < num_replicates. +keep_per_simulation: false + # Base RNG seed. seed: 20250812 @@ -69,7 +92,3 @@ conf_level: 0.95 # Optional, unset by default. # alpha: 0.05 # p-value threshold; otherwise derived from the sceptre object -# -# n_control_cells and cell_batches are NOT accepted by the pipeline: setting either stops the run -# at startup. Sampling controls costs 21-60% of power (docs/methods.md); to experiment with it, call -# src/run_power_simulation.R by hand. diff --git a/config/test.yml b/config/test.yml index 4a3bb5b..47c5f4b 100644 --- a/config/test.yml +++ b/config/test.yml @@ -45,8 +45,12 @@ n_splits: 4 test_max_splits: 3 reps_per_chunk: 2 -reps_per_null_chunk: 2 -test_max_null_reps: 2 + +# The synthetic object is a CRT screen, and the fast driver (the default) runs the permutation test +# only; so this runs the engine, which refits every gene itself. A stub run checks the default +# DAG, FIT_NULL_MODELS included, with: --driver fast --null_fits reuse. +driver: engine +null_fits: refit power_threshold: 0.8 conf_level: 0.95 diff --git a/containers/Dockerfile b/containers/Dockerfile index 9b7138a..e44baed 100644 --- a/containers/Dockerfile +++ b/containers/Dockerfile @@ -1,79 +1,52 @@ # Image for running WattEG on a cloud executor (the `gcb` profile). # -# /opt/pipeline is load-bearing. conf/gcb.config's beforeScript symlinks each task's ${projectDir} -# here; without it `pixi run` finds a manifest with no environment beside it and every task dies -# with "pixi: command not found". Change it here and change it there. +# - The environment is baked in: every task runs `pixi run --frozen ...`, and solving per task would +# repeat minutes of work thousands of times. +# - /opt/pipeline is load-bearing: conf/gcb.config symlinks each task's ${projectDir} to it. Change +# it here and change it there. +# - The pipeline is cloned at WATTEG_REF. ALWAYS pass a commit SHA; `main` makes the image silently +# non-reproducible. Rebuild whenever pixi.lock changes, including the pysceptre pin. +# - pysceptre's scripts/ (sceptre_io.py, which watteg-prepare-sim-input and make_test_data import) +# is not shipped in its wheel, so it is fetched at the commit pinned in pixi.lock and put on +# PYTHONPATH. Without it every run fails at PREPARE_SIM_INPUT with "No module named sceptre_io". +# - Keep this file under ~7 KB: Wave rejects a containerfile over 10,000 bytes base64-encoded. # -# The environment is baked in because every task shells out to `pixi run --frozen`, and because it -# cannot be rebuilt from a conda spec alone: sceptre and ondisc install from pinned commits and -# compile C++. `pixi run setup` is what does that. -# -# BUILD: -# wave --containerfile containers/Dockerfile --await -# docker build --build-arg WATTEG_REF= -t /watteg: -f containers/Dockerfile . -# -# ALWAYS PASS A COMMIT SHA. The default is `main`, which makes the image non-reproducible. -# REBUILD WHENEVER pixi.lock CHANGES -- nothing enforces it, and a stale image fails at the first -# task needing the new package with an R error about a missing library. -# -# Keep this file under ~7 KB: Wave rejects a containerfile over 10,000 bytes base64-encoded. -# -# Two stages, not one: a `RUN rm -rf` in a later layer records the deletion but still ships the -# bytes. Measured -- single stage 2.14 GiB, two stages 0.68 GiB. +# wave --containerfile containers/Dockerfile --build-arg WATTEG_REF= --await FROM ubuntu:22.04 AS builder - ENV DEBIAN_FRONTEND=noninteractive - -# ca-certificates: stock ubuntu:22.04 ships none, so every HTTPS call fails. RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates curl bzip2 git \ && rm -rf /var/lib/apt/lists/* - -# Pinned: an older pixi cannot read a newer lockfile format. ARG PIXI_VERSION=0.76.2 RUN curl -fsSL https://pixi.sh/install.sh | PIXI_VERSION="v${PIXI_VERSION}" PIXI_NO_PATH_UPDATE=1 bash ENV PATH="/root/.pixi/bin:${PATH}" - ARG WATTEG_REF=main RUN git clone https://github.com/EngreitzLab/WattEG.git /opt/pipeline \ && cd /opt/pipeline \ && git checkout "${WATTEG_REF}" \ && git rev-parse HEAD > /opt/pipeline/.image-built-from - WORKDIR /opt/pipeline - -# Mirrors workflow/slurm_executor/00_setup_env.sbatch: the two must not drift, or a cloud run and a -# Sherlock run stop being comparable. -RUN pixi install --frozen \ - && pixi install --frozen --environment build \ - && pixi run --frozen setup \ - && pixi run --frozen check-api - -RUN rm -rf /opt/pipeline/.pixi/envs/build /root/.cache/rattler /root/.cache/pixi /root/.pixi/cache - -# ---- runtime ------------------------------------------------------------------------------- -# Same base and the SAME PATHS: conda environments embed absolute paths. +RUN pixi install --frozen +RUN rev=$(grep -o 'pysceptre.git?tag=[^#]*#[0-9a-f]*' pixi.lock | head -1 | sed 's/.*#//') \ + && test -n "${rev}" \ + && git clone https://github.com/broadinstitute/pysceptre.git /opt/pysceptre \ + && git -C /opt/pysceptre checkout "${rev}" +RUN rm -rf /root/.cache/rattler /root/.cache/pixi /root/.cache/uv /root/.pixi/cache FROM ubuntu:22.04 - ENV DEBIAN_FRONTEND=noninteractive - RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ && rm -rf /var/lib/apt/lists/* - -# pixi comes too: process scripts invoke `pixi run` themselves. COPY --from=builder /root/.pixi/bin/pixi /root/.pixi/bin/pixi COPY --from=builder /opt/pipeline /opt/pipeline - +COPY --from=builder /opt/pysceptre/scripts /opt/pysceptre-scripts ENV PATH="/root/.pixi/bin:${PATH}" +ENV PYTHONPATH=/opt/pysceptre-scripts WORKDIR /opt/pipeline - -# Fail the BUILD rather than the first task of a 5,000-task sweep. -RUN pixi run --frozen check-api \ +RUN pixi run --frozen python -c "import watteg, pysceptre, sceptre_io; from pysceptre.pipeline.discovery import run_discovery_ntcells_complement; print('watteg', watteg.__version__)" \ + && pixi run --frozen watteg-prepare-sim-input --help > /dev/null \ && echo "built from $(cat /opt/pipeline/.image-built-from)" - ENV WATTEG_ROOT=/opt/pipeline - CMD ["/bin/bash"] diff --git a/docs/development.md b/docs/development.md index 96676f3..2e1a93c 100644 --- a/docs/development.md +++ b/docs/development.md @@ -7,23 +7,30 @@ nav_order: 6 ## Environment -[pixi](https://pixi.sh) manages everything except sceptre. +[pixi](https://pixi.sh) manages everything. ```sh -pixi install # runtime environment from pixi.lock -pixi run setup # install sceptre from the pinned commit -pixi run check-api # verify the pin against the internals this pipeline uses -pixi run lint # formatting and convention checks (read-only) +pixi install # the environment, from pixi.lock +pixi run -e dev test # the fast tests +pixi run -e dev format # ruff format + ruff check --fix +pixi run pipeline-test # a stub run of the Nextflow DAG +pixi run lint # the pre-commit checks, read-only pixi run install-hooks ``` -Three environments, so the one every task activates stays small: +Two environments: `default`, which every task activates, and `dev`, which adds pytest and ruff. -| Environment | Contains | Why separate | -|---|---|---| -| default | `r-base`, `r-optparse`, `nextflow`, and the packages sceptre reaches on our code path (`r-matrix`, `r-rcpp`, `r-dplyr`, `r-data.table`, `r-purrr`, `r-crayon`, `r-parallelly`, `r-withr`) | activated by every task | -| `build` | `r-remotes`, compilers, `r-ggplot2`, `r-cowplot`, `r-scales`, `r-bh` | only needed to compile sceptre | -| `dev` | `r-testthat` | only needed to run tests | +**There is no longer a `build` environment, a `setup` task or a `check-api` task.** Those existed +because sceptre was installed from a pinned commit with a local patch and compiled C++, ondisc from +another pinned commit, and the pipeline read several of sceptre's unexported S4 slots so the pin had +to be verified against them. None of that survives the Python port: pysceptre is a wheel built from +a git dependency pinned to a commit, the pipeline calls its documented entry points, and a version +bump is caught by this repository's own tests rather than by a bespoke checker. + +The sections below describe the R implementation, which still exists in `src/*.R` and `lib/*.R` and +is the reference the Python path was validated against. The environment that ran it is on the +**`r-implementation`** branch, an exact snapshot of the pipeline as it stood before the port. +(`legacy` is a different thing again: the Snakemake implementation that preceded both.) ### Why `ggplot2` is a build-only dependency diff --git a/docs/index.md b/docs/index.md index 18d6d90..f0a6dda 100644 --- a/docs/index.md +++ b/docs/index.md @@ -35,56 +35,48 @@ proportion over a finite number of simulations, every estimate is reported with ## Quickstart ```sh -# 1. environment (sceptre is installed from a pinned commit, not from a conda channel) +# 1. environment -- one Python package plus Nextflow, no R pixi install -pixi run setup -pixi run check-api # asserts the pinned sceptre exposes the internals this pipeline uses - -# 2. derive the simulation inputs from your sceptre object -Rscript src/prepare_sim_input.R \ - --sceptre-object results/sample1/sceptre_object.rds \ - --outdir prepared/ - -# 3. split the pairs into per-task chunks -Rscript src/split_pairs.R --pairs prepared/pairs.tsv --n-splits 280 --outdir splits/ - -# 4. simulate one chunk (repeat per split and per effect size) -Rscript src/run_power_simulation.R \ - --sim-input prepared/sim_input.rds \ - --sceptre-template prepared/sceptre_template.rds \ - --pairs splits/split_001.tsv \ - --grna-targets prepared/grna_targets.tsv \ + +# 2. the whole pipeline +nextflow run . -profile sherlock -params-file config/config.yml +``` + +Or step by step, each with `--help`: + +```sh +watteg-prepare-sim-input --dataset export/dataset.h5mu --outdir prepared/ +watteg-split-pairs --pairs prepared/pairs.tsv --n-splits 280 --outdir splits/ +watteg-run-power-simulation \ + --prepared prepared/ --pairs splits/split_001.tsv \ --effect-size 0.15 --reps 100 --seed 20250812 \ --out sim/split_001_es0.15.tsv - -# 5. power per pair, then one table across effect sizes -Rscript src/compute_power.R --simulations "$(ls sim/*_es0.15.tsv | paste -sd, -)" \ +watteg-compute-power --simulations sim/ \ --threshold-file prepared/discovery_threshold.txt --out power_es0.15.tsv -Rscript src/summarize_power.R --power power_es0.15.tsv,power_es0.2.tsv --out power_summary.tsv +watteg-summarize-power --power power_es0.15.tsv power_es0.2.tsv \ + --sim-input prepared/sim_input.h5 --out power_summary.tsv ``` -Every script is standalone and self-documenting via `--help`. See -[Usage]({{ site.baseurl }}{% link usage.md %}) for all parameters. +See [Usage]({{ site.baseurl }}{% link usage.md %}) for all parameters, including how to produce the +`.h5mu` from a sceptre object. ## Input -**One file: a sceptre object** on which `assign_grnas()` and `run_qc()` have been called, using -`grna_integration_strategy = "union"`. - -Everything else is derived from it. Earlier versions of this pipeline additionally required -`gene_grna_group_pairs.rds`, `grna_groups_table.rds` and a discovery-results file; all three -duplicated data already present in the object: +**One file: a `.h5mu`**, exported once from a sceptre object on which `assign_grnas()` and +`run_qc()` have been called, using `grna_integration_strategy = "union"`. Everything else — the +discovery pairs, the gRNA-to-target mapping, the significance threshold, the analysis parameters — +travels with it. -| Previously a separate input | Read instead from | -|---|---| -| `gene_grna_group_pairs.rds` | `@discovery_pairs_with_info` (which also carries `pass_qc`) | -| `grna_groups_table.rds` | `@grna_target_data_frame` | -| discovery results | `@discovery_result` | +Producing that export is the only step that needs R, and it is not part of the pipeline; see +[Usage]({{ site.baseurl }}{% link usage.md %}). It must be written with `--all-genes --all-cells`, +for reasons that are not cosmetic: the size factors reduce over the whole gene set, and are +computed against a geometric mean over every cell including the ones QC removed. ## Choosing parameters The one parameter that changes your *results* is `num_replicates`; `n_splits` and -`reps_per_chunk` only change how the work is divided. Read +`reps_per_chunk` only change how the work is divided, and that is checked end to end rather than +assumed — see Reproducibility in [Usage]({{ site.baseurl }}{% link usage.md %}). Read [Choosing num_replicates]({{ site.baseurl }}{% link choosing-num-replicates.md %}) before picking one — the right value depends on whether you report aggregate power, per-pair power, or make per-pair decisions at a cutoff. @@ -92,10 +84,16 @@ make per-pair decisions at a cutoff. Two settings deserve a warning, both documented in [Methods]({{ site.baseurl }}{% link methods.md %}): -- **`n_control_cells`** looks like a large speedup but biases power downward substantially - (measured: 29% relative loss at 5,000 controls). Leave it unset. - **`alpha`** should normally be left unset so the threshold is derived from the real discovery results, which reflects the multiple-testing correction actually applied. +- **`expression_model`** should be left at `fitted`. `size_factor` exists only to reproduce sweeps + produced before 2026-09-21; it mixes two statistical models of the same data and reproduces + 86.5% of the observed count variance against the fitted model's 99.5%. + +`n_control_cells` and `cell_batches` are gone. They sampled control cells to buy speed, cost 21-60% +of power, and were never on; the Python path is fast enough that the trade has no upside. See +[Plan - pysceptre backend]({{ site.baseurl }}{% link pysceptre-backend.md %}) section 9 for why +dropping the batch stratification does not expose the arms to drift. ## Documentation diff --git a/docs/methods.md b/docs/methods.md index ab48fa5..44f3c84 100644 --- a/docs/methods.md +++ b/docs/methods.md @@ -37,16 +37,36 @@ before, which cost several regenerations: - WattEG inherited the same order. On 2026-09-21 (`2b76284`) the reorder was moved before the centring. That fixed a real indexing bug and, with it, silently changed which quantity was simulated. -- The alternative, random guide effects, is *expected* power averaged over a prior on the - knockdown. It is a different quantity, and as the draws are parameterised it does not even start - at α: at effect size 0 a target's guides still move expression, so a null element on a highly - expressed gene is "detected" around 20% of the time. If uncertainty about guide efficiency is - wanted, it belongs in a separately labelled output with a multiplicative efficiency model. - -Two consequences to accept knowingly. WattEG does not reproduce the published DC-TAP per-pair power, -which is the other quantity. And the guides' spread barely moves power at effect sizes up to 0.5, -because spread around a pinned mean adds little variance to what the test sees: on moi5, no pair's -power moves by more than 0.02 between the spread described below and no spread at all. +- The alternative, random guide effects, is *expected* power averaged over the element's realised + knockdown. It is a different quantity, and with the old absolute spread it did not even start at + α: at effect size 0 a target's guides still moved expression, so a null element on a highly + expressed gene was "detected" around 20% of the time. + +Two consequences to accept knowingly. The fixed estimand does not reproduce the published DC-TAP +per-pair power, which is the other quantity. And the guides' spread barely moves power at effect +sizes up to 0.5, because spread around a pinned mean adds little variance to what the test sees: on +moi5, no pair's power moves by more than 0.02 between the spread described below and no spread at +all. + +### The random estimand, as an option + +`--estimand fixed | random` (Nextflow `estimand`), default `fixed`, added 2026-09-25. It became +safe to offer once the guide spread vanished at effect size 0 (next section): the null is now the +same simulation under both. + +| | `fixed` (default) | `random` | +|---|---|---| +| each guide's knockdown | Beta, mean es, sd `c * es * (1 - es)` | the same draw | +| the element's realised mean over its perturbed cells | pinned to es in every simulation | left where the draws put it | +| question answered | power for an element whose effect *is* es | power for an element whose effect is es *on average* | +| PerturbPlan setting that asks the same | `fold_change_sd = 0` | `fold_change_sd = c * es * (1 - es)` (0.0829 at es 0.15) | + +Under `random` the realised mean varies between simulations with sd +`c * es * (1 - es) * sqrt(sum n_g^2) / sum n_g` over the guides' perturbed-cell counts `n_g`: the +variance of a cell-weighted mean. Both estimands draw the same guide effects from the same stream; +`random` only skips the pin, and control cells are exactly 1 under both. The estimand is written +into every per-simulation row, the power table and the summary, and the power steps refuse input +that mixes them. It is implemented in the Python path only; the R reference has not had it added. ## Guide-to-guide variability @@ -94,12 +114,12 @@ to the original DC-TAP code. That counted the noise twice: a gene with theta 146 own simulated null data with theta 42, and power was understated for highly expressed, low-dispersion genes. -**The perturbed cells' mean is then pinned** to the requested relative expression, gene by gene: the -perturbed block is shifted so its row mean equals `1 - effect_size` exactly. This step is what makes -the effect *fixed* (previous section). It is not a correction for clamping, which is what this page -used to say. Because the draw's mean is already `1 - es`, the pin only removes one replicate's -sampling wobble. At strong knockdowns a plain shift could push some guides below zero, so the shift -is solved for exactly instead: the result is `max(v + c, 0)` for the one constant `c` that puts the +**Under the fixed estimand, the perturbed cells' mean is then pinned** to the requested relative +expression, gene by gene: the perturbed block is shifted so its row mean equals `1 - effect_size` +exactly. This step is what makes the effect *fixed* (previous section). It is not a correction for +clamping, which is what this page used to say. Because the draw's mean is already `1 - es`, the pin +only removes one simulation's sampling wobble. At strong knockdowns a plain shift could push some +guides below zero, so the shift is solved for exactly instead: the result is `max(v + c, 0)` for the one constant `c` that puts the mean on the target, which always exists for an effect size below 1. (An earlier version shifted, clamped and repeated; once most cells clamp that converges slowly, and at effect size ≥ 0.99 it ran out of iterations and stopped the run on a pin that exists.) @@ -112,45 +132,80 @@ cells carry more than one of their target's guides on moi5, so the choice barely ## Simulating counts -For each simulation, for gene *i* and cell *j*: +For each replicate, for gene *i* and cell *j*: ``` -mu[i, j] = exp(X[j, ] · beta[i, ]) * effect_size[i, j] -count[i, j] ~ NegBinomial(mu = mu[i, j], size = 1 / dispersion[i]) +mu[i, j] = baseline[i, j] * effect_size[i, j] +baseline[i, j] = exp(X[j] . beta[i]) +count[i, j] ~ NegBinomial(mu = mu[i, j], size = theta[i]) ``` -where - -- **`exp(X[j, ] · beta[i, ])`** is the expected count sceptre's own null model gives cell *j* for - gene *i*: `X` is the cell's covariate row (library size, detected genes, batch, ...) and `beta` - the gene's fitted coefficients. The simulation and the test that judges it are on one scale by - construction. It is the default, `--expression-model fitted`, since 2026-09-21. -- **`dispersion[i]`** is `1 / theta` from sceptre's own negative-binomial fit - (`@response_precomputations`), so the simulation inherits sceptre's dispersion estimates rather - than refitting them. - -`--expression-model size_factor` keeps the older `mean[i] * size_factor[j]` (a size-factor-normalised -mean times a DESeq2-style *poscounts* size factor). It exists only to reproduce sweeps made with it: -it mixes two models, runs about 4% low, and reproduces 86.5% of the observed count variance against -the fitted model's 99.5%. - -Each cell keeps **its own** covariates and, under `size_factor`, its own size factor. The original -code (DC-TAP, from 2025-04-11) shuffled the size factors across cells in every replicate, on the -reasoning that simulated library sizes should be a draw from the observed distribution rather than -tied to each cell's identity. That is incorrect here: `effect_size[i, j]` is indexed by cell, so -shuffling pairs one cell's perturbation status with a different cell's library size, and the -covariates the test adjusts for with yet another cell's. Before 2025-04-11 it applied no size -factors at all. WattEG removed the shuffle in `14c28b6`. - -Genes with no cached precomputation are a hard error rather than a silent skip. The previous +where `X` is the screen's covariate matrix and `beta[i]`, `theta[i]` come from **one fit** — the +Poisson GLM plus negative-binomial theta that sceptre's own `perform_response_precomputation` +performs, and that the null model of every discovery test is built from. Reproduced with pysceptre, +those agree with sceptre's cached values to about ten significant digits: theta to 2.8e-12 at the +median, and `exp(X·β)` to 9.2e-10 across 134.5 million gene × cell values. + +**The point is that the baseline is the test's own model.** A power analysis asks whether the +screen's test would have detected an effect, so the counts it is shown should be counts from the +model that test assumes. Taking both the level and the noise from one fit is what makes that true +by construction rather than by coincidence. + +**Each cell keeps its own covariates**, and so its own library size. The original code (DC-TAP, +from 2025-04-11) shuffled the size factors across cells in every replicate, on the reasoning that +simulated library sizes should be a draw from the observed distribution. That is incorrect here: +the effect size is indexed by cell, so shuffling pairs one cell's perturbation status with another +cell's library size, and the covariates the test adjusts for with yet another cell's. Before +2025-04-11 it applied no size factors at all. WattEG removed the shuffle in `14c28b6`. The fitted +baseline cannot reintroduce it: each cell's expected count is computed from that cell's own +covariate row. + +### What this replaced, and why + +Until 2026-09-21 the baseline was `mean[i] * size_factor[j]`: a size-factor-normalised gene mean +scaled by the cell's DESeq2 *poscounts* factor. Every sweep run before then used it, and +`--expression-model size_factor` still reproduces it. Three things were wrong with it, in +increasing order of weight. + +**It mixed two models.** The dispersion came from sceptre's negative binomial and the expression +level from a DESeq2 normalisation — one simulated gene, two statistical models of the same data. + +**It got the level wrong**, and the error survived the size factor. `mean[i]` sits 16 % below the +mean sceptre's model implies; multiplying by the cell's factor recovers most of that and leaves the +simulated genes about 4 % low on day0. The residual is a dropped covariance term: `mean[i]` is a +*mean of ratios*, and `E[x·sf] = E[x]E[sf] + Cov(x, sf)`. + +**It got the shape wrong, which the level hides.** sceptre's mean varies with every covariate — +library size, detected genes, batch, replicate — while `mean[i] * size_factor[j]` varies with a +single scalar per cell. Measured against the real counts on day0, over 60 genes and 567,690 cells: + +| | `mean[i] * size_factor[j]` | `exp(X·β)` | +|---|---:|---:| +| zero fraction, mean absolute error | 0.0053 | **0.0007** | +| predicted variance / observed | 0.865 | **0.995** | + +Note the variance error pulls the **opposite** way from the level error — less variance inflates +power where less expression deflates it — so which way the change moves simulated power is not +obvious and has not been measured. + +### Two guards that are not tidiness + +A gene with no fitted model is a hard error rather than a silent skip. The pre-refactor implementation stored dispersions in a list column with `NULL` holes; `unlist()` dropped them, -shortening the vector, and the negative-binomial draw then recycled it — so every gene after the -first gap would have been simulated with another gene's dispersion, with no warning. +shortening the vector, and the negative-binomial draw recycled it — so every gene after the first +gap was simulated with another gene's dispersion, with no warning. + +A theta clamped to the estimator's bounds `[0.01, 1000]` is **kept at the bound, with a +warning**, as sceptre keeps it. It used to be refused. That stopped the moi5 cis sweep on one gene +(mean 0.0085 counts per cell, theta at 0.01), and it would have dropped that gene's pairs while +the R implementation kept them. sceptre's own test uses the same clamped value in its null model, +so simulating from it keeps the simulation on the test's model. A gene that sparse has essentially +no power at any theta. ## Deciding whether a simulation "detects" the pair -The simulated counts are handed to sceptre's `run_discovery_analysis()` with the pair table narrowed -to the target under test, and a simulation counts as a detection when +The simulated counts are tested with the screen's own test, target by target (next section), +and a simulation counts as a detection when ``` p_value < threshold AND log_2_fold_change < 0 @@ -161,12 +216,99 @@ discovery analysis**, read from `@discovery_result`. Using the empirical thresho `alpha` matters: it encodes the correction actually applied to your data, at your number of tests. `--alpha` exists only for objects that have no discovery results. -At effect size 0 (the null arm) the same rule measures the rate of false calls **in the knockdown +At effect size 0 (no simulated effect) the same rule measures the rate of false calls **in the knockdown direction**. That is α for a left-sided test, but only about α/2 for a two-sided one, because half of the two-sided false calls have a positive fold change. +## Running the screen's test on each simulation + +Each simulation runs the screen's actual test on its actual cells, covariates, guide assignment and +threshold, including sceptre's permutation test with its escalation through three stages (pysceptre, +with the screen's own `B1`/`B2`/`B3` and side). The four settings below decide how that is laid out. +Each was measured before it became the default (2026-09-25; `docs/pysceptre-backend.md`, section 13). +Three of them change no result at all; the last is an approximation, and says so. + +### One permutation set per target (`--permutations per-target`) + +All simulations of a target are tested against one permutation set, drawn from a stream keyed on +(seed, target, effect size) and never on the simulation. That is what sceptre itself does: its +sampler reseeds `mt19937(4)` on every call (pinned commit 3ba046b), so the R implementation's +simulations already shared one fixed set, as the real screen's targets did. A per-target draw from +the run's seed was chosen over sceptre's fixed set so that targets of the same size do not all share +one draw. The simulated counts are the same either way; the p-values move by the Monte Carlo error +of the permutation test. `--permutations per-replicate`, one fresh set per simulation, was the +behaviour until 2026-09-25 and remains available with the engine. + +### How the permutation nulls are computed (`--nulls sparse`) + +pysceptre has two routes to a stage's null statistics. Its default for permutations, a prefix scan, +gathers a (B, n_trt, 14) array and takes a running sum over it, so that one set of draws can serve +many targets of different sizes. A simulation's call holds one target, so the running sum is thrown +away but for one column (227 MB at stage 2 for a 406-cell target). The sparse route multiplies a +sparse indicator matrix by the gene's pieces; pysceptre already takes it whenever the scan would be +too large. WattEG selects it through pysceptre's own documented signal, without changing +pysceptre. The p-values, z-statistics and stages were identical on 3,750 pair-tests, and every +output file is byte-identical; a stage-2 null went from 119.5 to 17.7 ms. + +### Testing a target's simulations together (`--driver fast`) + +The engine calls pysceptre once per (target, simulation), and each call redraws the same +permutations, rebuilds the same matrices and outer products, and computes each gene's pieces twice. +The fast driver does that work once per target and runs pysceptre's own per-gene steps +(`run_low_level_test_full`, its draw-matrix route, its fits) for every simulation. Its output is the +engine's under `per-target` and `sparse`, byte for byte, which the tests check against the engine on +every run. + +### Null-model fits + +sceptre's test needs each gene's null model: the Poisson GLM on the covariates plus the +negative-binomial theta, fitted to all cells. In the real screen each gene is fitted once and serves +the ~135 targets it is paired with. A simulation can do the same or refit, and the two are options: + +- **`--null-fits reuse`**, the default (R's `FIT_NULL_MODELS` approximation). Once per (gene, simulation), the + gene's counts are drawn with no knockdown, from their own stream keyed + `(seed, "__null_fit__|" + gene, simulation, 0)`, and pysceptre's own gene fit is run on them. Every + target the gene is tested with in that simulation, at every effect size, uses that fit; the score + and the permutations still come from the simulation's own counts. `FIT_NULL_MODELS` makes the fits + once per sample; a task given no file makes the same fits for its own genes. +- **`--null-fits refit`**. Each simulation refits every gene on its own counts, once per target, + as sceptre would if the simulated screen were the real one. + +Reuse is not exact, and it does not need to be: a target perturbs a few hundred of 131,055 cells, so +its knockdown barely moves a fit made on all of them. Measured on the moi5 reference targets with +the same counts and permutations (cis: 12 pairs x 100 simulations; trans: 255 pairs x 10): + +| | refit | reuse | flips | McNemar p | largest per-pair change in power | +|---|---:|---:|---:|---:|---:| +| cis calls | 808 | 805 | 5 / 2 | 0.45 | 0.02 | +| trans calls | 1,280 | 1,282 | 3 / 5 | 0.73 | 0.10 (one simulation of ten) | + +No pair moved by more than its own Wilson half-width, and p-values moved by a median of 0.04 in +log10 (an earlier benchmark put that at a quarter of what a change of permutation seed does). On the +fast driver the simulation's worker time fell 1.6-1.7x with the fits reused, the fit step excluded. + +A fit depends on (seed, gene, simulation) and nothing else — each gene's baseline and draw are made +alone and pysceptre fits one gene at a time — so no result depends on which genes or targets share +a task, and a file made by `watteg-fit-null-models` gives the same bytes as fits made in the task. +The file records the seed, the baseline model and a digest of `sim_input.h5`, and a simulation run +that does not match all three refuses it. + ## Why control-cell sampling is not used +**The flags are gone.** `n_control_cells` and `cell_batches` were removed with the Python port: +they bought speed at a measured 21–60 % of power, were off by default, and the port is the reason +speed stopped being the binding constraint. A knob that trades power for speed you no longer need +is a trap rather than an option. The measurement below is why, and is kept because the reasoning +would otherwise have to be rediscovered by whoever proposes the optimisation next. + +**On dropping the batch stratification with it**, which is the part that sounds risky. +`cell_batches` made the control draw keep the perturbed cells' batch composition, and without a +draw there is nothing to stratify — the control group is every non-perturbed cell, so its +composition is the dataset's. Batch is also conditioned on twice by the test itself: it is a +covariate of the per-gene model, and of the logistic fit the CRT draws its synthetic treated sets +from, so the null distribution is conditional on batch by construction. Cell-level matching is what +you reach for when the model cannot adjust for a confounder. + Using all non-perturbed cells as controls is expensive — a typical target has a few hundred perturbed cells against several hundred thousand controls, and every simulation simulates all of them. Sampling controls is the obvious optimisation, and it does not work. diff --git a/docs/output.md b/docs/output.md index 8d47143..7d61251 100644 --- a/docs/output.md +++ b/docs/output.md @@ -13,10 +13,10 @@ One row per element–gene pair, one set of columns per effect size. |---|---| | `grna_target` | Perturbation target (element), e.g. `chr2:201986246-201986547`. | | `response_id` | Gene. | -| `mean_pert_cells` | Mean number of perturbed cells across replicates. | +| `mean_pert_cells` | Mean number of perturbed cells across simulations. | | `average_expression_all_cells` | Raw mean expression of the gene across all cells. Not size-factor normalised — see the note below. | | `gene_mean` | The gene's size-factor-normalised mean (`mu`). Present only when `--sim-input` was supplied. **Not the same as `average_expression_all_cells`**: they correlate at r = 0.9999 but differ by a scale factor, and `mu` is the one the theory below uses. | -| `dispersion` | The gene's negative-binomial dispersion, **`1/theta`** rather than `theta` — the same convention `sim_input.rds` and `rnbinom(size = 1/dispersion)` use. Present only when `--sim-input` was supplied. | +| `dispersion` | The gene's negative-binomial dispersion, **`1/theta`** rather than `theta` — the same convention `sim_input.h5` and `NegBinomial(size = 1/dispersion)` use. Present only when `--sim-input` was supplied. | | `power_at_effect_size_15` | Power at a 15% knockdown. One column per effect size; the suffix is `effect_size × 100`, with any decimal point written as an underscore (0.125 → `power_at_effect_size_12_5`). | | `power_at_effect_size_15_ci_low`, `_ci_high` | 95% Wilson interval for that estimate. | | `power_at_effect_size_15_n_reps` | Simulations contributing to it. | @@ -24,6 +24,7 @@ One row per element–gene pair, one set of columns per effect size. | `min_detectable_effect_size_ci_low` | Optimistic edge, from `power_ci_high`. | | `min_detectable_effect_size_ci_high` | **Conservative edge, from `power_ci_low` — the column to use when interpreting a negative.** | | `max_effect_size_tested` | The largest effect size in the run, so `NA` above can be interpreted. | +| `estimand` | `fixed` or `random`, the question every column answers. The summary refuses inputs that mix them. | `NA` in any of the three is a statement about the effect sizes you ran, **not** evidence that a pair is undetectable. If you tested 0.15 and 0.2 and a pair needs 0.4, it will be `NA`. That is why @@ -56,12 +57,12 @@ SE^2 ~ (1 / n_pert_cells) * (1 / mu + 1 / theta) so anything that models power from covariates needs the gene's dispersion alongside its expression and the perturbed-cell count. Until these columns existed, only `average_expression_all_cells` -reached this table and every such analysis had to load a 16 MB `sim_input.rds` to find the rest. +reached this table and every such analysis had to load a separate `sim_input.h5` to find the rest. With the convention above, the bracket is `1/gene_mean + dispersion` — no conversion, because `dispersion` already *is* `1/theta`. -They are joined by `summarize_power.R` at summary time rather than emitted per simulation, because +They are joined by `watteg-summarize-power` at summary time rather than emitted per simulation, because they are per-gene constants: putting them in the simulation output would only help sweeps run after the change, while re-summarising an existing sweep from its stored power tables takes seconds. Both columns are absent if `--sim-input` is not passed, so older outputs and hand-run summaries are @@ -141,10 +142,17 @@ measured power >= 0.8 for *every* tested pair, so element-wide negative claims a | `power` | Fraction of simulations in which sceptre would have called the association. | | `power_ci_low`, `power_ci_high` | 95% Wilson score interval. | | `n_reps` | Simulations contributing. Can be below `--reps` if any simulation produced no fold-change estimate. | -| `mean_log_2_fold_change` | Mean simulated log₂ fold change across replicates. | +| `mean_log_2_fold_change` | Mean simulated log₂ fold change across simulations. | | `mean_pert_cells` | Mean perturbed cells. | | `average_expression_all_cells` | Raw mean expression. | | `effect_size` | The effect size simulated. | +| `estimand` | `fixed` or `random`: whether the element's realised mean effect was pinned to `effect_size` in every simulation or left free around it. See [Methods](methods.md#the-random-estimand-as-an-option). | + +By default this table is built inside the simulation: each task holds all simulations of its pairs +and writes per-pair counts (simulations called, simulations used, and the sums behind the means), +which `COMPUTE_POWER` adds up. That is the same table the per-simulation rows give, byte for byte: +the means are sums over counts either way, which is exactly how pandas computes a grouped mean, and +the rows are read back at full precision. The counts are not published. ### What `power` actually counts @@ -169,11 +177,13 @@ The interval is Wilson rather than `p̂ ± 1.96·SE` precisely because the norma `[0, 0]` for 0 successes, asserting certainty the data do not support. See [Choosing num_replicates]({{ site.baseurl }}{% link choosing-num-replicates.md %}). -## `per_replicate/es/*.tsv.gz` — per-simulation detail +## `per_replicate/replicates_es.parquet` — per-simulation detail (optional) -One row per (pair, simulation), gzipped. This is the only output from which power can be -**re-derived** — subsampling simulations to study a reduced design, bootstrapping, or re-thresholding -— so it is worth keeping even though it dominates the output volume. +One row per (pair, simulation). **Written only with `keep_per_simulation: true`**, or when +simulations are chunked across tasks (`reps_per_chunk` < `num_replicates`), where power is computed +from these rows. It is the only output from which power can be **re-derived** — subsampling +simulations to study a reduced design, bootstrapping, or re-thresholding — so keep it for a sweep +you will want to re-analyse. It dominates the output volume: 74 million rows for moi5 trans. | Column | Meaning | |---|---| @@ -181,9 +191,10 @@ One row per (pair, simulation), gzipped. This is the only output from which powe | `p_value` | From the resampling test on this simulation's simulated counts. | | `log_2_fold_change` | Its effect estimate. Power counts a simulation only when `p_value` beats the discovery threshold **and** this is negative. | | `rep` | Simulation index, unique across chunks thanks to `--rep-offset`. | -| `effect_size` | The effect size simulated. Constant within a file, and kept because `compute_power.R` uses it to refuse an input that mixes effect sizes. | +| `effect_size` | The effect size simulated. Constant within a file, and kept because `watteg-compute-power` uses it to refuse an input that mixes effect sizes. | | `num_pert_cells` | Perturbed cells for this target. | | `pass_qc`, `n_nonzero_trt`, `n_nonzero_cntrl` | Diagnostics, carried over from the **real** discovery pairs rather than recomputed from simulated data — they describe the observed experiment. | +| `estimand` | `fixed` or `random` (see the power table). The last column, so a file from before it existed is this one minus it. | **Five columns sceptre returns are dropped before writing**, because at 100 simulations × 34,886 pairs × 6 effect sizes they were 39 % of a 3 GB output and nothing read them: @@ -192,24 +203,30 @@ pairs × 6 effect sizes they were 39 % of a 3 GB output and nothing read them: |---|---| | `fold_change` | It is `2^log_2_fold_change` — the same number twice. | | `se_fold_change` | Read by nothing downstream. | -| `significant` | sceptre's own call at *its* threshold, not the discovery threshold this pipeline tests against. `compute_power.R` recomputes it, so keeping the column invited the wrong one being believed. | -| `average_expression_all_cells` | A per-*gene* constant that was repeated once per replicate. `summarize_power.R --sim-input` joins it from `sim_input.rds`, where it is stored once. | +| `significant` | sceptre's own call at *its* threshold, not the discovery threshold this pipeline tests against. `watteg-compute-power` recomputes it, so keeping the column invited the wrong one being believed. | +| `average_expression_all_cells` | A per-*gene* constant that was repeated once per simulation. `watteg-summarize-power --sim-input` joins it from `sim_input.h5`, where it is stored once. | Together with gzip that takes the per-simulation output from ~3 GB to a few hundred MB for a -six-point sweep at 100 replicates. Nothing needs a decompression step: `read.delim` sniffs the -magic number and handles `.tsv.gz` and `.tsv` alike, and `compute_power.R` accepts either. +six-point sweep at 100 simulations. Nothing needs a decompression step: `read.delim` sniffs the +magic number and handles `.tsv.gz` and `.tsv` alike, and `watteg-compute-power` accepts either. ## Intermediates | File | Contents | |---|---| -| `sim_input.rds` | Per-gene `mean`, `dispersion`, `average_expression_all_cells`; per-cell `size_factors` and categorical covariates; the gRNA and target perturbation matrices. No count matrix — the simulation draws counts rather than reading them. | -| `sceptre_template.rds` | The sceptre object with `@response_matrix` and `@grna_matrix` emptied. Neither is read by the discovery analysis once gRNAs are assigned. | +| `sim_input.h5` | Per-gene `mean`, `dispersion`, `average_expression_all_cells` and the model coefficients; per-cell `size_factors`; the covariate matrix; the gRNA and target perturbation matrices; and `cells_in_use`, each simulated cell's position in the original object. No count matrix — the simulation draws counts rather than reading them. | | `pairs.tsv` | `grna_target`, `response_id` for QC-passing pairs only. | -| `grna_targets.tsv` | `grna_id`, `grna_target`. | +| `pairs_with_info.tsv` | Every discovery pair with `n_nonzero_trt`, `n_nonzero_cntrl` and `pass_qc` from the real data. Constant across simulations, and the first thing to look at when a pair's power is surprising. | +| `grna_targets.tsv` | `grna_id`, `grna_target`. **Many-to-many**: a guide inside two overlapping candidate elements appears once per target, and this is the only place that mapping is recorded faithfully. | | `discovery_threshold.txt` | A single number: the p-value a simulation must beat. | -| `analysis_mode.tsv` | `resampling_mechanism` (`crt` or `permutations`), `run_permutations`, and `moi`. Which test produced these power numbers. | -| `null_precomputations.rds` | Per-gene null models, one set per simulation, fitted on a null simulation. | +| `null_fits.h5` | Under `null_fits: reuse` (the default): each gene's null-model fit per simulation, from `FIT_NULL_MODELS`, with the seed, the baseline model and a digest of `sim_input.h5` it was made for. About 2.5 MB for 244 genes x 100 simulations. | +| `analysis_mode.tsv` | The resampling mechanism, the MOI, the side, the `B1`/`B2`/`B3` budget and the multiple-testing alpha — the screen's own analysis parameters, so which test produced these numbers is readable without opening anything. | + +There is no `sceptre_template.rds`: it was an R object carrying the covariate matrix and the +analysis parameters, which are in `sim_input.h5` and `analysis_mode.tsv`. `null_fits.h5` plays the +part R's `null_precomputations.rds` did, made in Python by pysceptre's own fit (see +[Methods](methods.md#null-model-fits)); under `null_fits: refit` there is none, and every gene is +refitted on each simulation's own counts. `split_*.tsv` is **not published**. The splits are parallelisation bookkeeping — 1,000 files per sample — and they are regenerable: the bin packing is deterministic given `pairs.tsv` and @@ -217,10 +234,14 @@ sample — and they are regenerable: the bin packing is deterministic given `pai ## Two means, deliberately -`sim_input.rds` carries two per-gene means and they are not interchangeable: +`sim_input.h5` carries two per-gene means and they are not interchangeable: -- **`mean`** — size-factor normalised. This is what the simulation draws counts from. +- **`mean`** — size-factor normalised. The simulation drew counts from this until 2026-09-21; it + now draws from `exp(X·β)` instead, and `mean` is kept because `--expression-model size_factor` + reproduces the sweeps that were run on it. - **`average_expression_all_cells`** — raw. Reported in the output so power can be related to expression level. -Only the raw one appears in the output tables. +Only the raw one appears in the output tables. The two differ by more than a rounding: on day0 the +normalised mean sits 16 % below the raw one, which is what a normalisation does and is also why +feeding the wrong one to anything expecting counts is a scale error rather than a small one. diff --git a/docs/pysceptre-backend.md b/docs/pysceptre-backend.md new file mode 100644 index 0000000..798f1ce --- /dev/null +++ b/docs/pysceptre-backend.md @@ -0,0 +1,1155 @@ +--- +title: Plan - pysceptre backend +nav_order: 8 +--- + +# Plan: move the high-MOI power analysis onto pysceptre + +Replace the R/sceptre engine inside the power simulation with +[pysceptre](https://github.com/broadinstitute/pysceptre), so that the whole pipeline is one Python +package, `pixi.toml` drops R entirely, and the per-simulation cost falls by the margin pysceptre +already demonstrates on discovery analysis. + +This document is a plan, not a record of work done. Nothing below has been implemented. + +## 0. Branches, and why + +| Repo | Branch | Rule | +|---|---|---| +| `WattEG` | **`feat/pysceptre-backend`** (created for this work) | `main` stays the R implementation until this branch merges | +| `pysceptre` | **`v0.2.0`** (tag) | The §5 export work was done on `feature/watteg-simulation-support`, squash-merged into `0.1.1rc` as **`d96d48f`** alongside the analytical-power work, and has since reached `main` and been tagged. `pixi.toml` pins the **tag**, not a SHA — the references to `d96d48f` below are the history, not the pin | +| `WattEG` | **`r-implementation`** | the R pipeline, kept **maintained rather than frozen**. Branched from `main` at `7c07825` and since carrying the same three fixes this branch does (`d767af3`, `2b76284`, `b346296`) | +| `WattEG` | `legacy` | untouched (the Snakemake implementation that preceded both) | + +The R path is not deleted when the Python path lands. It is the reference the Python path is +measured against, and it is what the paper describes; retiring it is a separate decision, taken +after §8 reports. + +**It is also not frozen.** The first instinct was to leave `r-implementation` untouched so it would +keep reproducing the paper's numbers — but nothing has been published, so there is no obligation to +a set of numbers, and what that would have preserved is a bug. Two of the three fixes below change +results, and both were applied to R as well as to Python. The consequence is stated in §11b: the R +reference Stage B compares against has to be **regenerated**, not read off the existing sweep. + +## 1. What actually moves + +The pipeline is five steps plus two support steps. Only one of them is expensive, and only one of +them needs R. + +| Today | Fate under the Python backend | +|---|---| +| `src/prepare_sim_input.R` | **ported to Python**, reading a pysceptre export instead of the `.rds` (§4). Everything it computes — poscounts size factors, normalised means, dispersions, the discovery threshold — is computable from the exported matrix | +| `src/split_pairs.R` | ported, trivially | +| `src/fit_null_models.R` | **deleted** (§3.1) | +| `src/merge_null_models.R` | **deleted** (§3.1) | +| `src/run_power_simulation.R` + `lib/simulate.R` + `lib/pert_input.R` | **ported to Python**; the NB draw, guide-to-guide variability, centering and seeding are WattEG's method and stay in WattEG (§6) | +| `src/consolidate_replicates.R` | ported (pyarrow) | +| `src/compute_power.R` + `lib/stats.R` | ported (Wilson interval) | +| `src/summarize_power.R`, `src/fit_power_curve.R` | ported | +| `patches/`, `lib/apply_patch.R`, `src/check_sceptre_api.R`, `src/install_sceptre.R`, `src/install_ondisc.R`, `src/audit_dependencies.R` | **deleted.** All six exist only because the pipeline reaches into unexported sceptre S4 slots and patches its CRT path | +| `lib/sceptre_io.R` | **deleted from the pipeline**; its odm-materialisation logic moves to the one-off export (§4) | +| `src/make_test_data.R` | ported, or replaced by a synthetic fixture generated in Python | + +Everything in `workflow/slurm_executor/` and `workflow/compare_*.R` is scaffolding around the R +steps and follows them. + +## 2. The core mapping, and the reason it is fast + +Today the unit of work is **one `run_discovery_analysis()` call per (target, replicate)**: on +`day0_grna20` that is 3,026 targets x 100 simulations = 302,600 R calls per effect size, each one +carrying the full 567,690-cell bookkeeping for a median of 9 gene pairs. The measured cost model is +`1.140s + 0.5561s x pairs` per (target, simulation), **48 % of it in the per-target term**. + +pysceptre's `run_discovery_analysis` takes plain arrays — `response_matrix` (n_genes x n_cells), +`covariate_matrix`, `grna_target_cells` (dict target -> 0-based cell indices), and a `pairs` frame. +That signature lets one call cover **a whole replicate chunk of one target**: + +``` +for target in split: + rows = [f"{gene}@{rep}" for rep in reps for gene in target_genes] # pseudo-genes + counts = vstack(simulate(target, gene, rep) for rep in reps) # (genes*reps, n_cells) + pairs = DataFrame(response_id=rows, grna_target=[f"{target}@{rep}" ...]) # pseudo-targets + run_discovery_analysis(counts, rows, covariates, {f"{target}@{rep}": cells}, pairs, ...) +``` + +Five consequences, each verified against the pysceptre source rather than assumed: + +**2.1 Per-gene null fits come for free and are per-replicate.** `fit_all_genes` fits every row of +the response matrix independently (`_GENE_BATCH_WIDTH = 1`, `pipeline/discovery.py:89` — pinned at 1 +deliberately so a fit cannot depend on its batch companions). Stacking replicates as rows therefore +fits each `(gene, replicate)` null model **on that replicate's own simulated counts**. That is the +`cleared` configuration in [Status]({{ site.baseurl }}{% link status.md %}) — the faithful reference +that `null_fit` was built to approximate — obtained at no extra cost. §3.1. + +**2.2 Replicates must not share a CRT draw.** pysceptre seeds each target's resampling stream from +the *target's name* (`target_seed_sequence`), which is what makes its results independent of chunking +and target order. One shared key for all replicates would hand every replicate the **same** synthetic +treated-cell index sets, correlating the replicates: R redraws them per call, and the Wilson interval +in `compute_power` assumes independent Bernoulli trials. Hence the `target@rep` pseudo-target keys +above — independent streams by construction, and chunk-layout invariance inherited for free. + +The key must carry the **effect size** as well as the replicate — `target@es@rep` — or a single +`seed` would hand every effect size in a sweep the same synthetic index sets. R's `derive_seed` +includes `effect_size` for exactly this reason. (The alternative, deriving the per-call `seed` from +the effect size, works too but makes the invariance harder to see; prefer the key.) + +The cost is that the target's binomial GLM (perturbation status on covariates) is refit once per +replicate although the input is identical — precisely the redundancy the sceptre patch in `patches/` +removes on the R side, worth 999 -> 634 CPU-h there. pysceptre batches those fits across a target +chunk, so the redundancy is far cheaper than R's. **It is accepted, not fixed** — removing it would +mean changing the pysceptre package, and §5.3 says why that bar is not met. + +**2.3 Memory is a storage question, not a fitting question.** Both row-reading paths — `_gene_fit_job` +(`discovery.py:257`, which at `_GENE_BATCH_WIDTH = 1` fills a one-row float64 `Y`) and `_gene_job` +(`discovery.py:804`) — go through `_get_row` (`discovery.py:190`), which densifies **one row at a +time** and casts to float there. So the stacked matrix can be stored as `int16` or sparse without +any change to pysceptre. For the median target: 9 genes x 100 replicates x +567,690 cells is 1.0 GB as dense `int16`, against 8.2 GB as float64. The max target (36 pairs) is +4.1 GB, which is why `reps_per_chunk` stays a parameter and becomes the memory knob it always +implicitly was. Overflow guard: counts above `int16` range must promote, not wrap. + +**2.4 Call the inner entry point, not the public one.** `pipeline.api.run_discovery_analysis` +sizes `B2`/`B3` from `len(pairs)` (R's `run_qc` rule). With pseudo-pairs the pair count is inflated +by the replicate count, which under `resampling_approximation = "no_approximation"` would inflate +the `B3` budget ~100x. Call `pipeline.discovery.run_discovery_ntcells_complement` directly and pass +`B1`/`B2`/`B3` from the export's `metadata.json` — which is what R does, since the template carries +the real analysis's `@B1/@B2/@B3` slots and the simulation never re-derives them. + +**2.5 Where the time will actually go — and why "30 s" is not the answer.** A full +permutation-mode pysceptre discovery analysis on a real dataset runs in ~30 s. That number does not +transfer to this pipeline, and reading it as though it does would set the wrong expectations and +optimise the wrong thing. One discovery analysis fits **237 gene nulls** and tests ~35,000 pairs +once. One effect size of a 100-replicate power sweep fits **34,886 x 100 ≈ 3.5 million** per-(gene, +replicate) nulls, draws 3.5 million negative-binomial count vectors of 567,690 values, and runs 3.5 +million resampling tests. It is roughly four orders of magnitude more gene-fitting work than the +analysis whose runtime is 30 s. + +So the useful reading of that 30 s is a **per-unit rate**, and the three terms it decomposes into +are what the phase-3 benchmark has to report separately: + +1. drawing the counts (`rnbinom` was 7 % of R's time; in Python it will be a larger share, because + everything around it got faster); +2. the per-(gene, replicate) Poisson IRLS null fit — 59 % of R's time was `glm.fit`, and §2.1 makes + this term unavoidable rather than hoistable; +3. the per-pair resampling test against that target's CRT draws. + +Whichever dominates is where any later optimisation goes. The redundant binomial fits of §2.2 are a +fourth term and, on this reasoning, the smallest of the four — which is the quantitative case for +§5.3 staying unbuilt. + +## 3. Statistical decisions this forces, stated up front + +### 3.1 The null model becomes the faithful refit + +`fit_null_models.R` / `merge_null_models.R` and the whole `--null-precomputations` bundle exist for +one reason: R refitting the per-gene null inside every call costs **4.3x**, so the fits were hoisted +into their own Nextflow processes, fitted once per `(gene, simulation)` on a null simulation, and +injected. Status records the verdict: `null_fit` is **exactly equivalent** to the faithful +`cleared` refit (0 flips in 265 calls), and `as_is` — the inherited real-data cache — understates +power by +0.0063 mean. + +Under §2.1 the faithful refit is what happens anyway. So: delete both processes, delete the seed- +matching guard between bundle and simulation, delete the `@response_precomputations` trap in +`slim_sceptre_object`. **Expect the Python results to match `power_null_fit/`, not `power_as_is/`** +— that is the comparison baseline in §8, and picking the wrong one would manufacture a +0.006 +discrepancy out of a known, already-settled difference. + +### 3.2 Dispersions still come from real data + +`row_data$dispersion` is `1/theta` from sceptre's `@response_precomputations`, fitted on the **real** +counts — it sets the noise the simulation exists to reproduce, and it must not become a property of +the simulated counts. pysceptre has its own validated theta estimator (`glm/nb_theta.py`), so the +Python `prepare_sim_input` computes theta from the real matrix rather than reading a cache. +**Measured, and it settles the question.** Over day0's 237 genes and 567,690 cells, pysceptre's +fitted theta reproduces sceptre's cached theta to a **median relative difference of 2.8e-12 and a +worst case of 1.2e-9** — about ten significant digits — with 236 of 237 genes inside 1e-9. Since a +dispersion matters only through the variance of the counts drawn from it, that is nothing against +the 5–50 % effect sizes the sweep tests. One gene's theta MLE fell back to method of moments in +both implementations alike, and it is the gene the two differ on most. +`workflow/compare_dispersion.py` is the gate, at a tolerance of 1e-6 — generous against what the +port achieves and still tight enough to catch a wrong model. +`build_dispersion_vector`'s hard error on missing/non-finite dispersions carries over, and is +joined by one **deliberate deviation**: a theta clamped to the estimator's bounds is refused rather +than simulated from. sceptre clamps to exactly the same `[0.01, 1000]` that pysceptre does +(`perform_response_precomputation`: `max(min(theta, 1000), 0.01)`) and carries on, so R would +proceed where this stops. For an *analysis* that is reasonable; for a *simulation* a clamped theta +is not an estimate of anything, and drawing counts from it would state a noise level the data never +supported. Day0 has no clamped gene, so nothing is refused today. + +### 3.2b The simulated mean is biased low, and the fix is exact + +**sceptre is not involved, and this is the first thing to establish.** sceptre computes no size +factor, no geometric mean and no normalised mean — searching all 183 of its functions for +`size_factor|geomean|normaliz|offset` returns nothing — and its per-gene model is +`glm.fit(y = counts, x = covariate_matrix, family = poisson())` with no offset. Library size enters +it as ordinary covariates, `log(response_n_umis)` and `log(response_n_nonzero)`, which the GLM fits +coefficients for. The size factors and `row_data$mean` are **WattEG's simulation machinery alone**, +inherited from the original DC_TAP_Paper power simulation; they exist to generate synthetic counts +and sceptre never sees them. DESeq2 is not doing it wrong either — mean-of-ratios is exactly what +its `baseMean` is, and as a summary statistic it is fine. What follows is about one *composition*, +which is WattEG's own. + +**Found while checking a report that the simulation runs 16 % low.** It does not, but it does run +low. `row_data$mean` is 16 % below the raw mean on day0 — that much is true and expected, because +it is a *normalised* mean — but the simulation never uses it alone: `draw_counts` forms +`mu[i,j] = mean_i x size_factor_j x effect_size`, which puts the size factor back. With +`mean(sf) = 1.1389` on day0 the simulated per-gene expected raw mean lands at **0.959 of the real +one** at the median (range 0.89–1.04; 84 of 237 genes more than 5 % low, one more than 10 %). + +The residual is a real bias with a clean cause. `mean_i` is a **mean of ratios**, +`(1/n) sum_j counts[i,j]/sf_j`, and + + raw_mean = E[x.sf] = E[x].E[sf] + Cov(x, sf), x = counts/sf + +so multiplying by `mean(sf)` drops the covariance term. It is positive here — cells with larger +size factors still carry slightly more normalised counts, i.e. the normalisation under-corrects — +so the simulation draws low. **Whether that makes simulated power low is not established**, and +the obvious inference is unsafe: the same baseline also understates the count *variance* by 13.5 % +(§3.2c), and less variance inflates power where less expression deflates it. Two errors, opposite +signs, neither measured against the other. Stage B under both baselines is the experiment. + +**The alternative is a ratio of sums, and it is exact rather than better:** + + mean_i = rowSums(counts)_i / sum_j sf_j + +Then `sum_j mean_i.sf_j = sum_j counts[i,j]` identically, for every gene, by construction. On day0 +it raises each gene's mean by ~4.3 %. + +**Not changed, pending a decision.** It moves every number the paper reports, and the port's job +is to reproduce R first — that is what the phase-2 gate is. It is a better candidate for actually +changing than the §5.2 question, though: that one is a judgement about which cells belong in an +estimator, this one is an estimator that provably fails to reproduce the counts it was derived +from, with an exact alternative available. If it is taken, it is a one-line change in +`watteg/expression.py` and a corresponding one in `src/prepare_sim_input.R`, and both sweeps +have to be re-run. + +### 3.2c The same question on the analytical side, and what it suggests + +pysceptre's analytical power estimator hit this first, and its +`analytical_power/inputs.py` already says so: `baseline_expression_stats` "comes from a +normalisation scheme sceptre does not use, and on day0 it sits about 16 % below the mean sceptre's +own model implies", with `baseline_expression_stats_from_fits` recommended instead. + +**PerturbPlan is not doing anything wrong, and its source says so twice.** `compute_power_posthoc` +does not compute `expression_mean`; it takes `baseline_expression_stats` as an argument, and the +documentation defines it only as "a data frame ... with columns `response_id`, `expression_mean`, +and `expression_size`" — **it never states the scale.** The formula does, in two independent +places: `compute_distribution_teststat` uses `var_nb(mean, size) = mean + mean^2/size`, the +variance of the negative binomial the *observed counts* follow; and `compute_QC` takes +`P(count == 0)` from that same NB and feeds it to +`pbinom(n_nonzero_thresh - 1, num_cells, 1 - P0)` — the chance that enough cells have a nonzero +**raw count** to clear pairwise QC. The second settles it: only a distribution over actual counts +has a zero probability to ask about. So `expression_mean` must be E[observed count per cell], and +the defect is entirely in the input. + +**And the wrong scale costs more there than the flat 16 % suggests, because it is counted twice.** +Measured on day0, the normalised mean overstates `P(count == 0)` by 0.032 at the median and 0.073 +at most (0.304 against 0.247). That inflates `QC_prob`, and power is multiplied by +`1 - QC_prob`: at the median target's 396 treated cells, **28 of 237 genes carry an inflated +`QC_prob`, the worst by 0.20** — a fifth of that gene's power disappearing into a QC term, on top +of the separate understatement through the test statistic. + +What is wrong, then, is feeding it the poscounts normalised mean, and the two tools differ only in +how far that input travels: + +| | vs the mean sceptre's model implies | +|---|---| +| analytical formula — uses `expression_mean` **as-is**, no per-cell factor | **0.84, 16 % low** | +| WattEG's simulation — `mean_i x sf_j`, the factor multiplied back | **0.959, 4.1 % low** | + +"The mean sceptre's model implies" is measurable and is exactly the observed raw mean: +`mean(exp(X.beta))` reproduces it to 8e-9, because a Poisson GLM with an intercept satisfies +`sum(fitted) == sum(observed)`. That identity is what makes this comparison sharp rather than a +matter of taste. + +**The deeper point, which the level difference hides.** sceptre's model is +`E[count_ij] = exp(X_j . beta_i)`, a full covariate-dependent mean. WattEG simulates +`E[count_ij] = mean_i x sf_j`, one scalar per cell. Those differ in *shape across cells*, not only +in scale, so the ratio-of-sums fix in §3.2b patches the average and leaves the structure wrong. + +**So the candidate fix is bigger than §3.2b and subsumes it: simulate from `exp(X_j . beta_i)` +directly**, which is the same move the analytical side already made. It reproduces the observed +mean exactly rather than approximately, reproduces the per-cell variation the test's own model +assumes, and deletes the poscounts machinery entirely — with it go both §5.2's QC-cells question +and §3.2b's estimator question, which exist only because size factors do. `fit_dispersions` +already computes `fitted_coefs` and currently discards them. + +**The argument against, stated because it is real.** Simulating from the fitted model and then +testing with that same model makes the test perfectly specified by construction, which may +overstate power slightly; the present scheme is misspecified in the other direction. Neither is +neutral. "Matches the model the real data was fit with" is the more defensible starting point, but +this is a decision about what the power analysis *means*, not a bug fix, and it is the user's. + +**Taken, and implemented.** `exp(X . beta)` is the default baseline in the Python port +(`watteg/baseline.py`), `size_factor` survives as a validation fixture so Stage B can compare +like-for-like against sweeps produced with it, and `sim_input` format 2 carries `fitted_coefs`. +All four phase-2 gates stay green, because the change is additive: everything the port already +reproduced is untouched. + +**What this does *not* settle.** The effect on simulated power is unmeasured and not obviously +signed, for the reason in §3.2b. Every number here is day0; the mechanism is general but the +magnitudes are not. And the existing sweeps were run on the old baseline, so re-running them is a +separate, all-or-nothing decision: mixing the two scales within one analysis would be worse than +either alone. What that decision costs, concretely: every `power_summary.tsv` moves — `day0` at six +effect sizes, `moi5` cis at six, and the 742,525-pair `moi5` trans sweep at one. + +**That decision has since been taken, for a different reason.** The R implementation is *not* +unchanged: it carries this baseline (`d767af3`) and the centring fix (`2b76284`), so the sweeps have +to be regenerated regardless of how this question alone would have been decided. See §11b. + +Both per-gene means survive the change on purpose. `sim_input.h5` carries `fitted_coefs` *and* +`mean`, so an existing output can be audited against the scale that produced it without +re-deriving anything, and `--expression-model size_factor` reproduces it exactly. + +### 3.3 Seeding contract is preserved + +Today: `set.seed(derive_seed(seed, target, rep, effect_size))` before each replicate, and a separate +`rep = 0` key for per-target setup, which is what makes results invariant to split layout and +replicate chunking. Python equivalent: `np.random.SeedSequence` spawned from the same four-part key +(hashed, not R's integer arithmetic — the streams differ from R's either way). The invariance test +(1x4 against 2x2 chunking, and two different split layouts) ports directly and becomes a unit test +rather than an sbatch script. + +### 3.4 The `log_2_fold_change < 0` condition + +pysceptre returns `fold_change`, not `log_2_fold_change`; `compute_power`'s one-sided condition +becomes `fold_change < 1`, which is the same predicate. `pct_change_es` and its CI are a bonus the R +path never had. + +## 4. The input boundary — the one real design decision + +`prepare_sim_input.R` reads a `sceptre_object` `.rds`. Nothing in Python reads that. Two options: + +| | Keep `prepare_sim_input.R` in R | **Recommended: `.h5mu` in, R export run once, outside the pipeline** | +|---|---|---| +| Env | still needs `r-base` + pinned sceptre + ondisc + the patch machinery | pure Python; `pixi.toml` drops R | +| User cost | none | one `Rscript export_sceptre_dataset.R` per dataset, in a container we provide | +| Pipeline surface | unchanged | samplesheet column becomes `dataset` (`.h5mu`) instead of `sceptre_object` | + +Take the second. The stated goal is one Python package, and a single R step in the pipeline keeps the +entire R toolchain — pin, patch, `check-api`, two source installs — alive to serve it. The export is +a per-dataset, one-off, already-written script (`pysceptre/scripts/export_sceptre_dataset.R` + +`make_h5mu.py`) that handles odm-backed and in-memory matrices alike, so `lib/sceptre_io.R`'s +materialisation logic has an owner. + +**Keep a `--sceptre-object` path on the R side for one release** as an escape hatch and so §8 can run +both engines from the same object. + +What the export already carries and WattEG needs: the count matrix (`--all-genes`, required — +poscounts size factors are a whole-gene reduction), the covariate matrix restricted to +`cells_in_use`, QC-passing pairs, `discovery_result` (from which the nominal threshold is derived +exactly as `discovery_threshold()` does today), and in `metadata.json` the `side_code`, +`resampling_approximation`, `run_permutations`, `control_group_complement`, `B1/B2/B3`, +`multiple_testing_alpha` and both `n_nonzero_*` thresholds. That covers `analysis_mode` and every +pysceptre argument. + +What it does **not** carry is enough cells — see §5.2, which is a blocking gap, not a detail. + +## 5. What pysceptre must change — the export only + +**Status: done, in `../pysceptre` on `0.1.1rc`** — squash-merged as **`d96d48f`** ("ADD analytical +per-pair power, per-gRNA exports, and a minimal container"), which carries the export work together +with changes of its own. The three commits it squashes (`7e29efa`, `5117651`, `37f9d81`) survive +only on `feature/watteg-simulation-support`, so `d96d48f` is the reference that will keep +resolving. Both gaps are closed and verified against the real day0 object; §5.1 and §5.2 below are +kept as the record of what they were and of what closing them turned up. §5.3 remains unbuilt, as +planned. + +**The constraint that shapes this whole section: the pysceptre *package* does not change.** Both +gaps below are in `scripts/`, which pysceptre's own `CLAUDE.md` marks as *not shipped in the wheel* — +they are dataset-export tooling, not the statistical engine. Nothing in `src/pysceptre/` is touched, +so nothing this plan does can move a pysceptre result, and the validation burden stays on WattEG +where it belongs. + +Verified gaps, not speculation. + +**5.1 Individual targeting-gRNA assignments — blocking.** The export writes +`grna_assignments$grna_group_idxs`, which is the **union of each target's gRNAs**, plus individual +*non-targeting* gRNAs (`scripts/sceptre_export_lib.R:112-139`). WattEG's guide-to-guide variability +(`create_guide_pert_status`, `create_effect_size_matrix`, `guide_sd = 0.13`) needs **per-gRNA** +membership for targeting guides, and the `grna_id -> grna_target` map. Add both to the export: a +third block of assignment rows with `unit_kind = "targeting_grna"`, and `grna_target_data_frame` +written out whole. + +**What closing it turned up, and what it means for §6.** The gRNA -> target map is +**many-to-many**: 1,673 of day0's 43,736 guides sit inside two or three *overlapping* candidate +elements and so belong to two or three targets (45,463 design rows against 43,736 distinct ids). +R handles this without comment — `grna_map$grna_id[grna_map$grna_target == target]` selects by +target, so a shared guide is simply returned for both. Anything that collapses the map to one +target per guide — a `dict`, a `match()`, the per-unit `var` annotation — drops those guides from +every target but one, which on day0 would leave **216 of 3,071 targets simulating with an +incomplete guide set**, silently. + +So `perturbation.py` must read guides-per-target from `grna_target_data_frame`, **never** from the +gRNA assay's `var` annotation, which has one row per unit and therefore records `""` for +a shared guide. The export asserts the union round-trip exhaustively over every target at write +time, which is what caught this; all 3,071 day0 targets reproduce exactly. + +While the export is open: it writes only `response_id` and `grna_target` for the QC-passing pairs +(`qc_passing_pairs`), but the R simulation output carries `n_nonzero_trt`, `n_nonzero_cntrl` and +`pass_qc` from `@discovery_pairs_with_info` — real-data diagnostics, constant across replicates, and +the first thing anyone looks at when a pair's power is surprising. Write that frame whole, or drop +those three columns from the byte-compatibility promise in §6. Prefer writing it. + +All additive; no existing reader changes. + +**5.2 All cells, not just `cells_in_use` — blocking.** `prepare_sim_input.R:263-270` calls +`compute_expression_stats()` on `get_response_matrix(so)`, the **whole** matrix: 586,309 columns on +`day0_grna20`, against 567,690 in `cells_in_use`. Poscounts size factors are a per-cell reduction +over a per-gene geometric mean taken across *all* cells, so computing them on the QC-passing subset +gives different size factors and different normalised means — which is the input the simulation +draws from. The export writes `cells_in_use` only (`sceptre_export_lib.R:37,61,85`). Add an +`--all-cells` mode that writes the full matrix plus a `cells_in_use` index vector. + +Until that lands, phase 2's column-by-column gate **will** fail, and it would be easy to +misattribute the failure to theta (§3.2). It also constrains Stage A: matrices dumped from Python +have to be indexed the way `template@cells_in_use` expects before R can test them. + +**Closed by `--all-cells`, and this is what it buys.** Measured across the 18,619 cells QC removes +on day0, computed both ways: + +| Quantity | Median shift | Max | +|---|---:|---:| +| Raw gene mean | 2.2 % | 6.2 % | +| poscounts size factor (in-use cells) | 0.46 % | 1.3 % | +| Size-factor-normalised gene mean | 0.36 % | 3.0 % | + +The file keeps **one cell space** — under `--all-cells` the matrix columns, covariate rows and +every gRNA unit are absolute positions together — and `load_export` subsets back to `cells_in_use` +by default, so an analysis reads either file identically and only the simulation passes +`all_cells=True`. Verified on day0: the default read of the `--all-cells` export is identical to +the plain one across all 92,622,239 nonzeros, the covariates, all 46,789 units and the pair table. + +One thing the round-trip assertion forced into the open: **gRNA membership is post-QC in both +spaces.** `@grna_assignments` is built after QC while `@initial_grna_assignment_list` is the +pre-QC input, so a target's guides between them cover cells the target does not — 518 against 493 +on day0's first target. The guides are restricted to `cells_in_use`, which keeps the union +invariant true in every file and costs nothing, since those cells have no covariates and no test +sees them. `--all-cells` therefore adds cells to the **expression side only**. + +**A question for phase 2, not for the port.** Whether cells QC removed *should* enter the per-gene +geometric mean that sets the size factors is a scientific question, and the honest answer is that +R's implementation includes them because it reads the whole matrix, not because anyone chose it. +The port reproduces R first — that is what the phase-2 gate is for — and the table above is the +order-of-magnitude argument for deferring it: a 0.36 % shift in the gene mean the simulation draws +from, against effect sizes of 5–50 %. That is an estimate, not a measurement — `as_is` shifted +mean power by +0.0063 from coefficient differences far larger than this, so the direction is right +and the size is not established. Measure it once the Python path reproduces the R one. + +**5.3 A shared target fit across aliased targets — considered and NOT planned.** The `target@es@rep` +keys of §2.2 make pysceptre refit each target's binomial GLM once per replicate although the input +is identical. On the R side removing that redundancy was worth 999 -> 634 CPU-h, which is why it +gets a mention at all. Here it does not: pysceptre batches those fits across a target chunk, and a +full permutation-mode discovery analysis on a real dataset runs in **~30 s**, so the engine is not +plausibly the bottleneck in a simulation whose per-replicate cost is dominated by drawing counts and +fitting per-gene nulls (§2.5). + +Adopting it would mean an API change inside `src/pysceptre/` — an optional `target_fit_key` letting +several target keys share one fit while keeping their own CRT stream. That is a change to how +pysceptre works, so the bar is not "it would be faster": it is the phase-3 benchmark showing the +redundant fits are a **large** share of simulation wall clock. Absent that number, this stays +unbuilt, and the plan assumes it never gets built. + +**5.4 Nothing else.** The engine is used as published. If a change to `discovery.py` turns out to be +needed, that is a signal the mapping in §2 is wrong, not that pysceptre needs a WattEG-shaped hole in +it. + +## 6. Where the code lives + +The simulation model — NB draw from `mean x size_factor x effect_size`, per-guide effect sizes, +re-centering, the seeding scheme — is **WattEG's method**, not part of sceptre, and does not go into +pysceptre. New package in this repo: + +``` +src/watteg/ + sim_input.py # the container, ported from lib/sim_input.R + expression.py # poscounts size factors, normalised means, theta + perturbation.py # pert_input, guide status, effect-size matrix (lib/pert_input.R, lib/simulate.R) + simulate.py # draw_counts + engine.py # the pysceptre call: pseudo-gene/pseudo-target assembly + power.py # Wilson interval, power, MDES (lib/stats.R, compute_power.R) + seeds.py # derive_seed / SeedSequence + cli/ # one entry point per pipeline step +``` + +`pyproject.toml` with console scripts, so the Nextflow modules call `watteg-prepare-sim-input` etc. +rather than `Rscript src/...`. Output file names, columns and TSV/Parquet layouts stay **byte- +compatible** with the R path wherever they can — `consolidate_replicates`, `compute_power` and +`summarize_power` outputs are what the paper's figures read. + +## 7. The DAG afterwards + +``` +samplesheet -> PREPARE_SIM_INPUT -> SPLIT_PAIRS -> POWER_SIMULATION (split x effect size x rep chunk) + -> CONSOLIDATE_REPLICATES -> COMPUTE_POWER -> SUMMARIZE_POWER +``` + +Eight processes become six; `FIT_NULL_MODELS` and `MERGE_NULL_MODELS` go, and with them +`reps_per_null_chunk`, `test_max_null_reps`, the divisibility check on them, and one join in +`main.nf`. `reps_per_chunk` stays and becomes load-bearing for memory (§2.3). + +## 8. Validation — what would make this believable + +R and Python cannot agree draw for draw: different RNGs, different CRT streams, and §3.1 changes the +null model relative to what the R sweeps ran. Validate in stages, against the reference outputs +already in `WattEG-paper` rather than re-running R. + +**Which reference is which** — checked, because getting it backwards manufactures a discrepancy out +of a settled difference. `power_sweep/` holds the **six-effect-size sweep** (`power_es0.05` through +`power_es0.5`) and its `prepared/` carries `null_precomputations.rds`, so it ran the `null_fit` +configuration that §3.1 reproduces. `power_sweep_null/` holds `power_es0.0.tsv` only: it is the +**es = 0 null arm**, not the `null_fit` configuration. Stage B reads `power_sweep/`; Stage C reads +`power_sweep_null/`. + +**"Reproduce R" has a floor that is not the port's doing.** The existing sweeps ran on an x86 +cluster, where R's `sum()` accumulates in 80-bit `LDOUBLE`; on arm64 `.Machine$sizeof.longdouble` +is 8 and it accumulates in plain `double`. Measured in phase 2: **a local R run reproduces only 30 +of 20,000 published size factors bit for bit**, and differs from them by up to 6.2e-12 — the same +residual the Python port shows. So the published outputs cannot be reproduced exactly by R either, +and 1e-10 is what "reproduces R" can mean across platforms. That is a definition, not a caveat, and +it applies to every stage below. + +**No stage simulates in one language and tests in the other.** An earlier draft of this section +had one: dump Python-simulated counts and push the same matrices through both engines, so that any +difference was the engine alone. It is dropped, deliberately. It would have kept a working R +install, a pinned sceptre and a matrix-handoff harness alive purely to validate the thing that +exists to remove them, and it would have forced this pipeline's `sim_input` to carry the QC-failed +cells R's matrices span so R could index them. The Python path simulates and tests in Python. + +What that gives up, stated plainly: nothing measures the two engines against each other **on +simulated counts specifically**, which are denser and lower-variance than real ones. The answer +comes by transitivity instead — pysceptre is already validated against R sceptre on this very +screen's real discovery analysis (`test_day0_regression`: Spearman 0.9865 on p-values, fold change +agreeing to 2.6e-12, sensitivity 0.9882 against R's own BH calls) — plus Stage B end to end and +Stage C, which needs no second implementation at all because it has an absolute bar. + +**Stage 0 — measure the noise floor first.** Two independent runs of the same correct pipeline do +not agree pair for pair: power is a fraction over 100 Bernoulli draws, and near-threshold pairs +cross in both directions. Re-run the **Python** pipeline at a second seed on a small panel and +record its own Δpower spread and 0.8-line crossing count. That is the bar Stage B is read against, +and it costs one extra short run rather than an R install. + +**Stage B — end to end, against R's published power.** Full 100 simulations at effect size 0.15, +Python against `power_sweep/.../power_es0.15.tsv`. This is now the only stage that compares the two +implementations, so it carries the weight Stage A used to share. +- Report: per-pair Δpower distribution, the fraction exceeding each pair's Wilson half-width, the + mean shift, and the count crossing the 0.8 line in each direction. +- Acceptance: mean shift consistent with zero — two independent 100-draw estimates of the same + binomial p differ by about `sqrt(2p(1-p)/100)`, so ≈0.07 per pair at p = 0.5 and ≈0 in the mean + over 34,886 pairs — and a **symmetric** count of 0.8-line crossings. The 12.6 % of pairs status.md + calls ambiguous will move in both directions; that is expected, not a failure. What would not be + expected is a mean shift, or crossings running one way, which is precisely the signature `as_is` + showed (11,649 up against 3,631 down). + +**Stage C — the null arm.** Effect size 0 against `power_sweep_null/`: the empirical rejection rate +should sit at the nominal threshold in both. This is the pipeline's own calibration check, and the +one stage with an absolute bar rather than a relative one. + +**Stage D — invariance.** 1x4 against 2x2 replicate chunking, and two split layouts, byte-identical +(§3.3). Unit test, not a cluster job. + +## 9. Out of scope, said explicitly + +- **Low-MOI screens.** pysceptre covers the complement-control-group + CRT high-MOI path only. A + low-MOI object must fail at `prepare_sim_input` with a clear message naming the R path, not + silently produce numbers from the wrong control group. The check reads `control_group_complement` + and `run_permutations` out of the export metadata. +- **`--n-control-cells` and `--cell-batches`. Decided: neither is ported.** + + `--n-control-cells` draws a fixed number of control cells per target instead of using every + non-perturbed cell — on day0, 5,000 in place of ~567,000. It is a cost lever and nothing else, + and it was measured to cost **21–60 % of power**. `--cell-batches` stratifies that draw so the + sampled controls keep the perturbed cells' batch composition; it does nothing on its own, and + `run_power_simulation.R` refuses it without `--n-control-cells`. + + The question worth answering, because it is the one that sounds alarming: **does dropping + `--cell-batches` expose the analysis to batch drift between the two arms?** No, for two reasons. + + 1. With no subsampling there is no draw to stratify. The control group is every non-perturbed + cell, so its batch composition is the dataset's, not a sampling artefact. + 2. Batch is conditioned on by the test itself, twice. `batch_factorBatch 2/3/4` and + `replicate_factorRep 2/3/4` are columns of the covariate matrix, and that matrix enters both + the per-gene NB fit — so batch effects on expression are adjusted out — and the logistic fit + of perturbation status that the **CRT draws its synthetic treated sets from**. The null + distribution is therefore conditional on batch by construction. That is the formal guarantee, + and it is why this method does not need matched control cells: cell-level matching is what + you reach for when the model cannot adjust for a confounder, and here it can. + + Stratified sampling was never the defence against batch confounding. It was a patch for the + extra variance that careless subsampling adds on top of a model already handling it. + + The case against porting is stronger here than it was in R: the lever exists to buy speed, the + port is the reason speed stops being the binding constraint, and a knob that trades power for + speed you no longer need is a trap rather than an option. **`perturbation.py` should not grow a + control-sampling path**, and `sim_input.h5` carries no `batch_factor` or `replicate_factor`, + since `--cell-batches` was the only reader. + + **If control subsampling ever returns** — a screen large enough that even the Python path cannot + afford the full control set — stratification has to return with it, and the reasoning above is + why. That costs no format change: the design matrix holds both factors one-hot (`batch_factorBatch + 2/3/4` plus an all-zero reference level), so each is reconstructible in about fifteen lines, and + `sim_input` already stores categoricals as codes plus levels for exactly this. + + The R implementation keeps both flags. It is the reference the paper describes, and removing + options from it would change what that reference is. +- **`run_permutations = TRUE` screens.** pysceptre supports permutations, but its draws are sized by + the largest target in the run, which interacts badly with per-target calls. Refuse for now. + +## 10. Infrastructure + +- `pixi.toml`: drop `r-base`, `r-optparse`, `r-matrix`, `r-rcpp`, `r-dplyr`, `r-data.table`, + `r-purrr`, `r-crayon`, `r-parallelly`, `r-withr`, `r-nanoparquet`, `SCEPTRE_REF`/`SCEPTRE_SHA`, + `ONDISC_REF`/`ONDISC_SHA`, and the `setup` / `check-api` tasks. Add `python`, `numpy`, `scipy`, + `pandas`, `pyarrow`, `numba`, `mudata`. Keep `nextflow`. +- **pysceptre is still a pin.** It is on neither conda-forge nor bioconda, so it enters as a pixi + `[pypi-dependencies]` git dependency pinned by commit — the same shape as `SCEPTRE_SHA`, minus the + patch and the API check. `pixi.toml`'s comment block explaining why sceptre is pinned gets + rewritten, not deleted, and `check_sceptre_api.R`'s job — assert the pin still matches what we + call — passes to pysceptre's own test suite plus this repo's. +- A new container image for the `gcb` profile. The R image is not reusable. +- **`conf/*.config` needs recalibrating from scratch.** The last ten commits on `main` tuned + `POWER_SIMULATION`'s memory and machine type around R's 2.27 GB median / 3.27 GB max. Python's + footprint is dominated by the stacked count matrix (§2.3) and is a different function of + `reps_per_chunk` and pairs-per-target. Do not carry the closures over; re-measure, then rewrite + them. +- `.githooks/pre-commit` rejects camelCase **R** identifiers; add the Python equivalents (ruff, + matching pysceptre's `ruff.toml`) rather than leaving Python unlinted. + +## 11. Phases, with a gate that can stop the work + +1. ~~**Export gap** (pysceptre branch): §5.1 and §5.2, plus a round-trip test that the individual + targeting-gRNA unions reproduce `grna_group_idxs` exactly.~~ **Done** — squashed into + `d96d48f` on `0.1.1rc`. *Gate passed:* all 3,071 day0 targets reproduce exactly, + asserted at export time rather than in a test that can be skipped; the default read of an + `--all-cells` export is identical to a plain one on the real screen; 230 tests green, with the + export-format contract covered by 10 new ones that need neither R nor a real dataset; and + `test_day0_regression` passes 6/6 against a re-export of day0 (4 min, 34,886 pairs), so the + export changes move nothing the engine reads. +2. ~~**`prepare_sim_input` in Python** against the fixture: size factors, normalised means, theta, + threshold, pairs — each compared to the R output column by column.~~ **Done.** *Gate passed* + against the day0 `sim_input.rds` the day0 sweep was run on: `pairs.tsv`, `grna_targets.tsv` + and `discovery_threshold.txt` **byte-identical**; genes the same set in the same order; all + 3,071 target and 43,718 guide cell sets agreeing exactly; and the expression statistics + **bit-identical to a same-platform R run**, differing from the published ones only by the + 6.2e-12 platform residual above. Theta as in §3.2. Three defects the gates caught rather than + luck: counts stored as `uint16` made `np.log` return **float32** (2.5e-7 on the size factors); + `grna_perts` was missing the non-targeting guides, which would have kept the control arm's mean + while losing its guide-level variance; and `pairs.tsv`'s column order. (The second is moot since + 2026-09-24: every guide outside the target now has an effect of exactly 1, so control cells + carry no guide-level spread at all. See `methods.md`.) +3. ~~**Benchmark before committing to the shape.**~~ **Done, and it overturned §2.** The four + terms, measured: the per-pair test is **85 %** of the work, the per-gene Poisson fits 14 %, + drawing counts 5 %, and the per-target binomial fit and CRT draws **1.5 %** — of which the + redundancy stacking would remove is **0.8 %**. So there is almost nothing to amortise, the two + shapes measure the same, and **the simple one wins on simplicity alone**: `engine.py` makes one + call per (target, replicate), with no pseudo-genes, no pseudo-target keys, no replicate-chunk + memory knob and no question about replicates sharing a resampling stream. §5.3 is dead on its + own terms — 0.8 % was the number it had to beat. +4. ~~**`run_power_simulation` in Python** + Stage A validation.~~ **Done.** Stage A was dropped + (§8), so equivalence is measured on output: 40 replicates of one target, both implementations, + every bound derived from the data's own spread. Per-pair power agrees 8/8 within Monte Carlo + noise with no systematic shift, and each implementation's median fold change lands on + `log2(0.85)` — an *absolute* check, which is the only kind that can catch both being wrong the + same way. +5. ~~**The four cheap steps**~~ **Done** for `split_pairs`, `consolidate_replicates`, + `compute_power` and `summarize_power`, checked against R **on identical input**, which makes + them exact comparisons rather than statistical ones: every column agrees to machine epsilon, + in R's column order. The comparison caught `max_effect_size_tested` missing entirely and the + per-gene columns sitting in the wrong place. `fit_power_curve` is **not** ported — it serves + the paper's reduced-design study rather than the pipeline, and nothing in the DAG calls it. +6. **Nextflow rewiring** — done: `FIT_NULL_MODELS` and `MERGE_NULL_MODELS` are gone, the six + remaining processes call the `watteg-*` entry points, the samplesheet column is `dataset` + (a `.h5mu`) rather than `sceptre_object`, and `pixi.toml` holds no R. **Still open: the new + container and the resource recalibration.** The `conf/*.config` memory closures were tuned + around R's 2.27 GB median and must be re-measured, not carried over — the Python footprint is a + different function of the cell count and the gene count. +7. **Stage B/C/D validation** at full scale on one effect size. **Stage D is done** — four + replicates in one task against two tasks of two are byte-identical, and a different seed does + change the draws, so the invariance is not coming from the seed being ignored. It runs behind + `-m realdata`. **Stage B passed at full scale on moi5 cis, 2026-09-25** (§12). Stages 0 and C + are still open. +8. **Docs** — `README.md` and a pointer on `status.md` are done. `usage.md`, `methods.md` and + `output.md` still describe the R path. + +Phases 1–6 are done. Phase 7 is the one that decides whether `main` moves. + +## 11b. Running Stage B + +**The reference has to be regenerated.** This said the opposite until the centring bug was found, +and reading the existing `power_sweep/` tables was the whole reason Stage B was cheap. They were +produced by R before either fix, so they are a known-wrong reference: comparing against them would +report a difference that is real, correctly measured, and about the bug rather than the port. + +That means one R run on the Stage B targets, from `r-implementation` at `b346296` or later, and a +**re-prepare** as well as a re-simulate — the fitted baseline reads `fitted_coefs`, and no +`sim_input.rds` made before `d767af3` carries them. Both sides then run on their own defaults, +which is what makes the comparison a comparison of implementations. + +```sh +# 1. export the object with BOTH flags (once per dataset) +Rscript ../pysceptre/scripts/export_sceptre_dataset.R \ + --sceptre-object --out-dir export/ --all-genes --all-cells +python ../pysceptre/scripts/make_h5mu.py export/ + +# 2. prepare +watteg-prepare-sim-input --dataset export/dataset.h5mu --outdir prepared/ --n-jobs 8 + +# 3. simulate, on the default (fitted) baseline +watteg-run-power-simulation \ + --prepared prepared/ --pairs .tsv \ + --effect-size 0.15 --reps 100 --seed 20250812 --n-jobs 8 \ + --out stageb_sim.tsv + +# 4. the R reference, from the FIXED R, on the same targets and the same defaults. +# Run in a worktree of r-implementation; it needs its own prepare, and its own +# sceptre_template.rds and null-model fits, which the Python path does not have. +nextflow run -profile <...> -params-file <...> + +# 5. compare +workflow/compare_stage_b.py stageb_sim.tsv \ + /power/power_es0.15.tsv \ + --threshold-file prepared/discovery_threshold.txt +``` + +**Both sides on the default baseline.** `--expression-model size_factor` used to be mandatory here, +to match a reference that predated the baseline change. With the reference regenerated it would do +the opposite of its job: it would take the Python side off the model the R side is now using. + +**The R side can run short.** `compare_stage_b.py` computes the noise floor from each side's own +replicate count, so R at 20 replicates against Python at 100 is a valid comparison, just a blunter +one. Since R is the expensive side, that is where to spend less. + +**Cost, from the run's own timings**: `0.48s + 0.120s x pairs` per (target, replicate) at +`--n-jobs 8`. A 36-target, 428-pair sample is about 1.9 h at 100 replicates and 23 min at 20. +The whole sweep — 3,026 targets, 34,886 pairs — is roughly 130 h on one machine, which is what the +cluster is for. + +**What the replicate count buys.** The per-pair test compares two binomial estimates, so its floor +is `2*sqrt(p(1-p)/n_py + p(1-p)/n_r)` — at `p = 0.5` that is +/-0.32 with 20 Python replicates +against the reference's 100, and +/-0.13 with 100. The *aggregate* test is far sharper either way: +the shift in mean power over 428 pairs has a 2-se bound near 0.015 at 20 replicates. So a short run +already tests the thing most likely to be wrong -- a systematic bias from the port -- and a long +one is what makes the per-pair claim worth stating. + +## 12. What is left, and what would be wrong to skip + +**The two that block a real sweep.** + +- **Resource recalibration.** `conf/base.config`'s numbers are R's, now labelled as such rather + than left looking calibrated. A task uses `task.cpus` workers where R's used one, so both the + memory and the time models are a different shape. They err high, which wastes budget rather than + losing runs, but they are not a calibration until a real trace replaces them. +- **A container for the `gcb` profile.** The R image is not reusable and nothing has been built. + +**The one that decides whether this replaces the R path.** Stage B at full scale: one effect size, +100 replicates, all 34,886 pairs, against a **regenerated** R sweep (§11b). Everything measured so +far says the two agree — but on one target, eight pairs, forty replicates. That is evidence the +paths agree where they have been compared, and it is not the same claim. + +Stage B's first run at 36 targets **failed**, and that failure is what found the centring bug. Two +things follow that are easy to conflate. The first is that the failure was correct and its +diagnosis — 27 % less replicate spread in Python, with a floor that did not shrink with cell count, +pointing at a per-guide term — is now a **prediction**: a re-run against fixed R should show the +spread match, and if it does not, the diagnosis was wrong rather than incomplete. The second is +that a pass would confirm that diagnosis *and* the port at once, which is weaker than it sounds and +worth saying out loud rather than reporting as a clean green. + +> **Resolved 2026-09-24: the 27 % was an estimand difference, not a Python bug.** Python pinned the +> realised mean knockdown (a fixed element effect) from its first commit. The R code it was compared +> against, like the original DC-TAP code, centred on the wrong columns, so its realised mean was free +> to vary. On a real screen that is the same as not centring at all. Moving R's reorder before the +> centring (`2b76284`) made R pin too, and so silently changed which quantity R simulated; no one +> had chosen it. The owner has now chosen the fixed element effect for both languages, with control +> cells at exactly 1 (see `methods.md`, "What simulated power means"). A Stage B re-run therefore +> compares two implementations of the same estimand, and is expected to agree. + +### Stage B at full scale, 2026-09-25: the two implementations agree + +Both pipelines on moi5 cis: every one of the 33,066 pairs, es 0.15, 100 replicates each, the +screen's own permutation test, `guide_spread_c = 0.65`, same seed. R ran at `1f44a42` (run +`chaotic_lovelace`), Python at `6570024` (run `intergalactic_liskov`). The two prepares wrote +byte-identical `pairs.tsv` and the same discovery threshold, 0.000648397. + +| check | result | +|---|---| +| per-pair power within 2 se of Monte Carlo noise | **96.9 %** of pairs (≈95 % expected by chance) — pass | +| status at the 0.8 bar | **98.1 %** agree; 320 Python-only, 318 R-only, every one sitting on the bar — pass | +| mean power | Python 0.6040, R 0.6045 | +| median fold change | Python log2 −0.2352, R −0.2359, target log2(0.85) = −0.2345 | +| systematic shift | **−0.00055**, against a 2-se bound of 0.00047 (0.00048 clustered by target) — formally a fail | + +The shift is real at about 2.3 se and is 0.06 points of power. It is flat across expression +quintiles and sits in mid-power pairs (−0.19 points at power 0.3–0.7), the shape a small calibration +difference in the test gives. One is known and expected: R hoists the gRNA null model out of the +simulation (`FIT_NULL_MODELS`, fitted once per replicate on an independent null simulation), while +Python refits it inside every call (§3.1). A port bug would not show up as 0.06 points spread +evenly over 33,066 pairs with the 0.8 crossings symmetric to within two pairs. **Verdict: the Python +path computes the same power as the R path.** What Stage B does not cover: trans, where there is +no R sweep on this code, and other screens. + +**Stage 0, the noise floor, measured 2026-09-26 on the fast configuration** (`0651653`, cis, seeds +20250812 and 20250813): + +| comparison | mean diff | mean \|diff\| | within 2 se | same 0.8 call | +|---|---|---|---|---| +| Python vs Python, other seed | +0.0001 | 0.0263 | 97.0 % | 98.0 % (329 / 334) | +| Python (seed 20250812) vs R | −0.0003 | 0.0261 | 97.0 % | 98.0 % (320 / 325) | +| Python (seed 20250813) vs R | −0.0004 | 0.0265 | 97.0 % | 98.1 % (317 / 317) | + +- **Python and R now differ exactly as two Python runs with different seeds do.** What is left + between the implementations is Monte Carlo noise. +- **The mean shift is gone.** It is inside its 2-se bound (0.0005) at both seeds. Before one + permutation set per element and reused fits it was −0.00055; those two changes are not + separated. + +**Two pipeline bugs that only a real run could find**, both fixed; neither changes a number: + +- `prepare_sim_input` rebuilt `set(pairs["response_id"])` once per gene: 108 min on moi5 trans + (38,606 genes × 742,525 pairs), past the task's 1 h limit, against 92 s with the set built once + (`f2d684c`). cis hid it at 33,066 pairs. +- `COMPUTE_POWER` listed its inputs with `$(ls a b c)`. Only one pattern ever matches, `ls` exits 2, + and `bash -e` killed the task on that line with empty stdout and stderr (`efd0f05`). Every real + Python run so far died there; the stub never runs the script. The cis power table above was + computed from the pipeline's own consolidated Parquet with the module's exact command. + +**Cloud cost, measured, which replaces the R numbers in `conf/base.config` as a starting point.** +On `e2-standard-2` spot a POWER_SIMULATION task runs ~0.37 s per pair-replicate on cis, about 4× +the laptop's rate. Trans targets carry ~250 genes each, and there `--n-jobs` pays: on one target, +40 s per replicate at 1 worker, 26 at 2, 19 at 4 and 16 at 8, with byte-identical output. So trans +runs at `cpus = 4`, `reps_per_chunk = 10`; at 1 CPU a 20-replicate trans task needs ~2.5 h and would +hit the 2 h limit on every task. The memory closure asks 8 GB for any non-empty split (the predicted +figure always clears the 4 GB floor), while the 1,000 Python cis tasks peaked at 1.1-1.3 GB, +20-24 min each. + +**Three scientific questions are open and recorded, none of them acted on** (§3.2b, §3.2c, §5.2). +The largest, simulating from `exp(X . beta)`, has been taken; the other two are judgement calls +about which cells and which estimator belong, and both change every published number. + +**One thing deliberately not ported.** `fit_power_curve.R` serves the paper's reduced-design study +rather than the pipeline, and nothing in the DAG calls it. It stays in R. + +**The synthetic fixture.** `src/make_test_data.R` produces a sceptre object; the Python path needs +a `.h5mu`, so `assets/samplesheet_synthetic.csv` points at a file nothing generates yet. The stub +run works from a real export instead. + +**The numbers in `WattEG-paper`.** Both fixes change results, and every sweep in `power_sweep/` was +produced before them, as was §4 of `perturbplan_comparison.md`. Nothing was published, so this is a +regeneration rather than a correction — but it is a full sweep, and it is recorded in +`WattEG-paper/docs/bug_expression_scale_input.md` rather than here because it is the paper's work, +not the port's. + +## 13. Next: both estimands, and cis + trans in under an hour + +Decided with the owner on 2026-09-25. Items 1 and 2 are settled; items 3-5 wait on the speed +measurements in progress, and their numbers are estimates until then. + +**Status, 2026-09-25 (end of day).** Done: item 1 in Python and in R (`r-implementation` +`1a63eb3`); item 2 (`e6c6db6`); +3a (`3ea5baa`); 3b, the fast driver (`288cb0f`); 3c, fit reuse; item 4's per-pair power inside the +task, and its memory measurement. **The defaults are now the fast configuration:** `--permutations +per-target`, `--nulls sparse`, `--driver fast`, `--null-fits reuse` (CLI and Nextflow), with the +engine, `refit`, `scan` and `per-replicate` still selectable. A CRT screen needs `--driver engine +--null-fits refit`, because the fast driver runs the permutation test only. Not done: 3d (waits on +a pysceptre release) and item 6 (not to be run until decided). + +**Item 5, measured on the cloud the same evening (`0651653`, seed 20250812, 8 workers per task):** + +| | cis (`focused_hoover`) | trans (`maniac_bassi`) | +|---|---|---| +| wall clock, launch to summary | ~1 h | 1 h 52 min | +| simulation tasks | 100, median 11.9 min | 1,000, median 21.8 min, max 29.6 | +| cost | $2.48 | $67.57 (the previous trans run: $411, 5.6 h) | +| vs the previous Python run, same seed | +0.0003 mean power, 99.5 % same 0.8 call | +0.0002, 99.56 % same 0.8 call | + +- **Under an hour:** trans came in at 1 h 52 min, not under an hour. About 20 min of it is prepare + and the fitting step, which run before any simulation starts. +- **Per-task cost:** the simulation tasks ran at about 1.7x their laptop cost on 8 e2 vCPUs. +- **Laptop, single core, cis reference target:** 182 ms per pair-test before, 105 with the sparse + route, 70 with the fast driver, and 31.5 with fits reused from the file. + +**What does not change: the simulation keeps the full design.** Each replicate runs the screen's +actual test on its actual cells, covariates, guide assignment and threshold, including sceptre's +permutation test with its escalation. Every speed-up below has to preserve that. None of them +approximates the design. + +### 1. A second estimand: random guide effects -- done in Python and R + +**Done 2026-09-25 in Python** (`watteg.perturbation.effect_size_matrix(estimand=)`, `--estimand`, +Nextflow `estimand`; written as the last column of every per-simulation row, into the power table +and the summary, and refused when mixed). Checked on the shared fixture and the cis reference target: +- es = 0: identical rows under both (engine and fast driver, fixture; CLI, 20 simulations), apart + from the estimand label; the generator is left in the same state, so the counts are drawn alike. +- `fixed` output unchanged by the option: every earlier output (cis at es 0 and 0.15, trans, the + engine's per-replicate scan run) is today's file minus its new last column, byte for byte. +- `random`: the realised mean's sd over 2,000 draws matches `c * es * (1 - es) * sqrt(sum n_g^2) / + sum n_g` within 8 % at es 0.05, 0.15 and 0.5, and control cells stay exactly 1. +- cis reference target, 100 simulations, es 0.15: 808 calls of 1,200 under `fixed`, 781 under + `random` (mean power 0.673 against 0.651); the simulated fold change's sd over simulations rises + from 0.056 to 0.067 (median over pairs). + +**Still to do:** the R side (`simulate_effect_sizes` skipping `center_effect_size_matrix`) on the +`r-implementation` branch, so Stage B can check it; the cis sweep under `random` scored against +PerturbPlan (last check below), which waits on item 6. + +`--estimand fixed | random`, default `fixed`, in both implementations so Stage B can check it. + +| | `fixed` (today) | `random` | +|---|---|---| +| guide knockdown | Beta, mean es, sd `c * es * (1 - es)` | the same draw | +| element's realised mean over its perturbed cells | pinned to es in every replicate | left where the draw puts it | +| question answered | power for an element whose effect *is* es | power for an element whose effect is es *on average* | +| PerturbPlan setting that asks the same | `fold_change_sd = 0` | `fold_change_sd = c * es * (1 - es)` (0.0829 at es 0.15) | + +**Implementation.** Skip the pin and keep everything else. +- **Python:** `watteg.perturbation.effect_size_matrix` skips `_pin_to_mean`. +- **R:** `simulate_effect_sizes` skips `center_effect_size_matrix`. + +The control-cells-at-1 assertion stays in both. The estimand is written into every output, so a +power table always says which question it answers. + +**Why it is safe now and was not before.** The unpinned spread is zero at es = 0. The old absolute +`N(1 - es, 0.13)` gave a null element random effects, and "detected" it about 20 % of the time on +highly expressed genes. With the Beta spread the null arm is identical under both estimands. + +**Checks:** +- es = 0 gives byte-identical output under both estimands. +- Under `random`, the realised element mean has sd ≈ `c * es * (1 - es) * sqrt(sum n_g^2) / sum n_g` + over the guides' cell counts `n_g`: the variance of a cell-weighted mean, tested on the shared + fixture. +- `fixed` output is unchanged by the option's existence. +- A cis sweep under `random`, scored against PerturbPlan at `fold_change_sd = 0.0829`, compares the + same question at the same per-guide spread in both methods (WattEG-paper, + `docs/perturbplan_comparison.md`). + +### 2. One permutation set per target, drawn from the seed -- done, and the default + +Done in `e6c6db6`; the default since the fast configuration was adopted (2026-09-25), and recorded +in `methods.md`. + +All replicates of a target are tested against one permutation set, keyed on (seed, target, effect +size), not on the replicate. This is what sceptre itself does: its sampler reseeds +`mt19937(4)` on every call (pinned commit 3ba046b), so R's replicates already shared one fixed set, +as did the real screen. A per-target draw from the run's seed was chosen over sceptre's fixed set +so that one draw is not shared by every target of the same size. Outputs change relative to earlier +Python runs; that is expected and is recorded in `methods.md`. + +### 3. A faster driver + +#### 3a. First: take pysceptre's sparse route for the permutation nulls (measured, bit-identical) -- done + +Step 1 done in `3ea5baa` (`--nulls sparse`, now the default); step 2 is the fast driver (`288cb0f`). + +**The problem.** In a one-target call pysceptre computes the stage-1 and stage-2 permutation nulls +by a scan: it gathers a (B, n_trt, 14) array (227 MB at stage 2, n_trt = 406) and takes a running +sum over it, to read a single column (`PermutationPrefixSums`, `score_stat.py:411-441`). The scan +exists to serve many targets of different sizes from one set of draws. With one target it is pure +waste. pysceptre already has the alternative, a sparse indicator matrix times the gene's pieces +(`draws_to_matrix(...) @ stacked`, `score_stat.py:82-115, 274-300`). It uses that route whenever +the scan would exceed its memory limit, and the real moi5 screen takes it at stage 2 on its own, +because its largest target (607 cells) is above the limit (457). + +**Measured** (2026-09-25, single-core laptop, `speed/timing_decomposition/`). The route was switched +by making the scan decline at runtime, so pysceptre ran its own sparse code: + +| | today | sparse route | | +|---|---|---|---| +| stage-2 null, per escalated pair | 119.5 ms | 17.7 ms (4.0 build + 13.6 multiply) | | +| stage-1 null, per pair | 12.4 ms | 1.9 ms | | +| **cis**, per simulated pair-test (12 pairs x 100 simulations) | 182 ms | 87 ms | **2.1x** | +| **trans**, per simulated pair-test (255 pairs x 10 simulations) | 154 ms | 66 ms | **2.3x** | + +p-values, z-statistics and stages were identical on all 1,200 cis and 2,550 trans pair-tests, +max |dp| = 0. + +**Implementation.** Two steps, in order: +1. **Now, in the current engine.** Steer pysceptre onto the sparse route with a scoped, documented + runtime setting in `watteg.engine` (the scan's decline threshold), leaving pysceptre's source + untouched. It needs a test that p-values equal the scan route's on the shared fixture and on + one real target. This alone halves the cost of every sweep. +2. **Then, in the faster driver.** Build the sparse matrix once per target and stage and apply it + to every gene x simulation of that target in one product (the table below). With one + permutation set per target (item 2) the matrix is shared across simulations, and the build cost + (4.0 ms per pair-test today) disappears too. + +If pysceptre's owners want it, the same finding applies upstream: for a single target, the scan is +slower than the sparse route at every stage. That is a note for them, not a change made here. + +#### 3b. The rest of the driver -- done (`288cb0f`, `--driver fast`, now the default) + +A read-only map of pysceptre's discovery call found work that is repeated for no reason in the +simulation's call shape (one target, one replicate per call). All of it is avoidable without +editing pysceptre, by driving its public low-level functions (`fit_all_genes`, +`compute_precomputation_pieces`, `stack_pieces`, `run_low_level_test_full` with +`null_statistics_fn`): + +| work today | cost (laptop, cis profile) | replacement | +|---|---|---| +| all 30,497 permutations drawn per call, one `rng.choice` at a time | ~0.24 s per call | drawn once per target (item 2) | +| stage-1 and stage-2 nulls on the scan route | 119.5 / 12.4 ms (measured, 3a) | 3a first; then one sparse matrix per target and stage, `P @ [stacked_1 | ... | stacked_K]` for every gene x simulation at once; bit-identical per column (checked) | +| gene fits one replicate at a time, plus a duplicated `compute_precomputation_pieces` | ~44 + 5 ms per gene per replicate | fits batched across a target's replicates; not guaranteed bit-identical on Linux, so checked to tolerance | + +Measured breakdown of today's 182 ms per cis pair-test, against 1.2 ms in the real discovery of +the same screen: + +| cause | ms | share | fix | +|---|---|---|---| +| scan route, stage 2 | 83.5 | 46 % | 3a | +| gene refits | 49.3 | 27 % | batching across simulations, or R-style fit reuse | +| per-call overhead (draws) | 19.6 | 11 % | draws once per target | +| escalation rate (82 % of simulated pair-tests vs 3.3 % real) | 15.3 | 8 % | none: the cutoffs are below 1/500, so a callable pair must reach stage 2 | +| scan route, stage 1 | 10.5 | 6 % | 3a | + +- **R-style fit reuse, measured:** 1.24x on cis and 1.27-1.29x on trans. The call rate is + identical on cis (806 of 1,200), 8 pair-tests flip 4 each way, and p-values move a quarter as + much as a change of permutation seed does. +- **Estimated with everything combined:** about 25 ms per pair-test with fit reuse, or about 35-40 + ms with batched exact fits. **Exactness criterion:** on the +same permutation set, the fast driver's p-values match the current engine's per pair, bit for bit +where the operations are the same and to a stated tolerance where the fits are batched. + +#### 3c. Then: reuse each gene's null-model fit (decided 2026-09-25) -- done + +**Done 2026-09-25** (`watteg.null_fits`, `watteg-fit-null-models`, the `FIT_NULL_MODELS` process, +`--null-fits reuse | refit`, `--null-fits-file`). Checked: +- `refit` is byte-identical to the fast driver before the option existed, on the cis reference + target (100 simulations, es 0 and 0.15), the trans reference target (10 simulations) and the + engine's per-replicate scan output. +- The fits reproduce the benchmark's (`speed/hoist_null_fit/fits_cis.tsv`): 34 of 48 exactly, the + rest within 3e-15, the difference being the benchmark's many-gene baseline product. +- A fit depends only on (gene, simulation): a gene fitted alone equals it fitted with others; a + file made for more genes and simulations, and fits made in the task at 1 and 2 workers, give the + same bytes (unit test and realdata test); a file from another seed is refused. +- `reuse` against `refit`, same counts and permutations (per-target), on the fast driver: + + | | refit calls | reuse calls | flips | McNemar p | max per-pair \|d power\| | beyond Wilson half-width | + |---|---:|---:|---:|---:|---:|---:| + | cis, 12 pairs x 100 | 808 | 805 | 5 / 2 | 0.45 | 0.02 | 0 of 12 | + | trans, 255 pairs x 10 | 1,280 | 1,282 | 3 / 5 | 0.73 | 0.10 | 0 of 255 | + + Worker time at 8 workers, fit step excluded: cis 129.0 -> 74.2 s (1.74x), trans 236.5 -> 143.9 s + (1.64x), more than the benchmark's 1.24-1.29x because the fast driver removed the other overheads + the fit was averaged against. A task that fits its own genes pays the fits back (cis reference + target: 12.1 s of fits at 8 workers), so the saving on cis comes from `FIT_NULL_MODELS` fitting + each gene once per sweep. + +**Why.** The gene refit is the largest cost left after 3a and 3b: about 49 ms of every simulated +pair-test, 27 % of today's cost. That is because each simulation refits every gene once per target. +The real screen fits each gene once for about 135 targets. + +**What.** R's `FIT_NULL_MODELS` approximation, in Python: +- Fit each gene's null model once per simulation, on an independent es = 0 draw keyed + `rng_for(seed, "__null_fit__|" + gene, rep, 0.0)`. +- Use pysceptre's own `fit_all_genes`, at the same batch width and with the same `x_outer_flat`. +- Reuse that fit for every target the gene is tested against, and every effect size. +- The fits are a small file: about 2.5 MB for 244 genes x 100 simulations. + +**Pipeline.** A `FIT_NULL_MODELS` step between `PREPARE_SIM_INPUT` and `POWER_SIMULATION`, about +25-30 CPU-minutes per sweep, feeding every simulation task. The fast driver takes the fits in place +of calling `fit_all_genes` itself. `--null-fits reuse | refit` keeps the exact refit available; +the default is `reuse`. + +**Measured cost and fidelity** (`speed/hoist_null_fit/`, laptop, same counts and permutations): +- **Speed:** 1.24x on cis, 1.27-1.29x on trans. +- **Calls:** the call rate is identical on cis (806 of 1,200 pair-tests), with 8 flips, 4 each + way. trans calls 1,284 against 1,283. +- **p-values:** they move about a quarter as much as a change of permutation seed. +- **Bias:** no directional bias is detectable. + +**Checks:** +- `refit` stays byte-identical to today. +- `reuse` against `refit` on the cis and trans reference targets: call rate and per-pair power + within Monte Carlo noise. +- A fit is keyed only on (gene, simulation), so no result depends on which other genes or targets + share its task. + +#### 3d. After pysceptre's next release: retire `--nulls` + +pysceptre `dev` (0de1bff, broadinstitute/pysceptre#2) now chooses the scan only when it pays for +itself, which a single target never does. Once that is released: +- bump the pin and rebuild the image; +- drop the `--nulls` switch; +- keep the real-data test that compares the two routes. + +The issue stays open for drawing permutation stages lazily. + +### 4. Task shape and resources -- per-pair power inside the task: done + +**Done 2026-09-25: per-pair power inside each task.** When a task holds all simulations of its pairs +(`reps_per_chunk == num_replicates`, the default), `POWER_SIMULATION` writes per-pair counts +(`--partials-out`: simulations called, simulations used, the fold-change and cell-count sums, the +simulation range) and `COMPUTE_POWER` adds them up (`--partials`). The per-simulation table and +`CONSOLIDATE_REPLICATES` run only under `keep_per_simulation` (default false) or when simulations +are chunked across tasks. Checked: the power table from the counts equals the one from the rows, +byte for byte, in unit tests, a realdata test and a real local Nextflow run on moi5 cis (the +counts' table, and `watteg-compute-power` on the published Parquet of a `keep_per_simulation` run). +That needed one fix on the rows' side: pandas' default CSV parser returned a neighbouring float for +about half of a TSV's values (285,883 of 500,000 measured), so the rows are now read with +`float_precision="round_trip"`. The stub DAG passes in all four shapes (default, kept rows, +chunked simulations, fast driver with fits), and a tiny real run passes on moi5 cis (defaults) and +on day0 (a CRT screen, `--driver engine --null_fits refit`). + +**Measured 2026-09-25: memory and time of a task under the new defaults.** A 330-pair moi5 cis slice +(`watteg-split-pairs --n-splits 100`, split 1: 29 targets, 185 genes), 100 simulations, fits read +from a `FIT_NULL_MODELS` file, per-pair counts only: + +| | workers | peak memory | wall | worker time per pair-test | +|---|---:|---:|---:|---:| +| Linux container (fork; cgroup `memory.peak`) | 8 | **3.38 GB** | 278 s | -- | +| macOS laptop (spawn; summed RSS of the tree) | 8 | 9.73 GB | 296 s | 47.5 ms | +| macOS laptop | 12 | 10.2 GB | 272 s | 54.1 ms | +| macOS laptop, 3 such tasks at once | 3 x 4 | 4.1-4.2 GB each | 727 s for all 3 | 70 ms | +| `FIT_NULL_MODELS`, 244 genes, Linux | 8 | 3.04 GB | 59 s (24 simulations) | -- | +| `FIT_NULL_MODELS`, 244 genes x 100, macOS | 8 | 6.9 GB | 208 s | -- | + +`power_simulation_memory` stays **8 GB**: 2.4x the Linux peak, and no 8-vCPU predefined machine has +less. The macOS numbers are local-run numbers: a spawned worker loads its own copy of the inputs +(~0.6 GB) where a forked one shares the parent's. A task's wall time is bounded by its largest +target (221 s of the 296 s at 8 workers), so on one machine several smaller tasks at once use the +cores better than one wide task: 3 x 4 workers finished 3 slices in 727 s against 3 x 296 s one +after another. trans (~250 genes per target) is not measured under this configuration. + +- **A task holds whole targets with all their replicates,** and writes per-pair power directly. The + 74M-row consolidation and power steps become optional, kept only for a per-replicate table when + asked for. +- **8 workers per task** (`a85188a`, pushed): cis tasks fit in 8 GB. trans at 4 workers + averaged ~7.4 GB per task, so trans memory is set from a measurement with the fast driver, not + from the discovery benchmark. +- **Task sizing:** roughly 15-20 min at 8 workers, within the 10,000 preemptible-CPU quota. +- **Machine family:** e2 cores ran ~3x slower than the laptop; n2d or c2d are measured on one small + cloud run before a sweep. + +### 5. The target + +Both moi5 sweeps, cis (3.3M pair-tests) and trans (74M), in under an hour of wall clock, about 30-40 +min of it simulation. On the estimates above this needs a pair-test of ≤ 0.15-0.2 s per vCPU on +cloud against 0.55 s today. The measurements decide whether items 3-4 reach it. + +### 6. Re-check the guide spread, value and form (not yet run) + +The spread behind 0.0829 at es 0.15 is `c * es * (1 - es)` with c = 0.65 (`methods.md`, +"Guide-to-guide variability"). + +**Settled by the 2026-09-25 data audit** (WattEG-paper `analysis/guide_spread/build_data.R`): +- **The enhancer bins were noise-corrected, like the null pairs.** Each pair's spread is + `tau2 = var(fc) - cf_t * mean(se^2)`, where `cf_t` is calibrated on groups of 14 non-targeting + guides per expression tertile (`guide_spread_v2.R:97`, `:78-84`), and the bins pool `tau2`. + `methods.md` did not say so, and should. +- **The curve runs high at large effects.** It peaks at es 0.5 (sd 0.163), while the pooled bins + peak near es 0.37 (0.144) and fall to 0.108 at 0.59. The c that best fits the bins is 0.593, + against the 0.65 fitted per pair. + +**Still to do, before anything else depends on 0.65:** +- **Is the calibration adequate?** `cf_t` comes from non-targeting guide groups. Check it against + held-out non-targeting groups and against the null elements, per screen. +- **Refit both the value and the form:** + - reconcile the per-pair fit (0.65) with the bin fit (0.593); + - test whether a form that falls faster at large effects fits better than `c * es * (1 - es)`. +- **Carry any change through:** + - the default `guide_spread_c`; + - the PerturbPlan setting matched to the random estimand (item 1); + - the 0.0829 in WattEG-paper `docs/perturbplan_comparison.md`, `paper/06_prediction.md`, and the + figure `figures/guide_spread/guide_spread.png`. + +Under the fixed estimand the stakes are small: no moi5 pair's power moves by more than 0.02 between +this spread and none. Under the random estimand, and in the PerturbPlan comparison, the value +matters directly. diff --git a/docs/status.md b/docs/status.md index 41d09d2..005cb01 100644 --- a/docs/status.md +++ b/docs/status.md @@ -5,6 +5,27 @@ nav_order: 7 # Status and handoff +> **The pipeline now runs on Python, and most of this document describes the R implementation it +> replaced.** The port is recorded in +> [Plan - pysceptre backend]({{ site.baseurl }}{% link pysceptre-backend.md %}), which is the +> current account of what runs, what was measured and what is still open. Read this one for the R +> path's history, its measured numbers and the correctness fixes that produced them — all of which +> remain true of the R implementation, which still exists and is the reference the Python path was +> validated against. +> +> What changed that this document would otherwise mislead about: +> +> - **`FIT_NULL_MODELS` and `MERGE_NULL_MODELS` are gone.** The whole "which per-gene null model +> does the simulation test against" section below settled on `null_fit`, a hoist that existed +> because R refitting inside every call cost 4.3x. The Python path does that refit as a matter of +> course, so the hoist has nothing to buy. +> - **The simulation draws from `exp(X . beta)`, sceptre's own null model**, not from a +> size-factor-normalised mean. Every power number below predates that change. +> - **The input is a `.h5mu`, not a sceptre object**, and the environment holds no R. +> - **The cost model and the resource calibrations below are R's.** They are not valid for the +> Python path and have not yet been replaced. + + State of the refactor, written so the work can be picked up by someone else. **Short version:** the five pipeline steps are complete, run standalone, and have now run on diff --git a/docs/usage.md b/docs/usage.md index 8573069..bace641 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -5,201 +5,258 @@ nav_order: 2 # Usage -Every step is a standalone executable in `src/` that takes explicit command-line arguments and -prints `--help`. Nothing reads a global config object, so any step can be run, re-run or debugged -on its own. +```sh +nextflow run . -profile sherlock -params-file config/config.yml +``` -> **Orchestration is in progress.** The steps below are the complete, working pipeline as run by -> hand or from a shell loop. A Nextflow workflow that wires them together with a SLURM profile is -> the next piece of work and is not part of this repository yet. `config/config.yml` already holds -> the parameters it will consume. +That is the whole pipeline. Every step is also a console script taking explicit arguments and +printing `--help`, so any one of them can be run, re-run or debugged on its own; nothing reads a +global config object. + +--- + +## Getting a dataset in + +The pipeline's input is a `.h5mu`, not a sceptre object. Producing one is a one-off step per +dataset, and it is **the only place R appears**: + +```sh +Rscript pysceptre/scripts/export_sceptre_dataset.R \ + --sceptre-object results/sample1/sceptre_object.rds \ + --out-dir export/ --all-genes --all-cells +python pysceptre/scripts/make_h5mu.py export/ +``` + +The object must have been through `assign_grnas()` and `run_qc()` with +`grna_integration_strategy = "union"`. + +**Both flags are required, and for different reasons.** + +`--all-genes` because the poscounts size factors are a per-cell reduction over the *whole gene set*. + +`--all-cells` because they are also computed against a per-gene geometric mean taken over *every +cell in the object*, including the ones QC removed. An export restricted to the QC-passing cells +gives different size factors for the cells that remain, and different normalised means for every +gene — measured on day0, a 0.46 % shift in size factor and 2.2 % in the raw gene mean. Nothing +downstream would report it. `watteg-prepare-sim-input` warns if it is handed an export without +them. + +The export handles odm-backed (out-of-core) matrices itself, which is why nothing in the pipeline +takes an `--response-odm` flag any more. --- ## The five steps ``` -sceptre_object.rds +dataset.h5mu + | + | watteg-prepare-sim-input once per sample + +--> sim_input.h5 per-gene and per-cell statistics, the model + | coefficients, and the perturbation matrices + +--> pairs.tsv QC-passing discovery pairs + +--> pairs_with_info.tsv every pair with its real-data QC counts + +--> grna_targets.tsv gRNA -> target mapping (many-to-many) + +--> discovery_threshold.txt the p-value a simulation must beat + +--> analysis_mode.tsv which test the screen ran, and its resampling budget | - | prepare_sim_input.R once per sample - +--> sim_input.rds per-gene / per-cell statistics + perturbation matrices - +--> sceptre_template.rds the sceptre object with both count matrices emptied - +--> pairs.tsv QC-passing discovery pairs - +--> grna_targets.tsv gRNA -> target mapping - +--> discovery_threshold.txt the p-value a replicate must beat - +--> analysis_mode.tsv which test the screen ran (crt or permutations) and its MOI + | watteg-split-pairs once per sample + +--> split_001.tsv ... balanced chunks of targets | - | split_pairs.R once per sample - +--> split_001.tsv ... balanced chunks of targets + | watteg-fit-null-models once per sample, under --null-fits reuse + +--> null_fits.h5 each gene's null-model fit, per simulation | - | run_power_simulation.R once per (split, effect size, replicate chunk) - +--> sim_*.tsv one row per (pair, replicate) + | watteg-run-power-simulation once per (split, effect size, simulation chunk) + +--> *.partial.tsv.gz per-pair counts (--partials-out), by default + +--> *.tsv.gz one row per (pair, simulation) (--out), optional | - | compute_power.R once per effect size - +--> power_es*.tsv one row per pair, with confidence intervals + | watteg-consolidate-replicates once per effect size, only if the rows were written + +--> replicates_es*.parquet the same rows, as one file | - | summarize_power.R once per sample - +--> power_summary.tsv one row per pair, one column per effect size + | watteg-compute-power once per effect size, from the counts or the rows + +--> power_es*.tsv one row per pair, with confidence intervals + | + | watteg-summarize-power once per sample + +--> power_summary.tsv one row per pair, one column per effect size ``` ---- +There is no `sceptre_template.rds`. It was an R object carrying the covariate matrix and the +analysis parameters, which now live in `sim_input.h5` and `analysis_mode.tsv`. -## 1. `prepare_sim_input.R` +`watteg-fit-null-models` is the Python version of R's `FIT_NULL_MODELS`: each gene's null model is +fitted once per simulation, on an independent draw with no knockdown, and every target the gene is +tested with reuses that fit (`--null-fits reuse`). Without it (`--null-fits refit`) each simulation +refits every gene once per target, the exact configuration, at about 1.7× the cost of a simulated +pair-test. See [Methods](methods.md#null-model-fits). -Derives everything the simulation needs from the sceptre object, once, so the parallel tasks read -small files instead of a multi-gigabyte object. +--- + +## 1. `watteg-prepare-sim-input` ```sh -Rscript src/prepare_sim_input.R \ - --sceptre-object results/sample1/sceptre_object.rds \ - --outdir prepared/ +watteg-prepare-sim-input --dataset export/dataset.h5mu --outdir prepared/ ``` | Option | Default | Meaning | |---|---|---| -| `--sceptre-object` | required | Input sceptre object. The only required input. | -| `--response-odm` | — | Path to the response matrix's backing `.odm` file. Only needed if `--sceptre-object`'s response matrix is odm-backed (out-of-core); ignored otherwise. | -| `--outdir` | — | Directory for all outputs; or name each `--out-*` explicitly. | -| `--out-sim-input` | `/sim_input.rds` | Per-gene and per-cell statistics, plus the gRNA and target perturbation matrices. | -| `--out-sceptre-template` | `/sceptre_template.rds` | The sceptre object with `@response_matrix` and `@grna_matrix` emptied. | -| `--out-pairs` | `/pairs.tsv` | QC-passing discovery pairs. | -| `--out-grna-targets` | `/grna_targets.tsv` | gRNA → target mapping. | -| `--out-threshold` | `/discovery_threshold.txt` | Largest nominal p-value that survived correction in the real analysis. | -| `--out-analysis-mode` | `/analysis_mode.tsv` | The resampling mechanism (`crt` or `permutations`) and MOI, read off the object. Power is only meaningful against the test the screen actually ran, and that test is set upstream and inherited silently — see below. | -| `--all-genes` | off | Keep every gene, not just those in QC-passing pairs. Inspection only. | -| `--no-compress` | off | Write uncompressed `.rds`. Compression is on by default because it measured both smaller *and* faster to read. | - -It logs which categorical covariates are available for `--cell-batches`, which is worth reading: -the column is dataset-specific (e.g. `batch_factor`, `replicate_factor`) and is often **not** called -`batch`. - -## 2. `split_pairs.R` - -Splits targets into balanced chunks. Whole targets stay together, because the simulation builds -per-target state once and then loops over replicates. +| `--dataset` | required | An `--all-genes --all-cells` export. | +| `--outdir` | required | Directory for all six outputs. | +| `--threshold` | derived | Set the significance threshold instead of deriving it from the export's discovery result. | +| `--n-jobs` | 1 | Workers for the per-gene fits. | + +Fits each gene's Poisson GLM and negative-binomial theta — the same model sceptre's own +`perform_response_precomputation` fits, reproduced to about ten significant digits — and stores the +coefficients. A gene whose theta hits the estimator's bounds is **refused** rather than simulated +from: a clamped theta is not an estimate, and drawing counts from it would state a noise level the +data never supported. (sceptre clamps and carries on, which is reasonable for an analysis and not +for a simulation.) + +--- + +## 2. `watteg-split-pairs` ```sh -Rscript src/split_pairs.R --pairs prepared/pairs.tsv --n-splits 280 --outdir splits/ +watteg-split-pairs --pairs prepared/pairs.tsv --n-splits 280 --outdir splits/ ``` | Option | Default | Meaning | |---|---|---| -| `--pairs` | required | `pairs.tsv` from step 1. | | `--n-splits` | required | Number of chunks. Must not exceed the number of targets. | -| `--outdir` | `.` | Where to write the chunks. | -| `--prefix` | `split_` | Filename prefix; files are zero-padded so lexical order matches numeric order. | -| `--target-overhead` | `0` | Extra weight per target, in pair-equivalents, to account for the fixed per-target cost of a sceptre call. `0` balances purely on pair count. | +| `--target-overhead` | 2.0 | Pairs-equivalent fixed cost of a target, from the measured cost model. | +| `--prefix` | `split_` | Filename prefix; files are zero-padded so lexicographic order is numeric order. | -Balancing is on **pairs**, not on target count, and it reports the achieved imbalance so you can -see it. It also refuses to produce empty splits. +A target's pairs cannot be separated — the simulation draws one count matrix per (target, +simulation) and tests every one of that target's genes against it — so targets are the unit and the +job is bin packing. Weighted by pairs **plus an overhead**, because a task's cost is +`intercept + slope × pairs` and weighting by pairs alone over-fills the splits that hold many small +targets. -## 3. `run_power_simulation.R` +--- -The actual simulation. One invocation handles one split × one effect size × one chunk of replicates. +## 3. `watteg-run-power-simulation` ```sh -Rscript src/run_power_simulation.R \ - --sim-input prepared/sim_input.rds \ - --sceptre-template prepared/sceptre_template.rds \ - --pairs splits/split_001.tsv \ - --grna-targets prepared/grna_targets.tsv \ - --effect-size 0.15 \ - --reps 100 \ - --seed 20250812 \ +watteg-run-power-simulation \ + --prepared prepared/ --pairs splits/split_001.tsv \ + --effect-size 0.15 --reps 100 --seed 20250812 \ + --null-fits-file prepared/null_fits.h5 \ --out sim/split_001_es0.15.tsv ``` | Option | Default | Meaning | |---|---|---| -| `--effect-size` | required | Fractional decrease in expression: `0.15` = 15% knockdown. | +| `--effect-size` | required | A **fractional decrease**: 0.15 is a 15 % knockdown. | | `--reps` | required | Simulations in this chunk. | -| `--rep-offset` | `0` | Simulations already covered by earlier chunks; keeps the reported `rep` unique. | -| `--seed` | required | Base seed. Required, not optional — see *Reproducibility* below. | -| `--guide-spread-c` | `0.65` | Guide-to-guide spread: each guide's knockdown is Beta with mean es and sd c·es·(1−es), so zero at es = 0. Replaces `--guide-sd`, which is refused. See [Methods](methods.md). | -| `--n-control-cells` | unset | Sample this many controls instead of using all. **Biases power downward; leave unset.** | -| `--cell-batches` | unset | Covariate column to stratify control sampling by. Only with `--n-control-cells`. | -| `--gc-every` | `0` | Call `gc()` every N replicates. `0` disables it. | +| `--rep-offset` | 0 | Simulations already covered by earlier chunks, so `rep` stays unique. | +| `--seed` | required | Results are stochastic and must be reproducible. | +| `--estimand` | `fixed` | What the power is for: an element whose effect *is* es (`fixed`, the guides' mean pinned), or *is es on average* (`random`). See [Methods](methods.md#the-random-estimand-as-an-option). | +| `--guide-spread-c` | 0.65 | Guide-to-guide spread: each guide's knockdown is Beta with mean es and sd c·es·(1−es), so zero at es = 0. Replaces `--guide-sd`, which is refused. See [Methods](methods.md). | +| `--permutations` | `per-target` | One permutation set per target, drawn from the seed, shared by its simulations. `per-replicate` draws one per simulation (engine only). | +| `--nulls` | `sparse` | How the engine computes the permutation nulls; `scan` is pysceptre's default. Identical results. | +| `--driver` | `fast` | `fast` tests a target's simulations together (byte-identical to `engine` under `per-target` / `sparse`); `engine` calls pysceptre once per (target, simulation). | +| `--null-fits` | `reuse` | `reuse`: each gene's null model fitted once per simulation and shared across targets (needs `--driver fast`). `refit`: per target, exactly. | +| `--null-fits-file` | none | `watteg-fit-null-models`' output. Without it, a `reuse` task fits its own genes first; same output. | +| `--n-jobs` | 8 | Worker processes for the task. | +| `--expression-model` | `fitted` | Where a gene's unperturbed expected counts come from. | +| `--out` | none | One row per (pair, simulation), as TSV. | +| `--partials-out`, `--threshold-file` | none | Per-pair counts for `watteg-compute-power --partials`, taken at the discovery threshold. Complete when the task holds all simulations of its pairs. At least one of `--out` and `--partials-out` is required. | + +**The defaults are the fast configuration**, each part measured before it was adopted +([Methods](methods.md#running-the-screens-test-on-each-simulation)). The previous configuration is +`--driver engine --null-fits refit`, plus `--permutations per-replicate --nulls scan` for the exact +earlier behaviour. **A CRT screen needs `--driver engine --null-fits refit`**: the fast driver runs +the permutation test only, and says so rather than testing a CRT screen by permutations. + +**`--effect-size 0` is allowed on purpose.** It simulates no effect: perturbed cells are drawn from +the unperturbed mean and the screen's own test is run on them, so the p-values should be uniform. +It is the only way to measure this pipeline's type-I error from its own output. Run it as its own +sweep, not as another point on a curve. The two estimands are the same simulation there. + +**`--expression-model`.** `fitted` draws from `exp(X·β)`, the expected count sceptre's own null +model gives that cell, so the simulation and the test that judges it are on one scale. +`size_factor` reproduces the pre-2026-09-21 behaviour — a size-factor-normalised gene mean scaled +by the cell's poscounts factor — and exists only to compare against sweeps already run with it. It +mixes two models, runs about 4 % low, and reproduces 86.5 % of the observed count variance against +the fitted model's 99.5 %. + +--- -### Running the full grid by hand +## 3b. `watteg-fit-null-models` ```sh -for es in 0.15 0.2; do - for split in splits/split_*.tsv; do - name=$(basename "$split" .tsv) - Rscript src/run_power_simulation.R \ - --sim-input prepared/sim_input.rds \ - --sceptre-template prepared/sceptre_template.rds \ - --pairs "$split" --grna-targets prepared/grna_targets.tsv \ - --effect-size "$es" --reps 100 --seed 20250812 \ - --out "sim/${name}_es${es}.tsv" - done -done +watteg-fit-null-models --prepared prepared/ --reps 100 --seed 20250812 --n-jobs 8 \ + --out prepared/null_fits.h5 ``` -## 4. `compute_power.R` +| Option | Default | Meaning | +|---|---|---| +| `--reps`, `--rep-offset` | required, 0 | Fits simulations offset+1 .. offset+reps. | +| `--seed` | required | The simulation run's seed; recorded, and checked by the simulation. | +| `--expression-model` | `fitted` | The simulation run's baseline; recorded and checked. | +| `--pairs` | `pairs.tsv` in `--prepared` | Whose genes to fit. | +| `--n-jobs` | 8 | Workers; one simulation's fits are one unit of work. | + +Pass the file to `watteg-run-power-simulation --null-fits reuse --null-fits-file`. A task given no +file fits its own genes first with the same keyed draws, so the output is the same bytes either way; +the file only saves refitting a gene in every task that tests it. Refused for a CRT screen, whose +simulations the fast driver (the file's only reader) does not run. + +--- -Turns per-simulation results into power per pair, with a Wilson interval. +## 4. `watteg-compute-power` and `watteg-summarize-power` ```sh -Rscript src/compute_power.R \ - --simulations "$(ls sim/*_es0.15.tsv | paste -sd, -)" \ - --threshold-file prepared/discovery_threshold.txt \ +watteg-compute-power \ + --simulations sim/ --threshold-file prepared/discovery_threshold.txt \ --out power_es0.15.tsv + +watteg-summarize-power \ + --power power_es0.05.tsv power_es0.15.tsv \ + --sim-input prepared/sim_input.h5 --out power_summary.tsv ``` -| Option | Default | Meaning | -|---|---|---| -| `--simulations` | required | Comma-separated per-simulation TSVs. | -| `--threshold-file` | — | File from step 1. Mutually exclusive with `--alpha`. | -| `--alpha` | — | Explicit p-value threshold. Only for objects with no discovery results. | -| `--conf-level` | `0.95` | Confidence level for the Wilson interval. | +`watteg-compute-power` takes either `--partials` (the simulation tasks' per-pair counts) or +`--simulations` (one row per pair and simulation), and gives the same table from both when each +pair's simulations came from one task. Both take files or directories; a directory expands to the +`.tsv`, `.tsv.gz` and `.parquet` files inside it, sorted. In the pipeline the rows are written only +under `keep_per_simulation: true`, or when `reps_per_chunk` < `num_replicates`. `--power-threshold` (default 0.8) sets what "detectable" means +for the minimum-detectable-effect-size columns. -Run it once per effect size — it refuses input that mixes effect sizes, and refuses duplicated -`(target, gene, replicate)` rows, which is what an overlapping `--rep-offset` would produce. +--- -## 5. `summarize_power.R` +## Reproducibility -One row per pair, one `power_at_effect_size_` column per effect size, plus the smallest tested effect -size at which each pair reaches a target power. +A pair's power does not depend on how the work was split up. Every draw is keyed on +`(seed, target, simulation, effect size)` and nothing else, so running simulations 1–100 in one +task and in five tasks of twenty gives **byte-identical** results, and so does moving a target from +one split to another. A reused null-model fit is keyed on (seed, gene, simulation), so it is the +same wherever it was made. That is checked end to end: ```sh -Rscript src/summarize_power.R \ - --power power_es0.15.tsv,power_es0.2.tsv \ - --power-threshold 0.8 \ - --out power_summary.tsv +WATTEG_PREPARED=prepared/ pytest -m realdata ``` +The streams differ from the R implementation's and always would — different generators — so the two +agree statistically rather than draw for draw. See +[Plan - pysceptre backend]({{ site.baseurl }}{% link pysceptre-backend.md %}) for what has been +measured. + --- -## Reproducibility +## Sizing a cluster run -`--seed` is required. Seeds are derived per `(seed, target, replicate, effect_size)`, not per task, -which has two consequences worth relying on: +The unit of work is (split × effect size × simulation chunk), and essentially all of the compute is +in step 3. A task's cost follows `intercept + slope × pairs`, so: -- **Results do not depend on how work is divided.** The same `--seed` gives identical numbers - whether you use 1 split or 280, and whether simulations run in one chunk or ten. This is verified - in the test suite. -- **Runs are extensible.** Going from 100 to 400 simulations leaves simulations 1–100 byte-identical, - so you can add simulations with `--rep-offset 100` instead of recomputing. +- halving the **pairs** saves less than half, because the intercept does not move; +- halving the **simulations** saves exactly half; +- effect sizes cost the same as each other. -## Sizing a cluster run +`--n-jobs` buys wall clock rather than CPU time: measured on a 10-performance-core machine, 8 +workers gave 2.5× the wall-clock speed for 1.5× the CPU, and 14 was slower than 8. -Measured on a 292-gene × 586,309-cell dataset with 2,798 targets and 32,386 QC-passing pairs, using -all control cells: - -| Quantity | Value | -|---|---| -| One `run_discovery_analysis()` call | 3.4–4.8s | -| Total at 100 simulations | ~295 CPU-hours per effect size | -| Peak memory, simulation task | 1.5 GB → request 4 GB | -| Peak memory, `prepare_sim_input.R` | 7.7 GB → request 12 GB | -| Input read per task | 59 MB, ~1.1s | - -**Prefer more splits over more simulation chunks.** An extra split costs one extra ~1.1s input load; -an extra simulation chunk re-pays the *per-target* setup (control selection, guide status, cell -permutation) once per chunk per target. Only chunk simulations when you need more parallelism than -one-target-per-task, or when a single task would exceed your queue's time limit. - -For the dataset above, ~10 targets per task is about an hour of work, so `--n-splits 280` with no -simulation chunking gives 280 tasks per effect size — comfortably under the `MaxArraySize` of 1000 -that most SLURM installations use. +A task's wall time is bounded by its largest target, whose simulations are one unit of work. On one +machine, several narrower tasks at once therefore beat one wide one: on a 14-core laptop, three +330-pair cis tasks at 4 workers each finished in 727 s, against 296 s each at 8 workers one after +another. For a local sweep, `-profile local --power_simulation_cpus 4` with Nextflow's +`executor.cpus = 12` runs three at once; each took ~4.2 GB on macOS. diff --git a/main.nf b/main.nf index b36551a..52aea07 100644 --- a/main.nf +++ b/main.nf @@ -6,32 +6,43 @@ // // The DAG, and why it has the shape it has: // -// samplesheet -> PREPARE_SIM_INPUT ---+-> SPLIT_PAIRS -----------------+ -// | | -// +-> FIT_NULL_MODELS (x reps) | -// -> MERGE_NULL_MODELS -------+ -// v -// POWER_SIMULATION (split x effect size) -// -> collectFile by (sample, effect size) -// -> COMPUTE_POWER -> SUMMARIZE_POWER +// samplesheet -> PREPARE_SIM_INPUT -> SPLIT_PAIRS +// | -> POWER_SIMULATION (split x effect size x rep chunk) +// +-> FIT_NULL_MODELS ---^ -> per-pair counts -> COMPUTE_POWER +// -> [CONSOLIDATE_REPLICATES] -> SUMMARIZE_POWER // -// FIT_NULL_MODELS hangs off PREPARE_SIM_INPUT rather than off SPLIT_PAIRS because a gene's null -// model is independent of both the target and the effect size: it is fitted on a null simulation -// with no knockdown. One set of fits therefore serves every split and every effect size in a sweep -// -- 100 replicates, not 100 x however many effect sizes. Making it a sibling of SPLIT_PAIRS rather -// than a descendant is what expresses that. +// Power is computed inside each simulation task when the task holds all simulations of its pairs +// (reps_per_chunk == num_replicates, the default): it writes per-pair counts and COMPUTE_POWER adds +// them up, which gives the table the per-simulation rows give, byte for byte. The rows, and +// CONSOLIDATE_REPLICATES that publishes them as Parquet, run only under keep_per_simulation or when +// simulations are chunked across tasks, where power is computed from the rows as before. +// +// FIT_NULL_MODELS runs under --driver fast --null-fits reuse: each gene's null model is fitted once +// per simulation, on an independent draw with no knockdown, and every simulation task reuses that +// fit for every target the gene is tested with, instead of refitting it per target. It is R's old +// FIT_NULL_MODELS approximation, rebuilt in Python and measured before it was adopted +// (docs/pysceptre-backend.md, section 13, item 3c). --null-fits refit skips it and keeps the exact +// per-target refit. nextflow.enable.dsl = 2 include { PREPARE_SIM_INPUT } from './modules/local/prepare_sim_input' include { SPLIT_PAIRS } from './modules/local/split_pairs' include { FIT_NULL_MODELS } from './modules/local/fit_null_models' -include { MERGE_NULL_MODELS } from './modules/local/merge_null_models' include { POWER_SIMULATION } from './modules/local/power_simulation' include { CONSOLIDATE_REPLICATES } from './modules/local/consolidate_replicates' include { COMPUTE_POWER } from './modules/local/compute_power' include { SUMMARIZE_POWER } from './modules/local/summarize_power' +// Resolve a samplesheet path against the repository root rather than the launch directory, so a +// run's validity does not depend on where it was started from. A scheme-prefixed URI (gs://, s3://, +// az://) is absolute in the same sense a leading '/' is, and must not be prefixed either. +// +// Test the STRING, not file(p).isAbsolute(): Nextflow's file() resolves a relative path against +// launchDir and hands back an absolute path, so isAbsolute() is always true and the projectDir +// fallback would never fire -- silently making resolution depend on the launch directory, which is +// the thing this is here to prevent. +// // A function, not a closure assigned with `def`: Nextflow 26.04's strict syntax does not see the // latter from inside a workflow body. def resolve(p) { @@ -46,18 +57,36 @@ workflow { // ---- parameter checks ----------------------------------------------------------------- // // Cheap to check here, expensive to discover 40 minutes into a 1,000-task run. + // guide_sd was an absolute spread (0.13). Its replacement, guide_spread_c, is a coefficient on + // es * (1 - es), so reading an old 0.13 as c would shrink the spread fivefold. Refuse it. + if (params.guide_sd != null) { + error "guide_sd was replaced by guide_spread_c (default 0.65) on 2026-09-25; the spread is " + + "now c * es * (1 - es), not an absolute sd. Remove guide_sd from the params." + } if (!params.effect_sizes || params.effect_sizes.size() == 0) { error "effect_sizes is empty -- nothing to simulate." } + if (params.reps_per_chunk != params.num_replicates && !params.keep_per_simulation) { + log.warn "reps_per_chunk (${params.reps_per_chunk}) < num_replicates " + + "(${params.num_replicates}): a pair's simulations span several tasks, so power " + + "is computed from the per-simulation rows, which are written and consolidated " + + "as if keep_per_simulation were true." + } if (params.num_replicates % params.reps_per_chunk != 0) { error "num_replicates (${params.num_replicates}) must be a multiple of reps_per_chunk " + "(${params.reps_per_chunk}); otherwise the last chunk is short and the power " + "denominators differ between pairs." } - if (params.num_replicates % params.reps_per_null_chunk != 0) { - error "num_replicates (${params.num_replicates}) must be a multiple of " + - "reps_per_null_chunk (${params.reps_per_null_chunk}), or some replicate will have " + - "no null model fitted for it." + // The fast driver shares one set of permutation matrices across a target's simulations, which + // is the engine's test only when they share one permutation set; reused fits are read only by + // the fast driver. Both combinations would fail every simulation task. + if (params.driver == 'fast' && params.permutations != 'per-target') { + error "driver 'fast' needs permutations 'per-target' (got '${params.permutations}'); " + + "set driver 'engine' to run per-replicate permutations." + } + if (params.null_fits == 'reuse' && params.driver != 'fast') { + error "null_fits 'reuse' needs driver 'fast'; the engine refits every gene itself. " + + "Set null_fits 'refit' with driver 'engine'." } // The gcb profile has no default container_image -- see conf/gcb.config for why -- so a run // that forgets --container_image would otherwise fail 5-30 minutes in, on the first task, @@ -67,21 +96,6 @@ workflow { "pixi environment baked in -- see conf/gcb.config)." } - // Sampled control cells are not wired through the pipeline: the path was never reachable from - // it (prepare_sim_input.R does not declare these flags, and passing them there crashed the - // step), it diverges from the all-cells path in ways that were never validated, and sampling - // controls costs 21-60% of power (docs/methods.md). Refuse rather than silently ignore. - // guide_sd was an absolute spread (0.13). Its replacement, guide_spread_c, is a coefficient on - // es * (1 - es), so reading an old 0.13 as c would shrink the spread fivefold. Refuse it. - if (params.guide_sd != null) { - error "guide_sd was replaced by guide_spread_c (default 0.65) on 2026-09-25; the spread is " + - "now c * es * (1 - es), not an absolute sd. Remove guide_sd from the params." - } - if (params.n_control_cells || params.cell_batches) { - error "n_control_cells / cell_batches are not supported by the pipeline. Leave them unset; " + - "to experiment, call src/run_power_simulation.R by hand (see docs/methods.md)." - } - // ---- inputs --------------------------------------------------------------------------- // // Samplesheet paths are resolved against the repository root, not the launch directory. The @@ -95,6 +109,7 @@ workflow { // A scheme-prefixed URI (gs://, s3://, az://) is absolute in the same sense a leading '/' is -- // it already names a full location, not one relative to the repo. Without this check, // '${projectDir}/gs://bucket/obj' is nonsense and never exists. + // Emptiness is checked on the file, eagerly, rather than with .ifEmpty on the channel. // ifEmpty's closure is invoked while the DAG is being built, not when the channel turns out to // be empty, so `.ifEmpty { error ... }` aborts every run and -- worse -- reports the empty- @@ -112,21 +127,18 @@ workflow { .fromPath(sheet, checkIfExists: true) .splitCsv(header: true) .map { row -> - if (!row.sample?.trim() || !row.sceptre_object?.trim()) { + if (!row.sample?.trim() || !row.dataset?.trim()) { error "samplesheet ${params.samplesheet} needs non-empty 'sample' and " + - "'sceptre_object' columns; got: ${row}" - } - def obj = resolve(row.sceptre_object.trim()) - if (!obj.exists()) { - error "sample '${row.sample}': sceptre object not found at ${obj}" + "'dataset' columns; got: ${row}" } - // Optional: only set when the sceptre object's response matrix is odm-backed. Absent - // for every existing samplesheet, which is why the column and the check are optional. - def odm = row.response_odm?.trim() ? resolve(row.response_odm.trim()) : [] - if (odm && !odm.exists()) { - error "sample '${row.sample}': --response-odm file not found at ${odm}" + // A .h5mu from pysceptre's export, written with --all-genes --all-cells. The sceptre + // object is no longer an input to this pipeline at all: converting it is a one-off + // step that happens outside, which is what lets the environment hold no R. + def dataset = resolve(row.dataset.trim()) + if (!dataset.exists()) { + error "sample '${row.sample}': dataset not found at ${dataset}" } - [ [id: row.sample.trim()], obj, odm ] + [ [id: row.sample.trim()], dataset ] } // ---- step 1: reduce the sceptre object ------------------------------------------------- @@ -153,41 +165,34 @@ workflow { "${params.test_max_splits} of ${params.n_splits} splits. NOT a complete run." } - // ---- step 2b: null models, one task per replicate chunk -------------------------------- + // ---- step 3: the simulation ------------------------------------------------------------ // - // Fanned out over replicate offsets and joined back to the sample's prepared inputs. Divisibility - // of num_replicates by reps_per_null_chunk is checked above, so every chunk is full width. - // The chunk count is bounded when the range is built rather than with `take` afterwards, both - // because it avoids the operator-argument problem above and because it is what it means: there - // are only this many chunks, not "there are 100 and we ignore most of them". - def reps_to_fit = params.test_max_null_reps ?: params.num_replicates - def n_null_chunks = reps_to_fit.intdiv(params.reps_per_null_chunk) - - ch_null_offsets = Channel - .of(0.. [ i * params.reps_per_null_chunk, params.reps_per_null_chunk ] } - - ch_prepared = PREPARE_SIM_INPUT.out.sim_input - .join(PREPARE_SIM_INPUT.out.template) - .join(PREPARE_SIM_INPUT.out.grna_targets) - - FIT_NULL_MODELS(ch_prepared.combine(ch_null_offsets)) - - // ---- step 2c: merge the chunks --------------------------------------------------------- + // The per-sample inputs are one item; the fan-out is the cross product of splits, effect sizes + // and replicate chunks. Joining the sample's inputs first keeps them together, so `combine` + // only ever multiplies out the things that genuinely vary. // - // groupTuple with an explicit size would deadlock if a chunk failed; the default waits for the - // channel to close instead, and merge_null_models.R independently checks the replicate count. - ch_chunks = FIT_NULL_MODELS.out.chunk.groupTuple() - - def merged_reps = params.test_max_null_reps ?: params.num_replicates - MERGE_NULL_MODELS(ch_chunks, merged_reps) - - // ---- step 4: the simulation ------------------------------------------------------------ + // pairs_with_info is optional -- an export with no discovery_pairs_with_info produces none -- + // so it is mixed in with a default rather than joined, which would drop the sample entirely. // - // The per-sample inputs are one item; the fan-out is the cross product of splits, effect sizes - // and replicate chunks. Joining the null models in first keeps the sample's five inputs - // together, so `combine` only ever multiplies out the things that genuinely vary. - ch_sim_inputs = ch_prepared.join(MERGE_NULL_MODELS.out.null_models) + // The null-model fits: FIT_NULL_MODELS' file under null_fits 'reuse', an empty placeholder + // otherwise. The process is always wired, and fed nothing when it is not wanted, so there is + // one channel shape either way. + FIT_NULL_MODELS(PREPARE_SIM_INPUT.out.sim_input + .join(PREPARE_SIM_INPUT.out.pairs) + .join(PREPARE_SIM_INPUT.out.analysis_mode) + .filter { params.null_fits == 'reuse' }) + ch_null_fits = params.null_fits == 'reuse' + ? FIT_NULL_MODELS.out.fits + : PREPARE_SIM_INPUT.out.sim_input.map { meta, _sim_input -> [meta, []] } + + ch_sim_inputs = PREPARE_SIM_INPUT.out.sim_input + .join(PREPARE_SIM_INPUT.out.grna_targets) + .join(PREPARE_SIM_INPUT.out.analysis_mode) + .join(PREPARE_SIM_INPUT.out.pairs_with_info, remainder: true) + .map { meta, sim_input, grna_targets, analysis_mode, info -> + [meta, sim_input, grna_targets, analysis_mode, info ?: []] } + .join(ch_null_fits) + .join(PREPARE_SIM_INPUT.out.threshold) ch_rep_chunks = Channel .of(0..<(params.num_replicates.intdiv(params.reps_per_chunk))) @@ -196,8 +201,9 @@ workflow { // combine on the meta key so a multi-sample run pairs each sample with its own splits rather // than with every sample's. // No trailing map: `combine` flattens the [offset, reps] pair into two elements rather than - // keeping it as one, so this already emits the nine fields POWER_SIMULATION declares -- - // meta, sim_input, template, grna_targets, null_models, split, effect_size, rep_offset, reps. + // keeping it as one, so this already emits the eleven fields POWER_SIMULATION declares -- + // meta, sim_input, grna_targets, analysis_mode, pairs_with_info, null_fits, threshold, split, + // effect_size, rep_offset, reps. ch_sim_tasks = ch_sim_inputs .combine(ch_splits, by: 0) .combine(Channel.fromList(params.effect_sizes)) @@ -205,26 +211,24 @@ workflow { POWER_SIMULATION(ch_sim_tasks) - // ---- step 5: power, per (sample, effect size) ------------------------------------------- + // ---- step 4: the per-simulation rows, when they are written -------------------------------- // - // groupTuple over (meta, effect size) collects every split's output for one effect size. - // Deliberately not collectFile: compute_power.R takes the file list itself and validates the - // replicate count per pair, which a concatenation would hide. - ch_by_es = POWER_SIMULATION.out.sim.groupTuple(by: [0, 1]) + // groupTuple over (meta, effect size) collects every split's output for one effect size. With + // no rows written (the default) the channel is empty and CONSOLIDATE_REPLICATES never runs. + // Deliberately not collectFile: the power step takes the file list itself and validates it. + CONSOLIDATE_REPLICATES(POWER_SIMULATION.out.sim.groupTuple(by: [0, 1])) - // ---- step 4b: one Parquet per effect size ---------------------------------------------- + // ---- step 5: power, per (sample, effect size) ------------------------------------------- + // + // From the tasks' per-pair counts when each task held all simulations of its pairs; from the + // consolidated rows otherwise. `whole` mirrors the test POWER_SIMULATION makes. // - // The per-split TSVs are no longer published; this is. They cost 30-90 s of parsing per read - // as 1,000 gzipped files, and the per-replicate output is the one thing power can be - // re-derived from, so it is read repeatedly. The splits remain in the work directory, so a - // failed consolidation loses nothing. - CONSOLIDATE_REPLICATES(ch_by_es) - // Each sample's own threshold, joined on the meta key -- not `.first()`, which scored every - // sample of a multi-sample run against whichever sample's threshold arrived first. - COMPUTE_POWER(CONSOLIDATE_REPLICATES.out.parquet - .map { meta, es, f -> [meta, es, [f]] } - .combine(PREPARE_SIM_INPUT.out.threshold, by: 0)) + // sample of a multi-sample run against sample 1's. + ch_power_in = (params.reps_per_chunk as int) == (params.num_replicates as int) + ? POWER_SIMULATION.out.partials.groupTuple(by: [0, 1]) + : CONSOLIDATE_REPLICATES.out.parquet.map { meta, es, f -> [meta, es, [f]] } + COMPUTE_POWER(ch_power_in.combine(PREPARE_SIM_INPUT.out.threshold, by: 0)) // ---- step 6: one row per pair across every effect size --------------------------------- ch_all_power = COMPUTE_POWER.out.power diff --git a/modules/local/compute_power.nf b/modules/local/compute_power.nf index 9e21a9b..f0bbe94 100644 --- a/modules/local/compute_power.nf +++ b/modules/local/compute_power.nf @@ -1,4 +1,6 @@ -// Step 5 -- turn the per-simulation rows into a power estimate per pair. +// Step 5 -- a power estimate per pair, from the simulation tasks' per-pair counts or, when +// simulations were chunked across tasks, from the consolidated per-simulation rows. Both routes give +// the same table (watteg.power.power_from_counts; tests/test_power.py). // // Power is the fraction of simulations whose p-value clears the discovery threshold, and the // threshold comes from the sceptre object's own @discovery_result rather than from a nominal alpha, @@ -15,34 +17,49 @@ process COMPUTE_POWER { publishDir { "${params.outdir}/${meta.id}/power" }, mode: params.publish_mode input: - // One consolidated Parquet per effect size, from CONSOLIDATE_REPLICATES. Was 1,000 staged - // TSVs; the file list below is kept general so an older sweep's TSVs still work. + // Either every simulation task's per-pair counts for one effect size (*.partial.tsv.gz), or + // one consolidated Parquet from CONSOLIDATE_REPLICATES; the file list below is kept general so + // an older sweep's TSVs still work. // // The threshold rides in the same tuple, joined on meta in main.nf. It used to arrive as a second - // channel built with `.first()`, so in a multi-sample run every sample was scored against one - // sample's discovery threshold. + // channel built with `.first()`, so in a multi-sample run every sample was scored against sample + // 1's discovery threshold. tuple val(meta), val(effect_size), path(simulations, stageAs: 'sim/*'), path(threshold) output: tuple val(meta), val(effect_size), path("power_es${effect_size}.tsv"), emit: power script: - // params.alpha replaces the screen's own discovery threshold; compute_power.R takes exactly one - // of the two. It used to be passed to prepare_sim_input.R, which does not declare it. - def threshold_arg = params.alpha ? "--alpha ${params.alpha}" : "--threshold-file ${threshold}" """ - # A comma-separated list rather than a glob: compute_power.R accepts either, but an explicit - # list is a single token and its ordering is stable. - # Both forms matched: the per-simulation output is gzipped, but an older sweep being - # re-aggregated is plain .tsv. Missing the .gz here would have found no files and produced an - # empty power table rather than an error. - sim_list=\$(ls sim/*.parquet sim/*.tsv.gz sim/*.tsv 2>/dev/null | paste -sd, -) - echo "combining \$(ls sim/*.parquet sim/*.tsv.gz sim/*.tsv 2>/dev/null | wc -l) file(s)" + # An explicit list rather than a glob, so the ordering is stable and the count is checked. + # All three forms matched: the per-replicate output is gzipped, the consolidated one is + # Parquet, and an older sweep being re-aggregated is plain .tsv. Missing the .gz here would + # find no files and produce an empty power table rather than an error. + # + # nullglob, NOT `ls`. Only one of the three patterns ever matches, so `ls` exits 2 for the + # other two, and under Nextflow's `bash -e` that killed the task on this line with no output + # at all -- every real Python run, first seen on moi5 cis 2026-09-25. R's module escaped it + # only because it piped `ls` into `paste`, whose status is the one bash checks. + shopt -s nullglob + partial_list=(sim/*.partial.tsv.gz) + sim_list=(sim/*.parquet sim/*.tsv.gz sim/*.tsv) + shopt -u nullglob + if [ \${#partial_list[@]} -gt 0 ]; then + source_arg=--partials + sim_list=("\${partial_list[@]}") + else + source_arg=--simulations + fi + echo "combining \${#sim_list[@]} file(s) (\${source_arg})" + if [ \${#sim_list[@]} -eq 0 ]; then + echo "ERROR: no simulation files staged under sim/" >&2 + exit 1 + fi pixi run --frozen --manifest-path ${projectDir}/pixi.toml \\ - Rscript ${projectDir}/src/compute_power.R \\ - --simulations "\${sim_list}" \\ - ${threshold_arg} \\ + watteg-compute-power \\ + \${source_arg} "\${sim_list[@]}" \\ + --threshold-file ${threshold} \\ --conf-level ${params.conf_level} \\ --out power_es${effect_size}.tsv """ diff --git a/modules/local/consolidate_replicates.nf b/modules/local/consolidate_replicates.nf index 1a86da1..368ba39 100644 --- a/modules/local/consolidate_replicates.nf +++ b/modules/local/consolidate_replicates.nf @@ -5,6 +5,11 @@ // hundreds of times. One Parquet file reads in a second or two and takes the published output from // 6,000 files to 6. // +// It runs only when the per-simulation rows are written: under keep_per_simulation, or when +// simulations are chunked across tasks (reps_per_chunk < num_replicates). By default each +// simulation task writes per-pair counts instead, and COMPUTE_POWER builds the same power table +// from those, without the 74M-row table moi5 trans would otherwise need. +// // This is the ONLY published form of the per-simulation data. The per-split files stay in the work // directory, which is what makes the run resumable and preemption-tolerant, and means a failed // consolidation cannot lose anything. @@ -27,7 +32,7 @@ process CONSOLIDATE_REPLICATES { script: """ pixi run --frozen --manifest-path ${projectDir}/pixi.toml \\ - Rscript ${projectDir}/src/consolidate_replicates.R \\ + watteg-consolidate-replicates \\ --simulations sim \\ --out replicates_es${effect_size}.parquet """ diff --git a/modules/local/fit_null_models.nf b/modules/local/fit_null_models.nf index 17dbfa6..18bf953 100644 --- a/modules/local/fit_null_models.nf +++ b/modules/local/fit_null_models.nf @@ -1,51 +1,49 @@ -// Step 2b -- fit the per-gene null model on a null simulation of each replicate. +// Step 2b -- each gene's null-model fit, once per simulation, for every simulation task to reuse. // -// WHY THIS EXISTS AT ALL +// Runs only under --driver fast --null-fits reuse (the defaults). Without it each simulation refits +// every gene once per target it is tested with, which is the largest cost left in a simulated +// pair-test (~49 ms of it on cis); the real screen fits each gene once for ~135 targets. With it, +// each (gene, simulation) is fitted once, on an independent draw with no knockdown keyed +// rng_for(seed, "__null_fit__|" + gene, simulation, 0), and every target and effect size reuses the +// fit. R's FIT_NULL_MODELS approximation, measured to leave the moi5 cis call rate unchanged +// (docs/pysceptre-backend.md, section 13, item 3c; src/watteg/null_fits.py). // -// Before testing a pair, sceptre fits a Poisson GLM of the gene's counts on the cell covariates -- -// the null model the perturbed cells are compared against. It *skips* that fit whenever -// @response_precomputations already holds an entry for the response_id, and sceptre_template.rds -// inherited 272 such entries from the real discovery analysis, covering all 237 genes that have -// QC-passing pairs. So every simulated count was being tested against coefficients fitted to *real* -// counts, which understates power. Measured: +0.0063 mean power once corrected, concentrated in the -// transition band. See docs/status.md. -// -// WHY IT FANS OUT OVER REPLICATES AND NOTHING ELSE -// -// A gene's null model is fitted on a null simulation -- no knockdown -- so it depends on neither the -// target nor the effect size. One fit per (gene, simulation) therefore serves every target and every -// effect size in a sweep: 100 simulations, not 100 x targets x effect sizes. Fitting inside each -// simulation task would pay for it n_splits times over. -// -// The seed is derived from (seed, rep) only, deliberately not (seed, target, rep, effect_size), for -// exactly that reason. +// One task per sample, covering every gene in pairs.tsv and every simulation 1..num_replicates, so +// every simulation task, whatever its split or simulation chunk, finds its fits in one file. It +// parallelises over simulations with task.cpus workers: 244 genes x 100 simulations is ~26 CPU +// minutes on moi5 cis. The file is ~2.5 MB and records the seed, the baseline and a digest of +// sim_input.h5; POWER_SIMULATION refuses one that does not match its own run. process FIT_NULL_MODELS { - tag "${meta.id} reps ${rep_offset + 1}-${rep_offset + reps}" + tag "${meta.id}" + + publishDir { "${params.outdir}/${meta.id}/prepared" }, mode: params.publish_mode input: - tuple val(meta), path(sim_input), path(sceptre_template), path(grna_targets), val(rep_offset), val(reps) + // sim_input.h5 and analysis_mode.tsv keep their names when staged, so --prepared . finds them. + tuple val(meta), path(sim_input), path(pairs), path(analysis_mode) output: - tuple val(meta), path("chunk_${String.format('%04d', rep_offset)}.rds"), emit: chunk + tuple val(meta), path('null_fits.h5'), emit: fits script: - def chunk = "chunk_${String.format('%04d', rep_offset)}.rds" """ + # One thread per worker, as in POWER_SIMULATION. + export OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 + pixi run --frozen --manifest-path ${projectDir}/pixi.toml \\ - Rscript ${projectDir}/src/fit_null_models.R \\ - --sim-input ${sim_input} \\ - --sceptre-template ${sceptre_template} \\ - --grna-targets ${grna_targets} \\ - --reps ${reps} \\ - --rep-offset ${rep_offset} \\ + watteg-fit-null-models \\ + --prepared . \\ + --pairs ${pairs} \\ + --reps ${params.num_replicates} \\ --seed ${params.seed} \\ --expression-model ${params.expression_model} \\ - --out ${chunk} + --n-jobs ${task.cpus} \\ + --out null_fits.h5 """ stub: """ - touch chunk_${String.format('%04d', rep_offset)}.rds + touch null_fits.h5 """ } diff --git a/modules/local/merge_null_models.nf b/modules/local/merge_null_models.nf deleted file mode 100644 index 836002b..0000000 --- a/modules/local/merge_null_models.nf +++ /dev/null @@ -1,38 +0,0 @@ -// Step 2c -- merge the per-simulation null-model chunks into the bundle the simulation consumes. -// -// merge_null_models.R errors on overlapping --rep-offset ranges rather than silently keeping one of -// them, and on a simulation count that does not match --reps. Both matter here: a missing chunk would -// otherwise surface as a simulation that quietly tests some simulations against no null model. -// -// An entry is 11 named coefficients plus a theta scalar, so 100 simulations x 272 genes is a few -// hundred KB -- the bundle is small enough to hand to every simulation task. - -process MERGE_NULL_MODELS { - tag "${meta.id}" - - publishDir { "${params.outdir}/${meta.id}/prepared" }, mode: params.publish_mode - - input: - tuple val(meta), path(chunks, stageAs: 'chunks/*') - val expected_reps - - output: - tuple val(meta), path('null_precomputations.rds'), emit: null_models - - script: - """ - n_chunks=\$(ls chunks/*.rds | wc -l) - echo "merging \${n_chunks} chunk(s) covering ${expected_reps} simulations" - - pixi run --frozen --manifest-path ${projectDir}/pixi.toml \\ - Rscript ${projectDir}/src/merge_null_models.R \\ - --inputs 'chunks/*.rds' \\ - --reps ${expected_reps} \\ - --out null_precomputations.rds - """ - - stub: - """ - touch null_precomputations.rds - """ -} diff --git a/modules/local/power_simulation.nf b/modules/local/power_simulation.nf index 3889b8d..d8c45a3 100644 --- a/modules/local/power_simulation.nf +++ b/modules/local/power_simulation.nf @@ -1,80 +1,122 @@ -// Step 4 -- the simulation. One task per (split, effect size, simulation chunk). +// Step 3 -- the simulation. One task per (split, effect size, simulation chunk). // -// This is where essentially all of the compute goes: 635 CPU-hours per effect size on -// day0_grna20_no_shuffle at 100 simulations, against a few CPU-hours for everything else combined. +// This is where essentially all of the compute goes. // // WHY THE FAN-OUT IS OVER (split x effect size) AND NOT A LOOP OVER EFFECT SIZES // -// Looping inside the task would share the per-task setup -- the 16 MB sim_input.rds and 47 MB -// sceptre_template.rds reads, plus container and R startup -- across effect sizes. That setup is -// ~1 second against a task measured in tens of minutes, while the loop would multiply each task's -// duration by the number of effect sizes and divide the parallelism by the same factor. With -// thousands of cores available on `owners`, repeating a little I/O is much the better trade. +// Looping inside the task would share the per-task setup across effect sizes. That setup is a +// second or so against a task measured in minutes, while the loop would multiply each task's +// duration by the number of effect sizes and divide the parallelism by the same factor. // -// --null-precomputations is NOT optional here. Without it run_power_simulation.R either refits the -// null model inside every call (4.3x the cost) or -- if the template still carried the inherited -// real-data cache -- would silently test simulated counts against real-data coefficients and -// understate power. The template has that slot cleared by slim_sceptre_object(), and the script -// errors rather than falling back, so the failure mode that went unnoticed through the whole -// refactor cannot recur here. +// NULL-MODEL FITS. Under --driver fast --null-fits reuse each gene's null model comes from +// FIT_NULL_MODELS (one fit per gene per simulation, on an independent draw with no knockdown), staged +// here as null_fits.h5 and checked against this run's seed, baseline and sim_input before use. Under +// --null-fits refit the placeholder is empty and every gene is refitted on each simulation's own +// counts for every target, the exact configuration. R hoisted the fits for speed too; this is the +// same approximation, with its seed-matching guard. +// +// POWER INSIDE THE TASK. A task that holds all simulations of its pairs (reps_per_chunk == +// num_replicates, the default) writes each pair's counts -- simulations called, simulations used, +// the fold-change and cell-count sums -- and COMPUTE_POWER adds them up into the same power table +// the per-simulation rows give, byte for byte (tests/test_power.py). The per-simulation rows, 74M +// of them for moi5 trans, are written only under keep_per_simulation, or when simulations are +// chunked across tasks, where a pair's counts would come from several tasks and the rows are what +// power is computed from. process POWER_SIMULATION { tag "${meta.id} ${split.baseName} es${effect_size} reps ${rep_offset + 1}-${rep_offset + reps}" - // Published for the same reason the sbatch runner keeps them: they are the only record of the - // per-simulation p-values, they let a run be compared split by split against the other runner, - // and re-deriving one costs a full task. This is the bulky output -- ~1,000 files per effect - // size, a few hundred MB in total. - // NOT published. These per-split files are what makes the run resumable and preemption-tolerant -- -// 1,000 tasks write concurrently and no single-file format supports that -- but as a published -// artefact they were 6,000 files costing 30-90 s of parsing per read. CONSOLIDATE_REPLICATES turns -// each effect size into one Parquet file and publishes that instead. -// -// They survive in the work directory until Nextflow cleans it, so a failed consolidation cannot -// lose data. + // NOT published. These per-split files are what makes the run resumable and + // preemption-tolerant -- many tasks write concurrently and no single-file format supports that. + // COMPUTE_POWER publishes the power table; CONSOLIDATE_REPLICATES, when it runs, turns the + // per-simulation rows into one Parquet file per effect size and publishes that. They survive in + // the work directory until Nextflow cleans it, so a failed later step cannot lose data. input: - tuple val(meta), path(sim_input), path(sceptre_template), path(grna_targets), - path(null_models), path(split), val(effect_size), val(rep_offset), val(reps) + // analysis_mode.tsv is read by the CLI from --prepared (here, the task directory). It was not + // staged until 2026-09-24, so every real run of this process died with FileNotFoundError and + // every Python number so far came from direct CLI calls; the stub never reads it. + // + // null_fits is FIT_NULL_MODELS' file under --null-fits reuse, and an empty placeholder otherwise. + // threshold is the sample's discovery threshold, which the per-pair counts are taken at. + tuple val(meta), path(sim_input), path(grna_targets), path(analysis_mode), path(pairs_with_info), + path(null_fits), path(threshold), path(split), val(effect_size), val(rep_offset), val(reps) output: - tuple val(meta), val(effect_size), path(out_name), emit: sim + tuple val(meta), val(effect_size), path(out_name), emit: sim, optional: true + tuple val(meta), val(effect_size), path(counts_name), emit: partials, optional: true script: - // The simulation range is part of the filename so chunks of one split cannot collide, and so a - // stray file is attributable. With no chunking (reps_per_chunk == num_replicates) there is - // exactly one per split, which is what every measured run has done. - out_name = "${split.baseName}_es${effect_size}_rep${rep_offset}.tsv.gz" + // The simulation range is in the filenames so chunks of one split cannot collide and a stray + // file is attributable. + def stem = "${split.baseName}_es${effect_size}_rep${rep_offset}" + out_name = "${stem}.tsv.gz" + counts_name = "${stem}.partial.tsv.gz" + def whole = (params.reps_per_chunk as int) == (params.num_replicates as int) + def write_rows = params.keep_per_simulation || !whole + def fits_arg = null_fits ? "--null-fits-file ${null_fits}" : '' + def rows_arg = write_rows ? "--out ${stem}.tsv" : '' + def counts_arg = whole ? "--partials-out ${stem}.partial.tsv --threshold-file ${threshold}" : '' """ + # One thread per worker. The task's parallelism is its --n-jobs worker processes; a BLAS or + # OpenMP thread pool in the parent when it forks them is oversubscription at best and a known + # cause of crashed children at worst. + export OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 + pixi run --frozen --manifest-path ${projectDir}/pixi.toml \\ - Rscript ${projectDir}/src/run_power_simulation.R \\ - --sim-input ${sim_input} \\ - --sceptre-template ${sceptre_template} \\ + watteg-run-power-simulation \\ + --prepared . \\ --pairs ${split} \\ - --grna-targets ${grna_targets} \\ - --null-precomputations ${null_models} \\ --effect-size ${effect_size} \\ --reps ${reps} \\ --rep-offset ${rep_offset} \\ --guide-spread-c ${params.guide_spread_c} \\ + --estimand ${params.estimand} \\ --seed ${params.seed} \\ + --n-jobs ${task.cpus} \\ --expression-model ${params.expression_model} \\ - --out ${out_name} + --permutations ${params.permutations} \\ + --nulls ${params.nulls} \\ + --driver ${params.driver} \\ + --null-fits ${params.null_fits} ${fits_arg} \\ + ${rows_arg} ${counts_arg} - # A short file means simulation rows were lost, which compute_power.R would otherwise absorb as - # a smaller denominator for the affected pairs. + # A short file means rows were lost, which compute_power would otherwise absorb as a smaller + # denominator for the affected pairs. A target that perturbs no cell is skipped and writes + # nothing, so it shows here too. n_pairs=\$(( \$(wc -l < ${split}) - 1 )) - expected=\$(( n_pairs * ${reps} + 1 )) - actual=\$(gzip -cd ${out_name} | wc -l) - if [ "\${actual}" -ne "\${expected}" ]; then - echo "ERROR: wrote \${actual} lines, expected \${expected} (\${n_pairs} pairs x ${reps} reps + header)." >&2 - exit 1 + check_lines() { + local file=\$1 expected=\$2 what=\$3 + local actual=\$(wc -l < "\${file}") + if [ "\${actual}" -ne "\${expected}" ]; then + echo "ERROR: \${file} has \${actual} lines, expected \${expected} (\${what} + header)." >&2 + echo "A target that perturbs no cell is skipped and writes no rows; look for 'skipped' above." >&2 + exit 1 + fi + } + if [ -f ${stem}.tsv ]; then + check_lines ${stem}.tsv \$(( n_pairs * ${reps} + 1 )) "\${n_pairs} pairs x ${reps} simulations" + gzip -f ${stem}.tsv + fi + if [ -f ${stem}.partial.tsv ]; then + check_lines ${stem}.partial.tsv \$(( n_pairs + 1 )) "\${n_pairs} pairs" + # Every pair's counts must cover all of its simulations. + awk -F '\\t' 'NR == 1 { for (i = 1; i <= NF; i++) col[\$i] = i; next } + \$col["n_simulations"] != ${reps} { bad++ } + END { if (bad) { print "ERROR: " bad " pair(s) do not have ${reps} simulations" > "/dev/stderr"; exit 1 } }' \\ + ${stem}.partial.tsv + gzip -f ${stem}.partial.tsv fi """ stub: - out_name = "${split.baseName}_es${effect_size}_rep${rep_offset}.tsv.gz" + def stem = "${split.baseName}_es${effect_size}_rep${rep_offset}" + out_name = "${stem}.tsv.gz" + counts_name = "${stem}.partial.tsv.gz" + def whole = (params.reps_per_chunk as int) == (params.num_replicates as int) + def write_rows = params.keep_per_simulation || !whole """ - printf '' | gzip > ${out_name} + ${write_rows ? "printf '' | gzip > ${out_name}" : ''} + ${whole ? "printf '' | gzip > ${counts_name}" : ''} """ } diff --git a/modules/local/prepare_sim_input.nf b/modules/local/prepare_sim_input.nf index a401983..5a9fd52 100644 --- a/modules/local/prepare_sim_input.nf +++ b/modules/local/prepare_sim_input.nf @@ -1,12 +1,12 @@ -// Step 1 -- reduce the sceptre object to what the simulation actually needs. +// Step 1 -- reduce the dataset to what the simulation actually needs. // -// One input file, not four: the other three the old pipeline demanded duplicate data already inside -// the object (@discovery_pairs_with_info, @grna_target_data_frame, @discovery_result). See -// docs/status.md, "Measured improvements". +// The input is a .h5mu written by pysceptre's export, NOT a sceptre object: nothing in the Python +// path reads R, which is what lets the environment be one Python package. The export must have been +// written with --all-cells, because the poscounts size factors are a per-cell reduction over every +// cell in the object and reproducing R's needs every cell. See src/watteg/expression.py. // -// The emitted sceptre_template.rds has its @response_matrix emptied and, importantly, its inherited -// @response_precomputations cleared by slim_sceptre_object() -- that clearing is what makes an -// accidental as_is run impossible to reintroduce downstream. +// There is no sceptre_template.rds any more. That was an R object carrying the covariate matrix and +// the analysis parameters; those now live in sim_input.h5 and analysis_mode.tsv. process PREPARE_SIM_INPUT { tag "${meta.id}" @@ -14,40 +14,38 @@ process PREPARE_SIM_INPUT { publishDir { "${params.outdir}/${meta.id}/prepared" }, mode: params.publish_mode input: - tuple val(meta), path(sceptre_object), path(response_odm) + tuple val(meta), path(dataset) output: - tuple val(meta), path('sim_input.rds'), emit: sim_input - tuple val(meta), path('sceptre_template.rds'), emit: template - tuple val(meta), path('pairs.tsv'), emit: pairs - tuple val(meta), path('grna_targets.tsv'), emit: grna_targets + tuple val(meta), path('sim_input.h5'), emit: sim_input + tuple val(meta), path('pairs.tsv'), emit: pairs + tuple val(meta), path('pairs_with_info.tsv'), emit: pairs_with_info, optional: true + tuple val(meta), path('grna_targets.tsv'), emit: grna_targets tuple val(meta), path('discovery_threshold.txt'), emit: threshold - tuple val(meta), path('analysis_mode.tsv'), emit: analysis_mode - path 'versions.yml', emit: versions + tuple val(meta), path('analysis_mode.tsv'), emit: analysis_mode + path 'versions.yml', emit: versions script: - // response_odm is [] (no files staged) for every sample whose response matrix isn't odm-backed. - def response_odm_flag = response_odm ? "--response-odm ${response_odm}" : '' - // --n-control-cells, --cell-batches and --alpha used to be passed here. prepare_sim_input.R - // declares none of them, so setting any of those params made this step die on an optparse - // error. alpha now goes to COMPUTE_POWER; the pipeline refuses the other two (see main.nf). + def alpha = params.alpha ? "--threshold ${params.alpha}" : '' """ pixi run --frozen --manifest-path ${projectDir}/pixi.toml \\ - Rscript ${projectDir}/src/prepare_sim_input.R \\ - --sceptre-object ${sceptre_object} \\ + watteg-prepare-sim-input \\ + --dataset ${dataset} \\ --outdir . \\ - ${response_odm_flag} + --n-jobs ${task.cpus} \\ + ${alpha} cat <<-END_VERSIONS > versions.yml "${task.process}": - r-base: \$(pixi run --frozen --manifest-path ${projectDir}/pixi.toml Rscript -e 'cat(strsplit(R.version.string, " ")[[1]][3])') - sceptre: \$(pixi run --frozen --manifest-path ${projectDir}/pixi.toml Rscript -e 'cat(as.character(utils::packageVersion("sceptre")))') + python: \$(pixi run --frozen --manifest-path ${projectDir}/pixi.toml python -c 'import platform; print(platform.python_version())') + watteg: \$(pixi run --frozen --manifest-path ${projectDir}/pixi.toml python -c 'import watteg; print(watteg.__version__)') + pysceptre: \$(pixi run --frozen --manifest-path ${projectDir}/pixi.toml python -c 'import pysceptre; print(getattr(pysceptre, "__version__", "unknown"))') END_VERSIONS """ stub: """ - touch sim_input.rds sceptre_template.rds pairs.tsv grna_targets.tsv discovery_threshold.txt analysis_mode.tsv + touch sim_input.h5 pairs.tsv pairs_with_info.tsv grna_targets.tsv discovery_threshold.txt analysis_mode.tsv echo '"${task.process}": {}' > versions.yml """ } diff --git a/modules/local/split_pairs.nf b/modules/local/split_pairs.nf index 5d93ccd..674fc62 100644 --- a/modules/local/split_pairs.nf +++ b/modules/local/split_pairs.nf @@ -29,7 +29,7 @@ process SPLIT_PAIRS { script: """ pixi run --frozen --manifest-path ${projectDir}/pixi.toml \\ - Rscript ${projectDir}/src/split_pairs.R \\ + watteg-split-pairs \\ --pairs ${pairs} \\ --n-splits ${params.n_splits} \\ --outdir . diff --git a/modules/local/summarize_power.nf b/modules/local/summarize_power.nf index 000bded..170f9d5 100644 --- a/modules/local/summarize_power.nf +++ b/modules/local/summarize_power.nf @@ -19,7 +19,7 @@ process SUMMARIZE_POWER { // and could match one sample's power tables to another sample's sim_input. // // sim_input is here so the summary can carry the gene's dispersion and normalised mean, sparing - // every covariate model a 16 MB RDS read for the other half of SE^2 ~ (1/n)(1/mu + 1/theta). + // every covariate model a separate read for the other half of SE^2 ~ (1/n)(1/mu + dispersion). tuple val(meta), path(power_files, stageAs: 'power/*'), path(sim_input) output: @@ -27,12 +27,12 @@ process SUMMARIZE_POWER { script: """ - power_list=\$(ls power/*.tsv | paste -sd, -) + power_list=\$(ls power/*.tsv) echo "summarizing \$(ls power/*.tsv | wc -l) effect size(s)" pixi run --frozen --manifest-path ${projectDir}/pixi.toml \\ - Rscript ${projectDir}/src/summarize_power.R \\ - --power "\${power_list}" \\ + watteg-summarize-power \\ + --power \${power_list} \\ --power-threshold ${params.power_threshold} \\ --sim-input ${sim_input} \\ --out power_summary.tsv diff --git a/nextflow.config b/nextflow.config index f0a86d8..13b0897 100644 --- a/nextflow.config +++ b/nextflow.config @@ -29,11 +29,15 @@ params { // closures in conf/base.config therefore coerce before doing arithmetic -- see the note there. // Do not assume a MemoryUnit here. - // POWER_SIMULATION. Calibrated from 20 successful tender_mercator tasks, which peaked at - // 4.45-5.58 GiB. A fixed-floor fit gave 44 GiB per MiB of split input; 64 GiB per MiB is a - // conservative rounded slope because the unrounded fit under-predicted the largest task. - power_simulation_memory_floor = 4.GB - power_simulation_memory_slope_per_mb = 64000.MB + // POWER_SIMULATION: workers and memory per task. 8 GB is 2.4x the measured Linux peak of a + // 330-pair cis task at 8 workers (3.38 GB, 2026-09-25) and the smallest 8-vCPU machine; see + // conf/base.config. (Replaced the R-calibrated floor + slope-per-MiB-of-split on 2026-09-25: on + // the Python path the footprint follows the genes per target, not the split's file size.) + power_simulation_cpus = 8 + power_simulation_memory = 8.GB + + // FIT_NULL_MODELS: one task per sample, one worker per CPU, one simulation per unit of work. + fit_null_models_cpus = 8 // CONSOLIDATE_REPLICATES. Reads every per-split output for one effect size into one data frame // before writing the Parquet, so it scales with total staged input -- which is the GZIPPED @@ -61,17 +65,35 @@ params { // Simulation effect_sizes = [0.15] num_replicates = 100 + // Where a gene's unperturbed expected counts come from. 'fitted' is exp(X.beta), sceptre's own + // null model, so the simulation and the test that judges it are on one scale. 'size_factor' + // reproduces the pre-2026-09-21 behaviour and exists only to compare against sweeps produced + // with it. See src/watteg/baseline.py. + expression_model = 'fitted' + // THE FAST CONFIGURATION, the default since 2026-09-25; each part measured before it was adopted + // (docs/pysceptre-backend.md, section 13). The previous one stays selectable: driver 'engine', + // null_fits 'refit', and permutations 'per-replicate' / nulls 'scan' if wanted. The fast driver + // runs the permutation test only, so a CRT screen needs driver 'engine' and null_fits 'refit'. + // + // 'per-target' tests every simulation of a target against one permutation set drawn from the + // seed, as sceptre effectively does; 'per-replicate' draws a fresh set per simulation. + permutations = 'per-target' + // How the engine computes the permutation nulls; identical results, 'sparse' ~2x faster. + nulls = 'sparse' + // 'fast' tests a target's simulations together, byte-identical to 'engine' with permutations + // 'per-target' and nulls 'sparse' (src/watteg/fast_driver.py). Needs permutations 'per-target'. + driver = 'fast' + // 'reuse' fits each gene's null model once per simulation (FIT_NULL_MODELS) and shares it + // across targets; 'refit' refits per target, exactly. 'reuse' needs driver 'fast'. + null_fits = 'reuse' guide_spread_c = 0.65 // guide knockdown ~ Beta(mean es, sd c*es*(1-es)); docs/methods.md + // What the power is power for (docs/methods.md). 'fixed': an element whose effect IS the effect + // size, the guides' cell-weighted mean pinned to it in every simulation. 'random': an element + // whose effect is the effect size on average. Written into every output. + estimand = 'fixed' guide_sd = null // retired 2026-09-25: main.nf refuses it (see guide_spread_c) seed = 20250812 - // Where a gene's unperturbed expected counts come from. 'fitted' draws from exp(X %*% coefs), - // sceptre's own null model, so the simulation and the test that judges it are on one scale. - // 'size_factor' is the pre-2026-09-21 behaviour and exists only to reproduce a sweep made with - // it. It reaches BOTH the null-model fit and the simulation, because a null model fitted on one - // scale and applied on another is a null model of nothing. - expression_model = 'fitted' - // Parallelisation only -- results are invariant to this, since seeds derive from // (seed, target, rep, effect_size) rather than from the split layout. n_splits = 1000 @@ -80,9 +102,11 @@ params { // every measured run has done. Chunking re-pays the per-target setup per chunk. reps_per_chunk = 100 - // Simulations per null-model task. One fit covers every gene on one simulation in ~20 minutes, - // so 1 per task keeps the whole set at ~20 minutes rather than ~33 hours serial. - reps_per_null_chunk = 1 + // Keep one row per (pair, simulation), consolidated into a Parquet per effect size. Off by + // default: power is computed inside each simulation task from per-pair counts, which gives the + // same power table without the 74M-row per-simulation table of moi5 trans. Implied when + // reps_per_chunk < num_replicates, where power needs the rows. + keep_per_simulation = false // Reporting power_threshold = 0.8 @@ -96,9 +120,6 @@ params { // Optional, unset by default. See config/config.yml. alpha = null - // Must stay null: main.nf refuses both (sampled controls are not wired through the pipeline). - n_control_cells = null - cell_batches = null // Environment. The pipeline runs inside a container because Sherlock is CentOS 7 (glibc 2.17) // and the pinned r-base 4.4 needs glibc >= 2.28 -- see conf/sherlock.config. @@ -110,7 +131,6 @@ params { // Test-profile limits. Null in a real run; conf/test.config sets them. Declared here so a // typo in one of them is a missing-parameter error rather than a silently ignored setting. test_max_splits = null - test_max_null_reps = null } profiles { diff --git a/pixi.lock b/pixi.lock index da9687a..a016539 100644 --- a/pixi.lock +++ b/pixi.lock @@ -12,344 +12,35 @@ platforms: - __osx=13.0 - __archspec=0=m1 environments: - build: - channels: - - url: https://conda.anaconda.org/conda-forge/ - - url: https://conda.anaconda.org/bioconda/ - packages: - linux-64: - - conda: https://conda.anaconda.org/bioconda/linux-64/bioconductor-rhdf5lib-1.28.0-r44h15a9599_0.tar.bz2 - - conda: https://conda.anaconda.org/bioconda/noarch/nextflow-26.04.6-h2a3209d_1.conda - - 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/alsa-lib-1.2.16.1-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/binutils-2.46.1-default_h4852527_102.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.46.1-default_hfdba357_102.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.46.1-default_h4852527_102.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/bwidget-1.10.1-ha770c72_1.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/c-ares-1.34.8-h280c20c_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/c-compiler-1.11.0-h4d9bdce_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-he90730b_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-gcc-specs-14.4.0-h611768e_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/coreutils-9.11-h280c20c_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/curl-8.21.0-heca4667_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-1.11.0-hfcd1e18_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h27c8c51_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc-14.4.0-hc6a0c74_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_impl_linux-64-14.4.0-h9711763_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-14.4.0-hc469d41_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gfortran_impl_linux-64-14.4.0-he75539a_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-ha257d8a_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gsl-2.7-he838d99_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx-14.4.0-hd47ba16_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-14.4.0-hc436dd5_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-14.4.0-h82679ac_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.3.0-ha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-hbde042b_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h0c24ade_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_102.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.2.0-hdb68285_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-9_h4a7cf45_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-9_h0358290_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-heca4667_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-hd45a770_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h280c20c_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h3435931_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h73754d4_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.1.0-ha9f2e26_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.1.0-h69a702a_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.1.0-h69a702a_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.1.0-h79bb938_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-h45c3219_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.1.0-he0feb66_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.3.0-h17a8019_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-devel-14.3.0-h17a8019_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.2.0-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-9_h47877c9_openblas.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/libnghttp2-1.68.1-h877daf1_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.34-pthreads_h94d23a6_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libpsl-0.23.1-hf670292_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-14.4.0-hf39dbba_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.1.0-h934c35e_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-h9d88235_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.2-h5347b49_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_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/make-4.4.1-hb03c661_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/openjdk-25.0.2-ha668962_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.3-h35e630c_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.58.2-hda50119_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-haa7fec5_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb03c661_1003.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-base-4.4.3-h502d0c9_11.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-bit-4.6.0-r44h54b55ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-bit64-4.8.6-r44hddc5280_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-cli-3.6.6-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-colorspace-2.1_3-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-data.table-1.18.4-r44h1c8cec4_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-dplyr-1.2.1-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-ellipsis-0.3.3-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-fansi-1.0.7-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-farver-2.1.2-r44h3697838_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-glue-1.8.1-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-isoband-0.3.0-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-lattice-0.22_9-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-magrittr-2.0.5-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-matrix-1.7_6-r44h0e4624f_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-nanoparquet-0.5.1-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-parallelly-1.48.0-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-purrr-1.2.2-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-rcpp-1.1.2-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-readr-2.2.0-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-rlang-1.3.0-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-s7-0.2.2-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-tibble-3.3.1-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-tzdb-0.5.0-r44h3697838_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-utf8-1.2.6-r44h54b55ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-vctrs-0.7.3-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-vroom-1.7.1-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/sed-4.10-h19d0853_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd70dff1_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/tktable-2.10-h5a7a40f_8.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-h280c20c_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-h0d788c3_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxt-1.3.1-hb9d3cd8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/_r-mutex-1.0.1-anacondar_1.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-4.18.0-he073ed8_9.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-14.4.0-hd9a9cd0_101.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-14.4.0-ha5b54cb_101.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-assertthat-0.2.1-r44hc72bb7e_6.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-bh-1.87.0_1-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-clipr-0.8.1-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-cowplot-1.2.0-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-cpp11-0.5.5-r44h785f33e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-crayon-1.5.3-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-generics-0.1.4-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-getopt-1.21.1-r44ha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-ggplot2-4.0.3-r44h785f33e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-gtable-0.3.6-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-hms-1.1.4-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-labeling-0.4.3-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-lifecycle-1.0.5-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-munsell-0.5.1-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-optparse-1.8.2-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-pillar-1.11.1-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-pkgconfig-2.0.3-r44hc72bb7e_5.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-prettyunits-1.2.0-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-progress-1.2.3-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-r6-2.6.1-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-rcolorbrewer-1.1_3-r44h785f33e_4.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-remotes-2.5.0-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-scales-1.4.0-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-tidyselect-1.2.1-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-viridislite-0.4.3-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-withr-3.0.3-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_9.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda - osx-arm64: - - conda: https://conda.anaconda.org/bioconda/noarch/nextflow-26.04.6-h2a3209d_1.conda - - conda: https://conda.anaconda.org/bioconda/osx-arm64/bioconductor-rhdf5lib-1.28.0-r44h5badf08_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/_r-mutex-1.0.1-anacondar_1.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/compiler-rt_osx-arm64-19.1.7-he32a8d3_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/libcxx-headers-19.1.7-h707e725_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_osx-arm64-16.1.0-h2713bec_101.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-assertthat-0.2.1-r44hc72bb7e_6.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-bh-1.87.0_1-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-clipr-0.8.1-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-cowplot-1.2.0-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-cpp11-0.5.5-r44h785f33e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-crayon-1.5.3-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-generics-0.1.4-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-getopt-1.21.1-r44ha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-ggplot2-4.0.3-r44h785f33e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-gtable-0.3.6-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-hms-1.1.4-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-labeling-0.4.3-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-lifecycle-1.0.5-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-munsell-0.5.1-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-optparse-1.8.2-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-pillar-1.11.1-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-pkgconfig-2.0.3-r44hc72bb7e_5.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-prettyunits-1.2.0-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-progress-1.2.3-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-r6-2.6.1-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-rcolorbrewer-1.1_3-r44h785f33e_4.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-remotes-2.5.0-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-scales-1.4.0-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-tidyselect-1.2.1-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-viridislite-0.4.3-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-withr-3.0.3-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/sdkroot_env_osx-arm64-26.0-ha3f98da_7.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-7_kmp_llvm.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bwidget-1.10.1-hce30654_1.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/c-ares-1.34.8-h1a92334_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-compiler-1.11.0-h61f9b84_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-he0f2337_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cctools-1030.6.3-llvm19_1_hd01ab73_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cctools_impl_osx-arm64-1030.6.3-llvm19_1_hc7668c6_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cctools_osx-arm64-1030.6.3-llvm19_1_hd01ab73_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/clang-19-19.1.7-default_hf3020a7_9.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/clang-19.1.7-default_hf9bcbb7_9.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/clang_impl_osx-arm64-19.1.7-default_hc11f16d_9.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/clang_osx-arm64-19.1.7-h75f8d18_34.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/clangxx-19.1.7-default_hc995acf_9.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/clangxx_impl_osx-arm64-19.1.7-default_hc11f16d_9.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/clangxx_osx-arm64-19.1.7-h75f8d18_34.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/compiler-rt-19.1.7-h855ad52_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coreutils-9.11-h1a92334_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.21.0-hf618e03_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cxx-compiler-1.11.0-h88570a1_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.3-h2b252f5_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-h84a0fba_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gcc_impl_osx-arm64-16.1.0-h2d6a911_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gfortran_impl_osx-arm64-16.1.0-h68f779f_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-h784d473_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gsl-2.7-h6e638da_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-py310h579977c_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-hfd3d5f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ld64-956.6-llvm19_1_he86490a_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ld64_osx-arm64-956.6-llvm19_1_ha2625f7_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.2.0-h1eee2c3_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.5-h8664d51_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libasprintf-0.25.1-h493aca8_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-9_h51639a9_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-9_hb0561ab_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libclang-cpp19.1-19.1.7-default_hf3020a7_9.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.21.0-hf618e03_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.8-h55c6f16_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-devel-19.1.7-h6dc3340_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-he7e0567_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h1a92334_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-hcf2aa1b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-hdfa99f5_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.1.0-h3cf6597_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgettextpo-0.25.1-h493aca8_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.1.0-h07b0088_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.1.0-h32cdfcc_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-ha531971_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.3.0-h5a65909_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-h23cfdf5_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.25.1-h493aca8_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.2.0-h84a0fba_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-9_hd9741b5_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libllvm19-19.1.7-h8e0c9ce_2.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/libnghttp2-1.68.1-h8f3e76b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.34-openmp_he657e61_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-h132b30e_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpsl-0.23.1-h7a62e17_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsigtool-0.1.3-h98dc951_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.2-h282da08_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h202fb40_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.3-h5ef1a60_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.3-h5654f7c_0.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-22.1.8-hc7d1edf_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-tools-19-19.1.7-h91fd4e7_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-tools-19.1.7-h855ad52_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/make-4.4.1-h84a0fba_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/openjdk-25.0.2-h258754b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.3-hd24854e_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pango-1.58.2-hf80efc4_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.47-h30297fc_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h784d473_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-base-4.4.3-h35b0bb1_11.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-bit-4.6.0-r44h6168396_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-bit64-4.8.6-r44h864cdf1_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-cli-3.6.6-r44h1380947_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-colorspace-2.1_3-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-data.table-1.18.4-r44h73351a1_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-dplyr-1.2.1-r44h1380947_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-ellipsis-0.3.3-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-fansi-1.0.7-r44h6168396_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-farver-2.1.2-r44hc1cd577_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-glue-1.8.1-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-isoband-0.3.0-r44hc1cd577_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-lattice-0.22_9-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-magrittr-2.0.5-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-matrix-1.7_6-r44h3f89421_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-nanoparquet-0.5.1-r44h1380947_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-parallelly-1.48.0-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-purrr-1.2.2-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-rcpp-1.1.2-r44h1380947_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-readr-2.2.0-r44h1380947_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-rlang-1.3.0-r44h1380947_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-s7-0.2.2-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-tibble-3.3.1-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-tzdb-0.5.0-r44hc1cd577_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-utf8-1.2.6-r44h6168396_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-vctrs-0.7.3-r44h1380947_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-vroom-1.7.1-r44h1380947_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sigtool-codesign-0.1.3-h98dc951_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tapi-1600.0.11.8-hb561403_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hd3d0363_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tktable-2.10-h28d2b5e_8.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda default: channels: - url: https://conda.anaconda.org/conda-forge/ - url: https://conda.anaconda.org/bioconda/ + indexes: + - https://pypi.org/simple packages: linux-64: - - conda: https://conda.anaconda.org/bioconda/linux-64/bioconductor-rhdf5lib-1.28.0-r44h15a9599_0.tar.bz2 - conda: https://conda.anaconda.org/bioconda/noarch/nextflow-26.04.6-h2a3209d_1.conda - 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/alsa-lib-1.2.16.1-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.46.1-default_hfdba357_102.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.46.1-default_h4852527_102.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/bwidget-1.10.1-ha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.10.4-hb7a77c6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.14-h2aa3ae6_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.14.2-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.2-h720e601_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.7.1-h6ffeea8_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.11.0-h38ae05a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.27.3-h6f4d18d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.16.0-h21f4ec5_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.12.8-h46fcd08_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.7-h720e601_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.10-h720e601_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.40.1-h102d43b_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.833-hc7390e0_9.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.16.3-h206d751_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.13.3-h71f81a8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.18.0-h74b55db_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.14.0-hf596fc9_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.16.0-h1f05bef_1.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/c-ares-1.34.8-h280c20c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-he90730b_1.conda @@ -357,30 +48,39 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/curl-8.21.0-heca4667_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h27c8c51_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_impl_linux-64-16.1.0-h5fcb69b_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-16.1.0-h5fd2508_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gfortran_impl_linux-64-16.1.0-h08e1a1a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.3.1-h54a6638_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-ha257d8a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hb7133d2_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/google-crc32c-1.8.0-py312hda0ef95_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gsl-2.7-he838d99_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-16.1.0-he33a5f8_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-16.1.0-h5525346_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.16.0-nompi_py312hfaa9938_104.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.3.0-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h19486de_110.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-hbde042b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h0c24ade_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.2.0-hdb68285_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260526.0-cxx17_h0dc7533_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-25.0.0-hcc2f0d9_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-25.0.0-h635bf11_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-compute-25.0.0-h53684a4_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-25.0.0-h635bf11_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-25.0.0-h66fbfdd_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-9_h4a7cf45_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h39a168f_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-ha411449_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-h018ffa1_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-9_h0358290_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-heca4667_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-hd45a770_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h280c20c_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-h5348a74_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_1.conda @@ -391,6 +91,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.1.0-h79bb938_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-h45c3219_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.1.0-he0feb66_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-3.8.0-hbc29df5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-3.8.0-hdbdcf42_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.82.1-h792040b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.3.0-h17a8019_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-devel-14.3.0-h17a8019_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda @@ -398,52 +101,59 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-9_h47877c9_openblas.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/libnghttp2-1.68.1-h877daf1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.34-pthreads_h94d23a6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-1.27.0-h3133023_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-headers-1.27.0-ha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-25.0.0-h7376487_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.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/libpsl-0.23.1-hf670292_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-16.1.0-hf2715c6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.12.14-h0c77377_3_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h60473fc_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.4-h13e7031_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.1.0-h934c35e_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.1.0-hdf11a46_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.22.0-h7d032f7_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-h9d88235_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.2-h5347b49_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.11.3-hfe17d71_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.3-hcfc3c73_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.38-h280c20c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.4-hf3af7cc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.4-h7df9aa5_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/make-4.4.1-hb03c661_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/llvmlite-0.49.0-py312hb491018_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-hee9eb32_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py312h9be0db6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/msgspec-0.21.1-py312h5cc1888_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/nlohmann_json-3.12.0-h54a6638_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numba-0.67.0-py312h80505a6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numcodecs-0.16.5-py312hf79963d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.3-py312he827f4e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openjdk-25.0.2-ha668962_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.3-h35e630c_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.58.2-hda50119_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.3.1-h443056b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.6-np2py312h91ec553_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-haa7fec5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/prometheus-cpp-1.3.0-ha5d0236_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb03c661_1003.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-base-4.4.3-h502d0c9_11.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-bit-4.6.0-r44h54b55ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-bit64-4.8.6-r44hddc5280_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-cli-3.6.6-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-data.table-1.18.4-r44h1c8cec4_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-dplyr-1.2.1-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-ellipsis-0.3.3-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-fansi-1.0.7-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-glue-1.8.1-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-lattice-0.22_9-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-magrittr-2.0.5-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-matrix-1.7_6-r44h0e4624f_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-nanoparquet-0.5.1-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-parallelly-1.48.0-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-purrr-1.2.2-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-rcpp-1.1.2-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-readr-2.2.0-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-rlang-1.3.0-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-tibble-3.3.1-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-tzdb-0.5.0-r44h3697838_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-utf8-1.2.6-r44h54b55ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-vctrs-0.7.3-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-vroom-1.7.1-r44h3697838_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-25.0.0-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-25.0.0-py312h2054cf2_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.5-py312hc767a74_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h94463f1_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/sed-4.10-h19d0853_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.5-h7e3ee7f_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.18.1-py312h106c528_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd70dff1_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/tktable-2.10-h5a7a40f_8.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-2.4.1-py312h1b36aeb_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-h280c20c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-h0d788c3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_0.conda @@ -456,193 +166,207 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxt-1.3.1-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.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/zlib-1.3.2-h25fd6f3_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/_r-mutex-1.0.1-anacondar_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/anndata-0.13.3.post0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/array-api-compat-1.15.0-pyhc364b38_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/cached-property-2.0.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-2.0.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/donfig-0.8.1.post1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-4.18.0-he073ed8_9.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-16.1.0-h59071f9_101.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-16.1.0-h41cdd0d_101.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-assertthat-0.2.1-r44hc72bb7e_6.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-clipr-0.8.1-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-cpp11-0.5.5-r44h785f33e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-crayon-1.5.3-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-generics-0.1.4-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-getopt-1.21.1-r44ha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-hms-1.1.4-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-lifecycle-1.0.5-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-optparse-1.8.2-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-pillar-1.11.1-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-pkgconfig-2.0.3-r44hc72bb7e_5.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-prettyunits-1.2.0-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-progress-1.2.3-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-r6-2.6.1-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-tidyselect-1.2.1-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-withr-3.0.3-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_9.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/legacy-api-wrap-1.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mudata-0.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/natsort-8.4.0-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.2.3-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-9_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/scverse-misc-0.1.6-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/session-info2-0.4.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.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-inspection-0.4.4-pyhcf101f3_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/zarr-3.4.0-pyhc364b38_0.conda + - pypi: ./ + - pypi: git+https://github.com/broadinstitute/pysceptre.git?tag=v0.2.0#72b5e77a76a123afe80e5c55308d7883bd0927f4 + - pypi: https://files.pythonhosted.org/packages/43/3f/f88a53f60a472b46f4023f56d204dd7de33d34c5d2acbfa0d70a674e639e/threadpoolctl-3.7.0-py3-none-any.whl osx-arm64: - conda: https://conda.anaconda.org/bioconda/noarch/nextflow-26.04.6-h2a3209d_1.conda - - conda: https://conda.anaconda.org/bioconda/osx-arm64/bioconductor-rhdf5lib-1.28.0-r44h5badf08_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/_r-mutex-1.0.1-anacondar_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/anndata-0.13.3.post0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/array-api-compat-1.15.0-pyhc364b38_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/compiler-rt22_osx-arm64-22.1.8-h7e67a1e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/compiler-rt_osx-arm64-22.1.8-hce30654_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/libcxx-headers-22.1.8-h707e725_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_osx-arm64-16.1.0-h2713bec_101.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-assertthat-0.2.1-r44hc72bb7e_6.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-clipr-0.8.1-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-cpp11-0.5.5-r44h785f33e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-crayon-1.5.3-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-generics-0.1.4-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-getopt-1.21.1-r44ha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-hms-1.1.4-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-lifecycle-1.0.5-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-optparse-1.8.2-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-pillar-1.11.1-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-pkgconfig-2.0.3-r44hc72bb7e_5.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-prettyunits-1.2.0-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-progress-1.2.3-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-r6-2.6.1-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-tidyselect-1.2.1-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-withr-3.0.3-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/sdkroot_env_osx-arm64-26.0-ha3f98da_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-2.0.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-2.0.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/donfig-0.8.1.post1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/legacy-api-wrap-1.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mudata-0.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/natsort-8.4.0-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.2.3-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-9_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/scverse-misc-0.1.6-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/session-info2-0.4.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.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-inspection-0.4.4-pyhcf101f3_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/zarr-3.4.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bwidget-1.10.1-hce30654_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.10.4-h0f08c97_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.14-hb9ed60c_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.14.2-h84a0fba_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.2-hf0b4b85_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.7.1-ha581b6a_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.11.0-h27f0cb5_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.27.3-ha24efe8_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.16.0-hf1a914d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.12.8-hdc61527_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.7-hf0b4b85_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.10-hf0b4b85_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.40.1-h5b5d287_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.833-haa98fb3_9.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.16.3-he5ae378_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.13.3-h05177fb_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.18.0-h409340b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.14.0-h7cba3ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.16.0-h16bb3af_1.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/c-ares-1.34.8-h1a92334_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-he0f2337_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cctools-1030.6.3-llvm22_1_hbe26303_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cctools_impl_osx-arm64-1030.6.3-llvm22_1_h7bf7afb_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cctools_osx-arm64-1030.6.3-llvm22_1_hbe26303_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/clang-22-22.1.8-default_hed75349_6.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/clang-22.1.8-default_cfg_hbc7c751_6.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/clang-scan-deps-22.1.8-default_hc257da1_6.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/clang_impl_osx-arm64-22.1.8-default_hc257da1_6.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/clang_osx-arm64-22.1.8-hc95f77d_34.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/clangxx_impl_osx-arm64-22.1.8-default_h9cdd5ba_6.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/clangxx_osx-arm64-22.1.8-hc95f77d_34.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/compiler-rt-22.1.8-hce30654_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/compiler-rt22-22.1.8-hd34ed20_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coreutils-9.11-h1a92334_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.21.0-hf618e03_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.3-h2b252f5_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-h84a0fba_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gcc_impl_osx-arm64-16.1.0-h2d6a911_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gfortran_impl_osx-arm64-16.1.0-h68f779f_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-h784d473_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gsl-2.7-h6e638da_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.3.1-h784d473_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-h6f9ecc1_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/google-crc32c-1.8.0-py312h2be8f03_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.16.0-nompi_py312h3cfd1a2_104.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-1.14.6-nompi_had3affe_110.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-py310h579977c_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-hfd3d5f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ld64-956.6-llvm22_1_h5b97f1b_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ld64_osx-arm64-956.6-llvm22_1_h692d5aa_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.2.0-h1eee2c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260526.0-cxx17_h4c27e2a_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.5-h8664d51_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libasprintf-0.25.1-h493aca8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-25.0.0-haaed7fe_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-25.0.0-heaff1e6_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-compute-25.0.0-h93a2d87_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-25.0.0-heaff1e6_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-25.0.0-h2208e57_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-9_h51639a9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-h1dcdb26_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-h5295a6a_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-h2ddc9cb_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-9_hb0561ab_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libclang-cpp22.1-22.1.8-default_hc257da1_6.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libclang13-22.1.8-default_hed75349_6.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcompiler-rt-22.1.8-hd34ed20_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.21.0-hf618e03_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.8-h55c6f16_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-devel-22.1.8-h6dc3340_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-he7e0567_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h1a92334_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h4b5715a_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-hcf2aa1b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-hdfa99f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.1.0-h3cf6597_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgettextpo-0.25.1-h493aca8_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.1.0-h07b0088_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.1.0-h32cdfcc_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-ha531971_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.3.0-h5a65909_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-3.8.0-ha595bda_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-3.8.0-hc8aed40_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.82.1-hf3e839d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-h23cfdf5_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.25.1-h493aca8_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.2.0-h84a0fba_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-9_hd9741b5_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libllvm22-22.1.8-h89af1be_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/libnghttp2-1.68.1-h8f3e76b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.34-openmp_he657e61_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-h132b30e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-1.27.0-h46c1d2a_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-headers-1.27.0-hce30654_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-25.0.0-h0de02aa_4_cpu.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/libpsl-0.23.1-h7a62e17_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsigtool-0.1.3-h98dc951_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.12.14-h4e5ec87_3_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h5d15848_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.4-hca69786_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.2-h282da08_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h202fb40_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.22.0-h1fb9c8a_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.11.3-h2431656_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.3-h5ef1a60_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.3-h5654f7c_0.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-22.1.8-hc7d1edf_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-tools-22-22.1.8-hb545844_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-tools-22.1.8-hd34ed20_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/make-4.4.1-h84a0fba_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvmlite-0.49.0-py312ha6a25cf_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-hdca3b7d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.2-py312h67ba313_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgspec-0.21.1-py312h580468c_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/nlohmann_json-3.12.0-h784d473_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numba-0.67.0-py312h9052ff5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numcodecs-0.16.5-py312h5978115_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.3-py312hff34920_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjdk-25.0.2-h258754b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.3-hd24854e_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pango-1.58.2-hf80efc4_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.47-h30297fc_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h784d473_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-base-4.4.3-h35b0bb1_11.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-bit-4.6.0-r44h6168396_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-bit64-4.8.6-r44h864cdf1_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-cli-3.6.6-r44h1380947_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-data.table-1.18.4-r44h73351a1_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-dplyr-1.2.1-r44h1380947_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-ellipsis-0.3.3-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-fansi-1.0.7-r44h6168396_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-glue-1.8.1-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-lattice-0.22_9-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-magrittr-2.0.5-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-matrix-1.7_6-r44h3f89421_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-nanoparquet-0.5.1-r44h1380947_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-parallelly-1.48.0-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-purrr-1.2.2-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-rcpp-1.1.2-r44h1380947_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-readr-2.2.0-r44h1380947_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-rlang-1.3.0-r44h1380947_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-tibble-3.3.1-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-tzdb-0.5.0-r44hc1cd577_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-utf8-1.2.6-r44h6168396_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-vctrs-0.7.3-r44h1380947_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-vroom-1.7.1-r44h1380947_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.3.1-hef2a647_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.6-np2py312hd9090a6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/prometheus-cpp-1.3.0-h0967b3e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-25.0.0-py312h1f38498_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-25.0.0-py312h7d9569a_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.5-py312ha80e978_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.14-hd05a0c4_3_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py312h04c11ed_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-h5d60da5_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sigtool-codesign-0.1.3-h98dc951_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tapi-1600.0.11.8-hb561403_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/s2n-1.7.5-h1b28cb6_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.18.1-py312h84b0c4e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hd3d0363_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tktable-2.10-h28d2b5e_8.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-2.4.1-py312hbd136b4_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h74c22ad_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda + - pypi: ./ + - pypi: git+https://github.com/broadinstitute/pysceptre.git?tag=v0.2.0#72b5e77a76a123afe80e5c55308d7883bd0927f4 + - pypi: https://files.pythonhosted.org/packages/43/3f/f88a53f60a472b46f4023f56d204dd7de33d34c5d2acbfa0d70a674e639e/threadpoolctl-3.7.0-py3-none-any.whl dev: channels: - url: https://conda.anaconda.org/conda-forge/ - url: https://conda.anaconda.org/bioconda/ + indexes: + - https://pypi.org/simple packages: linux-64: - - conda: https://conda.anaconda.org/bioconda/linux-64/bioconductor-rhdf5lib-1.28.0-r44h15a9599_0.tar.bz2 - conda: https://conda.anaconda.org/bioconda/noarch/nextflow-26.04.6-h2a3209d_1.conda - 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/alsa-lib-1.2.16.1-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.46.1-default_hfdba357_102.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.46.1-default_h4852527_102.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/bwidget-1.10.1-ha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.10.4-hb7a77c6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.14-h2aa3ae6_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.14.2-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.2-h720e601_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.7.1-h6ffeea8_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.11.0-h38ae05a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.27.3-h6f4d18d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.16.0-h21f4ec5_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.12.8-h46fcd08_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.7-h720e601_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.10-h720e601_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.40.1-h102d43b_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.833-hc7390e0_9.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.16.3-h206d751_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.13.3-h71f81a8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.18.0-h74b55db_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.14.0-hf596fc9_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.16.0-h1f05bef_1.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/c-ares-1.34.8-h280c20c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-he90730b_1.conda @@ -650,30 +374,39 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/curl-8.21.0-heca4667_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h27c8c51_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_impl_linux-64-16.1.0-h5fcb69b_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-16.1.0-h5fd2508_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gfortran_impl_linux-64-16.1.0-h08e1a1a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.3.1-h54a6638_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-ha257d8a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hb7133d2_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/google-crc32c-1.8.0-py312hda0ef95_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gsl-2.7-he838d99_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-16.1.0-he33a5f8_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-16.1.0-h5525346_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.16.0-nompi_py312hfaa9938_104.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.3.0-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h19486de_110.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-hbde042b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h0c24ade_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.2.0-hdb68285_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260526.0-cxx17_h0dc7533_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-25.0.0-hcc2f0d9_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-25.0.0-h635bf11_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-compute-25.0.0-h53684a4_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-25.0.0-h635bf11_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-25.0.0-h66fbfdd_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-9_h4a7cf45_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h39a168f_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-ha411449_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-h018ffa1_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-9_h0358290_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-heca4667_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-hd45a770_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h280c20c_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-h5348a74_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_1.conda @@ -684,6 +417,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.1.0-h79bb938_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-h45c3219_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.1.0-he0feb66_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-3.8.0-hbc29df5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-3.8.0-hdbdcf42_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.82.1-h792040b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.3.0-h17a8019_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-devel-14.3.0-h17a8019_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda @@ -691,61 +427,60 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-9_h47877c9_openblas.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/libnghttp2-1.68.1-h877daf1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.34-pthreads_h94d23a6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-1.27.0-h3133023_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-headers-1.27.0-ha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-25.0.0-h7376487_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.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/libpsl-0.23.1-hf670292_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-16.1.0-hf2715c6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.12.14-h0c77377_3_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h60473fc_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.4-h13e7031_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.1.0-h934c35e_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.1.0-hdf11a46_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.22.0-h7d032f7_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-h9d88235_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.2-h5347b49_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/libutf8proc-2.11.3-hfe17d71_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.3-hcfc3c73_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.38-h280c20c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.4-hf3af7cc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.4-h7df9aa5_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/make-4.4.1-hb03c661_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/llvmlite-0.49.0-py312hb491018_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-hee9eb32_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py312h9be0db6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/msgspec-0.21.1-py312h5cc1888_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/nlohmann_json-3.12.0-h54a6638_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numba-0.67.0-py312h80505a6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numcodecs-0.16.5-py312hf79963d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.3-py312he827f4e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openjdk-25.0.2-ha668962_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.3-h35e630c_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.58.2-hda50119_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.3.1-h443056b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.6-np2py312h91ec553_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-haa7fec5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/prometheus-cpp-1.3.0-ha5d0236_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb03c661_1003.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-base-4.4.3-h502d0c9_11.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-bit-4.6.0-r44h54b55ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-bit64-4.8.6-r44hddc5280_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-brio-1.1.5-r44h54b55ab_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-cli-3.6.6-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-data.table-1.18.4-r44h1c8cec4_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-diffobj-0.3.8-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-digest-0.6.39-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-dplyr-1.2.1-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-ellipsis-0.3.3-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-fansi-1.0.7-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-fs-2.1.0-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-glue-1.8.1-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-jsonlite-2.0.0-r44h54b55ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-lattice-0.22_9-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-magrittr-2.0.5-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-matrix-1.7_6-r44h0e4624f_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-nanoparquet-0.5.1-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-parallelly-1.48.0-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-processx-3.9.0-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-ps-1.9.3-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-purrr-1.2.2-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-rcpp-1.1.2-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-readr-2.2.0-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-rlang-1.3.0-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-testthat-3.3.2-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-tibble-3.3.1-r44h54b55ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-tzdb-0.5.0-r44h3697838_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-utf8-1.2.6-r44h54b55ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-vctrs-0.7.3-r44h3697838_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/r-vroom-1.7.1-r44h3697838_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-25.0.0-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-25.0.0-py312h2054cf2_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.5-py312hc767a74_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h94463f1_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/sed-4.10-h19d0853_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.16.7-hbcb56a7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.5-h7e3ee7f_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.18.1-py312h106c528_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd70dff1_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/tktable-2.10-h5a7a40f_8.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-2.4.1-py312h1b36aeb_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-h280c20c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-h0d788c3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_0.conda @@ -758,223 +493,194 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxt-1.3.1-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.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/zlib-1.3.2-h25fd6f3_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/_r-mutex-1.0.1-anacondar_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/anndata-0.13.3.post0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/array-api-compat-1.15.0-pyhc364b38_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/cached-property-2.0.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-2.0.1-pyhcf101f3_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/deprecated-1.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/donfig-0.8.1.post1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-4.18.0-he073ed8_9.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-16.1.0-h59071f9_101.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-16.1.0-h41cdd0d_101.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-assertthat-0.2.1-r44hc72bb7e_6.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-callr-3.7.6-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-clipr-0.8.1-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-cpp11-0.5.5-r44h785f33e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-crayon-1.5.3-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-desc-1.4.3-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-evaluate-1.0.5-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-generics-0.1.4-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-getopt-1.21.1-r44ha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-hms-1.1.4-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-lifecycle-1.0.5-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-optparse-1.8.2-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-pillar-1.11.1-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-pkgbuild-1.4.8-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-pkgconfig-2.0.3-r44hc72bb7e_5.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-pkgload-1.5.3-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-praise-1.0.0-r44hc72bb7e_1009.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-prettyunits-1.2.0-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-progress-1.2.3-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-r6-2.6.1-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-rematch2-2.1.2-r44hc72bb7e_5.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-rprojroot-2.1.1-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-tidyselect-1.2.1-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-waldo-0.6.2-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-withr-3.0.3-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_9.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/legacy-api-wrap-1.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mudata-0.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/natsort-8.4.0-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_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/pydantic-2.13.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.15.0-pyhcf101f3_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-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.2.3-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-9_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/scverse-misc-0.1.6-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/session-info2-0.4.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_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-inspection-0.4.4-pyhcf101f3_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/zarr-3.4.0-pyhc364b38_0.conda + - pypi: ./ + - pypi: git+https://github.com/broadinstitute/pysceptre.git?tag=v0.2.0#72b5e77a76a123afe80e5c55308d7883bd0927f4 + - pypi: https://files.pythonhosted.org/packages/43/3f/f88a53f60a472b46f4023f56d204dd7de33d34c5d2acbfa0d70a674e639e/threadpoolctl-3.7.0-py3-none-any.whl osx-arm64: - conda: https://conda.anaconda.org/bioconda/noarch/nextflow-26.04.6-h2a3209d_1.conda - - conda: https://conda.anaconda.org/bioconda/osx-arm64/bioconductor-rhdf5lib-1.28.0-r44h5badf08_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/_r-mutex-1.0.1-anacondar_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/anndata-0.13.3.post0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/array-api-compat-1.15.0-pyhc364b38_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/compiler-rt22_osx-arm64-22.1.8-h7e67a1e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/compiler-rt_osx-arm64-22.1.8-hce30654_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/libcxx-headers-22.1.8-h707e725_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_osx-arm64-16.1.0-h2713bec_101.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-assertthat-0.2.1-r44hc72bb7e_6.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-callr-3.7.6-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-clipr-0.8.1-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-cpp11-0.5.5-r44h785f33e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-crayon-1.5.3-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-desc-1.4.3-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-evaluate-1.0.5-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-generics-0.1.4-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-getopt-1.21.1-r44ha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-hms-1.1.4-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-lifecycle-1.0.5-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-optparse-1.8.2-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-pillar-1.11.1-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-pkgbuild-1.4.8-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-pkgconfig-2.0.3-r44hc72bb7e_5.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-pkgload-1.5.3-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-praise-1.0.0-r44hc72bb7e_1009.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-prettyunits-1.2.0-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-progress-1.2.3-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-r6-2.6.1-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-rematch2-2.1.2-r44hc72bb7e_5.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-rprojroot-2.1.1-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-tidyselect-1.2.1-r44hc72bb7e_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-waldo-0.6.2-r44hc72bb7e_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/r-withr-3.0.3-r44hc72bb7e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/sdkroot_env_osx-arm64-26.0-ha3f98da_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-2.0.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-2.0.1-pyhcf101f3_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/deprecated-1.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/donfig-0.8.1.post1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/legacy-api-wrap-1.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mudata-0.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/natsort-8.4.0-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_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/pydantic-2.13.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.15.0-pyhcf101f3_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-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.2.3-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-9_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/scverse-misc-0.1.6-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/session-info2-0.4.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_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-inspection-0.4.4-pyhcf101f3_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/zarr-3.4.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bwidget-1.10.1-hce30654_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.10.4-h0f08c97_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.14-hb9ed60c_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.14.2-h84a0fba_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.2-hf0b4b85_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.7.1-ha581b6a_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.11.0-h27f0cb5_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.27.3-ha24efe8_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.16.0-hf1a914d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.12.8-hdc61527_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.7-hf0b4b85_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.10-hf0b4b85_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.40.1-h5b5d287_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.833-haa98fb3_9.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.16.3-he5ae378_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.13.3-h05177fb_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.18.0-h409340b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.14.0-h7cba3ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.16.0-h16bb3af_1.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/c-ares-1.34.8-h1a92334_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-he0f2337_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cctools-1030.6.3-llvm22_1_hbe26303_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cctools_impl_osx-arm64-1030.6.3-llvm22_1_h7bf7afb_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cctools_osx-arm64-1030.6.3-llvm22_1_hbe26303_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/clang-22-22.1.8-default_hed75349_6.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/clang-22.1.8-default_cfg_hbc7c751_6.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/clang-scan-deps-22.1.8-default_hc257da1_6.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/clang_impl_osx-arm64-22.1.8-default_hc257da1_6.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/clang_osx-arm64-22.1.8-hc95f77d_34.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/clangxx_impl_osx-arm64-22.1.8-default_h9cdd5ba_6.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/clangxx_osx-arm64-22.1.8-hc95f77d_34.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/compiler-rt-22.1.8-hce30654_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/compiler-rt22-22.1.8-hd34ed20_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coreutils-9.11-h1a92334_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/curl-8.21.0-hf618e03_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.3-h2b252f5_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-h84a0fba_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gcc_impl_osx-arm64-16.1.0-h2d6a911_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gfortran_impl_osx-arm64-16.1.0-h68f779f_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-h784d473_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gsl-2.7-h6e638da_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.3.1-h784d473_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-h6f9ecc1_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/google-crc32c-1.8.0-py312h2be8f03_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.16.0-nompi_py312h3cfd1a2_104.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-1.14.6-nompi_had3affe_110.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-py310h579977c_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-hfd3d5f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ld64-956.6-llvm22_1_h5b97f1b_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ld64_osx-arm64-956.6-llvm22_1_h692d5aa_5.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.2.0-h1eee2c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260526.0-cxx17_h4c27e2a_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.5-h8664d51_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libasprintf-0.25.1-h493aca8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-25.0.0-haaed7fe_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-25.0.0-heaff1e6_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-compute-25.0.0-h93a2d87_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-25.0.0-heaff1e6_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-25.0.0-h2208e57_4_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-9_h51639a9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-h1dcdb26_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-h5295a6a_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-h2ddc9cb_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-9_hb0561ab_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libclang-cpp22.1-22.1.8-default_hc257da1_6.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libclang13-22.1.8-default_hed75349_6.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcompiler-rt-22.1.8-hd34ed20_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.21.0-hf618e03_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.8-h55c6f16_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-devel-22.1.8-h6dc3340_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-he7e0567_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h1a92334_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h4b5715a_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-hcf2aa1b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-hdfa99f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.1.0-h3cf6597_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgettextpo-0.25.1-h493aca8_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.1.0-h07b0088_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.1.0-h32cdfcc_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-ha531971_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.3.0-h5a65909_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-3.8.0-ha595bda_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-3.8.0-hc8aed40_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.82.1-hf3e839d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-h23cfdf5_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.25.1-h493aca8_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.2.0-h84a0fba_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-9_hd9741b5_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libllvm22-22.1.8-h89af1be_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/libnghttp2-1.68.1-h8f3e76b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.34-openmp_he657e61_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-h132b30e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-1.27.0-h46c1d2a_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-headers-1.27.0-hce30654_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-25.0.0-h0de02aa_4_cpu.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/libpsl-0.23.1-h7a62e17_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsigtool-0.1.3-h98dc951_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.12.14-h4e5ec87_3_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h5d15848_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.4-hca69786_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.2-h282da08_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/libwebp-base-1.6.0-h202fb40_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.22.0-h1fb9c8a_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.11.3-h2431656_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.3-h5ef1a60_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.3-h5654f7c_0.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-22.1.8-hc7d1edf_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-tools-22-22.1.8-hb545844_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-tools-22.1.8-hd34ed20_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/make-4.4.1-h84a0fba_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvmlite-0.49.0-py312ha6a25cf_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-hdca3b7d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.2-py312h67ba313_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgspec-0.21.1-py312h580468c_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/nlohmann_json-3.12.0-h784d473_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numba-0.67.0-py312h9052ff5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numcodecs-0.16.5-py312h5978115_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.3-py312hff34920_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjdk-25.0.2-h258754b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.3-hd24854e_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pango-1.58.2-hf80efc4_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.47-h30297fc_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h784d473_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-base-4.4.3-h35b0bb1_11.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-bit-4.6.0-r44h6168396_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-bit64-4.8.6-r44h864cdf1_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-brio-1.1.5-r44h6168396_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-cli-3.6.6-r44h1380947_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-data.table-1.18.4-r44h73351a1_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-diffobj-0.3.8-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-digest-0.6.39-r44hc1cd577_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-dplyr-1.2.1-r44h1380947_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-ellipsis-0.3.3-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-fansi-1.0.7-r44h6168396_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-fs-2.1.0-r44h45a6d21_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-glue-1.8.1-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-jsonlite-2.0.0-r44h6168396_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-lattice-0.22_9-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-magrittr-2.0.5-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-matrix-1.7_6-r44h3f89421_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-nanoparquet-0.5.1-r44h1380947_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-parallelly-1.48.0-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-processx-3.9.0-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-ps-1.9.3-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-purrr-1.2.2-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-rcpp-1.1.2-r44h1380947_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-readr-2.2.0-r44h1380947_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-rlang-1.3.0-r44h1380947_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-testthat-3.3.2-r44h1380947_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-tibble-3.3.1-r44hbe92478_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-tzdb-0.5.0-r44hc1cd577_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-utf8-1.2.6-r44h6168396_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-vctrs-0.7.3-r44h1380947_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-vroom-1.7.1-r44h1380947_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.3.1-hef2a647_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.6-np2py312hd9090a6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/prometheus-cpp-1.3.0-h0967b3e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-25.0.0-py312h1f38498_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-25.0.0-py312h7d9569a_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.5-py312ha80e978_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.14-hd05a0c4_3_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py312h04c11ed_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-h5d60da5_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sigtool-codesign-0.1.3-h98dc951_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tapi-1600.0.11.8-hb561403_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.16.7-ha7a9cbd_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/s2n-1.7.5-h1b28cb6_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.18.1-py312h84b0c4e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hd3d0363_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tktable-2.10-h28d2b5e_8.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-2.4.1-py312hbd136b4_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h74c22ad_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda + - pypi: ./ + - pypi: git+https://github.com/broadinstitute/pysceptre.git?tag=v0.2.0#72b5e77a76a123afe80e5c55308d7883bd0927f4 + - pypi: https://files.pythonhosted.org/packages/43/3f/f88a53f60a472b46f4023f56d204dd7de33d34c5d2acbfa0d70a674e639e/threadpoolctl-3.7.0-py3-none-any.whl packages: -- conda: https://conda.anaconda.org/bioconda/linux-64/bioconductor-rhdf5lib-1.28.0-r44h15a9599_0.tar.bz2 - sha256: 2902414f21d583303d11cd1642375ec10fcb7ee31f08e64de9162808f6de0424 - md5: 2b17ee3f623b28fc848f8d6c56968694 - depends: - - libblas >=3.9.0,<4.0a0 - - libgcc >=13 - - liblapack >=3.9.0,<4.0a0 - - libzlib >=1.3.1,<2.0a0 - - r-base >=4.4,<4.5.0a0 - license: Artistic-2.0 - run_exports: - weak: - - bioconductor-rhdf5lib >=1.28.0,<1.29.0a0 - size: 4052640 - timestamp: 1734293576720 - conda: https://conda.anaconda.org/bioconda/noarch/nextflow-26.04.6-h2a3209d_1.conda sha256: 452805a6a3cc3f4e28417d855c7c03001dce1b2557a7e8ae46971d3a8778f9af md5: 4fea597e1977b0221de342a3bfb2d971 @@ -983,25 +689,12 @@ packages: - curl - openjdk >=17,<26 license: Apache-2.0 + purls: [] run_exports: weak: - nextflow >=26.4.6,<26.5.0a0 size: 37863659 timestamp: 1784828644646 -- conda: https://conda.anaconda.org/bioconda/osx-arm64/bioconductor-rhdf5lib-1.28.0-r44h5badf08_0.tar.bz2 - sha256: 847defe3911b6c828128f6ffe4cc421241844efb1018faac03a247ac67646cac - md5: f9ae20f5382e25b9239540c69f5e6886 - depends: - - libblas >=3.9.0,<4.0a0 - - liblapack >=3.9.0,<4.0a0 - - libzlib >=1.3.1,<2.0a0 - - r-base >=4.4,<4.5.0a0 - license: Artistic-2.0 - run_exports: - weak: - - bioconductor-rhdf5lib >=1.28.0,<1.29.0a0 - size: 4317684 - timestamp: 1738826869254 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda build_number: 20 sha256: 1dd3fffd892081df9726d7eb7e0dea6198962ba775bd88842135a4ddb4deb3c9 @@ -1013,6 +706,7 @@ packages: - openmp_impl <0.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: strong: - _openmp_mutex >=4.5 @@ -1026,172 +720,420 @@ packages: - libgcc >=14 license: LGPL-2.1-or-later license_family: LGPL + purls: [] run_exports: weak: - alsa-lib >=1.2.16.1,<1.3.0a0 size: 592377 timestamp: 1781521980743 -- conda: https://conda.anaconda.org/conda-forge/linux-64/binutils-2.46.1-default_h4852527_102.conda - sha256: 659c367ef49df7741749a2ad240b007d7df51b4f210505a78543deafb001e44c - md5: e8452fe381cac5fff20563a07722dfa5 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.10.4-hb7a77c6_1.conda + sha256: 845fe32ee750d4a4d3efdb1d95a8c29c3388e3ea9787b77341ec4abe4e198b11 + md5: 4347d5ffdf34adf9c5edd10837727d96 depends: - - binutils_impl_linux-64 >=2.46.1,<2.46.2.0a0 - license: GPL-3.0-only - license_family: GPL - run_exports: {} - size: 35399 - timestamp: 1784214547142 -- conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.46.1-default_hfdba357_102.conda - sha256: fb7bf36984a37ce7e4714d1d1da0bd0e3bfc679520f5cdc184afc676fd4b5da2 - md5: a0c5e0b7f58c8ceeb08e5bc41251d5a2 - depends: - - ld_impl_linux-64 2.46.1 default_hbd61a6d_102 - - sysroot_linux-64 - - zstd >=1.5.7,<1.6.0a0 - license: GPL-3.0-only - license_family: GPL - run_exports: {} - size: 3713752 - timestamp: 1784214522814 -- conda: https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.46.1-default_h4852527_102.conda - sha256: 08d7238663fc408ba2ab60b02fa3d06a7ca9d872962e03e90c7e0fdecb7ed1d0 - md5: 32fd07abe84eb14f17c7f5cc6fa8df82 - depends: - - binutils_impl_linux-64 2.46.1 default_hfdba357_102 - license: GPL-3.0-only - license_family: GPL - run_exports: {} - size: 36337 - timestamp: 1784214551894 -- conda: https://conda.anaconda.org/conda-forge/linux-64/bwidget-1.10.1-ha770c72_1.conda - sha256: c88dd33c89b33409ebcd558d78fdc66a63c18f8b06e04d170668ffb6c8ecfabd - md5: 983b92277d78c0d0ec498e460caa0e6d - depends: - - tk - license: TCL - run_exports: {} - size: 129594 - timestamp: 1750261567920 -- conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda - sha256: 1a0d382c515ebf55f8ee1f38c8b81bc95af5c2acc42ad53b66bc5df932032f96 - md5: e675fabcf81499adc7edf58124fb1e01 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-http >=0.11.0,<0.11.1.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + - aws-c-sdkutils >=0.2.7,<0.2.8.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - aws-c-auth >=0.10.4,<0.10.5.0a0 + size: 135073 + timestamp: 1784086842394 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.14-h2aa3ae6_4.conda + sha256: a67c944be1728f576c02fe8f28b0cbb78b5353415075db3da4ef71bc9dd8c00c + md5: 9c6072e7b882d35ac956c07e493d6a9e depends: - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 - libgcc >=14 - license: bzip2-1.0.6 - license_family: BSD + - openssl >=3.5.7,<4.0a0 + license: Apache-2.0 + license_family: Apache + purls: [] run_exports: weak: - - bzip2 >=1.0.8,<2.0a0 - size: 257808 - timestamp: 1785906269155 -- conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.8-h280c20c_1.conda - sha256: 5139b6afbfaca91c47104ba9a6a40f81211e1c9200e96b73ce3a56f0ca1902f4 - md5: 2cef891b791040aab83e218c7d137679 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + size: 56752 + timestamp: 1784043396713 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.14.2-hb03c661_0.conda + sha256: 701398f1c9978c41e93cb0947869a66b7f8004e9d6e548d63d11aedae83cfb02 + md5: f3e0b2e044485ae90d4a59c77e7a0182 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - constrains: - - c-ares-static <0a0 - license: MIT - license_family: MIT + license: Apache-2.0 + license_family: Apache + purls: [] run_exports: weak: - - c-ares >=1.34.8,<2.0a0 - size: 226755 - timestamp: 1786116641939 -- conda: https://conda.anaconda.org/conda-forge/linux-64/c-compiler-1.11.0-h4d9bdce_0.conda - sha256: 8e7a40f16400d7839c82581410aa05c1f8324a693c9d50079f8c50dc9fb241f0 - md5: abd85120de1187b0d1ec305c2173c71b - depends: - - binutils - - gcc - - gcc_linux-64 14.* - license: BSD-3-Clause - license_family: BSD - run_exports: {} - size: 6693 - timestamp: 1753098721814 -- conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-he90730b_1.conda - sha256: 06525fa0c4e4f56e771a3b986d0fdf0f0fc5a3270830ee47e127a5105bde1b9a - md5: bb6c4808bfa69d6f7f6b07e5846ced37 + - aws-c-common >=0.14.2,<0.14.3.0a0 + size: 246263 + timestamp: 1783637234072 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.2-h720e601_4.conda + sha256: 8e0a5513cfc42df8bd16adbd8e83a37d3ca89b8e04cb20eb04eb177bbbfea365 + md5: c9be3f5854f349ae77a1a174b59e11a8 depends: - __glibc >=2.17,<3.0.a0 - - fontconfig >=2.15.0,<3.0a0 - - fonts-conda-ecosystem - - icu >=78.1,<79.0a0 - - libexpat >=2.7.3,<3.0a0 - - libfreetype >=2.14.1 - - libfreetype6 >=2.14.1 - libgcc >=14 - - libglib >=2.86.3,<3.0a0 - - libpng >=1.6.53,<1.7.0a0 - - libstdcxx >=14 - - libxcb >=1.17.0,<2.0a0 - - libzlib >=1.3.1,<2.0a0 - - pixman >=0.46.4,<1.0a0 - - xorg-libice >=1.1.2,<2.0a0 - - xorg-libsm >=1.2.6,<2.0a0 - - xorg-libx11 >=1.8.12,<2.0a0 - - xorg-libxext >=1.3.6,<2.0a0 - - xorg-libxrender >=0.9.12,<0.10.0a0 - license: LGPL-2.1-only or MPL-1.1 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] run_exports: weak: - - cairo >=1.18.4,<2.0a0 - size: 989514 - timestamp: 1766415934926 -- conda: https://conda.anaconda.org/conda-forge/linux-64/conda-gcc-specs-14.4.0-h611768e_1.conda - sha256: 81327f9b1db24d4d6d2326a6dc21a799a427d0fd8b3a949881a939b3ce44cfc8 - md5: efd6d10c5ffe7431aca2fd8c78c60c32 - depends: - - gcc_impl_linux-64 >=14.4.0,<14.4.1.0a0 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - run_exports: {} - size: 32239 - timestamp: 1785374690936 -- conda: https://conda.anaconda.org/conda-forge/linux-64/coreutils-9.11-h280c20c_0.conda - sha256: 6b19fb6c45302bdd6c97c5ac219e14bcbb7d9055c758e79d8a2577e705a933f0 - md5: c63e2851f3d99a32f4b6991d0460dd25 + - aws-c-compression >=0.3.2,<0.3.3.0a0 + size: 22301 + timestamp: 1784042853709 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.7.1-h6ffeea8_4.conda + sha256: b1f35f7b7a5e745e2d56cf93212770bb56f5207fa9f0e38f98b0c05a1b898a90 + md5: 204d1e8d60c3ae839bd1898e4c7742c8 depends: - libgcc >=14 - __glibc >=2.17,<3.0.a0 - license: GPL-3.0-or-later - license_family: GPL - run_exports: {} - size: 3432565 - timestamp: 1786403825342 -- conda: https://conda.anaconda.org/conda-forge/linux-64/curl-8.21.0-heca4667_4.conda - sha256: 8f14ff477bf2cdf3cd4ff0ab2486b3e6490350da650e67be6c6a0b8a18994a90 - md5: a91f709770cddb5e7a027ab000c441a5 + - libstdcxx >=14 + - aws-checksums >=0.2.10,<0.2.11.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - aws-c-event-stream >=0.7.1,<0.7.2.0a0 + size: 59633 + timestamp: 1784075699558 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.11.0-h38ae05a_4.conda + sha256: 4b939b4a76a11c9ec50c891ae9bf232da8dcaf8797701df1e79ae51c988be2d4 + md5: 97f2799ae6ff7b6f52dfa321fef9676b depends: - __glibc >=2.17,<3.0.a0 - - krb5 >=1.22.2,<1.23.0a0 - - libcurl 8.21.0 heca4667_4 - libgcc >=14 - - libpsl >=0.23.0,<0.24.0a0 + - aws-c-compression >=0.3.2,<0.3.3.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - aws-c-http >=0.11.0,<0.11.1.0a0 + size: 231294 + timestamp: 1784075685646 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.27.3-h6f4d18d_1.conda + sha256: 94c32ca672ce290e250aea1cfb92582cca4658bdc3ec7cb1ceef254f34451595 + md5: bd19b685b88557830f1a617a6d404eb2 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + - s2n >=1.7.5,<1.7.6.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - aws-c-io >=0.27.3,<0.27.4.0a0 + size: 183100 + timestamp: 1784054829913 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.16.0-h21f4ec5_2.conda + sha256: 0252f072e2f493f8348575938c69b48b2799f29f0489c4ad2c5a919ab7e3d02e + md5: 9728195c2f600ef427a1964ee193e707 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - aws-c-io >=0.27.3,<0.27.4.0a0 + - aws-c-http >=0.11.0,<0.11.1.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - aws-c-mqtt >=0.16.0,<0.16.1.0a0 + size: 224933 + timestamp: 1784087527918 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.12.8-h46fcd08_1.conda + sha256: a423bffbb0e6cacb5e2a0861b918ba3180e5132509afb1faf225ee9f0ec8f981 + md5: 706aa99414d18db5b23475b45cc93a0b + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + - aws-c-http >=0.11.0,<0.11.1.0a0 + - openssl >=3.5.7,<4.0a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + - aws-c-auth >=0.10.4,<0.10.5.0a0 + - aws-checksums >=0.2.10,<0.2.11.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - aws-c-s3 >=0.12.8,<0.12.9.0a0 + size: 156174 + timestamp: 1784100985258 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.7-h720e601_2.conda + sha256: e419e179e04021fc680d98af23fac4b4c2369cea61171087e57a734e968dd9bd + md5: 816f62fa82532118ebb2398382090945 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - aws-c-sdkutils >=0.2.7,<0.2.8.0a0 + size: 68515 + timestamp: 1784044260935 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.10-h720e601_4.conda + sha256: 6cc8617854a43040291dea791fe111ba408e7a5bbd9ee9e5a99375da7a16846b + md5: 24ee781effc5779206a80139323c9caf + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - aws-checksums >=0.2.10,<0.2.11.0a0 + size: 101904 + timestamp: 1784044248506 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.40.1-h102d43b_3.conda + sha256: e0c3a119c7035a00e0da325187ee723e07e0e6337e50362349d178ab40961f97 + md5: 3315ce09371baf6d70248b8927aa9866 + depends: + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-sdkutils >=0.2.7,<0.2.8.0a0 + - aws-c-mqtt >=0.16.0,<0.16.1.0a0 + - aws-c-auth >=0.10.4,<0.10.5.0a0 + - aws-c-s3 >=0.12.8,<0.12.9.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + - aws-c-event-stream >=0.7.1,<0.7.2.0a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + - aws-c-http >=0.11.0,<0.11.1.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - aws-crt-cpp >=0.40.1,<0.40.2.0a0 + size: 416399 + timestamp: 1784113232967 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.833-hc7390e0_9.conda + sha256: 0c15c036c3b71471eecc58bbe08a68c66fd6a051b548dca7675c7c924ed3972b + md5: c65efa87d532339a090c87093097bf09 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libzlib >=1.3.2,<2.0a0 + - aws-c-event-stream >=0.7.1,<0.7.2.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - libcurl >=8.21.0,<9.0a0 + - aws-crt-cpp >=0.40.1,<0.40.2.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - aws-sdk-cpp >=1.11.833,<1.11.834.0a0 + size: 3394321 + timestamp: 1784137257627 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.16.3-h206d751_0.conda + sha256: fffc66e9be8806a92b314e27129c42b1298ea7065028c9e5d175dacb07829261 + md5: 0cabc152dc8896c3a4c4aebf2b308627 + depends: + - __glibc >=2.17,<3.0.a0 + - libcurl >=8.19.0,<9.0a0 + - libgcc >=14 + - libstdcxx >=14 + - openssl >=3.5.5,<4.0a0 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + size: 349147 + timestamp: 1775238757304 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.13.3-h71f81a8_2.conda + sha256: ebca774f1ebaa24c150730603b418b33fc7862811a16d097716b1a29f34798c5 + md5: f4fc754ec17176046988c46a54962a8c + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - libgcc >=14 + - libstdcxx >=14 + - openssl >=3.5.7,<4.0a0 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - azure-identity-cpp >=1.13.3,<1.13.4.0a0 + size: 250737 + timestamp: 1781268163278 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.18.0-h74b55db_1.conda + sha256: 04bb27dbf1d426b4447b28c3dc92ec01c807fa784eea86ffa1f8c2bd9b9e8076 + md5: 43151a3b0a8e037747d79a82dff9f967 + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - azure-storage-common-cpp >=12.14.0,<12.14.1.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - azure-storage-blobs-cpp >=12.18.0,<12.18.1.0a0 + size: 589716 + timestamp: 1781283775801 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.14.0-hf596fc9_1.conda + sha256: 8a806f9852d280926d7613df548889b49e2a1c5d836385bcb2cedb24e7b08c64 + md5: 7896f4a6ee78538e2d0261e3b36dfa69 + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libxml2 + - libxml2-16 >=2.14.6 + - openssl >=3.5.7,<4.0a0 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - azure-storage-common-cpp >=12.14.0,<12.14.1.0a0 + size: 159394 + timestamp: 1781268171097 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.16.0-h1f05bef_1.conda + sha256: 13e2e6eb942f65bf81e9089bf6b5926534d9245c781288c5270beb3a25c9156b + md5: 70972c9cb0893d6499dd4118415a966b + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - azure-storage-blobs-cpp >=12.18.0,<12.18.1.0a0 + - azure-storage-common-cpp >=12.14.0,<12.14.1.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - azure-storage-files-datalake-cpp >=12.16.0,<12.16.1.0a0 + size: 308699 + timestamp: 1781538835962 +- 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/c-ares-1.34.8-h280c20c_1.conda + sha256: 5139b6afbfaca91c47104ba9a6a40f81211e1c9200e96b73ce3a56f0ca1902f4 + md5: 2cef891b791040aab83e218c7d137679 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - c-ares-static <0a0 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - c-ares >=1.34.8,<2.0a0 + size: 226755 + timestamp: 1786116641939 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-he90730b_1.conda + sha256: 06525fa0c4e4f56e771a3b986d0fdf0f0fc5a3270830ee47e127a5105bde1b9a + md5: bb6c4808bfa69d6f7f6b07e5846ced37 + depends: + - __glibc >=2.17,<3.0.a0 + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - icu >=78.1,<79.0a0 + - libexpat >=2.7.3,<3.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libgcc >=14 + - libglib >=2.86.3,<3.0a0 + - libpng >=1.6.53,<1.7.0a0 + - libstdcxx >=14 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - pixman >=0.46.4,<1.0a0 + - xorg-libice >=1.1.2,<2.0a0 + - xorg-libsm >=1.2.6,<2.0a0 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxrender >=0.9.12,<0.10.0a0 + license: LGPL-2.1-only or MPL-1.1 + purls: [] + run_exports: + weak: + - cairo >=1.18.4,<2.0a0 + size: 989514 + timestamp: 1766415934926 +- conda: https://conda.anaconda.org/conda-forge/linux-64/coreutils-9.11-h280c20c_0.conda + sha256: 6b19fb6c45302bdd6c97c5ac219e14bcbb7d9055c758e79d8a2577e705a933f0 + md5: c63e2851f3d99a32f4b6991d0460dd25 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: GPL-3.0-or-later + license_family: GPL + purls: [] + run_exports: {} + size: 3432565 + timestamp: 1786403825342 +- conda: https://conda.anaconda.org/conda-forge/linux-64/curl-8.21.0-heca4667_4.conda + sha256: 8f14ff477bf2cdf3cd4ff0ab2486b3e6490350da650e67be6c6a0b8a18994a90 + md5: a91f709770cddb5e7a027ab000c441a5 + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.22.2,<1.23.0a0 + - libcurl 8.21.0 heca4667_4 + - libgcc >=14 + - libpsl >=0.23.0,<0.24.0a0 - libssh2 >=1.11.1,<2.0a0 - libzlib >=1.3.2,<2.0a0 - openssl >=3.5.7,<4.0a0 - zstd >=1.5.7,<1.6.0a0 license: curl license_family: MIT + purls: [] run_exports: {} size: 195067 timestamp: 1785500114646 -- conda: https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-1.11.0-hfcd1e18_0.conda - sha256: 3fcc97ae3e89c150401a50a4de58794ffc67b1ed0e1851468fcc376980201e25 - md5: 5da8c935dca9186673987f79cef0b2a5 - depends: - - c-compiler 1.11.0 h4d9bdce_0 - - gxx - - gxx_linux-64 14.* - license: BSD-3-Clause - license_family: BSD - run_exports: {} - size: 6635 - timestamp: 1753098722177 - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h27c8c51_0.conda sha256: 05b57e2f5aaf3adcccc80653c9795d29974f24dcf9daa40c98b254c99b726227 md5: bc8843ae1a655600014bb7a09d7b8b89 @@ -1205,6 +1147,7 @@ packages: - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - fontconfig >=2.18.3,<3.0a0 @@ -1218,125 +1161,28 @@ packages: - libfreetype 2.14.3 ha770c72_1 - libfreetype6 2.14.3 h73754d4_1 license: GPL-2.0-only OR FTL + purls: [] run_exports: weak: - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 size: 174748 timestamp: 1785641176027 -- conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_1.conda - sha256: 4846a3ca0402f3fe33ad84ed50ab213c6aafde4a0faef3c5002f6bf753e21671 - md5: 1cd10eda5692519d01bb20e086e214c9 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.3.1-h54a6638_0.conda + sha256: f7026323ac769ef432c747a6c22abd39c583d0d4f6985a7100fb371e2167f214 + md5: 04be12e7dd050aaf364ece2393988e85 depends: - - __glibc >=2.17,<3.0.a0 - libgcc >=14 - license: LGPL-2.1-or-later - run_exports: - weak: - - fribidi >=1.0.16,<2.0a0 - size: 61782 - timestamp: 1785912528684 -- conda: https://conda.anaconda.org/conda-forge/linux-64/gcc-14.4.0-hc6a0c74_1.conda - sha256: e8bedab429b82df70562525aeaaab9c256a8713aecd4fab696310807e10a7460 - md5: fcd2b41f019953fb6e89efaef2905ce2 - depends: - - conda-gcc-specs - - gcc_impl_linux-64 14.4.0 h9711763_1 - license: BSD-3-Clause - license_family: BSD - run_exports: {} - size: 29190 - timestamp: 1785374790020 -- conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_impl_linux-64-14.4.0-h9711763_1.conda - sha256: 7945f55ef3cbd81132dd42258295c906ef5b8df20936f6d0b9bdaa741a0e03c7 - md5: ccaabac23b7229f71df5aa354121c41f - depends: - - binutils_impl_linux-64 >=2.46.1 - - libgcc >=14.4.0 - - libgcc-devel_linux-64 14.4.0 hd9a9cd0_101 - - libgomp >=14.4.0 - - libsanitizer 14.4.0 hf39dbba_1 - - libstdcxx >=14.4.0 - - libstdcxx-devel_linux-64 14.4.0 ha5b54cb_101 - - sysroot_linux-64 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - run_exports: {} - size: 78573599 - timestamp: 1785374597260 -- conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_impl_linux-64-16.1.0-h5fcb69b_1.conda - sha256: 00c87015522248adb5565a1b8f977cfe927831dd7ef0cb0a5d13f896844af719 - md5: 419982d8913246db404319048e062d3e - depends: - - binutils_impl_linux-64 >=2.46.1 - - libgcc >=16.1.0 - - libgcc-devel_linux-64 16.1.0 h59071f9_101 - - libgomp >=16.1.0 - - libsanitizer 16.1.0 hf2715c6_1 - - libstdcxx >=16.1.0 - - libstdcxx-devel_linux-64 16.1.0 h41cdd0d_101 - - sysroot_linux-64 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - run_exports: {} - size: 85161422 - timestamp: 1785375529345 -- conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-14.4.0-hc469d41_0.conda - sha256: f529b3afdb6ed570618387d363c79cf5f1b2e638e76a5fa11b93e64b6b6d6d06 - md5: e5775fadf3bde7e53d0750c540586afa - depends: - - gcc_impl_linux-64 14.4.0.* - - binutils_linux-64 - - sysroot_linux-64 - license: BSD-3-Clause - license_family: BSD - run_exports: - strong: - - libgcc >=14 - size: 29731 - timestamp: 1785386556095 -- conda: https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-16.1.0-h5fd2508_0.conda - sha256: 22d2b2c0386fda70971c87afd4926cb20ba1a247421f5be617c43512570fa4f7 - md5: 15b9577e4be98443deb42e88e9c44656 - depends: - - gcc_impl_linux-64 16.1.0.* - - binutils_linux-64 - - sysroot_linux-64 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: - strong: - - libgcc >=16 - size: 29720 - timestamp: 1785386616206 -- conda: https://conda.anaconda.org/conda-forge/linux-64/gfortran_impl_linux-64-14.4.0-he75539a_1.conda - sha256: 5afd92c95741883e7df3a7bc38509480b6a9d80766aef58f80e716c24f59b6a7 - md5: 39093c2df9b13a3c6c9c0c9cb3ad4154 - depends: - - gcc_impl_linux-64 >=14.4.0 - - libgcc >=14.4.0 - - libgfortran5 >=14.4.0 - - libstdcxx >=14.4.0 - - sysroot_linux-64 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - run_exports: {} - size: 19142097 - timestamp: 1785374722378 -- conda: https://conda.anaconda.org/conda-forge/linux-64/gfortran_impl_linux-64-16.1.0-h08e1a1a_1.conda - sha256: e4e2e3ae7775b9f6fabcb8b0deb675c2fe5ed036d67713b21759f7f937488153 - md5: dad331198527e6b37015a35221daf18c - depends: - - gcc_impl_linux-64 >=16.1.0 - - libgcc >=16.1.0 - - libgfortran5 >=16.1.0 - - libstdcxx >=16.1.0 - - sysroot_linux-64 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - run_exports: {} - size: 21030256 - timestamp: 1785375669882 + weak: + - gflags >=2.3.1,<2.4.0a0 + size: 130991 + timestamp: 1785044715896 - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-ha257d8a_1.conda sha256: 3611c5d605cd56017704e6c01c7bd849e74a656a546d4c49d655fde49ddfeecc md5: bd7c3a8586ed0101f094962100d30a63 @@ -1345,11 +1191,44 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - giflib >=5.2.2,<5.3.0a0 size: 77403 timestamp: 1784694210187 +- conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hb7133d2_1.conda + sha256: f24feeda3aeec3a2cbb9fe2e0fabc4785372e70b6cc838c96023e08e17f4bfa0 + md5: 6941f96b8a8056677d79b4f5a2c4aaca + depends: + - __glibc >=2.17,<3.0.a0 + - gflags >=2.3.0,<2.4.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - glog >=0.7.1,<0.8.0a0 + size: 149031 + timestamp: 1784094370091 +- conda: https://conda.anaconda.org/conda-forge/linux-64/google-crc32c-1.8.0-py312hda0ef95_2.conda + sha256: 938dfd35b440e3c843d941289ae0ac94a7d9327aa479efe6e2e0ee33ee82863b + md5: c5d2e1c00832cf3d97018829efd3d480 + depends: + - __glibc >=2.17,<3.0.a0 + - libcrc32c >=1.1.2,<1.2.0a0 + - libgcc >=15 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/google-crc32c?source=compressed-mapping + run_exports: {} + size: 29520 + timestamp: 1788537554663 - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda sha256: 7fa3b6a9c081fa3e545573152a788d061a0a0ba57df7251cc0f4f75225fc93e7 md5: f9fe2984587fa8235a6af6004760cd18 @@ -1359,94 +1238,30 @@ packages: - libgcc >=14 license: LGPL-2.0-or-later license_family: LGPL + purls: [] run_exports: weak: - graphite2 >=1.3.15,<2.0a0 size: 102835 timestamp: 1786118485753 -- conda: https://conda.anaconda.org/conda-forge/linux-64/gsl-2.7-he838d99_0.tar.bz2 - sha256: 132a918b676dd1f533d7c6f95e567abf7081a6ea3251c3280de35ef600e0da87 - md5: fec079ba39c9cca093bf4c00001825de - depends: - - libblas >=3.8.0,<4.0a0 - - libcblas >=3.8.0,<4.0a0 - - libgcc-ng >=9.3.0 - license: GPL-3.0-or-later - license_family: GPL - run_exports: - weak: - - gsl >=2.7,<2.8.0a0 - size: 3376423 - timestamp: 1626369596591 -- conda: https://conda.anaconda.org/conda-forge/linux-64/gxx-14.4.0-hd47ba16_1.conda - sha256: defd5e344fd893fa35624777d547d6444cb25072a60f3ec43e4c114af6182156 - md5: 32f70241cdcf09432f269877506c8f0c +- conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.16.0-nompi_py312hfaa9938_104.conda + sha256: 64e1d13bc0ec29fa416aeefac77283eea349f3ed2acfcfc513f0cacd30fbcf6d + md5: 9ca94eb30bd0938240f32b6e6398b862 depends: - - gcc 14.4.0 hc6a0c74_1 - - gxx_impl_linux-64 14.4.0 hc436dd5_1 + - __glibc >=2.17,<3.0.a0 + - cached-property + - hdf5 >=1.14.6,<1.14.7.0a0 + - libgcc >=15 + - numpy >=1.25,<3 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/h5py?source=compressed-mapping run_exports: {} - size: 28527 - timestamp: 1785374804415 -- conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-14.4.0-hc436dd5_1.conda - sha256: 7531870fff6193e6501c11e777772836f2c654c3bc663eee1c58336695ef0e1d - md5: 6d4bd0c4d4c5d9fc7653db363b1c4db4 - depends: - - gcc_impl_linux-64 14.4.0 h9711763_1 - - libstdcxx-devel_linux-64 14.4.0 ha5b54cb_101 - - sysroot_linux-64 - - tzdata - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - run_exports: {} - size: 15871779 - timestamp: 1785374755447 -- conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-16.1.0-he33a5f8_1.conda - sha256: 4b7e7a082fab18a58409b05c2611b8edb4aeb06ee07be380a73da5de911da2ba - md5: aaeab97072d79e7945182dc7d4e1a035 - depends: - - gcc_impl_linux-64 16.1.0 h5fcb69b_1 - - libstdcxx-devel_linux-64 16.1.0 h41cdd0d_101 - - sysroot_linux-64 - - tzdata - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - run_exports: {} - size: 16633585 - timestamp: 1785375706410 -- conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-14.4.0-h82679ac_0.conda - sha256: 531b55ef516bd6e81c009a70036400b1b9ba2db6a35540c979b90f72e7187395 - md5: 5a2cae3743a93532d433a693062ae7f2 - depends: - - gxx_impl_linux-64 14.4.0.* - - gcc_linux-64 ==14.4.0 hc469d41_0 - - binutils_linux-64 - - sysroot_linux-64 - license: BSD-3-Clause - license_family: BSD - run_exports: - strong: - - libstdcxx >=14 - - libgcc >=14 - size: 28102 - timestamp: 1785386556095 -- conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-16.1.0-h5525346_0.conda - sha256: c8c0b721dadcc8d48d2a5a9ee56add4b46ce5427adbf6ff685e0f75fabd52cbd - md5: 4521cfa739a42179511b351566374c6e - depends: - - gxx_impl_linux-64 16.1.0.* - - gcc_linux-64 ==16.1.0 h5fd2508_0 - - binutils_linux-64 - - sysroot_linux-64 - license: BSD-3-Clause - license_family: BSD - run_exports: - strong: - - libstdcxx >=16 - - libgcc >=16 - size: 28116 - timestamp: 1785386616206 + size: 1353159 + timestamp: 1787108753979 - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.3.0-ha770c72_0.conda sha256: 511813aaad42ed2494efc77452738fed7734985e069efc08c279a701b1708ba0 md5: 7f42f814e1306f83e4cac267baa9acab @@ -1454,19 +1269,42 @@ packages: - libharfbuzz-devel 14.3.0 h17a8019_0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libharfbuzz >=14.3.0 size: 11045 timestamp: 1785770087614 -- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda - sha256: 9f07834f0c546ab14d885ce0366285f61f44e326c0edd1fc63b8294e113ae432 - md5: 72a381cbad04f24b1c2a43ef707f45b4 +- conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h19486de_110.conda + sha256: ac57ce3abbda2a82d6192bc36fbd7fe96e867d84c999ef81a3da034dfd01a995 + md5: 9c6e11a83468e1d5decae516077a73fb depends: - __glibc >=2.17,<3.0.a0 - - libstdcxx >=14 + - libaec >=1.1.5,<2.0a0 + - libcurl >=8.20.0,<9.0a0 + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.3.0 + - libstdcxx >=14 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.6,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - hdf5 >=1.14.6,<1.14.7.0a0 + size: 3721555 + timestamp: 1780581675871 +- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda + sha256: 9f07834f0c546ab14d885ce0366285f61f44e326c0edd1fc63b8294e113ae432 + md5: 72a381cbad04f24b1c2a43ef707f45b4 + depends: + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 - libgcc >=14 license: MIT + purls: [] run_exports: weak: - icu >=78.3,<79.0a0 @@ -1479,6 +1317,7 @@ packages: - __glibc >=2.17,<3.0.a0 - libgcc >=13 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - keyutils >=1.6.3,<2.0a0 @@ -1497,6 +1336,7 @@ packages: - openssl >=3.5.7,<4.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - krb5 >=1.22.2,<1.23.0a0 @@ -1512,6 +1352,7 @@ packages: - libtiff >=4.7.1,<4.8.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - lcms2 >=2.19.1,<3.0a0 @@ -1527,6 +1368,7 @@ packages: - binutils_impl_linux-64 2.46.1 license: GPL-3.0-only license_family: GPL + purls: [] run_exports: {} size: 745303 timestamp: 1784214507189 @@ -1539,11 +1381,31 @@ packages: - libstdcxx >=14 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - lerc >=4.2.0,<5.0a0 size: 271158 timestamp: 1785036167977 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260526.0-cxx17_h0dc7533_2.conda + sha256: 7bb3d495411b7059e85225aae500d84e7846a4bb02ebd77e4450200b20c180f0 + md5: 6983bfe8e09992014b9cf393769c7a84 + 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: 1436888 + timestamp: 1787217011773 - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda sha256: 822e4ae421a7e9c04e841323526321185f6659222325e1a9aedec811c686e688 md5: 86f7414544ae606282352fa1e116b41f @@ -1553,11 +1415,133 @@ packages: - libstdcxx >=14 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - libaec >=1.1.5,<2.0a0 size: 36544 timestamp: 1769221884824 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-25.0.0-hcc2f0d9_4_cpu.conda + build_number: 4 + sha256: b3b92d94fa20d1d195de702c4b61cd0c3438d26672cbaf6ebbf021114a840a77 + md5: c344b5608c65a09b341f2e784efb4422 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-crt-cpp >=0.40.1,<0.40.2.0a0 + - aws-sdk-cpp >=1.11.833,<1.11.834.0a0 + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - azure-identity-cpp >=1.13.3,<1.13.4.0a0 + - azure-storage-blobs-cpp >=12.18.0,<12.18.1.0a0 + - azure-storage-files-datalake-cpp >=12.16.0,<12.16.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libgcc >=14 + - libgoogle-cloud >=3.8.0,<3.9.0a0 + - libgoogle-cloud-storage >=3.8.0,<3.9.0a0 + - libopentelemetry-cpp >=1.27.0,<1.28.0a0 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libstdcxx >=14 + - libzlib >=1.3.2,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.3.1,<2.3.2.0a0 + - snappy >=1.2.2,<1.3.0a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - apache-arrow-proc =*=cpu + - arrow-cpp <0.0a0 + - parquet-cpp <0.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - libarrow >=25.0.0,<25.1.0a0 + size: 6598422 + timestamp: 1786315080658 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-25.0.0-h635bf11_4_cpu.conda + build_number: 4 + sha256: 886631b6e32f62e04cc314462c0fbbf9d461cfb6625d2384ae0f36b5a8a72890 + md5: 64fad2fca810b34f5bbc9738a6fb298f + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 25.0.0 hcc2f0d9_4_cpu + - libarrow-compute 25.0.0 h53684a4_4_cpu + - libgcc >=14 + - libstdcxx >=14 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - libarrow-acero >=25.0.0,<25.1.0a0 + size: 590543 + timestamp: 1786315249993 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-compute-25.0.0-h53684a4_4_cpu.conda + build_number: 4 + sha256: ad642502977edceeb4ca385e42f085fb4391c409d8d098866bc4a5e3fda7a04a + md5: 05f6b378ae80d2d06e35dc794412da3a + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 25.0.0 hcc2f0d9_4_cpu + - libgcc >=14 + - libre2-11 >=2025.11.5 + - libstdcxx >=14 + - libutf8proc >=2.11.3,<2.12.0a0 + - re2 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - libarrow-compute >=25.0.0,<25.1.0a0 + size: 2974934 + timestamp: 1786315141259 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-25.0.0-h635bf11_4_cpu.conda + build_number: 4 + sha256: 6715d36e6aaf00986876e67e97ae8e9b60afb2a7f52ab79b89543c37cd914e28 + md5: 26f7b996f0674217f78d5c6432445b71 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 25.0.0 hcc2f0d9_4_cpu + - libarrow-acero 25.0.0 h635bf11_4_cpu + - libarrow-compute 25.0.0 h53684a4_4_cpu + - libgcc >=14 + - libparquet 25.0.0 h7376487_4_cpu + - libstdcxx >=14 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - libarrow-dataset >=25.0.0,<25.1.0a0 + size: 591131 + timestamp: 1786315326056 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-25.0.0-h66fbfdd_4_cpu.conda + build_number: 4 + sha256: 7478e785ad4b7aee72e685921d849b3fb11b0c477dfea76d9e861ee5056b35a6 + md5: 287c5915a8b8bf79613840e0df9dc7b7 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libarrow 25.0.0 hcc2f0d9_4_cpu + - libarrow-acero 25.0.0 h635bf11_4_cpu + - libarrow-dataset 25.0.0 h635bf11_4_cpu + - libgcc >=14 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libstdcxx >=14 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - libarrow-substrait >=25.0.0,<25.1.0a0 + size: 500255 + timestamp: 1786315353425 - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-9_h4a7cf45_openblas.conda build_number: 9 sha256: 39c7b3c5427b435c9c059ede9da61d46d42574e5b846ad37fdc3af4a5eab1e48 @@ -1573,11 +1557,56 @@ packages: - mkl <2027 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libblas >=3.11.0,<4.0a0 size: 18033 timestamp: 1786059035239 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h39a168f_4.conda + sha256: c4e9854c585f3ee1785f651287ecdf8c28f30a2ea1b159a513005f8a0f6453a3 + md5: df210655e30a05e3b069c91b7aaddd2d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - libbrotlicommon >=1.2.0,<1.3.0a0 + size: 80561 + timestamp: 1788480364653 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-ha411449_4.conda + sha256: d5ed24da3e583a92227fe77280978b7933f07a39a2b60156c5ad95e5c76733fa + md5: 0cfd601eb03cca403dcc248844787486 + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.2.0 h39a168f_4 + - libgcc >=15 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - libbrotlidec >=1.2.0,<1.3.0a0 + size: 34739 + timestamp: 1788480374261 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-h018ffa1_4.conda + sha256: b9f3cb510337655485c2e79ce5f7dd468991b19ad29723ffa1175a0aed1b69ae + md5: 4136f6e4ef4835cc7df39a707cbd511c + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.2.0 h39a168f_4 + - libgcc >=15 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - libbrotlienc >=1.2.0,<1.3.0a0 + size: 298134 + timestamp: 1788480383223 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-9_h0358290_openblas.conda build_number: 9 sha256: 4c532a70ea9aeff2fa1aabaa4828ebc00c2ed12b22aa8ba19da5302b882fc82b @@ -1590,11 +1619,26 @@ packages: - liblapacke 3.11.0 9*_openblas license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libcblas >=3.11.0,<4.0a0 size: 17998 timestamp: 1786059041397 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 + sha256: fd1d153962764433fe6233f34a72cdeed5dcf8a883a85769e8295ce940b5b0c5 + md5: c965a5aa0d5c1c37ffc62dff36e28400 + depends: + - libgcc-ng >=9.4.0 + - libstdcxx-ng >=9.4.0 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - libcrc32c >=1.1.2,<1.2.0a0 + size: 20440 + timestamp: 1633683576494 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda sha256: 205c4f19550f3647832ec44e35e6d93c8c206782bdd620c1d7cf66237580ff9c md5: 49c553b47ff679a6a1e9fc80b9c5a2d4 @@ -1606,6 +1650,7 @@ packages: - libzlib >=1.3.1,<2.0a0 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - libcups >=2.3.3,<2.4.0a0 @@ -1626,6 +1671,7 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: curl license_family: MIT + purls: [] run_exports: weak: - libcurl >=8.21.0,<9.0a0 @@ -1639,6 +1685,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - libdeflate >=1.25,<1.26.0a0 @@ -1654,6 +1701,7 @@ packages: - ncurses >=6.5,<7.0a0 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - libedit >=3.1.20250104,<3.2.0a0 @@ -1667,11 +1715,27 @@ packages: - __glibc >=2.17,<3.0.a0 license: BSD-2-Clause OR GPL-2.0-or-later license_family: GPL + purls: [] run_exports: weak: - libev >=4.33,<4.34.0a0 size: 43220 timestamp: 1785917328200 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-h5348a74_2.conda + sha256: f874779772878f3b509c607de9089c282f71232fde4cac876fc660d29670f28d + md5: 2e699c6e151045b2cfea34b53ed69439 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - openssl >=3.5.8,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - libevent >=2.1.12,<2.1.13.0a0 + size: 431007 + timestamp: 1788870247867 - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_1.conda sha256: 16feffd9ddbbe5b718515d38ee376c685ba95491cd901244e24671d20b952a77 md5: b24d3c612f71e7aa74158d92106318b2 @@ -1682,6 +1746,7 @@ packages: - expat 2.8.1.* license: MIT license_family: MIT + purls: [] run_exports: {} size: 77856 timestamp: 1781203599810 @@ -1693,6 +1758,7 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: weak: - libffi >=3.7.0,<3.8.0a0 @@ -1704,6 +1770,7 @@ packages: depends: - libfreetype6 >=2.14.3 license: GPL-2.0-only OR FTL + purls: [] run_exports: {} size: 8419 timestamp: 1785641173212 @@ -1718,6 +1785,7 @@ packages: constrains: - freetype >=2.14.3 license: GPL-2.0-only OR FTL + purls: [] run_exports: {} size: 386042 timestamp: 1785641172605 @@ -1732,6 +1800,7 @@ packages: - libgcc-ng ==16.1.0=*_1 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: {} size: 1057877 timestamp: 1785375436766 @@ -1742,6 +1811,7 @@ packages: - libgcc 16.1.0 ha9f2e26_1 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: strong: - libgcc @@ -1756,6 +1826,7 @@ packages: - libgfortran-ng ==16.1.0=*_1 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: {} size: 28134 timestamp: 1785375470055 @@ -1769,6 +1840,7 @@ packages: - libgfortran 16.1.0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: {} size: 2538696 timestamp: 1785375448623 @@ -1785,6 +1857,7 @@ packages: constrains: - glib >2.66 license: LGPL-2.1-or-later + purls: [] run_exports: weak: - libglib >=2.88.3,<3.0a0 @@ -1797,11 +1870,82 @@ packages: - __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: 640415 timestamp: 1785375373755 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-3.8.0-hbc29df5_0.conda + sha256: 46126b858d173d6808262e39e3a8aa39946d39aebb5ffb720984dc44f74feafe + md5: 60ff8935e16bf2fd302289ec4f129df8 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libcurl >=8.21.0,<9.0a0 + - libgcc >=14 + - libgrpc >=1.82.1,<1.83.0a0 + - libopentelemetry-cpp >=1.27.0,<1.28.0a0 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libstdcxx >=14 + - openssl >=3.5.7,<4.0a0 + constrains: + - libgoogle-cloud 3.8.0 *_0 + license: Apache-2.0 + license_family: Apache + purls: [] + run_exports: + weak: + - libgoogle-cloud >=3.8.0,<3.9.0a0 + size: 2663465 + timestamp: 1786226759530 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-3.8.0-hdbdcf42_0.conda + sha256: 03437ce50129f9ca82a0400b7722cd2dd3ee3bfa50dcb557657f55a7de76ab6f + md5: 84a8d5a661792da2127ab908b5ae83ef + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libgcc >=14 + - libgoogle-cloud 3.8.0 hbc29df5_0 + - libstdcxx >=14 + - libzlib >=1.3.2,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + purls: [] + run_exports: + weak: + - libgoogle-cloud-storage >=3.8.0,<3.9.0a0 + size: 810573 + timestamp: 1786226867433 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.82.1-h792040b_0.conda + sha256: 1e657c1b802c111178bc1845fe06488db5f69a85e34e970ce0989810aa562d45 + md5: aa4571fc3bcf18ad12370429aa991223 + depends: + - __glibc >=2.17,<3.0.a0 + - c-ares >=1.34.6,<2.0a0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libgcc >=14 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libre2-11 >=2025.11.5 + - libstdcxx >=14 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.7,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.82.1 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - libgrpc >=1.82.1,<1.83.0a0 + size: 6957755 + timestamp: 1783588701960 - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.3.0-h17a8019_0.conda sha256: fb72d6f7e90d4927cb53a4e13592559ce74b65052323d5d6dd12499b026c680e md5: f33637ebded146eafa6b2197c8f597a6 @@ -1819,6 +1963,7 @@ packages: - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: {} size: 1333436 timestamp: 1785770053613 @@ -1841,6 +1986,7 @@ packages: - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libharfbuzz >=14.3.0 @@ -1853,6 +1999,7 @@ packages: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: LGPL-2.1-only + purls: [] run_exports: weak: - libiconv >=1.18,<2.0a0 @@ -1867,6 +2014,7 @@ packages: constrains: - jpeg <0.0.0a license: IJG AND BSD-3-Clause AND Zlib + purls: [] run_exports: weak: - libjpeg-turbo >=3.2.0,<4.0a0 @@ -1884,6 +2032,7 @@ packages: - liblapacke 3.11.0 9*_openblas license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - liblapack >=3.11.0,<3.12.0a0 @@ -1898,6 +2047,7 @@ packages: constrains: - xz 5.8.3.* license: 0BSD + purls: [] run_exports: weak: - liblzma >=5.8.3,<6.0a0 @@ -1917,11 +2067,26 @@ packages: - openssl >=3.5.5,<4.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libnghttp2 >=1.68.1,<2.0a0 size: 663344 timestamp: 1773854035739 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda + sha256: 927fe72b054277cde6cb82597d0fcf6baf127dcbce2e0a9d8925a68f1265eef5 + md5: d864d34357c3b65a4b731f78c0801dc4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: LGPL-2.1-only + license_family: GPL + purls: [] + run_exports: + weak: + - libnsl >=2.0.1,<2.1.0a0 + size: 33731 + timestamp: 1750274110928 - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.34-pthreads_h94d23a6_0.conda sha256: 23392fc4f4e5ba230fcd1ef825878ba5ca7ee4f6259fac0cbb13299134b7bf7a md5: c282d68f272927612462b5d626838ef1 @@ -1934,11 +2099,63 @@ packages: - 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: 5952629 timestamp: 1784287497473 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-1.27.0-h3133023_1.conda + sha256: 8f9281133322e9eb0bd4a5b11330db1c14f3b40c409639dd3805251151ca52e2 + md5: f749f223bede1bac7724e49d686ac598 + depends: + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libcurl >=8.21.0,<9.0a0 + - libgrpc >=1.82.0,<1.83.0a0 + - libopentelemetry-cpp-headers 1.27.0 ha770c72_1 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libzlib >=1.3.2,<2.0a0 + - nlohmann_json + - prometheus-cpp >=1.3.0,<1.4.0a0 + constrains: + - cpp-opentelemetry-sdk =1.27.0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - libopentelemetry-cpp >=1.27.0,<1.28.0a0 + size: 948783 + timestamp: 1783133058845 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-headers-1.27.0-ha770c72_1.conda + sha256: 31349543e3c59c64f17e24494939a22b0e1d611bdb19d98d115775dba423cbf7 + md5: 6cc68cbbe88746be8beaf027d6c64ad3 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: {} + size: 394726 + timestamp: 1783133038109 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-25.0.0-h7376487_4_cpu.conda + build_number: 4 + sha256: dba948e05d62de3833caa065c1b7de960f921bb0a8a7687b8593df95d2d925a9 + md5: d736a4f89a3b63f9970152923e4c3016 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 25.0.0 hcc2f0d9_4_cpu + - libgcc >=14 + - libstdcxx >=14 + - libthrift >=0.22.0,<0.22.1.0a0 + - openssl >=3.5.7,<4.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - libparquet >=25.0.0,<25.1.0a0 + size: 1412634 + timestamp: 1786315223543 - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda sha256: 377cfe037f3eeb3b1bf3ad333f724a64d32f315ee1958581fc671891d63d3f89 md5: eba48a68a1a2b9d3c0d9511548db85db @@ -1947,11 +2164,30 @@ packages: - libgcc >=14 - libzlib >=1.3.2,<2.0a0 license: zlib-acknowledgement + purls: [] run_exports: weak: - libpng >=1.6.58,<1.7.0a0 size: 317729 timestamp: 1776315175087 +- 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/libpsl-0.23.1-hf670292_0.conda sha256: 2e6405feb59e3f40a5a4641dfa73afda158046893aa73d478e23415e18997ece md5: c8217f5bdd5b018087bdea081912cda5 @@ -1962,39 +2198,59 @@ packages: - icu >=78.3,<79.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libpsl >=0.23.1,<0.24.0a0 size: 72505 timestamp: 1786443494718 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-14.4.0-hf39dbba_1.conda - sha256: 8e58b5e35c2d75d4f6a01d5650c5d672b8a96608a59af3a261050ccefe33702b - md5: 07c5c310a267103ab4dac904a933a4a4 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.12.14-h0c77377_3_cpython.conda + build_number: 3 + sha256: 6be16a4906d83eb8e9e04375d524f339f426a847cffd294f1ceb0611988dc17a + md5: d247b7632f09324c11f24b5270361385 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14.4.0 - - libstdcxx >=14.4.0 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL + - libgcc >=15 + - libstdcxx >=15 + license: Python-2.0 + purls: [] + run_exports: {} + size: 8770625 + timestamp: 1788392466381 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h60473fc_2.conda + sha256: 43132eb45a569b1f63199b0e7d5874d94227e9be950b327a323e7dcc1f8cd58c + md5: ee5c400d37b79db5d32a69ed1a79fc0b + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libgcc >=14 + - libstdcxx >=14 + constrains: + - re2 2025.11.05.* + license: BSD-3-Clause + license_family: BSD + purls: [] run_exports: weak: - - libsanitizer 14.4.0 - size: 7523825 - timestamp: 1785374558337 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-16.1.0-hf2715c6_1.conda - sha256: 85662ecadd3961bc96cbcc38dbc024a768cc35932c8440677ed028ea6322c36c - md5: abd77210925872ee084672cf5be1d491 + - libre2-11 >=2025.11.5 + size: 210598 + timestamp: 1781312565737 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.4-h13e7031_1.conda + sha256: f20d70da54e5b31dd4a51fb1efeaafafd5ab6dd8d7ac9d1438eaa2526ac4ed3d + md5: e72bbec309c2b0f37823ee7d4fabfcd3 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=16.1.0 - - libstdcxx >=16.1.0 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL + - icu >=78.3,<79.0a0 + - libgcc >=15 + - libzlib >=1.3.2,<2.0a0 + license: blessing + purls: [] run_exports: weak: - - libsanitizer 16.1.0 - size: 7780843 - timestamp: 1785375481116 + - libsqlite >=3.53.4,<4.0a0 + size: 974348 + timestamp: 1787051145557 - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda sha256: fa39bfd69228a13e553bd24601332b7cfeb30ca11a3ca50bb028108fe90a7661 md5: eecce068c7e4eddeb169591baac20ac4 @@ -2005,6 +2261,7 @@ packages: - openssl >=3.5.0,<4.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libssh2 >=1.11.1,<2.0a0 @@ -2020,9 +2277,41 @@ packages: - libstdcxx-ng ==16.1.0=*_1 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: {} size: 6631744 timestamp: 1785375462643 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.1.0-hdf11a46_1.conda + sha256: 2876ca4463d1b394eb969ce4a84d1620aa63fb8202a6397837d1e45ec76c1208 + md5: c94f06123272d8e129d4acf3a25ffb35 + depends: + - libstdcxx 16.1.0 h934c35e_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + run_exports: + strong: + - libstdcxx + size: 28253 + timestamp: 1785375500257 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.22.0-h7d032f7_2.conda + sha256: af6025aa4a4fc3f4e71334000d2739d927e2f678607b109ec630cc17d716918a + md5: b6e326fbe1e3948da50ec29cee0380db + depends: + - __glibc >=2.17,<3.0.a0 + - libevent >=2.1.12,<2.1.13.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.6,<4.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - libthrift >=0.22.0,<0.22.1.0a0 + size: 423861 + timestamp: 1777018957474 - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-h9d88235_0.conda sha256: b31346e1c01ab40a170e91147092ee8fd92b1dee3c66ee47ef025571c879b159 md5: c1fcb4a88bc15a9f77ad8d27d7af1df9 @@ -2038,37 +2327,40 @@ packages: - libzlib >=1.3.2,<2.0a0 - zstd >=1.5.7,<1.6.0a0 license: HPND + purls: [] run_exports: weak: - libtiff >=4.7.2,<4.8.0a0 size: 452337 timestamp: 1783084902636 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.2-h5347b49_0.conda - sha256: 9b1bdce27a7e31f7d241aeecff67a1f3101d52a2b1e33ccc2cdf2613072bf81f - md5: 01bb81d12c957de066ea7362007df642 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.11.3-hfe17d71_0.conda + sha256: ecbf4b7520296ed580498dc66a72508b8a79da5126e1d6dc650a7087171288f9 + md5: 1247168fe4a0b8912e3336bccdbf98a5 depends: - - libgcc >=14 - __glibc >=2.17,<3.0.a0 - license: BSD-3-Clause - license_family: BSD + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] run_exports: weak: - - libuuid >=2.42.2,<3.0a0 - size: 40017 - timestamp: 1781625522462 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.52.1-h280c20c_1.conda - sha256: 67761d0206f84140047a367eaf9befe03a7e157a29ee25aee0047b40801cc6b6 - md5: 01c4ed87826af55996768af6dbc936b4 + - libutf8proc >=2.11.3,<2.12.0a0 + size: 85969 + timestamp: 1768735071295 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.3-hcfc3c73_0.conda + sha256: aa58bbba56644ffd062a4a9b358782c5eb7560ccead2ab8f7c5c6ede0a7d33a6 + md5: 74a0a409d9f4561265d36b789d3f398a depends: - - libgcc >=14 - __glibc >=2.17,<3.0.a0 - license: MIT - license_family: MIT + - libgcc >=15 + license: BSD-3-Clause + license_family: BSD + purls: [] run_exports: weak: - - libuv >=1.52.1,<2.0a0 - size: 420040 - timestamp: 1785914567661 + - libuuid >=2.42.3,<3.0a0 + size: 39998 + timestamp: 1788347719520 - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda sha256: 8415001414f488c85b72b9d8cc2071dfb3981a47bc3c8eb56ef91a57d12eae7f md5: 9332b53d0ea93c5d39e33be03a0c611a @@ -2079,6 +2371,7 @@ packages: - libwebp 1.6.0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libwebp-base >=1.6.0,<2.0a0 @@ -2095,11 +2388,63 @@ packages: - xorg-libxdmcp license: MIT license_family: MIT + purls: [] run_exports: weak: - libxcb >=1.17.0,<2.0a0 size: 395888 timestamp: 1727278577118 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.38-h280c20c_0.conda + sha256: f7e9292dd219a6435bbb1223da9586c3e70d66d169c5a92f08db3f2127df04e9 + md5: f7a7ff5a6ab331e037abd34f379a631d + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: LGPL-2.1-or-later + purls: [] + run_exports: + weak: + - libxcrypt >=4.4.38 + size: 101957 + timestamp: 1785887123445 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.4-hf3af7cc_0.conda + sha256: 9a1685c8d8b8488cd7882a9cd6e673e687211ca763ebbcd1e91b6f594eeadc45 + md5: b7545c57a73a51d72aa6eee5695cf051 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=78.3,<79.0a0 + - libgcc >=15 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.3,<6.0a0 + - libzlib >=1.3.2,<2.0a0 + constrains: + - libxml2 2.15.4 + license: MIT + license_family: MIT + purls: [] + run_exports: {} + size: 569196 + timestamp: 1788635197456 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.4-h7df9aa5_0.conda + sha256: 053d85821ce5e36b7ce03faea5930382f6af695df9e2cd68e5d8d273072d1551 + md5: ea192d1ef556bf1d55de4acb8c0f5b65 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=78.3,<79.0a0 + - libgcc >=15 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.3,<6.0a0 + - libxml2-16 2.15.4 hf3af7cc_0 + - libzlib >=1.3.2,<2.0a0 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - libxml2 + - libxml2-16 >=2.15.4 + size: 47077 + timestamp: 1788635202698 - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda sha256: eb8a0db0aa570124f7d2a93d7c7f596e3390df5e047818d873baad32985fc736 md5: 0de0122d9570a8ab637c6b73db268389 @@ -2109,34 +2454,171 @@ packages: - 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/make-4.4.1-hb03c661_3.conda - sha256: 4c65d2769847778a6d84db6984ac81bcec6f8958d349f1fe57ae040ccc56a801 - md5: b4a198dc40024de2a59c1343e4ae4144 +- conda: https://conda.anaconda.org/conda-forge/linux-64/llvmlite-0.49.0-py312hb491018_3.conda + sha256: 632f565e1683c21dff60908dd077bda933cbc131ccf8cacde6a17a959e7e4c08 + md5: f471bb7144bf1cac276991d74cc662a7 depends: + - python - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - license: GPL-3.0-or-later - license_family: GPL + - libstdcxx >=15 + - libgcc >=15 + - python_abi 3.12.* *_cp312 + - zstd >=1.5.7,<1.6.0a0 + - libzlib >=1.3.2,<2.0a0 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/llvmlite?source=hash-mapping run_exports: {} - size: 510695 - timestamp: 1785879853297 -- conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda - sha256: 5d46557214ed184381dafe835b7c94a474a1c3b307a08a250b1ea4779b44ffb3 - md5: ee6c0cd80a60961a1f48aa3e0b91f986 + size: 40829232 + timestamp: 1788518429834 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-hee9eb32_2.conda + sha256: b8c0e930183e6b7f83cd35ace102f2b8c2f0faae41dc05255dc72f247dfc556a + md5: e38a3253d72ab07d471483f24947e2a2 depends: + - libgcc >=15 + - libstdcxx >=15 - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - license: X11 AND BSD-3-Clause + license: BSD-2-Clause + license_family: BSD + purls: [] run_exports: weak: - - ncurses >=6.6,<7.0a0 - size: 911196 + - lz4-c >=1.10.0,<1.11.0a0 + size: 181812 + timestamp: 1786717122815 +- conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.2.2-py312h9be0db6_2.conda + sha256: 147ba1c9ebaeba03c95c1ac018523978c8a07387e2413799b09b69017e976b58 + md5: e9600e8080bf57f25ad2e9f78564c895 + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=15 + - libgcc >=15 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/msgpack?source=compressed-mapping + run_exports: {} + size: 114404 + timestamp: 1788525103553 +- conda: https://conda.anaconda.org/conda-forge/linux-64/msgspec-0.21.1-py312h5cc1888_1.conda + sha256: 9d3d6b355388a24fcc1ea129bd94e099b0e235d5e2848173c06ea856116b661b + md5: eaf2b621a3900cad16277f1b00a8feef + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/msgspec?source=hash-mapping + run_exports: {} + size: 221115 + timestamp: 1788774834070 +- 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/nlohmann_json-3.12.0-h54a6638_2.conda + sha256: 8f8b554c74b032b3e63a8828fb0ecc7ae4f5c5a6bd0afcf75423d5ff79290cb0 + md5: fe93be7dd9209e6ae673962e3031ff51 + constrains: + - nlohmann_json-abi ==3.12.0 + license: MIT + license_family: MIT + purls: [] + run_exports: {} + size: 136372 + timestamp: 1785920438981 +- conda: https://conda.anaconda.org/conda-forge/linux-64/numba-0.67.0-py312h80505a6_1.conda + sha256: 9d31b33a0a4a547a5b1827270c4741477a13a01b46443848156f137501829a69 + md5: c5a5f2e76e1dbb4372f73aed9779fd5d + depends: + - python + - llvmlite >=0.49.0,<0.50.0a0 + - numpy >=1.22.3,<2.6 + - libstdcxx >=15 + - libgcc >=15 + - _openmp_mutex >=4.5 + - __glibc >=2.17,<3.0.a0 + - python_abi 3.12.* *_cp312 + - numpy >=1.25,<3 + constrains: + - tbb >=2021.6.0 + - libopenblas !=0.3.6 + - cuda-version >=11.2 + - cudatoolkit >=11.2 + - scipy >=1.0 + - cuda-python >=11.6 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/numba?source=hash-mapping + run_exports: {} + size: 6107213 + timestamp: 1787145521097 +- conda: https://conda.anaconda.org/conda-forge/linux-64/numcodecs-0.16.5-py312hf79963d_0.conda + sha256: ae1ec07448a10cdfcaf5df4a818291b0f4a99eb398e02ea5d7fc5d3c76be108f + md5: 86a969eeb489119374ec1d2e863777e6 + depends: + - __glibc >=2.17,<3.0.a0 + - deprecated + - libgcc >=14 + - libstdcxx >=14 + - msgpack-python + - numpy >=1.23,<3 + - numpy >=1.24 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - typing_extensions + license: MIT + license_family: MIT + purls: + - pkg:pypi/numcodecs?source=hash-mapping + run_exports: {} + size: 813229 + timestamp: 1764782491676 +- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.3-py312he827f4e_0.conda + sha256: 5b0e4c7757a2df883c5615a8f64bc4c113c4cd303ae6caa3ada5d397d114ce2a + md5: 5abf073f2cf066f2f3d28229b3cbe94d + depends: + - python + - libstdcxx >=15 + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + - python_abi 3.12.* *_cp312 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - liblapack >=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: 9230861 + timestamp: 1788731992112 - conda: https://conda.anaconda.org/conda-forge/linux-64/openjdk-25.0.2-ha668962_0.conda sha256: 3825a4c84676a8a5cc23b397a2911e4efa4a805daf2af764153bd904e142ec41 md5: a41092b0177362dbe5eb2a18501e86c0 @@ -2170,6 +2652,7 @@ packages: - libcups >=2.3.3,<2.4.0a0 license: GPL-2.0-or-later WITH Classpath-exception-2.0 license_family: GPL + purls: [] run_exports: {} size: 122465031 timestamp: 1771443671180 @@ -2182,34 +2665,93 @@ packages: - libgcc >=14 license: Apache-2.0 license_family: Apache + purls: [] run_exports: weak: - openssl >=3.6.3,<4.0a0 size: 3182423 timestamp: 1785913583650 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.58.2-hda50119_0.conda - sha256: 48f27a6c3e4062bc09dafe9c7f6b288c5de5655e81095ab7f1aad920b2163b7b - md5: 6a2822aaf9a34ac3708904a47ff3dd7e +- conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.3.1-h443056b_0.conda + sha256: ae96bc0895a2279f2ea296879e0475333e838cc88754b0c1a4903dd92a7e1b21 + md5: 1280a5f8270f30b89cc8373ecfe8899d depends: - - __glibc >=2.17,<3.0.a0 - - cairo >=1.18.4,<2.0a0 - - fontconfig >=2.18.2,<3.0a0 - - fonts-conda-ecosystem - - fribidi >=1.0.16,<2.0a0 - - libexpat >=2.8.1,<3.0a0 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 + - tzdata - libgcc >=14 - - libglib >=2.88.3,<3.0a0 - - libharfbuzz >=14.3.0 - - libpng >=1.6.58,<1.7.0a0 + - libstdcxx >=14 + - __glibc >=2.17,<3.0.a0 + - snappy >=1.2.2,<1.3.0a0 + - libabseil >=20260526.0,<20260527.0a0 + - libabseil * cxx17* - libzlib >=1.3.2,<2.0a0 - license: LGPL-2.1-or-later + - libprotobuf >=7.35.1,<7.35.2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] run_exports: weak: - - pango >=1.58.2,<2.0a0 - size: 469916 - timestamp: 1786107384537 + - orc >=2.3.1,<2.3.2.0a0 + size: 1458252 + timestamp: 1784301236872 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.6-np2py312h91ec553_0.conda + sha256: a4c045408aea6536148307158fb873d8d8f5f1295931ea12494997eae778b096 + md5: 2143c2cef115b49a934d526f7da91696 + depends: + - python + - numpy >=1.26.0 + - python-dateutil >=2.8.2 + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=15 + - numpy >=1.25,<3 + - python_abi 3.12.* *_cp312 + constrains: + - adbc-driver-postgresql >=1.2.0 + - adbc-driver-sqlite >=1.2.0 + - beautifulsoup4 >=4.12.3 + - blosc >=1.21.3 + - bottleneck >=1.4.2 + - fastparquet >=2024.11.0 + - fsspec >=2024.10.0 + - gcsfs >=2024.10.0 + - html5lib >=1.1 + - hypothesis >=6.116.0 + - jinja2 >=3.1.5 + - lxml >=5.3.0 + - matplotlib >=3.9.3 + - numba >=0.60.0 + - numexpr >=2.10.2 + - odfpy >=1.4.1 + - openpyxl >=3.1.5 + - psycopg2 >=2.9.10 + - pyarrow >=13.0.0 + - pyiceberg >=0.8.1 + - pymysql >=1.1.1 + - pyqt5 >=5.15.9 + - pyreadstat >=1.2.8 + - pytables >=3.10.1 + - pytest >=8.3.4 + - pytest-xdist >=3.6.1 + - python-calamine >=0.3.0 + - pytz >=2024.2 + - pyxlsb >=1.0.10 + - qtpy >=2.4.2 + - scipy >=1.14.1 + - s3fs >=2024.10.0 + - sqlalchemy >=2.0.36 + - tabulate >=0.9.0 + - xarray >=2024.10.0 + - xlrd >=2.0.1 + - xlsxwriter >=3.2.0 + - zstandard >=0.23.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pandas?source=compressed-mapping + run_exports: {} + size: 14976520 + timestamp: 1789803399961 - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-haa7fec5_0.conda sha256: 5e6f7d161356fefd981948bea5139c5aa0436767751a6930cb1ca801ebb113ff md5: 7a3bff861a6583f1889021facefc08b1 @@ -2220,6 +2762,7 @@ packages: - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - pcre2 >=10.47,<10.48.0a0 @@ -2234,11 +2777,30 @@ packages: - __glibc >=2.17,<3.0.a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - pixman >=0.46.4,<1.0a0 size: 376704 timestamp: 1786106621354 +- conda: https://conda.anaconda.org/conda-forge/linux-64/prometheus-cpp-1.3.0-ha5d0236_0.conda + sha256: 013669433eb447548f21c3c6b16b2ed64356f726b5f77c1b39d5ba17a8a4b8bc + md5: a83f6a2fdc079e643237887a37460668 + depends: + - __glibc >=2.17,<3.0.a0 + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - zlib + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - prometheus-cpp >=1.3.0,<1.4.0a0 + size: 199544 + timestamp: 1730769112346 - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb03c661_1003.conda sha256: afc3b27b2cbb0487c1d0e963f96e71181ecfb623a24fb393bb19ff974a6382a1 md5: df2c27f36bdb0dde779f55b5df76a352 @@ -2247,699 +2809,330 @@ packages: - libgcc >=14 license: MIT license_family: MIT + purls: [] run_exports: {} size: 9115 timestamp: 1786067714761 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-base-4.4.3-h502d0c9_11.conda - sha256: f2e18482ab87de3d29520e69b20a34f785d738cb0c007b3c4ef8cf8cbce7fb09 - md5: fd9decaa93b00284232c91e7990fa842 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-25.0.0-py312h7900ff3_0.conda + sha256: 9b6c4aed5da2a3b8f649e1b73596e0867a5506bed9c3aa45dbaedfb674822191 + md5: 6f1918b7565c3cc5ed047a97ea510e55 + depends: + - libarrow-acero 25.0.0.* + - libarrow-dataset 25.0.0.* + - libarrow-substrait 25.0.0.* + - libparquet 25.0.0.* + - pyarrow-core 25.0.0 *_0_* + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: {} + size: 26079 + timestamp: 1784258397971 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-25.0.0-py312h2054cf2_0_cpu.conda + sha256: 8b54b732de45d85198e8e9ce5d5b5642c847f4379f917c64a4de48643a3305ee + md5: aff56abfcef5a031603250b7dc23e9d5 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 25.0.0.* *cpu + - libarrow-compute 25.0.0.* *cpu + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.2,<2.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - numpy >=1.23,<3 + - apache-arrow-proc * cpu + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/pyarrow?source=hash-mapping + run_exports: {} + size: 5395858 + timestamp: 1784258391353 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.5-py312hc767a74_2.conda + sha256: 64ef841c88756f8d1ca9c241c0ab1585ad5a9cb8d0bfe0555c201c2ad0e3c701 + md5: f7a033135df967ea983fe4833856d2b1 + depends: + - python + - typing-extensions >=4.6.0,!=4.7.0 + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: MIT + purls: + - pkg:pypi/pydantic-core?source=compressed-mapping + run_exports: {} + size: 1877770 + timestamp: 1789996105430 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda + build_number: 3 + sha256: 14c579b1016da04e4c9f1c5c857272d83ec447317d8c4074a07d59de3cef70ef + md5: 98be3cf76eca2e8871f907a03aed3b84 depends: - __glibc >=2.17,<3.0.a0 - - _openmp_mutex >=4.5 - - _r-mutex 1.* anacondar_1 - - bwidget - bzip2 >=1.0.8,<2.0a0 - - cairo >=1.18.4,<2.0a0 - - curl - - gcc_linux-64 >=14 - - gfortran_impl_linux-64 - - gsl >=2.7,<2.8.0a0 - - gxx_linux-64 >=14 - - icu >=78.3,<79.0a0 - - libblas >=3.9.0,<4.0a0 - - libcurl >=8.20.0,<9.0a0 - - libdeflate >=1.25,<1.26.0a0 + - ld_impl_linux-64 >=2.36.1 - libexpat >=2.8.1,<3.0a0 - - libgcc >=14 - - libgfortran - - libgfortran5 >=14.3.0 - - libglib >=2.88.1,<3.0a0 - - libiconv >=1.18,<2.0a0 - - libjpeg-turbo >=3.1.4.1,<4.0a0 - - liblapack >=3.9.0,<4.0a0 + - libffi >=3.7.0,<3.8.0a0 + - libgcc >=15 - liblzma >=5.8.3,<6.0a0 - - libpng >=1.6.58,<1.7.0a0 - - libstdcxx >=14 - - libtiff >=4.7.1,<4.8.0a0 - - libuuid >=2.42.1,<3.0a0 + - libnsl >=2.0.1,<2.1.0a0 + - libpython 3.12.14 h0c77377_3_cpython + - libsqlite >=3.53.4,<4.0a0 + - libuuid >=2.42.3,<3.0a0 + - libxcrypt >=4.4.38 - libzlib >=1.3.2,<2.0a0 - - make - - pango >=1.56.4,<2.0a0 - - pcre2 >=10.47,<10.48.0a0 + - ncurses >=6.6,<7.0a0 + - openssl >=3.5.8,<4.0a0 - readline >=8.3,<9.0a0 - - sed - tk >=8.6.13,<8.7.0a0 - - tktable - - tzdata >=2024a - - xorg-libice >=1.1.2,<2.0a0 - - xorg-libsm >=1.2.6,<2.0a0 - - xorg-libx11 >=1.8.13,<2.0a0 - - xorg-libxt >=1.3.1,<2.0a0 - license: GPL-2.0-or-later - license_family: GPL + - tzdata + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + purls: [] run_exports: weak: - - r-base >=4.4,<4.5.0a0 + - python_abi 3.12.* *_cp312 noarch: - - r-base >=4.4,<4.5.0a0 - size: 27061572 - timestamp: 1781505150459 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-bit-4.6.0-r44h54b55ab_1.conda - sha256: 66f050183d8ffd9de6c4b763f43109490196f2a11dfad98e9e8892175aaa6263 - md5: 4e905067722f1416de735a3d6b33825c + - python + size: 23044161 + timestamp: 1788392496306 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_1.conda + sha256: cb142bfd92f6e55749365ddc244294fa7b64db6d08c45b018ff1c658907bfcbf + md5: 15878599a87992e44c059731771591cb depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - - r-base >=4.4,<4.5.0a0 - license: GPL-2.0-or-later - license_family: GPL2 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pyyaml?source=hash-mapping run_exports: {} - size: 621865 - timestamp: 1757441702226 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-bit64-4.8.6-r44hddc5280_0.conda - sha256: 4441230276e44b790d4c1f452e0d206b90da70d3c37f67616a2b2c9ce30ebb28 - md5: 711b03f9367a9d1323c2282008794168 + size: 198293 + timestamp: 1770223620706 +- conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h94463f1_2.conda + sha256: 7279908454f0c87b84ebc4aec6924f02f41a105d88420fb35dfaf5ecd976e0f5 + md5: d83f2ee212d217a6d745a00e5be84671 depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=15 - - r-base >=4.4,<4.5.0a0 - - r-bit >=4.0.0 - license: GPL-2.0-only - license_family: GPL2 - run_exports: {} - size: 575930 - timestamp: 1788276187401 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-brio-1.1.5-r44h54b55ab_2.conda - sha256: 978484617257c31d7ea2977e970069a0f6c1456f315b95f946d81d6012dec527 - md5: 740449b857685bb63ffd29f10643f126 + - libre2-11 2025.11.05 h60473fc_2 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - libre2-11 >=2025.11.5 + - re2 + size: 27397 + timestamp: 1781312576962 +- conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda + sha256: 12ffde5a6f958e285aa22c191ca01bbd3d6e710aa852e00618fa6ddc59149002 + md5: d7d95fc8287ea7bf33e0e7116d2b95ec depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - - r-base >=4.4,<4.5.0a0 - license: MIT - license_family: MIT - run_exports: {} - size: 44540 - timestamp: 1757421410587 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-cli-3.6.6-r44h3697838_0.conda - sha256: 158a5ffe707dfa11bde1ede789ee9dc6521e8eebc5b8278f83b9e9f787180d68 - md5: 13cca7c3f7d182bf222f3c70303c91be + - ncurses >=6.5,<7.0a0 + license: GPL-3.0-only + license_family: GPL + purls: [] + run_exports: + weak: + - readline >=8.3,<9.0a0 + size: 345073 + timestamp: 1765813471974 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.16.7-hbcb56a7_0.conda + noarch: python + sha256: 5ed4c55297861e06c25fadf4b93c7ace90093368a8a8e71a0cc690d3ce806ca3 + md5: 23da3766254f778b56fe168882b84058 depends: + - python - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 - - r-base >=4.4,<4.5.0a0 + - libgcc >=15 + constrains: + - __glibc >=2.17 license: MIT license_family: MIT + purls: + - pkg:pypi/ruff?source=compressed-mapping run_exports: {} - size: 1322870 - timestamp: 1775733707285 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-colorspace-2.1_3-r44h54b55ab_0.conda - sha256: eac31cb3b2a46d641ba03d1c1501b30f7608eb4c6e743e9b63fd00461171ca78 - md5: c163d0ec145b9291fc76251c111bdf6b + size: 8979648 + timestamp: 1789115793914 +- conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.5-h7e3ee7f_1.conda + sha256: 7369ac21f3561e2203f5b1600ef0671270057380783ca4b9e15f679ba25db575 + md5: fa1c00d999e83ec20173c9775f71a1f1 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - - r-base >=4.4,<4.5.0a0 + - openssl >=3.5.7,<4.0a0 + license: Apache-2.0 + license_family: Apache + purls: [] + run_exports: + weak: + - s2n >=1.7.5,<1.7.6.0a0 + size: 392607 + timestamp: 1783164766248 +- conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.18.1-py312h106c528_0.conda + sha256: 1304a96048fd68ae50e30ca4891e4d2851c20c5687b3152045930541a4008121 + md5: 9f57b4b1fa3b51dbf1df6eca1e6c0cfa + depends: + - __glibc >=2.17,<3.0.a0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libgcc >=15 + - libgfortran + - libgfortran5 >=15.3.0 + - liblapack >=3.9.0,<4.0a0 + - libstdcxx >=15 + - numpy <2.8 + - numpy >=1.25,<3 + - numpy >=2.0.0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/scipy?source=compressed-mapping run_exports: {} - size: 2543436 - timestamp: 1784283278940 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-data.table-1.18.4-r44h1c8cec4_0.conda - sha256: 1ed7f608b57a162526ae9e7b04d61150e9a229e19d61fa5f414d716fcee088f5 - md5: 6e6526066b997fcce2750d7541c8af7e + size: 17399079 + timestamp: 1789137054012 +- conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda + sha256: 48f3f6a76c34b2cfe80de9ce7f2283ecb55d5ed47367ba91e8bb8104e12b8f11 + md5: 98b6c9dc80eb87b2519b97bcf7e578dd depends: - - __glibc >=2.17,<3.0.a0 - - _openmp_mutex >=4.5 - libgcc >=14 - - libzlib >=1.3.2,<2.0a0 - - r-base >=4.4,<4.5.0a0 - license: MPL-2.0 - license_family: OTHER - run_exports: {} - size: 2566069 - timestamp: 1783428411623 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-diffobj-0.3.8-r44h54b55ab_0.conda - sha256: 99616a256fb84ab852490363702a12a71bcfadcc05e041ca6c4695dbeb9b0201 - md5: 3a99eb9769d5ae4fbf7a69900a85fc65 - depends: - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 - libgcc >=14 - - r-base >=4.4,<4.5.0a0 - - r-crayon >=1.3.2 - license: GPL-2.0-or-later - license_family: GPL2 - run_exports: {} - size: 1013506 - timestamp: 1784366923475 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-digest-0.6.39-r44h3697838_0.conda - sha256: 829b5a49aa497c7cab48915ee3e5ffb4f651160027961faefe412166202b9ca1 - md5: c09d9af7929f64611137e83de28db915 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - snappy >=1.2.2,<1.3.0a0 + size: 45829 + timestamp: 1762948049098 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd70dff1_3.conda + build_number: 103 + sha256: 43624eab22f5f29df7d6ffe914cf442f28fd559b55b290906255492826e636e8 + md5: 48a1049e710857572fc2a832aa394d9f depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - - libstdcxx >=14 - - r-base >=4.4,<4.5.0a0 - license: GPL-2.0-or-later - license_family: GPL2 - run_exports: {} - size: 218462 - timestamp: 1763566657101 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-dplyr-1.2.1-r44h3697838_0.conda - sha256: 7fadc351f4016640e98f68e1a5d0f434993794c79ca5130864a77a91570d4a9f - md5: e6bf69914670ac1931dcff0372516c50 + - 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: 3550916 + timestamp: 1784229071544 +- conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-2.4.1-py312h1b36aeb_0.conda + sha256: b07ed016c0befa20362bbd1b41cd6d730407f27f171ad15811347741a9553bb7 + md5: d06605fe1a45b11cb4f21d2438a3e3a9 depends: + - python - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 - - r-base >=4.4,<4.5.0a0 - - r-ellipsis - - r-generics - - r-glue >=1.3.2 - - r-lifecycle >=1.0.0 - - r-magrittr >=1.5 - - r-pillar >=1.5.1 - - r-r6 - - r-rlang >=0.4.10 - - r-tibble >=2.1.3 - - r-tidyselect >=1.1.0 - - r-vctrs >=0.3.5 - license: MIT - license_family: MIT + - libgcc >=15 + - python_abi 3.12.* *_cp312 + license: BSD-2-Clause + purls: + - pkg:pypi/wrapt?source=compressed-mapping run_exports: {} - size: 1448674 - timestamp: 1775207125376 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-ellipsis-0.3.3-r44h54b55ab_0.conda - sha256: ac44b0472993c0b620b71a0356545242ea853a894dab21add2964b7f2d924f19 - md5: 309df67a7a2f04427edfed0c88732fd4 + size: 145019 + timestamp: 1789986016000 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-h280c20c_0.conda + sha256: 49b532d1df875c6749d9078b56a76f3f5db49a5abe0ca620b593ed474ef0ebf1 + md5: 85c9442aec283b4e464fa9ecc484a2f3 depends: - - __glibc >=2.17,<3.0.a0 - libgcc >=14 - - r-base >=4.4,<4.5.0a0 - - r-rlang >=0.3.0 + - __glibc >=2.17,<3.0.a0 license: MIT license_family: MIT - run_exports: {} - size: 33480 - timestamp: 1775287245091 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-fansi-1.0.7-r44h54b55ab_0.conda - sha256: f738c07d48dabdf29ffb228f5eb752e00abe8f2a025c3032eb3b2974224d369c - md5: cb8edd5978ccf005e53ae370ae1b15d0 + purls: [] + run_exports: + weak: + - xorg-libice >=1.1.2,<2.0a0 + size: 62517 + timestamp: 1786474410404 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-h0d788c3_1.conda + sha256: ef907caee0665cf3b0f775602cc50e388e3bade8ce22ecade0873ff26604b2fd + md5: aa7459ed9ad086ba11dda843d764b33d depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - - r-base >=4.4,<4.5.0a0 - license: GPL-2.0-or-later - license_family: GPL3 - run_exports: {} - size: 329490 - timestamp: 1763566093770 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-farver-2.1.2-r44h3697838_2.conda - sha256: 4386f196e54365a83cf0df0c2a389cb286e488bb868f96c4d6c0a4cc1bb69c84 - md5: 830031b95e37195f00524cabc98adba4 + - xorg-libice >=1.1.2,<2.0a0 + - libuuid >=2.42.2,<3.0a0 + license: MIT + purls: [] + run_exports: + weak: + - xorg-libsm >=1.2.6,<2.0a0 + size: 30739 + timestamp: 1786545374265 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_0.conda + sha256: 516d4060139dbb4de49a4dcdc6317a9353fb39ebd47789c14e6fe52de0deee42 + md5: 861fb6ccbc677bb9a9fb2468430b9c6a depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - - libstdcxx >=14 - - r-base >=4.4,<4.5.0a0 + - libxcb >=1.17.0,<2.0a0 license: MIT license_family: MIT - run_exports: {} - size: 1427819 - timestamp: 1757441247787 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-fs-2.1.0-r44h3697838_0.conda - sha256: 0c1e4f774db77e4ee5b46de3266835f009cade13174d5a0dae04585641d8753e - md5: 36e7df839c01c17b2c3dbf79147f70de + purls: [] + run_exports: + weak: + - xorg-libx11 >=1.8.13,<2.0a0 + size: 839652 + timestamp: 1770819209719 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_2.conda + sha256: cbf891f6cc1a859347680af1c8562bc6b033bf182a2a3bb536016932be3206de + md5: f06ef439c280a5f90b8bf62355008dbc depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - - libstdcxx >=14 - - libuv >=1.51.0,<2.0a0 - - r-base >=4.4,<4.5.0a0 license: MIT license_family: MIT - run_exports: {} - size: 243769 - timestamp: 1777152730611 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-glue-1.8.1-r44h54b55ab_0.conda - sha256: f35d216e9961d3c8b092b7a77418d2acc2c1597229211f244b2e073465555110 - md5: b89f52d1c0cab3a259ef35834eb0a0d9 + purls: [] + run_exports: + weak: + - xorg-libxau >=1.0.12,<2.0a0 + size: 16419 + timestamp: 1786381001122 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_2.conda + sha256: c50a16c05ccd7fe7dd6d6cfb539f4e9a491d50f9ed7a5c902fec638f7d0d27be + md5: 2e66c929f3d879708335b6ea4557c838 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - - r-base >=4.4,<4.5.0a0 license: MIT license_family: MIT - run_exports: {} - size: 170945 - timestamp: 1776413606623 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-isoband-0.3.0-r44h3697838_0.conda - sha256: 8067242cd89c8467f186577c7f2249620393eadc5bff1be3f270cceaf2dffb5e - md5: 94d30cca33a0f27ac7bc992fe0a88d5e + purls: [] + run_exports: + weak: + - xorg-libxdmcp >=1.1.5,<2.0a0 + size: 21120 + timestamp: 1786381006369 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-hb03c661_0.conda + sha256: 79c60fc6acfd3d713d6340d3b4e296836a0f8c51602327b32794625826bd052f + md5: 34e54f03dfea3e7a2dcf1453a85f1085 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - - libstdcxx >=14 - - r-base >=4.4,<4.5.0a0 - - r-cli - - r-cpp11 - - r-rlang - license: MIT - license_family: MIT - run_exports: {} - size: 1658393 - timestamp: 1766530457133 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-jsonlite-2.0.0-r44h54b55ab_1.conda - sha256: faff2faef7e73c5fd02fea6bbe8aa9e444fabf7f56e8d932c7babc51ee76a290 - md5: 26de9e385370e6bf7d0ca13a3e630d7f - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - r-base >=4.4,<4.5.0a0 - license: MIT - license_family: MIT - run_exports: {} - size: 639260 - timestamp: 1757419504603 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-lattice-0.22_9-r44h54b55ab_0.conda - sha256: dcdd1c915ccb9b6d13344c1769198d95334eb7b645c6bf902f14aceb0c792db4 - md5: 5e5f1e762d462c14fdc6ecf90abe448b - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - r-base >=4.4,<4.5.0a0 - license: GPL-2.0-or-later - license_family: GPL3 - run_exports: {} - size: 1417686 - timestamp: 1770694118995 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-magrittr-2.0.5-r44h54b55ab_0.conda - sha256: a827edd45d915ca2e5a950a940cdc0e0f5288c3de6d63d7e6f887e9e8ec0d8e3 - md5: 6ff0837b78eb1c0d443eab95c7255089 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - r-base >=4.4,<4.5.0a0 - license: MIT - license_family: MIT - run_exports: {} - size: 211764 - timestamp: 1775298614193 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-matrix-1.7_6-r44h0e4624f_0.conda - sha256: 7c4865fac87d54bf07dad26759694710beee5abacdc66b24bef5d79b4503fb96 - md5: 852b37b5350a80218cf6e93b5723811e - depends: - - __glibc >=2.17,<3.0.a0 - - libblas >=3.9.0,<4.0a0 - - libgcc >=14 - - liblapack >=3.9.0,<4.0a0 - - r-base >=4.4,<4.5.0a0 - - r-lattice - license: GPL-2.0-or-later - license_family: GPL3 - run_exports: {} - size: 4316599 - timestamp: 1786437958094 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-nanoparquet-0.5.1-r44h3697838_0.conda - sha256: ece5a6ae5cc2c096a76dacb581a95ad0b343d5f77b31e169aac2037187e6e4f5 - md5: bef3b03f040a2be0a8aa87023b31752b - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 - - r-base >=4.4,<4.5.0a0 - license: MIT + file - license_family: MIT - run_exports: {} - size: 954891 - timestamp: 1777144276626 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-parallelly-1.48.0-r44h54b55ab_0.conda - sha256: 4e9571bc8f0967ffd7d7c9e888172fb3031f74e2b9c4270300dd46373d5c7bb2 - md5: f699c669905baee95c964822ce194473 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - r-base >=4.4,<4.5.0a0 - license: LGPL-2.1-or-later - license_family: LGPL - run_exports: {} - size: 642691 - timestamp: 1782771946838 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-processx-3.9.0-r44h54b55ab_0.conda - sha256: a5709a9eac9b91090b4602b0159ed94c0328d904349d39f08735dad1cf90e368 - md5: a514fa73c8a7b0bec2d1c6bc0913f813 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - r-base >=4.4,<4.5.0a0 - - r-ps >=1.2.0 - - r-r6 - license: MIT - license_family: MIT - run_exports: {} - size: 410728 - timestamp: 1776865710444 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-ps-1.9.3-r44h54b55ab_0.conda - sha256: 8a3560c71204ba5dd72643c0c06efb626b45f26f55e12d38a4fc854cbce58edd - md5: f9a8ca27e2f6ba95cd8f4f185b8f4f54 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - r-base >=4.4,<4.5.0a0 - license: BSD-3-Clause - license_family: BSD - run_exports: {} - size: 414055 - timestamp: 1777148026559 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-purrr-1.2.2-r44h54b55ab_0.conda - sha256: 3a9dec6181e7077520b2c86654ea613f86b1875f98ff5e20b072133ae26a289c - md5: bb8a0c5371d5881cbfac7fd503c907ba - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - r-base >=4.4,<4.5.0a0 - - r-cli >=3.4 - - r-lifecycle >=1.0.3 - - r-magrittr >=1.5 - - r-rlang >=0.4.10 - - r-vctrs >=0.5 - license: MIT - license_family: MIT - run_exports: {} - size: 547801 - timestamp: 1775853620724 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-rcpp-1.1.2-r44h3697838_0.conda - sha256: ae8897648d121d72714cac0f0b77a816a9a236d7ab356c9bcab4a926f9cbcc2b - md5: 396ccb81d4f4506a8f0cc37c115ac2c0 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 - - r-base >=4.4,<4.5.0a0 - license: GPL-2.0-or-later - license_family: GPL2 - run_exports: {} - size: 2119520 - timestamp: 1783264475928 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-readr-2.2.0-r44h3697838_0.conda - sha256: 07eeb2d9cc4f2dd206422e4b19dddb8a777c23a1dcdad9874a8ea9ab9f3b4250 - md5: 4c35df23b810271b5aa0283176d625b1 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 - - r-base >=4.4,<4.5.0a0 - - r-cli - - r-clipr - - r-cpp11 - - r-crayon - - r-hms >=0.4.1 - - r-lifecycle >=0.2.0 - - r-r6 - - r-rlang - - r-tibble - - r-tzdb >=0.1.1 - - r-vroom >=1.5.4 - license: MIT - license_family: MIT - run_exports: {} - size: 811815 - timestamp: 1771573592843 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-rlang-1.3.0-r44h3697838_0.conda - sha256: 67df0312f6842745cab29128ca7603e11abe37f4de34831d51f55954ab9e3328 - md5: 25ac627a5d249fc98a71ecae2fad0978 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 - - r-base >=4.4,<4.5.0a0 - license: GPL-3.0-only - license_family: GPL3 - run_exports: {} - size: 1592747 - timestamp: 1783260208938 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-s7-0.2.2-r44h54b55ab_0.conda - sha256: 8f88140a600c1d71bfae9cb51c9ae41a163d9c482faa901d223d6317d47e1071 - md5: bf56b6c392ae730a2328840d83f00f47 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - r-base >=4.4,<4.5.0a0 - license: MIT - license_family: MIT - run_exports: {} - size: 311052 - timestamp: 1776861445572 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-testthat-3.3.2-r44h3697838_0.conda - sha256: e5eb7862965a8e8293641367eecc6fc48b9d57958b50b3d1ae7980315064b913 - md5: 023ba33273b38b9c3ec1f7b88d1fe057 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 - - r-base >=4.4,<4.5.0a0 - - r-brio >=1.1.3 - - r-callr >=3.7.3 - - r-cli >=3.6.1 - - r-desc >=1.4.2 - - r-digest >=0.6.33 - - r-evaluate >=1.0.1 - - r-jsonlite >=1.8.7 - - r-lifecycle >=1.0.3 - - r-magrittr >=2.0.3 - - r-pkgload >=1.3.2.1 - - r-praise >=1.0.0 - - r-processx >=3.8.2 - - r-ps >=1.7.5 - - r-r6 >=2.5.1 - - r-rlang >=1.1.1 - - r-waldo >=0.6.0 - - r-withr >=3.0.2 - license: MIT - license_family: MIT - run_exports: {} - size: 1845818 - timestamp: 1768138291423 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-tibble-3.3.1-r44h54b55ab_0.conda - sha256: 2c9351a0390512341bd1e03352728e176912fabacbe44e8912a0d42d262f1351 - md5: 4896d527628b3637c7e5bd2132a4c6a1 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - r-base >=4.4,<4.5.0a0 - - r-fansi >=0.4.0 - - r-lifecycle >=1.0.0 - - r-magrittr - - r-pillar >=1.8.1 - - r-pkgconfig - - r-rlang >=1.0.2 - - r-vctrs >=0.4.2 - license: MIT - license_family: MIT - run_exports: {} - size: 590155 - timestamp: 1768139123115 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-tzdb-0.5.0-r44h3697838_2.conda - sha256: 4640ae24c27ce009406b392c739d9ef6015ed6243fa3f6b8b466ccc47cf9ad9f - md5: 22de54c4ce316337baf45289e63ab601 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 - - r-base >=4.4,<4.5.0a0 - - r-cpp11 >=0.5.2 - license: MIT - license_family: MIT - run_exports: {} - size: 554579 - timestamp: 1757489995373 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-utf8-1.2.6-r44h54b55ab_1.conda - sha256: 8baeb9d46c17ce6578155af0f2dce665267eb8a16fa263659ad33e5005c3ed3f - md5: c7bce6987e0f5aaf9bdf5296dd3693c4 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - r-base >=4.4,<4.5.0a0 - license: Apache-2.0 - license_family: APACHE - run_exports: {} - size: 147724 - timestamp: 1757424747047 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-vctrs-0.7.3-r44h3697838_0.conda - sha256: b3e9ab33e1b23036f628307a6ffd440276895292929ee9dfd916ed74db64cf6f - md5: 77dedc64b9fe9f8452c57b503d231f96 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 - - r-base >=4.4,<4.5.0a0 - - r-cli >=3.4.0 - - r-glue - - r-lifecycle >=1.0.3 - - r-rlang >=1.0.6 - license: MIT - license_family: MIT - run_exports: {} - size: 1807489 - timestamp: 1775897992371 -- conda: https://conda.anaconda.org/conda-forge/linux-64/r-vroom-1.7.1-r44h3697838_0.conda - sha256: 841aa5353981cbece9570266ba7bc237268ff29ac8a2e04840a224d21cda69d1 - md5: 39e33cbc8478e7b0868b821d8b7b0cbe - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libstdcxx >=14 - - r-base >=4.4,<4.5.0a0 - - r-bit64 - - r-cli - - r-cpp11 >=0.2.0 - - r-crayon - - r-glue - - r-hms - - r-lifecycle - - r-progress >=1.2.1 - - r-rlang >=0.4.2 - - r-tibble >=2.0.0 - - r-tidyselect - - r-tzdb >=0.1.1 - - r-vctrs >=0.2.0 - - r-withr - license: MIT - license_family: MIT - run_exports: {} - size: 935663 - timestamp: 1774940647571 -- conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - sha256: 12ffde5a6f958e285aa22c191ca01bbd3d6e710aa852e00618fa6ddc59149002 - md5: d7d95fc8287ea7bf33e0e7116d2b95ec - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - ncurses >=6.5,<7.0a0 - license: GPL-3.0-only - license_family: GPL - run_exports: - weak: - - readline >=8.3,<9.0a0 - size: 345073 - timestamp: 1765813471974 -- conda: https://conda.anaconda.org/conda-forge/linux-64/sed-4.10-h19d0853_0.conda - sha256: b013d2085ce4ac01daec7b30472e9f3b6c2d69eb3a3a85a6cee3e01a3cb4f61a - md5: e4a1ff2e59a5d9b38be71d15c93cf1bd - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - license: GPL-3.0-only - license_family: GPL - run_exports: {} - size: 268482 - timestamp: 1776859422619 -- conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd70dff1_3.conda - build_number: 103 - sha256: 43624eab22f5f29df7d6ffe914cf442f28fd559b55b290906255492826e636e8 - md5: 48a1049e710857572fc2a832aa394d9f - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libzlib >=1.3.2,<2.0a0 - constrains: - - xorg-libx11 >=1.8.13,<2.0a0 - license: TCL - run_exports: - weak: - - tk >=8.6.13,<8.7.0a0 - size: 3550916 - timestamp: 1784229071544 -- conda: https://conda.anaconda.org/conda-forge/linux-64/tktable-2.10-h5a7a40f_8.conda - sha256: 3e20b2f2902a1f402ef2420ce2b9e8c91f9e02748d55530894ac1f640561fdd0 - md5: 72628f56d7a99b86efa435a0b97a4b47 - depends: - - tk - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - tk >=8.6.13,<8.7.0a0 - - xorg-libx11 >=1.8.13,<2.0a0 - license: TCL - run_exports: {} - size: 102544 - timestamp: 1773732786017 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-h280c20c_0.conda - sha256: 49b532d1df875c6749d9078b56a76f3f5db49a5abe0ca620b593ed474ef0ebf1 - md5: 85c9442aec283b4e464fa9ecc484a2f3 - depends: - - libgcc >=14 - - __glibc >=2.17,<3.0.a0 - license: MIT - license_family: MIT - run_exports: - weak: - - xorg-libice >=1.1.2,<2.0a0 - size: 62517 - timestamp: 1786474410404 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-h0d788c3_1.conda - sha256: ef907caee0665cf3b0f775602cc50e388e3bade8ce22ecade0873ff26604b2fd - md5: aa7459ed9ad086ba11dda843d764b33d - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - xorg-libice >=1.1.2,<2.0a0 - - libuuid >=2.42.2,<3.0a0 - license: MIT - run_exports: - weak: - - xorg-libsm >=1.2.6,<2.0a0 - size: 30739 - timestamp: 1786545374265 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_0.conda - sha256: 516d4060139dbb4de49a4dcdc6317a9353fb39ebd47789c14e6fe52de0deee42 - md5: 861fb6ccbc677bb9a9fb2468430b9c6a - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libxcb >=1.17.0,<2.0a0 - license: MIT - license_family: MIT - run_exports: - weak: - - xorg-libx11 >=1.8.13,<2.0a0 - size: 839652 - timestamp: 1770819209719 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_2.conda - sha256: cbf891f6cc1a859347680af1c8562bc6b033bf182a2a3bb536016932be3206de - md5: f06ef439c280a5f90b8bf62355008dbc - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - license: MIT - license_family: MIT - run_exports: - weak: - - xorg-libxau >=1.0.12,<2.0a0 - size: 16419 - timestamp: 1786381001122 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_2.conda - sha256: c50a16c05ccd7fe7dd6d6cfb539f4e9a491d50f9ed7a5c902fec638f7d0d27be - md5: 2e66c929f3d879708335b6ea4557c838 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - license: MIT - license_family: MIT - run_exports: - weak: - - xorg-libxdmcp >=1.1.5,<2.0a0 - size: 21120 - timestamp: 1786381006369 -- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-hb03c661_0.conda - sha256: 79c60fc6acfd3d713d6340d3b4e296836a0f8c51602327b32794625826bd052f - md5: 34e54f03dfea3e7a2dcf1453a85f1085 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libx11 >=1.8.12,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxext >=1.3.7,<2.0a0 @@ -2954,6 +3147,7 @@ packages: - xorg-libx11 >=1.8.12,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxfixes >=6.0.2,<7.0a0 @@ -2970,6 +3164,7 @@ packages: - xorg-libxfixes >=6.0.2,<7.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxi >=1.8.3,<2.0a0 @@ -2986,6 +3181,7 @@ packages: - xorg-libxrender >=0.9.12,<0.10.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxrandr >=1.5.5,<2.0a0 @@ -3000,6 +3196,7 @@ packages: - xorg-libx11 >=1.8.10,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxrender >=0.9.12,<0.10.0a0 @@ -3016,6 +3213,7 @@ packages: - xorg-libx11 >=1.8.10,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxt >=1.3.1,<2.0a0 @@ -3032,11 +3230,26 @@ packages: - xorg-libxi >=1.7.10,<2.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - xorg-libxtst >=1.2.5,<2.0a0 size: 32808 timestamp: 1727964811275 +- 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 + purls: [] + run_exports: + weak: + - yaml >=0.2.5,<0.3.0a0 + size: 84936 + timestamp: 1787228426393 - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_3.conda sha256: 16080a1c7724f7d25727cdc23c7658e0cec2db52448c1dc0c33467ee2c6e1c62 md5: 6acb86426229f96f93e5468d1df3a5e8 @@ -3045,6 +3258,7 @@ packages: - libzlib 1.3.2 h25fd6f3_3 license: Zlib license_family: Other + purls: [] run_exports: weak: - libzlib >=1.3.2,<2.0a0 @@ -3058,67 +3272,158 @@ packages: - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - zstd >=1.5.7,<1.6.0a0 size: 601375 timestamp: 1764777111296 -- conda: https://conda.anaconda.org/conda-forge/noarch/_r-mutex-1.0.1-anacondar_1.tar.bz2 - sha256: e58f9eeb416b92b550e824bcb1b9fb1958dee69abfe3089dfd1a9173e3a0528a - md5: 19f9db5f4f1b7f5ef5f6d67207f25f38 - license: BSD +- conda: https://conda.anaconda.org/conda-forge/noarch/anndata-0.13.3.post0-pyhd8ed1ab_0.conda + sha256: 22f79e872295c9d2959627571697721cea90c0e5c344fab320a87debe0693a94 + md5: 35d13d079876f32cda60a40c44ef17aa + depends: + - array-api-compat >=1.7.1 + - h5py >=3.11 + - legacy-api-wrap + - natsort + - numpy >=2.1 + - packaging >=24.2 + - pandas >=2.3 + - pydantic-settings + - python >=3.12 + - python-dotenv + - scipy >=1.14,!=1.17.0 + - scverse-misc >=0.1.0 + - typing_extensions >=4.16 + - zarr >=3.1 + constrains: + - awkward >=2.3.2 + - dask >=2023.5.1,!=2024.8.*,!=2024.9.*,!=2025.2.*,!=2025.3.*,!=2025.4.*,!=2025.5.*,!=2025.6.*,!=2025.7.*,!=2025.8.* + - xarray >=2025.06.1 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/anndata?source=hash-mapping + run_exports: {} + size: 146200 + timestamp: 1787898570013 +- conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.8.0-pyhd8ed1ab_0.conda + sha256: b8fcb994134d3918d1c64a9d62f4168ff85d5bfb89e698102fe4ed679fe0df24 + md5: 108c928d2a8551832dbc762b535e90bb + depends: + - python >=3.10 + - typing-extensions >=4.0.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/annotated-types?source=hash-mapping run_exports: {} - size: 3566 - timestamp: 1562343890778 + size: 19461 + timestamp: 1784935220549 +- conda: https://conda.anaconda.org/conda-forge/noarch/array-api-compat-1.15.0-pyhc364b38_0.conda + sha256: 328865729d20c18ad2bb2472ae88dbe04e20291b4e36d9fab89d10b5a2badf35 + md5: c316aba22bb6b6f1e1cacc609c007973 + depends: + - python >=3.10 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/array-api-compat?source=hash-mapping + run_exports: {} + size: 62003 + timestamp: 1780923775972 - 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/compiler-rt22_osx-arm64-22.1.8-h7e67a1e_1.conda - sha256: 0d3cb7b6386265b8a308b1a0a83a0e718e9d78b0e61f7d7a7bb80b47760e35da - md5: 3e936f877a0eb8381d30f14810a39acd - constrains: - - compiler-rt >=9.0.1 - license: Apache-2.0 WITH LLVM-exception - license_family: APACHE - run_exports: {} - size: 11005161 - timestamp: 1781741132641 -- conda: https://conda.anaconda.org/conda-forge/noarch/compiler-rt_osx-arm64-19.1.7-he32a8d3_1.conda - sha256: 8c32a3db8adf18ed58197e8895ce4f24a83ed63c817512b9a26724753b116f2a - md5: 8d99c82e0f5fed6cc36fcf66a11e03f0 +- conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-2.0.1-pyhcf101f3_0.conda + sha256: 39358005c3cc44d8315ed79789b18a960d69365be979970cc6bae8f7a6177703 + md5: 70e764e549c6c25de5e429d6e4a28b39 depends: - - clang 19.1.7.* - constrains: - - compiler-rt 19.1.7 - - clangxx 19.1.7 - license: Apache-2.0 WITH LLVM-exception - license_family: APACHE + - cached_property >=2.0.1,<2.0.2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/cached-property?source=compressed-mapping run_exports: {} - size: 10490535 - timestamp: 1757411851093 -- conda: https://conda.anaconda.org/conda-forge/noarch/compiler-rt_osx-arm64-22.1.8-hce30654_1.conda - sha256: e8eccc163a8ca1fcd051826d566934d3785413b5a57d658902eb775026af3856 - md5: d50cec26659fa7e7c285803c77abab9b + size: 3720 + timestamp: 1787678456483 +- conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-2.0.1-pyhcf101f3_0.conda + sha256: 4bd96dab5a434e4ee59fb6910cae704d4c5b453907d4886272a9943d37e42b6f + md5: 256d863ded0f79464391a075944d24a6 depends: - - compiler-rt22_osx-arm64 22.1.8 h7e67a1e_1 - constrains: - - clang 22.1.8 - license: Apache-2.0 WITH LLVM-exception - license_family: APACHE + - python >=3.10 + - python + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/cached-property?source=compressed-mapping + run_exports: {} + size: 16597 + timestamp: 1787678456483 +- 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 + purls: + - pkg:pypi/colorama?source=hash-mapping + run_exports: {} + size: 27011 + timestamp: 1733218222191 +- conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.3.1-pyhd8ed1ab_1.conda + sha256: 7d57a7b8266043ffb99d092ebc25e89a0a2490bed4146b9432c83c2c476fa94d + md5: 5498feb783ab29db6ca8845f68fa0f03 + depends: + - python >=3.10 + - wrapt <3,>=1.10 + license: MIT + license_family: MIT + purls: + - pkg:pypi/deprecated?source=hash-mapping run_exports: {} - size: 16698 - timestamp: 1781741176960 + size: 15896 + timestamp: 1768934186726 +- conda: https://conda.anaconda.org/conda-forge/noarch/donfig-0.8.1.post1-pyhd8ed1ab_1.conda + sha256: d58e97d418f71703e822c422af5b9c431e3621a0ecdc8b0334c1ca33e076dfe7 + md5: c56a7fa5597ad78b62e1f5d21f7f8b8f + depends: + - python >=3.9 + - pyyaml + license: MIT + license_family: MIT + purls: + - pkg:pypi/donfig?source=hash-mapping + run_exports: {} + size: 22491 + timestamp: 1734368817583 +- 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/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 sha256: 58d7f40d2940dd0a8aa28651239adbf5613254df0f75789919c4e6762054403b md5: 0c96522c6bdaed4b1566d11387caaf45 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 397370 timestamp: 1566932522327 @@ -3127,6 +3432,7 @@ packages: md5: 34893075a5c9e55cdafac56607368fc6 license: OFL-1.1 license_family: Other + purls: [] run_exports: {} size: 96530 timestamp: 1620479909603 @@ -3135,6 +3441,7 @@ packages: md5: 4d59c254e01d9cde7957100457e2d5fb license: OFL-1.1 license_family: Other + purls: [] run_exports: {} size: 700814 timestamp: 1620479612257 @@ -3143,6 +3450,7 @@ packages: md5: 49023d73832ef61042f6a237cb2687e7 license: LicenseRef-Ubuntu-Font-Licence-Version-1.0 license_family: Other + purls: [] run_exports: {} size: 1620504 timestamp: 1727511233259 @@ -3153,6 +3461,7 @@ packages: - fonts-conda-forge license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 3667 timestamp: 1566974674465 @@ -3166,562 +3475,321 @@ packages: - font-ttf-source-code-pro license: BSD-3-Clause license_family: BSD + purls: [] run_exports: {} size: 4059 timestamp: 1762351264405 -- conda: https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-4.18.0-he073ed8_9.conda - sha256: 41557eeadf641de6aeae49486cef30d02a6912d8da98585d687894afd65b356a - md5: 86d9cba083cd041bfbf242a01a7a1999 - constrains: - - sysroot_linux-64 ==2.28 - license: LGPL-2.0-or-later AND LGPL-2.0-or-later WITH exceptions AND GPL-2.0-or-later - license_family: GPL - run_exports: {} - size: 1278712 - timestamp: 1765578681495 -- conda: https://conda.anaconda.org/conda-forge/noarch/libcxx-headers-19.1.7-h707e725_2.conda - sha256: 36485e6807e03a4f15a8018ec982457a9de0a1318b4b49a44c5da75849dbe24f - md5: de91b5ce46dc7968b6e311f9add055a2 - depends: - - __unix - constrains: - - libcxx-devel 19.1.7 - license: Apache-2.0 WITH LLVM-exception - license_family: Apache - run_exports: {} - size: 830747 - timestamp: 1764647922410 -- conda: https://conda.anaconda.org/conda-forge/noarch/libcxx-headers-22.1.8-h707e725_0.conda - sha256: 0b9853ca0d29729488519ab5c61401b1ade22b15841dae8bf299e6980fe1c964 - md5: 2306682595f6d2a5e67fab177427e139 - depends: - - __unix - constrains: - - clangxx >=19 - - gxx_osx-64 >=14 - - libcxx-devel 22.1.8 - - gxx_osx-arm64 >=14 - - gxx_linux-64 >=14 - license: Apache-2.0 WITH LLVM-exception - license_family: Apache - run_exports: {} - size: 1149924 - timestamp: 1781670214284 -- conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-14.4.0-hd9a9cd0_101.conda - sha256: dd2fd8417476a4086dc5a5ac64a884cf953c7799ac329ddea9da36f056008110 - md5: 8bd1e927275c36238cc26cc9181fd191 - depends: - - __unix - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - run_exports: {} - size: 3084295 - timestamp: 1785374455100 -- conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-16.1.0-h59071f9_101.conda - sha256: b314251d957b16c71ec241119ac09b9530c5c4ce140026ec98d297f55d6c5e08 - md5: 19b0151ecb1d122f706ebd2f82f9a017 - depends: - - __unix - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - run_exports: {} - size: 3096495 - timestamp: 1785375361053 -- conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_osx-arm64-16.1.0-h2713bec_101.conda - sha256: 12ae436b386718bb7cc1116d2280955876b7b50756f953a906468c137b9e8f5d - md5: d6fa28799d42103b71788d401fa272e2 - depends: - - __unix - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - run_exports: {} - size: 2130892 - timestamp: 1785374444424 -- conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-14.4.0-ha5b54cb_101.conda - sha256: 726c006d8f078a68b15d715b64e8c72a4b10907fa1a812dfcaef9ed888c03480 - md5: 774c4627558281a7f6f7ae7b39d6fac1 - depends: - - __unix - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - run_exports: {} - size: 19516469 - timestamp: 1785374476067 -- conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-16.1.0-h41cdd0d_101.conda - sha256: c1521172f2fdf5510d79b621ea835064209fe3131518e0d8b2b43362a75e7b4c - md5: 8593636203272a748b63d56cbd674753 - depends: - - __unix - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - run_exports: {} - size: 22519609 - timestamp: 1785375386152 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-assertthat-0.2.1-r44hc72bb7e_6.conda - sha256: 7ce5cad4870512fce1cec74f88673ea84ba0e4b00b872f4c0b1a937b44690e71 - md5: a9e34b8723a9a38a0fb18746ad30a546 - depends: - - r-base >=4.4,<4.5.0a0 - license: GPL-3.0-only - license_family: GPL3 - run_exports: {} - size: 72978 - timestamp: 1757447415952 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-bh-1.87.0_1-r44hc72bb7e_1.conda - sha256: 564bb989ec8dbf572876e9cd1c84de670c5168a7bb107d2043fc33cf5a80c183 - md5: b7c43e2c7fd3f9b9599123ea9a4be21d - depends: - - r-base >=4.4,<4.5.0a0 - license: BSL-1.0 - license_family: OTHER - run_exports: {} - size: 11611316 - timestamp: 1757467893201 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-callr-3.7.6-r44hc72bb7e_2.conda - sha256: e85f816c249baa83e8bbfd8aa15ef9249151d9aaae69c5010ea209e8d1fa3ff9 - md5: 020dde50545870904c62062ffab4b621 - depends: - - r-base >=4.4,<4.5.0a0 - - r-processx >=3.4.0 - - r-r6 - license: MIT - license_family: MIT - run_exports: {} - size: 454467 - timestamp: 1757475630913 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-clipr-0.8.1-r44hc72bb7e_0.conda - sha256: 76885d94ceca6385e0dc429eef07eade090b55d3eafdaf352b0f08f710a53a90 - md5: bdfd9e2af6b3570288c989ae0646d3ca - depends: - - r-base >=4.4,<4.5.0a0 - license: GPL-3.0-only - license_family: GPL3 - run_exports: {} - size: 73340 - timestamp: 1779692451417 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-cowplot-1.2.0-r44hc72bb7e_2.conda - sha256: 907ebee50b156c5e6d525c96e3d0e209e4ada6872bf114efb1764e6c9b187d7d - md5: b718aae0cdf42ba260463538a492d33d - depends: - - r-base >=4.4,<4.5.0a0 - - r-ggplot2 >=3.5.2 - - r-gtable - - r-rlang - - r-scales - license: GPL-2.0-only - license_family: GPL2 - run_exports: {} - size: 1315162 - timestamp: 1757516606330 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-cpp11-0.5.5-r44h785f33e_0.conda - sha256: 58eaa9586c61a3f1adbbe84ef4d059c78a6eca282708c2c4e34878efff19f5ab - md5: a7c91479f77c69bc54216d97b918028d - depends: - - r-base >=4.4,<4.5.0a0 - license: MIT - license_family: MIT - run_exports: {} - size: 248360 - timestamp: 1778089255619 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-crayon-1.5.3-r44hc72bb7e_2.conda - sha256: 93bb26deef54d51293c3582cfbb51987a7af6ca93d498c4ffbe8ebdcfe52cad0 - md5: 2b72da5cca65460ba6ac1f81b4ff72cc - depends: - - r-base >=4.4,<4.5.0a0 - license: MIT - license_family: MIT - run_exports: {} - size: 167903 - timestamp: 1757452343297 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-desc-1.4.3-r44hc72bb7e_2.conda - sha256: 0b511eadd8299b370b23f45268a9f3de9015457913e86e373fc34184e65c155e - md5: 24fe88af88fc79dd249b6ffdbd32af06 - depends: - - r-base >=4.4,<4.5.0a0 - - r-cli - - r-r6 - - r-rprojroot - license: MIT - license_family: MIT - run_exports: {} - size: 341014 - timestamp: 1757463156255 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-evaluate-1.0.5-r44hc72bb7e_1.conda - sha256: f7d36f6a5d47c637a08af09878d60559dd13147ef2d725837b3b8e2411c47f0a - md5: 7b03982e4e8e348f02a51e61e6bb9cc2 +- conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda + sha256: e1a9e3b1c8fe62dc3932a616c284b5d8cbe3124bbfbedcf4ce5c828cb166ee19 + md5: 9614359868482abba1bd15ce465e3c42 depends: - - r-base >=4.4,<4.5.0a0 + - python >=3.10 license: MIT license_family: MIT + purls: + - pkg:pypi/iniconfig?source=hash-mapping run_exports: {} - size: 111297 - timestamp: 1757447669350 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-generics-0.1.4-r44hc72bb7e_1.conda - sha256: 82a8f7ee79ff61f2378dab81b81ecbc7745b01f9be5df78f6f0e073a53f507af - md5: a593d8a24e2d841863085e8c5b50b9fa + size: 13387 + timestamp: 1760831448842 +- conda: https://conda.anaconda.org/conda-forge/noarch/legacy-api-wrap-1.5-pyhd8ed1ab_0.conda + sha256: 680ae16bf64fc2023601415b5841f297baa269714ee14fff452941d77b73112c + md5: 0f67063cc1769bcb0e4e5ecbca22f8b6 depends: - - r-base >=4.4,<4.5.0a0 - license: MIT - license_family: MIT - run_exports: {} - size: 89321 - timestamp: 1757455977099 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-getopt-1.21.1-r44ha770c72_0.conda - sha256: 510cd24f85ffc12255d2835222ec73a4fc5f0a06156149b391c257a020db3617 - md5: 269f86f4e57541661f30727e0334f074 - depends: - - r-base >=4.4,<4.5.0a0 - license: GPL-2.0-or-later - license_family: GPL3 - run_exports: {} - size: 67089 - timestamp: 1777325951139 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-ggplot2-4.0.3-r44h785f33e_0.conda - sha256: 56e4cf8d4d759af520c372ce229cc0e732377c641682cb0eaae620e15856116f - md5: 57e8127cadd9933ddbd38fdf1d9d52e1 - depends: - - r-base >=4.4,<4.5.0a0 - - r-cli - - r-glue - - r-gtable >=0.3.6 - - r-isoband - - r-lifecycle >=1.0.1 - - r-rlang >=1.1.0 - - r-s7 - - r-scales >=1.4.0 - - r-vctrs >=0.6.0 - - r-withr >=2.5.0 - license: MIT - license_family: MIT - run_exports: {} - size: 7802183 - timestamp: 1776860388013 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-gtable-0.3.6-r44hc72bb7e_1.conda - sha256: e7621253555552d6c29103a8e21c0396712b5de09012263e882bfa0b1e59c812 - md5: 1f5b8f9f0cdb53b843c4e2481e22c8c0 - depends: - - r-base >=4.4,<4.5.0a0 - - r-cli - - r-glue - - r-lifecycle - - r-rlang - license: MIT - license_family: MIT - run_exports: {} - size: 228683 - timestamp: 1757463528706 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-hms-1.1.4-r44hc72bb7e_0.conda - sha256: 5a016b1533a9dc39c66f61209b0a70979289f18cbeace0377b3dfdb63c69465f - md5: 6bb21cd6a482d5c9bb068368a018e750 - depends: - - r-base >=4.4,<4.5.0a0 - - r-ellipsis - - r-lifecycle - - r-pkgconfig - - r-rlang - - r-vctrs >=0.2.1 - license: MIT - license_family: MIT + - python >=3.10 + license: MPL-2.0 + license_family: MOZILLA + purls: + - pkg:pypi/legacy-api-wrap?source=hash-mapping + run_exports: {} + size: 15868 + timestamp: 1762187673390 +- conda: https://conda.anaconda.org/conda-forge/noarch/mudata-0.4.1-pyhd8ed1ab_0.conda + sha256: c9d39ce227478fef04ea839d1b2f0744a2933d93da2f9fdb57ce66588b2f92fc + md5: b5cffe579ad4848a2a2d51d479026ee9 + depends: + - anndata >=0.11 + - h5py + - numpy + - pandas >=1.4 + - python >=3.12 + - scipy + - scverse-misc >=0.1.1 + - session-info2 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/mudata?source=hash-mapping run_exports: {} - size: 112923 - timestamp: 1760687917284 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-labeling-0.4.3-r44hc72bb7e_2.conda - sha256: e2fe5839dce12c1e7fffdfb83387ae58c93417acb520cd258e8460cc7da533db - md5: 891625b27729556fcac5cb5a3040b10f + size: 44359 + timestamp: 1787038276820 +- conda: https://conda.anaconda.org/conda-forge/noarch/natsort-8.4.0-pyhcf101f3_2.conda + sha256: aeb1548eb72e4f198e72f19d242fb695b35add2ac7b2c00e0d83687052867680 + md5: e941e85e273121222580723010bd4fa2 depends: - - r-base >=4.4,<4.5.0a0 - license: MIT - license_family: MIT - run_exports: {} - size: 70089 - timestamp: 1757456017204 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-lifecycle-1.0.5-r44hc72bb7e_0.conda - sha256: 872d78ae8959050dd75e20633a33943e1a93244909bbc20fb65ca6864d6ee694 - md5: 95dd0aa693924627e51f312be131e942 - depends: - - r-base >=4.4,<4.5.0a0 - - r-cli >=3.4.0 - - r-glue - - r-rlang >=1.0.6 - license: MIT - license_family: GPL3 - run_exports: {} - size: 132074 - timestamp: 1767865659674 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-munsell-0.5.1-r44hc72bb7e_2.conda - sha256: dc7e6deee2782e64e061976cb895f40e95105bd99b297af83ab4a7c33b17fff0 - md5: 79407650271d42d8aadfe756f89521c7 - depends: - - r-base >=4.4,<4.5.0a0 - - r-colorspace + - python >=3.9 + - python license: MIT license_family: MIT + purls: + - pkg:pypi/natsort?source=hash-mapping run_exports: {} - size: 247057 - timestamp: 1757455963570 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-optparse-1.8.2-r44hc72bb7e_0.conda - sha256: 44c322a73600f2f1d8a692678f36825aae652c7d5e01a515d0d40542ba311f46 - md5: 23363abab9212ac2e0ee10ab0f9bb92e - depends: - - r-base >=4.4,<4.5.0a0 - - r-getopt >=1.20.2 - license: GPL-2.0-or-later - license_family: GPL2 - run_exports: {} - size: 119837 - timestamp: 1776419497661 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-pillar-1.11.1-r44hc72bb7e_0.conda - sha256: 10739851530bc98feff7bf68acd2285dfddc7a8587b33c51a495f014744a3f33 - md5: 89635525b35ad6b443343237b65526ae - depends: - - r-base >=4.4,<4.5.0a0 - - r-cli - - r-crayon >=1.3.4 - - r-ellipsis - - r-fansi - - r-lifecycle - - r-rlang >=0.3.0 - - r-utf8 >=1.1.0 - - r-vctrs >=0.2.0 - license: GPL-3.0-only - license_family: GPL3 - run_exports: {} - size: 629814 - timestamp: 1758149812405 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-pkgbuild-1.4.8-r44hc72bb7e_1.conda - sha256: 9fdfee8ede6ca0dc94e8f11eeaf7808570a050fbf59de96fb83e09f3133bebd2 - md5: 842576a213f96e7abacdc1a5afc42dc8 - depends: - - r-base >=4.4,<4.5.0a0 - - r-callr >=3.2.0 - - r-cli - - r-crayon - - r-desc - - r-prettyunits - - r-r6 - - r-rprojroot - - r-withr >=2.1.2 - license: GPL-3.0-only - license_family: GPL3 - run_exports: {} - size: 221541 - timestamp: 1757496269823 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-pkgconfig-2.0.3-r44hc72bb7e_5.conda - sha256: 6845c972210ee39fed0db7326685067cd213fc8d1136e0a2060063f3921ee494 - md5: bdc0f46c7111e1d4f7eb7133374b47b7 + size: 39262 + timestamp: 1770905275632 +- conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda + sha256: c432626b16768b8dab228bfb706f7060c2d462a21c516d240f68f2f902b5a044 + md5: 936687ed80f295a1f5dbcf8bd34c252c depends: - - r-base >=4.4,<4.5.0a0 - license: MIT - license_family: MIT - run_exports: {} - size: 27374 - timestamp: 1757447594870 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-pkgload-1.5.3-r44hc72bb7e_0.conda - sha256: 9bab6c6330b92e7b4fb5037994da88368405c4f03d1e6982065da1fc425e7c17 - md5: 2882ea0da6144447729f7836116c74fd - depends: - - r-base >=4.4,<4.5.0a0 - - r-cli >=3.3.0 - - r-desc - - r-fs - - r-glue - - r-lifecycle - - r-pkgbuild - - r-processx - - r-rlang >=1.1.1 - - r-rprojroot - - r-withr >=2.4.3 - license: GPL-3.0-only - license_family: GPL3 + - python >=3.9 + - python + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/packaging?source=hash-mapping run_exports: {} - size: 243363 - timestamp: 1781541382323 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-praise-1.0.0-r44hc72bb7e_1009.conda - sha256: ab412aae97116199eca6ca377bef26aed160e835a9920a4764e160bd25b02233 - md5: 3be32de189a039db4fc08888bd95c2b8 + size: 116363 + timestamp: 1785888127370 +- conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda + sha256: e14aafa63efa0528ca99ba568eaf506eb55a0371d12e6250aaaa61718d2eb62e + md5: d7585b6550ad04c8c5e21097ada2888e depends: - - r-base >=4.4,<4.5.0a0 + - python >=3.9 + - python license: MIT license_family: MIT - run_exports: {} - size: 25896 - timestamp: 1757447349076 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-prettyunits-1.2.0-r44hc72bb7e_2.conda - sha256: cbee3643f2e5b45e52921f5a6246f572cadf5dcf540d352bbc0ff4ad0a96aa4f - md5: d0ddc1e17050afcb5f13ecdc1f4aba92 - depends: - - r-assertthat - - r-base >=4.4,<4.5.0a0 - - r-magrittr + purls: + - pkg:pypi/pluggy?source=hash-mapping + run_exports: {} + size: 25877 + timestamp: 1764896838868 +- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.5-pyhcf101f3_0.conda + sha256: 701ed8122021f62e1c9a46bdf4932ada911c10afa56ba0459f56da940bcbe5a7 + md5: 2d0f9304fc1cb1e3e94e7b37c14cd70a + depends: + - typing-inspection >=0.4.2 + - typing_extensions >=4.14.1 + - python >=3.10 + - annotated-types >=0.6.0 + - pydantic-core ==2.46.5 + - python license: MIT license_family: MIT - run_exports: {} - size: 161041 - timestamp: 1757463122545 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-progress-1.2.3-r44hc72bb7e_2.conda - sha256: 23cf5fe12d5344af6dcc74d89504dee70b2534c62dee48b03515e44c27465c45 - md5: a6a93fa6ae11444ea50d897f35db7e5d - depends: - - r-base >=4.4,<4.5.0a0 - - r-crayon - - r-hms - - r-prettyunits - - r-r6 + purls: + - pkg:pypi/pydantic?source=compressed-mapping + run_exports: {} + size: 346633 + timestamp: 1787941273167 +- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.15.0-pyhcf101f3_0.conda + sha256: 674e1ba12010a88441f817a318d3cec7e7a7682813ebd3f427fbd1c6766a16af + md5: 687adac0b3eb2dd73762e9f912b4549e + depends: + - typing-inspection >=0.4.0 + - python >=3.10 + - pydantic >=2.7.0 + - python-dotenv >=0.21.0 + - python license: MIT license_family: MIT + purls: + - pkg:pypi/pydantic-settings?source=compressed-mapping run_exports: {} - size: 96089 - timestamp: 1757484966329 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-r6-2.6.1-r44hc72bb7e_1.conda - sha256: 6cd12790c46f54c67dba4a08961c69cc82f025f2ba7a30f8fae3940d81def8bd - md5: a6809636e421b99a22d374a752c1a2d6 + size: 60984 + timestamp: 1786146776076 +- conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.21.0-pyhcf101f3_0.conda + sha256: f5f015ff1bc3e1b7fc08eee096b1865234189ae0b82bebdb86a5369116e42fa0 + md5: 2882dee445dfa45b0c5afce3ffb7730a depends: - - r-base >=4.4,<4.5.0a0 + - python >=3.10 + - python + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/pygments?source=compressed-mapping + run_exports: {} + size: 959376 + timestamp: 1786995678795 +- 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: 94729 - timestamp: 1757447588692 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-rcolorbrewer-1.1_3-r44h785f33e_4.conda - sha256: 7fe0b7282c290b8267d093cdfb4ae093d4a5fdf65f97fdac1d6db998bb96bc32 - md5: bbaae25579d5cea71278441c0b61fde3 - depends: - - r-base >=4.4,<4.5.0a0 + purls: + - pkg:pypi/pytest?source=hash-mapping + run_exports: {} + size: 306724 + timestamp: 1782127176429 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + sha256: d6a17ece93bbd5139e02d2bd7dbfa80bee1a4261dced63f65f679121686bf664 + md5: 5b8d21249ff20967101ffa321cab24e8 + depends: + - python >=3.9 + - six >=1.5 + - python license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/python-dateutil?source=hash-mapping run_exports: {} - size: 67302 - timestamp: 1757452497965 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-rematch2-2.1.2-r44hc72bb7e_5.conda - sha256: dbce30e4ef2ffae2bb3cff19ca247cac23a863e7c7db7ca407d6c25552e56944 - md5: 9c13ea9c0d41379ad6deee9e0c3932e9 - depends: - - r-base >=4.4,<4.5.0a0 - - r-tibble - license: MIT - license_family: MIT - run_exports: {} - size: 56015 - timestamp: 1757496162089 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-remotes-2.5.0-r44hc72bb7e_2.conda - sha256: 1f4d85feeeab79048cde675287b65e74ee56f0f1df3e9ed537649a735634e28d - md5: af39b8f71e6979bd00e9ef71e40bed1e - depends: - - r-base >=4.4,<4.5.0a0 - license: GPL-2.0-or-later - license_family: GPL3 - run_exports: {} - size: 436897 - timestamp: 1757451587405 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-rprojroot-2.1.1-r44hc72bb7e_1.conda - sha256: 23db8cfaf9c46b48fe8969c716887972f2d243e60268cca6999fdf1402b89f87 - md5: 51b5479ff88c1260e80ec9195189be13 + size: 233310 + timestamp: 1751104122689 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.2.3-pyhcf101f3_0.conda + sha256: ad7b2dd059c1b693a09f799cbaf4b9d06ec7677c02c9447be98e0b33a49bc04c + md5: b55edb60d527ce56226a1026abcb3e2c depends: - - r-base >=4.4,<4.5.0a0 - license: MIT - license_family: MIT + - python >=3.10 + - python + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/python-dotenv?source=compressed-mapping run_exports: {} - size: 116982 - timestamp: 1757447577256 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-scales-1.4.0-r44hc72bb7e_1.conda - sha256: 4e28620ef9c4777bb165f87404466652baac788ccf40904d148ebd27430baa4f - md5: d5a3c79508fd7f6be657f69119265398 - depends: - - r-base >=4.4,<4.5.0a0 - - r-farver >=2.0.0 - - r-labeling - - r-lifecycle - - r-munsell >=0.5 - - r-r6 - - r-rcolorbrewer - - r-viridislite - license: MIT - license_family: MIT + size: 28328 + timestamp: 1787003234261 +- conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-9_cp312.conda + build_number: 9 + sha256: ee9c2922e07afc85fc82d5fa82c9ac2c79da3be3283a5e17bf2f2ae38dbd02fb + md5: 4c32076993e6270825441d059ab5c18b + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: {} + size: 6751 + timestamp: 1788302823694 +- conda: https://conda.anaconda.org/conda-forge/noarch/scverse-misc-0.1.6-pyhc364b38_0.conda + sha256: 877ce46c989725e8222d04d4a48cf514320e5ad205fae2113afd6f03eb3c6663 + md5: 92a11adeee6050707b1be302f1748cc5 + depends: + - python >=3.12 + - session-info2 + - typing_extensions + - python + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scverse-misc?source=compressed-mapping run_exports: {} - size: 780906 - timestamp: 1757487429584 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-tidyselect-1.2.1-r44hc72bb7e_2.conda - sha256: 11958a4c8f9e414aa6d7a9d5fc1161423258fce9ab9a19f23f8957dc83688470 - md5: d51e16d0d45d1f5a21f74a9562ffd37d - depends: - - r-base >=4.4,<4.5.0a0 - - r-cli >=3.3.0 - - r-glue >=1.3.0 - - r-lifecycle >=1.0.3 - - r-rlang >=1.0.4 - - r-vctrs >=0.5.2 - - r-withr + size: 31251 + timestamp: 1789207356173 +- conda: https://conda.anaconda.org/conda-forge/noarch/session-info2-0.4.2-pyhd8ed1ab_0.conda + sha256: acb94fa16691e0ebf23cb0b5f12c836cca2984a2278d46cab5e8b869c0040b24 + md5: b23c403fbe4282746ac475bb2e098b26 + depends: + - python >=3.12 + license: MPL-2.0 + license_family: MOZILLA + purls: + - pkg:pypi/session-info2?source=hash-mapping + run_exports: {} + size: 28094 + timestamp: 1786093213770 +- conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda + sha256: 458227f759d5e3fcec5d9b7acce54e10c9e1f4f4b7ec978f3bfd54ce4ee9853d + md5: 3339e3b65d58accf4ca4fb8748ab16b3 + depends: + - python >=3.9 + - python license: MIT license_family: MIT + purls: + - pkg:pypi/six?source=hash-mapping run_exports: {} - size: 219708 - timestamp: 1757475755088 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-viridislite-0.4.3-r44hc72bb7e_0.conda - sha256: 4b81c43659edb19d326787aed0593a02d2c083e2f34b817d0a1855259c570c53 - md5: 7cd715023440607bb66311acf346b124 + size: 18455 + timestamp: 1753199211006 +- conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda + sha256: 91cafdb64268e43e0e10d30bd1bef5af392e69f00edd34dfaf909f69ab2da6bd + md5: b5325cf06a000c5b14970462ff5e4d58 depends: - - r-base >=4.4,<4.5.0a0 + - python >=3.10 + - python license: MIT license_family: MIT - run_exports: {} - size: 1304861 - timestamp: 1770191459985 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-waldo-0.6.2-r44hc72bb7e_1.conda - sha256: 1df50e9e24eebacb023a3ce10ad8dbc2e72f4ececef481173b8b3af64486b98d - md5: af452397f5133a113606b71049cc2f78 - depends: - - r-base >=4.4,<4.5.0a0 - - r-cli - - r-diffobj >=0.3.4 - - r-fansi - - r-glue - - r-rematch2 - - r-rlang >=1.0.0 - - r-tibble + purls: + - pkg:pypi/tomli?source=hash-mapping + run_exports: {} + size: 21561 + timestamp: 1774492402955 +- 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-inspection-0.4.4-pyhcf101f3_0.conda + sha256: 293c66b208468d186a94ff486c2ffbf67859a2bde8c88e965fc9d06c517191b2 + md5: 880e91eac5d926568ef278e20554148e + depends: + - python >=3.10 + - typing_extensions >=4.15.0 + - python license: MIT license_family: MIT - run_exports: {} - size: 144342 - timestamp: 1757501600562 -- conda: https://conda.anaconda.org/conda-forge/noarch/r-withr-3.0.3-r44hc72bb7e_0.conda - sha256: 016bb35ac5d143c59c846c51184e08a1594cf43efc64381753cf9f7fdc14afaf - md5: 9a8c141f710d08ba084adf5e145672b0 - depends: - - r-base >=4.4,<4.5.0a0 - license: GPL-2.0-or-later - license_family: GPL2 - run_exports: {} - size: 235711 - timestamp: 1781856638734 -- conda: https://conda.anaconda.org/conda-forge/noarch/sdkroot_env_osx-arm64-26.0-ha3f98da_7.conda - sha256: fabfe031ede99898cb2b0b805f6c0d64fcc24ecdb444de3a83002d8135bf4804 - md5: 5f0ebbfea12d8e5bddff157e271fdb2f - license: BSD-3-Clause - license_family: BSD - run_exports: {} - size: 4971 - timestamp: 1771434195389 -- conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_9.conda - sha256: c47299fe37aebb0fcf674b3be588e67e4afb86225be4b0d452c7eb75c086b851 - md5: 13dc3adbc692664cd3beabd216434749 - depends: - - __glibc >=2.28 - - kernel-headers_linux-64 4.18.0 he073ed8_9 - - tzdata - license: LGPL-2.0-or-later AND LGPL-2.0-or-later WITH exceptions AND GPL-2.0-or-later - license_family: GPL - run_exports: - strong: - - __glibc >=2.28,<3.0.a0 - size: 24008591 - timestamp: 1765578833462 + purls: + - pkg:pypi/typing-inspection?source=compressed-mapping + run_exports: {} + size: 21070 + timestamp: 1786818918217 +- 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/zarr-3.4.0-pyhc364b38_0.conda + sha256: 4db3753430e25375065c2ecb594388277737155a414af51caa253abee7ac2aa3 + md5: 4079af986ade5294974a3f3d851735e7 + depends: + - python >=3.12 + - packaging >=22.0 + - numpy >=2 + - numcodecs >=0.14 + - typing_extensions >=4.14 + - donfig >=0.8 + - google-crc32c >=1.5 + - msgspec >=0.19 + - python + constrains: + - fsspec >=2023.10.0 + - obstore >=0.5.1 + license: MIT + license_family: MIT + purls: + - pkg:pypi/zarr?source=compressed-mapping + run_exports: {} + size: 498894 + timestamp: 1789719089193 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda build_number: 7 sha256: 7acaa2e0782cad032bdaf756b536874346ac1375745fb250e9bdd6a48a7ab3cd @@ -3730,421 +3798,337 @@ packages: - llvm-openmp >=9.0.1 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - _openmp_mutex >=4.5 size: 8325 timestamp: 1764092507920 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/bwidget-1.10.1-hce30654_1.conda - sha256: 66ccefd46364f1ef536c42e7ee24d0377c2ece073734df614c6509b08e2bdf62 - md5: c42706e35f3bb26537b065a5f9ae764d - depends: - - tk - license: TCL - run_exports: {} - size: 129989 - timestamp: 1750261536876 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda - sha256: 8ec22f0ba25cbfc2e64d70cf29459eccd7ffdf6436f6a6ff15bbfef799f7d4f6 - md5: b50612e7d190b8061ab4e7dc119cf4d5 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.10.4-h0f08c97_1.conda + sha256: 8e503ab875683720c4ddcce216ad2aa96c32b90bc59e9a692c694e07ba4e5d8d + md5: c5d9f5796fb392f0984def10b26f1175 depends: - __osx >=11.0 - license: bzip2-1.0.6 - license_family: BSD + - aws-c-http >=0.11.0,<0.11.1.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + - aws-c-sdkutils >=0.2.7,<0.2.8.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] run_exports: weak: - - bzip2 >=1.0.8,<2.0a0 - size: 124965 - timestamp: 1785906749812 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.8-h1a92334_1.conda - sha256: 104b41473845649101ba8ebf8221c7431256465d34ce380b10e9a90558ed33ac - md5: 7d9390a4d4b43f91652823d870f68065 + - aws-c-auth >=0.10.4,<0.10.5.0a0 + size: 117350 + timestamp: 1784086893887 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.14-hb9ed60c_4.conda + sha256: 9760cd6c5cc16ecd989866e86fea1fbe23bff3279831ce6f13b83afb6f6f2075 + md5: 5f5ba0cd72e15095ede1ad38e8907f5f depends: - __osx >=11.0 - constrains: - - c-ares-static <0a0 - license: MIT - license_family: MIT + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: Apache + purls: [] run_exports: weak: - - c-ares >=1.34.8,<2.0a0 - size: 197274 - timestamp: 1786116660078 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-compiler-1.11.0-h61f9b84_0.conda - sha256: b51bd1551cfdf41500f732b4bd1e4e70fb1e74557165804a648f32fa9c671eec - md5: 148516e0c9edf4e9331a4d53ae806a9b - depends: - - cctools >=949.0.1 - - clang_osx-arm64 19.* - - ld64 >=530 - - llvm-openmp - license: BSD-3-Clause - license_family: BSD - run_exports: {} - size: 6697 - timestamp: 1753098737760 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-he0f2337_1.conda - sha256: cde9b79ee206fe3ba6ca2dc5906593fb7a1350515f85b2a1135a4ce8ec1539e3 - md5: 36200ecfbbfbcb82063c87725434161f + - aws-c-cal >=0.9.14,<0.9.15.0a0 + size: 45835 + timestamp: 1784043800544 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.14.2-h84a0fba_0.conda + sha256: 0cc9a2a36387975a643cc33d7b60db894650efa707ff73a85e93f80f03904c97 + md5: 35d52f06a5eaaa66c62dd37a4d82d780 depends: - __osx >=11.0 - - fontconfig >=2.15.0,<3.0a0 - - fonts-conda-ecosystem - - icu >=78.1,<79.0a0 - - libcxx >=19 - - libexpat >=2.7.3,<3.0a0 - - libfreetype >=2.14.1 - - libfreetype6 >=2.14.1 - - libglib >=2.86.3,<3.0a0 - - libpng >=1.6.53,<1.7.0a0 - - libzlib >=1.3.1,<2.0a0 - - pixman >=0.46.4,<1.0a0 - license: LGPL-2.1-only or MPL-1.1 + license: Apache-2.0 + license_family: Apache + purls: [] run_exports: weak: - - cairo >=1.18.4,<2.0a0 - size: 900035 - timestamp: 1766416416791 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cctools-1030.6.3-llvm19_1_hd01ab73_5.conda - sha256: c72058e16dc45c3e565613bdf7c075354963e4b869a3ff76be4d0a6160c7ed76 - md5: 4ac48b22145b0cf5fcb9cbb48d48733b - depends: - - cctools_impl_osx-arm64 1030.6.3 llvm19_1_hc7668c6_5 - - ld64 956.6 llvm19_1_he86490a_5 - - libllvm19 >=19.1.7,<19.2.0a0 - license: APSL-2.0 - license_family: Other - run_exports: {} - size: 24481 - timestamp: 1785270932465 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cctools-1030.6.3-llvm22_1_hbe26303_5.conda - sha256: c2431becc11a32e157a1c22c44f368773c009e298ba0127c89e614c8d3493a36 - md5: ee35bbae0edb9cc3b69445b0e8f90773 - depends: - - cctools_impl_osx-arm64 1030.6.3 llvm22_1_h7bf7afb_5 - - ld64 956.6 llvm22_1_h5b97f1b_5 - - libllvm22 >=22.1.8,<22.2.0a0 - license: APSL-2.0 - license_family: Other - run_exports: {} - size: 24351 - timestamp: 1785270934554 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cctools_impl_osx-arm64-1030.6.3-llvm19_1_hc7668c6_5.conda - sha256: 2cae8beaf880fbf92ccd4534e6ba0463259512587bb6cc0128798ca5c4305d2c - md5: 5fbea6ce6ff8af995623a85b5215df26 + - aws-c-common >=0.14.2,<0.14.3.0a0 + size: 228084 + timestamp: 1783637822478 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.2-hf0b4b85_4.conda + sha256: 8363308db46a84c5d7e9a309aaca8a18541f46f3b6e7b7027479a95ab910e19a + md5: 6e0883cca4143f456e019f751d0b9f3b depends: - __osx >=11.0 - - ld64_osx-arm64 >=956.6,<956.7.0a0 - - libcxx - - libllvm19 >=19.1.7,<19.2.0a0 - - libzlib >=1.3.2,<2.0a0 - - llvm-tools 19.1.* - - sigtool-codesign - constrains: - - ld64 956.6.* - - clang 19.1.* - - cctools 1030.6.3.* - license: APSL-2.0 - license_family: Other - run_exports: {} - size: 748451 - timestamp: 1785270905693 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cctools_impl_osx-arm64-1030.6.3-llvm22_1_h7bf7afb_5.conda - sha256: 4e973a6236849d4f52b4463609654980ce60e6cd3cad71de9480f63ff9767548 - md5: 65296f920674a115310cc3f3ce1bc8fc + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - aws-c-compression >=0.3.2,<0.3.3.0a0 + size: 21774 + timestamp: 1784042939907 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.7.1-ha581b6a_4.conda + sha256: d1742afa3329b3ea892b5f0a75b36b4217a7c3eb43250e827c56f122343e9317 + md5: 4ce3e032bfa6d72114e481c2dc99e2e2 depends: + - libcxx >=19 - __osx >=11.0 - - ld64_osx-arm64 >=956.6,<956.7.0a0 - - libcxx - - libllvm22 >=22.1.8,<22.2.0a0 - - libzlib >=1.3.2,<2.0a0 - - llvm-tools 22.1.* - - sigtool-codesign - constrains: - - ld64 956.6.* - - cctools 1030.6.3.* - - clang 22.1.* - license: APSL-2.0 - license_family: Other - run_exports: {} - size: 752126 - timestamp: 1785270918177 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cctools_osx-arm64-1030.6.3-llvm19_1_hd01ab73_5.conda - sha256: dcaffeb8cef0915519488d8ed767e9fec5af23598de0737ef3bc4256339d9382 - md5: acb70bf225797b6faf8d14e56f201074 - depends: - - cctools_impl_osx-arm64 1030.6.3 llvm19_1_hc7668c6_5 - - ld64_osx-arm64 956.6 llvm19_1_ha2625f7_5 - constrains: - - cctools 1030.6.3.* - license: APSL-2.0 - license_family: Other - run_exports: {} - size: 23652 - timestamp: 1785270935447 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cctools_osx-arm64-1030.6.3-llvm22_1_hbe26303_5.conda - sha256: 4c3231dad1abca8b04b8ddbb1cdbad6fe30d8573b18797c8a752afd4d1560d90 - md5: 6426ea39a027d0fb8013521b40b1b095 - depends: - - cctools_impl_osx-arm64 1030.6.3 llvm22_1_h7bf7afb_5 - - ld64_osx-arm64 956.6 llvm22_1_h692d5aa_5 - constrains: - - cctools 1030.6.3.* - license: APSL-2.0 - license_family: Other - run_exports: {} - size: 23619 - timestamp: 1785270937403 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/clang-19-19.1.7-default_hf3020a7_9.conda - sha256: a1449c64f455d43153036f54c68cb075a52c1d9f3350a91f4a8936ecf1675c6b - md5: 5a77d772c22448f6ab340fbfff55db48 + - aws-checksums >=0.2.10,<0.2.11.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - aws-c-event-stream >=0.7.1,<0.7.2.0a0 + size: 54275 + timestamp: 1784075773654 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.11.0-h27f0cb5_4.conda + sha256: 851a01b9e6f0078107549ec1d1545cd27a50a073e4e9599d6f8f4ba69a8cba30 + md5: 9e435c44eafb6aba85f0133f4c579d1c depends: - __osx >=11.0 - - libclang-cpp19.1 19.1.7 default_hf3020a7_9 - - libcxx >=19.1.7 - - libllvm19 >=19.1.7,<19.2.0a0 - license: Apache-2.0 WITH LLVM-exception - license_family: Apache - run_exports: {} - size: 763361 - timestamp: 1776988759708 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/clang-19.1.7-default_hf9bcbb7_9.conda - sha256: 8268c23a000cfeee1b83e19c59eb018ec07583905f69bfee01beac8aedd8c4df - md5: 20056c993a8c9df01e04a0e165579ec1 - depends: - - cctools - - clang-19 19.1.7.* default_* - - clang_impl_osx-arm64 19.1.7 default_hc11f16d_9 - - ld64 - - ld64_osx-arm64 * llvm19_1_* - license: Apache-2.0 WITH LLVM-exception - license_family: Apache - run_exports: {} - size: 24962 - timestamp: 1776989044302 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/clang-22-22.1.8-default_hed75349_6.conda - sha256: 89c0d72eebd973a3c6bbccd83363968a6cbe0ba01eb79c55783bc7943fa87796 - md5: 8a1a3ac4f1f86f9ff5c63fa6e6c1ef57 - depends: - - compiler-rt22 ==22.1.8 - - libclang-cpp22.1 ==22.1.8 default_hc257da1_6 - - libcxx >=22.1.8 + - aws-c-io >=0.27.3,<0.27.4.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - aws-c-compression >=0.3.2,<0.3.3.0a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - aws-c-http >=0.11.0,<0.11.1.0a0 + size: 177761 + timestamp: 1784075780838 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.27.3-ha24efe8_1.conda + sha256: 2b127f1ccd9f022c945363e1e8eb6fd4a5fbc89677ac678abde6dc022ba26765 + md5: c4d73dc09e972a895d5a3dc7ce158be9 + depends: - __osx >=11.0 - - libzlib >=1.3.2,<2.0a0 - - libxml2 - - libxml2-16 >=2.14.6 - - zstd >=1.5.7,<1.6.0a0 - - libclang-cpp22.1 >=22.1.8,<22.2.0a0 - - libllvm22 >=22.1.8,<22.2.0a0 - license: Apache-2.0 WITH LLVM-exception + - s2n >=1.7.5,<1.7.6.0a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 license_family: APACHE - run_exports: {} - size: 924920 - timestamp: 1786527707120 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/clang-22.1.8-default_cfg_hbc7c751_6.conda - sha256: e5e09704a7b39e001a7aae3cd13e8d6f0161c75cdc58e5a82d46328730792cf8 - md5: 87a36460157785b995bf8d6e4a5025dd - depends: - - clang-22 ==22.1.8 default_* - - llvm-openmp >=22.1.8 - - clang_impl_osx-arm64 ==22.1.8 default_hc257da1_6 - - cctools - - ld64 - - ld64_osx-arm64 * llvm22_1_* - - llvm-tools ==22.1.8 + purls: [] + run_exports: + weak: + - aws-c-io >=0.27.3,<0.27.4.0a0 + size: 189938 + timestamp: 1784054954272 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.16.0-hf1a914d_2.conda + sha256: b352abb6f7cd42bdbbdef647409cf0d94f6edc841151dc3405bf3023b761e65b + md5: 742911742b564828dbaaffaa0f1a21eb + depends: - __osx >=11.0 - license: Apache-2.0 WITH LLVM-exception + - aws-c-common >=0.14.2,<0.14.3.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + - aws-c-http >=0.11.0,<0.11.1.0a0 + license: Apache-2.0 license_family: APACHE - run_exports: {} - size: 32177 - timestamp: 1786527707120 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/clang-scan-deps-22.1.8-default_hc257da1_6.conda - sha256: d5ab08fccabeb9b0e081b0303ed613fa91c0210878c50d4f05cd9d6e07f6e348 - md5: da854da44e32397c0eedae9957259326 - depends: - - libclang13 >=22.1.8 - - libclang-cpp22.1 >=22.1.8,<22.2.0a0 - - libcxx >=22.1.8 + purls: [] + run_exports: + weak: + - aws-c-mqtt >=0.16.0,<0.16.1.0a0 + size: 158513 + timestamp: 1784087664452 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.12.8-hdc61527_1.conda + sha256: f56014c02923a32cb3387eab782420ff40c9aae68c95cf08bfd2143a5c3c2cdd + md5: ea0e1e78de6375004530dd9f49d7ae46 + depends: - __osx >=11.0 - - libzlib >=1.3.2,<2.0a0 - - libxml2 - - libxml2-16 >=2.14.6 - - zstd >=1.5.7,<1.6.0a0 - - libllvm22 >=22.1.8,<22.2.0a0 - license: Apache-2.0 WITH LLVM-exception + - aws-c-http >=0.11.0,<0.11.1.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + - aws-c-auth >=0.10.4,<0.10.5.0a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + - aws-checksums >=0.2.10,<0.2.11.0a0 + license: Apache-2.0 license_family: APACHE - run_exports: {} - size: 117238 - timestamp: 1786527707120 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/clang_impl_osx-arm64-19.1.7-default_hc11f16d_9.conda - sha256: 56db3a98eda7032a0aefe38f146a4b29df9d75d08c71bf7f7d6412effe775dd1 - md5: 2aec2e39be3b4999bda2a3e5bd4cd2e6 - depends: - - cctools_impl_osx-arm64 - - clang-19 19.1.7.* default_* - - compiler-rt 19.1.7.* - - compiler-rt_osx-arm64 - - ld64_osx-arm64 * llvm19_1_* - - llvm-openmp >=19.1.7 - - llvm-tools 19.1.7.* - license: Apache-2.0 WITH LLVM-exception - license_family: Apache - run_exports: {} - size: 24905 - timestamp: 1776989025990 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/clang_impl_osx-arm64-22.1.8-default_hc257da1_6.conda - sha256: a31dd186791d4eed3ec8a40cb2aebdfefd07a66f47739790a3ab87c6d0336a50 - md5: 3c3886653db4f1fd24ff9ce536e606b1 - depends: - - clang-22 ==22.1.8 default_* - - compiler-rt_osx-arm64 ==22.1.8 - - compiler-rt ==22.1.8 - - cctools_impl_osx-arm64 - - ld64_osx-arm64 * llvm22_1_* + purls: [] + run_exports: + weak: + - aws-c-s3 >=0.12.8,<0.12.9.0a0 + size: 133754 + timestamp: 1784101134287 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.7-hf0b4b85_2.conda + sha256: 3ee2dcefcd45283508a3049bd4f5b508a46c16af906a0c3d351d0f9d81738464 + md5: de81e53b45b103470d3be61984dcc292 + depends: - __osx >=11.0 - - libzlib >=1.3.2,<2.0a0 - - libxml2 - - libxml2-16 >=2.14.6 - - zstd >=1.5.7,<1.6.0a0 - license: Apache-2.0 WITH LLVM-exception + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 license_family: APACHE - run_exports: {} - size: 31736 - timestamp: 1786527707120 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/clang_osx-arm64-19.1.7-h75f8d18_34.conda - sha256: 307875e8c6e41c59adc5aea0ac2e4980e2f85574b98738936810dac2b30e9d52 - md5: a687765928cf0ad08ba9786f1da5c59b - depends: - - cctools_osx-arm64 - - clang 19.* - - clang_impl_osx-arm64 19.1.7.* - - sdkroot_env_osx-arm64 - license: BSD-3-Clause - license_family: BSD - run_exports: {} - size: 20718 - timestamp: 1785272092513 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/clang_osx-arm64-22.1.8-hc95f77d_34.conda - sha256: a9b0fab88fee7ecba58d56f31a0a8244be7258c7ef764932f04440932ee49850 - md5: 1f4e1b97ecae7a6a73e3eabc59a79b16 - depends: - - cctools_osx-arm64 - - clang_impl_osx-arm64 22.1.8.* - - sdkroot_env_osx-arm64 - license: BSD-3-Clause - license_family: BSD - run_exports: {} - size: 20683 - timestamp: 1785272135295 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/clangxx-19.1.7-default_hc995acf_9.conda - sha256: 88697ecd1e5689e15c12d334bae2bb3900dffd91efd4686cd9eea9e1095ee986 - md5: 9a1ac8e5124fcc201adb20a103d51cc6 - depends: - - clang 19.1.7 default_hf9bcbb7_9 - - clangxx_impl_osx-arm64 19.1.7.* default_* - - libcxx-devel 19.1.* - license: Apache-2.0 WITH LLVM-exception - license_family: Apache - run_exports: {} - size: 24924 - timestamp: 1776989215095 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/clangxx_impl_osx-arm64-19.1.7-default_hc11f16d_9.conda - sha256: 6b5ebc5f369ad5373091edc3d4c4d2e1f39169b7adb080395965646eb8aee7c9 - md5: 8b7425e84f940861653c919142435bde - depends: - - clang-19 19.1.7.* default_* - - clang_impl_osx-arm64 19.1.7 default_hc11f16d_9 - - libcxx-devel 19.1.* - license: Apache-2.0 WITH LLVM-exception - license_family: Apache - run_exports: {} - size: 24861 - timestamp: 1776989199328 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/clangxx_impl_osx-arm64-22.1.8-default_h9cdd5ba_6.conda - sha256: 4bdedff1b5090add04ac4f3d5e75da64785972eda6dddf4b084cd5d2a3518756 - md5: 0b32fafa8c031ddaffcf16ccbfe51597 - depends: - - clang-22 ==22.1.8 default_* - - clang_impl_osx-arm64 ==22.1.8 default_hc257da1_6 - - clang-scan-deps ==22.1.8 default_hc257da1_6 - - libcxx-devel 22.1.* + purls: [] + run_exports: + weak: + - aws-c-sdkutils >=0.2.7,<0.2.8.0a0 + size: 60771 + timestamp: 1784044312402 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.10-hf0b4b85_4.conda + sha256: 13313afb01bf4f1c328695724d5b16263810f84d7572f6cb0b4d12f877d7f1d6 + md5: 99cd08b2a8261083e4ad9414cbe737df + depends: + - __osx >=11.0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - aws-checksums >=0.2.10,<0.2.11.0a0 + size: 92225 + timestamp: 1784044297006 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.40.1-h5b5d287_3.conda + sha256: aa4d21bdc3c891d2dca32121ef543aa7f2d8d83868b347207f04ebf65dd5d65d + md5: e850d4d6e349471c50fbed1f55fd3069 + depends: + - libcxx >=19 + - __osx >=11.0 + - aws-c-s3 >=0.12.8,<0.12.9.0a0 + - aws-c-sdkutils >=0.2.7,<0.2.8.0a0 + - aws-c-io >=0.27.3,<0.27.4.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + - aws-c-event-stream >=0.7.1,<0.7.2.0a0 + - aws-c-auth >=0.10.4,<0.10.5.0a0 + - aws-c-http >=0.11.0,<0.11.1.0a0 + - aws-c-mqtt >=0.16.0,<0.16.1.0a0 + - aws-c-cal >=0.9.14,<0.9.15.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - aws-crt-cpp >=0.40.1,<0.40.2.0a0 + size: 275833 + timestamp: 1784113326407 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.833-haa98fb3_9.conda + sha256: 215efb2e64b92094c3229c7d43177873068d0a088d7e94ff2c1447360f17a6c8 + md5: 1748ae5f4015e2fec7009ff36cbfafa0 + depends: + - libcxx >=19 - __osx >=11.0 + - libcurl >=8.21.0,<9.0a0 + - aws-crt-cpp >=0.40.1,<0.40.2.0a0 - libzlib >=1.3.2,<2.0a0 + - aws-c-event-stream >=0.7.1,<0.7.2.0a0 + - aws-c-common >=0.14.2,<0.14.3.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - aws-sdk-cpp >=1.11.833,<1.11.834.0a0 + size: 2834544 + timestamp: 1784137336612 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.16.3-he5ae378_0.conda + sha256: 9645073c4682ad3a334a2b06c5fc0ddc57fc9f0f537cc3123053634c92c28625 + md5: 4f42d997f42a8d34ff74cb677cf0c828 + depends: + - __osx >=11.0 + - libcurl >=8.19.0,<9.0a0 + - libcxx >=19 + - openssl >=3.5.5,<4.0a0 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + size: 290309 + timestamp: 1775239330289 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.13.3-h05177fb_2.conda + sha256: d950fb513311a15cd4ae663cdacb2c035122f609a9c790973661b38e0882e40e + md5: e1701f6b2ce6f47aeb6cbfab132403d8 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - libcxx >=19 + - openssl >=3.5.7,<4.0a0 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - azure-identity-cpp >=1.13.3,<1.13.4.0a0 + size: 168032 + timestamp: 1781268747743 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.18.0-h409340b_1.conda + sha256: 3f096d7cd6fd7788d57887c3d3e48be1ebc5c3a971666ddc0dd8a3493a5b7cb5 + md5: 0948246cb8e514e4ae1cfe7dbb4046c7 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - azure-storage-common-cpp >=12.14.0,<12.14.1.0a0 + - libcxx >=19 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - azure-storage-blobs-cpp >=12.18.0,<12.18.1.0a0 + size: 436462 + timestamp: 1781284116423 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.14.0-h7cba3ab_1.conda + sha256: 9a1b6e5284f1f242330dfa1e21408ffd823a76df424108d8c319c2a46df61dba + md5: 2af5d1b5365c4a3de8ed8ec8438fe6ec + depends: + - __osx >=11.0 + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - libcxx >=19 - libxml2 - libxml2-16 >=2.14.6 - - zstd >=1.5.7,<1.6.0a0 - license: Apache-2.0 WITH LLVM-exception - license_family: APACHE - run_exports: {} - size: 31881 - timestamp: 1786527707120 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/clangxx_osx-arm64-19.1.7-h75f8d18_34.conda - sha256: 4efe6cbc9134b76785f9e72cbc668ebf2b130f93c5d5a3924212afa99de642ef - md5: 060bf4d7ab209f8e0c70d77d8e7b576c - depends: - - cctools_osx-arm64 - - clang_osx-arm64 19.1.7 h75f8d18_34 - - clangxx 19.* - - clangxx_impl_osx-arm64 19.1.7.* - - sdkroot_env_osx-arm64 - license: BSD-3-Clause - license_family: BSD + - openssl >=3.5.7,<4.0a0 + license: MIT + license_family: MIT + purls: [] run_exports: - strong: - - libcxx >=19 - size: 19429 - timestamp: 1785272095233 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/clangxx_osx-arm64-22.1.8-hc95f77d_34.conda - sha256: db85e6a36a53d88d4922d482bc3072d997bec784144a9feb11ed3635871cc8c9 - md5: 39cd8098b0da45caccb268c0ac3872f6 - depends: - - cctools_osx-arm64 - - clang_osx-arm64 22.1.8 hc95f77d_34 - - clangxx_impl_osx-arm64 22.1.8.* - - sdkroot_env_osx-arm64 - license: BSD-3-Clause - license_family: BSD + weak: + - azure-storage-common-cpp >=12.14.0,<12.14.1.0a0 + size: 128710 + timestamp: 1781268693348 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.16.0-h16bb3af_1.conda + sha256: fb210d8d068df72a68c5fa655cf1f816792e278da3041c485478e7924d6e80eb + md5: 06ba52b8a66fd041f4910b547e3f3da1 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - azure-storage-blobs-cpp >=12.18.0,<12.18.1.0a0 + - azure-storage-common-cpp >=12.14.0,<12.14.1.0a0 + - libcxx >=19 + license: MIT + license_family: MIT + purls: [] run_exports: - strong: - - libcxx >=22 - size: 19380 - timestamp: 1785272140393 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/compiler-rt-19.1.7-h855ad52_1.conda - sha256: b58a481828aee699db7f28bfcbbe72fb133277ac60831dfe70ee2465541bcb93 - md5: 39451684370ae65667fa5c11222e43f7 + weak: + - azure-storage-files-datalake-cpp >=12.16.0,<12.16.1.0a0 + size: 206698 + timestamp: 1781541197750 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda + sha256: 8ec22f0ba25cbfc2e64d70cf29459eccd7ffdf6436f6a6ff15bbfef799f7d4f6 + md5: b50612e7d190b8061ab4e7dc119cf4d5 depends: - __osx >=11.0 - - clang 19.1.7.* - - compiler-rt_osx-arm64 19.1.7.* - license: Apache-2.0 WITH LLVM-exception - license_family: APACHE - run_exports: {} - size: 97085 - timestamp: 1757411887557 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/compiler-rt-22.1.8-hce30654_1.conda - sha256: eb0c8aee28824932e465960e313ce41486bb17b440d8195526733044006ee4ae - md5: 7c0a8e9c95ac40105b4c548d992ca537 - depends: - - compiler-rt22 22.1.8 hd34ed20_1 - - libcompiler-rt 22.1.8 hd34ed20_1 - constrains: - - clang 22.1.8 - license: Apache-2.0 WITH LLVM-exception - license_family: APACHE - run_exports: {} - size: 16555 - timestamp: 1781741177869 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/compiler-rt22-22.1.8-hd34ed20_1.conda - sha256: b7da9c52cbccfa8c2449a9e404225696f60bce30f5270fd6d7be1c5cfb5dc478 - md5: 97912eef554a369ab285baefdf843a86 + 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/c-ares-1.34.8-h1a92334_1.conda + sha256: 104b41473845649101ba8ebf8221c7431256465d34ce380b10e9a90558ed33ac + md5: 7d9390a4d4b43f91652823d870f68065 depends: - __osx >=11.0 - - compiler-rt22_osx-arm64 22.1.8.* - license: Apache-2.0 WITH LLVM-exception - license_family: APACHE - run_exports: {} - size: 99905 - timestamp: 1781741175808 + constrains: + - c-ares-static <0a0 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - c-ares >=1.34.8,<2.0a0 + size: 197274 + timestamp: 1786116660078 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coreutils-9.11-h1a92334_0.conda sha256: c121e881b96fdbb1f91fbd1e288c9eab062b345f381521db27a514669c1fc5ef md5: b9c285813535217aba1d2c8b55bf947a @@ -4152,6 +4136,7 @@ packages: - __osx >=11.0 license: GPL-3.0-or-later license_family: GPL + purls: [] run_exports: {} size: 1624074 timestamp: 1786403832459 @@ -4169,107 +4154,99 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: curl license_family: MIT + purls: [] run_exports: {} size: 181023 timestamp: 1785500142429 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cxx-compiler-1.11.0-h88570a1_0.conda - sha256: 99800d97a3a2ee9920dfc697b6d4c64e46dc7296c78b1b6c746ff1c24dea5e6c - md5: 043afed05ca5a0f2c18252ae4378bdee +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.3.1-h784d473_0.conda + sha256: 3028f8f857d438444d3fb56c9c74d46699cb04e676c6575747cf3ad8d9a308b1 + md5: 96aea99abfaf8d1e7731c03e3f70fb02 depends: - - c-compiler 1.11.0 h61f9b84_0 - - clangxx_osx-arm64 19.* + - libcxx >=19 + - __osx >=11.0 license: BSD-3-Clause license_family: BSD - run_exports: {} - size: 6715 - timestamp: 1753098739952 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.3-h2b252f5_0.conda - sha256: 6ec1937b910b2ac468459c2f7844b7c98238ada39ede05461dbd4dc764e519a5 - md5: d718960dea5abf5b582fe8ecf363c11d - depends: - - __osx >=11.0 - - libexpat >=2.8.1,<3.0a0 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - - libintl >=0.25.1,<1.0a0 - - libzlib >=1.3.2,<2.0a0 - license: MIT - license_family: MIT + purls: [] run_exports: weak: - - fontconfig >=2.18.3,<3.0a0 - - fonts-conda-ecosystem - size: 267950 - timestamp: 1786381516923 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-h84a0fba_1.conda - sha256: 6dd18694340b84290bb1906cc1359502b974aada6a8476cfe6dec3ce0e860af8 - md5: 2bb7d7dd91116b8c85e805b0e08cc67b + - gflags >=2.3.1,<2.4.0a0 + size: 93701 + timestamp: 1785044718935 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-h6f9ecc1_1.conda + sha256: 1ac5991fd354de81baf3892fee0ad09438623698fcad73758521dfe90711ed44 + md5: 68937f90f7930d49e106b94e95d4536c depends: - __osx >=11.0 - license: LGPL-2.1-or-later + - gflags >=2.3.0,<2.4.0a0 + - libcxx >=19 + license: BSD-3-Clause + license_family: BSD + purls: [] run_exports: weak: - - fribidi >=1.0.16,<2.0a0 - size: 60230 - timestamp: 1785912572097 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gcc_impl_osx-arm64-16.1.0-h2d6a911_1.conda - sha256: e206305029f67292d37072f8ed07283fa3cfd71f11353150b52ff80a3e785df3 - md5: b0352ae5efef44ca5181577f4da76e1b + - glog >=0.7.1,<0.8.0a0 + size: 112670 + timestamp: 1784094909098 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/google-crc32c-1.8.0-py312h2be8f03_2.conda + sha256: a7b4b658c852f4b86e2f8f22691e9acab6d36c962de43c9982cb05189a8a59a6 + md5: 80d9d966c6100d1665548869c4ed1817 depends: - - cctools_osx-arm64 - - clang - - ld64_osx-arm64 - - libgcc >=16.1.0 - - libgcc-devel_osx-arm64 16.1.0 h2713bec_101 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL + - __osx >=11.0 + - libcrc32c >=1.1.2,<1.2.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/google-crc32c?source=hash-mapping run_exports: {} - size: 50663693 - timestamp: 1785374498080 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gfortran_impl_osx-arm64-16.1.0-h68f779f_1.conda - sha256: d1a5d01fa7ff4637854776b87040e254447aa044194d8c74213c3c5228a21664 - md5: ac2c651e56d38db2506fbb8dcb33594d + size: 29484 + timestamp: 1788538177501 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.16.0-nompi_py312h3cfd1a2_104.conda + sha256: 3b54ad1fa52e4b2f0e6108e902f21ae3250b9fecf773013f276bc7ffee3b5a71 + md5: 10a45e9c6959baeda6ee1b08620db390 depends: - - gcc_impl_osx-arm64 >=16.1.0 - - libgcc >=16.1.0 - - libgfortran5 >=16.1.0 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL + - __osx >=11.0 + - cached-property + - hdf5 >=1.14.6,<1.14.7.0a0 + - numpy >=1.25,<3 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/h5py?source=compressed-mapping run_exports: {} - size: 13593519 - timestamp: 1785374627472 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-h784d473_1.conda - sha256: 471f34a187fdb4f2df33e26f2e471b16c239a5b277903f643d9c3a8c9a9f44ec - md5: 0c7b78d9ffff4f5a6ca28d346f734d8f + size: 1140532 + timestamp: 1787108753682 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-1.14.6-nompi_had3affe_110.conda + sha256: 6964fee3d3a4a59c85d2589eb5e8c8bff7dde9721854f493cc7b9aca5cd7d4be + md5: 65797138355b90a8e219afcf7e77b273 depends: - - libcxx >=19 - __osx >=11.0 - license: LGPL-2.0-or-later - license_family: LGPL - run_exports: - weak: - - graphite2 >=1.3.15,<2.0a0 - size: 86493 - timestamp: 1786118637573 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gsl-2.7-h6e638da_0.tar.bz2 - sha256: 979c2976adcfc70be997abeab2ed8395f9ac2b836bdcd25ed5d2efbf1fed226b - md5: 2a2126a940e033e7225a5dc7215eea9a - depends: - - libblas >=3.9.0,<4.0a0 - - libcblas >=3.9.0,<4.0a0 - license: GPL-3.0-or-later - license_family: GPL + - libaec >=1.1.5,<2.0a0 + - libcurl >=8.20.0,<9.0a0 + - libcxx >=19 + - libgfortran + - libgfortran5 >=14.3.0 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.6,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] run_exports: weak: - - gsl >=2.7,<2.8.0a0 - size: 2734398 - timestamp: 1626369562748 + - hdf5 >=1.14.6,<1.14.7.0a0 + size: 3290207 + timestamp: 1780581564967 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-py310h579977c_2.conda sha256: 6cdb5dee54c72e56ab189fb3ad33cb28533553d42590e7e831160248f4416a43 md5: a5efc0b42bb8b42e97d0a29ae3e3c187 depends: - __osx >=11.0 license: MIT + purls: [] run_exports: weak: - icu >=78.3,<79.0a0 @@ -4286,90 +4263,30 @@ packages: - openssl >=3.5.7,<4.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - krb5 >=1.22.2,<1.23.0a0 size: 1159780 timestamp: 1781859501654 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ld64-956.6-llvm19_1_he86490a_5.conda - sha256: 65a01b4adde1c1c7f4f6195590e90280bcb6ac34c03a81297a594a7830003c31 - md5: 972af8a8dee1002cc8a9d331d273326c - depends: - - ld64_osx-arm64 956.6 llvm19_1_ha2625f7_5 - - libllvm19 >=19.1.7,<19.2.0a0 - constrains: - - cctools_osx-arm64 1030.6.3.* - - cctools 1030.6.3.* - license: APSL-2.0 - license_family: Other - run_exports: {} - size: 21905 - timestamp: 1785270919676 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ld64-956.6-llvm22_1_h5b97f1b_5.conda - sha256: 649aed47b17fb6dd0036972eb67cda49d930edb33bfbd42080d0f56e689927cc - md5: e36ce4ef652fd1d571d32df59e6aafe6 - depends: - - ld64_osx-arm64 956.6 llvm22_1_h692d5aa_5 - - libllvm22 >=22.1.8,<22.2.0a0 - constrains: - - cctools_osx-arm64 1030.6.3.* - - cctools 1030.6.3.* - license: APSL-2.0 - license_family: Other - run_exports: {} - size: 21744 - timestamp: 1785270927117 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ld64_osx-arm64-956.6-llvm19_1_ha2625f7_5.conda - sha256: 430018eaf9d94fa663be4535ce94e32ca9be782b2498b4db497bfb73ea643c01 - md5: d92bcd8b46949ed3390b0c2c313eae49 - depends: - - __osx >=11.0 - - libcxx - - libllvm19 >=19.1.7,<19.2.0a0 - - sigtool-codesign - - tapi >=1600.0.11.8,<1601.0a0 - constrains: - - ld64 956.6.* - - clang 19.1.* - - cctools_impl_osx-arm64 1030.6.3.* - - cctools 1030.6.3.* - license: APSL-2.0 - license_family: Other - run_exports: {} - size: 1038517 - timestamp: 1785270876807 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ld64_osx-arm64-956.6-llvm22_1_h692d5aa_5.conda - sha256: 0ae96297b92a8148219d3450b989caaa37c6a8f03c3b891d204946e9b2a8f69d - md5: b84ec86a7de90fd23133d5f527943329 - depends: - - __osx >=11.0 - - libcxx - - libllvm22 >=22.1.8,<22.2.0a0 - - sigtool-codesign - - tapi >=1600.0.11.8,<1601.0a0 - constrains: - - cctools_impl_osx-arm64 1030.6.3.* - - ld64 956.6.* - - cctools 1030.6.3.* - - clang 22.1.* - license: APSL-2.0 - license_family: Other - run_exports: {} - size: 1038789 - timestamp: 1785270893798 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.2.0-h1eee2c3_0.conda - sha256: c97aa17d16d2ac332ba9f184e82ce8f72dcb10e9a10c5f299030be2d44e191b9 - md5: e429aec4037d5cb8fd34ded9f5dadd39 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260526.0-cxx17_h4c27e2a_2.conda + sha256: d9c62dae9d8f5bebadf72fcf369c00cc353183cb2521f9fffbf4c2f70b58bef9 + md5: 2aa5e7dc7b5d218908effd2a8c70a8a8 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: - - lerc >=4.2.0,<5.0a0 - size: 166477 - timestamp: 1785036480092 + - libabseil >=20260526.0,<20260527.0a0 + - libabseil =*=cxx17* + size: 1277537 + timestamp: 1787217005681 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.5-h8664d51_0.conda sha256: af9cd8db11eb719e38a3340c88bb4882cf19b5b4237d93845224489fc2a13b46 md5: 13e6d9ae0efbc9d2e9a01a91f4372b41 @@ -4378,23 +4295,140 @@ packages: - libcxx >=19 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - libaec >=1.1.5,<2.0a0 size: 30390 timestamp: 1769222133373 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libasprintf-0.25.1-h493aca8_0.conda - sha256: 7265547424e978ea596f51cc8e7b81638fb1c660b743e98cc4deb690d9d524ab - md5: 0deb80a2d6097c5fb98b495370b2435b - depends: - - __osx >=11.0 - - libcxx >=18 - license: LGPL-2.1-or-later +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-25.0.0-haaed7fe_4_cpu.conda + build_number: 4 + sha256: 3c289bab2a8c4be0ef86363c97c5532dbcc289708b8523d6a3cfef35a2dd0b9b + md5: 6c06263450ab8f5f4580effead8b2ca4 + depends: + - __osx >=12.0 + - aws-crt-cpp >=0.40.1,<0.40.2.0a0 + - aws-sdk-cpp >=1.11.833,<1.11.834.0a0 + - azure-core-cpp >=1.16.3,<1.16.4.0a0 + - azure-identity-cpp >=1.13.3,<1.13.4.0a0 + - azure-storage-blobs-cpp >=12.18.0,<12.18.1.0a0 + - azure-storage-files-datalake-cpp >=12.16.0,<12.16.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libcxx >=21 + - libgoogle-cloud >=3.8.0,<3.9.0a0 + - libgoogle-cloud-storage >=3.8.0,<3.9.0a0 + - libopentelemetry-cpp >=1.27.0,<1.28.0a0 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libzlib >=1.3.2,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.3.1,<2.3.2.0a0 + - snappy >=1.2.2,<1.3.0a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - apache-arrow-proc =*=cpu + - arrow-cpp <0.0a0 + - parquet-cpp <0.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - libarrow >=25.0.0,<25.1.0a0 + size: 4294348 + timestamp: 1786315141730 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-25.0.0-heaff1e6_4_cpu.conda + build_number: 4 + sha256: 154feae16991d0e50d4b4d45291904cfbf1c8c97955237f71a84975b363cdd04 + md5: 5810871245167355126d623aa5ae1096 + depends: + - __osx >=12.0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libarrow 25.0.0 haaed7fe_4_cpu + - libarrow-compute 25.0.0 h93a2d87_4_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.27.0,<1.28.0a0 + - libprotobuf >=7.35.1,<7.35.2.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - libarrow-acero >=25.0.0,<25.1.0a0 + size: 519885 + timestamp: 1786315467845 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-compute-25.0.0-h93a2d87_4_cpu.conda + build_number: 4 + sha256: 5b725a3048bd53e9e2cee2577a26724467b35a956cc1207f5bde0321d128dbaf + md5: 1f47858e8a29295f723b63cd318a31c4 + depends: + - __osx >=12.0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libarrow 25.0.0 haaed7fe_4_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.27.0,<1.28.0a0 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libre2-11 >=2025.11.5 + - libutf8proc >=2.11.3,<2.12.0a0 + - re2 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - libarrow-compute >=25.0.0,<25.1.0a0 + size: 2228808 + timestamp: 1786315247988 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-25.0.0-heaff1e6_4_cpu.conda + build_number: 4 + sha256: 580382aae415669e2ff4af670db9381462d5d966ecde7d35f041356344b77dd1 + md5: 5d2b0865a12a8a380c4ad11eae754f30 + depends: + - __osx >=12.0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libarrow 25.0.0 haaed7fe_4_cpu + - libarrow-acero 25.0.0 heaff1e6_4_cpu + - libarrow-compute 25.0.0 h93a2d87_4_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.27.0,<1.28.0a0 + - libparquet 25.0.0 h0de02aa_4_cpu + - libprotobuf >=7.35.1,<7.35.2.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - libarrow-dataset >=25.0.0,<25.1.0a0 + size: 519748 + timestamp: 1786315616734 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-25.0.0-h2208e57_4_cpu.conda + build_number: 4 + sha256: 4f5d1e1e1ecead58d80472e2965143a4d4daf37d401a527334116caf5b5cff9b + md5: e8627e23ad2343bdd0765b49ad7795df + depends: + - __osx >=12.0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libarrow 25.0.0 haaed7fe_4_cpu + - libarrow-acero 25.0.0 heaff1e6_4_cpu + - libarrow-dataset 25.0.0 heaff1e6_4_cpu + - libcxx >=21 + - libprotobuf >=7.35.1,<7.35.2.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] run_exports: weak: - - libasprintf >=0.25.1,<1.0a0 - size: 52316 - timestamp: 1751558366611 + - libarrow-substrait >=25.0.0,<25.1.0a0 + size: 455505 + timestamp: 1786315676110 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-9_h51639a9_openblas.conda build_number: 9 sha256: 0437866fe43b4c911470d3e7ddea18d78390bd7062d9563ce6d38c8ba5798405 @@ -4410,11 +4444,53 @@ packages: - mkl <2027 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libblas >=3.11.0,<4.0a0 size: 18162 timestamp: 1786058887392 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-h1dcdb26_4.conda + sha256: 8a6e4789a6049dabb41c6fcaf6719232245484d81dfeb199b5abdb95c84c9fc2 + md5: 8f3981871d167a6cd323e636e1929dd4 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - libbrotlicommon >=1.2.0,<1.3.0a0 + size: 79648 + timestamp: 1788480342707 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-h5295a6a_4.conda + sha256: 4e1f5989561de4546209b42c935795a41fa4265e0b670861480f1adffec623a8 + md5: da537a1643ef3e13bdccf16cca206f2c + depends: + - __osx >=11.0 + - libbrotlicommon 1.2.0 h1dcdb26_4 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - libbrotlidec >=1.2.0,<1.3.0a0 + size: 29864 + timestamp: 1788480352084 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-h2ddc9cb_4.conda + sha256: 4d2da97825b221054a39f492e4dffe361e1931c8381d0367f1e0598a50d02f34 + md5: 39a25d500b191c16996239c4afa104f1 + depends: + - __osx >=11.0 + - libbrotlicommon 1.2.0 h1dcdb26_4 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - libbrotlienc >=1.2.0,<1.3.0a0 + size: 295505 + timestamp: 1788480359341 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-9_hb0561ab_openblas.conda build_number: 9 sha256: c4c71f20fdb20c86bf6f61c8c31bb349e4055bb4d19928e8580bb5615039cb4b @@ -4427,90 +4503,40 @@ packages: - liblapacke 3.11.0 9*_openblas license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - libcblas >=3.11.0,<4.0a0 size: 18110 timestamp: 1786058893756 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libclang-cpp19.1-19.1.7-default_hf3020a7_9.conda - sha256: e05c4830a117492996bac1ad55cd7ee3e57f63b46da8a324862efbee9279ab44 - md5: ddb70ebdcbf3a44bddc2657a51faf490 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 + sha256: 58477b67cc719060b5b069ba57161e20ba69b8695d154a719cb4b60caf577929 + md5: 32bd82a6a625ea6ce090a81c3d34edeb depends: - - __osx >=11.0 - - libcxx >=19.1.7 - - libllvm19 >=19.1.7,<19.2.0a0 - license: Apache-2.0 WITH LLVM-exception - license_family: Apache + - libcxx >=11.1.0 + license: BSD-3-Clause + license_family: BSD + purls: [] run_exports: weak: - - libclang-cpp19.1 >=19.1.7,<19.2.0a0 - size: 14064699 - timestamp: 1776988581784 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libclang-cpp22.1-22.1.8-default_hc257da1_6.conda - sha256: 5f16d1b4324284f2a360f56566867fd05c4a9dfbddff650887788f6285f956a9 - md5: 08920f1eb3b689caa8a7274d8ce81cb8 + - libcrc32c >=1.1.2,<1.2.0a0 + size: 18765 + timestamp: 1633683992603 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.21.0-hf618e03_4.conda + sha256: d25be36712d7f854a5f93513b9fbf1b0ee3219975b7fef4a3ee071b021b10167 + md5: 66cf9c5003ee81ecdb0dc9f9df17bbe5 depends: - - libcxx >=22.1.8 - __osx >=11.0 + - krb5 >=1.22.2,<1.23.0a0 + - libnghttp2 >=1.68.1,<2.0a0 + - libpsl >=0.23.0,<0.24.0a0 + - libssh2 >=1.11.1,<2.0a0 - libzlib >=1.3.2,<2.0a0 - - libxml2 - - libxml2-16 >=2.14.6 + - openssl >=3.5.7,<4.0a0 - zstd >=1.5.7,<1.6.0a0 - - libllvm22 >=22.1.8,<22.2.0a0 - license: Apache-2.0 WITH LLVM-exception - license_family: APACHE - run_exports: - weak: - - libclang-cpp22.1 >=22.1.8,<22.2.0a0 - size: 15931214 - timestamp: 1786527707120 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libclang13-22.1.8-default_hed75349_6.conda - sha256: 8d7d0ecabc11a7ed0b8cccff96815d8cc1cc712affc0dcdc540d9e18f6e7158f - md5: 15daddf398a7a982faf7e88f38ea60cc - depends: - - libclang-cpp22.1 ==22.1.8 default_hc257da1_6 - - libcxx >=22.1.8 - - __osx >=11.0 - - libzlib >=1.3.2,<2.0a0 - - libxml2 - - libxml2-16 >=2.14.6 - - zstd >=1.5.7,<1.6.0a0 - - libllvm22 >=22.1.8,<22.2.0a0 - license: Apache-2.0 WITH LLVM-exception - license_family: APACHE - run_exports: - weak: - - libclang13 >=22.1.8 - size: 9989893 - timestamp: 1786527707120 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcompiler-rt-22.1.8-hd34ed20_1.conda - sha256: e19bef885123e78e93169f2814ff1c41650ee554b0e2b2b79f7398e09803df94 - md5: 45e4352d41a29e442f79f7708d12b655 - depends: - - __osx >=11.0 - constrains: - - compiler-rt >=9.0.1 - license: Apache-2.0 WITH LLVM-exception - license_family: APACHE - run_exports: - weak: - - libcompiler-rt >=22.1.8 - size: 1376251 - timestamp: 1781741160097 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.21.0-hf618e03_4.conda - sha256: d25be36712d7f854a5f93513b9fbf1b0ee3219975b7fef4a3ee071b021b10167 - md5: 66cf9c5003ee81ecdb0dc9f9df17bbe5 - depends: - - __osx >=11.0 - - krb5 >=1.22.2,<1.23.0a0 - - libnghttp2 >=1.68.1,<2.0a0 - - libpsl >=0.23.0,<0.24.0a0 - - libssh2 >=1.11.1,<2.0a0 - - libzlib >=1.3.2,<2.0a0 - - openssl >=3.5.7,<4.0a0 - - zstd >=1.5.7,<1.6.0a0 - license: curl - license_family: MIT + license: curl + license_family: MIT + purls: [] run_exports: weak: - libcurl >=8.21.0,<9.0a0 @@ -4523,43 +4549,10 @@ packages: - __osx >=11.0 license: Apache-2.0 WITH LLVM-exception license_family: Apache + purls: [] run_exports: {} size: 569349 timestamp: 1781670209146 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-devel-19.1.7-h6dc3340_2.conda - sha256: ec07ebaa226792f4e2bf0f5dba50325632a7474d5f04b951d8291be70af215da - md5: 9f7810b7c0a731dbc84d46d6005890ef - depends: - - libcxx >=19.1.7 - - libcxx-headers >=19.1.7,<19.1.8.0a0 - license: Apache-2.0 WITH LLVM-exception - license_family: Apache - run_exports: {} - size: 23000 - timestamp: 1764648270121 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-devel-22.1.8-h6dc3340_0.conda - sha256: e734ac1ed426d09363d242674a286dc004f041ad1167e73912e11d3634cee09d - md5: f7784a43f0ed7158e918fd8ec0843b47 - depends: - - libcxx >=22.1.8 - - libcxx-headers >=22.1.8,<22.1.9.0a0 - license: Apache-2.0 WITH LLVM-exception - license_family: Apache - run_exports: {} - size: 21687 - timestamp: 1781670229781 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-he7e0567_1.conda - sha256: d896f4aa4ce4c590c2838678cb1917356fdb461d2a189991c0280c818c362172 - md5: 78650d671cb56909bb3e5c13bce310f9 - depends: - - __osx >=11.0 - license: MIT - license_family: MIT - run_exports: - weak: - - libdeflate >=1.25,<1.26.0a0 - size: 55727 - timestamp: 1785909153744 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda sha256: 66aa216a403de0bb0c1340a88d1a06adaff66bae2cfd196731aa24db9859d631 md5: 44083d2d2c2025afca315c7a172eab2b @@ -4569,6 +4562,7 @@ packages: - ncurses >=6.5,<7.0a0 license: BSD-2-Clause license_family: BSD + purls: [] run_exports: weak: - libedit >=3.1.20250104,<3.2.0a0 @@ -4581,11 +4575,26 @@ packages: - __osx >=11.0 license: BSD-2-Clause OR GPL-2.0-or-later license_family: GPL + purls: [] run_exports: weak: - libev >=4.33,<4.34.0a0 size: 39991 timestamp: 1785917376956 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h4b5715a_2.conda + sha256: 6dee15644c34e437920f1147974532cdcc039359b241b7eaa5fbdee275ec4621 + md5: 9de672ef7d51541ea5176779bfc3e473 + depends: + - __osx >=11.0 + - openssl >=3.5.8,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - libevent >=2.1.12,<2.1.13.0a0 + size: 369136 + timestamp: 1788870250682 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_1.conda sha256: 5af74261101e3c777399c6294b2b5d290e508153268eb2e9ff99c4d69834612f md5: a915151d5d3c5bf039f5ccc8402a436f @@ -4595,6 +4604,7 @@ packages: - expat 2.8.1.* license: MIT license_family: MIT + purls: [] run_exports: {} size: 69362 timestamp: 1781203631990 @@ -4605,33 +4615,12 @@ packages: - __osx >=11.0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libffi >=3.7.0,<3.8.0a0 size: 43734 timestamp: 1783521647536 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_1.conda - sha256: d5637b01941c0fc8f5cbb1f170c238f4ee153b3c1708b9d50f4f1305438ff051 - md5: 0582e67cd14cfed773be2f3b1aba08e0 - depends: - - libfreetype6 >=2.14.3 - license: GPL-2.0-only OR FTL - run_exports: {} - size: 8365 - timestamp: 1780933612390 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-hdfa99f5_1.conda - sha256: abbfffd8a8c776bb8b59a10c8247fc3aa6b17ba0051e9f6d199dca38479f214f - md5: a0bb0678f67c464938d3693fa96f6884 - depends: - - __osx >=11.0 - - libpng >=1.6.58,<1.7.0a0 - - libzlib >=1.3.2,<2.0a0 - constrains: - - freetype >=2.14.3 - license: GPL-2.0-only OR FTL - run_exports: {} - size: 338442 - timestamp: 1780933611662 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.1.0-h3cf6597_1.conda sha256: fdd1502babb50b802d090496586231f56236d7a6fc042a4e1ac2dee48da8366b md5: 0124dc2e6f70f3e8ebea643b42b18abb @@ -4642,23 +4631,10 @@ packages: - libgcc-ng ==16.1.0=*_1 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: {} size: 364162 timestamp: 1785374452947 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgettextpo-0.25.1-h493aca8_0.conda - sha256: 3ba35ff26b3b9573b5df5b9bbec5c61476157ec3a9f12c698e2a9350cd4338fd - md5: 98acd9989d0d8d5914ccc86dceb6c6c2 - depends: - - __osx >=11.0 - - libiconv >=1.18,<2.0a0 - - libintl 0.25.1 h493aca8_0 - license: GPL-3.0-or-later - license_family: GPL - run_exports: - weak: - - libgettextpo >=0.25.1,<1.0a0 - size: 183091 - timestamp: 1751558452316 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.1.0-h07b0088_1.conda sha256: d7c6dd601dbbde495ab213a76d690b2fec19455d26c595ec0257cfde3e80166b md5: d6f10dbb9c5830f540904c91ea5b252a @@ -4668,6 +4644,7 @@ packages: - libgfortran-ng ==16.1.0=*_1 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: {} size: 98528 timestamp: 1785374566402 @@ -4680,82 +4657,89 @@ packages: - libgfortran 16.1.0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL + purls: [] run_exports: {} size: 556657 timestamp: 1785374459225 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-ha531971_1.conda - sha256: c9f7273bbe06d1693ff3a2b6f8b49b74e2ef6cbac2cead7aae767f2f5191b7aa - md5: 70a6bcf13dc0dd00fe3fe91190d38771 - depends: - - __osx >=11.0 - - pcre2 >=10.47,<10.48.0a0 - - libffi >=3.7.0,<3.8.0a0 - - libintl >=0.25.1,<1.0a0 - - libiconv >=1.18,<2.0a0 - - libzlib >=1.3.2,<2.0a0 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-3.8.0-ha595bda_0.conda + sha256: a48050bcf3827e0fc10de5a7d251760b8b84011f3aeea23ae18b5e0ce25aff19 + md5: 4f34ca73f16b37fae583ce16c48b5492 + depends: + - __osx >=12.0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libcurl >=8.21.0,<9.0a0 + - libcxx >=19 + - libgrpc >=1.82.1,<1.83.0a0 + - libopentelemetry-cpp >=1.27.0,<1.28.0a0 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - openssl >=3.5.7,<4.0a0 constrains: - - glib >2.66 - license: LGPL-2.1-or-later + - libgoogle-cloud 3.8.0 *_0 + license: Apache-2.0 + license_family: Apache + purls: [] run_exports: weak: - - libglib >=2.88.3,<3.0a0 - size: 4447961 - timestamp: 1786457780347 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.3.0-h5a65909_0.conda - sha256: 5803da482e914fc5d7ea82b31789471973b03ca58e9caffedd86e175c3aee447 - md5: 19248fa96b4c573e46bfec7fa3c875fa - depends: - - __osx >=11.0 - - cairo >=1.18.4,<2.0a0 - - graphite2 >=1.3.15,<2.0a0 - - icu >=78.3,<79.0a0 + - libgoogle-cloud >=3.8.0,<3.9.0a0 + size: 1804429 + timestamp: 1786225612587 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-3.8.0-hc8aed40_0.conda + sha256: 13251d6a72028a13a9d142815b23818a56f6f88a67f9dd5f19a881eb2fcf5636 + md5: 36ba29b4cf02ed97ce269939b750961a + depends: + - __osx >=12.0 + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl - libcxx >=19 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - - libglib >=2.88.3,<3.0a0 - - libpng >=1.6.58,<1.7.0a0 + - libgoogle-cloud 3.8.0 ha595bda_0 - libzlib >=1.3.2,<2.0a0 - license: MIT - license_family: MIT - run_exports: {} - size: 942277 - timestamp: 1785770026085 + - openssl + license: Apache-2.0 + license_family: Apache + purls: [] + run_exports: + weak: + - libgoogle-cloud-storage >=3.8.0,<3.9.0a0 + size: 541527 + timestamp: 1786225749476 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.82.1-hf3e839d_0.conda + sha256: b9d0f510488074e889ca3cecd2b7490e8e830f7a60e2e91809a90355bf4d1a57 + md5: 5f7f645a15eedc24da7f271dc2086bcf + depends: + - __osx >=12.0 + - c-ares >=1.34.6,<2.0a0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libcxx >=19 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libre2-11 >=2025.11.5 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.7,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.82.1 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - libgrpc >=1.82.1,<1.83.0a0 + size: 4898235 + timestamp: 1783587327477 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-h23cfdf5_2.conda sha256: de0336e800b2af9a40bdd694b03870ac4a848161b35c8a2325704f123f185f03 md5: 4d5a7445f0b25b6a3ddbb56e790f5251 depends: - __osx >=11.0 license: LGPL-2.1-only + purls: [] run_exports: weak: - libiconv >=1.18,<2.0a0 size: 750379 timestamp: 1754909073836 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.25.1-h493aca8_0.conda - sha256: 99d2cebcd8f84961b86784451b010f5f0a795ed1c08f1e7c76fbb3c22abf021a - md5: 5103f6a6b210a3912faf8d7db516918c - depends: - - __osx >=11.0 - - libiconv >=1.18,<2.0a0 - license: LGPL-2.1-or-later - run_exports: - weak: - - libintl >=0.25.1,<1.0a0 - size: 90957 - timestamp: 1751558394144 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.2.0-h84a0fba_1.conda - sha256: 05006418f9392c9b723e8428808db106de0350a497832f95f921b85ff1072310 - md5: b2f8c8e5a7651a1d7c05404c3a159517 - depends: - - __osx >=11.0 - constrains: - - jpeg <0.0.0a - license: IJG AND BSD-3-Clause AND Zlib - run_exports: - weak: - - libjpeg-turbo >=3.2.0,<4.0a0 - size: 558459 - timestamp: 1785896382474 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-9_hd9741b5_openblas.conda build_number: 9 sha256: db09e8e6a58415da1d866221cf518e53e84c6766a4500faae716cfa204f696ff @@ -4768,45 +4752,12 @@ packages: - liblapacke 3.11.0 9*_openblas license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - liblapack >=3.11.0,<3.12.0a0 size: 18144 timestamp: 1786058899889 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libllvm19-19.1.7-h8e0c9ce_2.conda - sha256: 46f8ff3d86438c0af1bebe0c18261ce5de9878d58b4fe399a3a125670e4f0af5 - md5: d1d9b233830f6631800acc1e081a9444 - depends: - - __osx >=11.0 - - libcxx >=19 - - libxml2 - - libxml2-16 >=2.14.5 - - libzlib >=1.3.1,<2.0a0 - - zstd >=1.5.7,<1.6.0a0 - license: Apache-2.0 WITH LLVM-exception - license_family: Apache - run_exports: - weak: - - libllvm19 >=19.1.7,<19.2.0a0 - size: 26914852 - timestamp: 1757353228286 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libllvm22-22.1.8-h89af1be_1.conda - sha256: 9c0ece5160e3d8749f43f8dd86777be4cfdb51490fb32f4d8269f8fd9866ce55 - md5: 5726aa6dda93e10bd614e434e9c9b8fc - depends: - - __osx >=11.0 - - libcxx >=19 - - libxml2 - - libxml2-16 >=2.14.6 - - libzlib >=1.3.2,<2.0a0 - - zstd >=1.5.7,<1.6.0a0 - license: Apache-2.0 WITH LLVM-exception - license_family: Apache - run_exports: - weak: - - libllvm22 >=22.1.8,<22.2.0a0 - size: 30057877 - timestamp: 1781783269086 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_1.conda sha256: 23d0630046a3e8b164d8f80f2b74ed2605af2e7050ab9913018056402fae4311 md5: 8ab10323068b107661a4b9a4af84f3b5 @@ -4815,6 +4766,7 @@ packages: constrains: - xz 5.8.3.* license: 0BSD + purls: [] run_exports: weak: - liblzma >=5.8.3,<6.0a0 @@ -4833,6 +4785,7 @@ packages: - openssl >=3.5.5,<4.0a0 license: MIT license_family: MIT + purls: [] run_exports: weak: - libnghttp2 >=1.68.1,<2.0a0 @@ -4850,849 +4803,641 @@ packages: - 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: 4318474 timestamp: 1784288246205 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-h132b30e_0.conda - sha256: 66eae34546df1f098a67064970c92aa14ae7a7505091889e00468294d2882c36 - md5: 2259ae0949dbe20c0665850365109b27 - depends: - - __osx >=11.0 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-1.27.0-h46c1d2a_1.conda + sha256: 9c1840af12f31643723a7ea7f6bde6a3a394783ced6aa65b75a9fc3ecb84f39f + md5: a6c2fc0c5ddf105b3d3e353c1383a492 + depends: + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libcurl >=8.21.0,<9.0a0 + - libgrpc >=1.82.0,<1.83.0a0 + - libopentelemetry-cpp-headers 1.27.0 hce30654_1 + - libprotobuf >=7.35.1,<7.35.2.0a0 - libzlib >=1.3.2,<2.0a0 - license: zlib-acknowledgement - run_exports: - weak: - - libpng >=1.6.58,<1.7.0a0 - size: 289546 - timestamp: 1776315246750 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpsl-0.23.1-h7a62e17_0.conda - sha256: 0140cb0d059ac531e476b85a543668b69b7dffec16f0d26269ab6c9b70546921 - md5: 75a4cdf128d016141db61732ea462276 - depends: - - __osx >=11.0 - - libcxx >=19 - - icu >=78.3,<79.0a0 - license: MIT - license_family: MIT - run_exports: - weak: - - libpsl >=0.23.1,<0.24.0a0 - size: 72950 - timestamp: 1786443660105 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsigtool-0.1.3-h98dc951_1.conda - sha256: fcfa03afe31f40dfabf011629d99836adbebbc3ed1b38c7e9eee9ffc7581975b - md5: c70eb797aa92a294b09ef8f41f6bd578 - depends: - - __osx >=11.0 - - openssl >=3.5.7,<4.0a0 - license: MIT - license_family: MIT - run_exports: {} - size: 36651 - timestamp: 1786115069018 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda - sha256: 8bfe837221390ffc6f111ecca24fa12d4a6325da0c8d131333d63d6c37f27e0a - md5: b68e8f66b94b44aaa8de4583d3d4cc40 - depends: - - libzlib >=1.3.1,<2.0a0 - - openssl >=3.5.0,<4.0a0 - license: BSD-3-Clause - license_family: BSD - run_exports: - weak: - - libssh2 >=1.11.1,<2.0a0 - size: 279193 - timestamp: 1745608793272 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.2-h282da08_0.conda - sha256: 253153cabf9469170e02b21a6bc9aa598048e7c34966b1103af52d27d2a708a4 - md5: 6fa87106b3c041ad6d965a9411e79b94 - depends: - - __osx >=11.0 - - lerc >=4.1.0,<5.0a0 - - libcxx >=19 - - libdeflate >=1.25,<1.26.0a0 - - libjpeg-turbo >=3.1.4.1,<4.0a0 - - liblzma >=5.8.3,<6.0a0 - - libwebp-base >=1.6.0,<2.0a0 - - libzlib >=1.3.2,<2.0a0 - - zstd >=1.5.7,<1.6.0a0 - license: HPND - run_exports: - weak: - - libtiff >=4.7.2,<4.8.0a0 - size: 387825 - timestamp: 1783085754081 -- 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 - run_exports: - weak: - - libuv >=1.52.1,<2.0a0 - size: 122729 - timestamp: 1785914645797 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h202fb40_1.conda - sha256: 0ff54650d470c7e54cbeffdd53a8c063e055a7bcf784388c0385ce5c4741b0f4 - md5: 168a13e329259710b28277abc1395b8e - depends: - - __osx >=11.0 + - nlohmann_json + - prometheus-cpp >=1.3.0,<1.4.0a0 constrains: - - libwebp 1.6.0 - license: BSD-3-Clause - license_family: BSD - run_exports: - weak: - - libwebp-base >=1.6.0,<2.0a0 - size: 294522 - timestamp: 1785955350410 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.3-h5ef1a60_0.conda - sha256: ff75b84cdb9e8d123db2fa694a8ac2c2059516b6cbc98ac21fb68e235d0fd354 - md5: 19edaa53885fc8205614b03da2482282 - depends: - - __osx >=11.0 - - icu >=78.3,<79.0a0 - - libiconv >=1.18,<2.0a0 - - liblzma >=5.8.3,<6.0a0 - - libzlib >=1.3.2,<2.0a0 - constrains: - - libxml2 2.15.3 - license: MIT - license_family: MIT - run_exports: {} - size: 466360 - timestamp: 1776377102261 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.3-h5654f7c_0.conda - sha256: 2fe1d8de0854342ae9cabe408b476935f82f5636e153b3b497456264dc8ff3a1 - md5: 8e037d73747d6fe34e12d7bcac10cf21 - depends: - - __osx >=11.0 - - icu >=78.3,<79.0a0 - - libiconv >=1.18,<2.0a0 - - liblzma >=5.8.3,<6.0a0 - - libxml2-16 2.15.3 h5ef1a60_0 - - libzlib >=1.3.2,<2.0a0 - license: MIT - license_family: MIT - run_exports: - weak: - - libxml2 - - libxml2-16 >=2.15.3 - size: 41102 - timestamp: 1776377119495 -- 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 - run_exports: - weak: - - libzlib >=1.3.2,<2.0a0 - size: 47822 - timestamp: 1785277049190 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.8-hc7d1edf_0.conda - sha256: ccbaad6bbc88f135ab849bc36af5fa6eda36a9ed18ce6f58e3dde3d11784c156 - md5: a9c118f6343fb6301b6f3b4e94c4c562 - depends: - - __osx >=11.0 - constrains: - - intel-openmp <0.0a0 - - openmp 22.1.8|22.1.8.* - license: Apache-2.0 WITH LLVM-exception + - cpp-opentelemetry-sdk =1.27.0 + license: Apache-2.0 license_family: APACHE - run_exports: - strong: - - llvm-openmp >=22.1.8 - size: 286313 - timestamp: 1781736516782 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-tools-19-19.1.7-h91fd4e7_2.conda - sha256: 73f9506f7c32a448071340e73a0e8461e349082d63ecc4849e3eb2d1efc357dd - md5: 8237b150fcd7baf65258eef9a0fc76ef - depends: - - __osx >=11.0 - - libcxx >=19 - - libllvm19 19.1.7 h8e0c9ce_2 - - libzlib >=1.3.1,<2.0a0 - - zstd >=1.5.7,<1.6.0a0 - license: Apache-2.0 WITH LLVM-exception - license_family: Apache - run_exports: {} - size: 16376095 - timestamp: 1757353442671 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-tools-19.1.7-h855ad52_2.conda - sha256: 09750c33b5d694c494cad9eafda56c61a62622264173d760341b49fb001afe82 - md5: 3e3ac06efc5fdc1aa675ca30bf7d53df - depends: - - __osx >=11.0 - - libllvm19 19.1.7 h8e0c9ce_2 - - llvm-tools-19 19.1.7 h91fd4e7_2 - constrains: - - llvm 19.1.7 - - llvmdev 19.1.7 - - clang-tools 19.1.7 - - clang 19.1.7 - license: Apache-2.0 WITH LLVM-exception - license_family: Apache - run_exports: {} - size: 88390 - timestamp: 1757353535760 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-tools-22-22.1.8-hb545844_1.conda - sha256: a5ac8bd2fd67c6e71c991e3172c0ec8430de4ec91506e93c0f1afb56a88ed8e6 - md5: 67a51d348fcfc95393fd0f7d92e119cf - depends: - - __osx >=11.0 - - libcxx >=19 - - libllvm22 22.1.8 h89af1be_1 - - libzlib >=1.3.2,<2.0a0 - - zstd >=1.5.7,<1.6.0a0 - license: Apache-2.0 WITH LLVM-exception - license_family: Apache - run_exports: {} - size: 17832371 - timestamp: 1781783379957 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-tools-22.1.8-hd34ed20_1.conda - sha256: f5933c1fee348ece7129a5e07af2c8051984ed3a3d2d5f95e4c5144e23f29f55 - md5: 3ed593360f7932817da40db4f96f803f - depends: - - __osx >=11.0 - - libllvm22 22.1.8 h89af1be_1 - - llvm-tools-22 22.1.8 hb545844_1 - constrains: - - llvmdev 22.1.8 - - llvm 22.1.8 - - clang 22.1.8 - - clang-tools 22.1.8 - license: Apache-2.0 WITH LLVM-exception - license_family: Apache - run_exports: {} - size: 52210 - timestamp: 1781783441469 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/make-4.4.1-h84a0fba_3.conda - sha256: 36c769a71cfd0222de8114a6016f1def3d0411a6202aa811bca50e581450f71f - md5: 5e4c7921fa8489236f1599274b478bac - depends: - - __osx >=11.0 - license: GPL-3.0-or-later - license_family: GPL - run_exports: {} - size: 275107 - timestamp: 1785879985161 -- 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/openjdk-25.0.2-h258754b_0.conda - sha256: f6f06243c1a35b6590f06be386bd5cefc5b1773b985a61b7917b93dab4cf18ec - md5: a4024e03865a7411c1028eac83d1956c - depends: - - __osx >=11.0 - - libzlib >=1.3.1,<2.0a0 - license: GPL-2.0-or-later WITH Classpath-exception-2.0 - license_family: GPL - run_exports: {} - size: 199165105 - timestamp: 1771443790144 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.3-hd24854e_1.conda - sha256: 66be2283b5b37dcda1332b5e74c1782a8cb14fd2e62e0d38017c2d35bf73c119 - md5: 65d1906712b85d1679263c518d011b5b - depends: - - __osx >=11.0 - - ca-certificates + - libopentelemetry-cpp >=1.27.0,<1.28.0a0 + size: 564868 + timestamp: 1783133075023 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-headers-1.27.0-hce30654_1.conda + sha256: 01caeebb19f70563f4ab1b97fcdf06af13be8637e9776619c8840b49070b287c + md5: 7f61001d82fd50385d4f9fb57f936e95 license: Apache-2.0 - license_family: Apache + license_family: APACHE + purls: [] + run_exports: {} + size: 394664 + timestamp: 1783133038717 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-25.0.0-h0de02aa_4_cpu.conda + build_number: 4 + sha256: 318412a665f7637598aef088a9dbc30c2a52614eebe1bde6e75be76708875271 + md5: ebf0d32466e8875bf91749744b97165a + depends: + - __osx >=12.0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 + - libarrow 25.0.0 haaed7fe_4_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.27.0,<1.28.0a0 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - libthrift >=0.22.0,<0.22.1.0a0 + - openssl >=3.5.7,<4.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] run_exports: weak: - - openssl >=3.6.3,<4.0a0 - size: 3109132 - timestamp: 1785913735357 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pango-1.58.2-hf80efc4_0.conda - sha256: c300fba11c4cd7cdd7609b6f165981af24d2181d40280ca6af420710c4c7d42e - md5: 83c3d3d895dd96f87b0a1916e2c41f70 - depends: - - __osx >=11.0 - - cairo >=1.18.4,<2.0a0 - - fontconfig >=2.18.2,<3.0a0 - - fonts-conda-ecosystem - - fribidi >=1.0.16,<2.0a0 - - libexpat >=2.8.1,<3.0a0 - - libfreetype >=2.14.3 - - libfreetype6 >=2.14.3 - - libglib >=2.88.3,<3.0a0 - - libharfbuzz >=14.3.0 - - libpng >=1.6.58,<1.7.0a0 + - libparquet >=25.0.0,<25.1.0a0 + size: 1096341 + timestamp: 1786315419506 +- 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: LGPL-2.1-or-later - run_exports: - weak: - - pango >=1.58.2,<2.0a0 - size: 444011 - timestamp: 1786108209790 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.47-h30297fc_0.conda - sha256: 5e2e443f796f2fd92adf7978286a525fb768c34e12b1ee9ded4000a41b2894ba - md5: 9b4190c4055435ca3502070186eba53a - depends: - - __osx >=11.0 - - bzip2 >=1.0.8,<2.0a0 - - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - - pcre2 >=10.47,<10.48.0a0 - size: 850231 - timestamp: 1763655726735 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h784d473_3.conda - sha256: 4779cd57231ce2e96fac643fcbdd4a6f51a57986264545445574e9c4acf526d3 - md5: 9a99c0b60efe41c194d01c182d000733 - depends: - - __osx >=11.0 - - libcxx >=19 - license: MIT - license_family: MIT - run_exports: - weak: - - pixman >=0.46.4,<1.0a0 - size: 198717 - timestamp: 1786106922508 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-base-4.4.3-h35b0bb1_11.conda - sha256: 5d50ea1dbf7b64725bd604da667cd10a274c1d891ac48b9156e7c99d5d2523d7 - md5: 7dc0924196669416d243f5b133f19e59 - depends: - - __osx >=11.0 - - _r-mutex 1.* anacondar_1 - - bwidget - - bzip2 >=1.0.8,<2.0a0 - - cairo >=1.18.4,<2.0a0 - - clang_osx-arm64 >=19 - - clangxx_osx-arm64 >=19 - - curl - - fontconfig >=2.18.1,<3.0a0 - - fonts-conda-ecosystem - - gfortran_impl_osx-arm64 - - gsl >=2.7,<2.8.0a0 - - icu >=78.3,<79.0a0 - - libasprintf >=0.25.1,<1.0a0 - - libblas >=3.9.0,<4.0a0 - - libcurl >=8.20.0,<9.0a0 - - libcxx >=19 - - libdeflate >=1.25,<1.26.0a0 - - libexpat >=2.8.1,<3.0a0 - - libgettextpo >=0.25.1,<1.0a0 - - libgfortran - - libgfortran5 >=14.3.0 - - libglib >=2.88.1,<3.0a0 - - libiconv >=1.18,<2.0a0 - - libintl >=0.25.1,<1.0a0 - - libjpeg-turbo >=3.1.4.1,<4.0a0 - - liblapack >=3.9.0,<4.0a0 - - liblzma >=5.8.3,<6.0a0 - - libpng >=1.6.58,<1.7.0a0 - - libtiff >=4.7.1,<4.8.0a0 - - libzlib >=1.3.2,<2.0a0 - - llvm-openmp >=19.1.7 - - make - - pango >=1.56.4,<2.0a0 - - pcre2 >=10.47,<10.48.0a0 - - readline >=8.3,<9.0a0 - - tk >=8.6.13,<8.7.0a0 - - tktable - - tzdata >=2024a - license: GPL-2.0-or-later - license_family: GPL - run_exports: - weak: - - r-base >=4.4,<4.5.0a0 - noarch: - - r-base >=4.4,<4.5.0a0 - size: 27633518 - timestamp: 1781506291412 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-bit-4.6.0-r44h6168396_1.conda - sha256: 3d102b94036665931f8f930d4bdadf68d6bbf634a4b080cbccac857eb280a95c - md5: 817b181ef5056d41a6379751894fb00c - depends: - - __osx >=11.0 - - r-base >=4.4,<4.5.0a0 - license: GPL-2.0-or-later - license_family: GPL2 - run_exports: {} - size: 611884 - timestamp: 1757442004408 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-bit64-4.8.6-r44h864cdf1_0.conda - sha256: 382cc8c23f20dce6cc575de6609e50b5fea7ed1440020e77bdb67a5d292813fe - md5: c02fd0b46ac839accab66734c635ef72 - depends: - - __osx >=11.0 - - r-base >=4.4,<4.5.0a0 - - r-bit >=4.0.0 - license: GPL-2.0-only - license_family: GPL2 - run_exports: {} - size: 571095 - timestamp: 1788276601762 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-brio-1.1.5-r44h6168396_2.conda - sha256: 8fdeb734f259b3b4212e6099c695537bfa2c4affa303897d3bc0d2fa21e7f1fb - md5: 29cad55e2ccda4f88a79b76910768308 - depends: - - __osx >=11.0 - - r-base >=4.4,<4.5.0a0 - license: MIT - license_family: MIT - run_exports: {} - size: 44352 - timestamp: 1757421771711 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-cli-3.6.6-r44h1380947_0.conda - sha256: 7a3b600bc7f01518c34647fdedcacf578035de1ad1cc8d1ecf1f65ce80268adf - md5: b6342df713443364fa7c27e7d7ab3bfa - depends: - - __osx >=11.0 - - libcxx >=19 - - r-base >=4.4,<4.5.0a0 - license: MIT - license_family: MIT - run_exports: {} - size: 1325539 - timestamp: 1775734162346 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-colorspace-2.1_3-r44hbe92478_0.conda - sha256: 607c9edcc9737a61ce21f04d18c313ffa7500e92cccde1a38fa862851b692010 - md5: 9dc5294ca79abd1df064e64167ec46d8 - depends: - - __osx >=11.0 - - r-base >=4.4,<4.5.0a0 - license: BSD-3-Clause - license_family: BSD - run_exports: {} - size: 2543593 - timestamp: 1784283671856 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-data.table-1.18.4-r44h73351a1_0.conda - sha256: 11cbbf6022cd018f8fc10461a5bbca7a32a41b96ab402d72a5283cdae22d8415 - md5: 0f8dbc4e046ab0fea2fc14e98382c3e2 - depends: - - __osx >=11.0 - - libzlib >=1.3.2,<2.0a0 - - llvm-openmp >=19.1.7 - - r-base >=4.4,<4.5.0a0 - license: MPL-2.0 - license_family: OTHER - run_exports: {} - size: 2524750 - timestamp: 1783428449985 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-diffobj-0.3.8-r44hbe92478_0.conda - sha256: ecb34995c9695637d0774a4d66504e4ef9e6f37616f7a9a47ddc3d9688ed6471 - md5: 76c661b366f9af4f2440105206ff3da9 - depends: - - __osx >=11.0 - - r-base >=4.4,<4.5.0a0 - - r-crayon >=1.3.2 - license: GPL-2.0-or-later - license_family: GPL2 - run_exports: {} - size: 1003948 - timestamp: 1784367277480 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-digest-0.6.39-r44hc1cd577_0.conda - sha256: 945e67be78a409972cf3ceed7553f564477b41c170ac9397364667c3ed2241ef - md5: 4c2634f2998034129027c1c497593446 - depends: - - __osx >=11.0 - - libcxx >=19 - - r-base >=4.4,<4.5.0a0 - license: GPL-2.0-or-later - license_family: GPL2 - run_exports: {} - size: 208639 - timestamp: 1763567147222 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-dplyr-1.2.1-r44h1380947_0.conda - sha256: fb9adcd9828a48b6a343566fc41a051a1e5525e2963ca1f78bdc864f62366f05 - md5: 2d01bf37917c63141f80ce03e5b19623 - depends: - - __osx >=11.0 - - libcxx >=19 - - r-base >=4.4,<4.5.0a0 - - r-ellipsis - - r-generics - - r-glue >=1.3.2 - - r-lifecycle >=1.0.0 - - r-magrittr >=1.5 - - r-pillar >=1.5.1 - - r-r6 - - r-rlang >=0.4.10 - - r-tibble >=2.1.3 - - r-tidyselect >=1.1.0 - - r-vctrs >=0.3.5 - license: MIT - license_family: MIT - run_exports: {} - size: 1445578 - timestamp: 1775207488829 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-ellipsis-0.3.3-r44hbe92478_0.conda - sha256: 6d9eec4fa6261822a00fe72b8bbfea7d905bb9e87883657808189bc2ffb46895 - md5: db8b6ddcb30fee47bc25b116816e0c03 - depends: - - __osx >=11.0 - - r-base >=4.4,<4.5.0a0 - - r-rlang >=0.3.0 - license: MIT - license_family: MIT - run_exports: {} - size: 34252 - timestamp: 1775287721272 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-fansi-1.0.7-r44h6168396_0.conda - sha256: 9d3fe7009ecb519c2aef6d3d174d8844d3b5829fa89371689be08416dce1a061 - md5: 3ebccb0b86f52c0f1abfd8a5fc200da9 - depends: - - __osx >=11.0 - - r-base >=4.4,<4.5.0a0 - license: GPL-2.0-or-later - license_family: GPL3 - run_exports: {} - size: 324050 - timestamp: 1763566765615 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-farver-2.1.2-r44hc1cd577_2.conda - sha256: cd6fe9eae71e49645e5abfa17fbc26402a3d5b600d3d64b8b757dfc40d1bfa9f - md5: 403a700560fb4dd30d32da9d77c2bc8d + - libprotobuf >=7.35.1,<7.35.2.0a0 + size: 2877232 + timestamp: 1787657252942 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpsl-0.23.1-h7a62e17_0.conda + sha256: 0140cb0d059ac531e476b85a543668b69b7dffec16f0d26269ab6c9b70546921 + md5: 75a4cdf128d016141db61732ea462276 depends: - __osx >=11.0 - libcxx >=19 - - r-base >=4.4,<4.5.0a0 + - icu >=78.3,<79.0a0 license: MIT license_family: MIT + purls: [] + run_exports: + weak: + - libpsl >=0.23.1,<0.24.0a0 + size: 72950 + timestamp: 1786443660105 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.12.14-h4e5ec87_3_cpython.conda + build_number: 3 + sha256: 8077074cff173a979bbd32b79455ba6f1b12468528eac155e96d8550c38770f6 + md5: 7fd5ad6d9d1ae0af3ae9f039a992a148 + depends: + - __osx >=11.0 + - libcxx >=21 + license: Python-2.0 + purls: [] run_exports: {} - size: 1364816 - timestamp: 1757441903999 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-fs-2.1.0-r44h45a6d21_0.conda - sha256: 482d0809cd039b9c86527fff21584f0b3dd2ef6a860433e4136b09a0e42a61e4 - md5: 25237c24099b2d514d2fbb57ab4deda2 + size: 1741765 + timestamp: 1788407528160 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h5d15848_2.conda + sha256: 06f49291605c379467987989ff08d44b470a23afeb690d7e078517871969bff7 + md5: b750c4f83563c7528634bdcfd28622ce depends: - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260526.0,<20260527.0a0 - libcxx >=19 - - libuv >=1.51.0,<2.0a0 - - r-base >=4.4,<4.5.0a0 - license: MIT - license_family: MIT - run_exports: {} - size: 236657 - timestamp: 1777152778881 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-glue-1.8.1-r44hbe92478_0.conda - sha256: 609404bb41166c46c695acd347d8e3d3662817b22c530e36717595bdee772e6d - md5: c591df72f9899cfce892fadb2ae841e1 + constrains: + - re2 2025.11.05.* + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - libre2-11 >=2025.11.5 + size: 166043 + timestamp: 1781312641918 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.4-hca69786_1.conda + sha256: 839b31d4830e896b4d315b551d27f2bb08026bc946df04bcc360d8627c3ba2cd + md5: fde96d40ebe9a9cb34e4122380189ddb depends: - __osx >=11.0 - - r-base >=4.4,<4.5.0a0 - license: MIT - license_family: MIT - run_exports: {} - size: 170947 - timestamp: 1776413888460 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-isoband-0.3.0-r44hc1cd577_0.conda - sha256: 4ddbf7ede8d3593a357ecd6eb0d4050d768987e1f55265cd6d158e94910375a1 - md5: 58cac23f153c99779d71fccfe68bcc94 + - icu >=78.3,<79.0a0 + - libzlib >=1.3.2,<2.0a0 + license: blessing + purls: [] + run_exports: + weak: + - libsqlite >=3.53.4,<4.0a0 + size: 942754 + timestamp: 1787051243846 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda + sha256: 8bfe837221390ffc6f111ecca24fa12d4a6325da0c8d131333d63d6c37f27e0a + md5: b68e8f66b94b44aaa8de4583d3d4cc40 + depends: + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - libssh2 >=1.11.1,<2.0a0 + size: 279193 + timestamp: 1745608793272 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.22.0-h1fb9c8a_2.conda + sha256: 568bb23db02b050c3903bec05edbcab84960c8c7e5a1710dac3109df997ac7f1 + md5: d006875f9a58a44f92aec9a7ebeb7150 depends: - __osx >=11.0 - libcxx >=19 - - r-base >=4.4,<4.5.0a0 - - r-cli - - r-cpp11 - - r-rlang - license: MIT - license_family: MIT - run_exports: {} - size: 1638787 - timestamp: 1766530745624 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-jsonlite-2.0.0-r44h6168396_1.conda - sha256: eda247436019daba0951da897d28d788a93230b3d621a7bf88a125d66ae62e0c - md5: ba74496412d0d7d6b463be89badd3bbd + - libevent >=2.1.12,<2.1.13.0a0 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.6,<4.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - libthrift >=0.22.0,<0.22.1.0a0 + size: 323017 + timestamp: 1777019893083 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.11.3-h2431656_0.conda + sha256: ae1a82e62cd4e3c18e005ae7ff4358ed72b2bfbfe990d5a6a5587f81e9a100dc + md5: 2255add2f6ae77d0a96624a5cbde6d45 depends: - __osx >=11.0 - - r-base >=4.4,<4.5.0a0 license: MIT license_family: MIT - run_exports: {} - size: 635615 - timestamp: 1757420155302 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-lattice-0.22_9-r44hbe92478_0.conda - sha256: 374fa9e9bd523d9e0c4e4cebbd38c766a4426c0ce21dc06f240a9d661e433058 - md5: c98ddce8b4aacd27f4427febd0a40138 + purls: [] + run_exports: + weak: + - libutf8proc >=2.11.3,<2.12.0a0 + size: 87916 + timestamp: 1768735311947 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.3-h5ef1a60_0.conda + sha256: ff75b84cdb9e8d123db2fa694a8ac2c2059516b6cbc98ac21fb68e235d0fd354 + md5: 19edaa53885fc8205614b03da2482282 depends: - __osx >=11.0 - - r-base >=4.4,<4.5.0a0 - license: GPL-2.0-or-later - license_family: GPL3 + - icu >=78.3,<79.0a0 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.3,<6.0a0 + - libzlib >=1.3.2,<2.0a0 + constrains: + - libxml2 2.15.3 + license: MIT + license_family: MIT + purls: [] run_exports: {} - size: 1415205 - timestamp: 1770694429864 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-magrittr-2.0.5-r44hbe92478_0.conda - sha256: 3307560747b5fda8d3a4475bae40e5a4cfea9f4f92221c8e5a018a7cad1dba17 - md5: 89a513908036c039689914f74f817b5e + size: 466360 + timestamp: 1776377102261 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.3-h5654f7c_0.conda + sha256: 2fe1d8de0854342ae9cabe408b476935f82f5636e153b3b497456264dc8ff3a1 + md5: 8e037d73747d6fe34e12d7bcac10cf21 depends: - __osx >=11.0 - - r-base >=4.4,<4.5.0a0 + - icu >=78.3,<79.0a0 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.3,<6.0a0 + - libxml2-16 2.15.3 h5ef1a60_0 + - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT - run_exports: {} - size: 211263 - timestamp: 1775299093094 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-matrix-1.7_6-r44h3f89421_0.conda - sha256: 0896c06b6914502a928140c183d5b0633402b17cc1f7eaed37e52d9d5545077e - md5: bf448919ea2ee00c47d3ee18ca5d956a + purls: [] + run_exports: + weak: + - libxml2 + - libxml2-16 >=2.15.3 + size: 41102 + timestamp: 1776377119495 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda + sha256: a18fa5d5bac452401459f966cf0d872224e8080c4ff93c77e168d43ab42ef9d7 + md5: f39288f0ea63ae962e1a2e4f355a0d75 depends: - __osx >=11.0 - - libblas >=3.9.0,<4.0a0 - - liblapack >=3.9.0,<4.0a0 - - r-base >=4.4,<4.5.0a0 - - r-lattice - license: GPL-2.0-or-later - license_family: GPL3 - run_exports: {} - size: 4150889 - timestamp: 1786437940233 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-nanoparquet-0.5.1-r44h1380947_0.conda - sha256: fa72bc03e4c2dd75de6397ddd5d6935204a4e12731fbbe1b88b0445f591c477d - md5: b1be54ab86a770fb1fefa28e6b2593bf + 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-22.1.8-hc7d1edf_0.conda + sha256: ccbaad6bbc88f135ab849bc36af5fa6eda36a9ed18ce6f58e3dde3d11784c156 + md5: a9c118f6343fb6301b6f3b4e94c4c562 depends: - __osx >=11.0 - - libcxx >=19 - - r-base >=4.4,<4.5.0a0 - license: MIT + file - license_family: MIT - run_exports: {} - size: 781810 - timestamp: 1777144793907 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-parallelly-1.48.0-r44hbe92478_0.conda - sha256: 69deb293691d8f544f7f02a1788b4ef16ee51e12df62cde8aa4ef004f7c93d32 - md5: 4f638603989e65840dfe78cced8aa260 + constrains: + - intel-openmp <0.0a0 + - openmp 22.1.8|22.1.8.* + license: Apache-2.0 WITH LLVM-exception + license_family: APACHE + purls: [] + run_exports: + strong: + - llvm-openmp >=22.1.8 + size: 286313 + timestamp: 1781736516782 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvmlite-0.49.0-py312ha6a25cf_3.conda + sha256: 35ca057b26522a44a0a58a3dcd083d02b5ce9424f96512802b01de2c39471272 + md5: 34c4344fcf4df4648f56e361faf3598c depends: + - python - __osx >=11.0 - - r-base >=4.4,<4.5.0a0 - license: LGPL-2.1-or-later - license_family: LGPL + - libcxx >=21 + - python_abi 3.12.* *_cp312 + - zstd >=1.5.7,<1.6.0a0 + - libzlib >=1.3.2,<2.0a0 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/llvmlite?source=hash-mapping run_exports: {} - size: 643344 - timestamp: 1782772272274 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-processx-3.9.0-r44hbe92478_0.conda - sha256: 67976a77cf644b85156dc6edbf7ef63cd6e0a984ea0660310585ba1bd71d1a83 - md5: c5fa9d5550e95566fef7082d64c33f74 + size: 30495799 + timestamp: 1788518430084 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-hdca3b7d_2.conda + sha256: 1887867b393eb3c2b336deaffcf228574821ea1e0dcb9ef7bf6c9f59cec40f03 + md5: d47f7f3481c6f2fcc4ed22802f61c6dd depends: - __osx >=11.0 - - r-base >=4.4,<4.5.0a0 - - r-ps >=1.2.0 - - r-r6 - license: MIT - license_family: MIT + - libcxx >=21 + license: BSD-2-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - lz4-c >=1.10.0,<1.11.0a0 + size: 171838 + timestamp: 1786717209785 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.2.2-py312h67ba313_2.conda + sha256: 0c46b4c5558de04c54e71a8c4dc8217f3157f35b922c09c90340a8dd41c44644 + md5: e3250b19686bec4b2ee9cf37f6c7b127 + depends: + - python + - libcxx >=21 + - __osx >=11.0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/msgpack?source=hash-mapping run_exports: {} - size: 398523 - timestamp: 1776866414094 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-ps-1.9.3-r44hbe92478_0.conda - sha256: 3a46645ce5bee19c6f38937732c8e395ce28925b978799f7244208ea823bed5b - md5: cb766e4dfff2a7bca8dae7ace174eaba + size: 100932 + timestamp: 1788525119108 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgspec-0.21.1-py312h580468c_1.conda + sha256: 1ed2f302ea034f4eb3672394a7dcf0da7dec81bc81e73dde4af4b489295d859d + md5: abf81186c64601851b77b90b8e7f2def depends: - __osx >=11.0 - - r-base >=4.4,<4.5.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD + purls: + - pkg:pypi/msgspec?source=hash-mapping run_exports: {} - size: 409333 - timestamp: 1777148912933 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-purrr-1.2.2-r44hbe92478_0.conda - sha256: 0bbba90fd77652a3ce62aceaa68a51aa893cad74255b6a6ca62cb0258da54ca4 - md5: 8f36a8cb7cd7db752f1de63c7b7365e2 + size: 198801 + timestamp: 1788775519399 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda + sha256: 7024a48c8c0d0114ed4ab53c76bf9275d50e91ba7cea367a9aead638d3c29c68 + md5: 3dfa0d0316dc246cd44937a557de4501 depends: - __osx >=11.0 - - r-base >=4.4,<4.5.0a0 - - r-cli >=3.4 - - r-lifecycle >=1.0.3 - - r-magrittr >=1.5 - - r-rlang >=0.4.10 - - r-vctrs >=0.5 + 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/nlohmann_json-3.12.0-h784d473_2.conda + sha256: 3c7235b1574907737d09b46b3a0f7f0f5fcafc2380f2c8d02fc55aa23ce47c3c + md5: 0debf38c50bb3ea6f712c87310a00289 + constrains: + - nlohmann_json-abi ==3.12.0 license: MIT license_family: MIT - run_exports: {} - size: 547690 - timestamp: 1775854066177 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-rcpp-1.1.2-r44h1380947_0.conda - sha256: 588b866bd2e921cac3473316248ceb0a053298f6e0e12747c3a9db6edba5fbfd - md5: c81d478481d9c91bec344e4f2092d5e4 - depends: + purls: [] + run_exports: {} + size: 137984 + timestamp: 1785920576349 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/numba-0.67.0-py312h9052ff5_1.conda + sha256: 9349740cc8fd898ac2bdc7b98240def21465446e11c0bb6ef15fc485821f9eb6 + md5: 9ca4ff4acd4ca347f63c746b94a3f87a + depends: + - python + - llvmlite >=0.49.0,<0.50.0a0 + - numpy >=1.22.3,<2.6 + - llvm-openmp >=21.1.8 + - libcxx >=21 - __osx >=11.0 - - libcxx >=19 - - r-base >=4.4,<4.5.0a0 - license: GPL-2.0-or-later - license_family: GPL2 + - numpy >=1.25,<3 + - python_abi 3.12.* *_cp312 + constrains: + - tbb >=2021.6.0 + - libopenblas >=0.3.18,!=0.3.20 + - cuda-version >=11.2 + - cudatoolkit >=11.2 + - scipy >=1.0 + - cuda-python >=11.6 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/numba?source=hash-mapping run_exports: {} - size: 2132256 - timestamp: 1783265116020 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-readr-2.2.0-r44h1380947_0.conda - sha256: b3c535dc796abd81e1a00aa0d16732a316382a4c501ab984339f5c2b366ea0c1 - md5: c78e4cb53d8a8b91be4be7c7b59122ba + size: 6086821 + timestamp: 1787145531837 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/numcodecs-0.16.5-py312h5978115_0.conda + sha256: 6bd51c8f1a194289f741dd7112f3ad071856443f707926233a1323347df149c0 + md5: 1de2e6a0fda23e8fb7e31c87b3a422a8 depends: - __osx >=11.0 + - deprecated - libcxx >=19 - - r-base >=4.4,<4.5.0a0 - - r-cli - - r-clipr - - r-cpp11 - - r-crayon - - r-hms >=0.4.1 - - r-lifecycle >=0.2.0 - - r-r6 - - r-rlang - - r-tibble - - r-tzdb >=0.1.1 - - r-vroom >=1.5.4 + - msgpack-python + - numpy >=1.23,<3 + - numpy >=1.24 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - typing_extensions license: MIT license_family: MIT + purls: + - pkg:pypi/numcodecs?source=hash-mapping run_exports: {} - size: 803568 - timestamp: 1771573983670 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-rlang-1.3.0-r44h1380947_0.conda - sha256: ba25fe7ca5990f6b0631480adf1081a1b2ea2072e67d1390cf68bc3957686b8c - md5: 736e5f677b1fef730ccaff85f93cbc43 + size: 660694 + timestamp: 1764782989453 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.3-py312hff34920_0.conda + sha256: 35088d35c23273076b9e2ba98e002ca5b765f743333a9889598cfd41d07b7eaa + md5: 6c950b1c9f19db9df914704508f3be20 depends: + - python - __osx >=11.0 - - libcxx >=19 - - r-base >=4.4,<4.5.0a0 - license: GPL-3.0-only - license_family: GPL3 - run_exports: {} - size: 1593330 - timestamp: 1783260600185 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-s7-0.2.2-r44hbe92478_0.conda - sha256: 1aae221e28e777867ab480dec7cf8829e6046a249883c79efd052a3d6202e37b - md5: fd92c946a6aafc9c7f8940384a5c60e7 + - libcxx >=21 + - python_abi 3.12.* *_cp312 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - liblapack >=3.9.0,<4.0a0 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/numpy?source=hash-mapping + run_exports: + weak: + - numpy >=1.25,<3 + size: 7114133 + timestamp: 1788731997804 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjdk-25.0.2-h258754b_0.conda + sha256: f6f06243c1a35b6590f06be386bd5cefc5b1773b985a61b7917b93dab4cf18ec + md5: a4024e03865a7411c1028eac83d1956c depends: - __osx >=11.0 - - r-base >=4.4,<4.5.0a0 - license: MIT - license_family: MIT + - libzlib >=1.3.1,<2.0a0 + license: GPL-2.0-or-later WITH Classpath-exception-2.0 + license_family: GPL + purls: [] run_exports: {} - size: 311169 - timestamp: 1776862054507 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-testthat-3.3.2-r44h1380947_0.conda - sha256: 0e6a3bdbc7fa682a13947acd84dd223ca8362ef7099279cf33ed8aebd05d0175 - md5: 85790fa8f7768c27caeeaef1d645e163 + size: 199165105 + timestamp: 1771443790144 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.3-hd24854e_1.conda + sha256: 66be2283b5b37dcda1332b5e74c1782a8cb14fd2e62e0d38017c2d35bf73c119 + md5: 65d1906712b85d1679263c518d011b5b depends: - __osx >=11.0 - - libcxx >=19 - - r-base >=4.4,<4.5.0a0 - - r-brio >=1.1.3 - - r-callr >=3.7.3 - - r-cli >=3.6.1 - - r-desc >=1.4.2 - - r-digest >=0.6.33 - - r-evaluate >=1.0.1 - - r-jsonlite >=1.8.7 - - r-lifecycle >=1.0.3 - - r-magrittr >=2.0.3 - - r-pkgload >=1.3.2.1 - - r-praise >=1.0.0 - - r-processx >=3.8.2 - - r-ps >=1.7.5 - - r-r6 >=2.5.1 - - r-rlang >=1.1.1 - - r-waldo >=0.6.0 - - r-withr >=3.0.2 - license: MIT - license_family: MIT - run_exports: {} - size: 1826573 - timestamp: 1768138651838 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-tibble-3.3.1-r44hbe92478_0.conda - sha256: f8e19ff2073a503eb15ad8d305600f75924f68caf599bcd6031b4009f09e6c8d - md5: c145e183683d1536fb15251a28bbec43 + - ca-certificates + license: Apache-2.0 + license_family: Apache + purls: [] + run_exports: + weak: + - openssl >=3.6.3,<4.0a0 + size: 3109132 + timestamp: 1785913735357 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.3.1-hef2a647_0.conda + sha256: 11b8e71ecdf9d50c9c5249e009b9f7d13d6df68091bd70fe202f3c3c16d0e3d8 + md5: d5626f645bea2fb646e12910c181fc79 depends: + - tzdata + - __osx >=12.0 + - libcxx >=19 + - libprotobuf >=7.35.1,<7.35.2.0a0 + - zstd >=1.5.7,<1.6.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.2,<1.3.0a0 + - libzlib >=1.3.2,<2.0a0 + - libabseil >=20260526.0,<20260527.0a0 + - libabseil * cxx17* + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - orc >=2.3.1,<2.3.2.0a0 + size: 513679 + timestamp: 1784301269750 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.6-np2py312hd9090a6_0.conda + sha256: 8b883408378d695f41c8a97116b69454cbd38b6356fa4d9b669a127186ea2b6e + md5: 4aa838a92c578c7fdcc2921fd17f14c7 + depends: + - python + - numpy >=1.26.0 + - python-dateutil >=2.8.2 + - libcxx >=21 - __osx >=11.0 - - r-base >=4.4,<4.5.0a0 - - r-fansi >=0.4.0 - - r-lifecycle >=1.0.0 - - r-magrittr - - r-pillar >=1.8.1 - - r-pkgconfig - - r-rlang >=1.0.2 - - r-vctrs >=0.4.2 - license: MIT - license_family: MIT + - python_abi 3.12.* *_cp312 + - numpy >=1.25,<3 + constrains: + - adbc-driver-postgresql >=1.2.0 + - adbc-driver-sqlite >=1.2.0 + - beautifulsoup4 >=4.12.3 + - blosc >=1.21.3 + - bottleneck >=1.4.2 + - fastparquet >=2024.11.0 + - fsspec >=2024.10.0 + - gcsfs >=2024.10.0 + - html5lib >=1.1 + - hypothesis >=6.116.0 + - jinja2 >=3.1.5 + - lxml >=5.3.0 + - matplotlib >=3.9.3 + - numba >=0.60.0 + - numexpr >=2.10.2 + - odfpy >=1.4.1 + - openpyxl >=3.1.5 + - psycopg2 >=2.9.10 + - pyarrow >=13.0.0 + - pyiceberg >=0.8.1 + - pymysql >=1.1.1 + - pyqt5 >=5.15.9 + - pyreadstat >=1.2.8 + - pytables >=3.10.1 + - pytest >=8.3.4 + - pytest-xdist >=3.6.1 + - python-calamine >=0.3.0 + - pytz >=2024.2 + - pyxlsb >=1.0.10 + - qtpy >=2.4.2 + - scipy >=1.14.1 + - s3fs >=2024.10.0 + - sqlalchemy >=2.0.36 + - tabulate >=0.9.0 + - xarray >=2024.10.0 + - xlrd >=2.0.1 + - xlsxwriter >=3.2.0 + - zstandard >=0.23.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pandas?source=compressed-mapping run_exports: {} - size: 590332 - timestamp: 1768139386281 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-tzdb-0.5.0-r44hc1cd577_2.conda - sha256: 1aadd8c39242501437400bb0cc889ed6251a49b3d3e9013b4af733e31fbab9ea - md5: 2bf8a2ea9f0fd51982d4e84a9b745995 + size: 13983864 + timestamp: 1789803426988 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/prometheus-cpp-1.3.0-h0967b3e_0.conda + sha256: 851a77ae1a8e90db9b9f3c4466abea7afb52713c3d98ceb0d37ba6ff27df2eff + md5: 7172339b49c94275ba42fec3eaeda34f depends: - __osx >=11.0 - - libcxx >=19 - - r-base >=4.4,<4.5.0a0 - - r-cpp11 >=0.5.2 + - libcurl >=8.10.1,<9.0a0 + - libcxx >=18 + - libzlib >=1.3.1,<2.0a0 + - zlib license: MIT license_family: MIT + purls: [] + run_exports: + weak: + - prometheus-cpp >=1.3.0,<1.4.0a0 + size: 173220 + timestamp: 1730769371051 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-25.0.0-py312h1f38498_0.conda + sha256: 6dc62ae41f925b968e8e5c9879f77dee3db89fdb94f6ba49f884f0ad2bb945e6 + md5: e936d3c2f56d36ded597eeea76aeed23 + depends: + - libarrow-acero 25.0.0.* + - libarrow-dataset 25.0.0.* + - libarrow-substrait 25.0.0.* + - libparquet 25.0.0.* + - pyarrow-core 25.0.0 *_0_* + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: [] run_exports: {} - size: 547234 - timestamp: 1757490583980 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-utf8-1.2.6-r44h6168396_1.conda - sha256: 7f890ef0ba543e8351af8ff89bbf85da8b45908b17726dab7dce885c0f1b745c - md5: 0a93729634c01615f566c7a9b2ae5ef8 + size: 25882 + timestamp: 1784258390841 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-25.0.0-py312h7d9569a_0_cpu.conda + sha256: 3c7ec6354bf8878f8045af7dd97a170300a9cae9ed430de3beb2ce18e8274bef + md5: 34596f53aa0ba292df864cff850e95fb depends: - __osx >=11.0 - - r-base >=4.4,<4.5.0a0 + - libarrow 25.0.0.* *cpu + - libarrow-compute 25.0.0.* *cpu + - libcxx >=21 + - libzlib >=1.3.2,<2.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - apache-arrow-proc * cpu + - numpy >=1.23,<3 license: Apache-2.0 license_family: APACHE + purls: + - pkg:pypi/pyarrow?source=hash-mapping run_exports: {} - size: 144919 - timestamp: 1757425308492 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-vctrs-0.7.3-r44h1380947_0.conda - sha256: 0e0c00c84f73258b7c32f66e41a219efc66b95d724ab9b6216203c1d0821f972 - md5: a91746997cd0f95ecc8640771d9722e5 + size: 4371556 + timestamp: 1784258363255 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.5-py312ha80e978_2.conda + sha256: 8b500b8e268e5f5b026d7c60fe7cc7ab6c6285a606cabffba09c40bb6e6e2522 + md5: 9fd849baadcaaf840cb14db14bff0803 depends: + - python + - typing-extensions >=4.6.0,!=4.7.0 + - __osx >=11.0 + - python_abi 3.12.* *_cp312 + constrains: - __osx >=11.0 - - libcxx >=19 - - r-base >=4.4,<4.5.0a0 - - r-cli >=3.4.0 - - r-glue - - r-lifecycle >=1.0.3 - - r-rlang >=1.0.6 license: MIT - license_family: MIT + purls: + - pkg:pypi/pydantic-core?source=compressed-mapping run_exports: {} - size: 1764693 - timestamp: 1775898358775 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/r-vroom-1.7.1-r44h1380947_0.conda - sha256: 0ec6e52b036e5f6fd449b1b1e4d53f7571a7a03acda7f5f493723ac2dadc477f - md5: 738f1226bc735fb89e80a211fecfcd6e + size: 1689941 + timestamp: 1789996115511 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.14-hd05a0c4_3_cpython.conda + build_number: 3 + sha256: fee8a2dd8a6ee98d430625d4c0f705e86c1154b1eeae5e10004c3c4d7d536eba + md5: 287a1be640df74bd3e2b9d76b2a7b300 depends: - __osx >=11.0 - - libcxx >=19 - - r-base >=4.4,<4.5.0a0 - - r-bit64 - - r-cli - - r-cpp11 >=0.2.0 - - r-crayon - - r-glue - - r-hms - - r-lifecycle - - r-progress >=1.2.1 - - r-rlang >=0.4.2 - - r-tibble >=2.0.0 - - r-tidyselect - - r-tzdb >=0.1.1 - - r-vctrs >=0.2.0 - - r-withr + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.8.1,<3.0a0 + - libffi >=3.7.0,<3.8.0a0 + - liblzma >=5.8.3,<6.0a0 + - libpython 3.12.14 h4e5ec87_3_cpython + - libsqlite >=3.53.4,<4.0a0 + - libzlib >=1.3.2,<2.0a0 + - ncurses >=6.6,<7.0a0 + - openssl >=3.5.8,<4.0a0 + - readline >=8.3,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + purls: [] + run_exports: + weak: + - python_abi 3.12.* *_cp312 + noarch: + - python + size: 11705033 + timestamp: 1788407558326 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py312h04c11ed_1.conda + sha256: 737959262d03c9c305618f2d48c7f1691fb996f14ae420bfd05932635c99f873 + md5: 95a5f0831b5e0b1075bbd80fcffc52ac + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT + purls: + - pkg:pypi/pyyaml?source=hash-mapping run_exports: {} - size: 832537 - timestamp: 1774941634408 + size: 187278 + timestamp: 1770223990452 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-h5d60da5_2.conda + sha256: ba1154085d70e8e8f94cfc38a018a13d1a734ff1bcc2ab384e4fc3e532b23066 + md5: a2578dc92c16ab5b6b183cfcdc8e8b79 + depends: + - libre2-11 2025.11.05 h5d15848_2 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - libre2-11 >=2025.11.5 + - re2 + size: 27372 + timestamp: 1781312662146 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda sha256: a77010528efb4b548ac2a4484eaf7e1c3907f2aec86123ed9c5212ae44502477 md5: f8381319127120ce51e081dce4865cf4 @@ -5701,34 +5446,80 @@ packages: - ncurses >=6.5,<7.0a0 license: GPL-3.0-only license_family: GPL + purls: [] run_exports: weak: - readline >=8.3,<9.0a0 size: 313930 timestamp: 1765813902568 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/sigtool-codesign-0.1.3-h98dc951_1.conda - sha256: 7efd6d7d18bf9cc5788bfe1c1e1620d9dbbe00a81c646814929a82ff31944cf3 - md5: a322c0a0d3b5be99ee11f9ccec99b60e +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.16.7-ha7a9cbd_0.conda + noarch: python + sha256: a8fdd5b1906b4265d61bde50646bc5f8a4605d1644198501f384973d08966874 + md5: 8287f02a7a4c8babf7d05b7966b3bfe3 depends: + - python + - __osx >=11.0 + constrains: - __osx >=11.0 - - libsigtool 0.1.3 h98dc951_1 - - openssl >=3.5.7,<4.0a0 license: MIT license_family: MIT + purls: + - pkg:pypi/ruff?source=compressed-mapping run_exports: {} - size: 114653 - timestamp: 1786115093248 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tapi-1600.0.11.8-hb561403_3.conda - sha256: 9f1cc109e6e57861110e9de6e03beca7fd2b7fbdb46124cccc07990b0844d88a - md5: 0676b8719dd1e9bb1003e59a481c6c3e + size: 8210669 + timestamp: 1789115818497 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/s2n-1.7.5-h1b28cb6_1.conda + sha256: 47ec36bdd05117c974853a8dbaa9d89605a5f70b592d47c7d198b20252ddf1f4 + md5: 94b3b302cd6c07c5ec68648b25c8c525 depends: - - libcxx >=19.0.0.a0 - __osx >=11.0 - - ncurses >=6.6,<7.0a0 - license: NCSA + - openssl >=3.5.7,<4.0a0 + license: Apache-2.0 + license_family: Apache + purls: [] + run_exports: + weak: + - s2n >=1.7.5,<1.7.6.0a0 + size: 276705 + timestamp: 1783165153651 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.18.1-py312h84b0c4e_0.conda + sha256: 338f3c98ff63120fc0f5686ab59fa16419d8941c82e2803996475f86220a1ef0 + md5: 2938bfc2403b10dd18ca4600846ffd90 + depends: + - __osx >=11.0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libcxx >=21 + - libgcc >=15 + - libgfortran + - libgfortran5 >=15.3.0 + - liblapack >=3.9.0,<4.0a0 + - numpy <2.8 + - numpy >=1.25,<3 + - numpy >=2.0.0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scipy?source=compressed-mapping run_exports: {} - size: 200397 - timestamp: 1785906507697 + size: 14163307 + timestamp: 1789136771189 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda + sha256: cb9305ede19584115f43baecdf09a3866bfcd5bcca0d9e527bd76d9a1dbe2d8d + md5: fca4a2222994acd7f691e57f94b750c5 + depends: + - libcxx >=19 + - __osx >=11.0 + license: BSD-3-Clause + license_family: BSD + purls: [] + run_exports: + weak: + - snappy >=1.2.2,<1.3.0a0 + size: 38883 + timestamp: 1762948066818 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hd3d0363_3.conda sha256: 47186bc7ab8d7e8bee86bbd1a917196f8c21cf63f081fc33cd6d1221af087580 md5: 8e3cf0e455e6b54519f0b1c72c61780a @@ -5736,22 +5527,38 @@ packages: - __osx >=11.0 - libzlib >=1.3.2,<2.0a0 license: TCL + purls: [] run_exports: weak: - tk >=8.6.13,<8.7.0a0 size: 3338712 timestamp: 1784229090530 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tktable-2.10-h28d2b5e_8.conda - sha256: ffb3c72193a9bdb847ea3c95326d299c5788d18d69069ae1e01e717f07fa3626 - md5: 4b5985e749e865290a22d9752955a6ce +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-2.4.1-py312hbd136b4_0.conda + sha256: 780ed94f86fdac07adb2dc560b5904478bbe55fb43877d51d42904ac9213570c + md5: fe877c9977ac08ac1cebe464f68a64c2 depends: - - tk + - python - __osx >=11.0 - - tk >=8.6.13,<8.7.0a0 - license: TCL + - python_abi 3.12.* *_cp312 + license: BSD-2-Clause + purls: + - pkg:pypi/wrapt?source=compressed-mapping run_exports: {} - size: 91561 - timestamp: 1773732981206 + size: 139441 + timestamp: 1789986025690 +- 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 + purls: [] + run_exports: + weak: + - yaml >=0.2.5,<0.3.0a0 + size: 77530 + timestamp: 1787228556857 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_3.conda sha256: ab46d85e4fcffff1b1c5cac517afa40b7ae784cf76fc9da1f55f6a6934291eb6 md5: 2c966485853aa27985fbec7e3fcc4e77 @@ -5760,6 +5567,7 @@ packages: - libzlib 1.3.2 h8088a28_3 license: Zlib license_family: Other + purls: [] run_exports: weak: - libzlib >=1.3.2,<2.0a0 @@ -5773,8 +5581,49 @@ packages: - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD + purls: [] run_exports: weak: - zstd >=1.5.7,<1.6.0a0 size: 433413 timestamp: 1764777166076 +- pypi: ./ + name: watteg + requires_dist: + - numpy>=1.24 + - scipy>=1.10 + - pandas>=2.0 + - h5py>=3.8 + - pyarrow>=14 + - pysceptre + - pytest>=7 ; extra == 'dev' + - ruff>=0.5 ; extra == 'dev' + requires_python: '>=3.10' +- pypi: git+https://github.com/broadinstitute/pysceptre.git?tag=v0.2.0#72b5e77a76a123afe80e5c55308d7883bd0927f4 + name: pysceptre + version: 0.2.0 + requires_dist: + - numpy>=1.24 + - scipy>=1.10 + - pandas>=2.0 + - threadpoolctl>=3.0 + - pytest>=7.0 ; extra == 'dev' + - ruff>=0.6 ; extra == 'dev' + - pre-commit>=3.0 ; extra == 'dev' + - numba>=0.58 ; extra == 'fast' + - mudata>=0.3 ; extra == 'io' + - anndata>=0.10 ; extra == 'io' + - pyarrow>=14 ; extra == 'io' + - matplotlib>=3.7 ; extra == 'io' + - pysceptre[io] ; extra == 'examples' + - scanpy>=1.10 ; extra == 'examples' + - igraph>=0.11 ; extra == 'examples' + - leidenalg>=0.10 ; extra == 'examples' + - mkdocs-material>=9.5 ; extra == 'docs' + - mkdocstrings[python]>=0.26 ; extra == 'docs' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/43/3f/f88a53f60a472b46f4023f56d204dd7de33d34c5d2acbfa0d70a674e639e/threadpoolctl-3.7.0-py3-none-any.whl + name: threadpoolctl + version: 3.7.0 + sha256: cd8b60b5641b45c67bbf73c64c843235fc2d8a480c87389f52f5dbee893b86be + requires_python: '>=3.9' diff --git a/pixi.toml b/pixi.toml index 1d788fb..2a87803 100644 --- a/pixi.toml +++ b/pixi.toml @@ -1,159 +1,58 @@ [workspace] name = "watteg" -description = "SCEPTRE-based power analysis for element-gene pairs in single-cell CRISPR screens" +description = "Power analysis for element-gene pairs in single-cell CRISPR screens" channels = ["conda-forge", "bioconda"] platforms = ["linux-64", "osx-arm64"] -# sceptre is installed from a pinned GitHub commit by `pixi run setup`, not from a conda -# channel: it is not published on conda-forge or bioconda. The pipeline reads several -# *unexported* S4 slots of `sceptre_object`, so the version must be pinned exactly and -# verified by `pixi run check-api`. See docs/development.md. -[activation.env] -R_LIBS_USER = "$PIXI_PROJECT_ROOT/.pixi/rlibs" -# SCEPTRE_REF is the DESCRIPTION version at the pinned commit, not a git tag: 0.99.0 is the -# in-development Bioconductor-prep version on `main` and is untagged (v0.10.3 is still the newest -# tag). check_sceptre_api.R compares it against packageVersion("sceptre"). -SCEPTRE_REF = "v0.99.0" -SCEPTRE_SHA = "3ba046b86f33e99dee633fbb8704ee8defdb442d" -# ondisc, for odm-backed (out-of-core) response matrices. Same story as sceptre: not on -# conda-forge or bioconda, so it is installed from a pinned commit by `pixi run setup`. ONDISC_REF -# is the DESCRIPTION version at the pinned commit; ondisc is untagged, so like SCEPTRE_REF it names -# a version rather than a tag. -ONDISC_REF = "v1.3.5" -ONDISC_SHA = "589803edea2a66ad15e7f07186bee16c7739ab8c" - -# Runtime dependencies. Kept deliberately small: our own scripts use base R for all file -# I/O and data manipulation (read.delim/write.table/merge/aggregate) rather than pulling in -# the tidyverse. Every entry below is either called by our code or reached by the sceptre -# code path this pipeline uses -- each one verified by grepping sceptre's sources, not by -# copying its DESCRIPTION. -[dependencies] -r-base = "4.4.*" -r-optparse = "*" # CLI parsing for every src/ script -nextflow = "*" # pipeline runner (the only package from bioconda) - -# --- reached by the sceptre functions we call (run_discovery_analysis, get_result, -# import_data, set_analysis_parameters, assign_grnas, run_qc) -------------------- -r-matrix = "*" # sceptre NAMESPACE import; also used directly by our scripts -r-rcpp = ">=1.0.9" # sceptre NAMESPACE import -r-dplyr = "*" # s4_analysis_functs_*, pairwise_qc_functs, import_functs, assign_grna_functions -# quoted: an unquoted r-data.table would parse as the dotted TOML key r-data.table -"r-data.table" = "*" # medium_level_functs_v2, pairwise_qc_functs, aux_functions -r-purrr = "*" # purrr::flatten() in medium_level_functs_v2.R:123,205 (precomputation path) -r-crayon = "*" # console messages in check_functions, import_functs, s4_helpers -# Added by sceptre 0.99.0. Both are in its DESCRIPTION Imports, which R CMD INSTALL enforces, and -# both are reached at runtime rather than only at build time: parallelly::availableCores() replaced -# parallel::detectCores() for core detection (aux_functions.R), and withr::with_seed/with_options -# are used throughout. The lower bound is not the DESCRIPTION's >=1.23.0 but the >=1.47.0 that -# aux_functions.R's own comment calls for; conda-forge resolves to 1.48.0, which satisfies both. -r-parallelly = ">=1.47.0" -r-withr = "*" - -# --- consolidating the per-replicate output into Parquet --------------------------------- -# nanoparquet, NOT arrow. Both read and write Parquet; arrow pulls in 52 packages to do it -- -# the whole AWS C SDK, Azure storage, Google Cloud, gRPC, protobuf, OpenTelemetry, Thrift and ORC, -# 2,041 lines of lockfile -- because it ships a cloud filesystem layer. This pipeline reads local -# files. nanoparquet is a self-contained implementation with no dependencies of its own. -# -# What it gives up: Arrow datasets, partitioning and predicate pushdown. None are needed here -- -# consolidation writes one file per effect size, and a reader wants either all of it or a column -# subset, both of which nanoparquet does. -r-nanoparquet = "*" - -# --- reached by ondisc, for odm-backed (out-of-core) response matrices ------------------ -# -# ondisc itself is installed from a pinned commit by `pixi run setup` (see ONDISC_SHA above), not -# listed here -- it is not on conda-forge or bioconda. Its DESCRIPTION Imports are, and R CMD -# INSTALL enforces all of them, so they have to be present before that install can run. -# -# Already covered above: Matrix, Rcpp, dplyr, data.table, crayon. New here: -"bioconductor-rhdf5lib" = "*" # ondisc NAMESPACE `import(Rhdf5lib)` -- loaded on every library(ondisc), - # not build-only, so this is a RUNTIME dependency. Also its LinkingTo. -r-readr = "*" # ondisc DESCRIPTION Imports; 2 readr:: references in its sources - -# Rhdf5lib ships HDF5 as static archives (libhdf5.a, libhdf5_cpp.a) and ondisc links them into -# ondisc.so, but static HDF5 still needs its own dependencies resolved -- ondisc's link line ends -# `-lcrypto -lcurl -lsz -laec -lz -ldl -lm`. Without them the build dies at the very last step with -# `ld: cannot find -lz`, after every object file has already compiled, which reads like a broken -# toolchain rather than a missing package. -# -# These are runtime dependencies, not build-only: only HDF5 itself is static, so ondisc.so carries -# DT_NEEDED entries for these and needs them present whenever it loads. They sit here rather than -# in [feature.build.dependencies] for that reason -- the build environment inherits [dependencies] -# anyway, so one entry covers both. -zlib = "*" # -lz -libcurl = "*" # -lcurl -openssl = "*" # -lcrypto -libaec = "*" # -lsz and -laec (szip) - -# One Bioconductor package is required, and only one: bioconductor-rhdf5lib, above, which arrives -# with ondisc and nothing else. The rest of this note stands, and is the reason that is worth -# stating rather than assuming. -# -# The old pipeline used SingleCellExperiment purely -# as a container -- a per-gene table, a per-cell table, two sparse perturbation matrices, -# and row/column subsetting. Nothing on the live code path touched rowRanges or any genomic -# range (the only GRanges use was in filt_max_dist_pert(), part of the dead code removed in -# Step 1), so lib/sim_input.R replaces it with a plain list plus subset helpers. That -# drops SingleCellExperiment, SummarizedExperiment, S4Vectors, GenomicRanges, IRanges, -# GenomeInfoDb, GenomeInfoDbData, DelayedArray, MatrixGenerics, Biobase, BiocGenerics, -# XVector, S4Arrays, SparseArray, abind and zlibbioc. +# No R. The pipeline was an R/sceptre pipeline until the Python port; every step now runs on +# pysceptre, so this environment is one Python package plus a workflow runner. What that removes is +# not just r-base: it is the pinned sceptre commit, the local patch applied to it, the pinned ondisc +# commit, the `setup` task that installed both from source, the `check-api` task that verified the +# pin against the unexported S4 slots the pipeline read, and the HDF5/curl/openssl link-line +# dependencies ondisc's static build needed. See docs/pysceptre-backend.md. # -# It also sidesteps a real setup failure: bioconductor-genomeinfodbdata installs its data -# through a conda post-link script, which pixi skips by default, so GenomeInfoDb -- and -# therefore SingleCellExperiment -- could not be loaded at all without every user first -# running `pixi config set --local run-post-link-scripts insecure`. -# -# Deliberately NOT included, unlike the old envs/power_analysis.yml: -# bioconductor-deseq2 only reachable via fit_negbinom_deseq2(), which nothing calls; -# poscounts size factors are computed directly in -# prepare_sim_input.R -# bioconductor-rtracklayer referenced nowhere in the codebase -# r-devtools was only there to install_github(sceptre) at runtime, which -# raced across parallel tasks and wrote into the shared env -# r-tidyverse/r-tibble replaced by base R in our scripts -# -# r-readr was in that list until ondisc arrived: our own scripts still use base R for all file -# I/O, but ondisc's DESCRIPTION Imports it and R CMD INSTALL enforces that field. It is here for -# ondisc's benefit, not ours -- the same reason most of the runtime list exists for sceptre's. +# The R implementation has not been deleted -- it is the reference the Python path was validated +# against and what the paper describes -- but it is no longer what this environment provides. To run +# it, use the `r-implementation` branch, an exact snapshot of the pipeline before the port. +# (`legacy` is a different thing: the Snakemake implementation that preceded both.) -# Building sceptre from source needs a compiler toolchain, remotes, and -- as verified by -# a failed install -- every package in sceptre's DESCRIPTION Imports, because R CMD INSTALL -# enforces that field even for packages absent from NAMESPACE. ggplot2, cowplot and scales -# are needed *only* for that check: at runtime they are confined to plotting_functions.R, -# qq_plot_helpers.R and write_outputs_to_directory(), none of which this pipeline calls. So -# they stay out of the runtime environment that every Nextflow task activates. -[feature.build.dependencies] -r-remotes = "*" -c-compiler = "*" -cxx-compiler = "*" -# Header-only Boost. sceptre declares `LinkingTo: Rcpp, BH`, so BH is used to *compile* the C++ and -# is never loaded at runtime -- confirmed by src/audit_dependencies.R, which finds it the only -# declared dependency with no reference anywhere in the package's code. It sat in the runtime -# environment until 2026-08-14 under a comment calling it a "NAMESPACE import", which it is not: -# sceptre's NAMESPACE imports only Matrix and Rcpp. -r-bh = "*" -r-ggplot2 = "*" -r-cowplot = "*" -r-scales = "*" +[dependencies] +python = "3.12.*" +numpy = ">=1.24" +scipy = ">=1.10" +pandas = ">=2.0" +h5py = ">=3.8" # the sim_input container +pyarrow = ">=14" # the consolidated per-replicate Parquet +numba = "*" # JIT for pysceptre's CRT sampler; it falls back without it, more slowly +anndata = "*" # reading the .h5mu pysceptre's export writes +mudata = "*" +nextflow = "*" # the pipeline runner (the only package from bioconda) + +[pypi-dependencies] +# pysceptre is not on conda-forge, bioconda or PyPI, so it enters as a git dependency pinned by +# commit. That is the same shape as the old SCEPTRE_SHA pin, minus the patch and the API check: the +# pipeline calls pysceptre's documented entry points rather than reaching into its internals, so a +# version bump is caught by this repository's own tests rather than by a bespoke checker. +# Pinned to a RELEASE TAG. A branch moves, and a sweep has to be re-runnable against the engine +# that produced it; a tag says which engine that was in a form a reader can look up. +pysceptre = { git = "https://github.com/broadinstitute/pysceptre.git", tag = "v0.2.0" } +watteg = { path = ".", editable = true } [feature.dev.dependencies] -r-testthat = "*" +pytest = ">=7" +ruff = ">=0.5" [environments] -build = ["build"] dev = ["dev"] [tasks] -check-api = { cmd = "Rscript src/check_sceptre_api.R", description = "Verify the pinned sceptre exposes every internal slot the pipeline depends on" } -pipeline-test = { cmd = "nextflow run . -profile stub -params-file config/test.yml -stub-run", description = "Check the Nextflow DAG wiring without running anything" } -install-hooks = { cmd = "./.githooks/install.sh", description = "Enable the repository pre-commit hook (core.hooksPath needs git >= 2.9; also symlinks into .git/hooks, which every git honours)" } +# The fixture is generated, not committed: it is ~1 MB of synthetic counts that any checkout can +# rebuild in a second, and a binary blob in git is a blob in every clone forever. +test-data = { cmd = "watteg-make-test-data --out tests/data/synthetic.h5mu", description = "Generate the synthetic dataset the stub run and smoke test use" } +pipeline-test = { cmd = "nextflow run . -profile stub -params-file config/test.yml -stub-run", depends-on = ["test-data"], description = "Check the Nextflow DAG wiring without running anything" } +install-hooks = { cmd = "./.githooks/install.sh", description = "Enable the repository pre-commit hook" } lint = { cmd = "./.githooks/pre-commit --all", description = "Run the pre-commit checks across every tracked file" } -# Scoped to the environments that carry the extra dependencies, so `pixi run setup` -# resolves to the build environment without the caller having to name it. -[feature.build.tasks] -setup = { cmd = "Rscript src/install_sceptre.R && Rscript src/install_ondisc.R", description = "Install sceptre (patched) and ondisc from their pinned commits into .pixi/rlibs" } - [feature.dev.tasks] -test = { cmd = "Rscript -e 'testthat::test_dir(\"tests/testthat\")'", description = "Run unit tests" } +test = { cmd = "pytest", description = "Run the unit tests" } +format = { cmd = "ruff format src tests workflow && ruff check --fix src tests workflow", description = "Format and lint" } diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..8b3af8a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,42 @@ +[project] +name = "watteg" +version = "0.1.0.dev0" +description = "Power analysis for element-gene pairs in single-cell CRISPR screens" +readme = "README.md" +license = { file = "LICENSE" } +requires-python = ">=3.10" +dependencies = [ + "numpy>=1.24", + "scipy>=1.10", + "pandas>=2.0", + "h5py>=3.8", + "pyarrow>=14", + "pysceptre", +] + +[project.scripts] +watteg-prepare-sim-input = "watteg.cli.prepare_sim_input:main" +watteg-run-power-simulation = "watteg.cli.run_power_simulation:main" +watteg-fit-null-models = "watteg.cli.fit_null_models:main" +watteg-split-pairs = "watteg.cli.split_pairs:main" +watteg-consolidate-replicates = "watteg.cli.consolidate_replicates:main" +watteg-compute-power = "watteg.cli.compute_power:main" +watteg-summarize-power = "watteg.cli.summarize_power:main" +watteg-make-test-data = "watteg.cli.make_test_data:main" + +[project.optional-dependencies] +dev = ["pytest>=7", "ruff>=0.5"] + +[tool.pytest.ini_options] +markers = [ + "realdata: needs a prepared dataset and runs the real engine; opt in with -m realdata", +] +addopts = "-m 'not realdata'" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["src/watteg"] + diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..6a6648a --- /dev/null +++ b/ruff.toml @@ -0,0 +1,6 @@ +# Matches pysceptre's, so the two halves of the port read the same. +line-length = 100 +target-version = "py310" + +[lint] +select = ["E", "F", "I", "UP", "B", "SIM"] diff --git a/src/watteg/__init__.py b/src/watteg/__init__.py new file mode 100644 index 0000000..29c0b6d --- /dev/null +++ b/src/watteg/__init__.py @@ -0,0 +1,3 @@ +"""Power analysis for element-gene pairs in single-cell CRISPR screens.""" + +__version__ = "0.1.0.dev0" diff --git a/src/watteg/baseline.py b/src/watteg/baseline.py new file mode 100644 index 0000000..5b6e22d --- /dev/null +++ b/src/watteg/baseline.py @@ -0,0 +1,105 @@ +"""A gene's expected counts per cell, before any perturbation. + +This is the number the simulation multiplies the effect size into, so it decides +what "unperturbed" means and therefore what power is measured against. There are +two ways to produce it and they are not equivalent. + +**`fitted` (the default): `exp(X_j . beta_i)`.** The expected count sceptre's own +null model gives that cell for that gene. Simulation and test then live on one +scale by construction rather than by coincidence. + +**`size_factor`: `mean_i x sf_j`.** What the R implementation did until +2026-09-21 -- a size-factor-normalised gene mean scaled by the cell's DESeq2 +"poscounts" factor. Kept because the sweeps already run were produced with it, +and a comparison against one of those is only interpretable like for like. It is +not what R does now: `r-implementation` defaults to the fitted baseline too. It is a +validation fixture, not a modelling choice anyone should make afresh. + +## Why the default changed + +Three reasons, in increasing order of how much they matter. + +**It mixes two models.** The R scheme takes its dispersion from sceptre's +negative-binomial fit and its level from a DESeq2 normalisation, so a simulated +gene's noise and its expression come from different statistical models of the +same data. + +**It gets the level wrong, and the error survives the size factor.** +`row_data$mean` sits 16 % below the mean sceptre's model implies; multiplying by +the cell's size factor recovers most of that, leaving the simulated genes about +4 % low on day0. The residual is a dropped covariance term -- `mean_i` is a mean +of ratios, and `E[x.sf] = E[x].E[sf] + Cov(x, sf)`. + +**It gets the shape wrong, which the level hides.** sceptre's mean varies with +every covariate -- library size, detected genes, batch, replicate -- while +`mean_i x sf_j` varies with a single scalar per cell. Measured against the real +counts on day0, over 60 genes and 567,690 cells: + +| | `mean_i x sf_j` | `exp(X . beta)` | +|---|---|---| +| zero fraction, mean absolute error | 0.0053 | **0.0007** | +| predicted variance / observed | 0.865 | **0.995** | + +The current scheme understates the count variance by 13.5 %. Note that pulls the +**opposite** way from the level error -- less variance inflates power, less +expression deflates it -- so the effect of the change on simulated power is not +obviously signed and has not been measured. Stage B under both modes is what +measures it. +""" + +from __future__ import annotations + +import numpy as np + +MODELS = ("fitted", "size_factor") + + +def fitted_baseline(fitted_coefs: np.ndarray, covariate_matrix: np.ndarray) -> np.ndarray: + """`exp(X . beta)` for one gene or many. + + `fitted_coefs` is `(p,)` for a single gene or `(n_genes, p)`; the result is + `(n_cells,)` or `(n_genes, n_cells)` to match. One matrix product, so the + coefficients are what a `sim_input` stores rather than the baseline itself: + 11 numbers per gene against one per cell per gene. + """ + coefs = np.asarray(fitted_coefs, dtype=float) + single = coefs.ndim == 1 + eta = covariate_matrix @ (coefs if single else coefs.T) + return np.exp(eta if single else eta.T) + + +def size_factor_baseline(mean: np.ndarray, size_factors: np.ndarray) -> np.ndarray: + """`mean_i x sf_j`, the R implementation's baseline. + + `mean` is `(n_genes,)` or a scalar; the result is `(n_genes, n_cells)` or + `(n_cells,)`. + """ + mean = np.asarray(mean, dtype=float) + if mean.ndim == 0: + return float(mean) * np.asarray(size_factors, dtype=float) + return np.outer(mean, size_factors) + + +def baseline_expression( + model: str, + *, + fitted_coefs: np.ndarray | None = None, + covariate_matrix: np.ndarray | None = None, + mean: np.ndarray | None = None, + size_factors: np.ndarray | None = None, +) -> np.ndarray: + """Dispatch on the model name, refusing the arguments the other one needs. + + Keyword-only and explicit about what is missing: passing `mean` to the + fitted model, or coefficients to the size-factor model, is a mistake that + would otherwise be silent in a pipeline where both are available. + """ + if model not in MODELS: + raise ValueError(f"model must be one of {list(MODELS)}, got {model!r}") + if model == "fitted": + if fitted_coefs is None or covariate_matrix is None: + raise ValueError("the 'fitted' baseline needs fitted_coefs and covariate_matrix") + return fitted_baseline(fitted_coefs, covariate_matrix) + if mean is None or size_factors is None: + raise ValueError("the 'size_factor' baseline needs mean and size_factors") + return size_factor_baseline(mean, size_factors) diff --git a/src/watteg/cli/__init__.py b/src/watteg/cli/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/watteg/cli/compute_power.py b/src/watteg/cli/compute_power.py new file mode 100644 index 0000000..bfb38ad --- /dev/null +++ b/src/watteg/cli/compute_power.py @@ -0,0 +1,125 @@ +"""Turn simulation results into a power estimate per pair. + + watteg-compute-power --partials partials/ --threshold-file discovery_threshold.txt \ + --out power_es0.15.tsv + watteg-compute-power --simulations sims/ --threshold-file discovery_threshold.txt \ + --out power_es0.15.tsv + +Two inputs, one table. `--partials` takes the per-pair counts each simulation task writes when it +holds all simulations of its pairs (`watteg-run-power-simulation --partials-out`), and adds them +up. `--simulations` takes one row per (pair, simulation), as TSV or Parquet. Both go through +`watteg.power.power_from_counts`, so they give the same table, byte for byte, when each pair's +simulations came from one task. + +Both take files or directories; a directory expands to the TSV and Parquet files inside it, sorted, +so row order does not depend on the filesystem. See `watteg/power.py` for what power means here and +why the interval is Wilson's. +""" + +from __future__ import annotations + +import argparse +import sys +from pathlib import Path + +import pandas as pd + +from watteg.power import compute_power, merge_power_counts, power_from_counts + +SUFFIXES = (".parquet", ".tsv", ".tsv.gz") + + +def expand(paths: list[Path]) -> list[Path]: + out: list[Path] = [] + for path in paths: + if path.is_dir(): + out.extend(sorted(p for p in path.iterdir() if p.name.endswith(SUFFIXES))) + else: + out.append(path) + return out + + +def read_one(path: Path) -> pd.DataFrame: + """One simulation output, its floats exactly as written. + + `round_trip`, not pandas' default parser: the default returns a neighbouring float for about + half of the p-values and fold changes a TSV holds (measured, 285,883 of 500,000), so a mean or a + value at the threshold would depend on the parser rather than on the simulation. + """ + if path.suffix == ".parquet": + return pd.read_parquet(path) + return pd.read_csv(path, sep="\t", float_precision="round_trip") + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__) + source = parser.add_mutually_exclusive_group(required=True) + source.add_argument( + "--simulations", nargs="+", type=Path, help="one row per (pair, simulation)" + ) + source.add_argument( + "--partials", + nargs="+", + type=Path, + help="per-pair counts from watteg-run-power-simulation --partials-out", + ) + parser.add_argument("--threshold-file", type=Path) + parser.add_argument( + "--alpha", type=float, help="set the threshold explicitly instead of reading it" + ) + parser.add_argument("--conf-level", type=float, default=0.95) + parser.add_argument("--out", type=Path, required=True) + args = parser.parse_args(argv) + + if (args.threshold_file is None) == (args.alpha is None): + raise SystemExit("pass exactly one of --threshold-file and --alpha") + threshold = ( + args.alpha if args.alpha is not None else float(args.threshold_file.read_text().split()[0]) + ) + + inputs = args.partials or args.simulations + paths = expand(inputs) + if not paths: + raise SystemExit(f"{'--partials' if args.partials else '--simulations'} matched no files") + frame = pd.concat([read_one(p) for p in paths], ignore_index=True) + if args.partials: + # The counts were taken at the threshold the simulation tasks were given; a different one + # here would silently mean a different question. + used = frame["threshold"].unique() if "threshold" in frame else [] + if len(used) != 1 or used[0] != threshold: + raise SystemExit( + f"the partial counts were taken at threshold(s) {list(used)}, not {threshold!r}" + ) + print( + f"read counts for {len(frame):,} pair chunk(s) from {len(paths)} file(s), " + f"threshold {threshold:.6g}" + ) + power = power_from_counts(merge_power_counts([frame]), conf_level=args.conf_level) + else: + print( + f"read {len(frame):,} simulation rows from {len(paths)} file(s), " + f"threshold {threshold:.6g}" + ) + power = compute_power(frame, threshold, conf_level=args.conf_level) + args.out.parent.mkdir(parents=True, exist_ok=True) + power.to_csv(args.out, sep="\t", index=False) + + width = power["power_ci_high"] - power["power_ci_low"] + print(f"wrote {len(power):,} pairs to {args.out}") + print(f" simulations per pair: {power['n_reps'].min()}-{power['n_reps'].max()}") + print( + f" mean power {power['power'].mean():.3f} | at 0: {(power['power'] == 0).sum():,} " + f"| at 1: {(power['power'] == 1).sum():,} " + f"| in (0.1, 0.9): {power['power'].between(0.1, 0.9, 'neither').sum():,}" + ) + print(f" median 95% CI width {width.median():.3f} (widest {width.max():.3f})") + if power["n_reps"].min() < 100: + print( + " note: below ~100 simulations a per-pair estimate is coarse; see " + "docs/choosing-num-replicates.md" + ) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/watteg/cli/consolidate_replicates.py b/src/watteg/cli/consolidate_replicates.py new file mode 100644 index 0000000..1a65ece --- /dev/null +++ b/src/watteg/cli/consolidate_replicates.py @@ -0,0 +1,59 @@ +"""Concatenate a sweep's per-split simulation output into one Parquet file. + + watteg-consolidate-replicates --simulations sims/ --out per_replicate/es0.15.parquet + +The per-replicate output is the one thing power can be re-derived from, so it is +read repeatedly -- and as 1,000 gzipped TSVs that costs 30-90 s of parsing every +time. One Parquet file per effect size is seconds. + +The split files stay in the work directory, so a failed consolidation loses +nothing. +""" + +from __future__ import annotations + +import argparse +import sys +from pathlib import Path + +import pandas as pd + +from watteg.cli.compute_power import expand, read_one + +# Stored as categories: a sweep repeats a few thousand target and gene names +# across millions of rows, and dictionary encoding is what makes the file small. +CATEGORICAL = ("grna_target", "response_id") + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--simulations", nargs="+", type=Path, required=True) + parser.add_argument("--out", type=Path, required=True) + parser.add_argument("--compression", default="zstd") + args = parser.parse_args(argv) + + paths = expand(args.simulations) + if not paths: + raise SystemExit(f"--simulations matched no files: {args.simulations}") + frame = pd.concat([read_one(p) for p in paths], ignore_index=True) + + if "effect_size" in frame.columns: + sizes = frame["effect_size"].unique() + if len(sizes) > 1: + raise SystemExit( + f"the input mixes effect sizes ({', '.join(map(str, sizes))}); consolidate " + "one effect size per file, or power would be averaged across knockdown levels" + ) + for column in CATEGORICAL: + if column in frame.columns: + frame[column] = frame[column].astype("category") + + args.out.parent.mkdir(parents=True, exist_ok=True) + frame.to_parquet(args.out, compression=args.compression, index=False) + size_mb = args.out.stat().st_size / 1e6 + print(f"wrote {len(frame):,} rows from {len(paths)} file(s) to {args.out} ({size_mb:.1f} MB)") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/watteg/cli/fit_null_models.py b/src/watteg/cli/fit_null_models.py new file mode 100644 index 0000000..838e8d2 --- /dev/null +++ b/src/watteg/cli/fit_null_models.py @@ -0,0 +1,120 @@ +"""Fit each gene's null model once per simulation, for every simulation task to reuse. + + watteg-fit-null-models --prepared prepared/ --reps 100 --seed 20250812 \ + --n-jobs 8 --out null_fits.h5 + +For each gene in the screen's pairs and each simulation, this draws the gene's counts with no +knockdown from its own stream, keyed `rng_for(seed, "__null_fit__|" + gene, simulation, 0.0)`, +and fits pysceptre's null model to them (`fit_all_genes`, at the discovery call's batch width). +`watteg-run-power-simulation --driver fast --null-fits reuse --null-fits-file null_fits.h5` then +tests every target against those fits instead of refitting each gene per target. See +`watteg/null_fits.py` for why that is sound and what it was measured to change. + +A fit depends on (seed, gene, simulation) only, so a task given this file and a task that fits its +own genes produce the same bytes. The file records the seed, the baseline model and a digest of +`sim_input.h5`; a simulation run that does not match all three refuses it. +""" + +from __future__ import annotations + +import argparse +import sys +import time +from pathlib import Path + +import pandas as pd + +from watteg.engine import AnalysisParams +from watteg.null_fits import compute_null_fits +from watteg.sim_input import read_sim_input +from watteg.workers import SHARED + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--prepared", type=Path, required=True, help="output directory of watteg-prepare-sim-input" + ) + parser.add_argument( + "--pairs", + type=Path, + default=None, + help="the pairs whose genes are fitted [default: pairs.tsv in --prepared]", + ) + parser.add_argument("--reps", type=int, required=True, help="simulations to fit") + parser.add_argument( + "--rep-offset", + type=int, + default=0, + help="simulations already covered, so this fits simulations offset+1 .. offset+reps", + ) + parser.add_argument( + "--seed", type=int, required=True, help="the simulation run's --seed; the file records it" + ) + parser.add_argument( + "--expression-model", + choices=("fitted", "size_factor"), + default="fitted", + help="the simulation run's --expression-model; the file records it [default %(default)s]", + ) + parser.add_argument( + "--n-jobs", + type=int, + default=8, + help="worker processes; one simulation's fits are one unit of work [default %(default)s]", + ) + parser.add_argument("--out", type=Path, required=True) + args = parser.parse_args(argv) + + if args.reps < 1: + raise SystemExit("--reps must be at least 1") + if args.rep_offset < 0: + raise SystemExit("--rep-offset must be non-negative") + + started = time.perf_counter() + params = AnalysisParams.from_analysis_mode(args.prepared / "analysis_mode.tsv") + if params.resampling_mechanism != "permutations": + # The fits are read only by the fast driver, which runs only the permutation test. Fitting + # a CRT screen's genes would cost the time and be thrown away. + raise SystemExit( + f"this screen used {params.resampling_mechanism!r}; reused null fits are read only by " + "the fast driver, which runs the permutation test only. Simulate it with --driver " + "engine --null-fits refit, and skip this step." + ) + sim = read_sim_input(args.prepared / "sim_input.h5") + pairs = pd.read_csv(args.pairs or args.prepared / "pairs.tsv", sep="\t") + if "response_id" not in pairs.columns: + raise SystemExit(f"{args.pairs} has no 'response_id' column") + genes = list(dict.fromkeys(pairs["response_id"])) + unknown = [g for g in genes if g not in set(sim.genes)] + if unknown: + raise SystemExit(f"{len(unknown)} gene(s) are not in sim_input.h5, e.g. {unknown[:3]}") + reps = range(args.rep_offset + 1, args.rep_offset + args.reps + 1) + workers = min(max(args.n_jobs, 1), len(reps)) + print( + f"fitting {len(genes)} genes x {len(reps)} simulations ({reps.start}-{reps.stop - 1}), " + f"seed {args.seed}, baseline {args.expression_model}, on {workers} worker(s)" + ) + + SHARED.update(sim=sim, params=params, grna_csc=sim.grna_perts.tocsc()) + fits = compute_null_fits( + sim, + genes, + reps, + seed=args.seed, + expression_model=args.expression_model, + workers=workers, + prepared=args.prepared, + ) + fits.write(args.out) + bad = {k: v for k, v in fits.summary().items() if v} + print( + f"wrote {len(genes)} x {len(reps)} fits to {args.out} " + f"({args.out.stat().st_size / 1e6:.1f} MB) in {time.perf_counter() - started:.1f}s" + + (f"; degenerate (gene, simulation) fits: {bad}" if bad else "") + ) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/watteg/cli/make_test_data.py b/src/watteg/cli/make_test_data.py new file mode 100644 index 0000000..080ae04 --- /dev/null +++ b/src/watteg/cli/make_test_data.py @@ -0,0 +1,182 @@ +"""Generate a small synthetic dataset, so the pipeline can be exercised without a screen. + + watteg-make-test-data --out tests/data/synthetic.h5mu + +The R implementation's `src/make_test_data.R` built a sceptre object; the Python path reads a +`.h5mu`, so this writes one directly in the shape pysceptre's export produces. It is not a +simulation of a screen in any scientific sense -- the counts are drawn from a negative binomial with +made-up covariate effects -- and nothing measured on it means anything. What it is for is checking +that the pipeline runs: the stub run, a smoke test, and anyone wanting to see the shape of the +outputs without a real dataset. + +The awkward cases are deliberately present, because they are the ones that have broken things: + + * a gRNA belonging to **two** targets, which is what overlapping candidate elements produce and + what a collapsed map silently drops; + * cells that **fail QC**, so `--all-cells` has something to carry and the two cell spaces differ; + * a cell carrying **no** gRNA at all; + * a gRNA with **no** cells. +""" + +from __future__ import annotations + +import argparse +import sys +from pathlib import Path + +import numpy as np +import pandas as pd +from scipy import sparse + + +def build( + *, + n_genes: int = 12, + n_cells: int = 4000, + n_targets: int = 6, + guides_per_target: int = 4, + n_ntc: int = 8, + n_failed_qc: int = 120, + seed: int = 0, +): + rng = np.random.default_rng(seed) + gene_ids = [f"gene{i:02d}" for i in range(n_genes)] + target_ids = [f"elem{i:02d}" for i in range(n_targets)] + + # Covariates: an intercept, a log library size, and a two-level batch. Enough structure for the + # fitted baseline to be a different thing from a single scalar per cell. + log_umis = rng.normal(8.0, 0.4, size=n_cells) + batch = rng.integers(0, 2, size=n_cells).astype(float) + covariates = np.column_stack([np.ones(n_cells), log_umis, batch]) + covariate_names = ["(Intercept)", "log(response_n_umis)", "batch_factorBatch 2"] + + coefs = np.column_stack( + [ + rng.normal(-7.0, 0.8, n_genes), # intercept + np.full(n_genes, 0.9), # library size + rng.normal(0.0, 0.2, n_genes), # batch + ] + ) + mu = np.exp(covariates @ coefs.T).T + theta = rng.uniform(2.0, 40.0, size=n_genes) + counts = rng.negative_binomial(theta[:, None], theta[:, None] / (theta[:, None] + mu)) + + # gRNA assignments. One guide is shared between the first two targets, which is the + # many-to-many case; one designed guide gets no cells at all. + design, membership = [], {} + shared = "grna_shared" + for t, target in enumerate(target_ids): + for g in range(guides_per_target): + guide = shared if (t < 2 and g == 0) else f"grna_{target}_{g}" + design.append((guide, target)) + if guide in membership: + continue + membership[guide] = rng.choice(n_cells, size=rng.integers(20, 60), replace=False) + design.append(("grna_never_assigned", target_ids[0])) + for i in range(n_ntc): + design.append((f"ntc_{i:02d}", "non-targeting")) + membership[f"ntc_{i:02d}"] = rng.choice(n_cells, size=rng.integers(20, 60), replace=False) + + in_use = np.ones(n_cells, dtype=bool) + in_use[rng.choice(n_cells, size=n_failed_qc, replace=False)] = False + + design = pd.DataFrame(design, columns=["grna_id", "grna_target"]) + guides_of = design.groupby("grna_target")["grna_id"].apply(list) + # A target's cells are the union of its guides', restricted to cells_in_use -- the invariant the + # export asserts, so the fixture has to satisfy it too. + target_cells = { + t: np.array(sorted({c for g in guides_of[t] for c in membership.get(g, []) if in_use[c]})) + for t in target_ids + } + return dict( + counts=counts, + gene_ids=gene_ids, + covariates=covariates, + covariate_names=covariate_names, + in_use=in_use, + design=design, + membership=membership, + target_cells=target_cells, + target_ids=target_ids, + ) + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--out", type=Path, required=True) + parser.add_argument("--seed", type=int, default=0) + args = parser.parse_args(argv) + + import pysceptre + + sys.path.insert(0, str(Path(pysceptre.__file__).resolve().parents[2] / "scripts")) + from sceptre_io import SceptreExport, write_h5mu + + d = build(seed=args.seed) + n_cells = d["counts"].shape[1] + + # Every pair, so the discovery result has something to derive a threshold from. + pairs = pd.DataFrame( + [(g, t) for t in d["target_ids"] for g in d["gene_ids"][:4]], + columns=["response_id", "grna_target"], + ) + # A p-value column with some significant entries: the threshold is the largest significant one. + rng = np.random.default_rng(args.seed + 1) + p = rng.uniform(0, 1, len(pairs)) ** 4 + discovery = pairs.assign(p_value=p, significant=p < 0.02) + + export = SceptreExport( + response_matrix=sparse.csr_matrix(d["counts"]), + gene_ids=d["gene_ids"], + covariate_matrix=d["covariates"], + grna_target_cells={t: np.asarray(c) for t, c in d["target_cells"].items()}, + pairs=pairs, + metadata={ + "n_cells": n_cells, + "n_cells_in_use": int(d["in_use"].sum()), + "n_genes": len(d["gene_ids"]), + "n_covariates": d["covariates"].shape[1], + "covariate_names": d["covariate_names"], + "n_targets": len(d["target_ids"]), + "n_pairs": len(pairs), + "n_nonzero": int(sparse.csr_matrix(d["counts"]).nnz), + "all_cells": True, + "side_code": 0, + "resampling_approximation": "skew_normal", + "run_permutations": False, + "low_moi": False, + "control_group_complement": True, + "multiple_testing_alpha": 0.1, + "B1": 499, + "B2": 4999, + "B3": 0, + "n_nonzero_trt_thresh": 7, + "n_nonzero_cntrl_thresh": 7, + "sceptre_version": "synthetic", + }, + discovery_result=discovery, + ntc_grna_cells={ + g: np.asarray(sorted(c)) for g, c in d["membership"].items() if g.startswith("ntc_") + }, + targeting_grna_cells={ + g: np.asarray(sorted(c)) for g, c in d["membership"].items() if not g.startswith("ntc_") + }, + grna_target_data_frame=d["design"], + discovery_pairs_with_info=pairs.assign( + n_nonzero_trt=50, n_nonzero_cntrl=1000, pass_qc=True + ), + in_use=d["in_use"], + ) + args.out.parent.mkdir(parents=True, exist_ok=True) + write_h5mu(export, args.out) + print(f"wrote {args.out} ({args.out.stat().st_size / 1e6:.1f} MB)") + print(f" {export.describe()}") + print( + f" {n_cells - int(d['in_use'].sum())} cells fail QC; " + f"1 gRNA belongs to two targets; 1 designed gRNA has no cells" + ) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/watteg/cli/prepare_sim_input.py b/src/watteg/cli/prepare_sim_input.py new file mode 100644 index 0000000..7030771 --- /dev/null +++ b/src/watteg/cli/prepare_sim_input.py @@ -0,0 +1,286 @@ +"""Turn a pysceptre export into the small inputs the power simulation needs. + +The step that makes the rest of the pipeline cheap: every one of the +`n_splits x n_effect_sizes x n_rep_chunks` parallel tasks reads what this writes, +and none of them reads a count matrix. See `watteg/sim_input.py` for why that is +enough. + + watteg-prepare-sim-input --dataset day0.h5mu --outdir prepared/ + +The input is a `.h5mu` written by pysceptre's `scripts/export_sceptre_dataset.R` +plus `make_h5mu.py`, **exported with `--all-cells`**. R read the sceptre object +directly; nothing in Python does, which is what lets the environment be one +Python package. The `--all-cells` requirement is not a detail -- see +`watteg/expression.py`. + +Outputs, matching the R step's names and columns so the two can be compared and +so downstream readers do not care which produced them: + + sim_input.h5 per-gene and per-cell statistics + the perturbation matrices + pairs.tsv the QC-passing discovery pairs + pairs_with_info.tsv every discovery pair with its real-data QC counts + grna_targets.tsv the gRNA -> target mapping + discovery_threshold.txt the nominal p-value a simulated pair has to beat + analysis_mode.tsv which test the screen was run under + +There is no `sceptre_template.rds`: that was an R object carrying the covariate +matrix and the analysis parameters, which now live in `sim_input.h5` and +`analysis_mode.tsv` respectively. +""" + +from __future__ import annotations + +import argparse +import sys +from pathlib import Path + +import numpy as np +import pandas as pd +from scipy import sparse + +from watteg.expression import compute_expression_stats +from watteg.gene_model import fit_gene_models +from watteg.sim_input import SimInput, write_sim_input + + +def _load_export(path: Path): + """pysceptre's loader lives in its `scripts/`, which is not shipped in the + wheel, so it is imported by path rather than by package.""" + import pysceptre + + scripts = Path(pysceptre.__file__).resolve().parents[2] / "scripts" + sys.path.insert(0, str(scripts)) + from sceptre_io import load_export, subset_to_cells_in_use + + return load_export(path, all_cells=True), subset_to_cells_in_use + + +def indicator_matrix( + units: list[str], cells_of: dict[str, np.ndarray], n_cells: int +) -> sparse.csr_matrix: + """A (units x cells) 0/1 matrix from a unit -> cells mapping. + + Built in one allocation from the concatenated indices rather than by + growing a matrix. Values are forced to 1: these are indicators, and a cell + that appears twice under one unit must not count double. + """ + rows = np.concatenate([np.full(cells_of[u].size, i) for i, u in enumerate(units)]) + cols = np.concatenate([cells_of[u] for u in units]) if units else np.empty(0, dtype=np.int64) + matrix = sparse.csr_matrix( + (np.ones(rows.size, dtype=np.int8), (rows, cols)), + shape=(len(units), n_cells), + ) + matrix.data[:] = 1 + return matrix + + +def discovery_threshold(discovery_result: pd.DataFrame | None) -> float: + """The largest p-value the real analysis still called significant. + + That is the bar a simulated pair has to clear, so power means "would this + screen have called it" rather than "would some other threshold have". R + derives it the same way, from the same column. + """ + if discovery_result is None or not len(discovery_result): + raise ValueError( + "the export carries no discovery_result, so no significance threshold can be " + "derived. Re-export with --discovery-result, or pass --threshold explicitly." + ) + for column in ("p_value", "significant"): + if column not in discovery_result: + raise ValueError(f"discovery_result has no '{column}' column") + # `significant` carries NA for pairs that failed pairwise QC and so were + # never tested. Not significant is the right reading of that, and the + # alternative is a cast that raises on the NA. + flagged = discovery_result["significant"].fillna(False).to_numpy(dtype=bool) + significant = discovery_result["p_value"][flagged] + if not len(significant): + raise ValueError( + "no pair in discovery_result is significant, so the largest significant p-value is " + "undefined. Pass --threshold to set it explicitly." + ) + threshold = float(significant.max()) + if not np.isfinite(threshold) or threshold <= 0 or threshold > 1: + raise ValueError(f"derived p-value threshold {threshold} is not a usable probability") + return threshold + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--dataset", type=Path, required=True, help="an --all-cells pysceptre export (.h5mu)" + ) + parser.add_argument("--outdir", type=Path, required=True) + parser.add_argument( + "--threshold", + type=float, + default=None, + help="set the significance threshold instead of deriving it from the discovery result", + ) + parser.add_argument("--n-jobs", type=int, default=1, help="workers for the per-gene fits") + args = parser.parse_args(argv) + + export, subset_to_cells_in_use = _load_export(args.dataset) + meta = export.metadata + n_all_cells = meta["n_cells"] + n_in_use = int(np.asarray(export.in_use, dtype=bool).sum()) + print(f"dataset: {args.dataset}") + print(f" {export.describe()}") + print(f" cells: {n_all_cells:,} exported, {n_in_use:,} passing QC") + if n_all_cells == n_in_use: + print( + " WARNING: this export carries no QC-failed cells. The size factors below are " + "computed over the cells present, which is not what the R implementation does -- " + "re-export with --all-cells to reproduce it (see watteg/expression.py)." + ) + if meta.get("run_permutations"): + print( + " NOTE: this screen used permutations, not the CRT path. Power is still correct for " + "THIS screen -- the simulation re-runs the test the screen actually ran -- but the " + "cost model in docs/status.md does not apply." + ) + + # --- statistics over every cell -------------------------------------------------------- + print("computing expression statistics over every cell ...") + stats = compute_expression_stats(export.response_matrix) + print(f" density {stats.density:.1%}") + + # --- everything else over cells_in_use ------------------------------------------------- + in_use = subset_to_cells_in_use(export) + pairs = in_use.pairs + # Built once. Inside the comprehension it was rebuilt for every gene -- 38,606 x 742,525 on + # moi5 trans, 108 min for a step that takes milliseconds, past the task's time limit. + paired = set(pairs["response_id"]) + genes = [g for g in in_use.gene_ids if g in paired] + print(f" {len(pairs):,} QC-passing pairs across {pairs['grna_target'].nunique():,} targets") + print(f" genes kept: {len(genes)} of {len(in_use.gene_ids)} (those in QC-passing pairs)") + + print(f"fitting the per-gene model for {len(genes)} genes over {n_in_use:,} cells ...") + models = fit_gene_models( + in_use.response_matrix, + in_use.gene_ids, + in_use.covariate_matrix, + genes, + n_jobs=args.n_jobs, + ) + # Named, not counted. A gene whose theta came from method of moments rather + # than the MLE is the first place to look when its power is surprising, and + # a count alone does not say which gene to look at. + for kind, affected in models.diagnostics.items(): + if affected: + shown = ", ".join(affected[:5]) + more = f" (+{len(affected) - 5} more)" if len(affected) > 5 else "" + print(f" {kind}: {len(affected)} gene(s) -- {shown}{more}") + + gene_rows = np.array([in_use.gene_ids.index(g) for g in genes]) + row_data = pd.DataFrame( + { + "mean": stats.normalized_mean[gene_rows], + "dispersion": models.dispersion, + "average_expression_all_cells": stats.average_expression_all_cells[gene_rows], + }, + index=genes, + ) + cells_in_use = np.flatnonzero(np.asarray(export.in_use, dtype=bool)) + col_data = pd.DataFrame({"size_factors": stats.size_factors[cells_in_use]}) + + if in_use.targeting_grna_cells is None or in_use.grna_target_data_frame is None: + raise ValueError( + f"{args.dataset} carries no individual targeting gRNAs. The simulation gives each " + "guide its own effect size and cannot run without them; re-export with a pysceptre " + "that writes the targeting_grna units." + ) + # NON-TARGETING GUIDES STAY IN grna_perts, so the matrix covers every gRNA + # the way R's does (it is built from @initial_grna_assignment_list), and a + # control cell's guide status points at the guide it actually carries. That + # keeps the status convention identical across the two implementations, + # which the shared-fixture tests check. It no longer changes a simulated + # number: since 2026-09-24 every guide outside the target has an effect of + # exactly 1 (see watteg.perturbation), so a control cell comes out at 1 + # whether it points at a non-targeting guide or at the no-effect row. Until + # then those guides drew N(1, 0.13), and dropping them would have + # removed that spread from the control cells. + guide_cells = dict(in_use.targeting_grna_cells) + guide_cells.update(in_use.ntc_grna_cells or {}) + grna_ids = sorted(guide_cells) + target_ids = sorted(in_use.grna_target_cells) + + sim = SimInput( + genes=genes, + cells_in_use=cells_in_use, + row_data=row_data, + col_data=col_data, + covariate_matrix=in_use.covariate_matrix, + covariate_names=list(meta["covariate_names"]), + fitted_coefs=models.fitted_coefs, + grna_ids=grna_ids, + grna_perts=indicator_matrix(grna_ids, guide_cells, n_in_use), + target_ids=target_ids, + cre_perts=indicator_matrix(target_ids, in_use.grna_target_cells, n_in_use), + ) + + args.outdir.mkdir(parents=True, exist_ok=True) + write_sim_input(sim, args.outdir / "sim_input.h5") + print(f"\nsim_input: {sim.describe()}") + + # Column order follows the R step's, so a reader of either does not have to + # care which produced the file. + pairs[["grna_target", "response_id"]].to_csv(args.outdir / "pairs.tsv", sep="\t", index=False) + + # The gRNA -> target map is MANY-TO-MANY: a guide inside two overlapping + # candidate elements belongs to both, and 1,673 of day0's 43,736 guides do. + # Written from the design frame and never from the per-unit annotation, + # which records "" for exactly those guides. See + # docs/pysceptre-backend.md section 5.1. + # Written whole, non-targeting rows included, as the R step writes it. They + # are inert -- the simulation looks guides up by target and no real target + # is called "non-targeting" -- and dropping them would make the file + # disagree with the screen's own design table for no gain. + in_use.grna_target_data_frame.to_csv(args.outdir / "grna_targets.tsv", sep="\t", index=False) + + # n_nonzero_trt, n_nonzero_cntrl and pass_qc, which the simulation reports beside each + # pair. They are facts about the REAL data and constant across replicates, so they are + # carried rather than recomputed per draw -- which is also what the R implementation does, + # reading them off the sceptre template's discovery_pairs_with_info. They are the first + # thing anyone looks at when a pair's power is surprising, and there is nowhere else to + # recover them from once the object is gone. + if in_use.discovery_pairs_with_info is not None: + in_use.discovery_pairs_with_info.to_csv( + args.outdir / "pairs_with_info.tsv", sep="\t", index=False + ) + else: + print( + " NOTE: the export carries no discovery_pairs_with_info, so pairs_with_info.tsv is " + "not written and the simulation will have no QC counts to report." + ) + + threshold = args.threshold or discovery_threshold(in_use.discovery_result) + (args.outdir / "discovery_threshold.txt").write_text(f"{threshold:.17g}\n") + + mechanism = "permutations" if meta.get("run_permutations") else "crt" + moi = "low" if meta.get("low_moi") else "high" + (args.outdir / "analysis_mode.tsv").write_text( + "\n".join( + [ + f"resampling_mechanism\t{mechanism}", + f"moi\t{moi}", + f"side\t{export.side}", + f"resampling_approximation\t{meta.get('resampling_approximation', '')}", + f"B1\t{meta.get('B1', '')}", + f"B2\t{meta.get('B2', '')}", + f"B3\t{meta.get('B3', '')}", + f"multiple_testing_alpha\t{meta.get('multiple_testing_alpha', '')}", + f"sceptre_version\t{meta.get('sceptre_version', '')}", + ] + ) + + "\n" + ) + print(f" discovery threshold: {threshold:.6g}") + print(f" resampling: {mechanism} (MOI: {moi}, side: {export.side})") + written = len(list(args.outdir.glob("*"))) + print(f"\nwrote {written} files to {args.outdir}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/src/watteg/cli/run_power_simulation.py b/src/watteg/cli/run_power_simulation.py new file mode 100644 index 0000000..254ea64 --- /dev/null +++ b/src/watteg/cli/run_power_simulation.py @@ -0,0 +1,538 @@ +"""Run the power simulation for one split, one effect size, one chunk of simulations. + + watteg-run-power-simulation --prepared prepared/ --pairs split_01.tsv \ + --effect-size 0.15 --reps 100 --seed 1 --null-fits-file null_fits.h5 --out sim.tsv + +For each target in the split and each simulation, this simulates a count matrix +under the given effect size and asks the screen's own test whether it would have +called the association. The fraction of simulations in which it would is the +power, computed downstream. + +**The defaults (2026-09-25)** are the fast configuration, each measured before it +was adopted (docs/pysceptre-backend.md, section 13): `--permutations per-target` +(one permutation set per target, as sceptre effectively uses), `--nulls sparse` +(pysceptre's draw-matrix route, identical results), `--driver fast` (a target's +simulations tested together, byte-identical to the engine under those two) and +`--null-fits reuse` (each gene's null model fitted once per simulation and shared +across targets; see watteg/null_fits.py). The previous configuration stays +selectable: `--driver engine --null-fits refit`, with `--permutations +per-replicate --nulls scan` if wanted. The fast driver runs the permutation test +only, so a CRT screen needs `--driver engine --null-fits refit`. + +Output columns match `src/run_power_simulation.R`'s, plus `estimand`, so the two +implementations' results can be compared without a conversion step and so +downstream readers do not care which produced a file. +""" + +from __future__ import annotations + +import argparse +import sys +import time +from pathlib import Path + +import numpy as np +import pandas as pd + +from watteg.engine import ( + DEFAULT_GUIDE_SPREAD_C, + NULL_ROUTES, + PERMUTATION_MODES, + AnalysisParams, + simulate_target, +) +from watteg.perturbation import ESTIMANDS +from watteg.sim_input import read_sim_input +from watteg.workers import SHARED as _SHARED +from watteg.workers import map_units + +# "engine" calls pysceptre once per (target, simulation). "fast" (watteg.fast_driver) runs a +# target's simulations together so its permutation matrices are built once, with output +# byte-identical to the engine's under --permutations per-target; it needs that mode, since the +# matrices are shared only when the simulations share one permutation set. +DRIVERS = ("engine", "fast") + +# What a reader needs, and nothing more. At 100 simulations x 34,886 pairs x six +# effect sizes the columns nothing reads were 39% of a 3 GB output. +KEEP = [ + "grna_target", + "response_id", + "p_value", + "log_2_fold_change", + "rep", + "effect_size", + "num_pert_cells", + "pass_qc", + "n_nonzero_trt", + "n_nonzero_cntrl", + # Last, so a table from before the option existed is this one minus its final column. + "estimand", +] + +# How each gene's null model is fitted under the fast driver. "refit" fits it on every simulation's +# own counts, once per target, as the engine does. "reuse" fits it once per (gene, simulation) on an +# independent null draw and shares it across targets and effect sizes (watteg.null_fits). +NULL_FIT_MODES = ("reuse", "refit") + + +def _run_unit(unit: tuple) -> tuple: + """Simulate and test one (target, simulation). pysceptre gets one worker: the task's + workers are already busy with other units, and nesting pools would oversubscribe.""" + target, genes, guides, rep = unit + shared = _SHARED + at = time.perf_counter() + frame = simulate_target( + shared["sim"], + target, + genes, + guides, + effect_size=shared["effect_size"], + reps=range(rep, rep + 1), + seed=shared["seed"], + params=shared["params"], + grna_csc=shared["grna_csc"], + guide_spread_c=shared["guide_spread_c"], + n_jobs=1, + expression_model=shared["expression_model"], + permutations=shared["permutations"], + nulls=shared["nulls"], + estimand=shared["estimand"], + ) + return target, frame, time.perf_counter() - at + + +def _run_fast_unit(unit: tuple) -> tuple: + """Simulate and test one target's simulations `start..stop-1` with the fast driver. + + Imported here, not at the top, so the default driver's start-up is exactly what it was. + """ + from watteg.fast_driver import simulate_target_fast + + target, genes, guides, start, stop = unit + shared = _SHARED + at = time.perf_counter() + frame = simulate_target_fast( + shared["sim"], + target, + genes, + guides, + effect_size=shared["effect_size"], + reps=range(start, stop), + seed=shared["seed"], + params=shared["params"], + grna_csc=shared["grna_csc"], + guide_spread_c=shared["guide_spread_c"], + expression_model=shared["expression_model"], + null_fits=shared.get("null_fits"), + estimand=shared["estimand"], + ) + return target, frame, time.perf_counter() - at + + +def _fast_units(genes_of, guides_of, reps: range, workers: int) -> list: + """Each target's simulations in contiguous chunks, enough of them to keep `workers` busy. + + A chunk pays the per-target setup once (its permutations and their matrices, about 0.3 s), so + chunks are as large as the worker count allows: one per target when targets outnumber workers, + and a single-target run at --n-jobs 4 in four. Chunk boundaries cannot move a result: each + simulation draws its counts from its own seeded stream, every simulation of a target shares the + one permutation set, and every fit and product column is computed on its own. + """ + per_target = min(len(reps), max(1, -(-workers // max(1, len(genes_of))))) + size = -(-len(reps) // per_target) + return [ + (t, list(g), guides_of[t], start, min(start + size, reps.stop)) + for t, g in genes_of.items() + for start in range(reps.start, reps.stop, size) + ] + + +def _map_units(units: list, workers: int, prepared: Path, settings: dict, fn=_run_unit) -> list: + """Run the units in parallel, in processes, in submission order (see `watteg.workers`). + + Each unit draws from its own seeded stream (`rng_for(seed, target, rep, effect_size)`), + so the output does not depend on the worker count or the order the units finish in. + """ + return map_units(units, workers, prepared, settings, fn) + + +# The per-pair counts a task writes for watteg-compute-power --partials, in this order. +PARTIAL_COLUMNS = [ + "grna_target", + "response_id", + "effect_size", + "estimand", + "threshold", + "rep_first", + "rep_last", + "n_simulations", + "successes", + "n_reps", + "sum_log_2_fold_change", + "sum_num_pert_cells", +] + + +def _write_partials(combined: pd.DataFrame, threshold_file: Path, out: Path) -> None: + """Per-pair counts from this task's rows: the same sums a table of all rows would give. + + The rows are the ones --out writes, in memory at full precision, so the counts equal those + computed from the written table read back exactly. A task whose every target was skipped + writes the header alone, which the pipeline's row count then reports. + """ + from watteg.power import power_counts + + threshold = float(threshold_file.read_text().split()[0]) + if combined.empty: + counts = pd.DataFrame(columns=PARTIAL_COLUMNS) + else: + counts = power_counts(combined, threshold) + counts = counts[[c for c in PARTIAL_COLUMNS if c in counts.columns]] + out.parent.mkdir(parents=True, exist_ok=True) + counts.to_csv(out, sep="\t", index=False) + print(f"wrote per-pair counts for {len(counts):,} pairs to {out} (threshold {threshold:.6g})") + + +def _null_fits_for_task(args, sim, split: pd.DataFrame, reps: range): + """The fits this task's genes need, from --null-fits-file or made here. + + Made once in the parent, before the simulation's workers start, so a gene tested against + several of the task's targets is fitted once per simulation rather than once per worker. + Either way only the task's genes x simulations are kept, which is what a spawned worker is + sent. + """ + from watteg.null_fits import NullFits, compute_null_fits, input_fingerprint + + genes = list(dict.fromkeys(split["response_id"])) + at = time.perf_counter() + if args.null_fits_file is not None: + fits = NullFits.read(args.null_fits_file) + try: + fits.check_matches( + seed=args.seed, + expression_model=args.expression_model, + fingerprint=input_fingerprint(sim), + genes=genes, + reps=reps, + ) + except ValueError as err: + raise SystemExit(f"{args.null_fits_file}: {err}") from None + source = f"read from {args.null_fits_file}" + else: + workers = min(max(args.n_jobs, 1), len(reps)) + fits = compute_null_fits( + sim, + genes, + reps, + seed=args.seed, + expression_model=args.expression_model, + workers=workers, + prepared=args.prepared, + ) + source = f"fitted in this task on {workers} worker(s)" + fits = fits.subset(genes, reps) + bad = {k: v for k, v in fits.summary().items() if v} + print( + f" null fits: {len(genes)} genes x {len(reps)} simulations, {source}, in " + f"{time.perf_counter() - at:.1f}s" + (f"; degenerate: {bad}" if bad else "") + ) + return fits + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--prepared", type=Path, required=True, help="output directory of watteg-prepare-sim-input" + ) + parser.add_argument( + "--pairs", + type=Path, + required=True, + help="one split, with columns grna_target and response_id", + ) + parser.add_argument( + "--effect-size", + type=float, + required=True, + help="a fractional decrease: 0.15 is a 15%% knockdown. 0 simulates no effect. Under " + "--estimand fixed the realised mean knockdown across the perturbed cells equals this " + "value exactly in every simulation; under --estimand random it is this value on " + "average (see docs/methods.md)", + ) + parser.add_argument( + "--estimand", + choices=ESTIMANDS, + default="fixed", + help="what the power is power for. 'fixed': an element whose effect IS --effect-size, " + "the guides' cell-weighted mean pinned to it in every simulation. 'random': an element " + "whose effect is --effect-size on average, the mean left where the guide draws put it " + "(the question PerturbPlan asks with fold_change_sd = c * es * (1 - es)). The same " + "simulation at --effect-size 0. Written into every output row [default %(default)s]", + ) + parser.add_argument("--reps", type=int, required=True) + parser.add_argument( + "--rep-offset", + type=int, + default=0, + help="simulations already covered by earlier chunks, so `rep` stays unique across them", + ) + parser.add_argument( + "--seed", + type=int, + required=True, + help="required: results are stochastic and must be reproducible", + ) + parser.add_argument( + "--out", type=Path, default=None, help="one row per (pair, simulation), as TSV" + ) + parser.add_argument( + "--partials-out", + type=Path, + default=None, + help="per-pair counts power is made of (watteg.power.power_counts), for " + "watteg-compute-power --partials. Complete only when this task holds all simulations of " + "its pairs; needs --threshold-file", + ) + parser.add_argument( + "--threshold-file", + type=Path, + default=None, + help="the discovery threshold a simulation must beat, for --partials-out", + ) + parser.add_argument( + "--guide-spread-c", + type=float, + default=DEFAULT_GUIDE_SPREAD_C, + help="guide-to-guide spread among the target's own guides: each guide's knockdown is " + "Beta with mean --effect-size and sd c * es * (1 - es) [default %(default)s, fitted to " + "per-guide data from three CRISPRi screens]. Zero at es = 0; must be in [0, 2). The " + "guides' mean is pinned to --effect-size, so this adds no uncertainty about the " + "element's effect. Other guides have no effect", + ) + parser.add_argument( + "--guide-sd", + type=float, + default=None, + help="retired; use --guide-spread-c. Passing it is an error", + ) + parser.add_argument( + "--n-jobs", + type=int, + default=8, + help="workers for this task. A unit of work is a (target, chunk of simulations) under " + "the fast driver and a (target, simulation) under the engine, so cis targets with a " + "handful of genes still keep every worker busy [default %(default)s]", + ) + parser.add_argument( + "--permutations", + choices=PERMUTATION_MODES, + default="per-target", + help="'per-target' tests every simulation of a target against one permutation set drawn " + "from the seed, as sceptre effectively does; 'per-replicate' draws a fresh set for each " + "simulation, and needs --driver engine [default %(default)s]. The simulated counts are " + "the same either way", + ) + parser.add_argument( + "--nulls", + choices=NULL_ROUTES, + default="sparse", + help="how the engine computes the permutation nulls: 'sparse' is pysceptre's draw-matrix " + "route, about twice as fast for one-target calls; 'scan' is pysceptre's own default. The " + "results are identical. The fast driver always takes the sparse route " + "[default %(default)s]", + ) + parser.add_argument( + "--driver", + choices=DRIVERS, + default="fast", + help="'fast' tests all of a target's simulations together, building its permutation " + "matrices once; the output is byte-identical to 'engine' with --permutations per-target " + "--nulls sparse. It needs --permutations per-target and a permutation-test screen (a " + "CRT screen needs --driver engine --null-fits refit), and always computes the nulls by " + "the sparse route [default %(default)s]", + ) + parser.add_argument( + "--null-fits", + choices=NULL_FIT_MODES, + default="reuse", + help="'reuse' fits each gene's null model once per simulation, on an independent draw " + "with no knockdown, and uses that fit for every target the gene is tested against (R's " + "FIT_NULL_MODELS approximation, docs/methods.md); it needs --driver fast. 'refit' fits " + "it on each simulation's own counts for every target, exactly, and is what --driver " + "engine does [default %(default)s]", + ) + parser.add_argument( + "--null-fits-file", + type=Path, + default=None, + help="with --null-fits reuse: the fits written by watteg-fit-null-models for this " + "prepared input and --seed. Without it the task fits its own genes first, with the same " + "keyed draws, so the values and the output are the same either way", + ) + parser.add_argument( + "--expression-model", + choices=("fitted", "size_factor"), + default="fitted", + help="'fitted' draws from exp(X.beta), sceptre's own null model. " + "'size_factor' reproduces the pre-2026-09-21 behaviour and exists " + "only for comparison against sweeps produced with it", + ) + args = parser.parse_args(argv) + + # Zero is allowed on purpose: it simulates no effect, and running it is the only + # way to measure this pipeline's false-call rate from its own output. Note what + # it measures: a call needs p < threshold AND a negative fold change, so with a + # two-sided p-value it estimates about alpha/2, not alpha. + if not 0.0 <= args.effect_size < 1.0: + raise SystemExit( + f"--effect-size must be a fractional decrease in [0, 1) (got {args.effect_size}); " + "0 simulates no effect" + ) + if args.reps < 1: + raise SystemExit("--reps must be at least 1") + if args.out is None and args.partials_out is None: + raise SystemExit("pass --out, --partials-out or both: otherwise nothing is written") + if args.partials_out is not None and args.threshold_file is None: + raise SystemExit("--partials-out needs --threshold-file: a call is p below the threshold") + if args.guide_sd is not None: + raise SystemExit( + "--guide-sd was replaced by --guide-spread-c on 2026-09-25: the spread is now " + "c * es * (1 - es) (default 0.65), not an absolute sd. Passing the old 0.13 as c " + "would shrink it fivefold, so it is refused rather than reinterpreted." + ) + if not 0.0 <= args.guide_spread_c < 2.0: + raise SystemExit(f"--guide-spread-c must be in [0, 2) (got {args.guide_spread_c})") + if args.driver == "fast" and args.permutations != "per-target": + raise SystemExit( + "--driver fast (the default) needs --permutations per-target: it builds a target's " + "permutation matrices once for all of its simulations, which is the engine's test " + "only when the simulations share one permutation set. Add --permutations per-target, " + "or use --driver engine --null-fits refit." + ) + + if args.null_fits == "reuse" and args.driver != "fast": + raise SystemExit( + "--null-fits reuse (the default) needs --driver fast: the engine calls pysceptre's " + "discovery entry point, which fits every gene itself. Add --null-fits refit to run " + "the engine." + ) + if args.null_fits_file is not None and args.null_fits != "reuse": + raise SystemExit("--null-fits-file is read only under --null-fits reuse") + + started = time.perf_counter() + sim = read_sim_input(args.prepared / "sim_input.h5") + params = AnalysisParams.from_analysis_mode(args.prepared / "analysis_mode.tsv") + if args.driver == "fast" and params.resampling_mechanism != "permutations": + raise SystemExit( + f"--driver fast (the default) runs the permutation test only, and this screen used " + f"{params.resampling_mechanism!r}. Use --driver engine --null-fits refit." + ) + design = pd.read_csv(args.prepared / "grna_targets.tsv", sep="\t") + split = pd.read_csv(args.pairs, sep="\t") + for column in ("grna_target", "response_id"): + if column not in split.columns: + raise SystemExit(f"{args.pairs} has no {column!r} column") + + # Many-to-many: a guide inside two overlapping elements belongs to both, so + # this is read from the design table and never from a per-unit annotation. + guides_of = design.groupby("grna_target")["grna_id"].apply(list) + genes_of = split.groupby("grna_target")["response_id"].apply(list) + reps = range(args.rep_offset + 1, args.rep_offset + args.reps + 1) + + print( + f"{len(genes_of)} targets / {len(split)} pairs, simulations {reps.start}-{reps.stop - 1}, " + f"effect size {args.effect_size} (relative expression {1 - args.effect_size:g}), " + f"estimand {args.estimand}\n" + f" baseline: {args.expression_model}, permutations {args.permutations}, " + + ( + f"nulls {args.nulls}, " + if args.driver == "engine" + else f"driver fast, nulls sparse (the fast driver's only route), null fits " + f"{args.null_fits}, " + ) + + f"n_jobs {args.n_jobs}, " + f"B1/B2/B3 {params.B1}/{params.B2}/{params.B3}, side_code {params.side_code}" + ) + + for target in genes_of.index: + if target not in guides_of: + raise SystemExit(f"no gRNA maps to target {target!r} in grna_targets.tsv") + + # WHY THE UNIT IS (target, simulations) AND NOT target. pysceptre's own n_jobs + # parallelises over the genes of one call, and a cis target has a median of 6 of + # them, so a task given 8 cores left most of them idle. Simulations are + # independent draws, so splitting them apart keeps every worker busy on cis and + # on trans alike. The per-target setup (guide assignment, baseline) is redone + # per unit; it is seeded, so it comes out identical, and it is under 1 % of a unit. + settings = dict( + effect_size=args.effect_size, + seed=args.seed, + guide_spread_c=args.guide_spread_c, + expression_model=args.expression_model, + permutations=args.permutations, + nulls=args.nulls, + estimand=args.estimand, + ) + _SHARED.update(sim=sim, params=params, grna_csc=sim.grna_perts.tocsc(), **settings) + if args.driver == "fast" and args.null_fits == "reuse": + settings["null_fits"] = _null_fits_for_task(args, sim, split, reps) + _SHARED["null_fits"] = settings["null_fits"] + if args.driver == "fast": + units = _fast_units(genes_of, guides_of, reps, max(args.n_jobs, 1)) + workers = min(max(args.n_jobs, 1), len(units)) + print(f" {len(units)} (target, simulation chunk) units on {workers} worker(s)") + results = _map_units(units, workers, args.prepared, settings, fn=_run_fast_unit) + else: + units = [(t, list(g), guides_of[t], r) for t, g in genes_of.items() for r in reps] + workers = min(max(args.n_jobs, 1), len(units)) + print(f" {len(units)} (target, simulation) units on {workers} worker(s)") + results = _map_units(units, workers, args.prepared, settings) + + by_target: dict[str, list] = {} + for target, frame, elapsed in results: + by_target.setdefault(target, []).append((frame, elapsed)) + frames = [] + for target, genes in genes_of.items(): + done = by_target[target] + if all(frame is None for frame, _ in done): + print(f" {target}: skipped, no perturbed cells") + continue + frames.extend(frame for frame, _ in done if frame is not None) + elapsed = sum(e for _, e in done) + print( + f" {target}: {len(genes)} pairs in {elapsed:.1f}s of worker time " + f"({elapsed / args.reps:.2f}s/simulation)" + ) + + # Every target in the split can have been skipped; an empty table with the + # right columns keeps the row-count check downstream meaningful. + combined = pd.concat(frames, ignore_index=True) if frames else pd.DataFrame(columns=KEEP) + for column in ("pass_qc", "n_nonzero_trt", "n_nonzero_cntrl"): + if column not in combined: + combined[column] = np.nan + combined["estimand"] = args.estimand + info = args.prepared / "pairs_with_info.tsv" + if info.exists(): + # Real-data QC counts, constant across simulations, joined rather than + # recomputed per draw -- which is what the R implementation does too. + known = pd.read_csv(info, sep="\t") + cols = [c for c in ("n_nonzero_trt", "n_nonzero_cntrl", "pass_qc") if c in known] + combined = combined.drop(columns=cols).merge( + known[["grna_target", "response_id", *cols]], + on=["grna_target", "response_id"], + how="left", + ) + + combined = combined[[c for c in KEEP if c in combined]] + if args.out is not None: + args.out.parent.mkdir(parents=True, exist_ok=True) + combined.to_csv(args.out, sep="\t", index=False) + print(f"\nwrote {len(combined):,} rows to {args.out}") + if args.partials_out is not None: + _write_partials(combined, args.threshold_file, args.partials_out) + print(f"done in {time.perf_counter() - started:.1f}s") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/watteg/cli/split_pairs.py b/src/watteg/cli/split_pairs.py new file mode 100644 index 0000000..c97fdcc --- /dev/null +++ b/src/watteg/cli/split_pairs.py @@ -0,0 +1,103 @@ +"""Split the discovery pairs into per-task chunks, balanced by cost. + + watteg-split-pairs --pairs pairs.tsv --outdir splits/ --n-splits 480 + +A target's pairs cannot be separated: the simulation draws one count matrix per +(target, replicate) and tests every one of that target's genes against it, so +splitting a target would simulate it twice. Targets are therefore the unit, and +the task is bin packing them. + +**Weighted by pairs plus a per-target overhead**, because a task's cost is not +proportional to its pairs. The measured model is `intercept + slope x pairs`, +and on day0 the intercept is a real share of a small target's cost, so weighting +by pairs alone over-fills the splits that hold many small targets. + +Longest-processing-time-first: sort by weight descending, then put each target +in whichever split is currently lightest. Deterministic, and within a small +constant factor of optimal for this shape. +""" + +from __future__ import annotations + +import argparse +import sys +from pathlib import Path + +import pandas as pd + + +def assign_splits(weights: pd.Series, n_splits: int) -> pd.Series: + """Target -> split number (1-based), by longest-processing-time-first.""" + load = [0.0] * n_splits + assignment = {} + for target, weight in weights.sort_values(ascending=False).items(): + lightest = min(range(n_splits), key=lambda k: load[k]) + assignment[target] = lightest + 1 + load[lightest] += float(weight) + return pd.Series(assignment, name="split") + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--pairs", type=Path, required=True) + parser.add_argument("--outdir", type=Path, required=True) + parser.add_argument("--n-splits", type=int, required=True) + parser.add_argument("--prefix", default="split_") + parser.add_argument( + "--target-overhead", + type=float, + default=2.0, + help="pairs-equivalent fixed cost of a target, from the measured cost model " + "[default %(default)s]", + ) + args = parser.parse_args(argv) + + if args.target_overhead < 0: + raise SystemExit("--target-overhead must not be negative") + + pairs = pd.read_csv(args.pairs, sep="\t") + for column in ("grna_target", "response_id"): + if column not in pairs.columns: + raise SystemExit(f"{args.pairs} has no {column!r} column") + if pairs.empty: + raise SystemExit(f"{args.pairs} contains no pairs") + + per_target = pairs.groupby("grna_target").size() + if args.n_splits > len(per_target): + raise SystemExit( + f"--n-splits ({args.n_splits}) exceeds the number of targets ({len(per_target)}). " + "Every split must hold at least one target; lower --n-splits." + ) + print( + f"{len(pairs):,} pairs over {len(per_target):,} targets " + f"({per_target.min()}-{per_target.max()} each, median {per_target.median():g})" + ) + + split_of = assign_splits(per_target + args.target_overhead, args.n_splits) + pairs = pairs.assign(split=pairs["grna_target"].map(split_of)) + + args.outdir.mkdir(parents=True, exist_ok=True) + # Zero-padded so lexicographic order matches numeric order, which keeps a + # workflow engine's channel ordering and a manual listing predictable. + width = max(2, len(str(args.n_splits))) + written = 0 + sizes = [] + for k in range(1, args.n_splits + 1): + chunk = pairs.loc[pairs["split"] == k, ["grna_target", "response_id"]] + chunk = chunk.sort_values(["grna_target", "response_id"]) + chunk.to_csv(args.outdir / f"{args.prefix}{k:0{width}d}.tsv", sep="\t", index=False) + written += len(chunk) + sizes.append(len(chunk)) + + if written != len(pairs): + raise SystemExit(f"wrote {written} pairs but read {len(pairs)}; the split lost rows") + print(f"wrote {args.n_splits} splits to {args.outdir}") + print( + f" pairs per split: {min(sizes)}-{max(sizes)}, " + f"imbalance (max/min) {max(sizes) / max(min(sizes), 1):.3f}" + ) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/watteg/cli/summarize_power.py b/src/watteg/cli/summarize_power.py new file mode 100644 index 0000000..6716728 --- /dev/null +++ b/src/watteg/cli/summarize_power.py @@ -0,0 +1,171 @@ +"""One row per pair, across every effect size in a sweep. + + watteg-summarize-power --power power_es0.05.tsv power_es0.15.tsv ... \ + --sim-input prepared/sim_input.h5 --out power_summary.tsv + +Each effect size contributes `power_at_effect_size_