diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ba24c8a24..0c6b246cd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,10 +2,14 @@ name: CI on: push: - branches: [rift_O4d, rift_O4d_junior] + branches: [rift_O4d, rift_O4d_junior, rift_O4d_gmm_gpu] pull_request: - branches: [rift_O4d, rift_O4d_junior] + branches: [rift_O4d, rift_O4d_junior, rift_O4d_gmm_gpu] workflow_dispatch: + schedule: + # Weekly canary so a fresh UPSTREAM release (e.g. swig>=4.4.0 -- see #136) + # is caught even when there is no RIFT commit. Mondays 06:00 UTC. + - cron: '0 6 * * 1' concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -17,7 +21,10 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.10', '3.11', '3.12', '3.13'] + # Smoke-test that the package resolves and installs across the supported + # Python range, including the legacy py3.9 lane (paired with the pinned + # numpy==1.24.4 used by the integrator gate below). + python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 @@ -60,11 +67,27 @@ jobs: import-check: needs: install runs-on: ubuntu-latest + # Verify every declared module imports cleanly under both the pinned + # legacy lane (py3.9 + numpy 1.24.4) and the modern lane (py3.12 + + # numpy 2.x). Catches platform-portability regressions like the + # np.float128 import-time crash on numpy 2.x systems without an + # extended-precision long double. + strategy: + fail-fast: false + matrix: + include: + - lane: legacy + python-version: '3.9' + numpy-pin: 'numpy==1.24.4' + - lane: modern + python-version: '3.12' + numpy-pin: 'numpy>=2.0,<3.0' + name: import-check (${{ matrix.lane }} py${{ matrix.python-version }}) steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: ${{ matrix.python-version }} cache: 'pip' cache-dependency-path: requirements.txt - name: Enable symlink @@ -73,8 +96,14 @@ jobs: run: | python -m pip install --upgrade pip --break-system-packages python -m pip install -r requirements.txt --break-system-packages + # Pin numpy AFTER requirements.txt so it overrides the unpinned + # 'numpy' line in requirements.txt without changing the file. + python -m pip install '${{ matrix.numpy-pin }}' --break-system-packages python -m pip install coverage pytest --break-system-packages python -m pip install --editable . --break-system-packages + - name: Show resolved versions + run: | + python -c "import sys, numpy, scipy; print('python', sys.version); print('numpy', numpy.__version__); print('scipy', scipy.__version__)" - name: Run import check run: python .travis/test-all-mod.py @@ -108,11 +137,29 @@ jobs: test-run: needs: install runs-on: ubuntu-latest + # Integrator + posterior gate. We run this in two CI lanes: + # - legacy : py3.9 + numpy 1.24.4 -- the historically known-good + # configuration on Linux x86_64 where np.float128 is real. + # - modern : py3.12 + numpy 2.x -- the forward-looking target. Catches + # numpy 2.x removals (np.product, np.cumproduct, np.in1d, + # np.alltrue, np.float_) and scipy >= 1.16 mvnun removal. + # Both lanes must pass test-integrate.sh's GMM/AC/AV consistency check. + strategy: + fail-fast: false + matrix: + include: + - lane: legacy + python-version: '3.9' + numpy-pin: 'numpy==1.24.4' + - lane: modern + python-version: '3.12' + numpy-pin: 'numpy>=2.0,<3.0' + name: test-run (${{ matrix.lane }} py${{ matrix.python-version }}) steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: ${{ matrix.python-version }} cache: 'pip' cache-dependency-path: requirements.txt - name: Enable symlink @@ -121,8 +168,14 @@ jobs: run: | python -m pip install --upgrade pip --break-system-packages python -m pip install -r requirements.txt --break-system-packages + # Pin numpy AFTER requirements.txt so it overrides the unpinned + # 'numpy' line in requirements.txt without changing the file. + python -m pip install '${{ matrix.numpy-pin }}' --break-system-packages python -m pip install coverage pytest pytest-cov --break-system-packages python -m pip install --editable . --break-system-packages + - name: Show resolved versions + run: | + python -c "import sys, numpy, scipy; print('python', sys.version); print('numpy', numpy.__version__); print('scipy', scipy.__version__)" - name: Run test scripts run: | . .travis/test-coord.sh @@ -135,11 +188,70 @@ jobs: if: failure() uses: actions/upload-artifact@v4 with: - name: test-logs + name: test-logs-${{ matrix.lane }}-py${{ matrix.python-version }} path: | **/*.log **/test-results/*.xml + container-dep-canary: + # Dependency-resolution canary for the container build. The container ships + # an UNPINNED dependency set (containers/requirements-container.txt) and + # clones RIFT at build time, so a fresh upstream release can silently break + # RIFT and only surface when a container rebuild fails (e.g. swig>=4.4.0, + # issue #136). This job installs that same unpinned set + exercises the pixi + # swig-post44 deployment lane and runs the import check, so we get an early + # warning. Runs on push/PR AND weekly (see on.schedule). + # + # Non-blocking: it tracks UPSTREAM changes outside any PR author's control, + # so a red run should alert maintainers, not block unrelated PRs. + runs-on: ubuntu-latest + continue-on-error: true + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.10' # matches the container's python3.10 + - name: Enable symlink + run: sudo ln -sf $(which python3) /usr/bin/python + - name: Install system dependencies + run: sudo apt-get update && sudo apt-get install -y libgsl-dev + - name: Install UNPINNED container dependency set (latest upstream) + run: | + python -m pip install --upgrade pip --break-system-packages + # Strip the GPU-only cupy line: there is no GPU/driver in CI and the + # canary's goal is dependency RESOLUTION + RIFT import, not cupy exec. + # (cupy is not actually listed in requirements-container.txt, but be + # defensive in case it is added later.) + grep -viE '^\s*cupy' containers/requirements-container.txt > /tmp/req-canary.txt + python -m pip install -r /tmp/req-canary.txt --break-system-packages + python -m pip install --editable . --break-system-packages + - name: Show resolved versions + run: | + python -c "import sys, numpy, scipy; print('python', sys.version); print('numpy', numpy.__version__); print('scipy', scipy.__version__)" + - name: Import check (latest container deps) + run: python .travis/test-all-mod.py + + container-swig-canary: + # Companion to container-dep-canary: exercise the pixi swig-post44 lane, + # which is the direct issue-#136 detector (swig>=4.4.0 breaking RIFT's + # generated bindings). Kept as its own job so a swig failure is distinct + # from a general dependency-resolution failure. Also non-blocking. + runs-on: ubuntu-latest + continue-on-error: true + steps: + - uses: actions/checkout@v4 + - name: Install system dependencies + run: sudo apt-get update && sudo apt-get install -y libgsl-dev + - uses: prefix-dev/setup-pixi@v0.8.1 + with: + environments: swig-post44 + - name: swig version (post-4.4 lane) + run: pixi run -e swig-post44 swig-version + - name: Install RIFT (post-4.4 swig lane) + run: pixi run -e swig-post44 install-rift + - name: Import check (post-4.4 swig lane) + run: pixi run -e swig-post44 import-check + docs: runs-on: ubuntu-latest permissions: diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1dbe43917..b519cb6c0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -16,6 +16,29 @@ stages: # - docker image # - deploy +.pixi_template: + stage: system tests + before_script: [] + variables: + PIXI_HOME: "$CI_PROJECT_DIR/.pixi" + PIXI_CACHE_DIR: "$CI_PROJECT_DIR/.pixi-cache" + XDG_CACHE_HOME: "$CI_PROJECT_DIR/.pixi-cache/xdg" + PATH: "$CI_PROJECT_DIR/.pixi/bin:$PATH" + cache: + key: pixi-$CI_JOB_NAME + paths: + - .pixi/ + - .pixi-cache/ + - .pixi/envs/ + script: + - apt-get update --assume-yes && apt-get install --assume-yes ca-certificates curl git + - curl -fsSL https://pixi.sh/install.sh | bash + - export PATH="$PIXI_HOME/bin:$PATH" + - pixi --version + - pixi run -e "$PIXI_ENV" swig-version + - pixi run -e "$PIXI_ENV" install-rift + - pixi run -e "$PIXI_ENV" import-check + .install_docker_dependencies: before_script: - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY @@ -66,6 +89,16 @@ test_run: - bash .travis/test-run-alts.sh - bash .travis/test-build.sh +pixi_swig_pre44: + extends: .pixi_template + variables: + PIXI_ENV: swig-pre44 + +pixi_swig_post44: + extends: .pixi_template + variables: + PIXI_ENV: swig-post44 + # build:test: # image: docker:latest # stage: docker image diff --git a/.travis/test-build.sh b/.travis/test-build.sh index d2f97c94d..f1dae7d7c 100755 --- a/.travis/test-build.sh +++ b/.travis/test-build.sh @@ -1,5 +1,13 @@ #! /bin/bash -# This is just a pipeline build test. The coinc file is from a synthetic event. +# Pipeline build test. The coinc file is from a synthetic event. +# +# Builds (does not submit) RIFT DAGs from a reference ini + coinc using fake +# data, and verifies that the per-distance likelihood export flags (Plan A +# density grid, Plan B fixed-distance slices) thread through +# util_RIFT_pseudo_pipe.py -> create_event_parameter_pipeline_* and land in the +# correct condor submit file (ILE_extr.sub, the extrinsic stage). + +set -e export RIFT_LOWLATENCY=True export SINGULARITY_RIFT_IMAGE=foo @@ -7,4 +15,50 @@ export SINGULARITY_RIFT_IMAGE=foo export SINGULARITY_BASE_EXE_DIR=/usr/bin/ alias gw_data_find=/bin/true # don't want to reall do the datafind job touch foo.cache -util_RIFT_pseudo_pipe.py --use-ini `pwd`/.travis/ref_ini/GW150914.ini --use-coinc `pwd`/.travis/ref_ini/coinc.xml --use-rundir `pwd`/test_build_pipe --fake-data-cache `pwd`/foo.cache + +REF_INI=`pwd`/.travis/ref_ini/GW150914.ini +COINC=`pwd`/.travis/ref_ini/coinc.xml + +# require a flag to be present in a file +assert_has() { # file pattern + if ! grep -q -- "$2" "$1"; then + echo "FAIL: expected '$2' in $1"; exit 1 + fi +} +# require a flag to be absent from a file +assert_absent() { # file pattern + if grep -q -- "$2" "$1"; then + echo "FAIL: did not expect '$2' in $1"; exit 1 + fi +} + +# --- 1. baseline build (original smoke test) --- +util_RIFT_pseudo_pipe.py --use-ini $REF_INI --use-coinc $COINC --use-rundir `pwd`/test_build_pipe --fake-data-cache `pwd`/foo.cache + +# --- 2. Plan-A distance-grid export, threaded onto the extrinsic stage --- +# Distance marginalization must stay ON for the intrinsic ILE jobs (speedup) +# and be disabled ONLY at the extrinsic export stage. The trailing space in +# the pattern matches the standalone --distance-marginalization flag but not +# --distance-marginalization-lookup-table. +util_RIFT_pseudo_pipe.py --use-ini $REF_INI --use-coinc $COINC --use-rundir `pwd`/test_build_grid --fake-data-cache `pwd`/foo.cache --add-extrinsic --export-marginal-distance-grid +assert_has `pwd`/test_build_grid/ILE_extr.sub "--export-marginal-distance-grid" +assert_has `pwd`/test_build_grid/ILE_extr.sub "--internal-use-lnL" +assert_absent `pwd`/test_build_grid/ILE.sub "--export-marginal-distance-grid" +assert_has `pwd`/test_build_grid/args_ile.txt "--distance-marginalization " +assert_has `pwd`/test_build_grid/ILE.sub "--distance-marginalization " +assert_absent `pwd`/test_build_grid/ILE_extr.sub "--distance-marginalization " +echo "OK: Plan-A grid export only on ILE_extr.sub; distance marginalization disabled only at the extrinsic stage" + +# --- 3. Plan-B distance-slice export, threaded onto the extrinsic stage --- +util_RIFT_pseudo_pipe.py --use-ini $REF_INI --use-coinc $COINC --use-rundir `pwd`/test_build_slices --fake-data-cache `pwd`/foo.cache --add-extrinsic --export-distance-slices 10 --export-distance-slices-wing-delta-lnL 7.0 --export-distance-slices-skip-threshold 1.0 +assert_has `pwd`/test_build_slices/ILE_extr.sub "--export-distance-slices 10" +assert_has `pwd`/test_build_slices/ILE_extr.sub "--distance-slice-wing-delta-lnL 7.0" +assert_has `pwd`/test_build_slices/ILE_extr.sub "--distance-slice-skip-threshold 1.0" +assert_has `pwd`/test_build_slices/ILE_extr.sub "--internal-use-lnL" +assert_absent `pwd`/test_build_slices/ILE.sub "--export-distance-slices" +assert_has `pwd`/test_build_slices/args_ile.txt "--distance-marginalization " +assert_has `pwd`/test_build_slices/ILE.sub "--distance-marginalization " +assert_absent `pwd`/test_build_slices/ILE_extr.sub "--distance-marginalization " +echo "OK: Plan-B slice export only on ILE_extr.sub; distance marginalization disabled only at the extrinsic stage" + +echo "test-build.sh: all pipeline-build checks passed" diff --git a/MonteCarloMarginalizeCode/Code/RIFT/calmarg/DESIGN_adaptive_driver.md b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/DESIGN_adaptive_driver.md new file mode 100644 index 000000000..d404ae285 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/DESIGN_adaptive_driver.md @@ -0,0 +1,321 @@ +# Adaptive calibration sampling: driver plan + +Status: **planning** (do not implement the multi-stage loop until we pick a path). + +## Zero-cal burn-in of the extrinsic sampler (proposed; ILE-level, analyze_event) + +**Problem.** Calibration marginalization makes the extrinsic integral much harder to +converge: with cal drawn from the broad PRIOR the per-time lnL has a large dynamic range, +so the adaptive sampler (AV) struggles to reach a useful `n_eff` -- and on the FIRST +iteration there is no learned cal proposal yet, so every cold-start ILE job is in this +regime. Empirically (local DAG run) iteration-0 points come out with `sigma ~ 0.7-0.9` +and few effective samples; high-SNR sources are hard even WITHOUT cal, and cal makes it +worse. We are effectively failing to seed the *intrinsic* grid because the *extrinsic* +sampler never gets going. + +**Idea (O'Shaughnessy).** Burn the sampler in on a *different, cheaper* likelihood first +-- the ZERO-CAL (n_cal=1) baseline -- until it reaches a minimal `n_eff`, so the +extrinsic sampling proposal is "roughly right", THEN switch to the full cal-marginalized +likelihood for the production estimate. The extrinsic posterior shape is nearly the same +with and without cal (cal mostly rescales / mildly shifts lnL), so the burned-in proposal +is an excellent warm start -- and the zero-cal evaluations are ~`n_cal`x cheaper. + +**Where it lives.** `analyze_event` in `integrate_likelihood_extrinsic_batchmode`. The +likelihood closures already read the module-scope `n_cal_for_likelihood`; the sampler also +already supports a warm start via `sampler.update_sampling_prior(..., external_rvs=...)` +(the existing `oracleRS` path, ~line 2078). Two viable mechanisms: + + 1. **Two-phase integrate (simplest).** Set `n_cal_for_likelihood = 1`, call + `sampler.integrate(likelihood_function, ..., n_eff=burn_in_neff)` (the closures now + evaluate the fast zero-cal baseline), then restore `n_cal_for_likelihood` and call + the production `sampler.integrate(...)` WITHOUT resetting -- reusing the adapted + proposal. Risk: AV's reset semantics across two integrate() calls in one + analyze_event are unverified (it "always resets every iteration" between DAG + iterations; need to confirm it does NOT reset at the start of integrate()). + 2. **Warm-start via update_sampling_prior (robust).** Run the zero-cal burn-in, + harvest its drawn extrinsic samples + lnL, and feed them to + `sampler.update_sampling_prior(external_rvs=...)` exactly like the oracle path, then + run the production integrate. Survives regardless of integrate()'s reset behavior. + +**Proposed flag.** `--calibration-burn-in-neff ` (0/None = off): target n_eff for +the zero-cal burn-in (capped by a fraction of n-max). Default off; opt-in. + +**Relation to the bigger seeding plan.** This is the in-job version of the same idea as +the *zero-cal pilots* that seed the intrinsic/extrinsic grids for high-SNR sources: burn +in cheaply, then pay for cal only once the sampling is on-target. The pilot (`pilot.py`, +util_CalPilotStage) seeds the CAL proposal across iterations; this burn-in seeds the +EXTRINSIC proposal within a single job. They compose. + +Status: implemented behind `--calibration-burn-in-neff` (two-phase integrate, toggling +`n_cal_for_likelihood`), correctness-safe (the production integral is always the full cal +one). BUT see the sampler limitation below. + +### Sampler limitation (measured / per review) -- BREADCRUMB for future work +The default and most efficient extrinsic sampler, **AV (mcsamplerAdaptiveVolume), +COMPLETELY RESETS between `integrate()` calls** -- there is currently no seedable AV. So +the two-phase burn-in gives AV **no speedup** (the adapted proposal is thrown away); it is +only ever correctness-safe overhead there. Moreover, **re-seeding AV is inherently +dangerous**: AV can only *contract* its volume, never *expand* or *shift* its boundaries, +so a burn-in proposal that is slightly off / too tight would trap the production phase in +the wrong region. The other integrators (**GMM**, **portfolio**) CAN reuse sampling +models (their `update_sampling_prior` / `gmm_dict` hooks) but are less efficient overall. + +Therefore the zero-cal burn-in only pays off once we have: + 1. a **seedable AV** (warm-start AV from external samples / a prior proposal), and/or + 2. a **more flexible, boundary-shifting AV** that can EXPAND and TRANSLATE its sampling + volume (not only contract) -- so a warm start can't trap it. +Both would be broadly useful well beyond calmarg (every iteration, every warm start). +Until then: keep the burn-in flag (harmless, gated, ready) but do NOT rely on it for AV; +for GMM/portfolio it can warm-start via their model-reuse hooks (untested). The cal PILOT +(across-iteration proposal learning) and the prior-shrinkage backstop remain the load- +bearing pieces for cal; extrinsic seeding waits on seedable AV. + +## Where we are + +- In-loop calmarg works and is validated (loop == fused == reference ~1e-14; CPU+GPU; + default/distmarg; phase-marg). See `DESIGN_calmarg_in_loop.md`. +- **Phase 0** (importance weights, `cal_log_weights`) is wired end-to-end: the + marginalizer computes `Z_cal = sum_c exp(log_w_c) integral L_c / sum_c exp(log_w_c)`. +- **Phase 1 core** (`adaptive.py`) is implemented and unit-tested standalone: a tempered + unimodal-Gaussian proposal in cal spline-node space, importance weights + `w_c = prior/proposal`, fit to the cal posterior, `neff` diagnostics. It needs an + `evaluate(nodes) -> log integral_theta L` callback to run for real. + +The open question is **how to supply that callback in the driver without making the run +expensive** — i.e., how to *learn* the cal proposal during a real analysis. + +## Key facts that shape the choice + +1. **The extrinsic integration is slow even at Lmax=2** (toy problem). So anything that + multiplies the number of full integrations (brute force, multi-stage adaptive) is + costly. Per-likelihood-evaluation timing (`backtest_calmarg.py --scan-ncal`, GPU, + 3 IFO, distmarg, 4096 extrinsic samples, ms/eval): + + | n_cal | reference (brute) | loop (Option B) | fused (Option C) | + |------:|------------------:|----------------:|-----------------:| + | 1 | 57.6 | 57.2 | 57.2 | + | 10 | 571 | 266 | 66.9 | + | 50 | 2854 | 1191 | 198 | + | 100 | 5702 | 2347 | 362 | + | 200 | 11427 | 4662 | 704 | + + The marginal cost of one extra cal realization is ~57 ms (brute), ~23 ms (loop), and + ~3.3 ms (fused) -- fused amortizes the cal axis ~18x better than brute force. A + brute-force reference at n_cal=200 is ~11 s **per likelihood evaluation**; a full + integration is thousands of evaluations, i.e. hours-to-infeasible -> reference only. + Fused at n_cal=200 is ~0.7 s/eval -> a production integration is feasible. Net: + **the production path must be fused**, and we still want to keep n_cal modest via a + learned proposal (the pilot below). +2. **Calibration is boring**: the cal posterior is smooth, unimodal, and — crucially — + nearly **independent of the extrinsic parameters** across the high-likelihood region + (it is set by the data + best-fit template, not by sky/inclination/etc). So we do + NOT need to relearn cal per extrinsic sample, nor iterate many times. +3. The calmarg lnL sitting ABOVE the no-cal baseline is **expected physics, not a bug**: + for cal-on-data, at fixed theta lnL_c = lnL_baseline + (delta.h|h) with mean 0, so + Z_cal(theta) = E_C[L] = L_baseline * exp(+ shift); logmeanexp(lnL_c) > mean(lnL_c) + (dominated by the best-fitting cal draws). Confirmed at the injection point on real + data: mean(lnL_c) ~ baseline, logmeanexp(lnL_c) above it by a positive margin. Small + cal variance -> shift ~ 0.5*Var_c[lnL_c]; high SNR -> larger (best-draw dominated). + This is ALSO why neff_cal collapses and adaptive sampling is needed. + +## Options (and recommendation) + +### A. Brute-force reference (DO — as validation, not production) +Marginalize cal "the hard way": draw a large prior cal set and run the full extrinsic +integral, or sample (theta, cal) jointly with no proposal learning. Cleanest and +unambiguous; the **ground truth** to validate B/C and to settle the baseline-vs-calmarg +question. Slow (cost ~ `n_cal` x single integration), so it is a *reference harness*, +not the production path. Implement as a mode that runs the existing integration with a +large prior cal set and high neff, and compare to the lazy/seeded result. + +### B. Expanding scope: portable (extrinsic + cal) distribution / normalizing flow (LONG TERM) +Capture the learned joint (extrinsic + cal) posterior in a **portable object** to pass +downstream — historically a normalizing flow. This is the decade-old "breadcrumbs" +goal; it has failed before partly because it was bolted on per-integrator and never +standardized. The cal framework sits deep in the core and faces the *same* challenge, +so the right move now is **not** to build a full NF, but to define a clean, +integrator-agnostic **breadcrumb interface**: a small object that can `save`/`load` a +learned proposal (start: Gaussian mean/cov over cal nodes + the importance weights; +later: an NF), with a stable schema. Build the hook; defer the NF. + +### C. Lazy pilot (RECOMMENDED first production path) +Because cal is boring and ~extrinsic-independent, learn it ONCE from a cheap pilot: + +1. Get a handful (K ~ tens) of **high-likelihood extrinsic test points** — e.g. the top-K + by lnL from the first ILE iteration / the proposed grid, or just the best-fit point. +2. At those K points, evaluate the per-cal-realization likelihood **fully** (this is K + cheap evaluations, embarrassingly parallel — "spam in parallel"). Average the + responsibilities over the K points (they agree, since cal is extrinsic-independent). +3. Fit the Gaussian proposal (`adaptive.fit_proposal`, tempered) -> seed the cal nodes. +4. Redraw the run's cal realizations from the proposal; set `cal_log_weights = + prior/proposal` (Phase 0). Run the main integration once with the seeded set. +5. (Optional) one refine pass if `neff_cal` is still low. + +This is a single extra pilot (not a multi-stage loop), exploits cal's boringness, reuses +Phase 0 + Phase 1, and degrades gracefully (if the pilot is poor, importance weights +keep it unbiased — just less efficient). + +## AGREED architecture and priority (do all of A, C, B to prep for the future) + +Priority order **A -> C -> B**: +- **A is the critical benchmark** -- the *only* validation. Build first. +- **C is production** (the parallel-pilot DAG below). +- **B is the future** (portable extrinsic+cal distribution / normalizing flow). Lay + breadcrumbs + stub code now so the plan is remembered. + +This is a deliberate "long jump": more structure than calmarg strictly needs, because +the same machinery generalizes to saving the **extrinsic** distribution (the decade-old +goal). Longer path, but richer payoff and easy to exploit later. + +### Source of pilot points: harvest from the previous iteration's `*.composite` +Every RIFT iteration already produces a `*.composite` of evaluated (intrinsic+extrinsic) +points with their lnL -- plenty of trials, no need for a dedicated pilot integration. +The pilot **harvests the top fraction by lnL (~top 5%)** from iteration N-1's composite +and does full cal there. (This same harvest generalizes to learning the extrinsic +proposal.) + +### Parallel-pilot DAG (nothing serial) +Per iteration N, run in parallel: +- **wide_N**: the normal ILE iteration, with `n_cal` modest, its cal realizations SEEDED + from the consolidated proposal produced after iteration N-1 (importance-weighted, + Phase 0). This is the production likelihood. +- **pilot_N**: harvest top-5% lnL points from iteration N-1's composite; do FULL cal at + those points (large prior `n_cal`, embarrassingly parallel -- "spam in parallel"); + emit a breadcrumb (per-point cal responsibilities / a fitted Gaussian). + +Then a **consolidation_N** job (the barrier between N and N+1) collects the pilot +breadcrumbs into a single consolidated cal proposal (Gaussian mean/cov over cal nodes + +importance-weight bookkeeping). **pilot_N informs wide_{N+1}** through that consolidated +proposal. A **cap** limits how many iterations keep pilot jobs active (once cal is +learned -- it is boring -- freeze the proposal and drop the pilots). + +``` + iter N-1.composite ──► pilot_N ──┐ + ├─► consolidation_N ──► wide_{N+1} (seeded) + (wide_N runs in parallel) ──┘ + (pilots run for the first ~K iterations, then frozen) +``` + +### B (breadcrumbs / future): portable distribution object +The consolidated proposal is a **portable save/load object** with a stable, +integrator-agnostic schema. Start: a Gaussian over cal spline nodes (mean, cov) + the +prior + importance-weight metadata. Designed from the start to ALSO carry an extrinsic +proposal (same harvest->fit->consolidate->seed structure). NF is a later drop-in behind +the same interface. Stub the schema + the consolidation/seed hooks now. + +## n_eff is conservative vs the true ESS (refines the starvation math) +RIFT's reported `n_eff` is a deliberately CONSERVATIVE lower bound -- the true effective +sample size (ESS) is meaningfully larger. So `n_eff(us)=100` yields appreciably more +usable fair-draw points than 100. Consequences: +- The earlier "low n_eff" worry was over-pessimistic: with enough samples the integrator + creeps up fine (the tune-condor run reached n_eff>200 on the moderate-SNR injection), and + the usable ESS is larger still. +- Pilot harvesting is LESS starved than the conservative count implied: top-fraction of + the composite + the larger-than-n_eff ESS means a real run can pull out enough + high-quality points to inform the cal proposal after all. The d(d+1)/2 requirement for + a FULL covariance still holds, but the prior-shrinkage backstop covers the residual + unconstrained directions, so we do not need to fully resolve every cal dof to be safe. + +## Build order (this branch) +1. **Timing data** -- done (`--scan-ncal`). +2. **A: brute-force reference** -- prior-only large-`n_cal`, converged; the ground truth. + Testable now in the backtest: brute-force (large prior set) vs adaptive-seeded must + agree on Z_cal while the seeded run has far higher `neff_cal`. +3. **B-lite breadcrumb I/O** -- `save/load` the cal proposal (Gaussian; schema with an + `extrinsic` slot reserved). Used by C. +4. **C core** -- harvest top-fraction from a `*.composite`; fit (adaptive.fit_proposal); + write/consolidate breadcrumbs; seed the next run's cal realizations. +5. **C DAG wiring** (pilot || wide || consolidation, the cap) in the pipeline builder -- + DONE (opt-in; default DAG byte-identical). See "DAG wiring" below. NEEDS a condor + smoke test on a real cluster run (cannot be exercised off-cluster), like the main-path + GPU end-to-end test. + +## DAG wiring (implemented; opt-in via `--calmarg-pilot`) + +A single per-iteration **calpilot** condor job collapses harvest -> dump -> fit -> +consolidate into one process (`bin/util_CalPilotStage.py`), so the pipeline-builder +surgery is minimal and the steps (which are serial anyway) stay in one place: + +``` + iteration N composite ──► CALPILOT_N (util_CalPilotStage.py): + 1. util_CalHarvestGrid.py top-frac high-lnL pts -> cal_pilot_grid_N.xml.gz + 2. ILE --calibration-dump-responsibilities (cheap: skips the extrinsic sampler) + [+ --calibration-proposal-breadcrumb cal_consolidated_{N-1}.npz -> refine] + 3. util_CalPilotFit.py -> cal_proposal_N.npz (auto-tempered) + 4. util_CalConsolidate.py -> cal_consolidated_N.npz + │ + (CALPILOT_N runs ∥ CIP_N/puff_N; parent = unify_N, does NOT gate them) + ▼ + wide ILE jobs of iteration N+1 --calibration-proposal-breadcrumb cal_consolidated_N.npz + (depend on CALPILOT_N; a missing breadcrumb at early N falls back to the prior) +``` + +- `dag_utils.write_calpilot_sub` defines the job; `create_event_parameter_pipeline_BasicIteration` + instantiates `calpilot_node` per active iteration (parent `unify_node`), records it, and + makes iteration N+1's wide ILE nodes depend on `calpilot_node[N]`. ILE nodes carry a new + `macroiterationprev` macro so the per-iteration breadcrumb path resolves. +- **Cap & cadence**: `--calmarg-pilot-max-it` (default 3), `--calmarg-pilot-cadence` + (default 1) -- pilots stop once cal is learned (cal is boring), freezing the proposal. +- `util_RIFT_pseudo_pipe.py`: `--calmarg-pilot[-cadence|-max-it|-top-fraction|-max-points]` + add the CEPP flags and append the `--calibration-proposal-breadcrumb + .../cal_consolidated_$(macroiterationprev).npz` to the wide ILE args (args_ile.txt). + +Run: add `--calmarg-pilot` to a `util_RIFT_pseudo_pipe.py` invocation that already uses +`--calmarg-envelope-directory ...`. Everything is opt-in; without `--calmarg-pilot` the +DAG and ILE behavior are unchanged. + +NOTE (subdag/exploded-ILE): the seed dependency is wired for the standard ILE batch path; +the `--ile-group-subdag` grouped path would need the dependency placed on the subdag node +(left as a follow-up; uncommon for calmarg runs). + +## Implemented executable decomposition (this branch) + +The pilot/seed loop is realized with two ILE flags + two thin CLIs, all opt-in (the +default DAG and likelihood are byte-identical when unused): + +- `generate_realizations.py` (refactored, prior draws byte-identical): + - `build_realizations_from_nodes(...)` -- spline construction, shared by prior & proposal. + - `node_prior(...)` -- the diagonal-Gaussian cal prior per detector. + - `draw_prior_realizations_with_nodes(...)` -- prior draws that KEEP the node vectors + (cold pilot, N=0). + - `seed_realizations_from_breadcrumb(...) -> (factors, cal_log_weights, nodes)` -- draw + cal realizations from a learned proposal + Phase-0 weights log(prior/proposal). +- `factored_likelihood.DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop(..., return_cal_components=True)` + returns the RAW per-realization time-integrated log L `(npts_extrinsic, n_cal)` before + the cal collapse (loop method). Validated: `logsumexp_c(components) - log(n_cal)` == + the cal-marg lnL to ~1e-16. +- ILE `--calibration-proposal-breadcrumb `: seed the run's cal realizations from the + proposal (+ thread `cal_log_weights` to all likelihood call sites & resample). This is + what **wide_{N+1}** uses. +- ILE `--calibration-dump-responsibilities ` (+ `--calibration-pilot-extrinsic`): + the **pilot**. Keeps the cal node draws; at each analyzed (harvested) intrinsic point, + evaluates `return_cal_components` over a uniform-prior extrinsic batch and accumulates + `int dOmega L_c` per realization; writes `(nodes, log_resp=log_w+log int L_c, prior...)`. + If `--calibration-proposal-breadcrumb` is ALSO given, the pilot draws FROM that proposal + (refinement: pilot_N seeded by consolidation_{N-1}) and folds `log_w` into `log_resp`. +- `bin/util_CalPilotFit.py`: pool dumps -> `adaptive.fit_proposal` (AUTO-TEMPERED: pick the + largest beta<=1 whose tempered neff >= `target_neff_frac*n_cal`, so a low-neff cold draw + cannot collapse the proposal) -> breadcrumb. +- `bin/util_CalConsolidate.py`: precision-weighted combine of pilot breadcrumbs (or a + single-input pass-through) -> the consolidated proposal that seeds wide_{N+1}. + +The across-DAG-iteration loop (pilot_N seeded by consolidation_{N-1}, refit, ...) is +exactly `adaptive.adaptive_cal` UNROLLED over RIFT iterations -- no extra serial cost. + +## Convergence characterization (measured) + +The cal node space is high-dimensional (2 * spline_count * n_det; e.g. 60 for 10 nodes x +3 IFOs). A single Gaussian proposal learned from one prior shot in this space converges +SLOWLY when the cal posterior is strongly displaced/narrowed vs the prior: in a stress +test (12 of 60 nodes offset 1 sigma, tightened to 0.5 sigma) the responsibility neff sits +~1-3 and `|mean-true|` only falls to ~0.5 sigma over many rounds -- and the reference +`adaptive.adaptive_cal` behaves the SAME (this is intrinsic to broad-prior importance +sampling in high-D, not a wiring defect). Two things make this acceptable: +1. **Correctness is independent of pilot quality.** The Phase-0 importance weights make + the marginalization UNBIASED for any proposal; a poor pilot only lowers `neff_cal`. +2. **Real cal is boring.** Posteriors are small, smooth, near-prior displacements; in a + benign regime (offset ~0.3 sigma) the prior is already a decent proposal and the pilot + gives a modest neff gain. The big wins are when cal is genuinely informative, where + the across-iteration climb accumulates. +For a sharp high-D posterior the right long-term tool is **B (normalizing flow)** behind +the same breadcrumb interface -- a single Gaussian is the deliberate first cut. diff --git a/MonteCarloMarginalizeCode/Code/RIFT/calmarg/DESIGN_calmarg_in_loop.md b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/DESIGN_calmarg_in_loop.md new file mode 100644 index 000000000..3f531a04a --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/DESIGN_calmarg_in_loop.md @@ -0,0 +1,307 @@ +# In-loop calibration marginalization in RIFT ILE + +Branch: `rift_O4d_junior_calmarg_in_loop` (off `rift_O4d_junior_distance`) + +## Motivation + +RIFT currently marginalizes over calibration uncertainty in **postprocessing** +(`bin/calibration_reweighting.py`, bilby-based): after `extrinsic_posterior_samples.dat` +is produced, each sample is reweighted against a set of random calibration draws. +The extrinsic samples entering this step are *not* informed by calibration, so for +high-SNR sources and/or broad calibration priors the reweighting is very inefficient +(most proposed samples get tiny weights). + +Modern GPUs are heavily under-utilized by RIFT's inner loop, so we move the +calibration marginalization **inside ILE**, marginalizing over calibration draws +on-board while the extrinsic likelihood is being evaluated. + +## Key idea: apply calibration to the *data* + +The factored likelihood evaluated by +`factored_likelihood.DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop` +combines two quantities: + +* `kappa_sq` — the **data-template** term, built from the GPU Q-product over the + precomputed rholm timeseries `rholmsArrayDict[det] = (t)`. This is the + **only** data-dependent quantity. +* `rho_sq` — the **template-template** cross terms `U,V` (`ctUArrayDict`, + `ctVArrayDict`), ``. These depend on the template and PSD but **not** + on the data. + +If calibration `C(f)` is applied to the **data** (`d -> C(f)·d`), then `rho_sq` is +**calibration-independent** and is computed once; only `kappa_sq` changes per +realization. This is what makes in-loop marginalization cheap. + +> **Convention note for review.** Bilby's `GravitationalWaveTransient` applies the +> calibration factor to the *template/response*, which also rescales the `` +> norm. Applying to the data (our choice) and applying to the template agree to +> first order in the calibration amplitude but differ at second order. The +> apply-to-data choice is what preserves the efficiency win (shared `U,V`). The +> backtest below quantifies the difference against `calibration_reweighting.py`. + +## Data layout + +`RIFT/calmarg/generate_realizations.py::create_realizations` draws `n_cal` complex, +two-sided calibration factors on the full FFT frequency grid (matching +`lalsimutils` packing) from a bilby envelope `.txt` file, shape `(npts_seg, n_cal)`. +Column `c` across detectors is one **joint** draw. + +`ComputeModeIPTimeSeries` (cal branch) applies realization `c` to the data and +concatenates the resulting windowed rholm into one timeseries: + +``` +rholm[det] = [ block_0 | block_1 | ... | block_{n_cal-1} ] length = N_window * n_cal +``` + +`PackLikelihoodDataStructuresAsArrays` carries this long array through unchanged, +so `rholmsArrayDict[det]` has shape `(n_lms, N_window * n_cal)`. Realization `c` is +selected simply by shifting the per-sample window offset: + +``` +ifirst_c = ifirst + c * N_window +``` + +## Marginalization (implemented: Option B) + +We Monte-Carlo marginalize over the `n_cal` draws: + +``` +Z_cal(theta) = (1/n_cal) * sum_c integral dt exp( lnL_t(theta, c) ) +``` + +`DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop` gains an `n_cal` argument. +With `n_cal == 1` the code path is byte-for-byte the original. With `n_cal > 1`: + +1. `rho_sq` is accumulated once in the detector loop (calibration-independent). +2. The per-detector Q-product inputs (`Q`, `FY_conj`, `ifirst`, `N_window`) are cached. +3. For each realization `c`, `kappa` is recomputed via the **existing** GPU kernel + (`Q_inner_product.Q_inner_product_cupy`) with `ifirst + c*N_window`, combined with + the shared `rho_sq` through the same `loglikelihood` callback (distance/phase marg), + and accumulated with a **streaming log-sum-exp** for numerical stability. +4. Finish with `simps` over time and `- log(n_cal)`. + +**Why Option B (cal loop) over the alternatives:** + +| Option | Idea | Memory | Kernel | Review cost | +|---|---|---|---|---| +| A | replicate extrinsic batch ×n_cal, one kernel call | ×n_cal (forces smaller batch) | reused verbatim | lowest LOC | +| **B (chosen)** | loop realizations, reuse kernel, stream log-sum-exp | **unchanged** | reused verbatim, n_cal launches | low | +| C | fused CUDA kernel: Q + loglikelihood + cal-LSE on-board | minimal | new kernel | highest | + +Option B is memory-neutral and reuses the validated kernel — the right +minimum-violence first step given GPUs have spare throughput. + +### Option C (implemented for the default helper) + +`cal_method='fused'` runs a single fused CUDA kernel +(`RIFT/likelihood/cuda_Q_fused_calmarg.cu`, wrapped by +`RIFT/likelihood/Q_fused_calmarg.py`). One thread per extrinsic sample loops over +realizations × time × detectors × modes, forms the data term `kappa`, applies the +default factored-likelihood helper `lnL_t = invDist*Re(kappa) - 0.5*rho_sq`, and +accumulates a streaming, Simpson-weighted log-sum-exp over `(c,t)` — returning +`lnL[j]` directly. No `(batch, n_cal, npts)` intermediate, no per-realization +Python launches. + +Time integration matches Option B exactly by passing the composite-Simpson weight +vector `w_t = simps(I, dx=deltaT)` (simps is linear, so its action is a fixed weight +vector) into the kernel. `rho_sq` is calibration-independent and passed in +pre-summed over detectors. + +**Validated** in the harness vs the brute-force reference and Option B to ~1e-15 on +GPU, single- and multi-detector (H1,L1,V1 — exercises the kernel's detector loop and +the per-detector ifirst stacking). Throughput (NVS 510, sm_30; single synthetic +detector): + +| case | reference | Option B | Option C | +|---|---|---|---| +| n_cal=100, 1024 samples | 695 ms | 170 ms | **22 ms** | +| n_cal=200, 8192 samples | 7080 ms | 2422 ms | **279 ms** | + +i.e. ~8–9× over Option B and ~25–32× over brute force, with bit-level agreement. + +### Option C, stage 2 — distance marginalization (implemented, separate kernel) + +The dominant production path uses the distance-marginalization `loglikelihood` +(sites 1828/1871). This is implemented as a **separate** kernel +(`RIFT/likelihood/cuda_Q_fused_calmarg_distmarg.cu`, wrapper +`Q_fused_calmarg_distmarg_cupy`), kept apart from the default-helper kernel on +purpose: it keeps each kernel's review surface small, leaves the simpler kernel as a +baseline, and leaves `cal_method='loop'` (Option B) as a full fallback for distmarg +on both CPU and GPU. + +It reproduces `distmarg_loglikelihood` exactly on-board: +`x0 = kappa/rho_sq`; `s = asinh(√bmax·(x0−xmin)) − asinh(√bmax·(xmax−x0))`; +`t = asinh(rho_sq/bref)`; bilinear interpolation of `lnI_array` at `(s,t)` (matching +`EvenBivariateLinearInterpolator`, with the same in-bounds mask, contributing 0 +otherwise); plus `exponent_max`. Selected via `cal_method='fused'` **and** passing a +`cal_distmarg` table dict (`lnI_array`, `s0/ds/smin/smax`, `t0/dt/tmax`, +`xmin/xmax/sqrt_bmax/bref`); with `cal_distmarg=None` the default-helper kernel is +used. + +**Validated** in the harness (`--loglikelihood distmarg`, which builds a +self-consistent table and the mirror Python closure for reference/Option B) to +~1e-14 vs the brute-force reference, single- and multi-detector (the asinh/bilinear +differ from numpy only at ULP level). Throughput (sm_30): + +| case (distmarg) | reference | Option B | Option C | +|---|---|---|---| +| n_cal=100, 1024 samp, 2 det | 1364 ms | 495 ms | **77 ms** | +| n_cal=200, 2048 samp, 3 det | 6136 ms | 2358 ms | **333 ms** | + +i.e. ~6–7× over Option B. + +**Scope / limitations of both fused kernels** (raise `NotImplementedError` +otherwise): GPU only; `phase_marginalization=False`; all detectors share +modes/length (true after global mode pruning). + +### Driver wiring (opt-in) + +`integrate_likelihood_extrinsic_batchmode` exposes the fused path behind +`--calibration-fused-kernel` (off by default). When set (and on GPU, with +calibration marginalization active), the driver packages the distance-marginalization +`lookup_table` (`s_array`, `t_array`, `lnI_array`, `bmax`, `bref`) plus `xmin/xmax` +into a `cal_distmarg` dict and passes `cal_method='fused'` at the **non-phase-marg** +distmarg call site. The phase-marg distmarg site and everything else stay on +`cal_method='loop'` (Option B), which remains the default and the fallback for all +cases. On CPU the flag is ignored with a warning (the kernel is GPU-only). + +**End-to-end status.** Run through `integrate_likelihood_extrinsic_batchmode` on the +CI fake data with `--distance-marginalization` + a real `util_InitMargTable` table + +`--calibration-envelope-directory --calibration-fused-kernel`. This caught a real +wiring bug — in the distmarg path `P.dist` is fixed at the fiducial, so `invDistMpc` +is a scalar, but the fused kernel wants one value per extrinsic sample; the fused +branch now broadcasts it to `(npts_extrinsic,)`. After the fix the fused path runs to +completion. Numerics were validated deterministically with +`backtest_calmarg.py --loglikelihood distmarg --real-table `: fused == reference +== loop to ~2e-14 on the production table. (A full *sampler* end-to-end numerical +comparison needs a larger GPU than the local 2 GB card, which OOMs / returns nan under +load.) + +Remaining: a full numerical end-to-end on a larger GPU; phase-marginalization support +in the fused kernels (then the phase-marg distmarg site can opt in too). + +## Driver wiring + +`bin/integrate_likelihood_extrinsic_batchmode` already had the scaffolding: + +* options `--calibration-envelope-directory`, `--calibration-n-realizations`, + `--calibration-spline-count`; +* builds `calibration_realization_dict` and passes it into `PrecomputeLikelihoodTerms`. + +This branch adds `n_cal_for_likelihood` (= `--calibration-n-realizations` when +calibration marginalization is active, else 1) and threads it into the three +production `DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop` call sites +(plain / distance-marg / distance+phase-marg). + +## Bug fixed + +In `ComputeModeIPTimeSeries`'s calibration branch the inner product was being taken +against the *original* `data` instead of the calibration-modified `data_now` +(`IP.ip(hlms[pair], data)` → `IP.ip(hlms[pair], data_now)`), so the calibration +factor was previously never applied. + +## Validation + +`RIFT/calmarg/test_calmarg_reduction.py` builds a synthetic 2-mode, single-detector +case and checks the `n_cal>1` result against a brute-force reference — running the +unchanged `n_cal==1` path on each realization block separately and combining by hand +(`logsumexp_c(lnL_c) - log n_cal`). Agreement is machine precision (~1e-15) on both +the CPU (`xpy=np`) and the production GPU (`xpy=cupy`, real `Q_inner_product` kernel) +paths. It also confirms the `n_cal==1` path is a regression-identical block-0 eval. + +## Backtest harness + +`RIFT/calmarg/backtest_calmarg.py` is the rig **Option C is developed against**. It +holds a `METHODS` registry — `reference` (brute-force per-block + logsumexp), +`in_loop_B` (the `n_cal>1` call), and `in_loop_C` (a stub raising `NotImplementedError` +until the fused kernel exists) — and evaluates each over synthetic inputs that +exercise the cal-block structure, reporting `max|lnL - reference|` and best-of-N +timing on CPU (`--backend cpu`) or GPU (`--backend gpu`). Wire the fused kernel into +`method_in_loop_C` and the harness validates it automatically. + +``` +python -m RIFT.calmarg.backtest_calmarg --backend gpu --n-cal 100 --npts-extrinsic 4096 --repeat 5 +``` + +Current status: `in_loop_B` reproduces `reference` to ~1e-15 on CPU and GPU, with and +without phase marginalization; on GPU it is ~3–4× faster than the brute-force +reference (which redundantly recomputes `rho_sq` per realization — exactly the +redundancy Option C removes). + +`run_physics_backtest()` in the same module is the **scaffold** (docstring + TODOs) for +the heavier real-data comparison vs bilby `calibration_reweighting.py`: load a real +ILE precompute + cal envelopes + the bilby data_dump, evaluate the in-loop calmarg +likelihood on the same extrinsic samples, and compare per-sample lnL and the +log-evidence shift. It needs frames/PSDs, so it runs on the stable host (not in CI). + +## Calibration MC error budget (implemented) + +The sampler's reported variance is the *extrinsic* sampling variance with the cal +draw set held fixed — it is structurally blind to the Monte-Carlo error of the +`(1/n_cal) sum_c` average. Empirically (demo `pp-run`, wide envelopes, `NCAL_DAG=20`) +this produced a 2d lnL surface with ~1.0 point-to-point noise quoted at sigma~0.18 +(chi^2/dof ~ 34 against a smooth surface fit). + +`adaptive.cal_mc_error_from_components(comp, cal_log_weights)` computes, from the +per-realization components on a modest extrinsic-prior batch (`return_cal_components`, +responsibilities are ~extrinsic-independent — same trick as the pilot): + +* `a_c = w_c Z_c / (n_cal Z)` — normalized per-draw contributions (sum to 1); +* `Var(lnZ) ~= n_cal * Var_c(a_c)` (delta method; reproduces the lognormal + `(e^{sigma^2}-1)/n_cal`, validated in `test_cal_mc_error.py`); +* `neff_cal = 1/sum a_c^2` — when `< 10` the estimate is a LOWER BOUND and the + point is flagged in the log. + +The driver folds this in quadrature into the reported sigma column and prints +`[calmarg error] sigma_lnZ: extrinsic X (+) cal Y -> total Z ; cal n_eff ...`. +The probe (`_cal_error_probe`) uses an ADAPTIVE extrinsic batch (doubling until the +estimate stabilizes, capped by `--calibration-mc-error-extrinsic`, default 8192, +0 disables) and draws distance from the RUN'S distance prior: the sampler's own +`prior_pdf['distance']` when distance is sampled (uniform proposal + importance +weight, so the cosmo/redshift variants are handled by construction), the `--d-prior` +pdf when distance marginalization is active, or the PINNED value (warned: at fixed +distance the distance/amplitude degeneracy cannot absorb amplitude-like cal +perturbations, so the estimate is conservative). + +**Adaptive draw count** (`--calibration-neff-cal-target`, default 10; +`--calibration-n-realizations-max`, default 8x initial): after the cal-block +precompute, the same probe measures `neff_cal` at this intrinsic point; while below +target the draw set is DOUBLED — fresh independent draws appended via +`_draw_more_calibration_draws` (extends the realization dict, importance weights, +and node bookkeeping in place), with an incremental `PrecomputeLikelihoodTerms` of +only the new blocks concatenated onto the packed rholm arrays. So +`--calibration-n-realizations` is a *starting* size, not a trusted constant. +`[calmarg adapt]` log lines record the escalation. + +**Sizing guidance** (toy-model scaling, see the paper repo +`demos/calmarg/cal_envelope_scaling.py`): per-draw spread `sigma_lnL ~ rho^2 eps_A` +(amplitude-envelope dominated, ~1.0 per 1% amplitude at network SNR 20), and +`n_cal ~ (e^{sigma_lnL^2}-1)/sigma_target^2`. GWTC-4-scale envelopes (<~2% / <~2 deg) +need `n_cal ~ 100-1000` at SNR 20: **start at 100 and let the adaptive escalation +work; 300 is a comfortable fixed choice**. Beyond ~3% amplitude (or proportionally +higher SNR) prior draws are hopeless; the learned-proposal machinery (pilot / +breadcrumbs) targets that regime but is EXPERIMENTAL — it must be validated against +the brute-force path before being relied on, and is deliberately kept out of the +active/default paths. Memory: realization blocks add ~0.3 MB/draw GPU-resident in +the demo config (88 MB at n_cal=300); per-eval cost is linear in n_cal (fused +kernel: ~0.25 s per 1000-sample chunk at n_cal=300 extrapolating the sm_30 timings +above). + +## Open items / future work + +* **Option C** fused kernel for maximum throughput; backtest vs Option B and vs the + bilby postprocessor on a high-SNR / broad-prior event. +* **Reproducibility:** `create_realizations` uses unseeded `np.random`; add a + `--calibration-seed` so a run's draw set is reproducible. DECISION (2026-06): + workers must KEEP drawing independent sets — common random numbers across + intrinsic points were considered and rejected (a shared draw set makes the lnL + surface artificially smooth and bakes its O(1/sqrt(n_eff_cal)) bias into the + posterior); the variance is instead disclosed via the cal MC error budget above + and beaten down with larger n_cal (now grown adaptively per point). +* **Calibration-parameter export:** Option B does not record which realization was + selected (acceptable per scope — parameter draws can be regenerated at the end as + the current `--dump_cal_realization` path does). +* **Grid sanity asserts:** verify `len(realizations) == data.length` and + `N_window*n_cal == rholm length` explicitly at setup time. +* **CPU + phase-marg + calmarg** uses an explicit einsum mirroring the kernel; covered + by the test for the non-phase-marg case. diff --git a/MonteCarloMarginalizeCode/Code/RIFT/calmarg/DESIGN_extrinsic_handoff.md b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/DESIGN_extrinsic_handoff.md new file mode 100644 index 000000000..8fd4f1b04 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/DESIGN_extrinsic_handoff.md @@ -0,0 +1,285 @@ +# Extrinsic handoff: carry the extrinsic posterior between iterations + +Status: **proof-of-concept implemented (GMM)**; AV partial-reset is future work (task #30). + +## The decade-old goal + +RIFT re-solves the *same* extrinsic integral (sky, distance, inclination, polarization, +orbital phase, time) on every intrinsic-grid point, every iteration. But the extrinsic +posterior is set by the data + best-fit template, not by the small intrinsic-grid moves -- +so it barely changes from iteration to iteration. Today each ILE job starts its extrinsic +sampler **cold** (a wide prior proposal) and re-discovers the same sky modes / distance +blob from scratch. This is the long-standing "save the extrinsic distribution to inform +the next iteration" idea: learn the extrinsic proposal once, hand it forward, and let the +next run start *on the answer*. + +This generalizes the calibration pilot's breadcrumb (`RIFT.calmarg.breadcrumbs`, +`RIFT.calmarg.generate_realizations.seed_realizations_from_breadcrumb`): the cal handoff +carries a Gaussian over spline-node parameters; the extrinsic handoff carries a learned +proposal over the extrinsic parameters in the SAME breadcrumb file. + +## GMM-first (implemented) + +RIFT's ensemble sampler (`mcsamplerEnsemble`) is **already seedable**. Its `gmm_dict` +maps parameter GROUPS -- tuples of indices into `params_ordered` -- to a fitted +`gaussian_mixture_model.gmm`; a non-`None` entry is used as the starting proposal and keeps +adapting. The standard groups (set in `analyze_event` ~line 1410) are: + + (right_ascension, declination) # sky + (distance, inclination) # distance/orientation + (phi_orb, psi) # phase/polarization + +So the GMM is the trivially-seedable model to prove the handoff with -- no new sampler +machinery, just pre-fill `gmm_dict`. A normalizing flow (or a seedable AV, below) can drop +in later behind the same fit/seed interface (`extrinsic['kind'] != 'gmm'`). + +### Pieces + +- **`RIFT.calmarg.extrinsic_handoff`** + - `fit_extrinsic_proposal(samples, log_weights, groups=STANDARD_GROUPS, bounds, n_comp=4)` + -- per group, fit RIFT's OWN `gmm.fit` (the exact fitter the sampler uses in + `update_sampling_prior`), using importance weights `lnL + ln prior - ln sampling_prior`. + Returns the portable breadcrumb `extrinsic` dict (per-group means/covs/weights/bounds + + parameter NAMES). GMMs may run on cupy -- inputs are moved via + `model.identity_convert_togpu` before `.fit`. + - `reconstruct_gmm(group, adapt=True)` -- rebuild a `gmm` from a stored group (means/ + covariances/weights restored in the model's internal normalized frame; `adapt=True` + lets the seeded components keep adapting, since the extrinsics drift slightly). + - `gmm_dict_from_breadcrumb(extrinsic, params_ordered, adapt=True)` -- build the + `{dim_group_tuple: gmm}` to seed the next sampler. Dim-groups are looked up by + parameter NAME against this run's `params_ordered`, so the handoff is robust to a + different parameter ordering between runs; groups whose params aren't all present are + skipped silently. + + Using RIFT's own fitter means the stored means/covariances are in exactly the model's + internal (normalized) frame and restore to a byte-identical model -- no coordinate + guesswork. + +- **`RIFT.calmarg.breadcrumbs` (schema v2)** -- the `save`/`load` object gained an + `extrinsic` slot alongside the existing `cal` slot. A breadcrumb can carry cal, extrinsic, + or both. Per group it stores `params`/`means`/`covariances`/`weights`/`bounds`. Schema + is additive (v1 cal-only breadcrumbs still load); bump `SCHEMA_VERSION` on incompatible + changes. + +- **ILE wiring** (`integrate_likelihood_extrinsic_batchmode`, execute-point -- needs a + container rebuild): + - `--extrinsic-proposal-output PATH` -- after `sampler.integrate`, harvest the run's + extrinsic posterior samples + importance weights from `sampler._rvs` (same weight recipe + as the distance-grid export, including the GMM sampler's raw-integrand storage), fit per + group, and `breadcrumbs.save(PATH, extrinsic=...)`. Wrapped in try/except so a + harvest/fit failure can never break a production integration. + - `--extrinsic-proposal-breadcrumb PATH` -- before integration, load the breadcrumb and + pre-fill `gmm_dict` for the matched dim-groups (`gmm_adapt=True`). Missing/unreadable + breadcrumb -> warn and fall back to the cold default. + +### Proof of concept + +`python -m RIFT.calmarg.extrinsic_handoff` builds a synthetic **bimodal** sky posterior + +unimodal distance/inclination blob, fits it, round-trips through a breadcrumb, seeds a fresh +GMM against a *shuffled* `params_ordered`, and confirms the seeded sky GMM reproduces BOTH +sky modes with ~the right mode fractions. `python -m RIFT.calmarg.breadcrumbs` confirms the +cal-Gaussian + extrinsic-GMM coexist and round-trip. Both PASS. + +## Pipeline wiring (implemented) + +The handoff is wired end-to-end through the pipeline, gated by `--extrinsic-handoff` and +**standalone** (it does NOT require the cal pilot -- it works on a plain fused / vanilla run): + +- **`util_RIFT_pseudo_pipe.py --extrinsic-handoff`** adds to `args_ile.txt`: + - `--extrinsic-proposal-output extr_proposal_$(macroiteration)_$(macroevent).npz` -- each + wide ILE job writes its own per-event proposal ($(macroevent) is the per-node macro); + - `--extrinsic-proposal-breadcrumb .../extr_consolidated_$(macroiterationprev).npz` -- the + seed from the previous iteration (OSG: basename + auto-added to the ILE transfer list + + an `extr_consolidated_-1.npz` placeholder for iteration 0; shared FS: absolute path). + It warns if `--ile-sampler-method` is not GMM (the seed is a no-op for other samplers). + +- **`util_ExtrinsicConsolidate.py`** (new) picks the single most representative per-event + proposal (default by lnL -- nearest the peak; `--select neff|n_samples` also available) and + writes `extr_consolidated_.npz`. It ALWAYS writes output (empty if nothing valid), so + the next iteration's seed/transfer never fails; unreadable/placeholder inputs are skipped. + +- **`dag_utils_generic.write_extrconsolidate_sub`** builds the consolidation job in the + **local universe** on the submit node: it is pure-python file selection (no GPU/ILE/ + container/frames), and on OSG the per-event ILE outputs are transferred back to + `/iteration__ile` (ILE's default output transfer), so a local-universe job reads + them from the shared FS with no per-event input transfer (which condor cannot glob). + +- **`create_event_parameter_pipeline_BasicIteration`** creates one consolidation node per + iteration, gated behind that iteration's `unify` node (all ILE done -> per-event proposals + present), and makes iteration N+1's wide ILE jobs depend on the iteration-N consolidation: + unify_{it} -> EXTRCONSOLIDATE_{it} -> wide ILE_{it+1} + (the consolidate barrier and the seed barrier), exactly mirroring the cal-pilot wiring. + +`make extr-build` (demo/rift/calmarg) builds a pipeline with `--extrinsic-handoff +--ile-sampler-method GMM` and validates the whole thread offline (args_ile.txt flags, +EXTRCONSOLIDATE.sub, and the unify->consolidate->next-ILE DAG edges). + +Because cal and extrinsic live in ONE breadcrumb object, a future refinement could ride the +extrinsic proposal on the cal pilot's existing consolidation/transfer instead of a separate +node; the standalone path was chosen first so the handoff works without the (heavier) cal +pilot. The convergence-subdag extension (`--first-iteration-jumpstart`) does not yet carry +`--extrinsic-handoff` -- same limitation as `--calmarg-pilot`. + +## Real-GPU validation (cardassia, NVS 510) and what it taught us + +Ran the full loop interactively on one intrinsic point, GMM sampler + calmarg-fused, on the +CI data: iteration-0 writes `extr_proposal_0_0.npz` -> `util_ExtrinsicConsolidate` picks it +-> iteration-1 ILE loads it and prints `Extrinsic GMM SEEDED ... for dim-groups +[(4,5),(3,2),(0,1)]` (all three standard groups) -> integrates -> writes +`extr_proposal_1_0.npz`. End-to-end the plumbing works on real hardware. Two bugs only the +GPU run surfaced, now fixed: + +1. **bounds left on the host.** `reconstruct_gmm` set means/covs/weights onto the GPU but + left `self.bounds` as numpy. The sampler's `score()`/`_normalize` write into an + `xpy.empty` (cupy) array, so a numpy `self.bounds` raised + `ValueError: non-scalar numpy.ndarray cannot be used for fill`. Fix: `model.bounds = + identity_convert_togpu(bounds)`. +2. **within-group parameter ORDER.** The sampler keys the phase/pol group as + `(psi, phi_orb)=(0,1)` but the breadcrumb stored `(phi_orb, psi)=(1,0)`, so that seed was + silently dropped (key mismatch). Fix: `gmm_dict_from_breadcrumb(existing_keys=...)` matches + each breadcrumb group to the sampler's actual gmm_dict key by dim-SET and permutes the + stored means/covariances/bounds columns into that key's order. + +**Seed quality depends on the SOURCE iteration's convergence.** When the ensemble sampler +hits a bad batch it calls `_reset()`, which sets every `gmm_dict[k]=None` -- i.e. it +**discards the seed and continues cold**. This is the correct safety net: a bad seed is +thrown away, never corrupting the result. In a deliberately tiny smoke (`--n-max 40000` on +the NVS 510 -> iteration-0 `n_eff ~ 1`), the iteration-0 proposal is near-degenerate, so the +seeded first batch produces zero/NaN effective weights and the sampler resets to cold. The +handoff is then correct-but-cosmetic. To see the seed actually ACCELERATE convergence you +need a source iteration that converged reasonably (`n_eff` in the hundreds) -- i.e. a real +`--n-max` (millions) and/or a larger GPU. A modest `cov_inflate` (default 2.0, ~1.4x width) +broadens the seed so the sampler can contract it -- good practice for a warm start, but it +mitigates rather than rescues a genuinely degenerate source. + +## Measured blocker: the GMM sampler does not converge on real sharp ILE peaks + +Trying to demonstrate the seed ACCELERATING convergence on the CI point (network SNR ~17.5, +lnLmax ~ 90-115) surfaced a hard limit of the *seedable* sampler itself, independent of the +handoff and of calibration: + +| config (single CI point, GMM sampler) | n_eff at ~200k samples | +|----------------------------------------------|------------------------| +| GMM + calmarg (n_cal=20) | ~1.0 (256k) | +| GMM, vanilla (no calmarg) | 1.00007 (196k, 50 it) | + +The ensemble (GMM) sampler collapses its mixture onto the single dominant sample at a sharp, +high-SNR peak and then stops improving -- n_eff is pinned at 1 with or without calmarg. +(The AV sampler, by contrast, reached n_eff in the hundreds at a few x10^6 samples in the +earlier calmarg tune runs -- AV's adaptive tessellation handles these peaks; GMM does not.) + +Consequence for the handoff: the GMM->GMM extrinsic handoff is correct and safe, but on real +high-SNR ILE likelihoods the GMM SOURCE iteration never converges to a good proposal, so there +is nothing useful to hand off, and the cold GMM baseline is equally stuck -- there is no +acceleration to measure. The handoff's value is therefore gated on a *seedable sampler that +actually converges*: + - **seedable / partial-reset AV (task #30, #25)** -- the real unlock: AV converges on these + peaks but resets every integrate() and has no seed path. This is now the critical-path + item for making the extrinsic handoff pay off on production data. + - or a **cross-sampler handoff**: converge with AV, fit the GMM to AV's posterior samples + (fit_extrinsic_proposal already does exactly this from any sampler's weighted samples), + and seed a GMM/flow refinement. The save side already accepts arbitrary samples+weights; + only the "harvest AV's _rvs and fit" wiring would be new. + +The handoff plumbing (save -> consolidate -> seed, all groups, GPU-correct) is done and is the +right substrate; the demonstration of speed-up waits on one of the above. + +## Seed adaptation: FREEZE by default (`--extrinsic-proposal-adapt`) + +Re-fitting a seeded GMM group on the first batch is fragile on these likelihoods: with +`adapt=True` the sampler's `_train` calls the GMM fit, whose `_initialize` does +`random.choice(p=weights)` and dies on the pathological first-batch weights +("probabilities are not non-negative") -> `_reset()` -> the seed is discarded. `_train` +already skips groups whose `gmm_adapt[group]` is False, so the ILE seed path now FREEZES the +seeded groups by default (`gmm_adapt=False`); `--extrinsic-proposal-adapt` opts back into +adaptation. Freezing is also the right semantics for a handed-off (especially cross-sampler) +proposal: trust it as-is rather than let GMM's adaptation degrade it. Result: with freeze the +seeded run completes with **0 resets** and the seed actually drives sampling. + +## Cross-sampler AV->GMM seed: partial result, integral still wrong (open) + +Per the chosen plan, converged iteration-0 with **AV** (which does make progress on this +point: n_eff ~7 at 400k, lnLmax ~143), fit the GMM to AV's posterior samples +(`fit_extrinsic_proposal` reads any sampler's `_rvs`), consolidated, and seeded a **frozen** +GMM run: + +- the seed lands cleanly (all 3 groups), **0 resets**, and n_eff rises from the cold ~1 to + **~5-10** -- the seed mechanism is injecting structure. +- BUT the seeded GMM's INTEGRAL is wrong: `sqrt(2 lnLmax)` prints `nan` and Z comes out + ~1e-4 (vs the cold GMM's valid ~1e43 and AV's lnLmax~143). High n_eff in the WRONG region + is worse than honest low n_eff: the frozen proposal is importance-sampling a region that is + consistent-but-displaced from the true posterior. + +Two suspects, not yet isolated (needs a focused audit, no more blind GPU time): +1. **coordinate convention** -- AV vs GMM may store extrinsic samples in `_rvs` under + different conventions (e.g. angle vs cosine for inclination/declination; the sampler adds + `inclination`/`declination` on `[-1,1]` = cosine when `--*-cosine-sampler` is set, but it + is not obvious AV's `_rvs` uses the same). A mismatch would place the fitted GMM in the + wrong frame. Same-sampler GMM->GMM has no such mismatch and round-trips cleanly. +2. **`cov_inflate` out of bounds** -- inflating the seed covariance (x2) can push a sampled + `distance` outside `[1,1000]` (or other hard edges) where the likelihood returns NaN, + contaminating lnLmax. Worth testing `cov_inflate=1` and clipping proposed samples. + +Net: the handoff machinery, the freeze, and the AV-source convergence all work; the +cross-sampler numeric correctness is one debugging session away (audit `_rvs` conventions + +inflation/bounds). The same-sampler GMM->GMM path is already numerically clean -- it just +needs a sampler that converges as a source, i.e. seedable AV (below). + +## Cross-sampler AV->GMM: numerics RESOLVED; benefit gated by GMM convergence + +Debugging the wrong-integral above (per user's steer) found and fixed FOUR real issues in the +save/seed path; the cross-sampler seed is now numerically correct: + +1. **tempered weights (save side).** The GPU/AV sampler (mcsamplerGPU) stores + `_rvs['log_weights'] = tempering_exp*lnL + ln(prior) - ln(s_prior)` -- the adapt-weight- + exponent (e.g. 0.1) baked in. Fitting the GMM to those flattened weights displaces the + proposal. Fix: build the weight from the raw, UNTEMPERED components + (`log_integrand + log_joint_prior - log_joint_s_prior`) and prefer them over `log_weights`. + (GMM's own `_rvs` has no tempering -> GMM->GMM was already fine.) This alone took the + seeded n_eff from ~5 to ~26. +2. **cov_inflate.** Inflating a FROZEN seed only widens it out of bounds; default is now 1.0 + (freeze handles robustness; inflation was for the adapt=True path). +3. **starved fit -> NaN component.** A low-ESS source over-parameterized (n_comp=4 vs few + effective samples) collapses a mixture component to a singular/NaN covariance, and one NaN + component poisons the whole seeded proposal. Fix: cap n_comp by the weight ESS + (`k <= ESS/(d+2)`) and drop any non-finite component (renormalize; skip the group if none + survive). +4. **distance sampled against a hard bound.** The real source of the persistent `nan` lnLmax: + with distance SAMPLED on `[1,1000]`, a seeded distance Gaussian spills past the bound -> + NaN likelihood. Distance marginalization (`--distance-marginalization` + a lookup table + from `util_InitMargTable`) removes distance from the extrinsic sampler entirely; with it on, + the seeded run's lnLmax is finite and the integral is valid. Distmarg is OPTIONAL with the + fused kernel, not required -- the fused kernel has both a non-distmarg kernel + (`Q_fused_calmarg_cupy`) and a distmarg kernel (`Q_fused_calmarg_distmarg_cupy`), and the + ILE binary wires whichever applies. In the pipeline it is `--internal-marginalize-distance` + (which composes cleanly with `--calmarg-fused-kernel`); in the demo it is the `PP_DMARG=1` + toggle. RECOMMENDED with `--extrinsic-handoff` precisely because it removes the distance + dimension + its hard bound from the seeded GMM proposal. + +Measured, distmarg on, single CI point (SNR~17.5), all fixes in: +- AV source converges to n_eff~4.7 (lnLmax~152), writes a clean 2-group (sky, phase/pol) proposal. +- seeded GMM: 0 resets, FINITE lnLmax, VALID integral -- but n_eff ~1.0, ~the same as the + cold GMM (~1.0-1.3). The seed neither helps nor hurts. + +**Conclusion.** The handoff (save -> consolidate -> seed) is now numerically correct and safe +end-to-end on real GPU data. But it does not ACCELERATE on this point because the seedable +sampler (GMM) does not converge here (n_eff~1 cold AND seeded), and the AV source (n_eff~5) is +too under-converged to provide a strongly-informative seed. GMM is seedable but weak; AV +converges but is not seedable. This is now hard evidence that the payoff requires a +**seedable / partial-reset AV (task #30, #25)** -- or a converged source (lower SNR / much +larger sample budget / better GPU) so the GMM seed has real information to carry. The numeric +substrate is done; the win is one of those two regimes away. + +## Why GMM first, and the AV limitation (task #30) + +The adaptive Voronoi sampler (AV, `mcsampler`) is the default extrinsic sampler and is more +efficient, but it **completely resets** between `integrate()` calls -- there is no seed path, +and re-seeding is dangerous because AV can only *contract* its boundaries, never expand or +shift them. So a naive AV warm-start could lock the sampler onto a stale region. The GMM +(and portfolio) samplers reuse sampling models cleanly and are trivially seedable, so they +are the right vehicle for the first working handoff. + +Future work (task #30): a **seedable / partial-reset AV** -- reset only some parameters, or +seed a proposal that AV is allowed to *expand* from, so the more-efficient sampler can also +benefit from the handoff. The breadcrumb `kind` field already leaves room for a non-GMM +model behind the same `save`/`load`/seed interface. diff --git a/MonteCarloMarginalizeCode/Code/RIFT/calmarg/adaptive.py b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/adaptive.py new file mode 100644 index 000000000..eb502050f --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/adaptive.py @@ -0,0 +1,285 @@ +""" +Adaptive calibration sampling (Phase 1). + +Motivation +---------- +In-loop calibration marginalization draws cal realizations from the PRIOR. As SNR +grows the calibration parameters become measurable, so the cal posterior pulls away +from the prior and almost all prior draws land in low-likelihood cal regions -- the +effective number of cal samples collapses. This module learns a unimodal Gaussian +PROPOSAL in cal spline-node space and uses importance weighting (w_c = prior/proposal) +so the marginalized result stays unbiased while the sampling efficiency recovers. + +Tempering +--------- +The per-realization responsibilities (log integral contributions) have a very large +dynamic range at high SNR -- a naive Gaussian fit would be dominated by a single +sample. We fit with TEMPERED weights softmax(beta * log_resp), starting at small beta +(broad, many samples contribute) and ramping beta -> 1 as the proposal narrows onto the +cal posterior. The importance weights used for the *marginalization itself* are always +the full (untempered) w_c = prior/proposal; tempering only shapes the proposal fit. + +This module is backend-agnostic numpy and has no GPU/lal dependency for the learning +machinery itself (it consumes an `evaluate` callback that runs the actual likelihood). +The cal-factor construction reuses the spline convention in generate_realizations. +""" +from __future__ import division + +import numpy as np +import scipy.interpolate +from scipy.special import logsumexp + +from RIFT.calmarg import generate_realizations as _gr + + +# --------------------------------------------------------------------------- +# Prior / node bookkeeping +# --------------------------------------------------------------------------- +def envelope_node_prior(fname, fmin, fmax, n_nodes): + """Per-node Gaussian prior (mean, sigma) for amplitude then phase nodes, and the + log10 spline node frequencies. Node vector layout: [amp_0..amp_{N-1}, ph_0..ph_{N-1}].""" + log_f = np.linspace(np.log10(fmin), np.log10(fmax), n_nodes) + dat_amp, dat_phase = _gr.retrieve_envelope_from_file(fname, frequency_array=10 ** log_f) + mean = np.concatenate([dat_amp[:, 1], dat_phase[:, 1]]) + sigma = np.concatenate([dat_amp[:, 2], dat_phase[:, 2]]) + sigma = np.where(sigma > 0, sigma, 1.0) # guard degenerate (delta) priors + return mean, sigma, log_f + + +def log_prior(nodes, prior_mean, prior_sigma): + """Independent-Gaussian prior log-pdf for each row of `nodes` (n_real, dim).""" + z = (nodes - prior_mean) / prior_sigma + return np.sum(-0.5 * z * z - np.log(prior_sigma * np.sqrt(2 * np.pi)), axis=1) + + +def _mvn_logpdf(nodes, mean, cov): + dim = mean.shape[0] + d = nodes - mean + L = np.linalg.cholesky(cov) + sol = np.linalg.solve(L, d.T).T # (n_real, dim) + quad = np.sum(sol * sol, axis=1) + logdet = 2.0 * np.sum(np.log(np.diag(L))) + return -0.5 * (quad + logdet + dim * np.log(2 * np.pi)) + + +# --------------------------------------------------------------------------- +# Cal-factor construction (spline; matches generate_realizations convention) +# --------------------------------------------------------------------------- +def nodes_to_cal_factors(amp_nodes, phase_nodes, log_f_nodes, T_segment, dT, fmin, fmax): + """Build two-sided complex calibration factors (npts_seg, n_real) from per-node + amplitude/phase values, on the lalsimutils FFT frequency packing. + + amp_nodes, phase_nodes : (n_real, n_nodes) + """ + n_real = amp_nodes.shape[0] + deltaF_seg = 1. / T_segment + npts_seg = int(T_segment / dT) + freq = deltaF_seg * np.array([npts_seg / 2 - k if k <= npts_seg / 2 else -k + npts_seg / 2 + for k in np.arange(npts_seg)]) + mask_in = (np.abs(freq) >= fmin) & (np.abs(freq) <= fmax) + mask_plus = mask_in & (freq > 0) + mask_minus = mask_in & (freq < 0) + lf_pos = np.log10(freq[mask_plus]) + lf_neg = np.log10(-freq[mask_minus]) + + out = np.ones((npts_seg, n_real), dtype=complex) + for i in range(n_real): + cs_a = scipy.interpolate.CubicSpline(log_f_nodes, amp_nodes[i]) + cs_p = scipy.interpolate.CubicSpline(log_f_nodes, phase_nodes[i]) + out[mask_plus, i] = cs_a(lf_pos) * np.exp(1j * cs_p(lf_pos)) + out[mask_minus, i] = cs_a(lf_neg) * np.exp(-1j * cs_p(lf_neg)) + return out + + +# --------------------------------------------------------------------------- +# Tempered proposal fit + diagnostics +# --------------------------------------------------------------------------- +def fit_proposal(nodes, log_resp, beta, cov_floor=1e-8, cov_inflate=1.0, + prior_sigma=None, shrink=None): + """Tempered weighted-Gaussian fit. Weights = softmax(beta * log_resp). + + beta in (0,1]: small -> broad (many samples), 1 -> full responsibility weighting. + + prior_sigma : if given (length-dim 1-sigma of the diagonal prior), SHRINK the fitted + covariance toward diag(prior_sigma**2). This is essential when the fit is starved + -- a weighted sample covariance from ~neff effective points cannot constrain the + dim*(dim+1)/2 entries of a dim-dimensional covariance (cal node space is ~60-D), + so the UNINFORMED directions otherwise collapse to ~0 variance. A near-zero + proposal variance is a near-delta: seeded draws are pinned and the importance + weights log(prior/proposal) blow up, producing the pathological seeded likelihoods + we saw. Shrinking keeps uninformed directions at ~prior width (log_w ~ 0 there). + shrink : explicit shrinkage weight rho in [0,1] toward the prior; default auto = + (dim+1)/(dim+1+neff), i.e. ~1 (all prior) when starved, ->0 (all data) when + neff >> dim. + + Returns (mean, cov).""" + lw = beta * log_resp + lw = lw - logsumexp(lw) + w = np.exp(lw) + mean = w @ nodes + d = nodes - mean + cov = (w[:, None] * d).T @ d + dim = mean.shape[0] + if prior_sigma is not None: + prior_sigma = np.asarray(prior_sigma, dtype=float) + neff = neff_from_logweights(beta * log_resp) + rho = shrink if shrink is not None else (dim + 1.0) / (dim + 1.0 + neff) + rho = float(min(max(rho, 0.0), 1.0)) + cov = (1.0 - rho) * cov_inflate * cov + rho * np.diag(prior_sigma ** 2) + else: + cov = cov_inflate * cov + cov = cov + cov_floor * np.eye(dim) + return mean, cov + + +def neff_from_logweights(log_w): + """Kish effective sample size from log-weights: (sum w)^2 / sum w^2.""" + return float(np.exp(2 * logsumexp(log_w) - logsumexp(2 * log_w))) + + +def cal_mc_error_from_components(comp, cal_log_weights=None, sample_log_weights=None): + """Calibration Monte-Carlo error budget for the cal-marginalized evidence. + + The in-loop marginalization estimates Z = E_c[ w_c Z_c ] over n_cal iid cal + draws, where Z_c = int dtheta p(theta) L(theta, c). The extrinsic sampler's + reported variance CANNOT see the spread over c (the draw set is held fixed for + the whole job), so this term must be estimated separately and added in + quadrature to the extrinsic sampling error. + + comp : (n_samples, n_cal) RAW per-realization time-integrated lnL at a batch of + extrinsic samples (``return_cal_components=True`` output). + cal_log_weights : (n_cal,) importance log-weights log(prior/proposal); + None = prior draws (uniform). + sample_log_weights : (n_samples,) posterior log-weights of the extrinsic batch. + For a batch drawn from the extrinsic PRIOR pass None: the marginal lnL of + each sample (logsumexp_c of comp+cal_log_weights) is then the correct + importance weight. + + Returns (sigma_lnZ_cal, neff_cal, a_c): + a_c : (n_cal,) normalized posterior contribution of realization c, + a_c = w_c Z_c / (n_cal Z); sums to 1. + sigma_lnZ_cal : delta-method standard error of lnZ from the cal MC average, + Var(lnZ) ~= n_cal * Var_c(a_c). (Lognormal cross-check: this + reproduces (exp(sigma_lnL^2)-1)/n_cal.) + neff_cal : Kish size 1 / sum_c a_c^2. When neff_cal is O(1) the + marginalization is dominated by a single draw and the error + estimate itself is a LOWER BOUND -- treat the point as unreliable. + """ + comp = np.atleast_2d(np.asarray(comp, dtype=float)) + n_samples, n_cal = comp.shape + logw = np.zeros(n_cal) if cal_log_weights is None else np.asarray(cal_log_weights, dtype=float) + lc = comp + logw[None, :] # log( w_c L_jc ) + lnL_marg = logsumexp(lc, axis=1) # per-sample log sum_c w_c L_jc (norm cancels) + log_r = lc - lnL_marg[:, None] # responsibilities r_jc, sum_c r_jc = 1 + if sample_log_weights is None: + slw = lnL_marg # prior-drawn batch -> weight by marginal L + else: + slw = np.asarray(sample_log_weights, dtype=float) + slw = slw - logsumexp(slw) # sum_j W_j = 1 + log_a = logsumexp(slw[:, None] + log_r, axis=0) # a_c = sum_j W_j r_jc + a_c = np.exp(log_a - logsumexp(log_a)) # exact renormalization + var_lnZ = n_cal * np.var(a_c, ddof=1) if n_cal > 1 else 0.0 + neff_cal = 1.0 / np.sum(a_c ** 2) + return float(np.sqrt(max(var_lnZ, 0.0))), float(neff_cal), a_c + + +# --------------------------------------------------------------------------- +# Adaptive loop +# --------------------------------------------------------------------------- +def adaptive_cal(evaluate, prior_mean, prior_sigma, n_nodes_amp, + n_real=200, n_iter=4, betas=None, rng=None, return_history=False): + """Run the adaptive cal-sampling loop. + + evaluate(nodes) -> log_L : callback returning, for each realization (row of + `nodes`), the extrinsic-marginalized log-likelihood log integral_theta + L(theta, cal(nodes_c)) -- NO prior, NO importance weight (the loop folds those + in). In practice `evaluate` builds the cal factors (nodes_to_cal_factors) and + runs the ILE integral per realization. + + The per-realization posterior responsibility (used to fit the next proposal and to + measure efficiency) is log_w + log_L = log( prior(c) * integral L / proposal(c) ), + i.e. posterior/proposal; neff of these -> n_real exactly when the proposal matches + the cal posterior. The final `log_w` are the importance weights for the + marginalization itself ( Z_cal = sum_c exp(log_w_c) integral L_c ). + + Returns dict with the final realizations' `nodes`, `log_w` (prior/proposal, for the + marginalization), `proposal` (mean,cov), and per-iteration `neff` history. + """ + rng = rng or np.random.default_rng() + dim = prior_mean.shape[0] + if betas is None: + # ramp tempering 0.3 -> 1.0 + betas = np.linspace(0.3, 1.0, n_iter) + mean = prior_mean.copy() + cov = np.diag(prior_sigma ** 2) + + history = [] + nodes = log_w = None + for it in range(n_iter): + nodes = rng.multivariate_normal(mean, cov, size=n_real) # (n_real, dim) + log_q = _mvn_logpdf(nodes, mean, cov) + log_p = log_prior(nodes, prior_mean, prior_sigma) + log_w = log_p - log_q # importance weights + log_L = np.asarray(evaluate(nodes)) # extrinsic-marg log-like + log_resp = log_w + log_L # posterior/proposal + # next proposal from tempered posterior responsibilities; inflate the covariance + # early (while tempering is on) to keep exploring, relax as beta -> 1. + beta = float(betas[min(it, len(betas) - 1)]) + mean, cov = fit_proposal(nodes, log_resp, beta, cov_inflate=1.0 + (1.0 - beta), + prior_sigma=prior_sigma) + neff_resp = neff_from_logweights(log_resp) + neff_w = neff_from_logweights(log_w) + history.append(dict(iter=it, beta=beta, neff_resp=neff_resp, neff_w=neff_w)) + + out = dict(nodes=nodes, log_w=log_w, proposal_mean=mean, proposal_cov=cov, + history=history) + if return_history: + out['history'] = history + return out + + +# --------------------------------------------------------------------------- +# Self-contained convergence demo (mock likelihood): no GPU/lal needed +# --------------------------------------------------------------------------- +if __name__ == "__main__": + # A "true" calibration sits ~3 sigma off the prior mean in node space, with a + # narrow likelihood (high SNR -> measurable cal). Prior-only sampling would have + # tiny neff; the adaptive loop should lock onto it and neff should climb. + rng = np.random.default_rng(1234) + dim = 8 + prior_mean = np.zeros(dim) + prior_sigma = np.ones(dim) + # measurable cal ~2 sigma off the prior, narrow likelihood (high SNR) + true_node = prior_mean + 2.0 * prior_sigma * rng.standard_normal(dim) / np.sqrt(dim) + like_sigma = 0.4 + + def evaluate(nodes): + # extrinsic-marginalized log-like proxy (no prior, no weights -- the loop adds them) + z = (nodes - true_node) / like_sigma + return -0.5 * np.sum(z * z, axis=1) + + # analytic cal posterior (Gaussian prior x Gaussian like): mean pulled from `true` + # toward the prior mean; this is the target the proposal should converge to. + w_like = 1.0 / like_sigma ** 2 + w_prior = 1.0 / prior_sigma ** 2 + post_mean = (true_node * w_like + prior_mean * w_prior) / (w_like + w_prior) + + # prior-only baseline: neff of the posterior responsibilities prior*L/prior = L + base = rng.multivariate_normal(prior_mean, np.diag(prior_sigma ** 2), size=300) + base_neff = neff_from_logweights(evaluate(base)) + err0 = float(np.max(np.abs(post_mean - prior_mean))) + print("prior-only neff_resp = %.1f / 300 (posterior is %.2f sigma off the prior mean)" + % (base_neff, err0)) + + res = adaptive_cal(evaluate, prior_mean, prior_sigma, n_nodes_amp=dim // 2, + n_real=300, n_iter=6, rng=rng) + for h in res['history']: + print("iter %d beta=%.2f neff_resp=%6.1f neff_w=%6.1f" % ( + h['iter'], h['beta'], h['neff_resp'], h['neff_w'])) + err = float(np.max(np.abs(res['proposal_mean'] - post_mean))) + print("proposal mean vs cal posterior: max|delta| = %.3f sigma" % err) + assert res['history'][-1]['neff_resp'] > 10 * base_neff, \ + "adaptive did not improve effective cal sample size" + assert err < 0.3, "proposal did not converge onto the cal posterior" + print("\nPASS: tempered adaptive cal sampling converges onto the cal posterior " + "and recovers effective samples.") diff --git a/MonteCarloMarginalizeCode/Code/RIFT/calmarg/backtest_calmarg.py b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/backtest_calmarg.py new file mode 100644 index 000000000..f0acb42b8 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/backtest_calmarg.py @@ -0,0 +1,536 @@ +""" +backtest_calmarg.py -- backtest harness for in-loop calibration marginalization + +PURPOSE +------- +Compare different implementations of the calibration-marginalized factored +likelihood against each other and against a brute-force reference, on controlled +inputs that exercise the per-realization block structure +(rholmsArrayDict[det] holding n_cal contiguous length-N_window blocks, selected by +ifirst -> ifirst + c*N_window). + +This is the rig Option C (a fused CUDA kernel) is developed against: register the +new implementation in METHODS and the harness reports lnL agreement (vs the +brute-force reference and vs Option B) and timing, on both CPU and GPU backends. + +It is deliberately self-contained (synthetic inputs, no frames/PSDs/cache needed), +so it runs anywhere RIFT + lal import. See run_physics_backtest() below for the +heavier real-data comparison vs bilby's calibration_reweighting.py. + +METHODS (the registry being backtested) +---------------------------------------- + reference : brute force -- run the unchanged n_cal==1 likelihood on each + realization block separately, combine logsumexp_c(lnL_c) - log(n_cal). + This is the ground truth the others must reproduce. + in_loop_B : DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop(..., n_cal=n_cal) + -- Option B (cal loop reusing the existing Q kernel, streaming LSE). + in_loop_C : Option C fused kernel -- STUB, raises NotImplementedError. Wire the + new implementation here when it exists; the harness will validate it. + +USAGE +----- + python -m RIFT.calmarg.backtest_calmarg --backend cpu --n-cal 20 + python -m RIFT.calmarg.backtest_calmarg --backend gpu --n-cal 100 --npts-extrinsic 4096 --repeat 5 + python -m RIFT.calmarg.backtest_calmarg --backend gpu --methods reference,in_loop_B,in_loop_C +""" +from __future__ import print_function + +import argparse +import time + +import numpy as np +import lal +from scipy.special import logsumexp + +import RIFT.likelihood.factored_likelihood as fl + + +# --------------------------------------------------------------------------- +# Synthetic case construction +# --------------------------------------------------------------------------- +def make_synthetic_case(n_cal=20, npts_extrinsic=64, N_window=256, npts=16, + deltaT=1.0/4096, dets=("H1", "L1"), seed=1234, + psd_UV=False): + """Build a controlled set of likelihood inputs with embedded cal-block + structure. The n_cal realization blocks are independent random rholm draws + (their physical relationship is irrelevant for backtesting that the *reduction* + over blocks is computed correctly -- the loglikelihood callback is applied + identically across methods, so method agreement holds regardless). + + Multiple detectors exercise the kernel's detector loop and the function's + per-detector stacking; each detector gets its own random rholms/U/V, and the + likelihood derives a distinct per-detector ifirst from the (real) detector + location, so the stacked-ifirst path is genuinely tested. + + N_window must exceed the sky time-delay spread (+-0.021 s) plus npts so the + per-sample window stays inside each block. + + Returns a dict ('case') of plain numpy arrays / scalars (rholms/U/V are dicts + keyed by detector); convert to a backend in the method functions. + """ + rng = np.random.default_rng(seed) + n_lms = 2 + npts_full = N_window * n_cal + dets = tuple(dets) + + case = dict( + dets=dets, n_cal=n_cal, n_lms=n_lms, N_window=N_window, npts=npts, + deltaT=deltaT, npts_extrinsic=npts_extrinsic, + lookupNK=np.array([[2, 2], [2, -2]], dtype=int), + tref=1000000000.0, + ) + case["rholms"] = {} + case["U"] = {} + case["V"] = {} + for det in dets: + case["rholms"][det] = (rng.standard_normal((n_lms, npts_full)) + + 1j*rng.standard_normal((n_lms, npts_full))) + if psd_UV: + # positive-definite U, V=0 -> rho_sq>0, required by the distmarg + # transforms (asinh(rho_sq/bref), x0=kappa/rho_sq); mirrors physical + case["U"][det] = np.eye(n_lms, dtype=complex) + case["V"][det] = np.zeros((n_lms, n_lms), dtype=complex) + else: + U = rng.standard_normal((n_lms, n_lms)) + 1j*rng.standard_normal((n_lms, n_lms)) + V = rng.standard_normal((n_lms, n_lms)) + 1j*rng.standard_normal((n_lms, n_lms)) + case["U"][det] = U + U.conj().T + case["V"][det] = V + V.conj().T + # epoch placed so the integration window sits near the middle of each block + case["epoch"] = case["tref"] - 0.03 + case["tvals"] = np.linspace(-(npts//2)*deltaT, (npts//2)*deltaT, npts) + + # extrinsic parameter arrays + case["phi"] = rng.uniform(0, 2*np.pi, npts_extrinsic) + case["theta"] = rng.uniform(0.2, np.pi-0.2, npts_extrinsic) + case["psi"] = rng.uniform(0, np.pi, npts_extrinsic) + case["incl"] = rng.uniform(0.2, np.pi-0.2, npts_extrinsic) + case["phiref"] = rng.uniform(0, 2*np.pi, npts_extrinsic) + case["dist"] = np.full(npts_extrinsic, 500.0) * (lal.PC_SI*1e6) # 500 Mpc + return case + + +class _PVec(object): + """Minimal stand-in for the vectorized ChooseWaveformParams object that the + likelihood reads (phi, theta, psi, incl, phiref, dist arrays; tref, deltaT + scalars).""" + pass + + +def _build_P(case, xpy): + P = _PVec() + for name in ("phi", "theta", "psi", "incl", "phiref", "dist"): + setattr(P, name, xpy.asarray(case[name])) + P.tref = case["tref"] + P.deltaT = case["deltaT"] + return P + + +def _backend(name): + if name == "cpu": + return np + if name == "gpu": + import cupy as cp + return cp + raise ValueError("backend must be 'cpu' or 'gpu', got %r" % name) + + +def _to_host(x): + try: + import cupy as cp + if isinstance(x, cp.ndarray): + return cp.asnumpy(x) + except ImportError: + pass + return np.asarray(x) + + +def _dicts(case, xpy, rholms): + """Build the per-detector dicts the likelihood expects from a rholms map.""" + dets = case["dets"] + lookupNKDict = {d: case["lookupNK"] for d in dets} + rholmsArrayDict = {d: xpy.asarray(rholms[d]) for d in dets} + ctU = {d: xpy.asarray(case["U"][d]) for d in dets} + ctV = {d: xpy.asarray(case["V"][d]) for d in dets} + epochDict = {d: case["epoch"] for d in dets} + return lookupNKDict, rholmsArrayDict, ctU, ctV, epochDict + + +def _block_rholms(case, c): + """Per-detector rholms restricted to realization block c.""" + N = case["N_window"] + return {d: case["rholms"][d][:, c*N:(c+1)*N] for d in case["dets"]} + + +# --------------------------------------------------------------------------- +# Distance-marginalization table + loglikelihood (mirror of the ILE driver, so the +# fused distmarg kernel can be validated against reference/Option B using the SAME +# table and transforms) +# --------------------------------------------------------------------------- +def _bilinear(s0, ds, t0, dt, fgrid, xpy): + """Mirror of EvenBivariateLinearInterpolator in the ILE driver.""" + dx_inv, dy_inv = 1.0/ds, 1.0/dt + + def call(x, y): + i_mid = dx_inv * (x - s0) + j_mid = dy_inv * (y - t0) + i_lo = xpy.floor(i_mid).astype(int); i_hi = xpy.ceil(i_mid).astype(int) + j_lo = xpy.floor(j_mid).astype(int); j_hi = xpy.ceil(j_mid).astype(int) + p = i_mid - i_lo; q = j_mid - j_lo + p_ = 1 - p; q_ = 1 - q + f = p_*q_ * fgrid[i_lo, j_lo] + f += p*q_ * fgrid[i_hi, j_lo] + f += p_*q * fgrid[i_lo, j_hi] + f += p*q * fgrid[i_hi, j_hi] + return f + return call + + +def make_distmarg_table(xpy, ns=64, nt=48, xmin=-1.0e4, xmax=1.0e4, + sqrt_bmax=1.0, bref=1.0, tmax=10.0, seed=7): + """Build a synthetic-but-self-consistent distance-marginalization table. + + s_array spans x0_to_s(xmin)..x0_to_s(xmax), so any x0 in (xmin,xmax) maps to an + in-bounds s; wide (xmin,xmax) keeps realized x0=kappa/rho_sq in range. lnI_array + is an arbitrary smooth surface -- physical values are irrelevant for backtesting + that the kernel reproduces the same transform the Python closure applies. + """ + def x0_to_s(x0): + return (np.arcsinh(sqrt_bmax*(x0 - xmin)) + - np.arcsinh(sqrt_bmax*(xmax - x0))) + smin = float(x0_to_s(xmin)) + smax = float(x0_to_s(xmax)) + s_array = np.linspace(smin, smax, ns) + t_array = np.linspace(0.0, tmax, nt) + SS, TT = np.meshgrid(s_array, t_array, indexing='ij') + lnI_array = -0.3*SS**2 + np.cos(TT) - 0.05*TT # smooth, arbitrary + + return dict( + lnI_array=xpy.asarray(lnI_array), + s0=float(s_array[0]), ds=float(s_array[1]-s_array[0]), + smin=float(s_array[0]), smax=float(s_array[-1]), + t0=float(t_array[0]), dt=float(t_array[1]-t_array[0]), + tmax=float(t_array[-1]), + xmin=float(xmin), xmax=float(xmax), + sqrt_bmax=float(sqrt_bmax), bref=float(bref), + ) + + +def load_real_distmarg_table(npz_path, xpy, dmin=1.0, dmax=1000.0): + """Load a real util_InitMargTable .npz into the same params dict the kernel + + mirror closure consume. Lets us backtest against the production table's actual + s/t ranges (e.g. t_array[0] may be > 0) deterministically.""" + import RIFT.likelihood.factored_likelihood as _fl + d = np.load(npz_path) + s_array = np.asarray(d["s_array"]); t_array = np.asarray(d["t_array"]) + bmax = float(np.asarray(d["bmax"])); bref = float(np.asarray(d["bref"])) + return dict( + lnI_array=xpy.asarray(d["lnI_array"]), + s0=float(s_array[0]), ds=float(s_array[1]-s_array[0]), + smin=float(s_array[0]), smax=float(s_array[-1]), + t0=float(t_array[0]), dt=float(t_array[1]-t_array[0]), + tmax=float(t_array[-1]), + xmin=float(_fl.distMpcRef/dmax), xmax=float(_fl.distMpcRef/dmin), + sqrt_bmax=float(np.sqrt(bmax)), bref=bref, + ) + + +def make_distmarg_loglikelihood(params, xpy): + """Python distmarg loglikelihood closure (mirror of the ILE driver), consuming + the same table the fused kernel uses.""" + xmin, xmax = params["xmin"], params["xmax"] + sqrt_bmax, bref = params["sqrt_bmax"], params["bref"] + smin, smax, tmax = params["smin"], params["smax"], params["tmax"] + intp = _bilinear(params["s0"], params["ds"], params["t0"], params["dt"], + params["lnI_array"], xpy) + + def loglikelihood(kappa_sq, rho_sq): + x0 = kappa_sq / rho_sq + s = (xpy.arcsinh(sqrt_bmax*(x0 - xmin)) + - xpy.arcsinh(sqrt_bmax*(xmax - x0))) + t = xpy.arcsinh(rho_sq / bref) + lnI = xpy.full_like(x0, -xpy.inf) + in_bounds = (s > smin) & (s < smax) & (t < tmax) + lnI[in_bounds] = intp(s[in_bounds], t[in_bounds]) + x0c = xpy.clip(x0, xmin, xmax) + return rho_sq * x0c * (x0 - 0.5*x0c) + lnI + return loglikelihood + + +# --------------------------------------------------------------------------- +# Method implementations (the registry being backtested) +# --------------------------------------------------------------------------- +def method_reference(case, xpy, phase_marginalization=False, loglikelihood=None): + """Brute force: per-block n_cal==1 evaluation, combined by hand.""" + if loglikelihood is None: + loglikelihood = fl._factored_lnL_helper + P = _build_P(case, xpy) + tvals = xpy.asarray(case["tvals"]) + n_cal = case["n_cal"] + lnL_blocks = np.zeros((n_cal, case["npts_extrinsic"])) + for c in range(n_cal): + lookupNKDict, rholmsArrayDict, ctU, ctV, epochDict = _dicts( + case, xpy, _block_rholms(case, c)) + out = fl.DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop( + tvals, P, lookupNKDict, rholmsArrayDict, ctU, ctV, + epochDict, Lmax=2, xpy=xpy, n_cal=1, + loglikelihood=loglikelihood, phase_marginalization=phase_marginalization) + lnL_blocks[c] = _to_host(out) + lw = case.get("cal_log_weights") + if lw is None: + return logsumexp(lnL_blocks, axis=0) - np.log(n_cal) + # unbiased importance estimate: (1/n_cal) sum_c w_c L_c -> normalize by log(n_cal) + lw = np.asarray(lw, dtype=float) + return logsumexp(lnL_blocks + lw[:, None], axis=0) - np.log(n_cal) + + +def method_in_loop_B(case, xpy, phase_marginalization=False, loglikelihood=None): + """Option B: single call with n_cal>1 (cal_method='loop').""" + if loglikelihood is None: + loglikelihood = fl._factored_lnL_helper + P = _build_P(case, xpy) + lookupNKDict, rholmsArrayDict, ctU, ctV, epochDict = _dicts( + case, xpy, case["rholms"]) + out = fl.DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop( + xpy.asarray(case["tvals"]), P, lookupNKDict, rholmsArrayDict, ctU, ctV, + epochDict, Lmax=2, xpy=xpy, n_cal=case["n_cal"], cal_method='loop', + cal_log_weights=case.get("cal_log_weights"), + loglikelihood=loglikelihood, phase_marginalization=phase_marginalization) + return _to_host(out) + + +def method_in_loop_C(case, xpy, phase_marginalization=False, loglikelihood=None): + """Option C: fused CUDA kernel (Q + default helper + cal log-sum-exp on-board). + + GPU-only and (for now) default helper / no phase marginalization; raises + NotImplementedError otherwise, so the harness SKIPs it on CPU. + """ + if loglikelihood is None: + loglikelihood = fl._factored_lnL_helper + P = _build_P(case, xpy) + lookupNKDict, rholmsArrayDict, ctU, ctV, epochDict = _dicts( + case, xpy, case["rholms"]) + out = fl.DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop( + xpy.asarray(case["tvals"]), P, lookupNKDict, rholmsArrayDict, ctU, ctV, + epochDict, Lmax=2, xpy=xpy, n_cal=case["n_cal"], cal_method='fused', + cal_distmarg=case.get("cal_distmarg"), + cal_log_weights=case.get("cal_log_weights"), + loglikelihood=loglikelihood, phase_marginalization=phase_marginalization) + return _to_host(out) + + +METHODS = { + "reference": method_reference, + "in_loop_B": method_in_loop_B, + "in_loop_C": method_in_loop_C, +} + + +# --------------------------------------------------------------------------- +# Comparison driver +# --------------------------------------------------------------------------- +def _sync(xpy): + if xpy is not np: + xpy.cuda.Stream.null.synchronize() + + +def run_backtest(methods, backend="cpu", repeat=3, phase_marginalization=False, + loglikelihood_mode="default", real_table=None, + random_cal_weights=False, **case_kwargs): + """Evaluate each method, time it, and report agreement vs 'reference' (if run) + and vs 'in_loop_B'. + + loglikelihood_mode: + 'default' -- the distance-unmarginalized helper. + 'distmarg' -- the distance-marginalization loglikelihood (uses positive-definite + U so rho_sq>0, builds a self-consistent table; reference/Option B + use the Python closure, Option C uses the fused distmarg kernel). + """ + xpy = _backend(backend) + loglikelihood = None + if loglikelihood_mode == "distmarg": + case_kwargs["psd_UV"] = True + case = make_synthetic_case(**case_kwargs) + case["dist"] = np.full(case["npts_extrinsic"], fl.distMpcRef) * (lal.PC_SI*1e6) + params = (load_real_distmarg_table(real_table, xpy) if real_table + else make_distmarg_table(xpy)) + case["cal_distmarg"] = params # consumed by the fused distmarg kernel + loglikelihood = make_distmarg_loglikelihood(params, xpy) + else: + case = make_synthetic_case(**case_kwargs) + if random_cal_weights: + # non-uniform importance log-weights, to validate the weighted reduction + rng = np.random.default_rng(20240601) + case["cal_log_weights"] = rng.normal(0.0, 1.5, size=case["n_cal"]) + # distmarg's asinh/bilinear differ at ULP level between numpy and the kernel, so + # the fused-vs-loop agreement is float-level rather than bit-level. + tol = 1e-9 if loglikelihood_mode == "default" else 1e-6 + print("# calmarg backtest backend=%s dets=%s n_cal=%d npts_extrinsic=%d N_window=%d npts=%d phase_marg=%s loglike=%s" + % (backend, ",".join(case["dets"]), case["n_cal"], case["npts_extrinsic"], + case["N_window"], case["npts"], phase_marginalization, loglikelihood_mode)) + + results = {} + timings = {} + for name in methods: + fn = METHODS[name] + try: + out = fn(case, xpy, phase_marginalization=phase_marginalization, + loglikelihood=loglikelihood) # warm-up / compile + _sync(xpy) + best = float("inf") + for _ in range(repeat): + t0 = time.perf_counter() + out = fn(case, xpy, phase_marginalization=phase_marginalization, + loglikelihood=loglikelihood) + _sync(xpy) + best = min(best, time.perf_counter() - t0) + results[name] = np.asarray(out) + timings[name] = best + print(" %-12s ok best %8.2f ms" % (name, best*1e3)) + except NotImplementedError as e: + print(" %-12s SKIP (%s)" % (name, e)) + except Exception as e: + print(" %-12s FAIL %s: %s" % (name, type(e).__name__, e)) + + # agreement + baseline = "reference" if "reference" in results else ( + "in_loop_B" if "in_loop_B" in results else None) + if baseline: + print("# max |lnL - %s| (tol %.0e):" % (baseline, tol)) + ok = True + for name, vals in results.items(): + if name == baseline: + continue + err = float(np.max(np.abs(vals - results[baseline]))) + flag = "OK" if err < tol else "**DIFF**" + if err >= tol: + ok = False + print(" %-12s %.3e %s" % (name, err, flag)) + print("# RESULT:", "PASS" if ok else "MISMATCH") + return ok + return True + + +# --------------------------------------------------------------------------- +# Physics backtest vs bilby calibration_reweighting.py (scaffold -- needs data) +# --------------------------------------------------------------------------- +def run_physics_backtest(precompute_or_config=None, cal_envelope_dir=None, + bilby_data_dump=None, **kwargs): + """Compare in-loop calibration marginalization to the bilby postprocessor on a + REAL event. This needs frames/PSDs/cache (or a saved ILE precompute) plus the + bilby data_dump used by calibration_reweighting.py, so it does NOT run in the + self-contained harness above. + + Intended flow (TODO, to run on the stable host): + 1. Build data_dict / psd_dict (real or injected) the same way ILE does, OR + load a saved precompute. + 2. cal = RIFT.calmarg.generate_realizations.create_realizations(env, ...) for + each detector from cal_envelope_dir. + 3. PrecomputeLikelihoodTerms(..., calibration_realizations=cal) -> cal-extended + rholms; pack with PackLikelihoodDataStructuresAsArrays. + 4. Evaluate the in-loop calmarg likelihood over the SAME extrinsic samples the + bilby reweighter used (read its posterior + weights), compare per-sample + lnL and the integrated log-evidence shift. + 5. Compare to bilby calibration_likelihood from calibration_reweighting.py. + Expect agreement to first order in cal amplitude; the apply-to-data + (RIFT) vs apply-to-template (bilby) convention differs at second order -- + quantify and record that difference here. + """ + raise NotImplementedError( + "Physics backtest needs real data/precompute + a bilby data_dump; " + "see docstring for the intended flow. Run on the stable host post-update.") + + +def scan_timing(methods, backend="gpu", n_cal_list=(1, 10, 50, 100, 200), + repeat=5, loglikelihood_mode="default", real_table=None, **case_kwargs): + """Per-likelihood-evaluation wall-time vs n_cal, to quantify the cost of + calibration marginalization (and the brute-force reference) for planning. + + Reports best-of-`repeat` ms per call for each method at each n_cal. The + reference (brute force) does n_cal separate n_cal==1 evaluations, so its cost + scales ~linearly in n_cal; loop reuses the kernel per realization; fused does it + in one launch. Multiply by (n_iterations * blocks-per-iteration) to estimate the + full-integration cost.""" + xpy = _backend(backend) + print("# timing scan backend=%s dets=%s npts_extrinsic=%d loglike=%s" + % (backend, case_kwargs.get("dets", "H1,L1"), + case_kwargs.get("npts_extrinsic", 64), loglikelihood_mode)) + print("# %-6s " % "n_cal" + "".join("%14s" % m for m in methods) + " (ms/eval, best of %d)" % repeat) + for n_cal in n_cal_list: + ck = dict(case_kwargs); ck["n_cal"] = n_cal + loglikelihood = None + if loglikelihood_mode == "distmarg": + ck["psd_UV"] = True + case = make_synthetic_case(**ck) + case["dist"] = np.full(case["npts_extrinsic"], fl.distMpcRef) * (lal.PC_SI*1e6) + params = (load_real_distmarg_table(real_table, xpy) if real_table + else make_distmarg_table(xpy)) + case["cal_distmarg"] = params + loglikelihood = make_distmarg_loglikelihood(params, xpy) + else: + case = make_synthetic_case(**ck) + row = [] + for name in methods: + fn = METHODS[name] + try: + fn(case, xpy, loglikelihood=loglikelihood) # warm-up + _sync(xpy) + best = float("inf") + for _ in range(repeat): + t0 = time.perf_counter() + fn(case, xpy, loglikelihood=loglikelihood) + _sync(xpy) + best = min(best, time.perf_counter() - t0) + row.append("%14.3f" % (best * 1e3)) + except Exception as e: + row.append("%14s" % ("ERR:" + type(e).__name__)) + print(" %-6d" % n_cal + "".join(row)) + + +def _parse_args(): + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + p.add_argument("--backend", default="cpu", choices=["cpu", "gpu"]) + p.add_argument("--methods", default="reference,in_loop_B,in_loop_C", + help="comma-separated subset of: %s" % ",".join(METHODS)) + p.add_argument("--n-cal", type=int, default=20) + p.add_argument("--dets", default="H1,L1", help="comma-separated detector prefixes") + p.add_argument("--npts-extrinsic", type=int, default=64) + p.add_argument("--N-window", type=int, default=256) + p.add_argument("--npts", type=int, default=16) + p.add_argument("--repeat", type=int, default=3, help="timing repetitions (best-of)") + p.add_argument("--loglikelihood", default="default", choices=["default", "distmarg"], + help="default helper, or distance-marginalization loglikelihood") + p.add_argument("--real-table", default=None, + help="path to a real util_InitMargTable .npz (distmarg mode) to backtest against") + p.add_argument("--random-cal-weights", action="store_true", + help="inject non-uniform per-realization importance log-weights (validate the weighted reduction)") + p.add_argument("--phase-marginalization", action="store_true") + p.add_argument("--seed", type=int, default=1234) + p.add_argument("--scan-ncal", default=None, + help="comma-separated n_cal values: time each method per n_cal instead of validating") + return p.parse_args() + + +if __name__ == "__main__": + args = _parse_args() + methods = [m.strip() for m in args.methods.split(",") if m.strip()] + unknown = [m for m in methods if m not in METHODS] + if unknown: + raise SystemExit("unknown methods: %s (known: %s)" + % (unknown, list(METHODS))) + dets = tuple(d.strip() for d in args.dets.split(",") if d.strip()) + if args.scan_ncal: + n_cal_list = [int(x) for x in args.scan_ncal.split(",") if x.strip()] + scan_timing(methods, backend=args.backend, n_cal_list=n_cal_list, + repeat=args.repeat, loglikelihood_mode=args.loglikelihood, + real_table=args.real_table, npts_extrinsic=args.npts_extrinsic, + N_window=args.N_window, npts=args.npts, seed=args.seed, dets=dets) + raise SystemExit(0) + ok = run_backtest( + methods, backend=args.backend, repeat=args.repeat, + phase_marginalization=args.phase_marginalization, + loglikelihood_mode=args.loglikelihood, real_table=args.real_table, + random_cal_weights=args.random_cal_weights, + n_cal=args.n_cal, npts_extrinsic=args.npts_extrinsic, + N_window=args.N_window, npts=args.npts, seed=args.seed, dets=dets) + raise SystemExit(0 if ok else 1) diff --git a/MonteCarloMarginalizeCode/Code/RIFT/calmarg/breadcrumbs.py b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/breadcrumbs.py new file mode 100644 index 000000000..07f1223bb --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/breadcrumbs.py @@ -0,0 +1,136 @@ +""" +Breadcrumbs: a portable, integrator-agnostic save/load object for a LEARNED proposal +distribution (Option B in DESIGN_adaptive_driver.md). + +The point of this module is the *interface and schema*, not the model. Today it carries +a Gaussian over calibration spline-node parameters (mean/cov) plus the prior; the schema +reserves an `extrinsic` slot so the SAME object can later carry the extrinsic proposal +(the decade-old "save the extrinsic distribution" goal), and a `kind` field so a +normalizing flow can drop in behind the same load()/sample() interface. + +Stored as a single .npz (arrays + a JSON metadata sidecar string). Keep the schema +STABLE: add fields, do not repurpose them; bump SCHEMA_VERSION on incompatible changes. + +EXTRINSIC slot (schema v2): a portable learned proposal over the EXTRINSIC parameters, +the decade-old "save the extrinsic distribution to inform the next iteration" goal. It is +a list of parameter GROUPS (matching the ILE GMM sampler's gmm_dict structure -- e.g. +(right_ascension, declination), (distance, inclination), (phi_orb, psi)); each group holds +a Gaussian mixture (means/covariances/weights) over its parameters, plus the parameter +NAMES (so the indices reconstruct against the next run's params_ordered) and the sampling +bounds. kind='gmm' now; a normalizing flow drops in later behind the same interface. +""" +from __future__ import division + +import json +import numpy as np + +SCHEMA_VERSION = 2 + + +def save(path, cal=None, extrinsic=None, kind="gaussian", meta=None): + """Write a breadcrumb file. + + cal : dict or None -- the learned calibration proposal, with keys + proposal_mean (dim,), proposal_cov (dim,dim), prior_mean (dim,), prior_sigma (dim,), + node_log_f (n_nodes,), n_nodes_amp (int), dets (list[str]). + Node-vector layout per detector: [amp_0..amp_{N-1}, phase_0..phase_{N-1}], + concatenated over `dets` in order. + extrinsic : dict or None -- the learned EXTRINSIC proposal: + {'kind': 'gmm', + 'groups': [ {'params': [name,...], 'means': (K,d), 'covariances': (K,d,d), + 'weights': (K,), 'bounds': (d,2)}, ... ]}. + One group per gmm_dict block; the GMM is over the group's params in `params` order. + kind : top-level kind tag ('gaussian' for the cal Gaussian; extrinsic kind is its own). + meta : json-able dict (iteration, n_pilot_points, neff_cal, source composite, ...). + """ + d = dict(schema_version=np.int64(SCHEMA_VERSION), kind=str(kind), + has_cal=np.bool_(cal is not None), has_extrinsic=np.bool_(extrinsic is not None), + meta_json=json.dumps(meta or {})) + if cal is not None: + d.update( + cal_proposal_mean=np.asarray(cal["proposal_mean"], dtype=float), + cal_proposal_cov=np.asarray(cal["proposal_cov"], dtype=float), + cal_prior_mean=np.asarray(cal["prior_mean"], dtype=float), + cal_prior_sigma=np.asarray(cal["prior_sigma"], dtype=float), + cal_node_log_f=np.asarray(cal["node_log_f"], dtype=float), + cal_n_nodes_amp=np.int64(cal["n_nodes_amp"]), + cal_dets=np.array(list(cal["dets"]), dtype=object), + ) + if extrinsic is not None: + groups = extrinsic["groups"] + d["ext_kind"] = str(extrinsic.get("kind", "gmm")) + d["ext_n_groups"] = np.int64(len(groups)) + for i, g in enumerate(groups): + d["ext_g%d_params" % i] = np.array(list(g["params"]), dtype=object) + d["ext_g%d_means" % i] = np.asarray(g["means"], dtype=float) + d["ext_g%d_covs" % i] = np.asarray(g["covariances"], dtype=float) + d["ext_g%d_weights" % i] = np.asarray(g["weights"], dtype=float) + d["ext_g%d_bounds" % i] = np.asarray(g["bounds"], dtype=float) + np.savez(path, **d) + return path + + +def load(path): + """Read a breadcrumb file -> dict {schema_version, kind, cal, extrinsic, meta}.""" + z = np.load(path, allow_pickle=True) + ver = int(z["schema_version"]) + if ver > SCHEMA_VERSION: + raise ValueError("breadcrumb schema_version %d newer than supported %d" + % (ver, SCHEMA_VERSION)) + out = dict(schema_version=ver, kind=str(z["kind"]), + meta=json.loads(str(z["meta_json"])), cal=None, extrinsic=None) + if bool(z["has_cal"]): + out["cal"] = dict( + proposal_mean=z["cal_proposal_mean"], proposal_cov=z["cal_proposal_cov"], + prior_mean=z["cal_prior_mean"], prior_sigma=z["cal_prior_sigma"], + node_log_f=z["cal_node_log_f"], n_nodes_amp=int(z["cal_n_nodes_amp"]), + dets=[str(x) for x in z["cal_dets"]], + ) + if "has_extrinsic" in z and bool(z["has_extrinsic"]): + groups = [] + for i in range(int(z["ext_n_groups"])): + groups.append(dict( + params=[str(x) for x in z["ext_g%d_params" % i]], + means=z["ext_g%d_means" % i], covariances=z["ext_g%d_covs" % i], + weights=z["ext_g%d_weights" % i], bounds=z["ext_g%d_bounds" % i], + )) + out["extrinsic"] = dict(kind=str(z["ext_kind"]), groups=groups) + return out + + +if __name__ == "__main__": + # round-trip smoke test + dim = 6 + cal = dict(proposal_mean=np.arange(dim, dtype=float), + proposal_cov=np.eye(dim) * 0.1, + prior_mean=np.zeros(dim), prior_sigma=np.ones(dim), + node_log_f=np.linspace(1, 3, dim // 2), n_nodes_amp=dim // 2, + dets=["H1", "L1", "V1"]) + import tempfile, os + p = os.path.join(tempfile.mkdtemp(), "bc.npz") + save(p, cal=cal, meta=dict(iteration=2, neff_cal=87.3)) + g = load(p) + assert g["kind"] == "gaussian" and g["cal"]["dets"] == ["H1", "L1", "V1"] + assert np.allclose(g["cal"]["proposal_mean"], cal["proposal_mean"]) + assert g["meta"]["iteration"] == 2 + + # extrinsic (GMM) round-trip + ext = dict(kind="gmm", groups=[ + dict(params=["right_ascension", "declination"], + means=np.array([[1.0, 0.2], [4.0, -0.3]]), + covariances=np.array([np.eye(2) * 0.05, np.eye(2) * 0.1]), + weights=np.array([0.6, 0.4]), + bounds=np.array([[0.0, 2 * np.pi], [-np.pi / 2, np.pi / 2]])), + dict(params=["distance", "inclination"], + means=np.array([[500.0, 1.0]]), covariances=np.array([np.diag([1e4, 0.1])]), + weights=np.array([1.0]), bounds=np.array([[1.0, 1000.0], [0.0, np.pi]])), + ]) + p2 = os.path.join(tempfile.mkdtemp(), "bc2.npz") + save(p2, cal=cal, extrinsic=ext, meta=dict(iteration=3)) + g2 = load(p2) + assert g2["extrinsic"]["kind"] == "gmm" and len(g2["extrinsic"]["groups"]) == 2 + assert g2["extrinsic"]["groups"][0]["params"] == ["right_ascension", "declination"] + assert np.allclose(g2["extrinsic"]["groups"][0]["means"], ext["groups"][0]["means"]) + assert np.allclose(g2["extrinsic"]["groups"][1]["covariances"], ext["groups"][1]["covariances"]) + assert g2["cal"] is not None # cal + extrinsic coexist in one breadcrumb + print("PASS: breadcrumb save/load round-trips (cal Gaussian + extrinsic GMM, schema v%d)." % SCHEMA_VERSION) diff --git a/MonteCarloMarginalizeCode/Code/RIFT/calmarg/extrinsic_handoff.py b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/extrinsic_handoff.py new file mode 100644 index 000000000..f4701aa76 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/extrinsic_handoff.py @@ -0,0 +1,232 @@ +""" +Extrinsic handoff: learn a portable proposal over the EXTRINSIC parameters from one +iteration's posterior samples, and seed the NEXT iteration's extrinsic sampler from it. + +This is the decade-old "save the extrinsic distribution to inform the next iteration" +goal, generalized from the calibration pilot's breadcrumb (RIFT.calmarg.breadcrumbs). +The extrinsic posterior barely changes from iteration to iteration (it is set by the data ++ best-fit template, not by the small intrinsic-grid moves), so carrying it forward lets +the sampler start near the answer instead of cold each time. + +GMM-first (this module): RIFT's ensemble sampler (mcsamplerEnsemble) is already seedable -- +its `gmm_dict` maps parameter GROUPS (tuples of indices into params_ordered) to a fitted +`gaussian_mixture_model.gmm`, and a non-None entry is used as the starting proposal. So +the handoff is: + fit_extrinsic_proposal(samples, log_weights, groups, bounds) # per group, RIFT gmm.fit + -> a portable breadcrumb 'extrinsic' dict (means/covs/weights/bounds + param names) + gmm_dict_from_breadcrumb(extrinsic, params_ordered) + -> reconstruct gmm objects, keyed by the dim-group indices, for the next sampler. + +We use RIFT's OWN gmm.fit (the same fitter the sampler uses in update_sampling_prior), so +the stored means/covariances are in exactly the model's internal (normalized) frame and +restore to a byte-identical model -- no coordinate guesswork. A normalizing flow can later +drop in behind the same fit/seed interface (breadcrumb kind != 'gmm'). + +The standard extrinsic groups (matching the ILE GMM gmm_dict) are + (right_ascension, declination), (distance, inclination), (phi_orb, psi). +""" +from __future__ import division + +import numpy as np + +# RIFT's ensemble-sampler GMM (the one gmm_dict expects). Imported lazily so this module +# is importable without the integrator stack (e.g. for breadcrumb round-trip tests). +def _gmm_module(): + import RIFT.integrators.gaussian_mixture_model as GMM + return GMM + + +STANDARD_GROUPS = [ + ["right_ascension", "declination"], + ["distance", "inclination"], + ["phi_orb", "psi"], +] + + +def fit_extrinsic_proposal(samples, log_weights, groups=None, bounds=None, + n_comp=4, max_iters=1000): + """Fit a per-group Gaussian mixture to extrinsic POSTERIOR samples. + + samples : dict {param_name: 1-D array (n,)} -- the extrinsic samples of one run. + log_weights : (n,) importance log-weights (log L + log prior - log sampling_prior), + the same weights the sampler's update_sampling_prior uses. None -> uniform. + groups : list of param-name lists (default STANDARD_GROUPS); only groups whose params + are ALL present in `samples` are fit. + bounds : dict {param_name: (lo, hi)} sampling bounds (required for the GMM frame). + n_comp : mixture components per group. + + Returns the breadcrumb 'extrinsic' dict: + {'kind': 'gmm', 'groups': [ {'params', 'means'(K,d), 'covariances'(K,d,d), + 'weights'(K,), 'bounds'(d,2)}, ... ]}. + """ + GMM = _gmm_module() + if groups is None: + groups = STANDARD_GROUPS + if bounds is None: + raise ValueError("fit_extrinsic_proposal needs per-parameter sampling bounds") + n = len(next(iter(samples.values()))) + lw = np.zeros(n) if log_weights is None else np.asarray(log_weights, dtype=float) + + # Effective sample size of the importance weights (Kish). The source run may have a low + # ESS (calmarg makes the extrinsic integral hard); fitting too many mixture components to + # too few effective samples STARVES the EM fit -- a component collapses onto ~1 sample and + # its covariance goes singular/NaN, poisoning the whole seeded proposal. Cap components by + # ESS below (mirrors the cal pilot's d(d+1)/2 reasoning). + _lwf = lw[np.isfinite(lw)] + if len(_lwf): + _w = np.exp(_lwf - _lwf.max()) + ess = float((_w.sum() ** 2) / np.sum(_w ** 2)) + else: + ess = float(n) + + out_groups = [] + for grp in groups: + if not all(p in samples for p in grp): + continue + d = len(grp) + sample_array = np.column_stack([np.asarray(samples[p], dtype=float) for p in grp]) + grp_bounds = np.array([list(bounds[p]) for p in grp], dtype=float) # (d, 2) + # need >~ (d+2) effective samples per component for a non-degenerate covariance + k = min(n_comp, max(1, int(ess // (d + 2))), max(1, sample_array.shape[0])) + model = GMM.gmm(k, grp_bounds, max_iters=max_iters) + # the model may run on cupy (GPU); move inputs onto its device first. + model.fit(model.identity_convert_togpu(sample_array), + log_sample_weights=model.identity_convert_togpu(lw)) + # model.means/.covariances are lists (length k) in the model's internal frame. + means = np.array([np.asarray(model.identity_convert(m)) for m in model.means]) # (k, d) + covs = np.array([np.asarray(model.identity_convert(c)) for c in model.covariances]) # (k, d, d) + weights = np.asarray(model.identity_convert(model.weights), dtype=float).reshape(-1) # (k,) + # DROP degenerate components. When the source run is starved (few effective samples + # vs n_comp x d), the EM fit collapses a component onto ~1 sample -> singular/NaN + # covariance and NaN mean. A single NaN component poisons the whole seeded proposal + # (NaN sampling-prior -> NaN lnL -> wrong integral). Keep only finite, positive-weight + # components and renormalize; if none survive, skip the group (it stays cold = safe). + good = (np.isfinite(means).all(axis=1) & np.isfinite(covs).reshape(len(covs), -1).all(axis=1) + & np.isfinite(weights) & (weights > 0)) + if not good.any(): + continue + means, covs, weights = means[good], covs[good], weights[good] + weights = weights / weights.sum() + out_groups.append(dict(params=list(grp), means=means, covariances=covs, + weights=weights, bounds=grp_bounds)) + return dict(kind="gmm", groups=out_groups) + + +def reconstruct_gmm(group, max_iters=1000, adapt=True, cov_inflate=1.0): + """Rebuild a RIFT gaussian_mixture_model.gmm from a stored breadcrumb group. + adapt=True -> the seeded components keep adapting in the next run (extrinsics drift a + little); adapt=False freezes them. + + cov_inflate (>=1) widens the seeded covariances (in the model's normalized frame). Default + 1.0 (no inflation): a FROZEN seed (the default seeding mode) should match the source + posterior, not be widened -- inflating only pushes samples past hard bounds (e.g. distance), + where the likelihood is NaN. Inflation is only useful for the adapt=True path (broaden so + the sampler can contract); the freeze path makes it unnecessary. + + All model arrays (means/covariances/weights AND bounds) are moved onto the model's device + (cupy on GPU): the sampler's score()/_normalize write into an xpy.empty array, so a + leftover numpy `self.bounds` raises 'non-scalar numpy.ndarray cannot be used for fill'.""" + GMM = _gmm_module() + means = np.asarray(group["means"]); covs = np.asarray(group["covariances"], dtype=float) * float(cov_inflate) + weights = np.asarray(group["weights"], dtype=float); bounds = np.asarray(group["bounds"], dtype=float) + k = means.shape[0] + model = GMM.gmm(k, bounds, max_iters=max_iters) + model.bounds = model.identity_convert_togpu(bounds) # must match self.xpy (GPU) + model.means = [model.identity_convert_togpu(means[i]) for i in range(k)] + model.covariances = [model.identity_convert_togpu(covs[i]) for i in range(k)] + model.weights = model.identity_convert_togpu(weights) + model.adapt = [bool(adapt)] * k + model.d = means.shape[1] + return model + + +def _permute_group(group, perm): + """Return a copy of a breadcrumb group with its parameter columns reordered by `perm` + (perm[j] = source column index that should land at output position j).""" + means = np.asarray(group["means"])[:, perm] + covs = np.asarray(group["covariances"])[:, perm][:, :, perm] + bounds = np.asarray(group["bounds"])[perm] + return dict(params=[group["params"][j] for j in perm], means=means, + covariances=covs, weights=group["weights"], bounds=bounds) + + +def gmm_dict_from_breadcrumb(extrinsic, params_ordered, adapt=True, existing_keys=None, cov_inflate=1.0): + """Build a gmm_dict {dim_group_tuple: gmm} to SEED mcsamplerEnsemble, from a breadcrumb + 'extrinsic' dict. dim_group_tuple are indices into `params_ordered` (the sampler's + parameter order this run), looked up by parameter NAME -- so the handoff is robust to a + different parameter ordering between runs. Groups whose params are not all present in + params_ordered this run are skipped (with no error). + + `existing_keys` (the sampler's actual gmm_dict keys this run) makes the seed robust to the + WITHIN-group parameter ORDER: the sampler may pair, e.g., (psi, phi_orb) while the + breadcrumb stored (phi_orb, psi). We match each breadcrumb group to the existing key with + the same dim SET, then permute the stored means/covariances/bounds columns into that key's + dim order -- so the seeded model lines up with how the sampler will draw/score it. Without + existing_keys the key is just the breadcrumb's own param order.""" + if extrinsic is None or extrinsic.get("kind") != "gmm": + return {} + name_to_idx = {p: i for i, p in enumerate(params_ordered)} + key_by_set = {frozenset(k): tuple(k) for k in existing_keys} if existing_keys is not None else None + gmm_dict = {} + for group in extrinsic["groups"]: + if not all(p in name_to_idx for p in group["params"]): + continue + grp_idx = [name_to_idx[p] for p in group["params"]] # dim index of each stored column + if key_by_set is not None: + target = key_by_set.get(frozenset(grp_idx)) + if target is None: + continue # sampler has no matching group + else: + target = tuple(grp_idx) + perm = [grp_idx.index(dim) for dim in target] # reorder stored cols -> target order + g = group if perm == list(range(len(perm))) else _permute_group(group, perm) + gmm_dict[target] = reconstruct_gmm(g, adapt=adapt, cov_inflate=cov_inflate) + return gmm_dict + + +# --------------------------------------------------------------------------- +# Proof-of-concept: fit a synthetic multi-cluster extrinsic posterior, round-trip it through +# a breadcrumb, and show a seeded GMM starts on the posterior (vs a cold wide prior). +# --------------------------------------------------------------------------- +if __name__ == "__main__": + from RIFT.calmarg import breadcrumbs + import tempfile, os + + rng = np.random.default_rng(0) + # A bimodal sky posterior (two sky modes) + a unimodal distance/inclination blob. + bounds = {"right_ascension": (0.0, 2 * np.pi), "declination": (-np.pi / 2, np.pi / 2), + "distance": (1.0, 1000.0), "inclination": (0.0, np.pi)} + n = 4000 + mode = rng.random(n) < 0.6 + ra = np.where(mode, rng.normal(1.0, 0.10, n), rng.normal(4.2, 0.15, n)) % (2 * np.pi) + dec = np.where(mode, rng.normal(0.2, 0.08, n), rng.normal(-0.4, 0.10, n)) + dist = np.clip(rng.normal(450.0, 60.0, n), 1, 1000) + incl = np.clip(rng.normal(1.1, 0.2, n), 0, np.pi) + samples = {"right_ascension": ra, "declination": dec, "distance": dist, "inclination": incl} + + ext = fit_extrinsic_proposal(samples, log_weights=None, bounds=bounds, n_comp=3) + print("fit %d groups: %s" % (len(ext["groups"]), [g["params"] for g in ext["groups"]])) + + # round-trip through a breadcrumb + p = os.path.join(tempfile.mkdtemp(), "ext.npz") + breadcrumbs.save(p, extrinsic=ext, meta=dict(iteration=1)) + g = breadcrumbs.load(p) + assert g["extrinsic"]["kind"] == "gmm" + assert np.allclose(g["extrinsic"]["groups"][0]["means"], ext["groups"][0]["means"]) + + # seed: reconstruct the gmm_dict against a (shuffled) params_ordered, draw from the + # seeded sky GMM, and check the draws land on the bimodal posterior (means recovered). + params_ordered = ["distance", "psi", "right_ascension", "phi_orb", "declination", "inclination"] + gmm_dict = gmm_dict_from_breadcrumb(g["extrinsic"], params_ordered) + sky_key = (params_ordered.index("right_ascension"), params_ordered.index("declination")) + assert sky_key in gmm_dict, "sky group not seeded" + sky = gmm_dict[sky_key] + draws = np.asarray(sky.identity_convert(sky.sample(3000))) + # nearest-mode recovery: each true mode should have draws clustered around it + for true_ra in (1.0, 4.2): + near = np.min(np.abs((draws[:, 0] - true_ra + np.pi) % (2 * np.pi) - np.pi)) + assert near < 0.5, "seeded GMM draws miss the sky mode at ra=%.1f" % true_ra + frac_mode1 = np.mean(np.abs(((draws[:, 0] - 1.0 + np.pi) % (2 * np.pi)) - np.pi) < 1.0) + print("seeded sky GMM: draws recover both modes; ~%.0f%% near mode-1 (true ~60%%)" % (100 * frac_mode1)) + print("PASS: extrinsic posterior -> breadcrumb -> seeded GMM reproduces the (bimodal) " + "sky distribution; ready to seed mcsamplerEnsemble's gmm_dict.") diff --git a/MonteCarloMarginalizeCode/Code/RIFT/calmarg/generate_realizations.py b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/generate_realizations.py index a4a8fbfc8..8b79ab1c2 100644 --- a/MonteCarloMarginalizeCode/Code/RIFT/calmarg/generate_realizations.py +++ b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/generate_realizations.py @@ -77,30 +77,76 @@ def nodes_to_spline_coefficients_matrix(n_points): return np.linalg.solve(tmp1, tmp2) -def create_realizations(fname, T_segment,dT, fmin, fmax, n_spline_points, n_realizations): - # NOTE - # - the bilby tool (because it needs high computational efficiency, being done many times) is much harder to read. We will use library code, because we only call it ONCE PER RUN - # - similarly, the LI/bilby tool uses a slightly different representation, because they are trying to avoid transcendental operations to improve efficiency - # Conversion tool - # spline_matrix = nodes_to_spline_coefficients_matrix(n_spline_points) - # STEP 0: logarithmic frequency spacing in positive freequency -# print(fname, T_segment, dT, fmin, fmax, n_spline_points, n_realizations) - +def node_prior(fname, fmin, fmax, n_spline_points): + """Return the calibration PRIOR over spline nodes for one detector, as the + diagonal Gaussian implied by the envelope file. + + The per-detector node vector is laid out as + [amp_0 .. amp_{N-1}, phase_0 .. phase_{N-1}] (N = n_spline_points) + with amp node i ~ N(median_amp_i, sigma_amp_i) and phase node i ~ + N(median_phase_i, sigma_phase_i), independent (this is exactly the prior that + create_realizations() draws from). + + Returns dict(mean, sigma, node_log_f, n_nodes_amp) -- mean/sigma length 2N. + """ log_freq_spline_locations = np.linspace(np.log10(fmin), np.log10(fmax), n_spline_points) - - # Localize data to location dat_amp, dat_phase = retrieve_envelope_from_file(fname, frequency_array=10**log_freq_spline_locations) - # Create random spline realizations + mean = np.concatenate([dat_amp[:, 1], dat_phase[:, 1]]) + sigma = np.concatenate([dat_amp[:, 2], dat_phase[:, 2]]) + return dict(mean=mean, sigma=sigma, node_log_f=log_freq_spline_locations, + n_nodes_amp=int(n_spline_points)) + + +def prior_cal_breadcrumb_dict(env_dir, dets, fmin, fmax, n_spline_points, fmin_ifo=None): + """Build the 'cal' breadcrumb dict for the broad PRIOR, with proposal == prior. + + Suitable as an iteration-0 placeholder breadcrumb: seeding from it + (seed_realizations_from_breadcrumb) draws cal realizations from the prior with ZERO + importance weights (log prior - log proposal = 0), i.e. it is equivalent to the cold + prior draws -- but, unlike a 0-byte placeholder, it LOADS cleanly (so an older ILE binary + that does not guard against an empty placeholder will not crash on it). + + Layout matches seed_realizations_from_breadcrumb: the full node vector is concatenated per + detector in `dets` order as [det0_amp_0..,det0_phase_0..,det1_amp..,...]; dim = 2N*len(dets). + """ + import os + means = []; sigmas = []; node_log_f = None + for ifo in dets: + fmin_here = fmin + if fmin_ifo and ifo in fmin_ifo: + fmin_here = fmin_ifo[ifo] + pr = node_prior(os.path.join(env_dir, ifo + ".txt"), fmin_here, fmax, n_spline_points) + means.append(pr["mean"]); sigmas.append(pr["sigma"]) + if node_log_f is None: + node_log_f = pr["node_log_f"] + prior_mean = np.concatenate(means); prior_sigma = np.concatenate(sigmas) + return dict(proposal_mean=prior_mean, proposal_cov=np.diag(prior_sigma ** 2), + prior_mean=prior_mean, prior_sigma=prior_sigma, + node_log_f=node_log_f, n_nodes_amp=int(n_spline_points), dets=list(dets)) + + +def _draw_amp_phase_nodes(dat_amp, dat_phase, n_spline_points, n_realizations): + """Draw amp/phase spline nodes from the prior, in the EXACT random-number order + create_realizations() has always used (so seeded behavior is byte-identical).""" amp_rand_array = np.zeros((n_spline_points, n_realizations)) phase_rand_array = np.zeros((n_spline_points, n_realizations)) -# print(amp_rand_array.shape, phase_rand_array.shape) - # Create random amplitudes, phases - # - not efficient, for loop : use matrix operations to speed up! for indx in np.arange(n_spline_points): - amp_rand_array[indx,:] = np.random.normal(loc=dat_amp[indx,1], scale=dat_amp[indx,2], size=n_realizations) - phase_rand_array[indx,:] = np.random.normal(loc=dat_phase[indx,1], scale=dat_phase[indx,2], size=n_realizations) + amp_rand_array[indx, :] = np.random.normal(loc=dat_amp[indx, 1], scale=dat_amp[indx, 2], size=n_realizations) + phase_rand_array[indx, :] = np.random.normal(loc=dat_phase[indx, 1], scale=dat_phase[indx, 2], size=n_realizations) + return amp_rand_array, phase_rand_array - # Create realizations (complex-valued array for TWO_SIDED system + +def build_realizations_from_nodes(amp_rand_array, phase_rand_array, T_segment, dT, + fmin, fmax, log_freq_spline_locations): + """Build the complex two-sided per-realization calibration factor array from + spline-node values. Factored out of create_realizations() so the SAME spline + construction is reused both for prior draws and for proposal-seeded draws + (seed_realizations_from_breadcrumb). + + amp_rand_array, phase_rand_array : (n_spline_points, n_realizations). + Returns dat_out (npts_seg, n_realizations) complex, unity outside [fmin,fmax]. + """ + n_realizations = amp_rand_array.shape[1] deltaF_seg = 1./T_segment npts_seg = int(T_segment/dT) # Match array locations from lalsimutils.evaluate_fvals! @@ -108,7 +154,7 @@ def create_realizations(fname, T_segment,dT, fmin, fmax, n_spline_points, n_rea mask_positive = freq_locations_physical > 0 mask_negative = freq_locations_physical < 0 mask_in_range = np.logical_and(np.abs(freq_locations_physical) >= fmin , np.abs(freq_locations_physical) <= fmax) - + dat_out = np.ones((npts_seg, n_realizations),dtype=complex) # default factor is unity # Loop over realizations, build up spline @@ -124,11 +170,143 @@ def create_realizations(fname, T_segment,dT, fmin, fmax, n_spline_points, n_rea dat_out[mask_plus, indx] = cs_amp( log10_freq_pos_in_range )*np.exp(1j*cs_phase(log10_freq_pos_in_range)) dat_out[mask_minus, indx] = cs_amp( log10_minus_freq_neg_in_range )*np.exp(-1j*cs_phase(log10_minus_freq_neg_in_range)) -# print(log10_freq_pos_in_range, cs_amp(log10_freq_pos_in_range), cs_phase(log10_freq_pos_in_range) ) -# print(dat_out[mask_plus]) return dat_out +def create_realizations(fname, T_segment,dT, fmin, fmax, n_spline_points, n_realizations): + # NOTE + # - the bilby tool (because it needs high computational efficiency, being done many times) is much harder to read. We will use library code, because we only call it ONCE PER RUN + # - similarly, the LI/bilby tool uses a slightly different representation, because they are trying to avoid transcendental operations to improve efficiency + # Conversion tool + # spline_matrix = nodes_to_spline_coefficients_matrix(n_spline_points) + # STEP 0: logarithmic frequency spacing in positive freequency +# print(fname, T_segment, dT, fmin, fmax, n_spline_points, n_realizations) + + log_freq_spline_locations = np.linspace(np.log10(fmin), np.log10(fmax), n_spline_points) + + # Localize data to location + dat_amp, dat_phase = retrieve_envelope_from_file(fname, frequency_array=10**log_freq_spline_locations) + # Create random spline realizations (prior draws -- same RNG order as always) + amp_rand_array, phase_rand_array = _draw_amp_phase_nodes(dat_amp, dat_phase, n_spline_points, n_realizations) + + # Create realizations (complex-valued array for TWO_SIDED system + return build_realizations_from_nodes(amp_rand_array, phase_rand_array, T_segment, dT, + fmin, fmax, log_freq_spline_locations) + + +def draw_prior_realizations_with_nodes(env_dir, dets, T_segment, dT, fmin, fmax, + n_spline_points, n_realizations, + fmin_ifo=None, rng=None): + """Draw PRIOR calibration realizations AND keep the spline-node draws. + + Same prior as create_realizations(), but it returns the node vectors (which + create_realizations discards) so a pilot can fit a proposal over them. Used by + the ILE --calibration-dump-responsibilities path. + + env_dir : directory of per-detector envelope files .txt. + dets : detector order; the returned node vector is concatenated per det as + [det0_amp, det0_phase, det1_amp, det1_phase, ...] (breadcrumb layout). + + Returns dict with: + realizations : {ifo: (npts_seg, n_realizations) complex} + nodes : (n_realizations, 2*n_spline_points*len(dets)) prior draws + prior_mean : (dim,) diagonal-Gaussian prior mean over the full node vector + prior_sigma : (dim,) prior sigma + node_log_f : (n_spline_points,) log10 spline node frequencies (det 0) + n_nodes_amp : n_spline_points + dets : list + """ + import os + if rng is None: + rng = np.random.default_rng() + priors = [] + for ifo in dets: + fmin_here = fmin + if fmin_ifo and ifo in fmin_ifo: + fmin_here = fmin_ifo[ifo] + priors.append(node_prior(os.path.join(env_dir, ifo + ".txt"), fmin_here, fmax, n_spline_points)) + prior_mean = np.concatenate([p["mean"] for p in priors]) + prior_sigma = np.concatenate([p["sigma"] for p in priors]) + dim = prior_mean.shape[0] + # diagonal-Gaussian prior draws over the full node vector + nodes = prior_mean[None, :] + prior_sigma[None, :] * rng.standard_normal((n_realizations, dim)) + + n_amp = int(n_spline_points) + dim_per_det = 2 * n_amp + realizations = {} + for i_det, ifo in enumerate(dets): + fmin_here = fmin + if fmin_ifo and ifo in fmin_ifo: + fmin_here = fmin_ifo[ifo] + log_freq_spline_locations = np.linspace(np.log10(fmin_here), np.log10(fmax), n_spline_points) + block = nodes[:, i_det*dim_per_det:(i_det+1)*dim_per_det] + realizations[ifo] = build_realizations_from_nodes( + block[:, :n_amp].T, block[:, n_amp:].T, T_segment, dT, fmin_here, fmax, + log_freq_spline_locations) + return dict(realizations=realizations, nodes=nodes, prior_mean=prior_mean, + prior_sigma=prior_sigma, node_log_f=priors[0]["node_log_f"], + n_nodes_amp=n_amp, dets=list(dets)) + + +def seed_realizations_from_breadcrumb(bc, T_segment, dT, fmin, fmax, n_spline_points, + n_realizations, fmin_ifo=None, rng=None): + """Draw n_realizations calibration factors per detector from a LEARNED Gaussian + proposal (a breadcrumb), and return the Phase-0 importance weights. + + This is the production seed path (Option C): instead of drawing cal nodes from + the broad prior, draw them from the consolidated pilot proposal (concentrated on + the high-likelihood cal region), and carry log_w = log prior - log proposal so the + marginalization stays unbiased. + + bc : breadcrumb dict (breadcrumbs.load(...)) OR its ["cal"] sub-dict. The proposal + is a joint Gaussian over the FULL multi-detector node vector, concatenated over + bc["dets"] in order: [det0_amp, det0_phase, det1_amp, det1_phase, ...]. + fmin : scalar template fmin (used for all dets) OR ignored per-det if fmin_ifo given. + fmin_ifo : optional {ifo: fmin} for per-detector low-frequency cutoffs. + rng : numpy Generator (default: a fresh default_rng()). + + Returns (dat_out_dict, cal_log_weights, nodes): + dat_out_dict : {ifo: (npts_seg, n_realizations) complex} + cal_log_weights: (n_realizations,) = log prior - log proposal (shared across dets, + since it is ONE joint draw per realization). + nodes : (n_realizations, dim) the proposal-drawn node vectors (so a pilot + can refit a proposal from a seeded run). + """ + from RIFT.calmarg import adaptive + cal = bc["cal"] if (isinstance(bc, dict) and "cal" in bc) else bc + if rng is None: + rng = np.random.default_rng() + mean = np.asarray(cal["proposal_mean"], dtype=float) + cov = np.asarray(cal["proposal_cov"], dtype=float) + prior_mean = np.asarray(cal["prior_mean"], dtype=float) + prior_sigma = np.asarray(cal["prior_sigma"], dtype=float) + dets = list(cal["dets"]) + n_amp = int(cal["n_nodes_amp"]) + dim_per_det = 2 * n_amp + assert mean.shape == (dim_per_det * len(dets),), \ + "breadcrumb proposal dim %s != 2*n_nodes_amp*len(dets)=%d" % (mean.shape, dim_per_det*len(dets)) + + # Draw the full multi-detector node vector from the proposal; importance weights. + nodes = rng.multivariate_normal(mean, cov, size=n_realizations) # (n_real, dim) + log_q = adaptive._mvn_logpdf(nodes, mean, cov) + log_p = adaptive.log_prior(nodes, prior_mean, prior_sigma) + cal_log_weights = log_p - log_q + + dat_out_dict = {} + for i_det, ifo in enumerate(dets): + fmin_here = fmin + if fmin_ifo and ifo in fmin_ifo: + fmin_here = fmin_ifo[ifo] + log_freq_spline_locations = np.linspace(np.log10(fmin_here), np.log10(fmax), n_spline_points) + block = nodes[:, i_det*dim_per_det:(i_det+1)*dim_per_det] # (n_real, 2N) + amp_rand_array = block[:, :n_amp].T # (N, n_real) + phase_rand_array = block[:, n_amp:].T + dat_out_dict[ifo] = build_realizations_from_nodes( + amp_rand_array, phase_rand_array, T_segment, dT, fmin_here, fmax, + log_freq_spline_locations) + return dat_out_dict, cal_log_weights, nodes + + if __name__ == "__main__": from matplotlib import pyplot as plt diff --git a/MonteCarloMarginalizeCode/Code/RIFT/calmarg/pilot.py b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/pilot.py new file mode 100644 index 000000000..c6cbb5149 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/pilot.py @@ -0,0 +1,167 @@ +""" +Calibration pilot / brute-force reference (Options A + C in DESIGN_adaptive_driver.md). + + A (brute-force reference, the ONLY validation): marginalize cal with a large PRIOR set, + converged. Slow, ground truth. `brute_force_logZcal`. + C (production pilot): harvest the top-fraction high-lnL points from the previous + iteration's *.composite, do full cal there, fit a Gaussian proposal, and seed the + next iteration's cal realizations with importance weights. `harvest_high_L`, + `fit_pilot_proposal`, `consolidate`, `seed_cal`. + +The actual DAG wiring (pilot_N || wide_N, consolidation barrier, pilot_N -> wide_{N+1}, +the iteration cap) lives in the pipeline builder; it is STUBBED here with TODOs so the +plan is remembered. The numeric core (fit, seed, brute-vs-seeded agreement) is real and +tested below. +""" +from __future__ import division + +import numpy as np +from scipy.special import logsumexp + +from RIFT.calmarg import adaptive, breadcrumbs + + +# --------------------------------------------------------------------------- +# C: harvest pilot points from a previous iteration's composite +# --------------------------------------------------------------------------- +def harvest_high_L(composite_path, top_fraction=0.05, lnL_col="lnL", max_points=512): + """Return the indices+rows of the top `top_fraction` of evaluated points by lnL from + a RIFT *.composite file (whitespace, named header). These are the pilot points where + we will do full calibration (the cal posterior is ~the same across the high-L region, + so a handful suffice).""" + arr = np.atleast_2d(np.genfromtxt(composite_path, names=True)) + names = arr.dtype.names + col = lnL_col if lnL_col in names else _guess_lnL_col(names) + lnL = arr[col] + n_keep = max(1, int(np.ceil(len(lnL) * top_fraction))) + if max_points: + n_keep = min(n_keep, max_points) + order = np.argsort(lnL)[::-1][:n_keep] + return order, arr[order] + + +def _guess_lnL_col(names): + for cand in ("lnL", "lnL_raw", "loglikelihood", "log_likelihood"): + if cand in names: + return cand + raise KeyError("no lnL-like column in composite; columns=%s" % (names,)) + + +# --------------------------------------------------------------------------- +# A: brute-force reference (prior-only, large n_cal) +# --------------------------------------------------------------------------- +def brute_force_logZcal(log_L): + """Self-normalized cal-marginalized log-likelihood from a LARGE PRIOR cal set: + log Z_cal = logmeanexp(log_L) (importance weights are uniform for prior draws). + Returns (logZ, neff).""" + log_L = np.asarray(log_L) + logZ = logsumexp(log_L) - np.log(len(log_L)) + neff = adaptive.neff_from_logweights(log_L) + return float(logZ), neff + + +# --------------------------------------------------------------------------- +# C: fit a pilot proposal, seed the next run, consolidate breadcrumbs +# --------------------------------------------------------------------------- +def fit_pilot_proposal(nodes, log_resp, prior_mean, prior_sigma, node_log_f, + n_nodes_amp, dets, beta=1.0, meta=None): + """Fit a Gaussian cal proposal from pilot evaluations and package it as a breadcrumb + `cal` dict. `log_resp` = posterior responsibility (log_w + log integral L) per + realization, averaged/accumulated over the harvested pilot points by the caller.""" + mean, cov = adaptive.fit_proposal(nodes, log_resp, beta) + return dict(proposal_mean=mean, proposal_cov=cov, + prior_mean=np.asarray(prior_mean), prior_sigma=np.asarray(prior_sigma), + node_log_f=np.asarray(node_log_f), n_nodes_amp=int(n_nodes_amp), + dets=list(dets)) + + +def seed_cal(cal_proposal, n_cal, rng=None): + """Draw `n_cal` cal node vectors from the learned proposal and return + (nodes, log_weights) where log_weights = log prior - log proposal (Phase 0 importance + weights for the marginalization). Feed nodes through + adaptive.nodes_to_cal_factors(...) per detector to get the actual cal factors.""" + rng = rng or np.random.default_rng() + mean = np.asarray(cal_proposal["proposal_mean"]) + cov = np.asarray(cal_proposal["proposal_cov"]) + nodes = rng.multivariate_normal(mean, cov, size=n_cal) + log_q = adaptive._mvn_logpdf(nodes, mean, cov) + log_p = adaptive.log_prior(nodes, np.asarray(cal_proposal["prior_mean"]), + np.asarray(cal_proposal["prior_sigma"])) + return nodes, (log_p - log_q) + + +def consolidate(breadcrumb_paths, out_path=None): + """Combine cal proposals from several pilot breadcrumbs into one (the consolidation + job between iteration N and N+1). Gaussian case: precision-weighted combination + (a moment-matched product/average of the per-pilot Gaussians).""" + cals = [breadcrumbs.load(p)["cal"] for p in breadcrumb_paths] + cals = [c for c in cals if c is not None] + if not cals: + raise ValueError("no cal proposals to consolidate") + # precision-weighted mean, average covariance (robust, simple) + Ps = [np.linalg.inv(c["proposal_cov"]) for c in cals] + P = np.sum(Ps, axis=0) + cov = np.linalg.inv(P) + mean = cov @ np.sum([Pi @ c["proposal_mean"] for Pi, c in zip(Ps, cals)], axis=0) + out = dict(cals[0]); out["proposal_mean"] = mean; out["proposal_cov"] = cov + if out_path: + breadcrumbs.save(out_path, cal=out, meta=dict(consolidated_from=len(cals))) + return out + + +# --------------------------------------------------------------------------- +# DAG job stubs (Option C pipeline wiring -- TODO; see DESIGN_adaptive_driver.md) +# --------------------------------------------------------------------------- +def pilot_job(prev_composite, data_args, out_breadcrumb, top_fraction=0.05, n_cal_full=1000): + """STUB. pilot_N: harvest top-fraction points from prev_composite, run FULL cal at + each (large prior n_cal, parallel), fit the proposal, write a breadcrumb. + TODO: wire to the ILE precompute/likelihood to get per-point per-realization lnL.""" + raise NotImplementedError("pilot_job: pipeline wiring TODO (see DESIGN_adaptive_driver.md)") + + +def consolidation_job(pilot_breadcrumbs, out_breadcrumb): + """consolidation_N: collect pilot breadcrumbs -> one consolidated proposal that seeds + wide_{N+1}. (The numeric core is `consolidate` above.)""" + return consolidate(pilot_breadcrumbs, out_path=out_breadcrumb) + + +# --------------------------------------------------------------------------- +# A-vs-C validation: brute force == pilot-seeded on Z_cal, at far higher efficiency +# --------------------------------------------------------------------------- +if __name__ == "__main__": + rng = np.random.default_rng(7) + dim = 8 + prior_mean = np.zeros(dim); prior_sigma = np.ones(dim) + true_node = prior_mean + 2.0 * prior_sigma * rng.standard_normal(dim) / np.sqrt(dim) + like_sigma = 0.4 + + def log_like(nodes): # extrinsic-marg log-like proxy (no prior) + z = (nodes - true_node) / like_sigma + return -0.5 * np.sum(z * z, axis=1) + + # A: brute force, large PRIOR set (ground truth) + big = rng.multivariate_normal(prior_mean, np.diag(prior_sigma ** 2), size=20000) + logZ_brute, neff_brute = brute_force_logZcal(log_like(big)) + print("A brute force : logZcal=%.4f neff=%.1f / 20000" % (logZ_brute, neff_brute)) + + # C: learn the proposal (pilot), then seed a SMALL set and importance-weight + res = adaptive.adaptive_cal(log_like, prior_mean, prior_sigma, n_nodes_amp=dim // 2, + n_real=300, n_iter=6, rng=rng) + cal = dict(proposal_mean=res["proposal_mean"], proposal_cov=res["proposal_cov"], + prior_mean=prior_mean, prior_sigma=prior_sigma, + node_log_f=np.linspace(1, 3, dim // 2), n_nodes_amp=dim // 2, + dets=["H1", "L1", "V1"]) + nodes, log_w = seed_cal(cal, n_cal=300, rng=rng) + log_resp = log_w + log_like(nodes) + # UNBIASED importance estimate Z_cal = (1/M) sum_c w_c L_c, w_c = prior/proposal, + # E[w]=1 -> normalize by log(M), NOT logsumexp(log_w) (the biased self-normalized form) + logZ_seeded = logsumexp(log_resp) - np.log(len(nodes)) + neff_seeded = adaptive.neff_from_logweights(log_resp) + print("C pilot-seeded: logZcal=%.4f neff=%.1f / 300" % (logZ_seeded, neff_seeded)) + print("agreement |dlogZ| = %.4f ; efficiency gain x%.0f" + % (abs(logZ_seeded - logZ_brute), (neff_seeded / 300) / (neff_brute / 20000))) + + assert abs(logZ_seeded - logZ_brute) < 0.1, "pilot-seeded Z disagrees with brute force" + assert (neff_seeded / 300) > 20 * (neff_brute / 20000), "pilot did not improve efficiency" + print("\nPASS: pilot-seeded (C) reproduces the brute-force reference (A) at far higher " + "effective sampling.") diff --git a/MonteCarloMarginalizeCode/Code/RIFT/calmarg/test_cal_mc_error.py b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/test_cal_mc_error.py new file mode 100644 index 000000000..23461ecb8 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/test_cal_mc_error.py @@ -0,0 +1,103 @@ +"""Unit test for adaptive.cal_mc_error_from_components. + +Validates the delta-method calibration MC error budget two ways, no GPU/lal needed: + +1. LOGNORMAL CLOSED FORM: for lnL_c ~ N(mu, s^2) iid, Var(ln Zhat) = (e^{s^2}-1)/n_cal + (to leading order). The estimator must reproduce this from a single draw set. + +2. BRUTE FORCE: redraw the cal set many times, measure the true scatter of + ln Zhat = logsumexp_c(lnL_c) - log n_cal directly, compare. + +Also checks the importance-weighted (cal_log_weights) path is unbiased, and that the +extrinsic batch weighting reduces to the same answer when responsibilities are +extrinsic-independent. + +Run: python -m RIFT.calmarg.test_cal_mc_error +""" +import numpy as np +from scipy.special import logsumexp + +from RIFT.calmarg.adaptive import cal_mc_error_from_components + + +def _true_scatter(sig, n_cal, trials, rng): + x = rng.normal(0.0, sig, size=(trials, n_cal)) + return np.std(logsumexp(x, axis=1) - np.log(n_cal)) + + +def test_lognormal_closed_form(): + rng = np.random.default_rng(7) + for sig in [0.3, 0.7, 1.0]: + n_cal = 400 + analytic = np.sqrt((np.exp(sig ** 2) - 1.0) / n_cal) + # average the estimator over independent draw sets (the estimator is itself + # a one-draw-set statistic, so compare in expectation) + est = [] + for _ in range(200): + comp = rng.normal(0.0, sig, size=(1, n_cal)) # 1 extrinsic sample suffices + s, neff, a = cal_mc_error_from_components(comp) + est.append(s) + assert abs(a.sum() - 1.0) < 1e-12 + est = np.mean(est) + assert abs(est - analytic) / analytic < 0.15, (sig, est, analytic) + print("test_lognormal_closed_form: OK") + + +def test_brute_force_scatter(): + rng = np.random.default_rng(11) + sig, n_cal = 1.2, 100 + truth = _true_scatter(sig, n_cal, 20000, rng) + est = np.mean([cal_mc_error_from_components(rng.normal(0, sig, (1, n_cal)))[0] + for _ in range(300)]) + # delta method degrades as neff_cal drops; require agreement within 30% + assert abs(est - truth) / truth < 0.30, (est, truth) + print("test_brute_force_scatter: OK (true {:.3f}, est {:.3f})".format(truth, est)) + + +def test_neff_dominated(): + # one realization dominating -> neff ~ 1 and a loud (lower-bound) sigma + comp = np.full((4, 50), -100.0) + comp[:, 3] = 0.0 + s, neff, a = cal_mc_error_from_components(comp) + assert neff < 1.5 + assert np.argmax(a) == 3 + print("test_neff_dominated: OK (neff {:.2f})".format(neff)) + + +def test_importance_weights_consistency(): + # drawing from a proposal with weights must agree with prior draws in expectation + rng = np.random.default_rng(3) + n_cal, sig = 800, 0.8 + # prior draws + s_prior = np.mean([cal_mc_error_from_components(rng.normal(0, sig, (1, n_cal)))[0] + for _ in range(100)]) + # 'proposal' = prior here, with identically zero log-weights: must match exactly in law + s_w = np.mean([cal_mc_error_from_components(rng.normal(0, sig, (1, n_cal)), + cal_log_weights=np.zeros(n_cal))[0] + for _ in range(100)]) + assert abs(s_prior - s_w) / s_prior < 0.2 + print("test_importance_weights_consistency: OK") + + +def test_extrinsic_batch_weighting(): + # responsibilities ~extrinsic-independent: a batch with a common per-sample offset + # (the extrinsic-dependent part) must give the same answer as a single sample. + rng = np.random.default_rng(5) + n_cal = 200 + base = rng.normal(0, 1.0, n_cal) + offsets = rng.normal(0, 5.0, 64) # huge extrinsic spread + comp = offsets[:, None] + base[None, :] + s_batch, neff_b, _ = cal_mc_error_from_components(comp) + s_one, neff_1, _ = cal_mc_error_from_components(base[None, :]) + assert abs(s_batch - s_one) < 1e-10 + assert abs(neff_b - neff_1) < 1e-8 + print("test_extrinsic_batch_weighting: OK") + + +if __name__ == "__main__": + test_lognormal_closed_form() + test_brute_force_scatter() + test_neff_dominated() + test_importance_weights_consistency() + test_extrinsic_batch_weighting() + print("ALL OK") diff --git a/MonteCarloMarginalizeCode/Code/RIFT/calmarg/test_calmarg_reduction.py b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/test_calmarg_reduction.py new file mode 100644 index 000000000..82b4de396 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/test_calmarg_reduction.py @@ -0,0 +1,100 @@ +""" +Validate the n_cal>1 calibration-marginalization reduction in +DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop against a brute-force reference: +running the (unchanged) n_cal==1 path on each realization block separately and +combining with lnL = logsumexp_c(lnL_c) - log(n_cal). + +Runs entirely on CPU (xpy=np), so no GPU required. +""" +import numpy as np +import lal +from scipy.special import logsumexp +import RIFT.likelihood.factored_likelihood as fl + +rng = np.random.default_rng(1234) + +det = "H1" +n_lms = 2 +N_window = 256 # per-realization buffer length (must exceed sky time-delay spread + npts) +npts = 16 # integration sub-window (len(tvals)) +n_cal = 5 +npts_extrinsic = 6 +deltaT = 1.0 / 4096 + +# lookup table of (l,m) pairs +lookupNKDict = {det: np.array([[2, 2], [2, -2]], dtype=int)} + +# concatenated rholm timeseries: (n_lms, N_window*n_cal); block c is realization c +npts_full = N_window * n_cal +rho = (rng.standard_normal((n_lms, npts_full)) + 1j*rng.standard_normal((n_lms, npts_full))) +rholmsArrayDict = {det: rho} + +# template-template cross terms (Hermitian-ish; values don't matter for the identity) +U = (rng.standard_normal((n_lms, n_lms)) + 1j*rng.standard_normal((n_lms, n_lms))) +U = U + U.conj().T +V = (rng.standard_normal((n_lms, n_lms)) + 1j*rng.standard_normal((n_lms, n_lms))) +ctUArrayDict = {det: U} +ctVArrayDict = {det: V} + +epochDict = {det: 0.0} + +# extrinsic parameter vector (mock P_vec) +class PV: pass +P = PV() +P.phi = rng.uniform(0, 2*np.pi, npts_extrinsic) +P.theta = rng.uniform(0.2, np.pi-0.2, npts_extrinsic) +P.psi = rng.uniform(0, np.pi, npts_extrinsic) +P.incl = rng.uniform(0.2, np.pi-0.2, npts_extrinsic) +P.phiref = rng.uniform(0, 2*np.pi, npts_extrinsic) +P.dist = np.full(npts_extrinsic, 500.0) * (lal.PC_SI*1e6) # 500 Mpc +P.tref = 1000000000.0 +P.deltaT = deltaT +# Place the integration window near the middle of the buffer so ifirst stays in +# [0, N_window-npts] for all sky positions (TimeDelayFromEarthCenter is +-0.021s). +epochDict[det] = P.tref - 0.03 + +tvals = np.linspace(-npts//2*deltaT, npts//2*deltaT, npts) + +# --- reference: per-block n_cal==1 evaluations, combined by hand --- +lnL_blocks = np.zeros((n_cal, npts_extrinsic)) +for c in range(n_cal): + block = rho[:, c*N_window:(c+1)*N_window].copy() + lnL_blocks[c] = fl.DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop( + tvals, P, lookupNKDict, {det: block}, ctUArrayDict, ctVArrayDict, epochDict, + Lmax=2, xpy=np, n_cal=1) +lnL_ref = logsumexp(lnL_blocks, axis=0) - np.log(n_cal) + +# --- new path: single call with n_cal>1 --- +lnL_new = fl.DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop( + tvals, P, lookupNKDict, rholmsArrayDict, ctUArrayDict, ctVArrayDict, epochDict, + Lmax=2, xpy=np, n_cal=n_cal) + +print("reference lnL :", np.array(lnL_ref)) +print("in-loop lnL :", np.array(lnL_new)) +maxerr = np.max(np.abs(np.array(lnL_new) - np.array(lnL_ref))) +print("max abs error :", maxerr) +assert maxerr < 1e-9, "MISMATCH: cal-marg reduction != brute-force reference" + +# --- return_cal_components: raw per-realization integrated log L, (npts_extrinsic, n_cal) --- +# Collapsing it by hand must reproduce the cal-marg lnL: +# lnL_marg(i) = logsumexp_c comp[i,c] - log(n_cal) (uniform weights) +comp = fl.DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop( + tvals, P, lookupNKDict, rholmsArrayDict, ctUArrayDict, ctVArrayDict, epochDict, + Lmax=2, xpy=np, n_cal=n_cal, return_cal_components=True) +comp = np.array(comp) +assert comp.shape == (npts_extrinsic, n_cal), "cal_components shape %s" % (comp.shape,) +# each column must equal the per-block n_cal==1 evaluation (raw, no weight) +assert np.max(np.abs(comp.T - lnL_blocks)) < 1e-9, "cal_components != per-block reference" +lnL_from_comp = logsumexp(comp, axis=1) - np.log(n_cal) +assert np.max(np.abs(lnL_from_comp - np.array(lnL_new))) < 1e-9, \ + "collapse of cal_components != cal-marg lnL" +print("cal_components check: max err vs blocks = %.2e ; collapse matches lnL" % + np.max(np.abs(comp.T - lnL_blocks))) + +# --- also confirm n_cal==1 on the full concat == block-0 evaluation (regression) --- +lnL_n1_full = fl.DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop( + tvals, P, lookupNKDict, {det: rho[:, :N_window].copy()}, ctUArrayDict, ctVArrayDict, + epochDict, Lmax=2, xpy=np, n_cal=1) +assert np.allclose(np.array(lnL_n1_full), np.array(lnL_blocks[0])), "block-0 regression failed" + +print("\nPASS: in-loop calibration marginalization matches brute-force reference.") diff --git a/MonteCarloMarginalizeCode/Code/RIFT/calmarg/test_precompute_alignment.py b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/test_precompute_alignment.py new file mode 100644 index 000000000..8439571bd --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/RIFT/calmarg/test_precompute_alignment.py @@ -0,0 +1,82 @@ +""" +Regression test for the calibration-marginalization PRECOMPUTE time alignment. + +This exercises the real PrecomputeLikelihoodTerms / ComputeModeIPTimeSeries path +(unlike backtest_calmarg.py / test_calmarg_reduction.py, which feed synthetic rholms +with a hand-set epoch and therefore cannot catch a precompute-alignment bug). + +With the calibration factor set to 1 (identity), the calibration-marginalized rholm +series must, block by block, reproduce the non-calibration rholm series -- same data AND +the same epoch. A wrong epoch on the concatenated series (the bug fixed in this branch) +shifts ifirst into the wrong realization block downstream, the signal is zeroed, and the +calmarg likelihood collapses. The epoch check below is the one that fails on that bug. + +Runs on CPU (no GPU needed). +""" +import numpy as np +import lal +import lalsimulation as lalsim + +import RIFT.lalsimutils as lalsimutils +import RIFT.likelihood.factored_likelihood as fl + +fSample = 4096.0 +fmin = 30.0 +fmax = 1700.0 +event_time = 1000000000.0 +t_window = 0.1 + +# A short BBH so the test is fast. +Psig = lalsimutils.ChooseWaveformParams( + fmin=fmin, radec=True, incl=0.0, phiref=0.0, theta=0.2, phi=0.0, psi=0.0, + m1=30 * lal.MSUN_SI, m2=30 * lal.MSUN_SI, + detector='H1', dist=200e6 * lal.PC_SI, deltaT=1. / fSample, + tref=event_time, deltaF=1. / 4.) + +data_dict = {} +for det in ("H1", "L1", "V1"): + P = Psig.manual_copy(); P.detector = det + data_dict[det] = lalsimutils.non_herm_hoff(P) +psd_dict = {det: lalsim.SimNoisePSDaLIGOZeroDetHighPower for det in data_dict} + +Lmax = 2 +n_cal = 5 + +# baseline (no calibration marginalization) +rholms_intp_b, ct_b, ctV_b, rholms_base, snr_b, _ = fl.PrecomputeLikelihoodTerms( + event_time, t_window, Psig, data_dict, psd_dict, Lmax, fmax, + analyticPSD_Q=True, verbose=False, quiet=True, skip_interpolation=True) + +# calibration marginalization with the IDENTITY calibration (factor == 1) +cal_real = {det: np.ones((data_dict[det].data.length, n_cal), dtype=complex) + for det in data_dict} +rholms_intp_c, ct_c, ctV_c, rholms_cal, snr_c, _ = fl.PrecomputeLikelihoodTerms( + event_time, t_window, Psig, data_dict, psd_dict, Lmax, fmax, + analyticPSD_Q=True, verbose=False, quiet=True, skip_interpolation=True, + calibration_realizations=cal_real) + +ok = True +for det in data_dict: + for pair in rholms_base[det]: + base = rholms_base[det][pair] + cal = rholms_cal[det][pair] + N_window = base.data.length + # (1) concatenated length is n_cal blocks + assert cal.data.length == N_window * n_cal, \ + "%s %s: cal length %d != %d*%d" % (det, pair, cal.data.length, N_window, n_cal) + # (2) EPOCH must match the non-calibration series (the alignment bug) + d_epoch = abs(float(cal.epoch) - float(base.epoch)) + # (3) every block must reproduce the baseline rholm (cal factor == 1) + block_err = 0.0 + for c in range(n_cal): + blk = cal.data.data[c * N_window:(c + 1) * N_window] + block_err = max(block_err, float(np.max(np.abs(blk - base.data.data)))) + flag_e = "OK" if d_epoch < 1e-9 else "**EPOCH MISMATCH**" + flag_b = "OK" if block_err < 1e-6 else "**BLOCK MISMATCH**" + print("%s %s : |delta epoch|=%.3e %s max|block-baseline|=%.3e %s" % ( + det, pair, d_epoch, flag_e, block_err, flag_b)) + if d_epoch >= 1e-9 or block_err >= 1e-6: + ok = False + +assert ok, "calmarg precompute alignment MISMATCH (epoch and/or block data)" +print("\nPASS: calmarg precompute is time-aligned with the baseline (epoch + per-block data).") diff --git a/MonteCarloMarginalizeCode/Code/RIFT/hyperpipe/config.py b/MonteCarloMarginalizeCode/Code/RIFT/hyperpipe/config.py index 171852982..f9e067032 100644 --- a/MonteCarloMarginalizeCode/Code/RIFT/hyperpipe/config.py +++ b/MonteCarloMarginalizeCode/Code/RIFT/hyperpipe/config.py @@ -208,12 +208,24 @@ def validate_config(cfg) -> None: if not isinstance(_get(arch, "n-samples-per-job"), int) or _get(arch, "n-samples-per-job") <= 0: raise ValueError("arch.n-samples-per-job must be a positive integer.") - # post: at least one of (coords-fit) must be set + # post: must have at least one fit dim AND at least one MC sampling dim. + # Fit basis = coords-fit + coords-implied; MC basis = coords-fit + coords-nofit. + # Pre-decoupling this only required coords-fit, because the fit basis was + # forced to equal the MC basis -- now an EOS-style "fit in a transformed + # basis" config can legally have empty coords-fit (everything routed via + # coords-implied + coords-nofit through the coordinate plugin). post = _get(cfg, "post") - if not _get(post, "coords-fit"): + has_fit = bool(_get(post, "coords-fit")) or bool(_get(post, "coords-implied")) + has_samp = bool(_get(post, "coords-fit")) or bool(_get(post, "coords-nofit")) + if not has_fit: raise ValueError( - "post.coords-fit must list at least one parameter " - "(e.g. 'x y z')." + "post: must list at least one fit dimension " + "(coords-fit or coords-implied; e.g. 'x y z' or 'u v w')." + ) + if not has_samp: + raise ValueError( + "post: must list at least one MC sampling dimension " + "(coords-fit or coords-nofit; e.g. 'x y z')." ) # init: must have either file or generation set diff --git a/MonteCarloMarginalizeCode/Code/RIFT/hyperpipe/coords.py b/MonteCarloMarginalizeCode/Code/RIFT/hyperpipe/coords.py index bc039846a..ccd6b18d7 100644 --- a/MonteCarloMarginalizeCode/Code/RIFT/hyperpipe/coords.py +++ b/MonteCarloMarginalizeCode/Code/RIFT/hyperpipe/coords.py @@ -177,12 +177,22 @@ def from_strings( ``coords_nofit`` : "delta_mc s1z s2z" (optional) ``likelihood_factor``: (module, function, ini) (any element may be None) """ - params = parse_parameter_list(coords_fit) - ranges = parse_range_string(coords_sample) - unknown = set(ranges) - set(params) + params = parse_parameter_list(coords_fit) + implied = parse_parameter_list(coords_implied) + nofit = parse_parameter_list(coords_nofit) + ranges = parse_range_string(coords_sample) + # coords-sample provides INTEGRATION ranges, so it has to cover every + # name in the MC SAMPLING basis -- which is coords-fit + coords-nofit. + # (implied names are fit-only and don't need a sample range.) Pre- + # decoupling this was just coords-fit because nofit/implied were + # rarely used and the sampling basis was forced to equal the fit + # basis; now we have to allow the nofit names too. + sampling_basis = set(params) | set(nofit) + unknown = set(ranges) - sampling_basis if unknown: raise ValueError( - f"coords-sample names a parameter not in coords-fit: {sorted(unknown)!r}" + f"coords-sample names a parameter not in coords-fit or " + f"coords-nofit: {sorted(unknown)!r}" ) lf: Optional[Tuple[str, Optional[str], Optional[str]]] = None if likelihood_factor: @@ -195,8 +205,8 @@ def from_strings( name=name or None, parameters=params, parameter_ranges=ranges, - implied=parse_parameter_list(coords_implied), - nofit=parse_parameter_list(coords_nofit), + implied=implied, + nofit=nofit, likelihood_factor=lf, ) @@ -210,13 +220,30 @@ def validate(self, strict_import: bool = False) -> None: environment (singularity image, OSG worker) and not necessarily on the submit host. """ - if not self.parameters: - raise ValueError("HyperCoordSpec requires at least one fitting parameter.") - missing = [p for p in self.parameters if p not in self.parameter_ranges] + # The fit basis is coords-fit + coords-implied; the sampling basis is + # coords-fit + coords-nofit. Both must be non-empty for the run to + # make sense. Pre-decoupling this only required coords-fit -- now + # an "EOS-style fit in a transformed basis" config can legally have + # empty coords-fit (everything goes through implied + nofit). + if not self.parameters and not self.implied: + raise ValueError( + "HyperCoordSpec requires at least one fit dimension " + "(coords-fit or coords-implied)." + ) + if not self.parameters and not self.nofit: + raise ValueError( + "HyperCoordSpec requires at least one MC sampling dimension " + "(coords-fit or coords-nofit)." + ) + # Every name in the SAMPLING basis must have an integration range + # (the integrator reads prior_range_map[p] for p in low_level_coord_names). + sampling_names = list(self.parameters) + list(self.nofit) + missing = [p for p in sampling_names if p not in self.parameter_ranges] if missing: raise ValueError( - f"No integration range supplied for parameter(s): {missing!r}. " - "Every entry in coords-fit must appear in coords-sample." + f"No integration range supplied for sampling parameter(s): " + f"{missing!r}. Every entry in coords-fit and coords-nofit must " + "appear in coords-sample." ) for p, (lo, hi) in self.parameter_ranges.items(): if not lo < hi: @@ -266,7 +293,9 @@ def to_parameter_args(self) -> str: bits.append(f"--parameter-implied {p}") for p in self.nofit: bits.append(f"--parameter-nofit {p}") - for p in self.parameters: + # Integration ranges cover the MC SAMPLING basis (parameters + nofit). + # Implied coordinates are fit-only and don't have a sampling range. + for p in list(self.parameters) + list(self.nofit): lo, hi = self.parameter_ranges[p] bits.append( f"--integration-parameter-range {p}:[{self._fmt_num(lo)},{self._fmt_num(hi)}]" @@ -290,12 +319,16 @@ def to_post_args(self) -> str: def to_puff_args(self, force_away: float = 0.03, puff_factor: float = 0.5) -> str: """Emit the puff-stage arg block. - By default we puff in every fitting parameter; this is what every - existing hyperpipe example does. Extra flags can be appended by the - caller. + The puff lane reads / writes grid files in the data-file column + basis, which is the MC sampling basis (coords-fit + coords-nofit). + Pre-decoupling this only emitted --parameter for coords-fit because + the sampling basis was forced to equal the fit basis; once those + diverge (EOSPosterior with --parameter-implied for a transformed + fit basis), the puff lane must continue to operate on the data- + file columns -- i.e. coords-fit + coords-nofit. """ bits = [f"--force-away {force_away}", f"--puff-factor {puff_factor}"] - for p in self.parameters: + for p in list(self.parameters) + list(self.nofit): bits.append(f"--parameter {p}") return " ".join(bits) @@ -304,8 +337,12 @@ def to_test_args(self, method: str = "JS", threshold: float = 0.05) -> str: Mirrors the args_test.txt pattern from the Gaussian demo: ``--parameter x --parameter y --parameter z --method JS --threshold 0.05`` + + Like the puff lane, this operates on the SAMPLING basis (coords-fit + + coords-nofit) -- the convergence-test driver reads grid / posterior + files whose columns are in the sampling basis. """ - bits = [f"--parameter {p}" for p in self.parameters] + bits = [f"--parameter {p}" for p in list(self.parameters) + list(self.nofit)] bits.append(f"--method {method}") bits.append(f"--threshold {threshold}") return " ".join(bits) diff --git a/MonteCarloMarginalizeCode/Code/RIFT/integrators/MonteCarloEnsemble.py b/MonteCarloMarginalizeCode/Code/RIFT/integrators/MonteCarloEnsemble.py old mode 100644 new mode 100755 index d86227d54..133661090 --- a/MonteCarloMarginalizeCode/Code/RIFT/integrators/MonteCarloEnsemble.py +++ b/MonteCarloMarginalizeCode/Code/RIFT/integrators/MonteCarloEnsemble.py @@ -11,8 +11,42 @@ import time from scipy.special import logsumexp +try: + import cupy + import cupyx.scipy.special + xpy_default = cupy + xpy_special_default = cupyx.scipy.special + identity_convert = cupy.asnumpy + identity_convert_togpu = cupy.asarray + cupy_ok = True +except ImportError: + xpy_default = np + xpy_special_default = None + identity_convert = lambda x: x + identity_convert_togpu = lambda x: x + cupy_ok = False + regularize_log_scale = 1e-64 # before taking np.log, add this, so we don't propagate infinities + +def _xpy_logsumexp(a, axis=None): + """Portable logsumexp (mirror of gaussian_mixture_model._xpy_logsumexp). + + cupyx.scipy.special.logsumexp is absent in the CUDA 10.2 cupy build needed + for older (sm_30) GPUs, so implement the reduction with cupy primitives and + fall back to scipy on CPU. + """ + if cupy_ok: + a = cupy.asarray(a) + a_max = cupy.amax(a, axis=axis, keepdims=True) + a_max = cupy.where(cupy.isfinite(a_max), a_max, cupy.zeros_like(a_max)) + out = cupy.log(cupy.sum(cupy.exp(a - a_max), axis=axis, keepdims=True)) + a_max + if axis is None: + return out.reshape(()) + return cupy.squeeze(out, axis=axis) + return logsumexp(a, axis=axis) + + try: from multiprocess import Pool except: @@ -61,7 +95,8 @@ class integrator: ''' def __init__(self, d, bounds, gmm_dict, n_comp, n=None, prior=None, - user_func=None, proc_count=None, L_cutoff=None, use_lnL=False,return_lnI=False,gmm_adapt=None,gmm_epsilon=None,tempering_exp=1,temper_log=False,lnw_failure_cut=None): + user_func=None, proc_count=None, L_cutoff=None, use_lnL=False,return_lnI=False,gmm_adapt=None,gmm_epsilon=None,tempering_exp=1,temper_log=False,lnw_failure_cut=None, + tempering_adapt=False, ess_target=None, ess_floor=None): # if 'return_lnI' is active, 'integral' holds the *logarithm* of the integral. # user-specified parameters self.d = d @@ -75,6 +110,11 @@ def __init__(self, d, bounds, gmm_dict, n_comp, n=None, prior=None, self.proc_count = proc_count self.use_lnL = use_lnL self.return_lnI = return_lnI + + self.xpy = xpy_default + self.identity_convert = identity_convert + self.identity_convert_togpu = identity_convert_togpu + # constants self.t = 0.02 # percent estimated error threshold if n is None: @@ -107,12 +147,28 @@ def __init__(self, d, bounds, gmm_dict, n_comp, n=None, prior=None, self.total_value = None self.n_max = float('inf') # saved values - self.cumulative_samples = np.empty((0, d)) - self.cumulative_values = np.empty(0) - self.cumulative_p = np.empty(0) - self.cumulative_p_s = np.empty(0) + self.cumulative_samples = self.xpy.empty((0, d)) + self.cumulative_values = self.xpy.empty(0) + self.cumulative_p = self.xpy.empty(0) + self.cumulative_p_s = self.xpy.empty(0) self.tempering_exp=tempering_exp self.temper_log=temper_log + # --- ESS-based tempering self-protection / self-tuning ------------- + # tempering_adapt: choose the refit exponent each chunk so the + # effective sample size of the refit weights hits ess_target + # (the user exponent becomes a cap, not a requirement). + # Always on (any settings): if the user exponent would leave the + # refit with ESS < ess_floor, the exponent is clamped down for that + # refit only. The evidence integral never uses these weights. + self.tempering_adapt = tempering_adapt + # largest number of mixture components in any group (for the floor) + if isinstance(n_comp, dict): + _k_max = max([v for v in n_comp.values()]) if len(n_comp)>0 else 1 + else: + _k_max = n_comp if n_comp else 1 + self.ess_floor = ess_floor if ess_floor else max(10.0, 2.0*_k_max) + self.ess_target = ess_target if ess_target else max(50.0, 0.05*self.n) + self.tempering_exp_running = tempering_exp # last exponent actually used if L_cutoff is None: self.L_cutoff = -1 else: @@ -120,73 +176,167 @@ def __init__(self, d, bounds, gmm_dict, n_comp, n=None, prior=None, def _calculate_prior(self): if self.prior is None: - self.prior_array = np.ones(self.n) + self.prior_array = self.xpy.ones(self.n) else: self.prior_array = self.prior(self.sample_array).flatten() def _sample(self): - self.sampling_prior_array = np.ones(self.n) - self.sample_array = np.empty((self.n, self.d)) + self.sampling_prior_array = self.xpy.ones(self.n) + self.sample_array = self.xpy.empty((self.n, self.d)) for dim_group in self.gmm_dict: # iterate over grouped dimensions # create a matrix of the left and right limits for this set of dimensions - new_bounds = np.empty((len(dim_group), 2)) + new_bounds = self.xpy.empty((len(dim_group), 2)) new_bounds = self.bounds[dim_group] if len(new_bounds.shape) < 2: - new_bounds = np.array([new_bounds]) - # index = 0 - # for dim in dim_group: - # new_bounds[index] = self.bounds[dim] - # index += 1 + new_bounds = self.xpy.array([new_bounds]) model = self.gmm_dict[dim_group] if model is None: # sample uniformly for this group of dimensions llim = new_bounds[:,0] rlim = new_bounds[:,1] - temp_samples = np.random.uniform(llim, rlim, (self.n, len(dim_group))) + temp_samples = self.xpy.random.uniform(llim, rlim, (self.n, len(dim_group))) # update responsibilities - vol = np.prod(rlim - llim) + vol = self.xpy.prod(rlim - llim) self.sampling_prior_array *= 1.0 / vol else: # sample from the gmm - temp_samples = model.sample(self.n)#, new_bounds) + temp_samples = model.sample(self.n) # update responsibilities - self.sampling_prior_array *= model.score(temp_samples)#, new_bounds) + self.sampling_prior_array *= model.score(temp_samples) index = 0 for dim in dim_group: - # put columns of temp_samples in final places in sample_array self.sample_array[:,dim] = temp_samples[:,index] index += 1 + def _log_ess(self, log_w): + """log of the Kish effective sample size of log-weights log_w.""" + return 2.*_xpy_logsumexp(log_w) - _xpy_logsumexp(2.*log_w) + + def _solve_tempering_exp(self, lnL, log_pq): + """ + Choose the tempering exponent beta for THIS refit from the effective + sample size of beta*lnL + log_pq (log_pq = ln p - ln p_s). + + - tempering_adapt: bisect so ESS(beta) ~ self.ess_target, with + beta <= beta_max = max(1, tempering_exp). As the proposal converges + the lnL spread across the cloud shrinks and beta rises automatically. + - otherwise: keep the user exponent unless ESS(user) < self.ess_floor, + in which case bisect down to the floor (pure safety net; cannot + crash the fit regardless of settings). + Returns (beta, log_ess_at_beta). + """ + ln_floor = self.xpy.log(self.ess_floor) + ln_target = self.xpy.log(self.ess_target) + beta_user = self.tempering_exp + if self.tempering_adapt: + beta_hi = max(1.0, beta_user) + ln_goal = ln_target + else: + beta_hi = beta_user + ln_goal = ln_floor + if self._log_ess(beta_user*lnL + log_pq) >= ln_floor: + return beta_user, self._log_ess(beta_user*lnL + log_pq) + # ESS is (near-)monotone decreasing in beta for peaked lnL; bisect. + if self._log_ess(beta_hi*lnL + log_pq) >= ln_goal: + return beta_hi, self._log_ess(beta_hi*lnL + log_pq) + lo, hi = 0.0, beta_hi + for _ in range(40): + mid = 0.5*(lo+hi) + if self._log_ess(mid*lnL + log_pq) >= ln_goal: + lo = mid + else: + hi = mid + return lo, self._log_ess(lo*lnL + log_pq) + def _train(self): - sample_array, value_array, sampling_prior_array = np.copy(self.sample_array), np.copy(self.value_array), np.copy(self.sampling_prior_array) + sample_array, value_array, sampling_prior_array = self.xpy.copy(self.sample_array), self.xpy.copy(self.value_array), self.xpy.copy(self.sampling_prior_array) if self.use_lnL: lnL = value_array else: - lnL = np.log(value_array+regularize_log_scale) # note we can get negative infinity here - log_weights = self.tempering_exp*lnL + np.log(self.prior_array) - sampling_prior_array + lnL = self.xpy.log(value_array+regularize_log_scale) + + # drop NaN evaluations up front (a NaN poisons every logsumexp below); + # -inf is fine (zero weight) and is kept. + prior_array = self.prior_array + mask_ok = ~self.xpy.isnan(lnL) + if not bool(self.xpy.all(mask_ok)): + sample_array = sample_array[mask_ok] + lnL = lnL[mask_ok] + sampling_prior_array = sampling_prior_array[mask_ok] + prior_array = prior_array[mask_ok] + + # replace -inf lnL (zero likelihood) by a finite very-low value: + # beta=0 would otherwise produce 0*(-inf)=NaN in the tempered weights + if not bool(self.xpy.all(self.xpy.isfinite(lnL))): + lnL_min = self.xpy.min(self.xpy.where(self.xpy.isfinite(lnL), lnL, self.xpy.inf)) + if not bool(self.xpy.isfinite(lnL_min)): + lnL_min = 0.0 + lnL = self.xpy.where(self.xpy.isfinite(lnL), lnL, lnL_min - 1000.) + + # ln p - ln p_s (NOTE: log of the sampling prior. The legacy code + # subtracted the *raw* sampling_prior_array from a log-quantity.) + log_pq = self.xpy.log(self.xpy.maximum(prior_array, 1e-300)) \ + - self.xpy.log(self.xpy.maximum(sampling_prior_array, 1e-300)) + + # ESS-protected/self-tuned tempering exponent for this refit + beta, log_ess = self._solve_tempering_exp(lnL, log_pq) + self.tempering_exp_running = beta + log_weights = beta*lnL + log_pq + adapt_mode = 'beta' + # Honest ESS of the FULL posterior weights (beta=1): how reachable the + # posterior is from the current proposal cloud. + log_ess1 = self._log_ess(lnL + log_pq) if self.temper_log: - log_weights =np.log(np.maximum(lnL,1e-5)) # simplest to do it this way + log_weights = self.xpy.log(self.xpy.maximum(lnL,1e-5)) + elif self.tempering_adapt and bool(log_ess1 < self.xpy.log(self.ess_floor)): + # BOOTSTRAP (rank-elite refit, cross-entropy-method style): while + # the posterior is out of reach of the cloud, beta-tempered + # honest weights equilibrate the proposal near the PRIOR (the + # solver keeps beta tiny while the cloud is broad, and the + # -ln p_s correction cancels incremental concentration) -- the + # proposal never localizes, exactly the eff_samp~1 stall seen in + # the ILE SNR sequence. Rank weights are lnL-scale-free and + # compound: fit the top-k samples BY lnL (prior/proposal + # corrected within the elite set), so the threshold ratchets up + # every chunk like the AV sampler's volume shrinking. Hand back + # to the honest beta-solver once ESS(beta=1) clears the floor, + # after which the refit target smoothly becomes L*p (beta->1). + k_elite = int(min(max(self.ess_target, self.ess_floor), len(lnL)//2)) + if k_elite >= 2: + gamma = self.xpy.sort(lnL)[-k_elite] + neg_inf = -self.xpy.inf*self.xpy.ones(lnL.shape) + log_weights = self.xpy.where(lnL >= gamma, log_pq, neg_inf) + adapt_mode = 'elite' + else: + # If even beta=0 leaves too few effective samples (proposal/prior + # pathologies), skip this refit: keep the current proposal rather + # than fit garbage (breadcrumb item 2). + if self.xpy.exp(log_ess) < min(self.ess_floor, self.d + 2): + print(" GMM refit skipped: ESS {:.1f} too low even untempered ".format(float(self.xpy.exp(log_ess)))) + return + if getattr(self, '_verbose_diag', False): + print(" GMM adapt[{}]: mode={} beta={:.3g} ESS_beta={:.1f} ESS_1={:.3g} max_lnL={:.1f}".format( + self.iterations, adapt_mode, float(beta), + float(self.xpy.exp(log_ess)), float(self.xpy.exp(log_ess1)), + float(self.xpy.max(lnL)))) + for dim_group in self.gmm_dict: # iterate over grouped dimensions if self.gmm_adapt: if (dim_group in self.gmm_adapt): - if not(self.gmm_adapt[dim_group]): # disabling adaptation requires user *specifically request* not to use that dimension set; all other choices lead to adaptation + if not(self.gmm_adapt[dim_group]): continue - # create a matrix of the left and right limits for this set of dimensions - new_bounds = np.empty((len(dim_group), 2)) + new_bounds = self.xpy.empty((len(dim_group), 2)) new_bounds = self.bounds[dim_group] -# index = 0 -# for dim in dim_group: -# new_bounds[index] = self.bounds[dim] -# index += 1 - model = self.gmm_dict[dim_group] # get model for this set of dimensions - temp_samples = np.empty((self.n, len(dim_group))) + if len(new_bounds.shape) < 2: + # 1-d group with flat bounds (per-dim default): GMM expects (d,2) + new_bounds = self.xpy.array([new_bounds]) + model = self.gmm_dict[dim_group] + temp_samples = self.xpy.empty((len(sample_array), len(dim_group))) index = 0 for dim in dim_group: - # get samples corresponding to the current model temp_samples[:,index] = sample_array[:,dim] index += 1 if model is None: - # model doesn't exist yet if isinstance(self.n_comp, int) and self.n_comp != 0: model = GMM.gmm(self.n_comp, new_bounds,epsilon=self.gmm_epsilon) model.fit(temp_samples, log_sample_weights=log_weights) @@ -196,7 +346,6 @@ def _train(self): else: model.update(temp_samples, log_sample_weights=log_weights) try: - # Verify model can evaluated! Quick and dirty test to confirm not singular model.score(temp_samples[:5]) self.gmm_dict[dim_group] = model except: @@ -205,125 +354,82 @@ def _train(self): def _calculate_results(self): if self.use_lnL: - lnL = np.copy(self.value_array) # changing the naming convention, just for this function, now that I know better + lnL = self.xpy.copy(self.value_array) else: - lnL = np.log(self.value_array+regularize_log_scale) - # strip off any samples with likelihoods less than our cutoff - mask = np.ones(lnL.shape,dtype=bool) - if not(self.L_cutoff is None): # if not none - if not(np.isinf(self.L_cutoff)): # and not infinite, then apply the cutoff - mask = lnL > (np.log(self.L_cutoff) if self.L_cutoff > 0 else -np.inf) -# print(mask, self.L_cutoff, lnL) + lnL = self.xpy.log(self.value_array+regularize_log_scale) + mask = self.xpy.ones(lnL.shape,dtype=bool) + if not(self.L_cutoff is None): + if not(self.xpy.isinf(self.L_cutoff)): + mask = lnL > (self.xpy.log(self.L_cutoff) if self.L_cutoff > 0 else -self.xpy.inf) lnL = lnL[mask] prior = self.prior_array[mask] sampling_prior = self.sampling_prior_array[mask] - # append to the cumulative arrays - self.cumulative_samples = np.append(self.cumulative_samples, self.sample_array[mask], axis=0) - self.cumulative_values = np.append(self.cumulative_values, lnL, axis=0) - self.cumulative_p = np.append(self.cumulative_p, prior, axis=0) - self.cumulative_p_s = np.append(self.cumulative_p_s, sampling_prior, axis=0) + self.cumulative_samples = self.xpy.append(self.cumulative_samples, self.sample_array[mask], axis=0) + self.cumulative_values = self.xpy.append(self.cumulative_values, lnL, axis=0) + self.cumulative_p = self.xpy.append(self.cumulative_p, prior, axis=0) + self.cumulative_p_s = self.xpy.append(self.cumulative_p_s, sampling_prior, axis=0) - # compute the log sample weights - log_weights = lnL + np.log(prior) - np.log(sampling_prior) - if np.any(np.isnan(log_weights)): + log_weights = lnL + self.xpy.log(prior) - self.xpy.log(sampling_prior) + if self.xpy.any(self.xpy.isnan(log_weights)): print(" NAN weight ") raise ValueError if self.terrible_lnw_threshold: - if np.max(log_weights) = n_adapt: adapting=False -# print('Iteration:', self.iterations) if err_count >= max_err: print('Exiting due to errors...') break @@ -354,11 +459,13 @@ def integrate(self, func, min_iter=10, max_iter=20, var_thresh=0.0, max_err=10, continue t1 = time.time() if self.proc_count is None: - self.value_array = func(np.copy(self.sample_array)).flatten() + # Ensure input to func is numpy for CPU-based user functions if needed, + # but the user is encouraged to support xpy. + self.value_array = func(self.xpy.copy(self.sample_array)).flatten() else: - split_samples = np.array_split(self.sample_array, self.proc_count) + split_samples = self.xpy.array_split(self.sample_array, self.proc_count) p = Pool(self.proc_count) - self.value_array = np.concatenate(p.map(func, split_samples), axis=0) + self.value_array = self.xpy.concatenate(p.map(func, split_samples), axis=0) p.close() cumulative_eval_time += time.time() - t1 self._calculate_prior() @@ -375,10 +482,10 @@ def integrate(self, func, min_iter=10, max_iter=20, var_thresh=0.0, max_err=10, continue self.iterations += 1 self.ntotal += self.n - testval =self.scaled_error_squared + testval = self.scaled_error_squared if not(self.return_lnI): - testval = np.log(self.scaled_error_squared) + self.log_error_scale_factor - if self.iterations >= min_iter and testval < np.log(var_thresh): + testval = self.xpy.log(self.scaled_error_squared) + self.log_error_scale_factor + if self.iterations >= min_iter and testval < self.xpy.log(var_thresh): break try: if adapting: @@ -400,10 +507,10 @@ def integrate(self, func, min_iter=10, max_iter=20, var_thresh=0.0, max_err=10, if epoch is not None and self.iterations % epoch == 0: self._reset() if verbose: - # Standard mcsampler message, to monitor convergence + print(self.scaled_error_squared) if not(self.return_lnI): - print(" : {} {} {} {} {} ".format((self.iterations-1)*self.n, self.eff_samp, np.sqrt(2*np.max(self.cumulative_values)), np.sqrt(2*(np.log(self.integral))), np.sqrt(self.scaled_error_squared )/self.integral/np.sqrt(self.iterations ) ) ) + print(" : {} {} {} {} {} ".format((self.iterations-1)*self.n, self.eff_samp, self.xpy.sqrt(2*self.xpy.max(self.cumulative_values)), self.xpy.sqrt(2*(self.xpy.log(self.integral))), self.xpy.sqrt(self.scaled_error_squared )/self.integral/self.xpy.sqrt(self.iterations ) ) ) else: - print(" : {} {} {} {} {} ".format((self.iterations-1)*self.n, self.eff_samp, np.sqrt(2*np.max(self.cumulative_values)), np.sqrt(2*self.integral), np.exp(0.5*(self.scaled_error_squared - self.integral*2) )/np.sqrt(self.iterations))) + print(" : {} {} {} {} {} ".format((self.iterations-1)*self.n, self.eff_samp, self.xpy.sqrt(2*self.xpy.max(self.cumulative_values)), self.xpy.sqrt(2*self.integral), self.xpy.exp(0.5*(self.scaled_error_squared - self.integral*2) )/self.xpy.sqrt(self.iterations))) print('cumulative eval time: ', cumulative_eval_time) print('integrator iterations: ', self.iterations) diff --git a/MonteCarloMarginalizeCode/Code/RIFT/integrators/gaussian_mixture_model.py b/MonteCarloMarginalizeCode/Code/RIFT/integrators/gaussian_mixture_model.py old mode 100644 new mode 100755 index 98fd8c256..05fdf1874 --- a/MonteCarloMarginalizeCode/Code/RIFT/integrators/gaussian_mixture_model.py +++ b/MonteCarloMarginalizeCode/Code/RIFT/integrators/gaussian_mixture_model.py @@ -14,6 +14,20 @@ import numpy as np from scipy.stats import multivariate_normal,norm +try: + import cupy + import cupyx.scipy.special + xpy_default = cupy + xpy_special_default = cupyx.scipy.special + identity_convert = cupy.asnumpy + identity_convert_togpu = cupy.asarray + cupy_ok = True +except ImportError: + xpy_default = np + xpy_special_default = None # scipy.special is used via scipy if needed + identity_convert = lambda x: x + identity_convert_togpu = lambda x: x + cupy_ok = False # 1. Try to find the legacy mvnun in known locations try: @@ -49,16 +63,118 @@ def mvnun(lower, upper, mean, cov, maxpts=None, abseps=1e-5, releps=1e-5): return p, 0 -#from scipy.misc import logsumexp from scipy.special import logsumexp from . import multivariate_truncnorm as truncnorm import itertools -# Equation references are from Numerical Recipes for general GMM and -# https://www.cs.nmsu.edu/~joemsong/publications/Song-SPIE2005-updated.pdf for -# online updating features +def _xpy_logsumexp(a, axis=None): + """Portable logsumexp. + + cupyx.scipy.special.logsumexp is only available in newer cupy releases; + the CUDA 10.2 cupy build required by older (sm_30/Kepler) cards does not + ship it. Implement the reduction directly with cupy primitives so the GPU + path works regardless of cupy version, and fall back to scipy on CPU. + """ + if cupy_ok: + a = cupy.asarray(a) + a_max = cupy.amax(a, axis=axis, keepdims=True) + a_max = cupy.where(cupy.isfinite(a_max), a_max, cupy.zeros_like(a_max)) + out = cupy.log(cupy.sum(cupy.exp(a - a_max), axis=axis, keepdims=True)) + a_max + if axis is None: + return out.reshape(()) + return cupy.squeeze(out, axis=axis) + return logsumexp(a, axis=axis) + + +# Symmetric (Hermitian) eigen-routines. cupy.linalg only provides the Hermitian +# variants (eigh/eigvalsh), not the general eig/eigvals. The matrices fed to +# _near_psd below are covariance/correlation matrices and hence symmetric, so +# the Hermitian routines are both correct and the only ones available on GPU. +if cupy_ok: + _xpy_eigvals = cupy.linalg.eigvalsh + _xpy_eig = cupy.linalg.eigh +else: + # Symmetric routines on CPU as well: the inputs are covariance/correlation + # matrices. eigvalsh/eigh are faster, return real eigenvalues (no spurious + # complex output from round-off asymmetry), and match the GPU path. + _xpy_eigvals = np.linalg.eigvalsh + _xpy_eig = np.linalg.eigh + + +def _near_psd_impl(x, epsilon, xpy): + ''' + Shared, hardened nearest-PSD projection for covariance matrices. + + Never raises on degenerate input: non-finite entries or non-positive + variances are repaired with an epsilon-scaled diagonal fallback before + the (symmetric, eigh-based) projection, and the projection loop is + bounded. Inputs are in normalized [-1,1] coordinates so an O(epsilon) + diagonal is always a meaningful scale. + ''' + n = x.shape[0] + # repair non-finite entries: they cannot reach the eigensolver + if not bool(xpy.all(xpy.isfinite(x))): + diag = xpy.diag(x).copy() + diag = xpy.where(xpy.isfinite(diag) & (diag > 0), diag, epsilon*xpy.ones(n)) + x = xpy.diag(diag) + # floor non-positive variances so the correlation rescaling is defined + diag = xpy.diag(x) + if bool(xpy.any(diag <= 0)): + floor = xpy.maximum(diag, epsilon) + x = x + xpy.diag(floor - diag) + x = 0.5 * (x + x.T) # symmetrize: eigh assumes it, round-off breaks it + for _ in range(10): # bounded: the legacy `while True` could spin forever + var_list = xpy.sqrt(xpy.diag(x)) + y = x / (var_list[:, None] * var_list[None, :]) + if bool(xpy.min(_xpy_eigvals(y)) > epsilon): + return x + eigval, eigvec = _xpy_eig(y) + val_psd = xpy.maximum(eigval, epsilon) + near_corr = eigvec @ xpy.diag(val_psd) @ eigvec.T + near_cov = near_corr * (var_list[:, None] * var_list[None, :]) + x = 0.5 * (near_cov.real + near_cov.real.T) + return x + + +def gpu_logpdf(x, mean, cov, xpy): + """ + GPU-compatible multivariate normal log-pdf. + x: (n, d) array + mean: (d,) array + cov: (d, d) array + + Uses Cholesky + a generic linear solve (xpy.linalg.solve) so the same + code path works for both numpy and cupy. Note: solve_triangular is + NOT in numpy.linalg or cupy.linalg (only in scipy.linalg / + cupyx.scipy.linalg), so we deliberately use the generic solver here. + """ + d = mean.shape[0] + diff = x - mean + # Use cholesky for efficiency and stability. cupy.linalg has no LinAlgError + # attribute (and cupy.linalg.cholesky returns NaN rather than raising on a + # non-PSD input), so catch the numpy error type and also treat a NaN factor + # as failure, falling back to an epsilon-regularized diagonal in both cases. + eps = 1e-6 * xpy.eye(d) + try: + L = xpy.linalg.cholesky(cov) + if bool(xpy.any(xpy.isnan(L))): + L = xpy.linalg.cholesky(cov + eps) + except np.linalg.LinAlgError: + L = xpy.linalg.cholesky(cov + eps) + + # Solve L*y = diff^T => y = L^-1 * diff^T + # diff is (n, d), so diff.T is (d, n) + y = xpy.linalg.solve(L, diff.T) + + # quad_form = sum(y^2, axis=0) + quad_form = xpy.sum(y**2, axis=0) + + # log_det = 2 * sum(log(diag(L))) + log_det = 2.0 * xpy.sum(xpy.log(xpy.diag(L))) + log_prob = -0.5 * (d * xpy.log(2 * xpy.pi) + log_det + quad_form) + return log_prob class estimator: ''' @@ -87,14 +203,26 @@ def __init__(self, k, max_iters=100, tempering_coeff=1e-8,adapt=None): self.cov_avg_ratio = 0.05 self.epsilon = 1e-4 self.tempering_coeff = tempering_coeff + self.xpy = xpy_default + self.identity_convert = identity_convert + self.identity_convert_togpu = identity_convert_togpu def _initialize(self, n, sample_array, log_sample_weights=None): - p_weights = np.exp(log_sample_weights - np.max(log_sample_weights)).flatten() - p_weights[np.isnan(p_weights)] = 0 # zero out the nan weights - p_weights /= np.sum(p_weights) - self.means = sample_array[np.random.choice(n, self.k, p=p_weights.astype(sample_array.dtype)), :] - self.covariances = [np.identity(self.d)] * self.k - self.weights = np.ones(self.k) / self.k + if log_sample_weights is None: + log_sample_weights = self.xpy.zeros(n) + finite_max = self.xpy.max(self.xpy.where(self.xpy.isfinite(log_sample_weights), log_sample_weights, -self.xpy.inf)) + if not bool(self.xpy.isfinite(finite_max)): + finite_max = 0.0 # no finite weights at all: fall back to uniform + p_weights = self.xpy.exp(log_sample_weights - finite_max).flatten() + p_weights[~self.xpy.isfinite(p_weights)] = 0 # zero out the nan/inf weights + w_sum = self.xpy.sum(p_weights) + if not bool(w_sum > 0): + p_weights = self.xpy.ones(n) + w_sum = 1.0 * n + p_weights /= w_sum + self.means = sample_array[self.xpy.random.choice(n, self.k, p=p_weights.astype(sample_array.dtype)), :] + self.covariances = [self.xpy.identity(self.d)] * self.k + self.weights = self.xpy.ones(self.k) / self.k self.adapt = [True] * self.k def _e_step(self, n, sample_array, log_sample_weights=None): @@ -102,50 +230,76 @@ def _e_step(self, n, sample_array, log_sample_weights=None): Expectation step ''' if log_sample_weights is None: - log_sample_weights = np.zeros(n) - p_nk = np.empty((n, self.k)) + log_sample_weights = self.xpy.zeros(n) + p_nk = self.xpy.empty((n, self.k)) for index in range(self.k): mean = self.means[index] cov = self.covariances[index] - log_p = np.log(self.weights[index]) - log_pdf = multivariate_normal.logpdf(x=sample_array, mean=mean, cov=cov, allow_singular=True) # (16.1.4) - # note that allow_singular=True in the above line is probably really dumb and - # terrible, but it seems to occasionally keep the whole thing from blowing up - # so it stays for now + log_p = self.xpy.log(self.weights[index]) + + if cupy_ok: + log_pdf = gpu_logpdf(sample_array, mean, cov, self.xpy) + else: + log_pdf = multivariate_normal.logpdf(x=sample_array, mean=mean, cov=cov, allow_singular=True) + p_nk[:,index] = log_pdf + log_p # (16.1.5) - p_xn = logsumexp(p_nk, axis=1)#, keepdims=True) # (16.1.3) - self.p_nk = p_nk - p_xn[:,np.newaxis] # (16.1.5) + + # Use cupy or scipy for logsumexp + p_xn = _xpy_logsumexp(p_nk, axis=1) + + self.p_nk = p_nk - p_xn[:,self.xpy.newaxis] # (16.1.5) # normalize log sample weights as well, before modifying things with them - self.p_nk += log_sample_weights[:,np.newaxis] - logsumexp(log_sample_weights) - self.log_prob = np.sum(p_xn + log_sample_weights) # (16.1.2) + ls_sum = _xpy_logsumexp(log_sample_weights) + + self.p_nk += log_sample_weights[:,self.xpy.newaxis] - ls_sum + + self.log_prob = self.xpy.sum(p_xn + log_sample_weights) def _m_step(self, n, sample_array): ''' - Maximization step + Maximization step. + + Works in the log domain: self.p_nk holds *log* responsibilities + (including the normalized log sample weights). Normalizing within + each component via logsumexp BEFORE exponentiating keeps the + means/covariances well-defined even when the raw weights span + thousands of nats (high-SNR refits): the dominant responsibilities + are O(1) by construction instead of all underflowing to zero. ''' - p_nk = np.exp(self.p_nk) - weights = np.sum(p_nk, axis=0) # weight of a single component + log_p_nk = self.p_nk + # per-component log total responsibility (log of the old `weights`) + log_w = _xpy_logsumexp(log_p_nk, axis=0) for index in range(self.k): if self.adapt[index]: - # (16.1.6) - w = weights[index] # should be 1 for a single component, note - p_k = p_nk[:,index] - mean = np.sum(np.multiply(sample_array, p_k[:,np.newaxis]), axis=0) - mean /= w - self.means[index] = mean - # (16.1.6) + if not bool(self.xpy.isfinite(log_w[index])): + # component received zero/non-finite weight: keep previous params + continue + # responsibilities normalized within this component: sum to 1 + r_k = self.xpy.exp(log_p_nk[:,index] - log_w[index]) + mean = self.xpy.sum(self.xpy.multiply(sample_array, r_k[:,self.xpy.newaxis]), axis=0) diff = sample_array - mean - cov = np.dot((p_k[:,np.newaxis] * diff).T, diff) / w -# cov = np.cov(diff.T, aweights=p_k)/w # don't reinvent the wheel -# if len(mean)<2: -# cov =np.array([[cov]]) - # attempt to fix non-positive-semidefinite covariances - self.covariances[index] = self._near_psd(cov) + cov = self.xpy.dot((r_k[:,self.xpy.newaxis] * diff).T, diff) + # Guard BEFORE _near_psd (breadcrumb item 1): a degenerate weighted + # covariance (all responsibility on ~1 sample, ESS < d+1) or any + # non-finite entry must not reach the eigensolver. Keep the + # previous covariance (identity at init) and only update the mean. + ess_k = 1.0 / self.xpy.sum(r_k**2) + cov_ok = bool(self.xpy.all(self.xpy.isfinite(cov))) \ + and bool(self.xpy.trace(cov) > 0) \ + and bool(ess_k >= self.d + 1) + self.means[index] = mean + if cov_ok: + self.covariances[index] = self._near_psd(cov) # (16.17) - weights /= np.sum(p_nk[:,self.adapt]) - # if we are not adapting some of the gaussians, we need to renormalize again. Note the weight of the fixed item remains fixed! - weights /= np.sum(weights) - self.weights = weights + # mixture weights via logsumexp over ALL components (the legacy + # double normalization cancels to exactly this softmax) + log_w_safe = self.xpy.where(self.xpy.isfinite(log_w), log_w, -self.xpy.inf*self.xpy.ones(self.k)) + log_norm = _xpy_logsumexp(log_w_safe) + if bool(self.xpy.isfinite(log_norm)): + weights = self.xpy.exp(log_w_safe - log_norm) + w_sum = self.xpy.sum(weights) + if bool(w_sum > 0) and bool(self.xpy.all(self.xpy.isfinite(weights))): + self.weights = weights / w_sum def _tol(self, n): @@ -158,49 +312,12 @@ def _tol(self, n): def _near_psd(self, x): ''' Calculates the nearest postive semi-definite matrix for a correlation/covariance matrix - - Code from here: - https://stackoverflow.com/questions/10939213/how-can-i-calculate-the-nearest-positive-semi-definite-matrix ''' - n = x.shape[0] - var_list = np.array([np.sqrt(x[i,i]) for i in range(n)]) - y = np.array([[x[i, j]/(var_list[i]*var_list[j]) for i in range(n)] for j in range(n)]) - while True: - epsilon = self.epsilon - if min(np.linalg.eigvals(y)) > epsilon: - return x - - # Removing scaling factor of covariance matrix - var_list = np.array([np.sqrt(x[i,i]) for i in range(n)]) - y = np.array([[x[i, j]/(var_list[i]*var_list[j]) for i in range(n)] for j in range(n)]) - - # getting the nearest correlation matrix - eigval, eigvec = np.linalg.eig(y) - val = np.matrix(np.maximum(eigval, epsilon)) - vec = np.matrix(eigvec) - T = 1/(np.multiply(vec, vec) * val.T) - T = np.matrix(np.sqrt(np.diag(np.array(T).reshape((n)) ))) - B = T * vec * np.diag(np.array(np.sqrt(val)).reshape((n))) - near_corr = B*B.T - - # returning the scaling factors - near_cov = np.array([[near_corr[i, j]*(var_list[i]*var_list[j]) for i in range(n)] for j in range(n)]) - if np.isreal(near_cov).all(): - break - else: - x = near_cov.real - return near_cov - + return _near_psd_impl(x, self.epsilon, self.xpy) + def fit(self, sample_array, log_sample_weights): ''' Fit the model to data - - Parameters - ---------- - sample_array : np.ndarray - Array of samples to fit - log_sample_weights : np.ndarray - Weights for samples ''' n, self.d = sample_array.shape self._initialize(n, sample_array, log_sample_weights) @@ -214,21 +331,24 @@ def fit(self, sample_array, log_sample_weights): count += 1 for index in range(self.k): cov = self.covariances[index] - # temper - # - note this introduces a PREFERRED LENGTH SCALE into the problem, which is dangerous - cov = (cov + self.tempering_coeff * np.eye(self.d)) / (1 + self.tempering_coeff) + cov = (cov + self.tempering_coeff * self.xpy.eye(self.d)) / (1 + self.tempering_coeff) self.covariances[index] = cov def print_params(self): ''' Prints the model's parameters in an easily-readable format ''' + # Convert to numpy for printing + means_np = [self.identity_convert(m) for m in self.means] + covs_np = [self.identity_convert(c) for c in self.covariances] + weights_np = self.identity_convert(self.weights) + if self.d ==1: print("GMM: component wt mean std ") for i in range(self.k): - mean = self.means[i] - cov = self.covariances[i] - weight = self.weights[i] + mean = means_np[i] + cov = covs_np[i] + weight = weights_np[i] if self.d >1: print('________________________________________\n') print('Component', i) @@ -245,23 +365,17 @@ def print_params(self): class gmm: ''' More sophisticated implementation built on top of estimator class - - Includes functionality to update with new data rather than re-fit, as well - as sampling and scoring of samples. - - Parameters - ---------- - k : int - Number of Gaussian components - max_iters : int - Maximum number of Expectation-Maximization iterations ''' - def __init__(self, k, bounds, max_iters=1000,epsilon=None,tempering_coeff=1e-8): + def __init__(self, k, bounds, max_iters=1000,epsilon=None,tempering_coeff=1e-8,memory_factor=3.0): self.k = k self.bounds = bounds - #self.tol = tol self.max_iters = max_iters + # update() merge memory: the old model enters the merge with weight + # min(N, memory_factor*M) instead of the full cumulative N, so an + # early bad fit cannot accumulate unbounded inertia (the proposal can + # always recover within ~memory_factor chunks). + self.memory_factor = memory_factor self.means = [None] * k self.covariances =[None] * k self.weights = [None] * k @@ -272,14 +386,17 @@ def __init__(self, k, bounds, max_iters=1000,epsilon=None,tempering_coeff=1e-8): self.N = 0 self.epsilon =epsilon if self.epsilon is None: - self.epsilon = 1e-6 # allow very strong correlations + self.epsilon = 1e-6 else: self.epsilon=epsilon self.tempering_coeff = tempering_coeff + self.xpy = xpy_default + self.identity_convert = identity_convert + self.identity_convert_togpu = identity_convert_togpu def _normalize(self, samples): n, d = samples.shape - out = np.empty((n, d)) + out = self.xpy.empty((n, d)) for i in range(d): [llim, rlim] = self.bounds[i] out[:,i] = (2.0 * samples[:,i] - (rlim + llim)) / (rlim - llim) @@ -287,7 +404,7 @@ def _normalize(self, samples): def _unnormalize(self, samples): n, d = samples.shape - out = np.empty((n, d)) + out = self.xpy.empty((n, d)) for i in range(d): [llim, rlim] = self.bounds[i] out[:,i] = 0.5 * ((rlim - llim) * samples[:,i] + (llim + rlim)) @@ -296,18 +413,11 @@ def _unnormalize(self, samples): def fit(self, sample_array, log_sample_weights=None): ''' Fit the model to data - - Parameters - ---------- - sample_array : np.ndarray - Array of samples to fit - sample_weights : np.ndarray - Weights for samples ''' self.N, self.d = sample_array.shape if log_sample_weights is None: - log_sample_weights = np.zeros(self.N) - # just use base estimator + log_sample_weights = self.xpy.zeros(self.N) + model = estimator(self.k, tempering_coeff=self.tempering_coeff,adapt=self.adapt) model.fit(self._normalize(sample_array), log_sample_weights) self.means = model.means @@ -328,208 +438,198 @@ def _match_components(self, new_model): dist = 0 i = 0 for j in order: - # get Mahalanobis distance between current pair of components - diff = new_model.means[j] - self.means[i] - cov_inv = np.linalg.inv(self.covariances[i]) - temp_cov_inv = np.linalg.inv(new_model.covariances[j]) + # These are likely small vectors, stay on CPU + diff = self.identity_convert(new_model.means[j]) - self.identity_convert(self.means[i]) + cov_inv = np.linalg.inv(self.identity_convert(self.covariances[i])) + temp_cov_inv = np.linalg.inv(self.identity_convert(new_model.covariances[j])) dist += np.sqrt(np.dot(np.dot(diff, cov_inv), diff)) dist += np.sqrt(np.dot(np.dot(diff, temp_cov_inv), diff)) i += 1 distances[index] = dist index += 1 - return orders[np.argmin(distances)] # returns order which gives minimum net Mahalanobis distance + return orders[np.argmin(distances)] def _merge(self, new_model, M): ''' - Merge corresponding components of new model and old model - - Refer to paper linked at the top of this file + Merge corresponding components of new model and old model. - M is the number of samples that the new model was fit using + The old model's merge weight is capped at memory_factor*M (bounded + memory): with the legacy cumulative self.N an early bad fit dominated + every later merge and the proposal could never recover. ''' + N_merge = min(self.N, self.memory_factor * M) if self.memory_factor else self.N order = self._match_components(new_model) for i in range(self.k): - j = order[i] # get corresponding component + j = order[i] old_mean = self.means[i] temp_mean = new_model.means[j] old_cov = self.covariances[i] temp_cov = new_model.covariances[j] old_weight = self.weights[i] temp_weight = new_model.weights[j] - denominator = (self.N * old_weight) + (M * temp_weight) # this shows up a lot so just compute it once - # start equation (6) - mean = (self.N * old_weight * old_mean) + (M * temp_weight * temp_mean) + denominator = (N_merge * old_weight) + (M * temp_weight) + + mean = (N_merge * old_weight * old_mean) + (M * temp_weight * temp_mean) mean /= denominator - # start equation (7) - cov1 = (self.N * old_weight * old_cov) + (M * temp_weight * temp_cov) + + cov1 = (N_merge * old_weight * old_cov) + (M * temp_weight * temp_cov) cov1 /= denominator - cov2 = (self.N * old_weight * old_mean * old_mean.T) + (M * temp_weight * temp_mean * temp_mean.T) + + # outer product for means + cov2 = (N_merge * old_weight * self.xpy.outer(old_mean, old_mean)) + (M * temp_weight * self.xpy.outer(temp_mean, temp_mean)) cov2 /= denominator - cov = cov1 + cov2 - mean * mean.T - # check for positive-semidefinite + + cov = cov1 + cov2 - self.xpy.outer(mean, mean) cov = self._near_psd(cov) - # start equation (8) - weight = denominator / (self.N + M) - # update everything + + weight = denominator / (N_merge + M) + self.means[i] = mean self.covariances[i] = cov self.weights[i] = weight def _near_psd(self, x): ''' - Calculates the nearest postive semi-definite matrix for a correlation/covariance matrix - - Code from here: - https://stackoverflow.com/questions/10939213/how-can-i-calculate-the-nearest-positive-semi-definite-matrix + Calculates the nearest postive semi-definite matrix for a correlation/covariance matrix ''' - n = x.shape[0] - var_list = np.array([np.sqrt(x[i,i]) for i in range(n)]) - y = np.array([[x[i, j]/(var_list[i]*var_list[j]) for i in range(n)] for j in range(n)]) - while True: - epsilon = self.epsilon - if min(np.linalg.eigvals(y)) > epsilon: - return x - - # Removing scaling factor of covariance matrix - var_list = np.array([np.sqrt(x[i,i]) for i in range(n)]) - y = np.array([[x[i, j]/(var_list[i]*var_list[j]) for i in range(n)] for j in range(n)]) - - # getting the nearest correlation matrix - eigval, eigvec = np.linalg.eig(y) - val = np.matrix(np.maximum(eigval, epsilon)) - vec = np.matrix(eigvec) - T = 1/(np.multiply(vec, vec) * val.T) - T = np.matrix(np.sqrt(np.diag(np.array(T).reshape((n)) ))) - B = T * vec * np.diag(np.array(np.sqrt(val)).reshape((n))) - near_corr = B*B.T - - # returning the scaling factors - near_cov = np.array([[near_corr[i, j]*(var_list[i]*var_list[j]) for i in range(n)] for j in range(n)]) - if np.isreal(near_cov).all(): - break - else: - x = near_cov.real - return near_cov + return _near_psd_impl(x, self.epsilon, self.xpy) def update(self, sample_array, log_sample_weights=None): ''' Updates the model with new data without doing a full retraining. - - Parameters - ---------- - sample_array : np.ndarray - Array of samples to fit - sample_weights : np.ndarray - Weights for samples ''' - self.tempering_coeff /= 2 + # halve the covariance regularizer but FLOOR it: an unbounded decay + # (the legacy behavior) eventually leaves sharp refits unregularized + self.tempering_coeff = max(self.tempering_coeff / 2, 1e-12) new_model = estimator(self.k, self.max_iters, self.tempering_coeff) - # Strip non-finite training data - indx_ok = np.isfinite(log_sample_weights) - new_model.fit(self._normalize(sample_array[indx_ok]), log_sample_weights[indx_ok]) + + # Filter non-finite + if log_sample_weights is not None: + indx_ok = self.xpy.isfinite(log_sample_weights) + s_filtered = sample_array[indx_ok] + w_filtered = log_sample_weights[indx_ok] + else: + s_filtered = sample_array + w_filtered = None + + new_model.fit(self._normalize(s_filtered), w_filtered) M, _ = sample_array.shape self._merge(new_model, M) self.N += M def score(self, sample_array,assume_normalized=True): ''' - Score samples (i.e. calculate likelihood of each sample) under the current - model. - - Note the bounds are stored *not* normalized, and we need to compensate for that. - Note the normalized bounds are always -1,1 ... but we won't hardcode that, in case normalization changes - - Parameters - ---------- - sample_array : np.ndarray - Array of samples to fit - bounds : np.ndarray - Bounds for samples, used for renormalizing scores + Score samples under the current model. ''' n, d = sample_array.shape - scores = np.zeros(n) - sample_array = self._normalize(sample_array) - bounds_normalized = np.zeros(self.bounds.shape) - bounds_normalized= self._normalize(self.bounds.T).T + scores = self.xpy.zeros(n) + sample_array_norm = self._normalize(sample_array) + + # bounds_normalized + bounds_norm = self._normalize(self.bounds.T).T normalization_constant = 0. + for i in range(self.k): w = self.weights[i] mean = self.means[i] cov = self.covariances[i] - if(len(mean)>1): - scores += multivariate_normal.pdf(x=sample_array, mean=mean, cov=cov, allow_singular=True) * w - normalization_constant += w*mvnun(bounds_normalized[:,0], bounds_normalized[:,1], mean, cov)[0] # this function is very fast at integrating multivariate normal distributions + + if self.d > 1: + if cupy_ok: + # Use gpu_logpdf and exponentiate + log_pdf = gpu_logpdf(sample_array_norm, mean, cov, self.xpy) + pdf = self.xpy.exp(log_pdf) + else: + pdf = multivariate_normal.pdf(x=sample_array_norm, mean=mean, cov=cov, allow_singular=True) + + scores += pdf * w + # mvnun is CPU only + mean_cpu = self.identity_convert(mean) + cov_cpu = self.identity_convert(cov) + bounds_norm_cpu = self.identity_convert(bounds_norm) + normalization_constant += w * mvnun(bounds_norm_cpu[:,0], bounds_norm_cpu[:,1], mean_cpu, cov_cpu)[0] else: sigma2 = cov[0,0] - val = 1./np.sqrt(2*np.pi*sigma2) * np.exp( - 0.5*( sample_array[:,0] - mean[0])**2/sigma2) + val = 1./self.xpy.sqrt(2*self.xpy.pi*sigma2) * self.xpy.exp( - 0.5*( sample_array_norm[:,0] - mean[0])**2/sigma2) scores += val * w - my_cdf = norm(loc=mean[0],scale=np.sqrt(sigma2)).cdf - normalization_constant += w*(my_cdf( bounds_normalized[0,1]) - my_cdf( bounds_normalized[0,0])) - # note that allow_singular=True in the above line is probably really dumb and - # terrible, but it seems to occasionally keep the whole thing from blowing up - # so it stays for now - # we need to renormalize the PDF - # to do this we sample from a full distribution (i.e. without truncation) and use the - # fraction of samples that fall inside the bounds to renormalize - #full_sample_array = self.sample(n, use_bounds=False) - #llim = np.rot90(self.bounds[:,[0]]) - #rlim = np.rot90(self.bounds[:,[1]]) - #n1 = np.greater(full_sample_array, llim).all(axis=1) - #n2 = np.less(full_sample_array, rlim).all(axis=1) - #normalize = np.array(np.logical_and(n1, n2)).flatten() - #m = float(np.sum(normalize)) / n - #scores /= m + + mean_cpu = self.identity_convert(mean)[0] + sigma_cpu = self.identity_convert(np.sqrt(sigma2)) + bounds_norm_cpu = self.identity_convert(bounds_norm[0]) + my_cdf = norm(loc=mean_cpu, scale=sigma_cpu).cdf + normalization_constant += w * (my_cdf(bounds_norm_cpu[1]) - my_cdf(bounds_norm_cpu[0])) + + # Floors: a sharply-truncated component can drive the mvnun + # normalization to 0 (0/0 -> NaN), and exactly-zero scores later become + # log(0) = -inf in the integrator's weights. 1e-300 keeps the log + # finite without affecting any sample that carries real weight. + normalization_constant = max(float(normalization_constant), 1e-300) scores /= normalization_constant - vol = np.prod(self.bounds[:,1] - self.bounds[:,0]) - scores *= 2.0**d / vol # account for renormalization of dimensions - return scores + vol = self.xpy.prod(self.bounds[:,1] - self.bounds[:,0]) + scores *= (2.0**self.d) / vol + return self.xpy.maximum(scores, 1e-300) def sample(self, n, use_bounds=True): ''' - Draw samples from the current model, either with or without bounds - - Parameters - ---------- - n : int - Number of samples to draw - bounds : np.ndarray - Bounds for samples + Draw samples from the current model. + + Note the model's means/covariances are stored in *normalized* coordinates + (the [-1, 1] image of self.bounds under self._normalize). Samples are + therefore drawn in normalized coordinates and then unnormalized back to + the original coordinate frame before being returned, matching the + pre-port behavior expected by MonteCarloEnsemble._sample(). ''' - sample_array = np.empty((n, self.d)) + # Sampling is kept on CPU for stability (truncnorm is CPU-only) + means_np = [self.identity_convert(m) for m in self.means] + covs_np = [self.identity_convert(c) for c in self.covariances] + weights_np = self.identity_convert(self.weights) + + # truncnorm bounds must match the coordinate frame of the model + # parameters (mean/cov), which is normalized [-1, 1]. + bounds_normalized = np.empty((self.d, 2)) + bounds_normalized[:, 0] = -1.0 + bounds_normalized[:, 1] = 1.0 + + sample_array_np = np.empty((n, self.d)) start = 0 - bounds = np.empty(self.bounds.shape) - bounds[:,0] = -1.0 - bounds[:,1] = 1.0 for component in range(self.k): - w = self.weights[component] - mean = self.means[component] - cov = self.covariances[component] - num_samples = int(n * w) # NOT a poisson draw, note : we draw exactly the expected number from each one (since we have a fixed number to fill) + w = weights_np[component] + mean = means_np[component] + cov = covs_np[component] + num_samples = int(n * w) if component == self.k - 1: end = n else: end = start + num_samples try: if not use_bounds: - sample_array[start:end] = np.random.multivariate_normal(mean, cov, end - start) + sample_array_np[start:end] = np.random.multivariate_normal(mean, cov, end - start) else: - sample_array[start:end] = truncnorm.sample(mean, cov, bounds, end - start) + sample_array_np[start:end] = truncnorm.sample(mean, cov, bounds_normalized, end - start) start = end - except: - print('Exiting due to non-positive-semidefinite') + except Exception as e: + print('Exiting due to non-positive-semidefinite', e) raise Exception("gmm covariance not positive-semidefinite") - return self._unnormalize(sample_array) + + # Move to xpy and unnormalize back to original [llim, rlim] coordinates, + # so callers receive samples in the same frame as self.bounds. + sample_array_xpy = self.identity_convert_togpu(sample_array_np) + return self._unnormalize(sample_array_xpy) def print_params(self): ''' Prints the model's parameters in an easily-readable format ''' + means_np = [self.identity_convert(m) for m in self.means] + covs_np = [self.identity_convert(c) for c in self.covariances] + weights_np = self.identity_convert(self.weights) + if self.d ==1: print("GMM: component wt mean_correct mean_normed std_normed ") for i in range(self.k): - mean = self.means[i] - cov = self.covariances[i] - weight = self.weights[i] + mean = means_np[i] + cov = covs_np[i] + weight = weights_np[i] if self.d >1: print('________________________________________\n') print('Component', i) diff --git a/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsampler.py b/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsampler.py index e02fbaa52..822605d7b 100644 --- a/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsampler.py +++ b/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsampler.py @@ -6,6 +6,7 @@ from collections import defaultdict import numpy +from RIFT.precision import RiftFloat # platform-portable replacement for np.float128 from scipy import integrate, interpolate from ..integrators.statutils import cumvar, welford, update, finalize import itertools @@ -411,7 +412,7 @@ def integrate(self, func, *args, **kwargs): # Determine stopping conditions # nmax = int(kwargs["nmax"]) if "nmax" in kwargs else float("inf") - neff = kwargs["neff"] if "neff" in kwargs else numpy.float128("inf") + neff = kwargs["neff"] if "neff" in kwargs else RiftFloat("inf") n = int(kwargs["n"]) if "n" in kwargs else min(1000, nmax) convergence_tests = kwargs["convergence_tests"] if "convergence_tests" in kwargs else None @@ -463,12 +464,12 @@ def integrate(self, func, *args, **kwargs): print(" Initiating multiprocessor pool : ", nProcesses) p = Pool(nProcesses) - int_val1 = numpy.float128(0) + int_val1 = RiftFloat(0) self.ntotal = 0 maxval = -float("Inf") maxlnL = -float("Inf") eff_samp = 0 - mean, var = None, numpy.float128(0) # to prevent infinite variance due to overflow + mean, var = None, RiftFloat(0) # to prevent infinite variance due to overflow if bShowEvaluationLog: print("iteration Neff sqrt(2*lnLmax) sqrt(2*lnLmarg) ln(Z/Lmax) int_var") @@ -501,7 +502,7 @@ def integrate(self, func, *args, **kwargs): # Calculate the overall p_s assuming each pdf is independent joint_p_s = numpy.prod(p_s, axis=0) joint_p_prior = numpy.prod(p_prior, axis=0) - joint_p_prior = numpy.array(joint_p_prior,dtype=numpy.float128) # Force type. Some type issues have arisen (dtype=object returns by accident) + joint_p_prior = numpy.array(joint_p_prior,dtype=RiftFloat) # Force type. Some type issues have arisen (dtype=object returns by accident) # print "Joint prior ", type(joint_p_prior), joint_p_prior.dtype, joint_p_prior # print "Joint sampling prior ", type(joint_p_s), joint_p_s.dtype @@ -850,7 +851,7 @@ def q_samp_vector(qmin,qmax,x): scale = 1./(1+qmin) - 1./(1+qmax) return 1/numpy.power((1+x),2)/scale def q_cdf_inv_vector(qmin,qmax,x): - return numpy.array((qmin + qmax*qmin + qmax*x - qmin*x)/(1 + qmax - qmax*x + qmin*x),dtype=np.float128) + return numpy.array((qmin + qmax*qmin + qmax*x - qmin*x)/(1 + qmax - qmax*x + qmin*x),dtype=RiftFloat) # total mass. Assumed used with q. 2M/Mmax^2-Mmin^2 def M_samp_vector(Mmin,Mmax,x): diff --git a/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsamplerAdaptiveVolume.py b/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsamplerAdaptiveVolume.py index b19687ceb..3e3579813 100644 --- a/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsamplerAdaptiveVolume.py +++ b/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsamplerAdaptiveVolume.py @@ -11,6 +11,7 @@ import numpy np=numpy #import numpy as np +from RIFT.precision import RiftFloat # platform-portable replacement for np.float128 from scipy import integrate, interpolate, special import itertools import functools @@ -19,7 +20,7 @@ try: import cupy - import cupyx # needed for logsumexp + import cupyx.scipy.special # needed for logsumexp xpy_default=cupy try: xpy_special_default = cupyx.scipy.special @@ -279,11 +280,17 @@ def add_parameter(self, params, pdf, cdf_inv=None, left_limit=None, right_limit def prior_prod(self, x): """ Evaluates prior_pdf(x), multiplying together all factors + + prior_pdf are host (numpy) functions in general, so evaluate them on a + CPU copy of the samples and convert the product back to the active + backend. identity_convert / identity_convert_togpu are no-ops when cupy + is not in use. """ p_out = xpy_default.ones(len(x)) + x_cpu = identity_convert(x) indx = 0 for param in self.params_ordered: - p_out *= self.prior_pdf[param](x[:,indx]) + p_out *= identity_convert_togpu(self.prior_pdf[param](x_cpu[:,indx])) indx +=1 return p_out @@ -469,7 +476,7 @@ def integrate_log(self, lnF, *args, xpy=xpy_default,**kwargs): # Determine stopping conditions # nmax = kwargs["nmax"] if "nmax" in kwargs else float("inf") - neff = kwargs["neff"] if "neff" in kwargs else numpy.float128("inf") + neff = kwargs["neff"] if "neff" in kwargs else RiftFloat("inf") n = int(kwargs["n"] if "n" in kwargs else min(100000, nmax)) convergence_tests = kwargs["convergence_tests"] if "convergence_tests" in kwargs else None save_no_samples = kwargs["save_no_samples"] if "save_no_samples" in kwargs else None @@ -551,12 +558,16 @@ def integrate_log(self, lnF, *args, xpy=xpy_default,**kwargs): rv = identity_convert_togpu(rv) # send random numbers to GPU : ugh log_joint_p_prior = identity_convert_togpu(log_joint_p_prior) # send to GPU if required. Don't waste memory reassignment otherwise - # Evaluate function, protecting argument order + # Evaluate function, protecting argument order. The user integrand is + # a host function in general, so feed it CPU samples; lnL is pushed + # back to the active backend just below. identity_convert is a no-op + # without cupy. + rv_cpu = identity_convert(rv) if 'no_protect_names' in kwargs: - unpacked0 = rv.T + unpacked0 = rv_cpu.T lnL = lnF(*unpacked0) # do not protect order else: - unpacked = dict(list(zip(self.params_ordered,rv.T))) + unpacked = dict(list(zip(self.params_ordered,rv_cpu.T))) lnL= lnF(**unpacked) # protect order using dictionary # take log if we are NOT using lnL if cupy_ok: @@ -643,7 +654,11 @@ def integrate_log(self, lnF, *args, xpy=xpy_default,**kwargs): # write out log integrand self._rvs['log_integrand'] = allloglkl - allp # remember 'allloglkl' really is Lp -- despite the misleading name! -- so we are *undoing* that self._rvs['log_joint_prior'] = allp - self._rvs['log_joint_s_prior'] = xpy_here.ones(len(allloglkl))*(np.log(1/V) - np.sum(np.log(self.dx0))) # effective uniform sampling on this volume + # ones_like(allloglkl) follows allloglkl's backend (cupy via numpy's + # __array_function__ dispatch when on GPU); xpy_here.ones(len) would + # instead create a host array, leaving this term on a different backend + # than log_integrand / log_joint_prior and breaking the arithmetic below. + self._rvs['log_joint_s_prior'] = xpy_here.ones_like(allloglkl)*(np.log(1/V) - np.sum(np.log(self.dx0))) # effective uniform sampling on this volume # Manual estimate of integrand, done transparently (no 'log aggregate' or running calculation -- so memory hog log_wt = self._rvs["log_integrand"] + self._rvs["log_joint_prior"] - self._rvs["log_joint_s_prior"] diff --git a/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsamplerEnsemble.py b/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsamplerEnsemble.py old mode 100644 new mode 100755 index a9a22c069..ac433f43a --- a/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsamplerEnsemble.py +++ b/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsamplerEnsemble.py @@ -1,10 +1,25 @@ - import sys import math import bisect from collections import defaultdict import numpy as np +from RIFT.precision import RiftFloat # platform-portable replacement for np.float128 + +try: + import cupy + import cupyx.scipy.special + xpy_default = cupy + xpy_special_default = cupyx.scipy.special + identity_convert = cupy.asnumpy + identity_convert_togpu = cupy.asarray + cupy_ok = True +except ImportError: + xpy_default = np + xpy_special_default = None + identity_convert = lambda x: x + identity_convert_togpu = lambda x: x + cupy_ok = False import itertools import functools @@ -38,27 +53,10 @@ class MCSampler(object): @staticmethod def match_params_from_args(args, params): - """ - Given two unordered sets of parameters, one a set of all "basic" elements - (strings) possible, and one a set of elements both "basic" strings and - "combined" (basic strings in tuples), determine whether the sets are equivalent - if no basic element is repeated. - - e.g. set A ?= set B - - ("a", "b", "c") ?= ("a", "b", "c") ==> True - (("a", "b", "c")) ?= ("a", "b", "c") ==> True - (("a", "b"), "d")) ?= ("a", "b", "c") ==> False # basic element 'd' not in set B - (("a", "b"), "d")) ?= ("a", "b", "d", "c") ==> False # not all elements in set B - represented in set A - """ not_common = set(args) ^ set(params) if len(not_common) == 0: - # All params match return True if all([not isinstance(i, tuple) for i in not_common]): - # The only way this is possible is if there are - # no extraneous params in args return False to_match = [i for i in not_common if not isinstance(i, tuple)] @@ -72,52 +70,31 @@ def match_params_from_args(args, params): def __init__(self): - # Total number of samples drawn self.ntotal = 0 - # Samples per iteration self.n = 0 - # Parameter names self.params = set() - self.params_ordered = [] # keep them in order. Important to break likelihood function need for names - # parameter -> pdf function object + self.params_ordered = [] self.pdf = {} - # If the pdfs aren't normalized, this will hold the normalization - # constant self._pdf_norm = defaultdict(lambda: 1) - # Cache for the sampling points self._rvs = {} - # parameter -> cdf^{-1} function object self.cdf = {} self.cdf_inv = {} - # params for left and right limits self.llim, self.rlim = {}, {} - # Keep track of the adaptive parameters self.adaptive = [] - - # Keep track of the adaptive parameter 1-D marginalizations self._hist = {} - - # MEASURES (=priors): ROS needs these at the sampler level, to clearly separate their effects - # ASSUMES the user insures they are normalized self.prior_pdf = {} - self.func = None self.sample_format = None self.curr_args = None + self.gmm_dict ={} + self.integrator = None - self.gmm_dict ={} # state variable - self.integrator = None # state variable - - # portfolio interfacing/GPU compatible cross-sampler operations - self.xpy = np - self.identity_convert = lambda x: x # if needed, convert to numpy format (e.g, cupy.asnumpy) - self.identity_convert_togpu = lambda x: x + self.xpy = xpy_default + self.identity_convert = identity_convert + self.identity_convert_togpu = identity_convert_togpu def clear(self): - """ - Clear out the parameters and their settings, as well as clear the sample cache. - """ self.params = set() self.params_ordered = [] self.pdf = {} @@ -133,17 +110,7 @@ def clear(self): def add_parameter(self, params, pdf=None, cdf_inv=None, left_limit=None, right_limit=None, prior_pdf=None, adaptive_sampling=False): - """ - Add one (or more) parameters to sample dimensions. params is either a string - describing the parameter, or a tuple of strings. The tuple will indicate to - the sampler that these parameters must be sampled together. left_limit and - right_limit are on the infinite interval by default, but can and probably should - be specified. If several params are given, left_limit, and right_limit must be a - set of tuples with corresponding length. Sampling PDF is required, and if not - provided, the cdf inverse function will be determined numerically from the - sampling PDF. - """ - self.params.add(params) # does NOT preserve order in which parameters are provided + self.params.add(params) self.params_ordered.append(params) if rosDebugMessages: print(" mcsampler: Adding parameter ", params, " with limits ", [left_limit, right_limit]) @@ -174,40 +141,35 @@ def add_parameter(self, params, pdf=None, cdf_inv=None, left_limit=None, right_ self.prior_pdf[params] = prior_pdf def evaluate(self, samples): - ''' - Interfaces between monte_carlo_integrator sample format (1 (n x d) array) - and likelihood function sample format (d 1D arrays in a list) - ''' - # integrand expects a list of 1D rows + # The user integrand is a host (numpy/scipy) function in general, so move + # samples to the CPU before calling it and push the result back to the + # active backend (cupy on GPU). This is a no-op when xpy is numpy. + samples = self.identity_convert(samples) temp = [] for index in range(len(self.curr_args)): temp.append(samples[:,index]) - temp_ret = self.func(*temp) - return np.rot90([temp_ret], -1) # monte_carlo_integrator expects a column + temp_ret = self.identity_convert_togpu(self.func(*temp)) + # column vector (n,1); cupy.rot90 does not accept array-likes/lists, and + # reshape is backend-agnostic and order-preserving (equiv. to the old + # np.rot90([temp_ret], -1)). + return temp_ret.reshape((-1, 1)) def calc_pdf(self, samples): - ''' - Similar to evaluate(), interfaces between sample formats. Must also handle - possibility of no prior for one of more dimensions - ''' n, _ = samples.shape temp_ret = self.xpy.ones((n, 1)) - # pdf functions expect 1D rows + # Prior pdfs are host functions in general; evaluate them on CPU samples + # and convert the result back to the active backend. + samples_cpu = self.identity_convert(samples) for index in range(len(self.curr_args)): if self.curr_args[index] in self.prior_pdf: pdf_func = self.prior_pdf[self.curr_args[index]] - temp_samples = samples[:,index] - # monte carlo integrator expects a column - temp_ret *= pdf_func(temp_samples).reshape( temp_ret.shape) #self.xpy.rot90([pdf_func(temp_samples)], -1) + temp_samples = samples_cpu[:,index] + pdf_vals = self.identity_convert_togpu(pdf_func(temp_samples)) + temp_ret *= pdf_vals.reshape( temp_ret.shape) return temp_ret def setup(self,n_comp=None,**kwargs): - """ - setup - - Call after add_parameter - """ integrator_func = kwargs['integrator_func'] if "integrator_func" in kwargs else None mcsamp_func = kwargs['mcsamp_func'] if "mcsamp_func" in kwargs else None proc_count = kwargs['proc_count'] if "proc_count" in kwargs else None @@ -221,105 +183,91 @@ def setup(self,n_comp=None,**kwargs): gmm_epsilon = kwargs['gmm_epsilon'] if "gmm_epsilon" in kwargs else None L_cutoff = kwargs["L_cutoff"] if "L_cutoff" in kwargs else None tempering_exp = kwargs["tempering_exp"] if "tempering_exp" in kwargs else 1.0 + tempering_adapt = kwargs["tempering_adapt"] if "tempering_adapt" in kwargs else False + ess_target = kwargs["ess_target"] if "ess_target" in kwargs else None + ess_floor = kwargs["ess_floor"] if "ess_floor" in kwargs else None lnw_failure_cut = kwargs["lnw_failure_cut"] if "lnw_failure_cut" in kwargs else None nmax = kwargs["nmax"] if "nmax" in kwargs else 1e6 neff = kwargs["neff"] if "neff" in kwargs else 1000 - n = kwargs["n"] if "n" in kwargs else min(1000, nmax) # chunk size + n = kwargs["n"] if "n" in kwargs else min(1000, nmax) - self.n = n # this needs to be set - self.curr_args = self.params_ordered # assume we integrate over all. State variable used in a few places + self.n = n + self.curr_args = self.params_ordered if 'gmm_dict' in list(kwargs.keys()): - gmm_dict = kwargs['gmm_dict'] # required + gmm_dict = kwargs['gmm_dict'] else: gmm_dict = None dim = len(self.params_ordered) bounds=[] for param in self.params_ordered: bounds.append([self.llim[param], self.rlim[param]]) - raw_bounds = np.array(bounds) + raw_bounds = self.xpy.array(bounds) if gmm_dict is None: + # See note in integrate(): dict keys must be host ints, not 0-d + # cupy arrays (which are unhashable). bounds = {} for indx in np.arange(len(raw_bounds)): bounds[(indx,)] = raw_bounds[indx] bounds=raw_bounds if correlate_all_dims: gmm_dict = {tuple(range(dim)):None} - bounds = {tuple(np.arange(len(bounds))): raw_bounds} + bounds = {tuple(range(dim)): raw_bounds} else: gmm_dict = {} for i in range(dim): gmm_dict[(i,)] = None else: - # create bounds that depend on the dimension specifiers in the gmm integrator bounds ={} for dims in gmm_dict: n_dims = len(dims) - bounds_here = np.empty((n_dims,2)) - for indx in np.arange(n_dims): - bounds_here[indx] = raw_bounds[dims[indx]] # pull out bounds index + bounds_here = self.xpy.empty((n_dims,2)) + for indx in range(n_dims): + bounds_here[indx] = raw_bounds[dims[indx]] bounds[dims]=bounds_here - - # instantiate an integrator object, as that is front end to all the things we need. - # we will need some dummy things self.integrator = monte_carlo.integrator(dim, bounds, gmm_dict, n_comp, n=self.n, prior=self.calc_pdf, - user_func=integrator_func, proc_count=proc_count,L_cutoff=L_cutoff,gmm_adapt=gmm_adapt,gmm_epsilon=gmm_epsilon,tempering_exp=tempering_exp) # reflect=reflect, + user_func=integrator_func, proc_count=proc_count,L_cutoff=L_cutoff,gmm_adapt=gmm_adapt,gmm_epsilon=gmm_epsilon,tempering_exp=tempering_exp, + tempering_adapt=tempering_adapt, ess_target=ess_target, ess_floor=ess_floor) def update_sampling_prior(self,ln_weights, n_history,tempering_exp=1,log_scale_weights=True,floor_integrated_probability=0,external_rvs=None,**kwargs): - """ - update_sampling_prior - - Attempt to duplicate code inside 'integrate' to update sampling prior based on information potentially including externally-obtained samples - """ rvs_here = self._rvs if external_rvs: rvs_here = external_rvs - xpy_here = np # force np internal, because we don't have GMM implemented - - # apply tempering exponent (structurally slightly different than in low-level code - not just to likelihood) - ln_weights = np.array(self.identity_convert(ln_weights)) # force copy + ln_weights = self.xpy.array(self.identity_convert(ln_weights)) ln_weights *= tempering_exp - gmm_dict = self.integrator.gmm_dict # direct acess + gmm_dict = self.integrator.gmm_dict - n_history_to_use = np.min([n_history, len(ln_weights), len(rvs_here[self.params_ordered[0]])] ) + n_history_to_use = self.xpy.min([n_history, len(ln_weights), len(rvs_here[self.params_ordered[0]])] ) - # Create appropriate history array - # keep in GPU form, because we don't need it all ! Only adapt in SOME dimensions, reduce bandwidth! sample_array = self.xpy.empty( (len(self.params_ordered), n_history_to_use)) for indx, p in enumerate(self.params_ordered): sample_array[indx] = rvs_here[p][-n_history_to_use:] sample_array = sample_array.T - - for dim_group in gmm_dict: # iterate over grouped dimensions + for dim_group in gmm_dict: if self.integrator.gmm_adapt: if (dim_group in self.integrator.gmm_adapt): - if not(self.integrator.gmm_adapt[dim_group]): # disabling adaptation requires user *specifically request* not to use that dimension set; all other choices lead to adaptation + if not(self.integrator.gmm_adapt[dim_group]): continue - # create a matrix of the left and right limits for this set of dimensions - new_bounds = np.empty((len(dim_group), 2)) + new_bounds = self.xpy.empty((len(dim_group), 2)) new_bounds = self.integrator.bounds[dim_group] - model = self.integrator.gmm_dict[dim_group] # get model for this set of dimensions - temp_samples = np.empty((n_history_to_use, len(dim_group))) + model = self.integrator.gmm_dict[dim_group] + temp_samples = self.xpy.empty((n_history_to_use, len(dim_group))) index = 0 for dim in dim_group: - # get samples corresponding to the current model - # send from GPU as needed temp_samples[:,index] = self.identity_convert(sample_array[:,dim]) index += 1 - # don't train with nan! - if any(np.isnan(ln_weights)): - ok_indx = ~np.isnan(ln_weights) + if self.xpy.any(self.xpy.isnan(ln_weights)): + ok_indx = ~self.xpy.isnan(ln_weights) temp_samples = temp_samples[ok_indx] ln_weights = ln_weights[ok_indx] if model is None: - # model doesn't exist yet if isinstance(self.integrator.n_comp, int) and self.integrator.n_comp != 0: model = GMM.gmm(self.integrator.n_comp, new_bounds,epsilon=self.integrator.gmm_epsilon) model.fit(temp_samples, log_sample_weights=ln_weights) @@ -332,11 +280,8 @@ def update_sampling_prior(self,ln_weights, n_history,tempering_exp=1,log_scale_w def draw_simplified(self,n,*args,**kwargs): - """ - Draw a set of random variates for parameter(s) args. Left and right limits are handed to the function. If args is None, then draw *all* parameters. 'rdict' parameter is a boolean. If true, returns a dict matched to param name rather than list. rvs must be either a list of uniform random variates to transform for sampling, or an integer number of samples to draw. - """ n_samples = int(n) - self.integrator.n = n # need to override this, so we sample with correct size + self.integrator.n = n if len(args) == 0: args = self.params @@ -346,8 +291,6 @@ def draw_simplified(self,n,*args,**kwargs): if 'save_no_samples' in list(kwargs.keys()): save_no_samples = kwargs['save_no_samples'] - - # Allocate memory. rv = self.xpy.empty((n_params, n_samples), dtype=np.float64) joint_p_s = self.xpy.ones(n_samples, dtype=np.float64) joint_p_prior = self.xpy.ones(n_samples, dtype=np.float64) @@ -365,14 +308,6 @@ def draw_simplified(self,n,*args,**kwargs): def integrate_log(self, func, *args,**kwargs): - ''' - Integrate the specified function over the specified parameters. - - func: function to integrate - - Simple wrapper to standardize interface - - ''' args_passed = {} args_passed.update(kwargs) args_passed['use_lnL']=True @@ -380,56 +315,12 @@ def integrate_log(self, func, *args,**kwargs): return integrate(func, *args, args_passed) def integrate(self, func, *args,**kwargs): - ''' - Integrate the specified function over the specified parameters. - - func: function to integrate - - args: list of parameters to integrate over - - direct_eval (bool): whether func can be evaluated directly with monte_carlo_integrator - format or not - - n_comp: number of gaussian components for model - - n: number of samples per iteration - - nmax: maximum number of samples for all iterations - - write_to_file (bool): write data to file - - gmm_dict: dictionary of dimensions and mixture models (see monte_carlo_integrator - documentation for more) - - var_thresh: result variance threshold for termination - - min_iter: minimum number of integrator iterations - - max_iter: maximum number of integrator iterations - - neff: eff_samp cutoff for termination - - reflect (bool): whether or not to reflect samples over boundaries (you should - basically never use this, it's really slow) - - mcsamp_func: function to be executed before mcsampler_new terminates (for example, - to print results or debugging info) - - integrator_func: function to be executed each iteration of the integrator (for - example, to print intermediate results) - - proc_count: size of multiprocessing pool. set to None to not use multiprocessing - tempering_exp -- Exponent to raise the weights of the 1-D marginalized histograms for adaptive sampling prior generation, by default it is 0 which will turn off adaptive sampling regardless of other settings - temper_log -- Adapt in min(ln L, 10^(-5))^tempering_exp - - max_err : Maximum number of errors allowed for GMM sampler - ''' nmax = kwargs["nmax"] if "nmax" in kwargs else 1e6 neff = kwargs["neff"] if "neff" in kwargs else 1000 - n = kwargs["n"] if "n" in kwargs else min(1000, nmax) # chunk size + n = kwargs["n"] if "n" in kwargs else min(1000, nmax) n_comp = kwargs["n_comp"] if "n_comp" in kwargs else 1 if 'gmm_dict' in list(kwargs.keys()): - gmm_dict = kwargs['gmm_dict'] # required + gmm_dict = kwargs['gmm_dict'] else: gmm_dict = None reflect = kwargs['reflect'] if "reflect" in kwargs else False @@ -446,17 +337,21 @@ def integrate(self, func, *args,**kwargs): gmm_epsilon = kwargs['gmm_epsilon'] if "gmm_epsilon" in kwargs else None L_cutoff = kwargs["L_cutoff"] if "L_cutoff" in kwargs else None tempering_exp = kwargs["tempering_exp"] if "tempering_exp" in kwargs else 1.0 + # --adapt-adapt: ESS-self-tuned refit exponent (previously silently + # dropped by this sampler; only mcsampler/mcsamplerGPU honored it) + tempering_adapt = kwargs["tempering_adapt"] if "tempering_adapt" in kwargs else False + ess_target = kwargs["ess_target"] if "ess_target" in kwargs else None + ess_floor = kwargs["ess_floor"] if "ess_floor" in kwargs else None lnw_failure_cut = kwargs["lnw_failure_cut"] if "lnw_failure_cut" in kwargs else None -# tempering_exp = kwargs["adapt_weight_exponent"] if "adapt_weight_exponent" in kwargs else 1.0 - max_err = kwargs["max_err"] if "max_err" in kwargs else 10 # default + max_err = kwargs["max_err"] if "max_err" in kwargs else 10 - verbose = kwargs["verbose"] if "verbose" in kwargs else False # default - super_verbose = kwargs["super_verbose"] if "super_verbose" in kwargs else False # default - dict_return_q = kwargs["dict_return"] if "dict_return" in kwargs else False # default. Method for passing back rich data structures for debugging + verbose = kwargs["verbose"] if "verbose" in kwargs else False + super_verbose = kwargs["super_verbose"] if "super_verbose" in kwargs else False + dict_return_q = kwargs["dict_return"] if "dict_return" in kwargs else False - tripwire_fraction = kwargs["tripwire_fraction"] if "tripwire_fraction" in kwargs else 2 # make it impossible to trigger - tripwire_epsilon = kwargs["tripwire_epsilon"] if "tripwire_epsilon" in kwargs else 0.001 # if we are not reasonably far away from unity, fail! + tripwire_fraction = kwargs["tripwire_fraction"] if "tripwire_fraction" in kwargs else 2 + tripwire_epsilon = kwargs["tripwire_epsilon"] if "tripwire_epsilon" in kwargs else 0.001 use_lnL = kwargs["use_lnL"] if "use_lnL" in kwargs else False return_lnI = kwargs["return_lnI"] if "return_lnI" in kwargs else False @@ -464,7 +359,6 @@ def integrate(self, func, *args,**kwargs): bFairdraw = kwargs["igrand_fairdraw_samples"] if "igrand_fairdraw_samples" in kwargs else False n_extr = kwargs["igrand_fairdraw_samples_max"] if "igrand_fairdraw_samples_max" in kwargs else None - # set up a lot of preliminary stuff self.func = func self.curr_args = args if n_comp is None: @@ -474,36 +368,36 @@ def integrate(self, func, *args,**kwargs): bounds=[] for param in args: bounds.append([self.llim[param], self.rlim[param]]) - raw_bounds = np.array(bounds) + raw_bounds = self.xpy.array(bounds) bounds=None - # generate default gmm_dict if not specified if gmm_dict is None: + # NOTE: dim-group / bounds dict keys must be *host* integers. Building + # them with self.xpy.arange would produce unhashable 0-d cupy arrays + # on GPU; keep this bookkeeping on the CPU with range/np.arange. bounds = {} for indx in np.arange(len(raw_bounds)): bounds[(indx,)] = raw_bounds[indx] bounds=raw_bounds if correlate_all_dims: gmm_dict = {tuple(range(dim)):None} - bounds = {tuple(np.arange(len(bounds))): raw_bounds} + bounds = {tuple(range(dim)): raw_bounds} else: gmm_dict = {} for i in range(dim): gmm_dict[(i,)] = None else: - # create bounds that depend on the dimension specifiers in the gmm integrator bounds ={} for dims in gmm_dict: n_dims = len(dims) - bounds_here = np.empty((n_dims,2)) - for indx in np.arange(n_dims): - bounds_here[indx] = raw_bounds[dims[indx]] # pull out bounds index + bounds_here = self.xpy.empty((n_dims,2)) + for indx in range(n_dims): + bounds_here[indx] = raw_bounds[dims[indx]] bounds[dims]=bounds_here -# bounds = np.array(bounds) - # do the integral integrator = monte_carlo.integrator(dim, bounds, gmm_dict, n_comp, n=n, prior=self.calc_pdf, - user_func=integrator_func, proc_count=proc_count,L_cutoff=L_cutoff,gmm_adapt=gmm_adapt,gmm_epsilon=gmm_epsilon,tempering_exp=tempering_exp) # reflect=reflect, + user_func=integrator_func, proc_count=proc_count,L_cutoff=L_cutoff,gmm_adapt=gmm_adapt,gmm_epsilon=gmm_epsilon,tempering_exp=tempering_exp, + tempering_adapt=tempering_adapt, ess_target=ess_target, ess_floor=ess_floor) if not direct_eval: func = self.evaluate if use_lnL: @@ -512,71 +406,69 @@ def integrate(self, func, *args,**kwargs): print(" ==> internal calculations and return values are lnI ") integrator.integrate(func, min_iter=min_iter, max_iter=max_iter, var_thresh=var_thresh, neff=neff, nmax=nmax,max_err=max_err,verbose=verbose,progress=super_verbose,tripwire_fraction=tripwire_fraction,tripwire_epsion=tripwire_epsilon,use_lnL=use_lnL,return_lnI=return_lnI,lnw_failure_cut=lnw_failure_cut) - # get results - self.n = int(integrator.n) self.ntotal = int(integrator.ntotal) integral = integrator.integral print("Result ",integrator.scaled_error_squared, integrator.integral) if not(return_lnI): - error_squared = integrator.scaled_error_squared * np.exp(integrator.log_error_scale_factor)/ (self.ntotal/self.n) + error_squared = integrator.scaled_error_squared * self.xpy.exp(integrator.log_error_scale_factor)/ (self.ntotal/self.n) else: - error_squared = integrator.scaled_error_squared - np.log(self.ntotal/self.n) + error_squared = integrator.scaled_error_squared - self.xpy.log(self.ntotal/self.n) eff_samp = integrator.eff_samp sample_array = integrator.cumulative_samples if not(return_lnI): - value_array = np.exp(integrator.cumulative_values) # stored as ln(integrand) ! + value_array = self.xpy.exp(integrator.cumulative_values) else: value_array = integrator.cumulative_values p_array = integrator.cumulative_p_s prior_array = integrator.cumulative_p - # user-defined function if mcsamp_func is not None: mcsamp_func(self, integrator) - # populate dictionary - + # Store sample history on the host so downstream (CPU) consumers -- + # weights, CDFs, posterior plots -- work regardless of backend. index = 0 for param in args: - self._rvs[param] = sample_array[:,index] + self._rvs[param] = self.identity_convert(sample_array[:,index]) index += 1 - self._rvs['joint_prior'] = prior_array - self._rvs['joint_s_prior'] = p_array - self._rvs['integrand'] = value_array + self._rvs['joint_prior'] = self.identity_convert(prior_array) + self._rvs['joint_s_prior'] = self.identity_convert(p_array) + self._rvs['integrand'] = self.identity_convert(value_array) - # Do a fair draw of points, if option is set. CAST POINTS BACK TO NUMPY, IDEALLY if bFairdraw and not(n_extr is None): - n_extr = int(np.min([n_extr,1.5*eff_samp,1.5*neff])) + # scalars: use Python min on floats. self.xpy.min([list]) fails on cupy + # (cupy.min has no list overload -> "'list' object has no attribute 'min'"), + # which crashed the GMM sampler's fairdraw export on GPU. + n_extr = int(min(float(n_extr), 1.5*float(eff_samp), 1.5*float(neff))) print(" Fairdraw size : ", n_extr) if return_lnI: ln_wt = integrator.cumulative_values else: - ln_wt = np.log(value_array) - ln_wt += np.log(prior_array/p_array) - ln_wt += - scipy.special.logsumexp(ln_wt) - wt = np.exp(ln_wt) + ln_wt = self.xpy.log(value_array) + ln_wt += self.xpy.log(prior_array/p_array) + ln_wt += - scipy.special.logsumexp(self.identity_convert(ln_wt)) + wt = self.xpy.exp(ln_wt) if n_extr < len(value_array): - indx_list = np.random.choice(np.arange(len(wt)), size=n_extr,replace=True,p=wt) # fair draw - # FIXME: See previous FIXME + indx_list = self.identity_convert(self.xpy.random.choice(self.xpy.arange(len(wt)), size=n_extr,replace=True,p=wt)) for key in list(self._rvs.keys()): if isinstance(key, tuple): self._rvs[key] = self._rvs[key][:,indx_list] else: self._rvs[key] = self._rvs[key][indx_list] - # if special return structure, fill it dict_return = {} if dict_return_q: dict_return["integrator"] = integrator - # write data to file if write_to_file: - dat_out = np.c_[sample_array, value_array, p_array] - np.savetxt('mcsampler_data.txt', dat_out, + dat_out = self.xpy.c_[sample_array, value_array, p_array] + np.savetxt('mcsampler_data.txt', self.identity_convert(dat_out), header=" ".join(['sample_array', 'value_array', 'p_array'])) - return integral, error_squared, eff_samp, dict_return + # Return scalars on the host so callers can do plain numpy arithmetic + # (np.sqrt, np.log, np.array([...])) on the results. + return self.identity_convert(integral), self.identity_convert(error_squared), self.identity_convert(eff_samp), dict_return def inv_uniform_cdf(a, b, x): @@ -588,46 +480,37 @@ def gauss_samp(mu, std, x): def gauss_samp_withfloor(mu, std, myfloor, x): return 1.0/np.sqrt(2*np.pi*std**2)*np.exp(-(x-mu)**2/2/std**2) + myfloor -#gauss_samp_withfloor_vector = np.vectorize(gauss_samp_withfloor,excluded=['mu','std','myfloor'],otypes=[np.float64]) gauss_samp_withfloor_vector = np.vectorize(gauss_samp_withfloor,otypes=[np.float64]) -# Mass ratio. PDF propto 1/(1+q)^2. Defined so mass ratio is < 1 -# expr = Integrate[1/(1 + q)^2, q] -# scale = (expr /. q -> qmax ) - (expr /. q -> qmin) -# (expr - (expr /. q -> qmin))/scale == x // Simplify -# q /. Solve[%, q][[1]] // Simplify -# % // CForm def q_samp_vector(qmin,qmax,x): scale = 1./(1+qmin) - 1./(1+qmax) return 1/np.power((1+x),2)/scale def q_cdf_inv_vector(qmin,qmax,x): - return np.array((qmin + qmax*qmin + qmax*x - qmin*x)/(1 + qmax - qmax*x + qmin*x),dtype=np.float128) + return np.array((qmin + qmax*qmin + qmax*x - qmin*x)/(1 + qmax - qmax*x + qmin*x),dtype=RiftFloat) -# total mass. Assumed used with q. 2M/Mmax^2-Mmin^2 def M_samp_vector(Mmin,Mmax,x): scale = 2./(Mmax**2 - Mmin**2) return x*scale def cos_samp(x): - return np.sin(x)/2 # x from 0, pi + return np.sin(x)/2 def dec_samp(x): - return np.sin(x+np.pi/2)/2 # x from 0, pi + return np.sin(x+np.pi/2)/2 cos_samp_vector = np.vectorize(cos_samp,otypes=[np.float64]) dec_samp_vector = np.vectorize(dec_samp,otypes=[np.float64]) def cos_samp_cdf_inv_vector(p): - return np.arccos( 2*p-1) # returns from 0 to pi + return np.arccos( 2*p-1) def dec_samp_cdf_inv_vector(p): - return np.arccos(2*p-1) - np.pi/2 # target from -pi/2 to pi/2 + return np.arccos(2*p-1) - np.pi/2 def pseudo_dist_samp(r0,r): - return r*r*np.exp( - (r0/r)*(r0/r)/2. + r0/r)+0.01 # put a floor on probability, so we converge. Note this floor only cuts out NEARBY distances + return r*r*np.exp( - (r0/r)*(r0/r)/2. + r0/r)+0.01 -#pseudo_dist_samp_vector = np.vectorize(pseudo_dist_samp,excluded=['r0'],otypes=[np.float64]) pseudo_dist_samp_vector = np.vectorize(pseudo_dist_samp,otypes=[np.float64]) def delta_func_pdf(x_0, x): @@ -641,36 +524,12 @@ def delta_func_samp(x_0, x): delta_func_samp_vector = np.vectorize(delta_func_samp, otypes=[np.float64]) class HealPixSampler(object): - """ - Class to sample the sky using a FITS healpix map. Equivalent to a joint 2-D pdf in RA and dec. - """ - @staticmethod def thph2decra(th, ph): - """ - theta/phi to RA/dec - theta (north to south) (0, pi) - phi (east to west) (0, 2*pi) - declination: north pole = pi/2, south pole = -pi/2 - right ascension: (0, 2*pi) - - dec = pi/2 - theta - ra = phi - """ return np.pi/2-th, ph @staticmethod def decra2thph(dec, ra): - """ - theta/phi to RA/dec - theta (north to south) (0, pi) - phi (east to west) (0, 2*pi) - declination: north pole = pi/2, south pole = -pi/2 - right ascension: (0, 2*pi) - - theta = pi/2 - dec - ra = phi - """ return np.pi/2-dec, ra def __init__(self, skymap, massp=1.0): @@ -689,43 +548,31 @@ def massp(self, value): norm = self.renormalize() def renormalize(self): - """ - Identify the points contributing to the overall cumulative probability distribution, and set the proper normalization. - """ res = healpy.npix2nside(len(self.skymap)) self.pdf_sorted = sorted([(p, i) for i, p in enumerate(self.skymap)], reverse=True) self.valid_points_decra = [] - cdf, np = 0, 0 + cdf, np_count = 0, 0 for p, i in self.pdf_sorted: if p == 0: - continue # Can't have a zero prior + continue self.valid_points_decra.append(HealPixSampler.thph2decra(*healpy.pix2ang(res, i))) cdf += p if cdf > self._massp: break self._renorm = cdf - # reset to indicate we'd need to recalculate this self.valid_points_hist = None return self._renorm def __expand_valid(self, min_p=1e-7): - # - # Determine what the 'quanta' of probabilty is - # if self._massp == 1.0: - # This is to ensure we don't blow away everything because the map - # is very spread out min_p = min(min_p, max(self.skymap)) else: - # NOTE: Only valid if CDF descending order is kept min_p = self.pseudo_pdf(*self.valid_points_decra[-1]) self.valid_points_hist = [] ns = healpy.npix2nside(len(self.skymap)) - # Renormalize first so that the vector histogram is properly normalized self._renorm = 0 - # Account for probability lost due to cut off for i, v in enumerate(self.skymap >= min_p): self._renorm += self.skymap[i] if v else 0 @@ -738,32 +585,21 @@ def __expand_valid(self, min_p=1e-7): self.valid_points_hist = np.array(self.valid_points_hist).T def pseudo_pdf(self, dec_in, ra_in): - """ - Return pixel probability for a given dec_in and ra_in. Note, uses healpy functions to identify correct pixel. - """ th, ph = HealPixSampler.decra2thph(dec_in, ra_in) res = healpy.npix2nside(len(self.skymap)) return self.skymap[healpy.ang2pix(res, th, ph)]/self._renorm def pseudo_cdf_inverse(self, dec_in=None, ra_in=None, ndraws=1, stype='vecthist'): - """ - Select points from the skymap with a distribution following its corresponding pixel probability. If dec_in, ra_in are suupplied, they are ignored except that their shape is reproduced. If ndraws is supplied, that will set the shape. Will return a 2xN np array of the (dec, ra) values. - stype controls the type of sampling done to retrieve points. Valid choices are - 'rejsamp': Rejection sampling: accurate but slow - 'vecthist': Expands a set of points into a larger vector with the multiplicity of the points in the vector corresponding roughly to the probability of drawing that point. Because this is not an exact representation of the proability, some points may not be represented at all (less than quantum of minimum probability) or inaccurately (a significant fraction of the fundamental quantum). - """ - if ra_in is not None: ndraws = len(ra_in) if ra_in is None: ra_in, dec_in = np.zeros((2, ndraws)) if stype == 'rejsamp': - # FIXME: This is only valid under descending ordered CDF summation ceiling = max(self.skymap) - i, np = 0, len(self.valid_points_decra) + i, np_count = 0, len(self.valid_points_decra) while i < len(ra_in): - rnd_n = np.random.randint(0, np) + rnd_n = np.random.randint(0, np_count) trial = np.random.uniform(0, ceiling) if trial <= self.pseudo_pdf(*self.valid_points_decra[rnd_n]): dec_in[i], ra_in[i] = self.valid_points_decra[rnd_n] @@ -772,77 +608,43 @@ def pseudo_cdf_inverse(self, dec_in=None, ra_in=None, ndraws=1, stype='vecthist' elif stype == 'vecthist': if self.valid_points_hist is None: self.__expand_valid() - np = self.valid_points_hist.shape[1] - rnd_n = np.random.randint(0, np, len(ra_in)) + np_count = self.valid_points_hist.shape[1] + rnd_n = np.random.randint(0, np_count, len(ra_in)) dec_in, ra_in = self.valid_points_hist[:,rnd_n] return np.array([dec_in, ra_in]) else: raise ValueError("%s is not a recgonized sampling type" % stype) -#pseudo_dist_samp_vector = np.vectorize(pseudo_dist_samp,excluded=['r0'],otypes=[np.float64]) pseudo_dist_samp_vector = np.vectorize(pseudo_dist_samp,otypes=[np.float64]) def sanityCheckSamplerIntegrateUnity(sampler,*args,**kwargs): return sampler.integrate(lambda *args: 1,*args,**kwargs) -### -### CONVERGENCE TESTS -### - - -# neff by another name: -# - value: tests for 'smooth' 1-d cumulative distributions -# - require require the most significant-weighted point be less than p of all cumulative probability -# - this test is *equivalent* to neff > 1/p -# - provided to illustrate the interface def convergence_test_MostSignificantPoint(pcut, rvs, params): - weights = rvs["weights"] #rvs["integrand"]* rvs["joint_prior"]/rvs["joint_s_prior"] + weights = rvs["weights"] indxmax = np.argmax(weights) wtSum = np.sum(weights) return weights[indxmax]/wtSum < pcut - -# normality test: is the MC integral normally distributed, with a small standard deviation? -# - value: tests for converged integral -# - arguments: -# - ncopies: # of sub-integrals -# - pcutNormalTest Threshold p-value for normality test -# - sigmaCutErrorThreshold Threshold relative error in the integral -# - implement normality test on **log(integral)** since the log should also be normally distributed if well converged -# - this helps us handle large orders-of-magnitude differences -# - compatible with a *relative* error threshold on integral -# - only works for *positive-definite* integrands -# - other python normality tests: -# scipy.stats.shapiro -# scipy.stats.anderson -# WARNING: -# - this test assumes *unsorted* past history: the 'ncopies' segments are assumed independent. -import scipy.stats as stats def convergence_test_NormalSubIntegrals(ncopies, pcutNormalTest, sigmaCutRelativeErrorThreshold, rvs, params): - weights = rvs["integrand"]* rvs["joint_prior"]/rvs["joint_s_prior"] # rvs["weights"] # rvs["weights"] is *sorted* (side effect?), breaking test. Recalculated weights are not. Use explicitly calculated weights until sorting effect identified -# weights = weights /np.sum(weights) # Keep original normalization, so the integral values printed to stdout have meaning relative to the overall integral value. No change in code logic : this factor scales out (from the log, below) + weights = rvs["integrand"]* rvs["joint_prior"]/rvs["joint_s_prior"] igrandValues = np.zeros(ncopies) - len_part = np.int(len(weights)/ncopies) # deprecated: np.floor->np.int + len_part = int(len(weights)/ncopies) for indx in np.arange(ncopies): - igrandValues[indx] = np.log(np.mean(weights[indx*len_part:(indx+1)*len_part])) # change to mean rather than sum, so sub-integrals have meaning - igrandValues= np.sort(igrandValues)#[2:] # Sort. Useful in reports - valTest = stats.normaltest(igrandValues)[1] # small value is implausible - igrandSigma = (np.std(igrandValues))/np.sqrt(ncopies) # variance in *overall* integral, estimated from variance of sub-integrals + igrandValues[indx] = np.log(np.mean(weights[indx*len_part:(indx+1)*len_part])) + igrandValues= np.sort(igrandValues) + valTest = stats.normaltest(igrandValues)[1] + igrandSigma = (np.std(igrandValues))/np.sqrt(ncopies) print(" Test values on distribution of log evidence: (gaussianity p-value; standard deviation of ln evidence) ", valTest, igrandSigma) print(" Ln(evidence) sub-integral values, as used in tests : ", igrandValues) - return valTest> pcutNormalTest and igrandSigma < sigmaCutRelativeErrorThreshold # Test on left returns a small value if implausible. Hence pcut ->0 becomes increasingly difficult (and requires statistical accidents). Test on right requires relative error in integral also to be small when pcut is small. FIXME: Give these variables two different names - - + return valTest> pcutNormalTest and igrandSigma < sigmaCutRelativeErrorThreshold from . import gaussian_mixture_model as GMM def create_wide_single_component_prior(bounds, epsilon=None): - """ - create_wide_single_component_prior(bounds) : returns a gmm dictionary which is very wide - """ model = GMM.gmm(1, bounds, epsilon=epsilon) widths = np.array([ bounds[k][1] - bounds[k][0] for k in np.arange(len(bounds))]) - model.means = [np.array([np.mean(bounds[k]) for k in np.arange(len(bounds))]) ] # single component + model.means = [np.array([np.mean(bounds[k]) for k in np.arange(len(bounds))]) ] model.covariances = [np.diag( widths**2)] model.weights = [1] model.adapt = [False] diff --git a/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsamplerGPU.py b/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsamplerGPU.py index 86241f966..9070e52f0 100644 --- a/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsamplerGPU.py +++ b/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsamplerGPU.py @@ -11,6 +11,7 @@ import numpy np=numpy #import numpy as np +from RIFT.precision import RiftFloat # platform-portable replacement for np.float128 from scipy import integrate, interpolate, special import itertools import functools @@ -19,7 +20,7 @@ try: import cupy - import cupyx # needed for logsumexp + import cupyx.scipy.special # needed for logsumexp xpy_default=cupy try: xpy_special_default = cupyx.scipy.special @@ -315,15 +316,20 @@ def cdf_inverse_from_hist(self, P, param,old_style=False): - for now, do on the CPU, since this is done rarely and involves fairly small arrays - this is very wasteful, since we are casting back to the CPU for ALL our sampling points """ - if old_style or not(cupy_ok): - dat_cdf = identity_convert(self.histogram_cdf[param]) - dat_edges = identity_convert(self.histogram_edges[param]) + # Use the CPU path whenever this sampler is not actually running on the + # GPU (self.xpy is numpy). The GPU interp() calls cupy.searchsorted, which + # requires cupy arrays; the histograms are numpy when self.xpy is numpy, + # so the module-level cupy_ok flag is not the right gate here. Instance + # converters are used so nothing is force-pushed across backends. + if old_style or not(cupy_ok) or (self.xpy is np): + dat_cdf = self.identity_convert(self.histogram_cdf[param]) + dat_edges = self.identity_convert(self.histogram_edges[param]) y = np.interp( - identity_convert(P), dat_cdf, + self.identity_convert(P), dat_cdf, dat_edges, ) # Return the value in the original scaling. - return identity_convert_togpu(y)*self.x_max_minus_min[param] + self.x_min[param] + return self.identity_convert_togpu(y)*self.x_max_minus_min[param] + self.x_min[param] dat_cdf = self.histogram_cdf[param] dat_edges =self.histogram_edges[param] y = interp(P,dat_cdf,dat_edges) @@ -546,7 +552,7 @@ def update_sampling_prior(self,ln_weights, n_history,tempering_exp=1,log_scale_w weights_alt = self.xpy.maximum(weights_alt, 1e-5) # prevent negative weights, in case integrating function with lnL < 0 # now treat as sum weights_alt = weights_alt/(weights_alt.sum()) - if weights_alt.dtype == numpy.float128: + if weights_alt.dtype == RiftFloat: weights_alt = weights_alt.astype(numpy.float64,copy=False) def function_wrapper(f, p): @@ -621,7 +627,7 @@ def integrate_log(self, lnF, *args, xpy=xpy_default,**kwargs): # Determine stopping conditions # nmax = kwargs["nmax"] if "nmax" in kwargs else float("inf") - neff = kwargs["neff"] if "neff" in kwargs else numpy.float128("inf") + neff = kwargs["neff"] if "neff" in kwargs else RiftFloat("inf") n = int(kwargs["n"] if "n" in kwargs else min(1000, nmax)) convergence_tests = kwargs["convergence_tests"] if "convergence_tests" in kwargs else None save_no_samples = kwargs["save_no_samples"] if "save_no_samples" in kwargs else None @@ -721,8 +727,10 @@ def integrate_log(self, lnF, *args, xpy=xpy_default,**kwargs): lnL= lnF(**unpacked) # protect order using dictionary # take log if we are NOT using lnL if cupy_ok: - if not(isinstance(lnL,cupy.ndarray)): - lnL = identity_convert_togpu(lnL) # send to GPU, if not already there + # instance converter tracks self.xpy; module-level converter would + # force lnL onto the GPU even in CPU mode (see note in integrate()). + if not(isinstance(lnL, self.xpy.ndarray)): + lnL = self.identity_convert_togpu(lnL) log_integrand =lnL + self.xpy.log(joint_p_prior) - self.xpy.log(joint_p_s) log_weights = tempering_exp*lnL + self.xpy.log(joint_p_prior) - self.xpy.log(joint_p_s) @@ -808,7 +816,7 @@ def inner(arg): weights_alt = self._rvs["log_integrand"][-n_history:]+np.max([maxlnL, 200]) # try to make sure we have some dynamic range here weights_alt = self.xpy.maximum(weights_alt, 1e-5) # prevent negative weights. NOTE THIS IS IMPORTANT: if you are integrating a function with lnL<0, use an offset! weights_alt = weights_alt/(weights_alt.sum()) - if weights_alt.dtype == numpy.float128: + if weights_alt.dtype == RiftFloat: weights_alt = weights_alt.astype(numpy.float64,copy=False) for itr, p in enumerate(self.params_ordered): @@ -980,7 +988,7 @@ def integrate(self, func, *args, **kwargs): # Determine stopping conditions # nmax = kwargs["nmax"] if "nmax" in kwargs else float("inf") - neff = kwargs["neff"] if "neff" in kwargs else numpy.float128("inf") + neff = kwargs["neff"] if "neff" in kwargs else RiftFloat("inf") n = int(kwargs["n"] if "n" in kwargs else min(1000, nmax)) convergence_tests = kwargs["convergence_tests"] if "convergence_tests" in kwargs else None save_no_samples = kwargs["save_no_samples"] if "save_no_samples" in kwargs else None @@ -1023,12 +1031,12 @@ def integrate(self, func, *args, **kwargs): if bShowEvaluationLog: print(" .... mcsampler : providing verbose output ..... ") - int_val1 = numpy.float128(0) + int_val1 = RiftFloat(0) self.ntotal = 0 maxval = -float("Inf") maxlnL = -float("Inf") eff_samp = 0 - mean, var = None, numpy.float128(0) # to prevent infinite variance due to overflow + mean, var = None, RiftFloat(0) # to prevent infinite variance due to overflow if cupy_ok: var = xpy_default.float64(0) # cupy doesn't have float128 @@ -1085,13 +1093,18 @@ def integrate(self, func, *args, **kwargs): fval = func(**unpacked) # Chris' original plan: note this insures the function arguments are tied to the parameters, using a dictionary. if cupy_ok: - if not(isinstance(fval,cupy.ndarray)): - fval = identity_convert_togpu(fval) # send to GPU, if not already there + # Use the *instance* converter (self.identity_convert_togpu), which + # tracks self.xpy. The module-level converter would force fval onto + # the GPU even when this sampler is running on the CPU (self.xpy is + # numpy by default), producing a numpy/cupy mismatch against the + # numpy joint_p_prior / joint_p_s built in draw_simplified. + if not(isinstance(fval, self.xpy.ndarray)): + fval = self.identity_convert_togpu(fval) # # Check if there is any practical contribution to the integral # - # FIXME: While not technically a fatal error, this will kill the + # FIXME: While not technically a fatal error, this will kill the # adaptive sampling if not(cupy_ok): # only do this check if not on GPU if fval.sum() == 0: @@ -1147,9 +1160,9 @@ def integrate(self, func, *args, **kwargs): if var is None: var=0 if mean is None: - mean=identity_convert_togpu(0.0) + mean=self.identity_convert_togpu(0.0) current_aggregate = [int(self.ntotal),mean, (self.ntotal-1)*var] - current_aggregate = update(current_aggregate, int_val,xpy=xpy_default) + current_aggregate = update(current_aggregate, int_val,xpy=self.xpy) outvals = finalize(current_aggregate) # print(var, outvals[-1]) var = outvals[-1] @@ -1159,7 +1172,7 @@ def integrate(self, func, *args, **kwargs): # running number of evaluations self.ntotal += n # FIXME: Likely redundant with int_val1 - mean = identity_convert_togpu(xpy_default.float64(int_val1/self.ntotal)) + mean = self.identity_convert_togpu(self.xpy.float64(int_val1/self.ntotal)) # this test should not be required (!), but ... nan can happen if np.isfinite(maxval): #not(np.isinf(maxval)): @@ -1228,7 +1241,7 @@ def inner(arg): weights_alt = weights_alt/(weights_alt.sum()) # Type convert as needed: if weights are float128, convert to float64; otherwise we hit a typing error later with bincount - if weights_alt.dtype == numpy.float128: + if weights_alt.dtype == RiftFloat: weights_alt = weights_alt.astype(numpy.float64,copy=False) # weights_alt = floor_integrated_probability*xpy_default.ones(len(weights_alt))/len(weights_alt) + (1-floor_integrated_probability)*weights_alt @@ -1412,7 +1425,7 @@ def q_samp_vector(qmin,qmax,x): scale = 1./(1+qmin) - 1./(1+qmax) return 1/numpy.power((1+x),2)/scale def q_cdf_inv_vector(qmin,qmax,x,xpy=xpy_default): - return np.array((qmin + qmax*qmin + qmax*x - qmin*x)/(1 + qmax - qmax*x + qmin*x),dtype=np.float128) + return np.array((qmin + qmax*qmin + qmax*x - qmin*x)/(1 + qmax - qmax*x + qmin*x),dtype=RiftFloat) # total mass. Assumed used with q. 2M/Mmax^2-Mmin^2 def M_samp_vector(Mmin,Mmax,x): diff --git a/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsamplerNFlow.py b/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsamplerNFlow.py index 965f7b27c..480cf7310 100644 --- a/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsamplerNFlow.py +++ b/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsamplerNFlow.py @@ -13,6 +13,7 @@ import numpy np=numpy #import numpy as np +from RIFT.precision import RiftFloat # platform-portable replacement for np.float128 from scipy import integrate, interpolate, special import itertools import functools @@ -62,7 +63,7 @@ try: import cupy - import cupyx # needed for logsumexp + import cupyx.scipy.special # needed for logsumexp xpy_default=cupy try: xpy_special_default = cupyx.scipy.special @@ -642,7 +643,7 @@ def update_sampling_prior(self, lnw, *args, xpy=xpy_default,no_protect_names=Tru # weights_alt = self.xpy.maximum(weights_alt, 1e-5) # prevent negative weights, in case integrating function with lnL < 0 # now treat as sum weights_alt = weights_alt/(weights_alt.sum()) - if weights_alt.dtype == numpy.float128: + if weights_alt.dtype == RiftFloat: weights_alt = weights_alt.astype(numpy.float64,copy=False) @@ -726,7 +727,7 @@ def integrate_log(self, lnF, *args, xpy=xpy_default,**kwargs): # Determine stopping conditions # nmax = kwargs["nmax"] if "nmax" in kwargs else float("inf") - neff = kwargs["neff"] if "neff" in kwargs else numpy.float128("inf") + neff = kwargs["neff"] if "neff" in kwargs else RiftFloat("inf") n = int(kwargs["n"] if "n" in kwargs else min(100000, nmax)) convergence_tests = kwargs["convergence_tests"] if "convergence_tests" in kwargs else None save_no_samples = kwargs["save_no_samples"] if "save_no_samples" in kwargs else None diff --git a/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsamplerPortfolio.py b/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsamplerPortfolio.py index 07fb1b2b8..016acce45 100644 --- a/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsamplerPortfolio.py +++ b/MonteCarloMarginalizeCode/Code/RIFT/integrators/mcsamplerPortfolio.py @@ -6,6 +6,7 @@ import numpy np=numpy #import numpy as np +from RIFT.precision import RiftFloat # platform-portable replacement for np.float128 from scipy import integrate, interpolate, special import itertools import functools @@ -17,7 +18,7 @@ try: import cupy - import cupyx # needed for logsumexp + import cupyx.scipy.special # needed for logsumexp xpy_default=cupy try: xpy_special_default = cupyx.scipy.special @@ -322,7 +323,7 @@ def integrate_log(self, lnF, *args, xpy=xpy_default,**kwargs): # Determine stopping conditions # nmax = kwargs["nmax"] if "nmax" in kwargs else float("inf") - neff = kwargs["neff"] if "neff" in kwargs else numpy.float128("inf") + neff = kwargs["neff"] if "neff" in kwargs else RiftFloat("inf") n = int(kwargs["n"] if "n" in kwargs else min(100000, nmax)) convergence_tests = kwargs["convergence_tests"] if "convergence_tests" in kwargs else None save_no_samples = kwargs["save_no_samples"] if "save_no_samples" in kwargs else None diff --git a/MonteCarloMarginalizeCode/Code/RIFT/integrators/statutils.py b/MonteCarloMarginalizeCode/Code/RIFT/integrators/statutils.py index 69d1986af..166d84562 100644 --- a/MonteCarloMarginalizeCode/Code/RIFT/integrators/statutils.py +++ b/MonteCarloMarginalizeCode/Code/RIFT/integrators/statutils.py @@ -1,4 +1,5 @@ import numpy +from RIFT.precision import RiftFloat # platform-portable replacement for np.float128 import scipy.special __author__ = "Chris Pankow , R. O'Shaughnessy" @@ -38,12 +39,12 @@ def cumvar(arr, mean=None, var=None, n=0): for algorithm details. """ if mean and var: - m, s = numpy.zeros(len(arr)+1), numpy.zeros(len(arr)+1,dtype=numpy.float128) + m, s = numpy.zeros(len(arr)+1), numpy.zeros(len(arr)+1,dtype=RiftFloat) m[0] = mean s[0] = var*(n-1) buf = numpy.array([0]) else: - m, s = numpy.zeros(arr.shape), numpy.zeros(arr.shape,dtype=numpy.float128) + m, s = numpy.zeros(arr.shape), numpy.zeros(arr.shape,dtype=RiftFloat) m[0] = arr[0] buf = numpy.array([]) diff --git a/MonteCarloMarginalizeCode/Code/RIFT/interpolators/BayesianLeastSquares.py b/MonteCarloMarginalizeCode/Code/RIFT/interpolators/BayesianLeastSquares.py index fa736f0ee..fc89b3706 100644 --- a/MonteCarloMarginalizeCode/Code/RIFT/interpolators/BayesianLeastSquares.py +++ b/MonteCarloMarginalizeCode/Code/RIFT/interpolators/BayesianLeastSquares.py @@ -7,6 +7,7 @@ import scipy.linalg as linalg import numpy as np +from RIFT.precision import RiftFloat # platform-portable replacement for np.float128 def fit_quadratic(x,y,x0=None,variable_symmetry_list=None,gamma_x=None,prior_x_gamma=None,prior_quadratic_gamma=None,verbose=False,n_digits=None,hard_regularize_negative=False,hard_regularize_scale=1): @@ -38,7 +39,7 @@ def fit_quadratic(x,y,x0=None,variable_symmetry_list=None,gamma_x=None,prior_x_g # Constant, linear, quadratic functions. # Beware of lambda: f_list = [(lambda x: k) for k in range(5)] does not work, but this does # f_list = [(lambda x,k=k: k) for k in range(5)] - f0 = [lambda z: np.ones(len(z),dtype=np.float128)] + f0 = [lambda z: np.ones(len(z),dtype=RiftFloat)] # indx_lookup_linear = {} # protect against packing errors # indx_here = len(f0) # f_linear = [] @@ -73,11 +74,11 @@ def fit_quadratic(x,y,x0=None,variable_symmetry_list=None,gamma_x=None,prior_x_g # print " Grid test " , pair, fn_now(np.array([1,0])), fn_now(np.array([0,1])), fn_now(np.array([1,1])) ,fn_now(np.array([1,-1])) - F = np.matrix(np.zeros((len(x), n_params_model),dtype=np.float128)) + F = np.matrix(np.zeros((len(x), n_params_model),dtype=RiftFloat)) for q in np.arange(n_params_model): - fval = f_list[q](np.array(x,dtype=np.float128)) + fval = f_list[q](np.array(x,dtype=RiftFloat)) F[:,q] = np.reshape(fval, (len(x),1)) - gamma = np.matrix( np.diag(np.ones(npts,dtype=np.float128))) + gamma = np.matrix( np.diag(np.ones(npts,dtype=RiftFloat))) if not(gamma_x is None): gamma = np.matrix(gamma_x) Gamma = F.T * gamma * F # Fisher matrix for the fit diff --git a/MonteCarloMarginalizeCode/Code/RIFT/lalsimutils.py b/MonteCarloMarginalizeCode/Code/RIFT/lalsimutils.py index c48fc1d4e..ad0d36bfb 100644 --- a/MonteCarloMarginalizeCode/Code/RIFT/lalsimutils.py +++ b/MonteCarloMarginalizeCode/Code/RIFT/lalsimutils.py @@ -51,7 +51,10 @@ try: import precession except ImportError: - print('Import Error - module missing. Please install the module "precession."') + # MUST go to stderr: many RIFT tools write data to stdout (composite/.dat files are + # built from it), and a stray stdout line here corrupts those files -> downstream + # parsers (CIP, util_CalHarvestGrid) choke on ragged/non-numeric rows. + print('Import Error - module missing. Please install the module "precession."', file=sys.stderr) def safe_int(mystr): try: return int(mystr) @@ -1864,7 +1867,15 @@ def copy_lsctables_sim_inspiral(self, row): setattr(swigrow, simattr,0) except: True # we don't really care if this doesn't happen - elif simattr in ["waveform", "source", "numrel_data", "taper","process_id"]: + continue + # Skip columns that are in the SCHEMA (validcolumns) but NOT present on this table + # INSTANCE. igwn_ligolw / current lalsuite write only the columns actually set, so + # the schema view and the written columns drift apart -- e.g. ILE-written + # sim_inspiral tables lack waveform/source/numrel_data/taper (and may lack numeric + # columns too). Bare getattr(row, missing) would raise AttributeError. + if not hasattr(row, simattr): + continue + if simattr in ["waveform", "source", "numrel_data", "taper","process_id"]: # unicode -> char* doesn't work setattr( swigrow, simattr, str(getattr(row, simattr)) ) elif not(lalmetaio_old_style) and ('end_time_ns' in simattr ): diff --git a/MonteCarloMarginalizeCode/Code/RIFT/likelihood/Q_fused_calmarg.py b/MonteCarloMarginalizeCode/Code/RIFT/likelihood/Q_fused_calmarg.py new file mode 100644 index 000000000..3f913667d --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/RIFT/likelihood/Q_fused_calmarg.py @@ -0,0 +1,276 @@ +""" +Fused calibration-marginalized factored log-likelihood (Option C). + +Two backends with identical results (validated against each other and the loop path): + - GPU: the CUDA kernels in cuda_Q_fused_calmarg*.cu (memory-efficient, one launch). + - CPU: pure-numpy implementations below (no CUDA needed -- runs on a laptop, and + gives an independent cross-check of the kernel math). + +cupy is imported lazily, so this module imports fine on a machine without CUDA. +See cuda_Q_fused_calmarg.cu for the array-layout documentation. +""" +from __future__ import division + +import os + +import numpy as np + +_kernel = None +_kernel_distmarg = None + + +def _load_kernel(filename, entry): + import cupy + path = os.path.join(os.path.dirname(__file__), filename) + if not os.path.isfile(path): + path = os.path.join(os.path.split(os.path.dirname(__file__))[0], filename) + with open(path, 'r') as f: + code = f.read() + return cupy.RawKernel(code, entry) + + +def _get_kernel(): + global _kernel + if _kernel is None: + _kernel = _load_kernel('cuda_Q_fused_calmarg.cu', "Q_fused_calmarg") + return _kernel + + +def _get_kernel_distmarg(): + global _kernel_distmarg + if _kernel_distmarg is None: + _kernel_distmarg = _load_kernel('cuda_Q_fused_calmarg_distmarg.cu', + "Q_fused_calmarg_distmarg") + return _kernel_distmarg + + +def _prep_log_w(cal_log_weights, n_cal): + """Return (log_w cupy array length n_cal, log_w_norm = log(n_cal)). + The cal-marginalized likelihood is the UNBIASED importance estimate + (1/n_cal) sum_c w_c L_c (w_c=prior/proposal, E[w]=1) -> normalization log(n_cal), + NOT logsumexp(log_w) (the biased self-normalized estimator). Uniform -> they agree.""" + import cupy + if cal_log_weights is None: + log_w = cupy.zeros(n_cal, dtype=cupy.float64) + else: + # cupy.asarray (not ascontiguousarray) so a host numpy array is accepted/transferred + log_w = cupy.ascontiguousarray(cupy.asarray(cal_log_weights, dtype=cupy.float64)) + assert log_w.shape == (n_cal,), \ + "cal_log_weights shape %s != (%d,)" % (log_w.shape, n_cal) + log_w_norm = float(np.log(n_cal)) + return log_w, log_w_norm + + +def Q_fused_calmarg_cupy(Q, A, ifirst, invDist, rho_sq, w_t, n_cal, N_window, + cal_log_weights=None, phase_marginalization=False, + threads_per_block=256): + """Compute the calibration-marginalized factored log likelihood per extrinsic + sample in a single kernel launch. + + Parameters + ---------- + Q : (n_det, npts_full, n_lms) complex128 + Per-detector rholm timeseries (transposed), holding n_cal contiguous + length-N_window calibration-realization blocks. + A : (n_det, n_ext, n_lms) complex128 + Per-detector conj(F * Ylm). + ifirst : (n_det, n_ext) int32 + Per-detector within-block window start index. + invDist : (n_ext,) float64 + distMpcRef / distMpc. + rho_sq : (n_ext, npts) float64 + Calibration-independent template (U,V) term, pre-summed over detectors. + w_t : (npts,) float64 + Composite-Simpson quadrature weights (including dx=deltaT). + n_cal : int + N_window : int + Per-realization block length inside Q (npts_full = N_window * n_cal). + + Returns + ------- + out : (n_ext,) float64 cupy array + log( (1/n_cal) sum_c sum_t w_t exp(lnL_t(j,c,t)) ). + """ + import cupy + Q = cupy.ascontiguousarray(Q, dtype=cupy.complex128) + A = cupy.ascontiguousarray(A, dtype=cupy.complex128) + ifirst = cupy.ascontiguousarray(ifirst, dtype=cupy.int32) + invDist = cupy.ascontiguousarray(invDist, dtype=cupy.float64) + rho_sq = cupy.ascontiguousarray(rho_sq, dtype=cupy.float64) + w_t = cupy.ascontiguousarray(w_t, dtype=cupy.float64) + + n_det, npts_full, n_lms = Q.shape + _, n_ext, _ = A.shape + npts = w_t.shape[0] + + assert A.shape == (n_det, n_ext, n_lms) + assert ifirst.shape == (n_det, n_ext) + assert invDist.shape == (n_ext,) + assert rho_sq.shape == (n_ext, npts) + assert npts_full == N_window * n_cal, \ + "npts_full=%d != N_window*n_cal=%d*%d" % (npts_full, N_window, n_cal) + + log_w, log_w_norm = _prep_log_w(cal_log_weights, n_cal) + out = cupy.empty(n_ext, dtype=cupy.float64) + + fn = _get_kernel() + grid = ((n_ext + threads_per_block - 1) // threads_per_block,) + block = (threads_per_block,) + fn(grid, block, ( + Q, A, ifirst, invDist, rho_sq, w_t, log_w, np.float64(log_w_norm), + np.int32(1 if phase_marginalization else 0), + np.int32(n_det), np.int32(n_cal), np.int32(N_window), np.int32(npts), + np.int32(n_lms), np.int32(n_ext), np.int32(npts_full), + out, + )) + return out + + +def Q_fused_calmarg_distmarg_cupy(Q, A, ifirst, invDist, rho_sq, w_t, + n_cal, N_window, distmarg, + cal_log_weights=None, phase_marginalization=False, + threads_per_block=256): + """Fused calibration + distance marginalization (Option C stage 2). + + Same as Q_fused_calmarg_cupy, but applies the distance-marginalization + loglikelihood on-board instead of the default helper. + + distmarg : dict with the distance-marginalization table and transform params: + lnI_array (ns, nt) float64, s0, ds, smin, smax, t0, dt, tmax, + xmin, xmax, sqrt_bmax, bref. + (s0/ds = s_array[0]/(s_array[1]-s_array[0]); smin/smax = s_array[0]/[-1]; + analogously for t; xmin=distMpcRef/dmax, xmax=distMpcRef/dmin.) + """ + import cupy + Q = cupy.ascontiguousarray(Q, dtype=cupy.complex128) + A = cupy.ascontiguousarray(A, dtype=cupy.complex128) + ifirst = cupy.ascontiguousarray(ifirst, dtype=cupy.int32) + invDist = cupy.ascontiguousarray(invDist, dtype=cupy.float64) + rho_sq = cupy.ascontiguousarray(rho_sq, dtype=cupy.float64) + w_t = cupy.ascontiguousarray(w_t, dtype=cupy.float64) + lnI = cupy.ascontiguousarray(distmarg["lnI_array"], dtype=cupy.float64) + + n_det, npts_full, n_lms = Q.shape + _, n_ext, _ = A.shape + npts = w_t.shape[0] + ns, nt = lnI.shape + + assert A.shape == (n_det, n_ext, n_lms) + assert ifirst.shape == (n_det, n_ext) + assert invDist.shape == (n_ext,) + assert rho_sq.shape == (n_ext, npts) + assert npts_full == N_window * n_cal, \ + "npts_full=%d != N_window*n_cal=%d*%d" % (npts_full, N_window, n_cal) + + log_w, log_w_norm = _prep_log_w(cal_log_weights, n_cal) + out = cupy.empty(n_ext, dtype=cupy.float64) + + fn = _get_kernel_distmarg() + grid = ((n_ext + threads_per_block - 1) // threads_per_block,) + block = (threads_per_block,) + fn(grid, block, ( + Q, A, ifirst, invDist, rho_sq, w_t, log_w, np.float64(log_w_norm), + np.int32(1 if phase_marginalization else 0), lnI, + np.float64(distmarg["s0"]), np.float64(distmarg["ds"]), + np.float64(distmarg["smin"]), np.float64(distmarg["smax"]), np.int32(ns), + np.float64(distmarg["t0"]), np.float64(distmarg["dt"]), + np.float64(distmarg["tmax"]), np.int32(nt), + np.float64(distmarg["xmin"]), np.float64(distmarg["xmax"]), + np.float64(distmarg["sqrt_bmax"]), np.float64(distmarg["bref"]), + np.int32(n_det), np.int32(n_cal), np.int32(N_window), np.int32(npts), + np.int32(n_lms), np.int32(n_ext), np.int32(npts_full), + out, + )) + return out + + +# --------------------------------------------------------------------------- +# CPU / numpy backend (no CUDA). Mirrors the kernels exactly; independent +# implementation, so agreement with the GPU path cross-validates both. +# --------------------------------------------------------------------------- +def _distmarg_lnL_numpy(kappa_sq, rho_sq, d): + """Numpy version of the on-board distmarg transform (mirrors + cuda_Q_fused_calmarg_distmarg.cu and the ILE EvenBivariateLinearInterpolator). + Out-of-table points return -inf (contribute nothing).""" + lnI_arr = np.asarray(d["lnI_array"], dtype=np.float64) + ns, nt = lnI_arr.shape + xmin, xmax = d["xmin"], d["xmax"] + sqrt_bmax, bref = d["sqrt_bmax"], d["bref"] + smin, smax, tmax = d["smin"], d["smax"], d["tmax"] + s0, ds, t0, dt = d["s0"], d["ds"], d["t0"], d["dt"] + + x0 = kappa_sq / rho_sq + s = np.arcsinh(sqrt_bmax * (x0 - xmin)) - np.arcsinh(sqrt_bmax * (xmax - x0)) + t = np.arcsinh(rho_sq / bref) + + out = np.full(x0.shape, -np.inf, dtype=np.float64) + i_mid = (s - s0) / ds + j_mid = (t - t0) / dt + i_lo = np.floor(i_mid).astype(int); i_hi = np.ceil(i_mid).astype(int) + j_lo = np.floor(j_mid).astype(int); j_hi = np.ceil(j_mid).astype(int) + ok = ((s > smin) & (s < smax) & (t < tmax) & + (i_lo >= 0) & (i_hi < ns) & (j_lo >= 0) & (j_hi < nt)) + if np.any(ok): + il, ih = i_lo[ok], i_hi[ok] + jl, jh = j_lo[ok], j_hi[ok] + p = i_mid[ok] - il; q = j_mid[ok] - jl + p_, q_ = 1.0 - p, 1.0 - q + lnI = (p_ * q_ * lnI_arr[il, jl] + p * q_ * lnI_arr[ih, jl] + + p_ * q * lnI_arr[il, jh] + p * q * lnI_arr[ih, jh]) + x0c = np.clip(x0[ok], xmin, xmax) + out[ok] = rho_sq[ok] * x0c * (x0[ok] - 0.5 * x0c) + lnI + return out + + +def Q_fused_calmarg_numpy(Q, A, ifirst, invDist, rho_sq, w_t, n_cal, N_window, + distmarg=None, cal_log_weights=None, + phase_marginalization=False): + """Pure-numpy equivalent of Q_fused_calmarg_cupy / _distmarg_cupy. + + Same arguments and result; distmarg=None uses the default helper, otherwise the + distmarg table dict. Materializes (n_cal, n_ext, npts) -- fine for CPU / testing. + """ + Q = np.asarray(Q, dtype=np.complex128) + A = np.asarray(A, dtype=np.complex128) + ifirst = np.asarray(ifirst, dtype=np.int64) + invDist = np.asarray(invDist, dtype=np.float64) + rho_sq = np.asarray(rho_sq, dtype=np.float64) + w_t = np.asarray(w_t, dtype=np.float64) + + n_det, npts_full, n_lms = Q.shape + _, n_ext, _ = A.shape + npts = w_t.shape[0] + assert npts_full == N_window * n_cal + + # log(n_cal): unbiased importance estimate (1/n_cal) sum_c w_c L_c (not self-normalized) + log_w = np.zeros(n_cal) if cal_log_weights is None else np.asarray(cal_log_weights, dtype=np.float64) + log_w_norm = float(np.log(n_cal)) + + tgrid = np.arange(npts) + lnLt_all = np.empty((n_cal, n_ext, npts), dtype=np.float64) + for c in range(n_cal): + kappa = np.zeros((n_ext, npts), dtype=np.complex128) + for dd in range(n_det): + within = ifirst[dd][:, None] + tgrid[None, :] # (n_ext, npts) + valid = (within >= 0) & (within < N_window) + idx = np.clip(within + c * N_window, 0, npts_full - 1) + gathered = Q[dd][idx] # (n_ext, npts, n_lms) + gathered[~valid] = 0.0 # out-of-block -> 0 + kappa += np.einsum("jl,jtl->jt", A[dd], gathered) + kappa_scaled = kappa * invDist[:, None] + kappa_sq = np.abs(kappa_scaled) if phase_marginalization else kappa_scaled.real + if distmarg is None: + lnLt = kappa_sq - 0.5 * rho_sq + else: + lnLt = _distmarg_lnL_numpy(kappa_sq, rho_sq, distmarg) + lnLt_all[c] = lnLt + log_w[c] + + # lnL[j] = log( sum_c sum_t w_t exp(lnLt_all[c,j,t]) ) - log_w_norm + mx = np.max(lnLt_all, axis=(0, 2)) # (n_ext,) + finite = np.isfinite(mx) + lnL = np.full(n_ext, -np.inf) + if np.any(finite): + contrib = w_t[None, None, :] * np.exp(lnLt_all[:, finite, :] - mx[finite][None, :, None]) + S = np.sum(contrib, axis=(0, 2)) # (n_ext_finite,) + lnL[finite] = mx[finite] + np.log(S) - log_w_norm + return lnL diff --git a/MonteCarloMarginalizeCode/Code/RIFT/likelihood/cuda_Q_fused_calmarg.cu b/MonteCarloMarginalizeCode/Code/RIFT/likelihood/cuda_Q_fused_calmarg.cu new file mode 100644 index 000000000..cf4f69163 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/RIFT/likelihood/cuda_Q_fused_calmarg.cu @@ -0,0 +1,114 @@ +#include + +/* + Fused calibration-marginalized factored log-likelihood (Option C). + + One thread per extrinsic sample. For each sample j we loop over the n_cal + calibration realizations (selected by shifting the rholm window offset by + c*N_window), the integration time window, the detectors and the (l,m) modes, + forming the data term kappa, applying the default (distance-unmarginalized) + factored-likelihood helper + + lnL_t(j,c,t) = invDist[j] * Re(kappa) - 0.5 * rho_sq[j,t] + + and accumulating a streaming, Simpson-weighted log-sum-exp over (c,t). The + result is the calibration-marginalized log likelihood + + out[j] = log( sum_c sum_t w_t * exp(lnL_t(j,c,t) + log_w[c]) ) - log_w_norm, + + where log_w[c] are per-realization importance log-weights (log_w_norm = + logsumexp(log_w)); for uniform weights log_w[c]=0 and log_w_norm=log(n_cal), giving + the plain (1/n_cal) average. This supports adaptive / importance cal sampling. + + rho_sq is calibration-independent and is passed in pre-summed over detectors. + w_t are the composite-Simpson quadrature weights (including dx=deltaT), so the + time integration matches Option B's simps() exactly. + + Array layouts (all C-contiguous): + Q : (n_det, npts_full, n_lms) complex128 rholm timeseries.T per det + A : (n_det, n_ext, n_lms) complex128 conj(F*Ylm) per det + ifirst : (n_det, n_ext) int32 within-block window offset + invDist : (n_ext,) float64 distMpcRef/distMpc + rho_sq : (n_ext, npts) float64 template (U,V) term, summed over det + w_t : (npts,) float64 Simpson weights * deltaT + out : (n_ext,) float64 +*/ + +extern "C" { + + __global__ void Q_fused_calmarg( + const complex * Q, + const complex * A, + const int * ifirst, + const double * invDist, + const double * rho_sq, + const double * w_t, + const double * log_w, + double log_w_norm, + int phase_marg, + int n_det, + int n_cal, + int N_window, + int npts, + int n_lms, + int n_ext, + int npts_full, + double * out + ){ + size_t j = threadIdx.x + (size_t)blockDim.x * blockIdx.x; + if (j >= (size_t)n_ext) return; + + const double inv = invDist[j]; + + /* streaming weighted log-sum-exp accumulators (first-iteration flag avoids + needing an explicit -infinity, which is awkward under NVRTC) */ + double m = 0.0; /* running max of (lnL_t + log_w[c]) */ + double S = 0.0; /* running sum_ w_t * exp(lnL_t + log_w[c] - m) */ + bool first = true; + + for (int c = 0; c < n_cal; ++c) { + const double lw = log_w[c]; /* per-realization importance log-weight */ + for (int t = 0; t < npts; ++t) { + complex kappa = complex(0.0, 0.0); + for (int d = 0; d < n_det; ++d) { + long within = (long)ifirst[(size_t)d * n_ext + j] + (long)t; + /* The window must stay inside THIS detector's realization block c, i.e. + the within-block offset must lie in [0, N_window). An out-of-range + offset (window extends past the rholm buffer for an extreme sky + position, or a pathological/NaN draw) means this detector contributes + zero at this (c,t) -- matching the per-block n_cal==1 behavior. This + also prevents reading a neighbouring block (a non-last-block overflow + would otherwise bleed into block c+1) or out of bounds entirely. */ + if (within < 0 || within >= (long)N_window) continue; + long idx = within + (long)c * N_window; + const complex * Qd = Q + (size_t)d * npts_full * n_lms; + const complex * Ad = A + ((size_t)d * n_ext + j) * n_lms; + long qrow = idx * (long)n_lms; + for (int lm = 0; lm < n_lms; ++lm) { + kappa += Ad[lm] * Qd[qrow + lm]; + } + } + + /* phase marginalization: use |kappa| (the (2,-2) conjugation is already baked + into Q/A by the caller), else Re(kappa). */ + double kre = phase_marg ? sqrt(kappa.real()*kappa.real() + kappa.imag()*kappa.imag()) : kappa.real(); + double lnLt = inv * kre - 0.5 * rho_sq[(size_t)j * npts + t] + lw; + double wt = w_t[t]; + + if (first) { + m = lnLt; + S = wt; + first = false; + } else if (lnLt > m) { + S = S * exp(m - lnLt) + wt; + m = lnLt; + } else { + S += wt * exp(lnLt - m); + } + } /* t */ + } /* c */ + + out[j] = m + log(S) - log_w_norm; + } /* Q_fused_calmarg */ + +} /* extern "C" */ diff --git a/MonteCarloMarginalizeCode/Code/RIFT/likelihood/cuda_Q_fused_calmarg_distmarg.cu b/MonteCarloMarginalizeCode/Code/RIFT/likelihood/cuda_Q_fused_calmarg_distmarg.cu new file mode 100644 index 000000000..457f28ff1 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/RIFT/likelihood/cuda_Q_fused_calmarg_distmarg.cu @@ -0,0 +1,147 @@ +#include + +/* + Fused calibration-marginalized factored log-likelihood with DISTANCE + marginalization (Option C, stage 2). Separate from cuda_Q_fused_calmarg.cu by + design: it keeps the simpler default-helper kernel untouched as a review baseline + and fallback, and the Python 'loop' path (Option B) remains a full fallback for + distmarg too. + + This reproduces the distance-marginalization loglikelihood used at the ILE + distmarg call sites (and EvenBivariateLinearInterpolator): + + x0 = kappa_sq / rho_sq (kappa_sq = invDist*Re(kappa)) + s = asinh(sqrt_bmax*(x0 - xmin)) - asinh(sqrt_bmax*(xmax - x0)) + t = asinh(rho_sq / bref) + lnI = bilinear interp of lnI_array at (s,t) if smin */ + if (x >= 0.0) return log(x + sqrt(x * x + 1.0)); + else return -log(-x + sqrt(x * x + 1.0)); + } + + __global__ void Q_fused_calmarg_distmarg( + const complex * Q, + const complex * A, + const int * ifirst, + const double * invDist, + const double * rho_sq, + const double * w_t, + const double * log_w, + double log_w_norm, + int phase_marg, + const double * lnI_array, + double s0, double ds, double smin, double smax, int ns, + double t0, double dt, double tmax, int nt, + double xmin, double xmax, double sqrt_bmax, double bref, + int n_det, + int n_cal, + int N_window, + int npts, + int n_lms, + int n_ext, + int npts_full, + double * out + ){ + size_t j = threadIdx.x + (size_t)blockDim.x * blockIdx.x; + if (j >= (size_t)n_ext) return; + + const double inv = invDist[j]; + + double m = 0.0; /* running max of lnL_t */ + double S = 0.0; /* running sum_ w_t * exp(lnL_t - m) */ + bool first = true; + + for (int c = 0; c < n_cal; ++c) { + const double lw = log_w[c]; /* per-realization importance log-weight */ + for (int t = 0; t < npts; ++t) { + complex kappa = complex(0.0, 0.0); + for (int d = 0; d < n_det; ++d) { + long within = (long)ifirst[(size_t)d * n_ext + j] + (long)t; + /* The window must stay inside THIS detector's realization block c, i.e. + the within-block offset must lie in [0, N_window). An out-of-range + offset (window extends past the rholm buffer for an extreme sky + position, or a pathological/NaN draw) means this detector contributes + zero at this (c,t) -- matching the per-block n_cal==1 behavior. This + also prevents reading a neighbouring block (a non-last-block overflow + would otherwise bleed into block c+1) or out of bounds entirely. */ + if (within < 0 || within >= (long)N_window) continue; + long idx = within + (long)c * N_window; + const complex * Qd = Q + (size_t)d * npts_full * n_lms; + const complex * Ad = A + ((size_t)d * n_ext + j) * n_lms; + long qrow = idx * (long)n_lms; + for (int lm = 0; lm < n_lms; ++lm) { + kappa += Ad[lm] * Qd[qrow + lm]; + } + } + + /* phase marginalization: |kappa| (conjugation baked into Q/A), else Re(kappa) */ + double kre = phase_marg ? sqrt(kappa.real()*kappa.real() + kappa.imag()*kappa.imag()) : kappa.real(); + double kappa_sq = inv * kre; + double rsq = rho_sq[(size_t)j * npts + t]; + double x0 = kappa_sq / rsq; + + double s = _asinh_stable(sqrt_bmax * (x0 - xmin)) + - _asinh_stable(sqrt_bmax * (xmax - x0)); + double tt = _asinh_stable(rsq / bref); + + /* in-bounds test matches distmarg_loglikelihood's mask */ + if (!(s > smin && s < smax && tt < tmax)) continue; + + double i_mid = (s - s0) / ds; + double j_mid = (tt - t0) / dt; + int i_lo = (int)floor(i_mid); + int i_hi = (int)ceil(i_mid); + int j_lo = (int)floor(j_mid); + int j_hi = (int)ceil(j_mid); + /* defensive guard against floating-point edge cases */ + if (i_lo < 0 || i_hi >= ns || j_lo < 0 || j_hi >= nt) continue; + + double p = i_mid - i_lo; + double q = j_mid - j_lo; + double p_ = 1.0 - p; + double q_ = 1.0 - q; + double lnI = p_ * q_ * lnI_array[(size_t)i_lo * nt + j_lo] + + p * q_ * lnI_array[(size_t)i_hi * nt + j_lo] + + p_ * q * lnI_array[(size_t)i_lo * nt + j_hi] + + p * q * lnI_array[(size_t)i_hi * nt + j_hi]; + + double x0c = x0; + if (x0c < xmin) x0c = xmin; + if (x0c > xmax) x0c = xmax; + double lnLt = rsq * x0c * (x0 - 0.5 * x0c) + lnI + lw; + + double wt = w_t[t]; + if (first) { + m = lnLt; + S = wt; + first = false; + } else if (lnLt > m) { + S = S * exp(m - lnLt) + wt; + m = lnLt; + } else { + S += wt * exp(lnLt - m); + } + } /* t */ + } /* c */ + + out[j] = m + log(S) - log_w_norm; + } /* Q_fused_calmarg_distmarg */ + +} /* extern "C" */ diff --git a/MonteCarloMarginalizeCode/Code/RIFT/likelihood/cuda_Q_inner_product.cu b/MonteCarloMarginalizeCode/Code/RIFT/likelihood/cuda_Q_inner_product.cu index d472d996a..bcb0d6285 100644 --- a/MonteCarloMarginalizeCode/Code/RIFT/likelihood/cuda_Q_inner_product.cu +++ b/MonteCarloMarginalizeCode/Code/RIFT/likelihood/cuda_Q_inner_product.cu @@ -36,11 +36,21 @@ extern "C" { complex out_tmp = 0.; - /* Take the outer product over the lm axis. */ - for (size_t i_lm = 0; i_lm < num_lms; ++i_lm) { - out_tmp += - A_sample[threadIdx.x*num_lms + i_lm] * - Q[(i_first_time+i_time)*num_lms + i_lm]; + /* Guard against out-of-range time offsets: for some sky positions the + window can extend a sample past the precomputed rholm buffer, and with + calibration marginalization the buffer is n_cal blocks long so an + over-read in the last block hits unmapped memory (CUDA illegal access). + Out-of-range time samples contribute zero rather than reading OOB. + (i_first_time is size_t, so a negative int index wraps to a large value + and is also caught here.) */ + size_t q_time = i_first_time + i_time; + if (q_time < (size_t)num_time_points) { + /* Take the outer product over the lm axis. */ + for (size_t i_lm = 0; i_lm < num_lms; ++i_lm) { + out_tmp += + A_sample[threadIdx.x*num_lms + i_lm] * + Q[q_time*num_lms + i_lm]; + } } out[i_output] = out_tmp; diff --git a/MonteCarloMarginalizeCode/Code/RIFT/likelihood/factored_likelihood.py b/MonteCarloMarginalizeCode/Code/RIFT/likelihood/factored_likelihood.py index d319ce535..a3af6a658 100644 --- a/MonteCarloMarginalizeCode/Code/RIFT/likelihood/factored_likelihood.py +++ b/MonteCarloMarginalizeCode/Code/RIFT/likelihood/factored_likelihood.py @@ -32,6 +32,7 @@ import RIFT.lalsimutils as lsu # problem of relative comprehensive import - dangerous due to package name log_loud = lsu.log_loud import numpy as np +from RIFT.precision import RiftFloat # platform-portable replacement for np.float128 try: import cupy from . import optimized_gpu_tools @@ -670,7 +671,7 @@ def FactoredLogLikelihoodTimeMarginalized(tvals, extr_params, rholms_intp, rholm Ylms = ComputeYlms(Lmax, incl, -phiref, selected_modes=rholms_intp[list(rholms.keys())[0]].keys()) # lnL = 0. - lnL = np.zeros(len(tvals),dtype=np.float128) + lnL = np.zeros(len(tvals),dtype=RiftFloat) for det in detectors: CT = crossTerms[det] CTV = crossTermsV[det] @@ -940,11 +941,21 @@ def ComputeModeIPTimeSeries(hlms, data, psd, fmin, fMax, fNyq, # Create multiple data realizations from the realizations, and construct a longer IP item. for index, calib_array in enumerate(calibration_realizations.T): #print(calib_array.shape, data.data.length, calibration_realizations.shape) + # Apply calibration to the DATA (d -> C(f) d), so the U,V terms + # stay calibration-independent and are computed only once downstream. data_now.data.data = calib_array * data.data.data - rho, rhoTS, rhoIdx, rhoPhase = IP.ip(hlms[pair], data) + rho, rhoTS, rhoIdx, rhoPhase = IP.ip(hlms[pair], data_now) rhoTS.epoch = data.epoch - hlms[pair].epoch tmp= lsu.DataRollBins(rhoTS, N_shift) # restore functionality for bidirectional shifts: waveform need not start at t=0 rholms_here = lal.CutCOMPLEX16TimeSeries(rhoTS, 0, N_window) + # CRITICAL: the concatenated series must carry the SAME epoch as the + # non-calibration branch (i.e. block 0's per-block epoch, after the roll/cut). + # Otherwise the time reference is wrong and ifirst lands in the wrong block, + # zeroing the signal -> the calmarg likelihood collapses. The within-block + # offset (ifirst) is identical for every block, so block 0's epoch is the + # reference for all of them. + if index == 0: + rholms_so_far.epoch = rholms_here.epoch indx_start = index*N_window rholms_so_far.data.data[indx_start:indx_start+N_window] = rholms_here.data.data rholms[pair] = rholms_so_far @@ -1570,7 +1581,7 @@ def DiscreteFactoredLogLikelihoodViaArray(tvals, P, lookupNKDict, rholmsArrayDi deltaT = P.deltaT - lnL = np.zeros(npts,dtype=np.float128) + lnL = np.zeros(npts,dtype=RiftFloat) for det in detectors: @@ -1648,10 +1659,10 @@ def DiscreteFactoredLogLikelihoodViaArrayVector(tvals, P_vec, lookupNKDict, rho deltaT = P_vec.deltaT # this is stored as a scalar # Array to use for work - lnL = np.zeros(npts,dtype=np.float128) - lnL_array = np.zeros((npts_extrinsic,npts),dtype=np.float128) + lnL = np.zeros(npts,dtype=RiftFloat) + lnL_array = np.zeros((npts_extrinsic,npts),dtype=RiftFloat) # Array to use for output - lnLmargOut = np.zeros(npts_extrinsic,dtype=np.float128) + lnLmargOut = np.zeros(npts_extrinsic,dtype=RiftFloat) # term1 = np.zeros(npts, dtype=complex) # workspace for det in detectors: # strings right now - need to change to make ufunc-able @@ -1827,14 +1838,46 @@ def _factored_lnL_helper(kappa_sq, rho_sq): return kappa_sq - 0.5 * rho_sq -def DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop(tvals, P_vec, lookupNKDict, rholmsArrayDict, ctUArrayDict,ctVArrayDict,epochDict,Lmax=2,array_output=False,xpy=np, loglikelihood=_factored_lnL_helper,return_lnLt=False,phase_marginalization=False): +def DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop(tvals, P_vec, lookupNKDict, rholmsArrayDict, ctUArrayDict,ctVArrayDict,epochDict,Lmax=2,array_output=False,xpy=np, loglikelihood=_factored_lnL_helper,return_lnLt=False,phase_marginalization=False,n_cal=1,cal_method='loop',cal_distmarg=None,cal_log_weights=None,return_cal_components=False): """ DiscreteFactoredLogLikelihoodViaArray uses the array-ized data structures to compute the log likelihood, - either as an array vs time *or* marginalized in time. + either as an array vs time *or* marginalized in time. This generally is marginally faster, particularly if Lmax is large. The timeseries quantities are computed via discrete shifts of an existing grid Note 'P' must have the *sampling rate* set to correctly interpret the event time. Note arguments passed are NOW ARRAYS, in contrast to similar function which does not have 'Vector' postfix + + Calibration marginalization (n_cal>1) + ------------------------------------- + When n_cal>1, the rholm timeseries are assumed to hold ``n_cal`` contiguous + calibration realizations, each of length N_window = npts_full/n_cal (built by + ComputeModeIPTimeSeries with the calibration applied to the *data*). Because + calibration is applied to the data, the template-template cross terms U,V + (rho_sq) are calibration-INDEPENDENT and are computed only once; only the data + term kappa changes per realization, selected by shifting the window offset + ifirst -> ifirst + c*N_window. We then Monte-Carlo marginalize: + Z_cal(theta) = (1/n_cal) sum_c integral dt exp( lnL_t(theta, c) ) + via a streaming log-sum-exp over the n_cal realizations (memory unchanged vs + the n_cal==1 path; one extra GPU kernel launch per realization). The n_cal==1 + code path below is unchanged. + + cal_method selects the n_cal>1 reduction: + 'loop' (default, Option B): Python loop over realizations reusing the + existing Q_inner_product kernel; works on CPU and GPU and with any + loglikelihood callback (distance/phase marginalization). + 'fused' (Option C): a single fused kernel (RIFT.likelihood.Q_fused_calmarg) + does the Q-product, the loglikelihood, and the cal+time log-sum-exp in + one launch -- a CUDA kernel on GPU, or pure numpy on CPU. Supports + phase marginalization (|kappa|). With cal_distmarg=None it + uses the default (distance-unmarginalized) helper kernel; with + cal_distmarg= it uses the separate distance-marginalization + kernel (Q_fused_calmarg_distmarg) reproducing distmarg_loglikelihood. + Raises NotImplementedError for unsupported combinations; the 'loop' + path is the fallback for everything (incl. distmarg, on CPU and GPU). + + cal_distmarg : dict or None + Distance-marginalization table+params for the fused distmarg kernel; see + RIFT.likelihood.Q_fused_calmarg.Q_fused_calmarg_distmarg_cupy. """ global distMpcRef @@ -1878,6 +1921,11 @@ def DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop(tvals, P_vec, lookupNKDic kappa_sq = xpy.zeros((npts_extrinsic, npts), dtype=np.complex128) rho_sq = xpy.zeros((npts_extrinsic, npts), dtype=np.float64) + # When marginalizing over calibration (n_cal>1), cache the per-detector data + # term inputs here; the calibration-independent rho_sq is still accumulated + # in the loop below, and kappa is recomputed per realization afterwards. + cal_cache = {} + if (xpy is np) or (optimized_gpu_tools is None): simps = my_simps elif not (xpy is np): @@ -2000,34 +2048,46 @@ def DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop(tvals, P_vec, lookupNKDic # xpy.conj(FY_dummy_t), Qlms, # ).real * (distMpcRef/distMpc)[...,None] - if not (xpy is np): - FY_conj = xpy.conj(F_vec_dummy_lm * Ylms_vec) - # Shape Q = (npts_time_full, nlms) - # Shape A=FY_conj = (npts_extrinsic, nlms) - # shape result = (npts_extrinsic, npts_time_*window* = npts) - Q_prod_result = Q_inner_product.Q_inner_product_cupy( - Q, FY_conj, - ifirst, npts, - ) + if n_cal == 1: + # ---- standard path (no calibration marginalization): UNCHANGED ---- + if not (xpy is np): + FY_conj = xpy.conj(F_vec_dummy_lm * Ylms_vec) + # Shape Q = (npts_time_full, nlms) + # Shape A=FY_conj = (npts_extrinsic, nlms) + # shape result = (npts_extrinsic, npts_time_*window* = npts) + Q_prod_result = Q_inner_product.Q_inner_product_cupy( + Q, FY_conj, + ifirst, npts, + ) + else: + # Use old code completely unchanged ... very wasteful on memory management! + Qlms = xpy.empty((npts_extrinsic, npts, n_lms), dtype=np.complex128) + for i in range(npts_extrinsic): + Qlms[i] = rholmsArrayDict[det][...,ifirst[i]:(ifirst[i]+npts)].T + if phase_marginalization: + Qlms[:, :, 1] = xpy.conj(Qlms[:, :, 1]) + + FY_dummy_t = np.broadcast_to( + (F_vec_dummy_lm * Ylms_vec)[:, np.newaxis], + Qlms.shape, + ) + + Q_prod_result = np.einsum( + "...i,...i", + np.conj(FY_dummy_t), Qlms, + ) + + kappa_sq += Q_prod_result * (distMpcRef/distMpc)[..., np.newaxis] else: - # Use old code completely unchanged ... very wasteful on memory management! - Qlms = xpy.empty((npts_extrinsic, npts, n_lms), dtype=np.complex128) - for i in range(npts_extrinsic): - Qlms[i] = rholmsArrayDict[det][...,ifirst[i]:(ifirst[i]+npts)].T - if phase_marginalization: - Qlms[:, :, 1] = xpy.conj(Qlms[:, :, 1]) - - FY_dummy_t = np.broadcast_to( - (F_vec_dummy_lm * Ylms_vec)[:, np.newaxis], - Qlms.shape, - ) - - Q_prod_result = np.einsum( - "...i,...i", - np.conj(FY_dummy_t), Qlms, - ) - - kappa_sq += Q_prod_result * (distMpcRef/distMpc)[..., np.newaxis] + # ---- calibration-marginalization path (Option B): cache pieces ---- + # The rholm timeseries hold n_cal contiguous realizations; realization c + # is selected later via ifirst -> ifirst + c*N_window_block. Q is + # (npts_full, n_lms) and FY_conj is (npts_extrinsic, n_lms); both already + # encode any phase-marginalization conjugation built above. + npts_full_det = Q.shape[0] + N_window_block = npts_full_det // n_cal + FY_conj_cal = xpy.conj(F_vec_dummy_lm * Ylms_vec) + cal_cache[det] = (Q, FY_conj_cal, ifirst, N_window_block) # lnL_t_accum += Q_prod_result * (distMpcRef/distMpc)[...,None] # lnL_t_accum += Q_inner_product.Q_inner_product_cupy( @@ -2046,21 +2106,158 @@ def DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop(tvals, P_vec, lookupNKDic # lnL_t_accum += lnL_t - if phase_marginalization: - lnL_t = loglikelihood(xpy.abs(kappa_sq), rho_sq) - else: - lnL_t = loglikelihood(kappa_sq.real, rho_sq) + if n_cal == 1: + if phase_marginalization: + lnL_t = loglikelihood(xpy.abs(kappa_sq), rho_sq) + else: + lnL_t = loglikelihood(kappa_sq.real, rho_sq) - # Take exponential of the log likelihood in-place. - lnLmax = xpy.max(lnL_t) - if return_lnLt: - return lnL_t #- lnLmax # we want the verbatim lnL_t values, no shift - L_t = xpy.exp(lnL_t - lnLmax, out=lnL_t) + # Take exponential of the log likelihood in-place. + lnLmax = xpy.max(lnL_t) + if return_lnLt: + return lnL_t #- lnLmax # we want the verbatim lnL_t values, no shift + L_t = xpy.exp(lnL_t - lnLmax, out=lnL_t) - L = simps(L_t, dx=deltaT, axis=-1) + L = simps(L_t, dx=deltaT, axis=-1) - # Compute log likelihood in-place. - lnL = lnLmax + xpy.log(L, out=L) + # Compute log likelihood in-place. + lnL = lnLmax + xpy.log(L, out=L) + + return lnL + + # ---- calibration-marginalization reduction (Option B) ---- + # Monte-Carlo marginalize over the n_cal calibration draws: + # Z_cal(theta) = (1/n_cal) sum_c \int dt exp( lnL_t(theta, c) ) + # rho_sq (the U,V term) is calibration-independent and was accumulated + # once above; only the data term kappa changes per realization, selected by + # shifting the window offset into block c. Accumulate a streaming log-sum-exp + # over realizations for numerical stability (S holds sum_c exp(lnL_t - max)). + # return_lnLt with calibration marginalization: return the cal-marginalized lnL(t) + # timeseries (weighted log-sum-exp over realizations at each time bin) so downstream + # time resampling/output works. This is produced by the loop reduction below; the + # fused kernel (which returns the time-integrated scalar) is skipped in that case. + + # Per-realization importance log-weights for the cal marginalization. Default + # (None) is uniform -> the plain (1/n_cal) average; non-uniform weights support + # adaptive / importance cal sampling (w_c = prior(c)/proposal(c), with E_q[w]=1). + # The cal-marginalized likelihood is the UNBIASED importance estimate of the prior + # integral: L_marg = integral pi(c) L(c) dc ~= (1/n_cal) sum_c w_c L_c. So the + # normalization is log(n_cal) -- NOT logsumexp(log_w) (the self-normalized/ratio + # estimator), which is biased by log(sum w / n_cal) for non-uniform weights. For + # uniform weights the two coincide. + if cal_log_weights is None: + cal_log_w = xpy.zeros(n_cal, dtype=np.float64) + else: + cal_log_w = xpy.asarray(cal_log_weights, dtype=np.float64) + cal_log_w_norm = float(np.log(n_cal)) + + if cal_method == 'fused' and not return_lnLt and not return_cal_components: + # ---- Option C: fused implementation (GPU CUDA kernel, or numpy on CPU) ---- + # (return_lnLt needs the per-time series, which the loop reduction produces, so + # the fused scalar kernel is bypassed when a timeseries is requested.) + if cal_distmarg is None and loglikelihood is not _factored_lnL_helper: + raise NotImplementedError("fused cal kernel needs either the default helper or a cal_distmarg table") + import RIFT.likelihood.Q_fused_calmarg as Q_fused_calmarg + dets = list(cal_cache.keys()) + # All detectors share modes/length; ifirst differs per detector (time delay) + Q_stack = xpy.stack([cal_cache[d][0] for d in dets]) # (n_det, npts_full, n_lms) + A_stack = xpy.stack([cal_cache[d][1] for d in dets]) # (n_det, npts_extrinsic, n_lms) + ifirst_stack = xpy.stack([cal_cache[d][2] for d in dets]).astype(np.int32) # (n_det, npts_extrinsic) + N_window_block = cal_cache[dets[0]][3] + # Simpson quadrature weight vector (incl. dx=deltaT), so time integration + # matches the loop path's simps() exactly. simps is linear -> weights = simps(I). + w_t = simps(xpy.eye(npts, dtype=np.float64), dx=deltaT, axis=-1) + # invDistMpc is a scalar when distance is marginalized (P.dist fixed at the + # fiducial) and a vector when distance is sampled; the kernel wants one value + # per extrinsic sample, so broadcast to (npts_extrinsic,). + invDist_vec = xpy.asarray(invDistMpc, dtype=np.float64) * xpy.ones(npts_extrinsic, dtype=np.float64) + if xpy is np: + # CPU: pure-numpy fused (no CUDA); independent cross-check of the kernel + return Q_fused_calmarg.Q_fused_calmarg_numpy( + Q_stack, A_stack, ifirst_stack, invDist_vec, rho_sq, w_t, + n_cal, N_window_block, distmarg=cal_distmarg, cal_log_weights=cal_log_weights, + phase_marginalization=phase_marginalization) + if cal_distmarg is None: + return Q_fused_calmarg.Q_fused_calmarg_cupy( + Q_stack, A_stack, ifirst_stack, invDist_vec, rho_sq, w_t, + n_cal, N_window_block, cal_log_weights=cal_log_weights, + phase_marginalization=phase_marginalization) + else: + return Q_fused_calmarg.Q_fused_calmarg_distmarg_cupy( + Q_stack, A_stack, ifirst_stack, invDist_vec, rho_sq, w_t, + n_cal, N_window_block, cal_distmarg, cal_log_weights=cal_log_weights, + phase_marginalization=phase_marginalization) + + running_max = None + S = xpy.zeros((npts_extrinsic, npts), dtype=np.float64) + # Pilot/adaptive support: optionally collect the RAW (un-importance-weighted) + # time-integrated log-likelihood per realization, one value per extrinsic point. + # This is the cal posterior responsibility used by util_CalPilotFit to learn a + # proposal. (loop method only; the fused scalar path is bypassed above.) + cal_components = xpy.zeros((npts_extrinsic, n_cal), dtype=np.float64) if return_cal_components else None + for c in range(n_cal): + kappa_sq_c = xpy.zeros((npts_extrinsic, npts), dtype=np.complex128) + for det in detectors: + Q_det, FY_conj_det, ifirst_det, N_window_block = cal_cache[det] + # Restrict to THIS realization block and use the within-block offset, so + # the window is confined to [0, N_window): an over-running window then + # zeros (the shared kernel / CPU fill below guard against N_window_block) + # instead of bleeding into the neighbouring block or reading out of + # bounds. This matches the per-block n_cal==1 reference and the fused + # kernel exactly. + Q_block = Q_det[c*N_window_block:(c+1)*N_window_block] # (N_window, n_lms) + ifirst_within = ifirst_det.astype(np.int32) + if not (xpy is np): + Q_prod_result = Q_inner_product.Q_inner_product_cupy( + Q_block, FY_conj_det, ifirst_within, npts, + ) + else: + n_lms_det = Q_block.shape[1] + Qlms = xpy.zeros((npts_extrinsic, npts, n_lms_det), dtype=np.complex128) + tgrid = np.arange(npts) + for i in range(npts_extrinsic): + idxs = int(ifirst_within[i]) + tgrid + valid = (idxs >= 0) & (idxs < N_window_block) + Qlms[i, valid] = Q_block[idxs[valid]] + # Q_det and FY_conj_det already encode any phase-marg conjugation + Q_prod_result = np.einsum("ej,etj->et", FY_conj_det, Qlms) + kappa_sq_c += Q_prod_result * invDistMpc[..., np.newaxis] + + if phase_marginalization: + lnL_t_c = loglikelihood(xpy.abs(kappa_sq_c), rho_sq) + else: + lnL_t_c = loglikelihood(kappa_sq_c.real, rho_sq) + if return_cal_components: + # RAW per-realization time-integrated log L (no importance weight), stable: + # log( simps_t exp(lnL_t,c) ) = m + log( simps_t exp(lnL_t,c - m) ) + m_raw = xpy.max(lnL_t_c, axis=-1, keepdims=True) + cal_components[:, c] = m_raw[:, 0] + xpy.log(simps(xpy.exp(lnL_t_c - m_raw), dx=deltaT, axis=-1)) + # fold in this realization's importance log-weight + lnL_t_c = lnL_t_c + cal_log_w[c] + + m_c = xpy.max(lnL_t_c) + if running_max is None: + running_max = m_c + elif m_c > running_max: + S *= xpy.exp(running_max - m_c) + running_max = m_c + S += xpy.exp(lnL_t_c - running_max) + + if return_cal_components: + # (npts_extrinsic, n_cal): RAW per-realization integrated log-likelihood. The + # caller (util_CalPilot / ILE dump) accumulates over the harvested extrinsic + # points to form the cal posterior responsibility, then fits a proposal. + return cal_components + + if return_lnLt: + # cal-marginalized lnL at each time bin: + # lnL_marg(t) = log( sum_c exp(log_w[c]) exp(lnL_t,c(t)) ) - log(n_cal) + # (the time integral is NOT taken; downstream resamples this timeseries). + return running_max + xpy.log(S) - cal_log_w_norm + + L = simps(S, dx=deltaT, axis=-1) + # lnL = max + log( sum_c exp(log_w[c]) \int dt exp(lnL_t - max) ) - log(n_cal) + lnL = running_max + xpy.log(L) - cal_log_w_norm return lnL diff --git a/MonteCarloMarginalizeCode/Code/RIFT/misc/container_manifest.py b/MonteCarloMarginalizeCode/Code/RIFT/misc/container_manifest.py new file mode 100644 index 000000000..573165e50 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/RIFT/misc/container_manifest.py @@ -0,0 +1,303 @@ +""" +container_manifest +================== + +Support for "container family" manifests used by the RIFT pipeline. + +Historically ``SINGULARITY_RIFT_IMAGE`` names a single ``.sif`` image (a local +path or an ``osdf://`` URL), and the ILE/CIP Condor jobs hard-code + + MY.SingularityImage = "" + +A *manifest* lets us instead advertise a *family* of images, each targeting a +different GPU compute capability, and let HTCondor pick the right one per matched +machine. When ``SINGULARITY_RIFT_IMAGE`` points at a ``.yaml``/``.yml`` file, +the job-submission code turns it into: + + * an expression-valued ``MY.SingularityImage`` -- a nested ``ifThenElse`` over + the matched machine's GPU capability attribute (default ``GPUs_Capability``) + that selects the highest-capability image the machine can run; and + * a ``require_gpus`` capability floor (the lowest capability any image in the + family supports), composed (``&&``) with any user-supplied + ``RIFT_REQUIRE_GPUS``; and + * for ``osdf://`` images, a *selective* ``transfer_input_files`` entry using + HTCondor ``$$()`` match-time substitution, so only the *matched* image is + transferred (CVMFS/local images are referenced in place and never + transferred). + +Single-``.sif`` behavior is completely unchanged: only ``.yaml``/``.yml`` values +exercise any of this. + +YAML schema +----------- + + version: 1 + capability_attr: GPUs_Capability # machine ClassAd attr the ifThenElse tests + fallback: ancient # label used as the innermost else-branch + containers: + - label: ancient + image: /cvmfs/.../rift_ancient_cuda11.sif # in-place (CVMFS/local) + cuda_capability_min: 3.0 # inclusive + cuda_capability_max: 7.0 # exclusive; null/omitted => open-ended + note: "cupy-cuda11x, ancient base" + - label: modern + image: osdf:///igwn/.../rift_modern_cuda12.sif # selectively transferred + cuda_capability_min: 7.0 + cuda_capability_max: null + note: "cupy-cuda12x, newer base" +""" + +import os + +__all__ = [ + "ContainerManifestError", + "is_container_manifest", + "load_container_manifest", + "build_singularity_image_expr", + "build_transfer_input_expr", + "build_require_gpus_floor", +] + +# Default machine ClassAd attribute advertising GPU compute capability. The +# user's pools advertise this via e.g. +# condor_status -constraint 'TotalGPUs > 0' -autoformat GPUs_DeviceName GPUs_Capability +DEFAULT_CAPABILITY_ATTR = "GPUs_Capability" + + +class ContainerManifestError(Exception): + """Raised for a missing/malformed container family manifest.""" + + +def is_container_manifest(value): + """Return True iff ``value`` (the ``SINGULARITY_RIFT_IMAGE`` string) names a + multi-container manifest rather than a single ``.sif``/``osdf://`` image. + + Pure string check (no filesystem access) so single-image callers pay zero + cost and their behavior is unchanged. + """ + if not value or not isinstance(value, str): + return False + return value.lower().endswith((".yaml", ".yml")) + + +def _image_needs_transfer(image): + """True iff ``image`` is a URL that must be fetched via Condor file transfer + (e.g. ``osdf://``). CVMFS/local paths (``/cvmfs/...``, ``./foo.sif``) are + resolved in place and return False. + """ + return "://" in image + + +def _image_runtime_path(image): + """The string used *inside* ``MY.SingularityImage`` for this image. + + Transferred (URL) images land in the job scratch dir under their basename, + so the pilot must reference ``./`` -- matching the existing + single-image osdf rewrite convention. In-place (CVMFS/local) images are + referenced verbatim. + """ + if _image_needs_transfer(image): + return "./{}".format(image.rstrip("/").split("/")[-1]) + return image + + +def _fmt_cap(value): + """Format a capability number for a ClassAd expression (e.g. 7.0 -> '7.0').""" + return repr(float(value)) + + +def load_container_manifest(path): + """Parse and validate a YAML container family manifest. + + Returns a dict ``{capability_attr, fallback, containers}`` where + ``containers`` is sorted by ``cuda_capability_min`` *descending* (containers + with no min sort last). + + Raises ``ContainerManifestError`` on a missing pyyaml, an unreadable or + malformed file, an empty container list, or an unknown ``fallback`` label. + """ + try: + import yaml + except ImportError as exc: # pragma: no cover - environment dependent + raise ContainerManifestError( + "PyYAML is required to read a container family manifest ({}); " + "install pyyaml or point SINGULARITY_RIFT_IMAGE at a single .sif".format(path) + ) from exc + + try: + with open(path, "r") as f: + data = yaml.safe_load(f) + except (IOError, OSError) as exc: + raise ContainerManifestError("Cannot read container manifest {}: {}".format(path, exc)) + except yaml.YAMLError as exc: + raise ContainerManifestError("Malformed container manifest {}: {}".format(path, exc)) + + if not isinstance(data, dict): + raise ContainerManifestError("Container manifest {} is not a mapping".format(path)) + + raw_containers = data.get("containers") + if not raw_containers or not isinstance(raw_containers, list): + raise ContainerManifestError( + "Container manifest {} must define a non-empty 'containers' list".format(path) + ) + + containers = [] + for idx, entry in enumerate(raw_containers): + if not isinstance(entry, dict): + raise ContainerManifestError( + "Container manifest {} entry #{} is not a mapping".format(path, idx) + ) + image = entry.get("image") + label = entry.get("label") + if not image: + raise ContainerManifestError( + "Container manifest {} entry #{} is missing 'image'".format(path, idx) + ) + if not label: + raise ContainerManifestError( + "Container manifest {} entry #{} is missing 'label'".format(path, idx) + ) + cap_min = entry.get("cuda_capability_min") + cap_max = entry.get("cuda_capability_max") + try: + cap_min = None if cap_min is None else float(cap_min) + cap_max = None if cap_max is None else float(cap_max) + except (TypeError, ValueError): + raise ContainerManifestError( + "Container manifest {} entry '{}' has non-numeric capability bounds".format( + path, label + ) + ) + containers.append( + { + "label": label, + "image": image, + "cuda_capability_min": cap_min, + "cuda_capability_max": cap_max, + "note": entry.get("note"), + } + ) + + # Sort by min capability descending; None mins (open-ended-low catch-alls) + # sort last. float('-inf') keeps them at the bottom. + containers.sort( + key=lambda c: (c["cuda_capability_min"] if c["cuda_capability_min"] is not None else float("-inf")), + reverse=True, + ) + + labels = {c["label"] for c in containers} + fallback = data.get("fallback") + if fallback is None: + # Default fallback = the most-compatible (lowest-min) container, i.e. the + # last one after the descending sort. This is the CPU-safe catch-all. + fallback = containers[-1]["label"] + elif fallback not in labels: + raise ContainerManifestError( + "Container manifest {} fallback '{}' is not one of {}".format( + path, fallback, sorted(labels) + ) + ) + + capability_attr = data.get("capability_attr") or DEFAULT_CAPABILITY_ATTR + + return { + "capability_attr": capability_attr, + "fallback": fallback, + "containers": containers, + } + + +def _capability_attr(manifest): + """Resolve the machine attribute used by the selection ifThenElse. + + Precedence: ``RIFT_GPU_CAPABILITY_ATTR`` env override > manifest + ``capability_attr`` > module default. + """ + return os.environ.get("RIFT_GPU_CAPABILITY_ATTR") or manifest["capability_attr"] + + +def _build_selector(manifest, value_fn, ternary=False): + """Build a nested capability selector over the family. + + ``value_fn(container)`` returns the ClassAd literal for a container branch + (already quoted as appropriate). The highest-min container is the outermost + test; the ``fallback`` container is the innermost else (catch-all, also used + when the capability attribute is ``undefined``). + + With ``ternary=False`` the selector uses ``ifThenElse(cond, a, b)`` (commas). + With ``ternary=True`` it uses the comma-free ClassAd ternary ``cond ? a : b`` + -- required when the result is embedded as one element of a comma-separated + ``transfer_input_files`` list, where internal commas would be mis-split. + """ + attr = _capability_attr(manifest) + containers = manifest["containers"] # sorted desc by min + by_label = {c["label"]: c for c in containers} + fb = by_label[manifest["fallback"]] + + # Containers that contribute a capability threshold test (exclude the + # fallback so it is not duplicated as both a branch and the else). + thresholds = [ + c + for c in containers + if c["cuda_capability_min"] is not None and c["label"] != fb["label"] + ] + # Fold ascending so the highest min ends up outermost. + thresholds.sort(key=lambda c: c["cuda_capability_min"]) + + expr = value_fn(fb) + for c in thresholds: + cond = "TARGET.{attr} >= {mn}".format(attr=attr, mn=_fmt_cap(c["cuda_capability_min"])) + if ternary: + expr = "({cond} ? {val} : {inner})".format(cond=cond, val=value_fn(c), inner=expr) + else: + expr = "ifThenElse({cond}, {val}, {inner})".format( + cond=cond, val=value_fn(c), inner=expr + ) + return expr + + +def build_singularity_image_expr(manifest): + """Return the unquoted ClassAd expression for ``MY.SingularityImage``. + + Each branch literal is the container's *runtime* path (CVMFS/local verbatim, + ``./`` for transferred images). + """ + return _build_selector( + manifest, lambda c: '"{}"'.format(_image_runtime_path(c["image"])) + ) + + +def build_transfer_input_expr(manifest): + """Return a single ``$$([ ... ])`` token for ``transfer_input_files`` that + fetches *only the matched* image, or ``None`` if no container in the family + needs transfer. + + Transfer branches yield the URL verbatim; in-place (CVMFS/local) branches + yield ``""`` (no transfer on those machines). Uses the comma-free ternary + form so the token survives comma-splitting of ``transfer_input_files``. + """ + if not any(_image_needs_transfer(c["image"]) for c in manifest["containers"]): + return None + + def value_fn(c): + return '"{}"'.format(c["image"]) if _image_needs_transfer(c["image"]) else '""' + + return "$$([ {} ])".format(_build_selector(manifest, value_fn, ternary=True)) + + +def build_require_gpus_floor(manifest): + """Return a ``require_gpus`` capability floor expression for the family, or + ``None``. + + The floor is the lowest ``cuda_capability_min`` across the family -- i.e. do + not match a GPU less capable than anything we ship. Uses the require_gpus + sub-ad attribute ``Capability`` (unprefixed -- *not* ``TARGET.`` and *not* + ``GPUs_Capability``). + + If any container has no min (open-ended-low catch-all), there is effectively + no lower bound and ``None`` is returned. + """ + mins = [c["cuda_capability_min"] for c in manifest["containers"]] + if any(m is None for m in mins) or not mins: + return None + return "Capability >= {}".format(_fmt_cap(min(mins))) diff --git a/MonteCarloMarginalizeCode/Code/RIFT/misc/dag_utils.py b/MonteCarloMarginalizeCode/Code/RIFT/misc/dag_utils.py index 0a1474528..3d70cc443 100644 --- a/MonteCarloMarginalizeCode/Code/RIFT/misc/dag_utils.py +++ b/MonteCarloMarginalizeCode/Code/RIFT/misc/dag_utils.py @@ -1284,6 +1284,93 @@ def write_consolidate_sub_simple(tag='consolidate', exe=None, base=None,target=N +def write_calpilot_sub(tag='calpilot', exe=None, log_dir=None, universe="vanilla", + working_directory=None, ile_args_file=None, top_fraction=0.05, + max_points=32, request_memory=4096, request_gpu=True, + use_singularity=False, singularity_image=None, + use_oauth_files=False, transfer_files=None, + max_runtime_minutes=300, **kwargs): + """Submit file for a calibration PILOT stage (Option C; see + RIFT/calmarg/DESIGN_adaptive_driver.md): harvest top-lnL points from iteration + $(macroiteration)'s composite, run ILE --calibration-dump-responsibilities on them, + fit + consolidate a cal proposal that seeds wide_{N+1}. Runs util_CalPilotStage.py. + + Macros expected at instantiation: macroiteration, macroiterationprev. + Produces /cal_consolidated_$(macroiteration).npz (consumed by wide_{N+1} ILE via + --calibration-proposal-breadcrumb). + """ + if use_singularity and (singularity_image == None): + print(" FAIL : Need to specify singularity_image to use singularity ") + sys.exit(0) + + singularity_image_used = "{}".format(singularity_image) # make copy + extra_files = [] + if singularity_image: + if 'osdf:' in singularity_image: + singularity_image_used = "./{}".format(singularity_image.split('/')[-1]) + extra_files += [singularity_image] + + exe = exe or which("util_CalPilotStage.py") + if use_singularity: + exe_base = os.path.basename(exe) + singularity_base_exe_path = "/usr/bin/" + if 'SINGULARITY_BASE_EXE_DIR' in list(os.environ.keys()): + singularity_base_exe_path = os.environ['SINGULARITY_BASE_EXE_DIR'] + exe = singularity_base_exe_path + exe_base + + wd = working_directory + job = pipeline.CondorDAGJob(universe=universe, executable=exe) + sub_name = tag + '.sub' + job.set_sub_file(sub_name) + + # arguments (per-iteration files via condor macros) + job.add_opt("composite", wd + "/consolidated_$(macroiteration).composite") + job.add_opt("ile-args-file", ile_args_file) + job.add_opt("iteration", "$(macroiteration)") + job.add_opt("output-breadcrumb", wd + "/cal_consolidated_$(macroiteration).npz") + job.add_opt("prev-breadcrumb", wd + "/cal_consolidated_$(macroiterationprev).npz") + job.add_opt("top-fraction", str(top_fraction)) + job.add_opt("max-points", str(max_points)) + job.add_opt("workdir", wd) + + uniq_str = "$(macroiteration)-$(cluster)-$(process)" + job.set_log_file("%s%s-%s.log" % (log_dir, tag, uniq_str)) + job.set_stderr_file("%s%s-%s.err" % (log_dir, tag, uniq_str)) + job.set_stdout_file("%s%s-%s.out" % (log_dir, tag, uniq_str)) + + if default_resolved_env: + job.add_condor_cmd('environment', default_resolved_env) + else: + job.add_condor_cmd('getenv', default_getenv_value) + job.add_condor_cmd('request_memory', str(request_memory) + "M") + if request_gpu: + job.add_condor_cmd('request_GPUs', '1') # the pilot runs ILE (GPU path) + if use_singularity: + job.add_condor_cmd('request_CPUs', str(1)) + job.add_condor_cmd('transfer_executable', 'False') + job.add_condor_cmd("MY.SingularityBindCVMFS", 'True') + job.add_condor_cmd("MY.SingularityImage", '"' + singularity_image_used + '"') + if use_oauth_files: + job.add_condor_cmd('use_oauth_services', use_oauth_files) + try: + job.add_condor_cmd('accounting_group', os.environ['LIGO_ACCOUNTING']) + job.add_condor_cmd('accounting_group_user', os.environ['LIGO_USER_NAME']) + except: + print(" LIGO accounting information not available. Add manually to %s !" % sub_name) + if not (max_runtime_minutes is None): + remove_str = 'JobStatus =?= 2 && (CurrentTime - JobStartDate) > ( {})'.format(60 * max_runtime_minutes) + job.add_condor_cmd('periodic_remove', remove_str) + if not transfer_files is None: + if not isinstance(transfer_files, list): + fname_str = transfer_files + ' '.join(extra_files) + else: + fname_str = ','.join(transfer_files + extra_files) + fname_str = fname_str.strip() + job.add_condor_cmd('transfer_input_files', fname_str) + job.add_condor_cmd('should_transfer_files', 'YES') + return job, sub_name + + def write_unify_sub_simple(tag='unify', exe=None, base=None,target=None,universe="vanilla",arg_str=None,log_dir=None, use_eos=False,ncopies=1,no_grid=False, max_runtime_minutes=60,extra_text='',**kwargs): """ Write a submit file for launching a consolidation job diff --git a/MonteCarloMarginalizeCode/Code/RIFT/misc/dag_utils_generic.py b/MonteCarloMarginalizeCode/Code/RIFT/misc/dag_utils_generic.py index 53edce805..6ac504f5e 100644 --- a/MonteCarloMarginalizeCode/Code/RIFT/misc/dag_utils_generic.py +++ b/MonteCarloMarginalizeCode/Code/RIFT/misc/dag_utils_generic.py @@ -125,6 +125,26 @@ def emit_dag(self, dag, path): import numpy as np import configparser +# Container family manifest support (Feature: multi-architecture container +# deployment). Import-guarded so that plain single-.sif runs never require +# pyyaml; only an actual .yaml/.yml manifest exercises this path. +try: + from RIFT.misc.container_manifest import ( + is_container_manifest, + load_container_manifest, + build_singularity_image_expr, + build_transfer_input_expr, + build_require_gpus_floor, + ContainerManifestError, + ) + _HAVE_CONTAINER_MANIFEST = True +except ImportError: + _HAVE_CONTAINER_MANIFEST = False + + def is_container_manifest(value): + # Without the helper module available, never treat a value as a manifest. + return False + __author__ = ( "Evan Ochsner , " "Chris Pankow " @@ -1902,7 +1922,21 @@ def write_CIP_sub(tag='integrate', exe=None, input_net='all.net',output='output- singularity_image_used = "{}".format(singularity_image) # make copy extra_files = [] - if singularity_image: + # Container family manifest support (see write_ILE_sub_simple). CIP jobs do + # not request GPUs, so no require_gpus floor is added here; on a CPU-only + # slot TARGET.GPUs_Capability is undefined and the selection expression + # collapses to the fallback image, which must be the CPU-safe one. + singularity_is_family = False + singularity_image_expr = None + singularity_transfer_expr = None + if singularity_image and is_container_manifest(singularity_image): + singularity_is_family = True + _manifest = load_container_manifest(singularity_image) + singularity_image_expr = build_singularity_image_expr(_manifest) + singularity_transfer_expr = build_transfer_input_expr(_manifest) + if singularity_transfer_expr: + extra_files += [singularity_transfer_expr] + elif singularity_image: if 'osdf:' in singularity_image: singularity_image_used = "./{}".format(singularity_image.split('/')[-1]) extra_files += [singularity_image] @@ -2022,7 +2056,11 @@ def write_CIP_sub(tag='integrate', exe=None, input_net='all.net',output='output- ile_job.add_condor_cmd('request_CPUs', str(1)) ile_job.add_condor_cmd('transfer_executable', 'False') ile_job.add_condor_cmd("MY.SingularityBindCVMFS", 'True') - ile_job.add_condor_cmd("MY.SingularityImage", '"' + singularity_image_used + '"') + if singularity_is_family: + # Expression-valued: emit raw, NO surrounding double quotes. + ile_job.add_condor_cmd("MY.SingularityImage", singularity_image_expr) + else: + ile_job.add_condor_cmd("MY.SingularityImage", '"' + singularity_image_used + '"') requirements.append("HAS_SINGULARITY=?=TRUE") if use_oauth_files: @@ -2219,12 +2257,32 @@ def write_ILE_sub_simple(tag='integrate', exe=None, log_dir=None, use_eos=False, singularity_image_used = "{}".format(singularity_image) # make copy extra_files = [] - if singularity_image: + # Container family manifest support: if singularity_image points at a + # .yaml/.yml manifest, build an expression-valued MY.SingularityImage plus a + # selective ($$()) transfer entry and a require_gpus capability floor. A + # plain .sif / osdf:// value keeps the legacy single-image behavior below. + singularity_is_family = False + singularity_image_expr = None + singularity_transfer_expr = None + singularity_require_gpus_floor = None + if singularity_image and is_container_manifest(singularity_image): + singularity_is_family = True + _manifest = load_container_manifest(singularity_image) + singularity_image_expr = build_singularity_image_expr(_manifest) + singularity_transfer_expr = build_transfer_input_expr(_manifest) + singularity_require_gpus_floor = build_require_gpus_floor(_manifest) + # Selective transfer: only the matched osdf image is fetched (via the + # $$() token, which is comma-free so it survives transfer_input_files + # comma-splitting). CVMFS/local images are referenced in place and + # never transferred, so the whole family is never pulled. + if singularity_transfer_expr: + extra_files += [singularity_transfer_expr] + elif singularity_image: if 'osdf:' in singularity_image: singularity_image_used = "./{}".format(singularity_image.split('/')[-1]) extra_files += [singularity_image] - + exe = exe or which("integrate_likelihood_extrinsic") frames_local = None if use_singularity: @@ -2381,7 +2439,12 @@ def write_ILE_sub_simple(tag='integrate', exe=None, log_dir=None, use_eos=False, ile_job.add_condor_cmd('request_CPUs', str(1)) ile_job.add_condor_cmd('transfer_executable', 'False') ile_job.add_condor_cmd("MY.SingularityBindCVMFS", 'True') - ile_job.add_condor_cmd("MY.SingularityImage", '"' + singularity_image_used + '"') + if singularity_is_family: + # Expression-valued: emit the ifThenElse raw, with NO surrounding + # double quotes (a classad expression must not be quoted). + ile_job.add_condor_cmd("MY.SingularityImage", singularity_image_expr) + else: + ile_job.add_condor_cmd("MY.SingularityImage", '"' + singularity_image_used + '"') ile_job.add_condor_cmd("MY.flock_local",'true') # jobs can match to local pool ! requirements.append("HAS_SINGULARITY=?=TRUE") # if not(use_simple_osg_requirements): @@ -2529,8 +2592,18 @@ def write_ILE_sub_simple(tag='integrate', exe=None, log_dir=None, use_eos=False, remove_str = 'JobStatus =?= 2 && (CurrentTime - JobStartDate) > ( {})'.format(60*max_runtime_minutes) ile_job.add_condor_cmd('periodic_remove', remove_str) - if 'RIFT_REQUIRE_GPUS' in os.environ: # new convention 'require_gpus = ' to specify conditions on GPU properties - ile_job.add_condor_cmd('require_gpus',os.environ['RIFT_REQUIRE_GPUS']) + # require_gpus: compose the user's RIFT_REQUIRE_GPUS (used today to block + # incompatible hosts by DeviceName) with the container family's capability + # floor (lowest capability any image in the family supports). Both apply; + # neither is silently dropped. (new convention 'require_gpus = ' specifies + # conditions on GPU properties) + require_gpus_terms = [] + if 'RIFT_REQUIRE_GPUS' in os.environ: + require_gpus_terms.append('({})'.format(os.environ['RIFT_REQUIRE_GPUS'])) + if singularity_is_family and singularity_require_gpus_floor: + require_gpus_terms.append('({})'.format(singularity_require_gpus_floor)) + if require_gpus_terms: + ile_job.add_condor_cmd('require_gpus', ' && '.join(require_gpus_terms)) ### @@ -2669,6 +2742,195 @@ def write_consolidate_sub_simple(tag='consolidate', exe=None, base=None,target=N +def write_calpilot_sub(tag='calpilot', exe=None, log_dir=None, universe="vanilla", + working_directory=None, ile_args_file=None, top_fraction=0.05, + max_points=32, request_memory=4096, request_gpu=True, + singularity_image=None, max_runtime_minutes=300, + use_osg=False, use_singularity=False, frames_dir=None, + use_oauth_files=False, transfer_files=None, **kwargs): + """Submit file for a calibration PILOT stage (Option C; see + RIFT/calmarg/DESIGN_adaptive_driver.md): harvest top-lnL points from iteration + $(macroiteration)'s composite, run ILE --calibration-dump-responsibilities on them, + fit + consolidate a cal proposal that seeds wide_{N+1}. Runs util_CalPilotStage.py. + + Macros expected at instantiation: macroiteration, macroiterationprev. + Produces /cal_consolidated_$(macroiteration).npz (consumed by wide_{N+1} ILE via + --calibration-proposal-breadcrumb). + + OSG/container (use_osg/use_singularity): the CALPILOT job runs ILE internally, so it + needs the SAME input set as a wide ILE job -- mirrors write_ILE_sub_simple: + - runs in the singularity image (exe at SINGULARITY_BASE_EXE_DIR); + - a prescript (calpilot_pre.sh) rebuilds local.cache from the transferred frames; + - transfer_input_files = transfer_files (PSD + cal envelopes) + frames_dir + the + composite + args_ile.txt; transfer_output_files = the consolidated breadcrumb; + - the stage args reference BASENAMES (the worker has no shared filesystem). + Refinement (--prev-breadcrumb) is skipped on OSG: the previous breadcrumb is produced + at runtime so it cannot be reliably listed for transfer (esp. iteration 0); each OSG + pilot is an independent cold start (safe -- the fit shrinks toward the prior). + NOTE: untested off-CIT; validate the container + transfer on a real OSG run. + """ + exe = exe or which("util_CalPilotStage.py") + wd = working_directory + on_osg = bool(use_osg or use_singularity) + exe_base = os.path.basename(exe) + frames_local = os.path.basename(frames_dir) if frames_dir else None + if transfer_files is None: + transfer_files = [] + transfer_files = list(transfer_files) + + if use_singularity: + base = os.environ.get('SINGULARITY_BASE_EXE_DIR', '/usr/bin/') + exe = base.rstrip('/') + '/' + exe_base + + # On OSG/container there is no shared FS: the stage's inputs are transferred FLAT into + # the job scratch dir, so reference them by basename and run in '.'; on a local shared + # FS use absolute paths. + if on_osg: + composite_arg = "consolidated_$(macroiteration).composite" + ile_args_arg = os.path.basename(ile_args_file) if ile_args_file else "args_ile.txt" + out_arg = "cal_consolidated_$(macroiteration).npz" + workdir_arg = "." + else: + composite_arg = wd + "/consolidated_$(macroiteration).composite" + ile_args_arg = ile_args_file + out_arg = wd + "/cal_consolidated_$(macroiteration).npz" + workdir_arg = wd + + # Prescript: on OSG, rebuild local.cache (relative paths) from the transferred frames, + # then exec the stage. Mirrors write_ILE_sub_simple's ile_pre.sh. + if on_osg and frames_local: + lalapps_path2cache = os.environ.get('LALAPPS_PATH2CACHE', 'lal_path2cache') + pre = 'calpilot_pre.sh' + with open(pre, 'w') as f: + f.write("#! /bin/bash -xe \n") + f.write("ls {0} | {1} 1> local.cache \n".format(frames_local, lalapps_path2cache)) + f.write("cat local.cache | awk '{print $1, $2, $3, $4}' > local_stripped.cache \n") + f.write("for i in `ls {0}`; do echo {0}/$i; done > base_paths.dat \n".format(frames_local)) + f.write("paste local_stripped.cache base_paths.dat > local_relative.cache \n") + f.write("cp local_relative.cache local.cache \n") + f.write('{0} "$@" \n'.format(exe)) + os.system("chmod a+x " + pre) + transfer_files += [os.path.abspath(pre), frames_dir] + exe = pre + + job = pipeline.CondorDAGJob(universe="vanilla", executable=exe) + sub_name = tag + '.sub' + job.set_sub_file(sub_name) + + # arguments (per-iteration files via condor macros) + job.add_opt("composite", composite_arg) + job.add_opt("ile-args-file", ile_args_arg) + job.add_opt("iteration", "$(macroiteration)") + job.add_opt("output-breadcrumb", out_arg) + if not on_osg: + job.add_opt("prev-breadcrumb", wd + "/cal_consolidated_$(macroiterationprev).npz") + job.add_opt("top-fraction", str(top_fraction)) + job.add_opt("max-points", str(max_points)) + job.add_opt("workdir", workdir_arg) + + uniq_str = "$(macroiteration)-$(cluster)-$(process)" + job.set_log_file("%s%s-%s.log" % (log_dir, tag, uniq_str)) + job.set_stderr_file("%s%s-%s.err" % (log_dir, tag, uniq_str)) + job.set_stdout_file("%s%s-%s.out" % (log_dir, tag, uniq_str)) + + if default_resolved_env: + job.add_condor_cmd('environment', default_resolved_env) + else: + job.add_condor_cmd('getenv', default_getenv_value) + job.add_condor_cmd('request_memory', str(request_memory) + "M") + if request_gpu: + job.add_condor_cmd('request_GPUs', '1') # the pilot runs ILE (GPU path) + + requirements = [] + if use_singularity and singularity_image: + job.add_condor_cmd('transfer_executable', 'False') + job.add_condor_cmd("MY.SingularityBindCVMFS", 'True') + job.add_condor_cmd("MY.SingularityImage", '"' + singularity_image + '"') + job.add_condor_cmd("MY.flock_local", 'true') + requirements.append("HAS_SINGULARITY=?=TRUE") + elif singularity_image: + job.add_condor_cmd("+SingularityImage", '"' + singularity_image + '"') + if use_oauth_files: + job.add_condor_cmd('use_oauth_services', use_oauth_files) + + if on_osg: + # absolute paths -> condor transfers each to the worker scratch dir by basename, + # which is what the stage args (basenames) reference. + transfer_files += [wd + "/consolidated_$(macroiteration).composite", ile_args_file] + job.add_condor_cmd('transfer_input_files', ','.join(transfer_files)) + job.add_condor_cmd('should_transfer_files', 'YES') + job.add_condor_cmd('when_to_transfer_output', 'ON_EXIT') + job.add_condor_cmd('transfer_output_files', 'cal_consolidated_$(macroiteration).npz') + if requirements: + job.add_condor_cmd('requirements', '&&'.join('({0})'.format(r) for r in requirements)) + + try: + job.add_condor_cmd('accounting_group', os.environ['LIGO_ACCOUNTING']) + job.add_condor_cmd('accounting_group_user', os.environ['LIGO_USER_NAME']) + except: + print(" LIGO accounting information not available. Add manually to %s !" % sub_name) + if not (max_runtime_minutes is None): + remove_str = 'JobStatus =?= 2 && (CurrentTime - JobStartDate) > ( {})'.format(60 * max_runtime_minutes) + job.add_condor_cmd('periodic_remove', remove_str) + return job, sub_name + + +def write_extrconsolidate_sub(tag='extrconsolidate', exe=None, log_dir=None, universe="local", + working_directory=None, request_memory=2048, select="lnL", + max_runtime_minutes=30, **kwargs): + """Submit file for the EXTRINSIC handoff consolidation stage (see + RIFT/calmarg/DESIGN_extrinsic_handoff.md). Runs util_ExtrinsicConsolidate.py. + + After iteration $(macroiteration)'s wide ILE jobs each drop a per-event extrinsic + proposal breadcrumb (extr_proposal_$(macroiteration)_.npz, written by ILE + --extrinsic-proposal-output), this job picks the single most representative one and + writes /extr_consolidated_$(macroiteration).npz , which SEEDS the wide ILE jobs of + iteration $(macroiteration)+1 via --extrinsic-proposal-breadcrumb. + + Runs in the LOCAL universe on the submit node: it is pure-python file selection (no GPU, + no ILE, no container, no frames). On OSG the per-event ILE outputs are transferred back + to /iteration__ile on the submit node (ILE's transfer_output_files default), so a + local-universe job reads them directly from the shared FS -- no per-event input transfer + (which condor cannot glob anyway). The output breadcrumb is ALWAYS written (empty if no + valid input), so the next iteration's seed/transfer never fails. + + Macros expected at instantiation: macroiteration. + """ + exe = exe or which("util_ExtrinsicConsolidate.py") + wd = working_directory + job = pipeline.CondorDAGJob(universe=universe, executable=exe) + sub_name = tag + '.sub' + job.set_sub_file(sub_name) + + # per-event proposals land in the ILE initialdir; pick best -> consolidated breadcrumb in + # wd (where wide_{N+1} ILE's --extrinsic-proposal-breadcrumb path points). + job.add_opt("input-glob", wd + "/iteration_$(macroiteration)_ile/extr_proposal_$(macroiteration)_*.npz") + job.add_opt("output", wd + "/extr_consolidated_$(macroiteration).npz") + job.add_opt("iteration", "$(macroiteration)") + job.add_opt("select", select) + + uniq_str = "$(macroiteration)-$(cluster)-$(process)" + job.set_log_file("%s%s-%s.log" % (log_dir, tag, uniq_str)) + job.set_stderr_file("%s%s-%s.err" % (log_dir, tag, uniq_str)) + job.set_stdout_file("%s%s-%s.out" % (log_dir, tag, uniq_str)) + + if default_resolved_env: + job.add_condor_cmd('environment', default_resolved_env) + else: + job.add_condor_cmd('getenv', default_getenv_value) + job.add_condor_cmd('request_memory', str(request_memory) + "M") + + try: + job.add_condor_cmd('accounting_group', os.environ['LIGO_ACCOUNTING']) + job.add_condor_cmd('accounting_group_user', os.environ['LIGO_USER_NAME']) + except: + print(" LIGO accounting information not available. Add manually to %s !" % sub_name) + if not (max_runtime_minutes is None): + remove_str = 'JobStatus =?= 2 && (CurrentTime - JobStartDate) > ( {})'.format(60 * max_runtime_minutes) + job.add_condor_cmd('periodic_remove', remove_str) + return job, sub_name + + def write_unify_sub_simple(tag='unify', exe=None, base=None,target=None,universe="vanilla",arg_str=None,log_dir=None, use_eos=False,ncopies=1,no_grid=False, max_runtime_minutes=60,extra_text='',**kwargs): """ Write a submit file for launching a consolidation job @@ -3493,6 +3755,56 @@ def write_cat_sub(tag='cat', exe=None, file_prefix=None,file_postfix=None,file_o +def write_consolidate_distance_grids_sub(tag='consolidate_dgrid', exe=None, + input_glob=None, file_output=None, + search_dir='.', universe='local', + log_dir=None, no_grid=False, **kwargs): + """Consolidate per-event .dgrid (Plan A) / .dslice (Plan B) files. + + Wraps ``util_ConsolidateDistanceGrids.py``: writes a thin shell driver + that runs the consolidator over ``input_glob`` (a find-style pattern, e.g. + ``EXTR_out.xml_*_.dgrid``) in ``search_dir`` and emits the concatenated + table at ``file_output``. Mirrors ``write_cat_sub`` so it slots into the + same post-extrinsic part of the DAG. + """ + exe = exe or which("util_ConsolidateDistanceGrids.py") + if not exe: + exe = "util_ConsolidateDistanceGrids.py" + + cmdname = tag + '.sh' + with open(cmdname, 'w') as f: + f.write("#! /bin/bash\n") + f.write("set -e\n") + f.write("cd " + search_dir + "\n") + # --allow-empty keeps the post-extrinsic job from failing the DAG if a + # re-run already consumed the per-event files or none were produced. + f.write(exe + " --input-glob '" + input_glob + "'" + " --output " + file_output + " --allow-empty\n") + os.system("chmod a+x " + cmdname) + + ile_job = CondorDAGJob(universe=universe, executable=cmdname) + if no_grid: + ile_job.add_condor_cmd("MY.DESIRED_SITES", '"nogrid"') + ile_job.add_condor_cmd("MY.flock_local", 'true') + + ile_sub_name = tag + '.sub' + ile_job.set_sub_file(ile_sub_name) + + uniq_str = "$(cluster)-$(process)" + ile_job.set_log_file("%s%s-%s.log" % (log_dir, tag, uniq_str)) + ile_job.set_stderr_file("%s%s-%s.err" % (log_dir, tag, uniq_str)) + ile_job.set_stdout_file("%s%s-%s.out" % (log_dir, tag, uniq_str)) + + ile_job.add_condor_cmd('getenv', default_getenv_value) + try: + ile_job.add_condor_cmd('accounting_group', os.environ['LIGO_ACCOUNTING']) + ile_job.add_condor_cmd('accounting_group_user', os.environ['LIGO_USER_NAME']) + except: + print(" LIGO accounting information not available. You must add this manually to integrate.sub !") + + return ile_job, ile_sub_name + + def write_convertpsd_sub(tag='convert_psd', exe=None, ifo=None,file_input=None,target_dir=None,arg_str='',log_dir=None, universe='local',**kwargs): """ Write script to convert PSD from one format to another. Needs to be called once per PSD file being used. diff --git a/MonteCarloMarginalizeCode/Code/RIFT/misc/distance_grid.py b/MonteCarloMarginalizeCode/Code/RIFT/misc/distance_grid.py new file mode 100644 index 000000000..4e3ebcc6d --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/RIFT/misc/distance_grid.py @@ -0,0 +1,187 @@ +"""Per-intrinsic likelihood-vs-distance export for ILE. + +The exported ``lnL`` is the *pure* extrinsic-marginalized likelihood as a +function of luminosity distance:: + + L_pure(d) = integral L(d, Omega) pi_Omega(Omega) dOmega + +i.e. the distance sampling prior has been divided out. Downstream consumers +can re-marginalize over distance with any prior pi'(d):: + + L_marg' = sum_k exp(lnL[k]) * pi'(dist[k]) * dist_weight[k] + +For convenience the grid also carries ``ln_prior_d_sampling``, the per-bin +log of the distance prior that ILE used while integrating, so the original +marginal likelihood can be reproduced exactly:: + + lnL_marg = logsumexp(lnL + ln_prior_d_sampling + log(dist_weight)) +""" +import numpy as np + + +DISTANCE_GRID_FIELDS = ( + "lnL", + "sigmaL", + "m1", + "m2", + "s1x", + "s1y", + "s1z", + "s2x", + "s2y", + "s2z", + "lambda1", + "lambda2", + "eccentricity", + "meanPerAno", + "eos_index", + "dist", + "dist_weight", + "ln_prior_d_sampling", +) + + +def _logsumexp(vals): + vals = np.asarray(vals, dtype=float) + vmax = np.max(vals) + if not np.isfinite(vmax): + return vmax + return vmax + np.log(np.sum(np.exp(vals - vmax))) + + +def _as_positive_integer(value, default): + if value is None: + return default + value = int(value) + if value < 1: + raise ValueError("distance grid size must be positive") + return value + + +def _weighted_blocks(distance, ln_prior_d, probability, n_grid): + """Sort samples by distance, split into n_grid equal-count blocks, and + return per-block (center, mass, width, mean ln-prior).""" + order = np.argsort(distance) + distance = np.asarray(distance, dtype=float)[order] + probability = np.asarray(probability, dtype=float)[order] + ln_prior_d = np.asarray(ln_prior_d, dtype=float)[order] + + finite = np.isfinite(distance) & np.isfinite(probability) & (probability > 0) & np.isfinite(ln_prior_d) + distance = distance[finite] + probability = probability[finite] + ln_prior_d = ln_prior_d[finite] + if len(distance) == 0: + raise ValueError("no finite positive-weight distance samples to export") + + n_grid = min(_as_positive_integer(n_grid, len(distance)), len(distance)) + blocks = np.array_split(np.arange(len(distance)), n_grid) + grid_dist = np.empty(len(blocks)) + grid_mass = np.empty(len(blocks)) + grid_ln_prior = np.empty(len(blocks)) + for i, block in enumerate(blocks): + w = probability[block] + grid_mass[i] = np.sum(w) + grid_dist[i] = np.sum(distance[block] * w) / grid_mass[i] + # weighted average of ln_prior_d (in log space, by importance weights): + # log E_w[pi_d] = logsumexp(ln_prior_d + log w) - log sum_w + grid_ln_prior[i] = ( + _logsumexp(ln_prior_d[block] + np.log(w)) - np.log(grid_mass[i]) + ) + + if len(grid_dist) == 1: + width = np.array([max(np.ptp(distance), np.finfo(float).eps)]) + else: + edges = np.empty(len(grid_dist) + 1) + edges[1:-1] = 0.5 * (grid_dist[1:] + grid_dist[:-1]) + edges[0] = min(distance[0], grid_dist[0] - (edges[1] - grid_dist[0])) + edges[-1] = max(distance[-1], grid_dist[-1] + (grid_dist[-1] - edges[-2])) + width = np.diff(edges) + width = np.maximum(width, np.finfo(float).eps) + + return grid_dist, grid_mass, width, grid_ln_prior + + +def build_distance_grid(distance, ln_weights, lnL_marginal, sigmaL, params, + ln_prior_d_at_samples, n_grid=None): + """Build a likelihood-vs-distance grid from weighted ILE samples. + + Parameters + ---------- + distance : array + Per-sample luminosity distances drawn by the ILE sampler. + ln_weights : array + Per-sample log importance weights, ``log L_i + log pi(theta_i) - log q(theta_i)``, + with ``pi`` and ``q`` being the joint prior and proposal used by ILE. + These weights include the distance prior. + lnL_marginal : float + The marginalized lnL the ILE batchmode would report (``log_res + + manual_avoid_overflow_logarithm``). Used as the absolute calibration. + sigmaL : float + ILE's reported lnL uncertainty. Carried verbatim into the grid. + params : dict + Intrinsic parameters to broadcast across the grid rows (mass, spins, + tides, ...). Missing keys default to 0. + ln_prior_d_at_samples : array + Per-sample log of the *distance* prior pi_d(d_i) used by ILE. This + is divided out so the exported ``lnL`` is a pure likelihood, not a + density-times-prior. + n_grid : int, optional + Number of grid bins. Defaults to ``len(distance)``. + """ + ln_weights = np.asarray(ln_weights, dtype=float) + ln_norm = _logsumexp(ln_weights) + probability = np.exp(ln_weights - ln_norm) + grid_dist, grid_mass, grid_width, grid_ln_prior = _weighted_blocks( + distance, ln_prior_d_at_samples, probability, n_grid) + + dtype = [(name, float) for name in DISTANCE_GRID_FIELDS] + grid = np.zeros(len(grid_dist), dtype=dtype) + # Pure likelihood density in d: subtract log mean prior_d in bin so + # exp(lnL) = L_marg * p_post(d) / pi_d(d) = L(d) [extrinsic-marginalized]. + grid["lnL"] = ( + lnL_marginal + np.log(grid_mass) - np.log(grid_width) - grid_ln_prior + ) + grid["sigmaL"] = sigmaL + grid["dist"] = grid_dist + grid["dist_weight"] = grid_width + grid["ln_prior_d_sampling"] = grid_ln_prior + + for name in DISTANCE_GRID_FIELDS: + if name in {"lnL", "sigmaL", "dist", "dist_weight", "ln_prior_d_sampling"}: + continue + grid[name] = float(params.get(name, 0.0)) + return grid + + +def save_distance_grid(fname, grid): + header = " ".join(grid.dtype.names) + np.savetxt(fname, np.column_stack([grid[name] for name in grid.dtype.names]), header=header) + + +def load_distance_grid(fname): + return np.genfromtxt(fname, names=True) + + +def reconstruct_marginal_lnL(grid, ln_prior_d=None): + """Reconstruct the marginal lnL by integrating exp(lnL)*prior(d) over the + grid. If ``ln_prior_d`` is None and the grid has the ``ln_prior_d_sampling`` + column, that column (the sampling prior) is used. Otherwise integrates + against a flat prior (treats lnL as already-pure). Pass a callable + ``ln_prior_d(d)`` to integrate against a custom distance prior. + """ + names = grid.dtype.names + if "dist_weight" not in names: + # legacy grids without dist_weight: trapezoidal + order = np.argsort(grid["dist"]) + trap = np.trapezoid if hasattr(np, "trapezoid") else np.trapz + return np.log(trap(np.exp(grid["lnL"][order]), grid["dist"][order])) + + log_dw = np.log(grid["dist_weight"]) + if ln_prior_d is not None: + ln_pi = np.asarray(ln_prior_d(grid["dist"]), dtype=float) + return _logsumexp(grid["lnL"] + ln_pi + log_dw) + if "ln_prior_d_sampling" in names: + return _logsumexp(grid["lnL"] + grid["ln_prior_d_sampling"] + log_dw) + # legacy grids with dist_weight but no separate prior column: treat lnL + # as a pre-multiplied density (old format) + return _logsumexp(grid["lnL"] + log_dw) diff --git a/MonteCarloMarginalizeCode/Code/RIFT/misc/distance_slices.py b/MonteCarloMarginalizeCode/Code/RIFT/misc/distance_slices.py new file mode 100644 index 000000000..cc124a137 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/RIFT/misc/distance_slices.py @@ -0,0 +1,538 @@ +"""Plan-B distance-slice export for ILE. + +After ILE finishes its normal extrinsic integration at one intrinsic point, +this module produces K independent fixed-distance integrals: for each slice +d_k, an estimate of + + L_pure(d_k) = integral L(d_k, Omega) pi_Omega(Omega) dOmega + +i.e. the extrinsic-marginalized likelihood at distance d_k, with the +distance prior divided out. These can be re-marginalized against any prior +downstream and (because they are independent integrals, not bin +re-weightings of one shared sampler state) the *shape* L_pure(d) is honest +at the n_eff RIFT routinely uses. + +Two estimators are provided: + +* ``importance_reweight_slices`` -- reuses the Omega samples from the main + integration and re-evaluates ``like_to_integrate`` at each slice distance. + Cost: K * N likelihood evaluations using the already-precomputed + rholms_intp / cross_terms (cheap). Best when the Omega posterior is + close to d-independent (typical: sky/inclination weakly couple to d + except via the overall amplitude). + +* ``fresh_sample_slices`` -- builds a fresh, low-dim sampler over Omega + only at fixed d_k. More expensive but doesn't assume the main run's + Omega proposal is good for d_k. Intended primarily as a cross-check. + +The output schema is one row per (intrinsic, d_slice) pair so the file is +the natural Plan-B analogue of ``.composite``. Target size: <~ 10x the +original ``.composite`` (K=10 by default). +""" +import numpy as np + + +DISTANCE_SLICE_FIELDS = ( + "lnL", # extrinsic-marginalized lnL at d=dist (pure likelihood, + # i.e. distance sampling prior divided out) + "sigmaL", # log-space uncertainty on the slice integral + "neff", # effective sample count contributing to the slice + "ntotal", # total samples consumed by the slice estimator + "method", # 0 = importance_reweight, 1 = fresh_sample + "m1", + "m2", + "s1x", + "s1y", + "s1z", + "s2x", + "s2y", + "s2z", + "lambda1", + "lambda2", + "eccentricity", + "meanPerAno", + "eos_index", + "dist", + "ln_prior_d_sampling", # log pi_d(d_k) under the ILE sampling prior, + # so default reconstruction reproduces log_res +) + + +METHOD_REWEIGHT = 0 +METHOD_FRESH = 1 + + +def _logsumexp(x): + x = np.asarray(x, dtype=float) + m = np.max(x) + if not np.isfinite(m): + return m + return m + np.log(np.sum(np.exp(x - m))) + + +def quantile_slice_centers(distance_samples, ln_weights, n_slices): + """Choose K slice centers as equi-probable quantiles of the posterior in d. + + Falls back to uniform-in-log-d if the posterior is degenerate (n_eff < 2). + """ + distance_samples = np.asarray(distance_samples, float) + ln_weights = np.asarray(ln_weights, float) + finite = np.isfinite(ln_weights) & np.isfinite(distance_samples) + d = distance_samples[finite] + lw = ln_weights[finite] + if len(d) == 0: + raise ValueError("no finite samples to choose slice centers from") + p = np.exp(lw - _logsumexp(lw)) + n_eff = 1.0 / np.sum(p**2) + if n_eff < 2.0: + # degenerate posterior; cover the sample range uniformly in log d + d_lo, d_hi = float(d.min()), float(d.max()) + d_lo = max(d_lo, 1e-3) + return np.exp(np.linspace(np.log(d_lo), np.log(d_hi), n_slices)) + order = np.argsort(d) + d_sorted = d[order] + cdf = np.cumsum(p[order]) + cdf /= cdf[-1] + quant = (np.arange(n_slices) + 0.5) / n_slices + return np.interp(quant, cdf, d_sorted) + + +def _ln_omega_iw_factor(rvs, ln_prior_d_at_samples, ln_proposal_d_at_samples): + """log( pi_Omega(Omega_i) / q_Omega(Omega_i) ) per sample. + + Decomposes the stored joint weight ln(pi_joint/q_joint) into a distance + piece and an Omega piece, returning the Omega piece. + """ + # Pull joint prior / proposal ratio + if "joint_prior" in rvs and "joint_s_prior" in rvs: + jp = np.asarray(rvs["joint_prior"], float) + jsp = np.asarray(rvs["joint_s_prior"], float) + with np.errstate(divide="ignore"): + ln_pi_over_q_joint = np.log(np.maximum(jp, np.finfo(float).tiny)) \ + - np.log(np.maximum(jsp, np.finfo(float).tiny)) + elif "log_joint_prior" in rvs and "log_joint_s_prior" in rvs: + ln_pi_over_q_joint = np.asarray(rvs["log_joint_prior"], float) \ + - np.asarray(rvs["log_joint_s_prior"], float) + else: + raise KeyError("sampler._rvs missing joint prior/proposal columns") + return ln_pi_over_q_joint - (np.asarray(ln_prior_d_at_samples, float) + - np.asarray(ln_proposal_d_at_samples, float)) + + +def importance_reweight_slices( + sampler, like_to_integrate, d_slices, + ln_prior_d_at_samples, ln_proposal_d_at_samples, + manual_overflow=0.0, return_lnL=True, +): + """Importance-reweight existing Omega samples at K slice distances. + + Returns + ------- + lnL_slices : (K,) array + Extrinsic-marginalized lnL at each d_k (pure likelihood, with + ``manual_overflow`` restored so the value is directly comparable + to ILE's reported ``log_res``). + sigmaL_slices : (K,) array + Per-slice 1-sigma uncertainty in lnL (Monte Carlo standard error). + neff_slices : (K,) array + Effective sample count at each slice. + ntotal : int + Total samples consumed (same for all slices: it is N). + """ + rvs = sampler._rvs + if "distance" not in rvs: + raise KeyError("sampler._rvs has no 'distance' samples") + N = len(rvs["distance"]) + ln_omega_iw = _ln_omega_iw_factor(rvs, ln_prior_d_at_samples, + ln_proposal_d_at_samples) + + # Identify the param signature of like_to_integrate + arg_names = like_to_integrate.__code__.co_varnames[ + :like_to_integrate.__code__.co_argcount] + + # Build per-arg arrays from the sampler's stored samples; distance gets + # broadcast per slice below. + fixed_inputs = {} + for a in arg_names: + if a == "distance": + continue + if a not in rvs: + raise KeyError("sampler._rvs missing required column {!r} for " + "slice reweighting".format(a)) + fixed_inputs[a] = np.asarray(rvs[a]) + + K = len(d_slices) + lnL_out = np.empty(K) + sigmaL_out = np.empty(K) + neff_out = np.empty(K) + for k, d_k in enumerate(d_slices): + like_inputs = [] + for a in arg_names: + if a == "distance": + like_inputs.append(np.full(N, float(d_k))) + else: + like_inputs.append(fixed_inputs[a]) + lnL_at = like_to_integrate(*like_inputs) + lnL_at = np.asarray(lnL_at, dtype=np.float64) + if not return_lnL: + # function returned exp(lnL - overflow); take log + with np.errstate(divide="ignore"): + lnL_at = np.log(np.maximum(lnL_at, np.finfo(float).tiny)) + # ln L_k(Omega_i) was returned with manual_overflow subtracted; add + # it back so the slice marginal matches log_res's overflow scaling. + ln_terms = lnL_at + manual_overflow + ln_omega_iw + lnL_marg = _logsumexp(ln_terms) - np.log(N) + # Slice n_eff in the importance sample + m = np.max(ln_terms) + if not np.isfinite(m): + neff_out[k] = 0.0 + else: + w = np.exp(ln_terms - m) + neff_out[k] = (w.sum())**2 / np.sum(w**2) + # MC std error of the log of the mean: approx by w-std / w-mean + # std(lnI) ~ sqrt(var(w)/mean(w)^2 / N) + if np.isfinite(m) and neff_out[k] > 1: + mean_w = np.mean(np.exp(ln_terms - m)) + var_w = np.var(np.exp(ln_terms - m)) + with np.errstate(invalid="ignore"): + sigmaL_out[k] = np.sqrt(var_w / (N * max(mean_w, np.finfo(float).tiny)**2)) + else: + sigmaL_out[k] = np.inf + lnL_out[k] = lnL_marg + + return lnL_out, sigmaL_out, neff_out, N + + +def is_uninformative(lnL_core, threshold=1.0): + """Detect a non-detectable event from the core slices via an absolute lnL. + + In RIFT's framing lnL is a likelihood ratio relative to the noise + hypothesis, so it carries an absolute scale. If the *peak* lnL across the + core slices does not exceed ``threshold`` nats, the event is effectively + undetected -- its distance posterior carries no information worth probing, + so wing integrations are wasted compute and we skip them. + + This intentionally does NOT key off the spread ``max - min``: a high-SNR + event with a flat distance profile (e.g. well-constrained inclination but + unconstrained distance) has a small spread yet a large peak lnL, and *does* + deserve wings. A relative-spread test would wrongly skip it. + """ + finite = np.isfinite(lnL_core) + if not np.any(finite): + return True + return np.max(lnL_core[finite]) < threshold + + +def _log_uniform_wings(d_min, d_max, d_core_lo, d_core_hi, n_wing, min_log_gap): + """Log-uniform wing placement across the full spans outside the core. + + Half below the core, half above (lower half gets the extra when n_wing is + odd). This is the likelihood-shape-agnostic fallback used whenever the + parabolic fit is degenerate. + """ + n_low = (n_wing + 1) // 2 + n_high = n_wing - n_low + wings = [] + if d_core_lo > d_min * np.exp(min_log_gap) and n_low > 0: + wings.append(np.exp(np.linspace(np.log(d_min), + np.log(d_core_lo), + n_low + 2)[1:-1])) + if d_max > d_core_hi * np.exp(min_log_gap) and n_high > 0: + wings.append(np.exp(np.linspace(np.log(d_core_hi), + np.log(d_max), + n_high + 2)[1:-1])) + if not wings: + return np.array([]) + return np.sort(np.concatenate(wings)) + + +def fit_lnL_parabola_in_inv_d(d_core, lnL_core): + """Fit lnL_core to a quadratic in u = 1/dist. + + Near the peak the extrinsic-marginalized lnL is well modeled by + + lnL(d) ~= lnL_peak - 0.5 * A^2 * (1/d - 1/d_peak)^2 + + which is a downward parabola in u = 1/d. Returns ``(a, b, c)`` from + ``lnL ~= a u^2 + b u + c`` (so ``A^2 = -2 a`` and the vertex sits at + ``u_peak = -b/2a``), or ``None`` if the fit is degenerate (fewer than 3 + distinct finite core points, no lnL variation, or a non-downward fit). + """ + d_core = np.asarray(d_core, float) + lnL_core = np.asarray(lnL_core, float) + finite = np.isfinite(d_core) & (d_core > 0) & np.isfinite(lnL_core) + if np.sum(finite) < 3: + return None + u = 1.0 / d_core[finite] + y = lnL_core[finite] + if np.ptp(u) <= 0 or np.ptp(y) <= 0: + return None + try: + a, b, c = np.polyfit(u, y, 2) + except Exception: + return None + if not (np.isfinite(a) and np.isfinite(b) and np.isfinite(c)) or a >= 0: + return None + return float(a), float(b), float(c) + + +def _parabolic_wing_bounds(d_core, lnL_core, lnL_peak, delta_lnL_target, + d_min, d_max): + """Boundary distances where the lnL parabola drops ``delta_lnL_target``. + + Solves the fitted ``lnL(u) = a u^2 + b u + c`` (u = 1/dist) for the two + u where lnL equals ``(lnL_peak or fitted vertex) - delta_lnL_target``, + then maps back to distance and clamps to ``[d_min, d_max]``. + + Returns ``(d_small_bound, d_large_bound)`` or ``None`` if the fit is + degenerate (caller falls back to log-uniform). + """ + fit = fit_lnL_parabola_in_inv_d(d_core, lnL_core) + if fit is None: + return None + a, b, c = fit + vertex_u = -b / (2.0 * a) + vertex_val = c - b * b / (4.0 * a) + target = (vertex_val if lnL_peak is None else float(lnL_peak)) \ + - float(delta_lnL_target) + disc = b * b - 4.0 * a * (c - target) + if disc > 0: + sq = np.sqrt(disc) + r1 = (-b - sq) / (2.0 * a) + r2 = (-b + sq) / (2.0 * a) + u_lo, u_hi = min(r1, r2), max(r1, r2) + else: + # target above the fitted vertex (observed peak exceeds the fit, or + # delta too small): fall back to the vertex-symmetric half-width, + # which always yields real roots for a downward parabola. + half_width = np.sqrt(-float(delta_lnL_target) / a) + u_lo, u_hi = vertex_u - half_width, vertex_u + half_width + # u_lo -> larger distance boundary; u_hi -> smaller distance boundary. + d_large = 1.0 / u_lo if u_lo > 0 else d_max + d_small = 1.0 / u_hi if u_hi > 0 else d_min + d_small = float(np.clip(d_small, d_min, d_max)) + d_large = float(np.clip(d_large, d_min, d_max)) + if not (d_large > d_small): + return None + return d_small, d_large + + +def pick_wing_centers(d_min, d_max, d_core, n_wing, + lnL_core=None, lnL_peak=None, delta_lnL_target=7.0, + min_log_gap=0.05): + """Place K_wing slice centers outside the core span. + + When ``lnL_core`` is supplied and a quadratic fit of lnL vs 1/dist is + non-degenerate, the wing span on each side is bounded by the parabolic + model: wings extend from the core edge out to where lnL drops + ``delta_lnL_target`` nats below the peak (default 7, i.e. prior weight + < ~10^-3 outside). This concentrates wing compute where the likelihood + actually has support instead of spreading it across the whole prior range. + + Falls back to ``_log_uniform_wings`` (likelihood-agnostic, full-range) + whenever the fit is degenerate or leaves no room outside the core. Half + the wings go below the core, half above (lower half gets the extra when + n_wing is odd). Returns a sorted array of distances. + """ + n_wing = int(n_wing) + if n_wing <= 0: + return np.array([]) + d_core = np.asarray(d_core, float) + finite = np.isfinite(d_core) & (d_core > 0) + d_core_lo = float(np.min(d_core[finite])) if np.any(finite) else d_min + d_core_hi = float(np.max(d_core[finite])) if np.any(finite) else d_max + + bounds = None + if lnL_core is not None: + bounds = _parabolic_wing_bounds(d_core, lnL_core, lnL_peak, + delta_lnL_target, d_min, d_max) + if bounds is None: + return _log_uniform_wings(d_min, d_max, d_core_lo, d_core_hi, + n_wing, min_log_gap) + + d_small_bound, d_large_bound = bounds + n_low = (n_wing + 1) // 2 + n_high = n_wing - n_low + wings = [] + if d_core_lo > d_small_bound * np.exp(min_log_gap) and n_low > 0: + wings.append(np.exp(np.linspace(np.log(d_small_bound), + np.log(d_core_lo), + n_low + 2)[1:-1])) + if d_large_bound > d_core_hi * np.exp(min_log_gap) and n_high > 0: + wings.append(np.exp(np.linspace(np.log(d_core_hi), + np.log(d_large_bound), + n_high + 2)[1:-1])) + if not wings: + return _log_uniform_wings(d_min, d_max, d_core_lo, d_core_hi, + n_wing, min_log_gap) + return np.sort(np.concatenate(wings)) + + +def fresh_sample_slices(reference_sampler, like_to_integrate, d_slices, + n_max=20000, n_eff_target=30, n_chunk=2000, + return_lnL=True, verbose=False): + """Independent Omega-only integration at each pinned distance d_k. + + Build a fresh AdaptiveVolume sampler for the Omega parameters by + cloning the reference sampler's per-parameter (pdf, prior, bounds) + config, then integrate the cached likelihood with distance pinned to + each slice. Cost per slice: up to ``n_max`` cached-likelihood + evaluations (no waveform/PSD regeneration). + + Returns the same (lnL, sigmaL, neff, ntotal_array) tuple shape as + ``importance_reweight_slices``. + """ + from RIFT.integrators import mcsamplerAdaptiveVolume + + arg_names = like_to_integrate.__code__.co_varnames[ + :like_to_integrate.__code__.co_argcount] + if "distance" not in arg_names: + raise ValueError("like_to_integrate has no 'distance' arg; fresh " + "slice integration not applicable") + omega_params = [a for a in arg_names if a != "distance"] + missing = [p for p in omega_params + if p not in reference_sampler.params_ordered] + if missing: + raise KeyError("reference_sampler missing Omega params {!r} needed " + "for fresh slice integration".format(missing)) + + K = len(d_slices) + lnL_out = np.full(K, -np.inf) + sigmaL_out = np.full(K, np.inf) + neff_out = np.zeros(K) + ntotal_out = np.zeros(K, dtype=int) + + for k, d_k in enumerate(d_slices): + sampler = mcsamplerAdaptiveVolume.MCSampler() + for p in omega_params: + sampler.add_parameter( + p, + pdf=reference_sampler.pdf[p], + prior_pdf=reference_sampler.prior_pdf[p], + left_limit=float(reference_sampler.llim[p]), + right_limit=float(reference_sampler.rlim[p]), + adaptive_sampling=True, + ) + + d_fixed = float(d_k) + # Per-Omega-param bounds, used to clip values defensively against + # boundary noise (e.g. np.random.uniform can return rlim - 1ULP which + # makes downstream arccos(...) NaN). + omega_bounds = {p: (float(reference_sampler.llim[p]), + float(reference_sampler.rlim[p])) + for p in omega_params} + + def like_at_pinned_d(**kw): + # AV's integrate_log passes Omega params as kwargs by name. + sample = next(iter(kw.values())) + N_eval = len(sample) + d_arr = np.full(N_eval, d_fixed) + full = {} + for p, arr in kw.items(): + lo, hi = omega_bounds.get(p, (-np.inf, np.inf)) + # nudge inward by a tiny epsilon relative to range, so arccos + # and friends never see the exact boundary + eps = 1e-12 * max(abs(hi - lo), 1.0) + full[p] = np.clip(np.asarray(arr, float), lo + eps, hi - eps) + full["distance"] = d_arr + return like_to_integrate(*(full[a] for a in arg_names)) + + try: + res = sampler.integrate_log( + like_at_pinned_d, + *omega_params, + nmax=int(n_max), neff=int(n_eff_target), n=int(n_chunk), + tempering_exp=0.1, n_adapt=10, + verbose=verbose, + ) + except Exception as e: + print(" fresh slice d={:.2f} failed: {!r}".format(d_k, e)) + continue + # AV's integrate_log returns (log_int, log(rel_var) + 2*log_int, + # eff_samp, dict). Convert to sigma_lnL ~ sqrt(rel_var). + if isinstance(res, tuple): + lnI = float(res[0]) + if len(res) > 1: + log_abs_var = float(res[1]) + ln_rel_var = log_abs_var - 2.0 * lnI + sigma = float(np.exp(0.5 * ln_rel_var)) if np.isfinite(ln_rel_var) else np.inf + else: + sigma = np.nan + neff_val = float(res[2]) if len(res) > 2 else np.nan + else: + lnI = float(res) + sigma = np.nan + neff_val = np.nan + # When return_lnL=True the cached like_to_integrate returned + # lnL - manual_overflow, so integrate_log's lnI is log of the integral + # of exp(lnL - overflow). We restore the overflow OUTSIDE this helper + # (caller knows manual_avoid_overflow_logarithm). + lnL_out[k] = lnI + if not(np.isnan(sigma)): + sigmaL_out[k] = sigma + if not(np.isnan(neff_val)): + neff_out[k] = neff_val + ntotal_out[k] = int(getattr(sampler, "ntotal", 0)) + + return lnL_out, sigmaL_out, neff_out, ntotal_out + + +def build_distance_slice_table(d_slices, lnL_slices, sigmaL_slices, + neff_slices, ntotal, method_code, + params, ln_prior_d_at_slices): + """Assemble the K-row slice table for one intrinsic point.""" + d_slices = np.asarray(d_slices, float) + K = len(d_slices) + dtype = [(name, float) for name in DISTANCE_SLICE_FIELDS] + table = np.zeros(K, dtype=dtype) + table["lnL"] = lnL_slices + table["sigmaL"] = sigmaL_slices + table["neff"] = neff_slices + table["ntotal"] = float(ntotal) + table["method"] = float(method_code) + table["dist"] = d_slices + table["ln_prior_d_sampling"] = ln_prior_d_at_slices + for name in DISTANCE_SLICE_FIELDS: + if name in {"lnL", "sigmaL", "neff", "ntotal", "method", "dist", + "ln_prior_d_sampling"}: + continue + table[name] = float(params.get(name, 0.0)) + return table + + +def save_distance_slice_table(fname, table): + header = " ".join(table.dtype.names) + np.savetxt(fname, np.column_stack([table[n] for n in table.dtype.names]), + header=header) + + +def load_distance_slice_table(fname): + return np.genfromtxt(fname, names=True) + + +def reconstruct_marginal_lnL(table, ln_prior_d=None): + """Re-marginalize the slice table over distance with the given prior. + + Default (``ln_prior_d=None``): use ``ln_prior_d_sampling`` stored in the + table -- reproduces ILE's reported ``log_res`` up to MC noise. + + Pass a callable ``ln_prior_d(d)`` to integrate against a custom prior. + + Uses the trapezoid rule on the K slice points -- the slices were placed + at equi-probable quantiles, so this gives a moderate-K decent integral. + """ + order = np.argsort(table["dist"]) + d = table["dist"][order] + lnL = table["lnL"][order] + if ln_prior_d is None: + ln_pi = table["ln_prior_d_sampling"][order] + else: + ln_pi = np.asarray(ln_prior_d(d), float) + log_integrand = lnL + ln_pi + # logsumexp trapezoid + m = np.max(log_integrand) + if not np.isfinite(m): + return m + integrand = np.exp(log_integrand - m) + trap = np.trapezoid if hasattr(np, "trapezoid") else np.trapz + return m + np.log(trap(integrand, d)) diff --git a/MonteCarloMarginalizeCode/Code/RIFT/physics/EOSManager.py b/MonteCarloMarginalizeCode/Code/RIFT/physics/EOSManager.py index 4ab41eda5..72ddf7516 100644 --- a/MonteCarloMarginalizeCode/Code/RIFT/physics/EOSManager.py +++ b/MonteCarloMarginalizeCode/Code/RIFT/physics/EOSManager.py @@ -753,6 +753,54 @@ def make_spec_param_eos(self, xvar='energy_density', yvar='pressure',npts=500, p # https://github.com/oshaughn/RIT-matters/blob/master/communications/20230130-ROSKediaYelikar-EOSManagerSpectralUpdates/demo_reprimand.py +# --- RePrimAnd version-compatibility shims -------------------------------------- +# RePrimAnd's Python API changed (>= ~1.4): the NS-accuracy factory was renamed +# tov_acc_simple -> star_acc_simple (with two leading bool flags), and +# make_tov_branch_stable replaced num_samp/mgrav_min with mg_cut_low_rel/ +# mg_cut_low_abs/gm1_step. These helpers target the modern API (tested vs 1.7) +# but transparently fall back to the legacy one, so RIFT works with either. +# docs: https://wokast.github.io/RePrimAnd/ (tov_solver_ref, ns_seqs_ref) +def _pyr_interval(lo, hi): + """Closed interval object across pyreprimand versions.""" + for nm in ("range", "interval", "range_t"): + f = getattr(pyr, nm, None) + if f is not None: + return f(lo, hi) + raise AttributeError("pyreprimand: no interval/range constructor found") + +def _pyr_star_acc(acc_tov, acc_deform, minsteps, need_deform=True, need_bulk=False): + """NS-solution accuracy spec across pyreprimand versions. + + Modern: star_acc_simple(need_deform, need_bulk, acc_tov, acc_deform, minsteps). + Legacy: tov_acc_simple(acc_tov, acc_deform, minsteps). + """ + if hasattr(pyr, "star_acc_simple"): + # modern star_acc_simple takes keyword-only args: (*, need_deform, ...) + return pyr.star_acc_simple(need_deform=need_deform, need_bulk=need_bulk, + acc_tov=acc_tov, acc_deform=acc_deform, + minsteps=minsteps) + return pyr.tov_acc_simple(acc_tov, acc_deform, minsteps) + +def _pyr_tov_branch(eos, acc, mgrav_min=0.0): + """Stable TOV branch across pyreprimand versions. + + Modern make_tov_branch_stable(eos, acc, mg_cut_low_rel=0.2, mg_cut_low_abs=0.0, + gm1_initial=1.2, gm1_step=0.004, max_margin=1e-2). + Legacy make_tov_branch_stable(eos, acc, num_samp=..., mgrav_min=...). + We map the old absolute low-mass cutoff mgrav_min -> mg_cut_low_abs (and turn + off the relative cutoff so the absolute one is authoritative). + """ + try: + return pyr.make_tov_branch_stable(eos, acc, mg_cut_low_rel=0.0, + mg_cut_low_abs=mgrav_min) + except TypeError: + try: + return pyr.make_tov_branch_stable(eos, acc) # modern defaults + except TypeError: # legacy API + return pyr.make_tov_branch_stable(eos, acc, num_samp=2000, + mgrav_min=mgrav_min) + + class EOSReprimand(EOSConcrete): """Pass param_dict as the dictionary of 'pseudo_enthalpy','rest_mass_density','energy_density','pressure','sound_speed_over_c' for being resolved into a TOV sequence. CGS Units only except sound_speed_over_c. Instead you can send a lalsim_eos which processes lalsim eos object type and produces a TOV sequence. @@ -812,7 +860,7 @@ def update(self,param_dict,specific_internal_energy, RePrimAnd_scale = 1e-6): eps_0 = 0.0 # energy density at zero pressure pts_per_mag =800 # points log spaced per decaded in some parameter isentropic = True - rgrho = pyr.range(min(rho_unew)*1.0000001, max(rho_unew) / 1.0000001) + rgrho = _pyr_interval(min(rho_unew)*1.0000001, max(rho_unew) / 1.0000001) # Instantiate EOS if specific_internal_energy: self.pyr_eos = pyr.make_eos_barotr_spline(rho_unew, spec_int_energy_unew, press_unew, cs, temp, efrac, isentropic, rgrho, n_poly, unew, pts_per_mag) @@ -866,14 +914,14 @@ def make_mr_lambda_reprimand(eos,n_bins=800,save_tov_sequence=False,read_tov_seq assert has_reprimand #Make TOV sequence - acc_tov=RePrimAnd_scale*1e-2; acc_deform=RePrimAnd_scale; minsteps=500; num_samp=2000; mgrav_min=0.3 - acc = pyr.tov_acc_simple(acc_tov, acc_deform, minsteps) - try: seq = pyr.make_tov_branch_stable(eos, acc, num_samp=num_samp, mgrav_min=mgrav_min) - except: - if read_tov_sequence: - sol_units = pyr.units.geom_solar(msun_si=lal.MSUN_SI) - seq = pyr.load_star_branch(eos, sol_units) - else: raise Exception("No EOS supplied.") + acc_tov=RePrimAnd_scale*1e-2; acc_deform=RePrimAnd_scale; minsteps=500; mgrav_min=0.3 + acc = _pyr_star_acc(acc_tov, acc_deform, minsteps) # modern star_acc_simple (legacy tov_acc_simple fallback) + if read_tov_sequence: + # load a previously saved branch (modern: load_star_branch(fname, units)) + sol_units = pyr.units.geom_solar(msun_si=lal.MSUN_SI) + seq = pyr.load_star_branch("tov.seq.h5", sol_units) + else: + seq = _pyr_tov_branch(eos, acc, mgrav_min=mgrav_min) # modern make_tov_branch_stable if save_tov_sequence: try: #bpath = p.parent diff --git a/MonteCarloMarginalizeCode/Code/RIFT/precision.py b/MonteCarloMarginalizeCode/Code/RIFT/precision.py new file mode 100644 index 000000000..2f6976be6 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/RIFT/precision.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- +""" +RIFT.precision +============== + +Centralized high-precision floating-point dtype for RIFT. + +RIFT historically used ``numpy.float128`` directly throughout the integrators +to suppress overflow when accumulating very large or very small probability +weights. ``numpy.float128`` is, however, a *platform-dependent* alias: + +* On x86_64 Linux it is ``numpy.longdouble`` with 80-bit extended precision + stored in 16 bytes, and exposes the alias ``numpy.float128``. +* On macOS arm64 (Apple Silicon), Windows MSVC, and many embedded / non-x86 + Linux builds, ``numpy.longdouble`` has the same width as ``numpy.float64`` + (8 bytes), and ``numpy.float128`` does not exist. +* In NumPy 2.x the ``numpy.float128`` alias was further narrowed and is now + only present where the platform actually has a wider long double type. + +Hardcoding ``numpy.float128`` therefore breaks imports on any platform that +lacks an extended-precision long double. This module provides: + +* ``RiftFloat`` -- the dtype to use for high-precision accumulators. Equals + ``numpy.longdouble`` whenever the platform really gives extra precision + (``itemsize > 8``), otherwise falls back to ``numpy.float64``. +* ``RIFT_FLOAT_HIGH_PRECISION`` -- ``True`` iff ``RiftFloat`` is wider than + ``numpy.float64``. Use this if a code path needs to know whether the + extra precision is actually available (e.g. to skip a downcast that is + only meaningful when float128 is real). + +Recommended usage +----------------- + +Replace :: + + import numpy + neff = numpy.float128("inf") + arr = numpy.array(values, dtype=numpy.float128) + if weights.dtype == numpy.float128: + weights = weights.astype(numpy.float64) + +with :: + + from RIFT.precision import RiftFloat + neff = RiftFloat("inf") + arr = numpy.array(values, dtype=RiftFloat) + if weights.dtype == RiftFloat: + weights = weights.astype(numpy.float64) + +When ``RiftFloat`` is ``numpy.float64`` the type-equality guards become +self-consistent (the ``astype(float64)`` is a no-op) and no platform-specific +branching is required at the call site. +""" + +import numpy as _np + +__all__ = [ + "RiftFloat", + "RIFT_FLOAT_HIGH_PRECISION", + "RIFT_FLOAT_NAME", +] + + +def _select_high_precision_dtype(): + """Pick the widest available real-floating dtype, falling back to float64. + + Prefers ``numpy.longdouble`` (always defined) whenever the platform + actually gives more than 8-byte storage; otherwise returns + ``numpy.float64`` so that consumers can use the result as a drop-in + replacement for ``numpy.float128`` without raising on platforms that + do not expose it. + """ + longdouble_itemsize = _np.dtype(_np.longdouble).itemsize + if longdouble_itemsize > 8: + return _np.longdouble, True + # Some NumPy 1.x builds expose float128 even when longdouble is 8 bytes + # (older alias kept for ABI stability). Treat that as high-precision. + if hasattr(_np, "float128"): + try: + if _np.dtype(_np.float128).itemsize > 8: + return _np.float128, True + except TypeError: + pass + return _np.float64, False + + +RiftFloat, RIFT_FLOAT_HIGH_PRECISION = _select_high_precision_dtype() +RIFT_FLOAT_NAME = _np.dtype(RiftFloat).name diff --git a/MonteCarloMarginalizeCode/Code/bin/create_event_parameter_pipeline_AlternateIteration b/MonteCarloMarginalizeCode/Code/bin/create_event_parameter_pipeline_AlternateIteration old mode 100644 new mode 100755 index 10b549cae..6cc17fc24 --- a/MonteCarloMarginalizeCode/Code/bin/create_event_parameter_pipeline_AlternateIteration +++ b/MonteCarloMarginalizeCode/Code/bin/create_event_parameter_pipeline_AlternateIteration @@ -186,18 +186,32 @@ parser.add_argument("--cip-args",default=None,help="filename of args_cip.txt fil parser.add_argument("--cip-args-list",default=None,help="filename of args_cip_list.file, which holds CIP arguments. Overrides cip-args if present. One CIP_n.sub file is created for each line in the file, which is used for an integer m iterations, where m is the first item of each line (normally 'X' in CIP)") parser.add_argument("--cip-explode-jobs",default=None,type=int,help="Number of CIP jobs to use to use in parallel to produce posterior samples for each iteration. Code will generate a fit first, save it, use this number of workers in parallel to generate samples, and then consolidates the samples together. Note that --n-output-samples and --n-eff are NOT adjusted ... if the user wants to adaptively fix the resolution, that needs to be controlled at a higher level") parser.add_argument("--cip-explode-jobs-flat",action='store_true',help="Pass to use the same arguments for all worker jobs. The main job will be /bin/true.") +parser.add_argument("--cip-explode-jobs-dag",action='store_true',help="Accepted for parity with the BasicIteration path (where each worker gets its own node rather than a 'queue N' statement). AlternateIteration uses 'queue N' workers regardless, so this is accepted but not acted on.") +parser.add_argument("--cip-explode-jobs-last",default=None,type=int,help="Like cip_explode_jobs, but ONLY for last batch. Only applies if using --cip-args-list.") parser.add_argument("--puff-exe",default=None,help="util_ParameterPuffball.py") parser.add_argument("--puff-args",default=None,help="util_ParameterPuffball arguments. If not specified, puffball will not be performed ") parser.add_argument("--puff-cadence",default=None,type=int,help="Every n iterations (not including 0), the puffball code will be applied. Puffball points will be done *in addition* to the usual results from the DAG. (The puffball is based on perturbing points from that iteration, and this will roughly double that iteration in ILE job size). Proposed value 2 (i.e., puff overlap-grid-2, ...-4, ...-6. If not specified, puffball will not be performed ") parser.add_argument("--puff-max-it",default=-1,type=int,help="Maximum iteration number that puffball is applied. If negative, puffball is not applied ") parser.add_argument("--last-iteration-extrinsic",action='store_true',help="Configure last iteration to extract *one* set of extrinsic parameters from each intrinsic point. [This is highly inefficient, but people like having one extrinsic point per intrinsic point.] Requires --convert-args") parser.add_argument("--last-iteration-extrinsic-nsamples",default=3000,type=int,help="Construct this number of extrinsic samples") +parser.add_argument("--last-iteration-extrinsic-samples-per-ile",default=5,type=int,help="Draw this many samples from each ILE job (controls the extrinsic-stage resample/downsample count)") +parser.add_argument("--last-iteration-extrinsic-samples-per-ile-internal",default=10,type=int,help="Draw this many samples from each ILE job (BasicIteration time-resampling path). Accepted for parity; AlternateIteration uses its convert/resample/cat extrinsic path, so this is accepted but not acted on.") +parser.add_argument("--last-iteration-extrinsic-time-resampling",action='store_true',help="BasicIteration last-iteration time-resampling code path. Accepted for parity; AlternateIteration uses its convert/resample/cat extrinsic path, so this is accepted but not acted on.") +parser.add_argument("--last-iteration-extrinsic-batched-convert",action='store_true',help="BasicIteration batched extrinsic converter. Accepted for parity; AlternateIteration uses its own per-job convert/resample/cat path, so this is accepted but not acted on.") +parser.add_argument("--last-iteration-export-marginal-distance-grid", action='store_true', help="Add argument to ILE_extr") +parser.add_argument("--last-iteration-export-distance-slices", default=0, type=int, help="If >0, the ILE_extr (extrinsic) stage exports K-row .dslice files: Plan-B fixed-distance extrinsic-marginalized likelihoods. Adds --export-distance-slices K (+ --internal-use-lnL) to ILE_extr and strips --distance-marginalization.") +parser.add_argument("--last-iteration-export-distance-slices-n-core", default=0, type=int, help="Passthrough to ILE --n-distance-slice-core for the extrinsic-stage .dslice export (0 = ILE default).") +parser.add_argument("--last-iteration-export-distance-slices-n-wing", default=0, type=int, help="Passthrough to ILE --n-distance-slice-wing for the extrinsic-stage .dslice export (0 = ILE default).") +parser.add_argument("--last-iteration-export-distance-slices-wing-delta-lnL", default=None, type=float, help="Passthrough to ILE --distance-slice-wing-delta-lnL for the extrinsic-stage .dslice export.") +parser.add_argument("--last-iteration-export-distance-slices-skip-threshold", default=None, type=float, help="Passthrough to ILE --distance-slice-skip-threshold for the extrinsic-stage .dslice export.") parser.add_argument("--ile-args",default=None,help="filename of args_ile.txt file which holds ILE arguments. Should NOT conflict with arguments auto-set by this DAG ... in particular, i/o arguments will be modified") parser.add_argument("--ile-exe",default=None,help="filename of ILE or equivalent executable. Will default to `which integrate_likelihood_extrinsic` in low-level code") parser.add_argument("--subdag-exe",default=None,help="filename of subdag writing command (e.g., create_event_dag_via_grid). Very restrictive arguments, should only use standard code unless you are an expert!") +parser.add_argument("--n-iterations-subdag-max",default=10,type=int,help="Number of iterations to perform in subdag, maximum. Accepted for parity with the BasicIteration run-to-convergence CIP subdag, which AlternateIteration does not implement, so this is accepted but not acted on.") parser.add_argument("--ile-retries",default=0,type=int,help="Number of retry attempts for ILE jobs. (These can fail)") parser.add_argument("--general-retries",default=0,type=int,help="Number of retry attempts for internal jobs (convert, CIP, ...). (These can fail, albeit more rarely, usually due to filesystem problems)") parser.add_argument("--general-request-disk",default="4M",type=str,help="Request disk passed to condor. Must be done for all jobs now") +parser.add_argument("--ile-request-disk",default="10M",type=str,help="Request disk passed to condor for ILE. Accepted for parity; AlternateIteration sizes ILE disk via its own request_disk logic (general-request-disk / OSG default), so this is accepted but not acted on.") parser.add_argument("--ile-n-events-to-analyze",default=1,type=int,help="If >1, you are using ILE_batchmode. Structures the DAG correctly to account for batch cadence") parser.add_argument("--ile-runtime-max-minutes",default=None,type=int,help="If not none, kills ILE jobs that take longer than the specified integer number of minutes. Do not use unless an expert") parser.add_argument("--cip-exe",default=None,help="filename of CIP or equivalent executable. Will default to `which util_ConstructIntrinsicPosterior_GenericCoordinates` in low-level code") @@ -288,10 +302,21 @@ if not (opts.cip_args is None): print("CIP", cip_args) cip_args_lines = None +cip_args_prefixes = [] # so it works correctly even in flat mode if not (opts.cip_args_list is None): with open(opts.cip_args_list) as f: cip_args_lines = f.readlines() - cip_args_n = [int(x.split(' ')[0]) for x in cip_args_lines] # Pull off the integer + # Pull off the leading token. It is usually an integer (number of iterations), but util_RIFT_pseudo_pipe.py + # also emits 'Z' (run-to-convergence marker) and 'G' (grid-stage marker) prefixes; tolerate those here. + cip_args_prefixes = [(x.split(' ')[0]) for x in cip_args_lines] + cip_args_n = (cip_args_prefixes.copy()) + for indx in np.arange(len(cip_args_n)): + if cip_args_prefixes[indx][0]=='Z': + cip_args_n[indx] = 1 # one nominal iteration; AlternateIteration has no run-to-convergence CIP stage + elif cip_args_prefixes[indx][0] =='G': + cip_args_n[indx] = int(cip_args_prefixes[indx][1:]) # integer after G assumed + else: + cip_args_n[indx] = int(cip_args_n[indx]) cip_args_lines = [' '.join(x.split(' ')[1:]) for x in cip_args_lines] # pull off the integer cip_args_lines = [x.replace('[', ' \'[').replace(']', ']\'').rstrip() for x in cip_args_lines] cip_args_lines = [x.lstrip() for x in cip_args_lines] # remove leading whitespace @@ -611,12 +636,28 @@ if not (fetch_args is None): if (opts.last_iteration_extrinsic): - n_points_per_ILE = 5 + n_points_per_ILE = opts.last_iteration_extrinsic_samples_per_ile # ILE job with modified output format # - note we *double* the memory request, because we need space to save samples ile_args_extr = ile_args + " --save-P 0.01 --save-samples --n-eff " +str(2*n_points_per_ILE) # modify convergence criteria so output of reasonable size # - note we *disable* --no-adapt-after-first (if present), so each point is independent (e.g., in sky location) ile_args_extr = ile_args_extr.replace('--no-adapt-after-first','') + if opts.last_iteration_export_marginal_distance_grid: + ile_args_extr += " --export-marginal-distance-grid " + ile_args_extr = ile_args_extr.replace("--distance-marginalization ", ' ') + if opts.last_iteration_export_distance_slices and opts.last_iteration_export_distance_slices > 0: + ile_args_extr += " --export-distance-slices {} ".format(opts.last_iteration_export_distance_slices) + if opts.last_iteration_export_distance_slices_n_core: + ile_args_extr += " --n-distance-slice-core {} ".format(opts.last_iteration_export_distance_slices_n_core) + if opts.last_iteration_export_distance_slices_n_wing: + ile_args_extr += " --n-distance-slice-wing {} ".format(opts.last_iteration_export_distance_slices_n_wing) + if opts.last_iteration_export_distance_slices_wing_delta_lnL is not None: + ile_args_extr += " --distance-slice-wing-delta-lnL {} ".format(opts.last_iteration_export_distance_slices_wing_delta_lnL) + if opts.last_iteration_export_distance_slices_skip_threshold is not None: + ile_args_extr += " --distance-slice-skip-threshold {} ".format(opts.last_iteration_export_distance_slices_skip_threshold) + if "--internal-use-lnL" not in ile_args_extr: + ile_args_extr += " --internal-use-lnL " + ile_args_extr = ile_args_extr.replace("--distance-marginalization ", ' ') ileExtr_job, ileExtr_job_name = dag_utils.write_ILE_sub_simple(tag='ILE_extr',log_dir=None,arg_str=ile_args_extr,output_file="EXTR_out.xml",simple_unique=True,ncopies=1,exe=ile_exe,transfer_files=transfer_file_names,request_memory=opts.request_memory_ILE*2,request_gpu=opts.request_gpu_ILE,use_cvmfs_frames=opts.use_cvmfs_frames,request_disk=request_disk) ileExtr_job.add_condor_cmd("initialdir",opts.working_directory+"/iteration_$(macroiteration)_ile") ileExtr_job.set_log_file(opts.working_directory+"/iteration_$(macroiteration)_ile/logs/ILEextr-$(macroevent)-$(cluster)-$(process).log") @@ -627,29 +668,94 @@ if (opts.last_iteration_extrinsic): ileExtr_job.set_sub_file(fname) ileExtr_job.write_sub_file() - # Convert task - convert_args_extr = " --convention LI --export-cosmology --use-interpolated-cosmology " - if not (convert_args is None): - convert_args_extr += convert_args - convertExtr_job, convertExtr_job_name = dag_utils.write_convert_sub(tag='convert_extr',log_dir=None,arg_str=convert_args_extr,file_input=opts.working_directory+"/iteration_$(macroiteration)_ile/EXTR_out-$(macroevent).xml_$(macroindx)_.xml.gz",file_output=opts.working_directory+"/iteration_$(macroiteration)_ile/EXTR_out-$(macroevent).xml_$(macroindx)_.dat", out_dir=opts.working_directory+"/iteration_$(macroiteration)_ile/",universe=local_worker_universe,no_grid=no_worker_grid) - convertExtr_job.add_condor_cmd("initialdir",opts.working_directory) - convertExtr_job.set_log_file(opts.working_directory+"/iteration_$(macroiteration)_ile/logs/convert-$(macroevent)-$(macroindx).log") - convertExtr_job.set_stderr_file(opts.working_directory+"/iteration_$(macroiteration)_ile/logs/convert-$(macroevent)-$(macroindx).err") - if opts.use_full_submit_paths: - fname = opts.working_directory+"/"+convertExtr_job.get_sub_file() - convertExtr_job.set_sub_file(fname) - convertExtr_job.write_sub_file() - - # Resample task - resample_args = ' --n-output-samples ' + str(n_points_per_ILE) # pick 5 random points from each ILE run - resample_job, resample_job_name = dag_utils.write_resample_sub('resample',log_dir=None,arg_str=resample_args,file_input=opts.working_directory+"/iteration_$(macroiteration)_ile/EXTR_out-$(macroevent).xml_$(macroindx)_.dat",file_output=opts.working_directory+"/iteration_$(macroiteration)_ile/EXTR_out-$(macroevent).xml_$(macroindx)_.downsampled_dat",universe=local_worker_universe,no_grid=no_worker_grid) - resample_job.add_condor_cmd("initialdir",opts.working_directory) - resample_job.set_log_file(opts.working_directory+"/iteration_$(macroiteration)_ile/logs/resample-$(macroevent)-$(macroindx).log") - resample_job.set_stderr_file(opts.working_directory+"/iteration_$(macroiteration)_ile/logs/resample-$(macroevent)-$(macroindx).err") - if opts.use_full_submit_paths: - fname = opts.working_directory+"/"+resample_job.get_sub_file() - resample_job.set_sub_file(fname) - resample_job.write_sub_file() + # Convert task. Three modes, ported from BasicIteration (the maintained path): + # (1) time-resampling: a single batched convert job per iteration (allinone_convert.sh) that writes + # extrinsic_posterior_samples.dat directly, so no separate resample/cat is needed + # (2) batched convert: one convert job per ILE output group (batch_convert.sh) + # (3) old style: per-(event,indx) convert + resample, combined later by the cat job + extra_text = '' # AlternateIteration has no --condor-local-nonworker-igwn-prefix option, so no environment prefix is prepended + convertExtr_job = None + resample_job = None + batchConvertExtr_job = None + if opts.last_iteration_extrinsic_time_resampling: + # igwn_ligolw add on all final output, then a single convert + convert_args_extr = " --convention LI --export-cosmology --use-interpolated-cosmology " + if not (convert_args is None): + convert_args_extr += convert_args + relevant_path = dag_utils.which('util_JoinExtrXML.py') + relevant_path_2 = dag_utils.which('convert_output_format_ile2inference') + # randomization: 'shuf' is preferred, but otherwise use ' sort -R'. Note performed locally, so local filesystem/os is fine. + extra_shuffle_command = ' | cat' + which_shuf = which('shuf'); which_sort = which('sort') + if isinstance(which_shuf, str): + extra_shuffle_command = ' | {} '.format(which_shuf) + elif isinstance(which_sort, str): + extra_shuffle_command = ' | {} -R '.format(which_sort) + with open("allinone_convert.sh",'w') as f: + f.write(f"""#! /bin/bash +{extra_text} +{relevant_path} ./iteration_$1_ile/'EXTR_out-*.xml_*_.xml.gz' --output ./tmp_converted.xml.gz +{relevant_path_2} {convert_args_extr} ./tmp_converted.xml.gz > ./tmp_converted.dat +head -n 1 ./tmp_converted.dat > ./extrinsic_posterior_samples.dat +sed 1d ./tmp_converted.dat {extra_shuffle_command} >> ./extrinsic_posterior_samples.dat +""") + os.system("chmod a+x allinone_convert.sh") + batchConvertExtr_job, batchConvertExtr_job_name = dag_utils.write_convert_sub(exe=opts.working_directory+"/allinone_convert.sh",tag='convert_extr',log_dir=None,arg_str='',file_input="$(macroiteration) ",file_output="/dev/null", out_dir=opts.working_directory,universe=local_worker_universe,no_grid=no_worker_grid) + batchConvertExtr_job.add_condor_cmd("initialdir",opts.working_directory) + batchConvertExtr_job.set_log_file(opts.working_directory+"/iteration_$(macroiteration)_ile/logs/batchconvert-$(macroevent).log") + batchConvertExtr_job.set_stderr_file(opts.working_directory+"/iteration_$(macroiteration)_ile/logs/batchconvert-$(macroevent).err") + batchConvertExtr_job.add_condor_cmd('request_disk',opts.general_request_disk) + if opts.use_full_submit_paths: + fname = opts.working_directory+"/"+batchConvertExtr_job.get_sub_file() + batchConvertExtr_job.set_sub_file(fname) + batchConvertExtr_job.write_sub_file() + elif opts.last_iteration_extrinsic_batched_convert: + # Batched conversion: one job for a great many items + convert_args_extr = " --convention LI --export-cosmology --use-interpolated-cosmology " + if opts.last_iteration_extrinsic_samples_per_ile: + convert_args_extr += " --n-output-samples-per-file {}".format(opts.last_iteration_extrinsic_samples_per_ile) + if not (convert_args is None): + convert_args_extr += convert_args + relevant_path = dag_utils.which('util_BatchConvertResampleILEOutput.py') + with open("batch_convert.sh",'w') as f: + f.write("""#! /bin/bash +{} +{} {}/iteration_$1_ile/EXTR_out-$2.xml_*_.xml.gz {} +""".format(extra_text,relevant_path,opts.working_directory,convert_args_extr)) + os.system("chmod a+x batch_convert.sh") + batchConvertExtr_job, batchConvertExtr_job_name = dag_utils.write_convert_sub(exe=opts.working_directory+"/batch_convert.sh",tag='convert_extr',log_dir=None,arg_str='',file_input="$(macroiteration) $(macroevent)",file_output=opts.working_directory+"/iteration_$(macroiteration)_ile/EXTR_out-$(macroevent).downsampled_dat.dat", out_dir=opts.working_directory+"/iteration_$(macroiteration)_ile/",universe=local_worker_universe,no_grid=no_worker_grid) + batchConvertExtr_job.add_condor_cmd("initialdir",opts.working_directory) + batchConvertExtr_job.set_log_file(opts.working_directory+"/iteration_$(macroiteration)_ile/logs/batchconvert-$(macroevent).log") + batchConvertExtr_job.set_stderr_file(opts.working_directory+"/iteration_$(macroiteration)_ile/logs/batchconvert-$(macroevent).err") + batchConvertExtr_job.add_condor_cmd('request_disk',opts.general_request_disk) + if opts.use_full_submit_paths: + fname = opts.working_directory+"/"+batchConvertExtr_job.get_sub_file() + batchConvertExtr_job.set_sub_file(fname) + batchConvertExtr_job.write_sub_file() + else: + # Old style convert + resample, per (event, indx) + convert_args_extr = " --convention LI --export-cosmology --use-interpolated-cosmology " + if not (convert_args is None): + convert_args_extr += convert_args + convertExtr_job, convertExtr_job_name = dag_utils.write_convert_sub(tag='convert_extr',log_dir=None,arg_str=convert_args_extr,file_input=opts.working_directory+"/iteration_$(macroiteration)_ile/EXTR_out-$(macroevent).xml_$(macroindx)_.xml.gz",file_output=opts.working_directory+"/iteration_$(macroiteration)_ile/EXTR_out-$(macroevent).xml_$(macroindx)_.dat", out_dir=opts.working_directory+"/iteration_$(macroiteration)_ile/",universe=local_worker_universe,no_grid=no_worker_grid) + convertExtr_job.add_condor_cmd("initialdir",opts.working_directory) + convertExtr_job.set_log_file(opts.working_directory+"/iteration_$(macroiteration)_ile/logs/convert-$(macroevent)-$(macroindx).log") + convertExtr_job.set_stderr_file(opts.working_directory+"/iteration_$(macroiteration)_ile/logs/convert-$(macroevent)-$(macroindx).err") + if opts.use_full_submit_paths: + fname = opts.working_directory+"/"+convertExtr_job.get_sub_file() + convertExtr_job.set_sub_file(fname) + convertExtr_job.write_sub_file() + + # Resample task + resample_args = ' --n-output-samples ' + str(n_points_per_ILE) # pick n_points_per_ILE random points from each ILE run + resample_job, resample_job_name = dag_utils.write_resample_sub('resample',log_dir=None,arg_str=resample_args,file_input=opts.working_directory+"/iteration_$(macroiteration)_ile/EXTR_out-$(macroevent).xml_$(macroindx)_.dat",file_output=opts.working_directory+"/iteration_$(macroiteration)_ile/EXTR_out-$(macroevent).xml_$(macroindx)_.downsampled_dat",universe=local_worker_universe,no_grid=no_worker_grid) + resample_job.add_condor_cmd("initialdir",opts.working_directory) + resample_job.set_log_file(opts.working_directory+"/iteration_$(macroiteration)_ile/logs/resample-$(macroevent)-$(macroindx).log") + resample_job.set_stderr_file(opts.working_directory+"/iteration_$(macroiteration)_ile/logs/resample-$(macroevent)-$(macroindx).err") + if opts.use_full_submit_paths: + fname = opts.working_directory+"/"+resample_job.get_sub_file() + resample_job.set_sub_file(fname) + resample_job.write_sub_file() # Combination task at end -- probably should be a general utility cat_job, cat_job_name = dag_utils.write_cat_sub(file_prefix='EXTR', file_postfix='.downsampled_dat.dat',file_output='extrinsic_posterior_samples.dat',universe=local_worker_universe,no_grid=no_worker_grid) @@ -1043,6 +1149,7 @@ if opts.use_bw_psd: n_group = opts.ile_n_events_to_analyze +unify_node_list = [] # populated per iteration; used below to attach SCRIPT POST checks confirming nonempty composites for it in np.arange(it_start,opts.n_iterations): consolidate_now = None fit_node_now = None @@ -1056,7 +1163,9 @@ for it in np.arange(it_start,opts.n_iterations): unify_node.add_macro("macroiteration",it) unify_node.add_parent(con_node) unify_node.set_retry(opts.general_retries) - + if not(it ==0): # don't require the first composite to be nonempty + unify_node_list.append(unify_node) + # Create subdags n_jobs_this_time = opts.n_samples_per_job if it ==it_start: @@ -1225,21 +1334,33 @@ if opts.last_iteration_extrinsic: # - *not* always same as number of ILE events being analyzed # - *assumes* grid files have sufficiently large numbers of samples to allow this! (as in many other cases) n_jobs_extrinsic = int(opts.last_iteration_extrinsic_nsamples/(1.0*n_group)) + + if opts.last_iteration_extrinsic_time_resampling: + # a single iteration-level convert job (allinone_convert.sh) consumes all ILE output and writes the posterior directly + convert_node = pipeline.CondorDAGNode(batchConvertExtr_job) + convert_node.set_retry(opts.ile_retries) # this can fail too + convert_node.add_macro("macroiteration",it) # needed so we find the correct data to read + for event in np.arange(n_jobs_extrinsic): # Add task per ILE operation ile_node = pipeline.CondorDAGNode(ileExtr_job) # ile_node.set_priority(JOB_PRIORITIES["ILE"]) ile_node.set_retry(opts.ile_retries) ile_node.add_macro("macroevent", event*n_group) + ile_node.add_macro("macrongroup", n_group) ile_node.add_macro("macroiteration", it) if not(parent_fit_node is None): ile_node.add_parent(parent_fit_node) dag.add_node(ile_node) # Add convert and resample task *for each output file* - if opts.last_iteration_extrinsic_batched_convert: + if opts.last_iteration_extrinsic_time_resampling: + # establish parent-child relationship with the single iteration-level convert job + convert_node.add_parent(ile_node) + elif opts.last_iteration_extrinsic_batched_convert: convert_node = pipeline.CondorDAGNode(batchConvertExtr_job) convert_node.add_macro("macroevent", event*n_group) + convert_node.add_macro("macrongroup", n_group) convert_node.add_macro("macroiteration", it) convert_node.set_retry(opts.ile_retries) # this can fail too convert_node.add_parent(ile_node) @@ -1268,8 +1389,11 @@ if opts.last_iteration_extrinsic: # Add nodes dag.add_node(convert_node) dag.add_node(resample_node) - - dag.add_node(cat_node) + + if not(opts.last_iteration_extrinsic_time_resampling): + dag.add_node(cat_node) + else: + dag.add_node(convert_node) # this is the time-resampling task that writes the posterior directly # Create final node for overall plots. (Note: default setup is designed to enable plots of the last two iterations *at each step* but this seems like overkill) if plot_args: diff --git a/MonteCarloMarginalizeCode/Code/bin/create_event_parameter_pipeline_BasicIteration b/MonteCarloMarginalizeCode/Code/bin/create_event_parameter_pipeline_BasicIteration old mode 100644 new mode 100755 index af774bd33..fdb3365b4 --- a/MonteCarloMarginalizeCode/Code/bin/create_event_parameter_pipeline_BasicIteration +++ b/MonteCarloMarginalizeCode/Code/bin/create_event_parameter_pipeline_BasicIteration @@ -75,15 +75,21 @@ from RIFT.misc.dag_utils_generic import mkdir from RIFT.misc.dag_utils_generic import which -def add_batch_ILE_nodes_to_dag(my_dag,my_ile_job,my_parent_node, my_child_node, n_max, n_group, it, n_retries=3,it_start=0,convert_psd_node_list=[],node_list_dict={}): +def add_batch_ILE_nodes_to_dag(my_dag,my_ile_job,my_parent_node, my_child_node, n_max, n_group, it, n_retries=3,it_start=0,convert_psd_node_list=[],node_list_dict={},extra_parent_nodes=None): + if extra_parent_nodes is None: + extra_parent_nodes = [] for event in np.arange(n_max): ile_node = pipeline.CondorDAGNode(my_ile_job) ile_node.set_retry(n_retries) ile_node.add_macro("macroevent", event*n_group) ile_node.add_macro("macrongroup", n_group) ile_node.add_macro("macroiteration", it) + ile_node.add_macro("macroiterationprev", it-1) # Option C cal-pilot seed path if not(my_parent_node is None): ile_node.add_parent(my_parent_node) + for node in extra_parent_nodes: + if not(node is None): + ile_node.add_parent(node) if it == it_start: for node in convert_psd_node_list: # for every PSD conversion job, make sure PSD is present before we run the first iteration! ile_node.add_parent(node) @@ -239,6 +245,13 @@ parser.add_argument("--puff-exe",default=None,help="util_ParameterPuffball.py") parser.add_argument("--puff-args",default=None,help="util_ParameterPuffball arguments. If not specified, puffball will not be performed ") parser.add_argument("--puff-cadence",default=None,type=int,help="Every n iterations (not including 0), the puffball code will be applied. Puffball points will be done *in addition* to the usual results from the DAG. (The puffball is based on perturbing points from that iteration, and this will roughly double that iteration in ILE job size). Proposed value 2 (i.e., puff overlap-grid-2, ...-4, ...-6. If not specified, puffball will not be performed ") parser.add_argument("--puff-max-it",default=-1,type=int,help="Maximum iteration number that puffball is applied. If negative, puffball is not applied ") +parser.add_argument("--calmarg-pilot",action='store_true',help="Option C adaptive calibration: add per-iteration cal PILOT jobs (harvest top-lnL points from iteration N's composite, run ILE --calibration-dump-responsibilities, fit+consolidate a cal proposal that SEEDS wide_{N+1} via --calibration-proposal-breadcrumb). Requires the wide ILE args to already carry the calibration envelope + (per-iteration) proposal-breadcrumb path.") +parser.add_argument("--calmarg-pilot-cadence",default=1,type=int,help="Run a cal pilot every n iterations. Default 1 (every iteration until the cap).") +parser.add_argument("--calmarg-pilot-max-it",default=3,type=int,help="Stop launching cal pilots after this iteration (cal is boring -> freeze the proposal once learned). Default 3.") +parser.add_argument("--calmarg-pilot-top-fraction",default=0.05,type=float,help="Fraction of highest-lnL composite points the pilot harvests for full cal.") +parser.add_argument("--calmarg-pilot-max-points",default=32,type=int,help="Cap on harvested pilot points per iteration.") +parser.add_argument("--extrinsic-handoff",action='store_true',help="Extrinsic handoff (GMM sampler): each iteration's wide ILE jobs write a per-event extrinsic proposal (--extrinsic-proposal-output), a per-iteration LOCAL-universe consolidation job (util_ExtrinsicConsolidate.py) picks the most representative one -> extr_consolidated_.npz, which SEEDS wide_{N+1} via --extrinsic-proposal-breadcrumb. Requires the wide ILE args to already carry those flags (added by util_RIFT_pseudo_pipe.py --extrinsic-handoff).") +parser.add_argument("--extrinsic-handoff-select",default="lnL",help="Metric the extrinsic consolidation ranks per-event proposals by (lnL|neff|n_samples). Default lnL (most peak-representative).") parser.add_argument("--first-iteration-jumpstart",action='store_true',help="No ILE jobs the first iteration. Assumes you already have .composite files and want to get going. Particularly helpful for subdag systems") parser.add_argument("--last-iteration-extrinsic",action='store_true',help="Configure last iteration to extract *one* set of extrinsic parameters from each intrinsic point. [This is highly inefficient, but people like having one extrinsic point per intrinsic point.] Requires --convert-args") parser.add_argument("--last-iteration-extrinsic-nsamples",default=3000,type=int,help="Construct this number of extrinsic samples") @@ -246,6 +259,12 @@ parser.add_argument("--last-iteration-extrinsic-samples-per-ile",default=5,type= parser.add_argument("--last-iteration-extrinsic-samples-per-ile-internal",default=10,type=int,help="Draw this many samples from each ILE job") parser.add_argument("--last-iteration-extrinsic-batched-convert",action='store_true',help="Used batched converter for output of extrinsic samples") parser.add_argument("--last-iteration-extrinsic-time-resampling",action='store_true',help="Last iterations use time resampling (+ the fairdraw is done inside ILE itself), so different code path for final stages") +parser.add_argument("--last-iteration-export-marginal-distance-grid", action='store_true', help="Add argument to ILE_extr") +parser.add_argument("--last-iteration-export-distance-slices", default=0, type=int, help="If >0, the ILE_extr (extrinsic) stage exports K-row .dslice files: Plan-B fixed-distance extrinsic-marginalized likelihoods. Adds --export-distance-slices K (+ --internal-use-lnL) to ILE_extr and strips --distance-marginalization.") +parser.add_argument("--last-iteration-export-distance-slices-n-core", default=0, type=int, help="Passthrough to ILE --n-distance-slice-core for the extrinsic-stage .dslice export (0 = ILE default).") +parser.add_argument("--last-iteration-export-distance-slices-n-wing", default=0, type=int, help="Passthrough to ILE --n-distance-slice-wing for the extrinsic-stage .dslice export (0 = ILE default).") +parser.add_argument("--last-iteration-export-distance-slices-wing-delta-lnL", default=None, type=float, help="Passthrough to ILE --distance-slice-wing-delta-lnL for the extrinsic-stage .dslice export.") +parser.add_argument("--last-iteration-export-distance-slices-skip-threshold", default=None, type=float, help="Passthrough to ILE --distance-slice-skip-threshold for the extrinsic-stage .dslice export.") parser.add_argument("--ile-args",default=None,help="filename of args_ile.txt file which holds ILE arguments. Should NOT conflict with arguments auto-set by this DAG ... in particular, i/o arguments will be modified") parser.add_argument("--ile-exe",default=None,help="filename of ILE or equivalent executable. Will default to `which integrate_likelihood_extrinsic` in low-level code") parser.add_argument("--ile-retries",default=0,type=int,help="Number of retry attempts for ILE jobs. (These can fail)") @@ -545,6 +564,10 @@ if not (opts.transfer_file_list is None): for line in f.readlines(): transfer_file_names.append(line.rstrip()) print(" Input files to transfer to job working directory (note!)", transfer_file_names) +# Clean snapshot of the transfer list (PSD + cal envelopes only) BEFORE the grid is +# appended and BEFORE write_ILE_sub_simple mutates it in place (frames_dir, ile_pre.sh). +# The CALPILOT job (built later) needs this clean set, not the wide-ILE-polluted one. +transfer_file_names_pilot_base = list(transfer_file_names) ### ### Fiducial fit job (=sanity check that code will run) @@ -772,6 +795,25 @@ if (opts.last_iteration_extrinsic): ile_args_extr = ile_args + " --save-P 0.01 --save-samples --n-eff " +str(n_eff_last) # modify convergence criteria so output of reasonable size/matching needs of extrinsic output # - note we *disable* --no-adapt-after-first (if present), so each point is independent (e.g., in sky location) ile_args_extr = ile_args_extr.replace('--no-adapt-after-first','') + if opts.last_iteration_export_marginal_distance_grid: + ile_args_extr += " --export-marginal-distance-grid " + ile_args_extr = ile_args_extr.replace("--distance-marginalization ", ' ') # *currently* cannot use distance marginalization in the last step if we want distance grid output + if opts.last_iteration_export_distance_slices and opts.last_iteration_export_distance_slices > 0: + # Plan-B distance slices: K independent fixed-d extrinsic integrals per + # intrinsic point, emitted as a .dslice file. Requires lnL mode and no + # distance marginalization (same constraints as the grid export above). + ile_args_extr += " --export-distance-slices {} ".format(opts.last_iteration_export_distance_slices) + if opts.last_iteration_export_distance_slices_n_core: + ile_args_extr += " --n-distance-slice-core {} ".format(opts.last_iteration_export_distance_slices_n_core) + if opts.last_iteration_export_distance_slices_n_wing: + ile_args_extr += " --n-distance-slice-wing {} ".format(opts.last_iteration_export_distance_slices_n_wing) + if opts.last_iteration_export_distance_slices_wing_delta_lnL is not None: + ile_args_extr += " --distance-slice-wing-delta-lnL {} ".format(opts.last_iteration_export_distance_slices_wing_delta_lnL) + if opts.last_iteration_export_distance_slices_skip_threshold is not None: + ile_args_extr += " --distance-slice-skip-threshold {} ".format(opts.last_iteration_export_distance_slices_skip_threshold) + if "--internal-use-lnL" not in ile_args_extr: + ile_args_extr += " --internal-use-lnL " + ile_args_extr = ile_args_extr.replace("--distance-marginalization ", ' ') if opts.last_iteration_extrinsic_time_resampling: ile_args_extr += " --resample-time-marginalization --fairdraw-extrinsic-output --fairdraw-extrinsic-output-n-max {} ".format(n_points_per_ILE) print(" Time resampling in extraction iteration: **DISABLING** distance marginalization if present ") @@ -892,6 +934,43 @@ sed 1d ./tmp_converted.dat {extra_shuffle_command} >> ./extrinsic_posterior_sam cat_job.set_sub_file(fname) cat_job.write_sub_file() + # Per-distance likelihood export: consolidate per-event .dgrid (Plan A) + # and/or .dslice (Plan B) files into a single table at the run root. The + # consolidated file is the "net" intrinsic+distance grid downstream tools + # like util_ConstructEOSPosterior.py consume. Each consolidation is + # gated on the corresponding --last-iteration-export-* flag so we never + # emit an empty sub for runs that did not request the export. + if opts.last_iteration_export_marginal_distance_grid: + dgrid_job, dgrid_job_name = dag_utils.write_consolidate_distance_grids_sub( + tag='consolidate_dgrid', + input_glob='EXTR_out.xml_*_.dgrid', + file_output=opts.working_directory + '/all_dgrid.dat', + search_dir=opts.working_directory + '/iteration_$(macroiteration)_ile', + log_dir=opts.working_directory + '/iteration_$(macroiteration)_ile/logs/', + universe=local_worker_universe, no_grid=no_worker_grid, + ) + dgrid_job.add_condor_cmd("initialdir", opts.working_directory) + dgrid_job.add_condor_cmd('request_disk', opts.general_request_disk) + if opts.use_full_submit_paths: + fname = opts.working_directory + "/" + dgrid_job.get_sub_file() + dgrid_job.set_sub_file(fname) + dgrid_job.write_sub_file() + if opts.last_iteration_export_distance_slices and opts.last_iteration_export_distance_slices > 0: + dslice_job, dslice_job_name = dag_utils.write_consolidate_distance_grids_sub( + tag='consolidate_dslice', + input_glob='EXTR_out.xml_*_.dslice', + file_output=opts.working_directory + '/all_dslice.dat', + search_dir=opts.working_directory + '/iteration_$(macroiteration)_ile', + log_dir=opts.working_directory + '/iteration_$(macroiteration)_ile/logs/', + universe=local_worker_universe, no_grid=no_worker_grid, + ) + dslice_job.add_condor_cmd("initialdir", opts.working_directory) + dslice_job.add_condor_cmd('request_disk', opts.general_request_disk) + if opts.use_full_submit_paths: + fname = opts.working_directory + "/" + dslice_job.get_sub_file() + dslice_job.set_sub_file(fname) + dslice_job.write_sub_file() + ## Consolidate job(s) # - consolidate output of single previous job @@ -1049,6 +1128,48 @@ if puff_args and puff_cadence: puff_job.write_sub_file() +## calibration PILOT job (Option C adaptive cal; see RIFT/calmarg/DESIGN_adaptive_driver.md) +calpilot_job = None +calpilot_node_per_iteration = {} +if opts.calmarg_pilot: + calpilot_log_dir = opts.working_directory + "/iteration_$(macroiteration)_cip/logs/" + # On OSG the CALPILOT job runs ILE internally, so it needs the same container + input + # transfer set as the wide ILE jobs (PSD + cal envelopes + frames), plus the composite. + # transfer_file_names[:-1] = the helper transfer list WITHOUT the wide grid (the pilot + # harvests its own grid from the transferred composite). + calpilot_job, calpilot_job_name = dag_utils.write_calpilot_sub( + tag='CALPILOT', log_dir=calpilot_log_dir, working_directory=opts.working_directory, + ile_args_file=opts.working_directory + "/args_ile.txt", + top_fraction=opts.calmarg_pilot_top_fraction, max_points=opts.calmarg_pilot_max_points, + request_memory=opts.request_memory_ILE, request_gpu=opts.request_gpu_ILE, + universe="vanilla", + singularity_image=(singularity_image if (opts.use_singularity or opts.use_osg or opts.condor_containerize_nonworker) else None), + use_osg=opts.use_osg, use_singularity=opts.use_singularity, frames_dir=opts.frames_dir, + use_oauth_files=opts.use_oauth_files, + transfer_files=(list(transfer_file_names_pilot_base) if opts.use_osg else None)) + calpilot_job.add_condor_cmd("initialdir", opts.working_directory) + calpilot_job.add_condor_cmd('request_disk',opts.general_request_disk) + if opts.use_full_submit_paths: + calpilot_job.set_sub_file(opts.working_directory + "/" + calpilot_job.get_sub_file()) + calpilot_job.write_sub_file() + + +## EXTRINSIC handoff consolidation job (see RIFT/calmarg/DESIGN_extrinsic_handoff.md). +## Pure-python LOCAL-universe job: picks the most representative per-event extrinsic +## proposal of iteration $(macroiteration) -> extr_consolidated_.npz, seeding wide_{N+1}. +extrconsolidate_job = None +extrconsolidate_node_per_iteration = {} +if opts.extrinsic_handoff: + extrconsolidate_log_dir = opts.working_directory + "/iteration_$(macroiteration)_ile/logs/" + extrconsolidate_job, extrconsolidate_job_name = dag_utils.write_extrconsolidate_sub( + tag='EXTRCONSOLIDATE', log_dir=extrconsolidate_log_dir, + working_directory=opts.working_directory, select=opts.extrinsic_handoff_select) + extrconsolidate_job.add_condor_cmd("initialdir", opts.working_directory) + if opts.use_full_submit_paths: + extrconsolidate_job.set_sub_file(opts.working_directory + "/" + extrconsolidate_job.get_sub_file()) + extrconsolidate_job.write_sub_file() + + ## fetch job. Fetch is before CURRENT iteration, so it is just-in-time and can be used at iteration 0 if fetch_args: fetch_job, fetch_job_name = dag_utils.write_puff_sub(tag='FETCH',log_dir=None,arg_str=fetch_args,request_memory=opts.request_memory_ILE,input_net=None,output=opts.working_directory+'/fetch-$(macroiteration).xml.gz',out_dir=opts.working_directory,exe=opts.fetch_ext_grid_exe,universe=local_worker_universe,no_grid=no_worker_grid,extra_text=extra_text) @@ -1403,6 +1524,7 @@ if opts.comov_distance_reweighting: # parent_fit_node = None +last_puff_node = None last_node=None if opts.gridinit_args: @@ -1519,6 +1641,22 @@ for it in np.arange(it_start,opts.n_iterations): dag.add_node(main_analysis_node) else: add_batch_ILE_nodes_to_dag(dag, ile_job, parent_fit_node, con_node, indx_max, n_group_here, it, n_retries=opts.ile_retries,it_start=it_start,convert_psd_node_list=convert_psd_node_list,node_list_dict=ile_node_list_per_iteration) + # Option C: the wide ILE jobs of iteration `it` are SEEDED from the cal proposal + # learned by the PREVIOUS iteration's pilot -> make them depend on calpilot_{it-1} + # so cal_consolidated_{it-1}.npz exists (the seed barrier). The ILE args carry + # --calibration-proposal-breadcrumb .../cal_consolidated_$(macroiterationprev).npz; + # a missing file (early iterations) falls back to the prior inside ILE. + if calpilot_job and ((it-1) in calpilot_node_per_iteration): + for _ile_node in ile_node_list_per_iteration[it]: + _ile_node.add_parent(calpilot_node_per_iteration[it-1]) + # Extrinsic handoff: the wide ILE jobs of iteration `it` are SEEDED from the + # extrinsic proposal consolidated from the PREVIOUS iteration -> depend on + # extrconsolidate_{it-1} so extr_consolidated_{it-1}.npz exists (the seed barrier). + # The ILE args carry --extrinsic-proposal-breadcrumb .../extr_consolidated_$(macroiterationprev).npz; + # a missing/empty file (early iterations) falls back to the cold default inside ILE. + if extrconsolidate_job and ((it-1) in extrconsolidate_node_per_iteration): + for _ile_node in ile_node_list_per_iteration[it]: + _ile_node.add_parent(extrconsolidate_node_per_iteration[it-1]) # for event in np.arange(indx_max): #np.arange(n_jobs_this_time): # # Add task per ILE operation # ile_node = pipeline.CondorDAGNode(ile_job) @@ -1539,7 +1677,19 @@ for it in np.arange(it_start,opts.n_iterations): if puff_args and puff_cadence: if it>it_start and it <= puff_max_it and (it-1)%puff_cadence ==0: # we made a puffball last iteration, so run it through ILE now print(" ILE jobs for puffball on iteration ", it) - add_batch_ILE_nodes_to_dag(dag, ilePuff_job, parent_fit_node, con_node, indx_max, n_group_here, it, n_retries=opts.ile_retries,node_list_dict=ile_node_list_per_iteration) + # The puffball ILE jobs run with the SAME args_ile as the normal wide ILE, so they read + # the same iteration-(it-1) seed breadcrumb (--calibration/--extrinsic-proposal-breadcrumb). + # The normal-ILE seed barriers (above) were applied via ile_node_list_per_iteration BEFORE + # these jobs existed, so thread the same barriers here via extra_parent_nodes: the puffball + # ILE must also wait for last_puff_node (the puff side path) AND, when pilots/handoff are + # active, for the it-1 consolidation that PRODUCES the seed file (else the puff jobs race + # it and silently fall back to the prior). + _puff_extra = [last_puff_node] + if calpilot_job and ((it-1) in calpilot_node_per_iteration): + _puff_extra.append(calpilot_node_per_iteration[it-1]) + if extrconsolidate_job and ((it-1) in extrconsolidate_node_per_iteration): + _puff_extra.append(extrconsolidate_node_per_iteration[it-1]) + add_batch_ILE_nodes_to_dag(dag, ilePuff_job, parent_fit_node, con_node, indx_max, n_group_here, it, n_retries=opts.ile_retries,node_list_dict=ile_node_list_per_iteration,extra_parent_nodes=_puff_extra) # for event in np.arange(indx_max): # ile_node = pipeline.CondorDAGNode(ilePuff_job) # only difference is here: uses puffball, which by construction is the same size/ perturbed points # ile_node.set_retry(opts.ile_retries) @@ -1809,11 +1959,43 @@ for it in np.arange(it_start,opts.n_iterations): puff_node.set_retry(opts.general_retries) if not (parent_fit_node is None): puff_node.add_parent(parent_fit_node) # only fit if we have results from the previous iteration - dag.add_node(puff_node) - - parent_fit_node = puff_node + dag.add_node(puff_node) + # PUFF FIX (c4c1455e): the puffball node is a barrier for the NEXT iteration's PUFFBALL + # ILE jobs ONLY -- it must NOT be folded into parent_fit_node (the normal fit chain), or + # the next iteration's NORMAL ILE jobs would wait on the puff side path and the pipeline + # halts at the PUFF stage. Keep parent_fit_node = the fit chain; carry the puff barrier + # separately in last_puff_node, applied to ilePuff jobs via extra_parent_nodes. + last_puff_node = puff_node + + # Calibration PILOT for this iteration (Option C). Runs IN PARALLEL with CIP/puff -- + # it does NOT gate them (parent_fit_node is left untouched) -- harvesting iteration + # it's composite (via unify_node, which guarantees a non-empty composite) and emitting + # cal_consolidated_.npz, which SEEDS the wide ILE jobs of iteration it+1. + if calpilot_job and (it <= opts.calmarg_pilot_max_it) and (it % opts.calmarg_pilot_cadence == 0): + print(" Calibration pilot for iteration ", it) + calpilot_node = pipeline.CondorDAGNode(calpilot_job) + calpilot_node.add_macro("macroiteration", it) + calpilot_node.add_macro("macroiterationprev", it-1) + calpilot_node.set_category("CALPILOT") + calpilot_node.set_retry(opts.general_retries) + calpilot_node.add_parent(unify_node) # need iteration it's (non-empty) composite + dag.add_node(calpilot_node) + calpilot_node_per_iteration[it] = calpilot_node + + # Extrinsic handoff consolidation for this iteration. Runs IN PARALLEL with CIP/puff + # (does NOT gate them) -- it depends on unify_node, which guarantees all of iteration + # it's ILE jobs finished (so the per-event extr_proposal__*.npz are present), and + # emits extr_consolidated_.npz, which SEEDS the wide ILE jobs of iteration it+1. + if extrconsolidate_job: + extrconsolidate_node = pipeline.CondorDAGNode(extrconsolidate_job) + extrconsolidate_node.add_macro("macroiteration", it) + extrconsolidate_node.set_category("EXTRCONSOLIDATE") + extrconsolidate_node.set_retry(opts.general_retries) + extrconsolidate_node.add_parent(unify_node) # all of iteration it's ILE jobs done + dag.add_node(extrconsolidate_node) + extrconsolidate_node_per_iteration[it] = extrconsolidate_node + - # Create convert node, which depends on fit node, *if* tests are being performed if opts.test_args: @@ -1856,6 +2038,19 @@ if opts.last_iteration_extrinsic: cat_node = pipeline.CondorDAGNode(cat_job) cat_node.add_macro("macroiteration", it) # needed to identify log file location + # Per-distance likelihood export consolidation nodes (depend directly on + # ILE_extr, since .dgrid / .dslice are emitted by ILE itself with no + # convert/resample step in between). + dgrid_node = dslice_node = None + if opts.last_iteration_export_marginal_distance_grid: + dgrid_node = pipeline.CondorDAGNode(dgrid_job) + dgrid_node.add_macro("macroiteration", it) + dgrid_node.set_retry(opts.ile_retries) + if opts.last_iteration_export_distance_slices and opts.last_iteration_export_distance_slices > 0: + dslice_node = pipeline.CondorDAGNode(dslice_job) + dslice_node.add_macro("macroiteration", it) + dslice_node.set_retry(opts.ile_retries) + # Perform final ILE run on all points, saving samples # Need to perform number of events CONSISTENT WITH TARGET SAMPLE SIZE # - *not* always same as number of ILE events being analyzed @@ -1881,6 +2076,13 @@ if opts.last_iteration_extrinsic: dag.add_node(ile_node) # ile_node_list_per_iteration[it].append(ile_node) + # Distance-grid / -slice consolidation runs after EVERY ILE_extr + # finishes, since each ILE_extr emits one .dgrid / .dslice file. + if dgrid_node is not None: + dgrid_node.add_parent(ile_node) + if dslice_node is not None: + dslice_node.add_parent(ile_node) + # Add convert and resample task *for each output file* if opts.last_iteration_extrinsic_time_resampling: # establish parent-child relationship @@ -1924,6 +2126,10 @@ if opts.last_iteration_extrinsic: else: dag.add_node(convert_node) # this is the time resampling task last_node=convert_node + if dgrid_node is not None: + dag.add_node(dgrid_node) + if dslice_node is not None: + dag.add_node(dslice_node) # Create rotation job (if extrinsic available) if opts.frame_rotation and opts.last_iteration_extrinsic: diff --git a/MonteCarloMarginalizeCode/Code/bin/create_event_parameter_pipeline_BasicMultiApproxIteration b/MonteCarloMarginalizeCode/Code/bin/create_event_parameter_pipeline_BasicMultiApproxIteration old mode 100644 new mode 100755 index 82c064979..5627e2227 --- a/MonteCarloMarginalizeCode/Code/bin/create_event_parameter_pipeline_BasicMultiApproxIteration +++ b/MonteCarloMarginalizeCode/Code/bin/create_event_parameter_pipeline_BasicMultiApproxIteration @@ -187,6 +187,12 @@ parser.add_argument("--puff-cadence",default=None,type=int,help="Every n iterati parser.add_argument("--puff-max-it",default=-1,type=int,help="Maximum iteration number that puffball is applied. If negative, puffball is not applied ") parser.add_argument("--last-iteration-extrinsic",action='store_true',help="Configure last iteration to extract *one* set of extrinsic parameters from each intrinsic point. [This is highly inefficient, but people like having one extrinsic point per intrinsic point.] Requires --convert-args") parser.add_argument("--last-iteration-extrinsic-nsamples",default=3000,type=int,help="Construct this number of extrinsic samples") +parser.add_argument("--last-iteration-export-marginal-distance-grid", action='store_true', help="Add argument to ILE_extr") +parser.add_argument("--last-iteration-export-distance-slices", default=0, type=int, help="If >0, the ILE_extr (extrinsic) stage exports K-row .dslice files: Plan-B fixed-distance extrinsic-marginalized likelihoods. Adds --export-distance-slices K (+ --internal-use-lnL) to ILE_extr and strips --distance-marginalization.") +parser.add_argument("--last-iteration-export-distance-slices-n-core", default=0, type=int, help="Passthrough to ILE --n-distance-slice-core for the extrinsic-stage .dslice export (0 = ILE default).") +parser.add_argument("--last-iteration-export-distance-slices-n-wing", default=0, type=int, help="Passthrough to ILE --n-distance-slice-wing for the extrinsic-stage .dslice export (0 = ILE default).") +parser.add_argument("--last-iteration-export-distance-slices-wing-delta-lnL", default=None, type=float, help="Passthrough to ILE --distance-slice-wing-delta-lnL for the extrinsic-stage .dslice export.") +parser.add_argument("--last-iteration-export-distance-slices-skip-threshold", default=None, type=float, help="Passthrough to ILE --distance-slice-skip-threshold for the extrinsic-stage .dslice export.") parser.add_argument("--ile-args",default=None,help="filename of args_ile.txt file which holds ILE arguments. Should NOT conflict with arguments auto-set by this DAG ... in particular, i/o arguments will be modified") parser.add_argument("--ile-exe",default=None,help="filename of ILE or equivalent executable. Will default to `which integrate_likelihood_extrinsic` in low-level code") parser.add_argument("--ile-retries",default=0,type=int,help="Number of retry attempts for ILE jobs. (These can fail)") @@ -261,10 +267,21 @@ if not (opts.cip_args is None): print("CIP", cip_args) cip_args_lines = None +cip_args_prefixes = [] # so it works correctly even in flat mode if not (opts.cip_args_list is None): with open(opts.cip_args_list) as f: cip_args_lines = f.readlines() - cip_args_n = [int(x.split(' ')[0]) for x in cip_args_lines] # Pull off the integer + # Pull off the leading token. It is usually an integer (number of iterations), but util_RIFT_pseudo_pipe.py + # also emits 'Z' (run-to-convergence marker) and 'G' (grid-stage marker) prefixes; tolerate those here. + cip_args_prefixes = [(x.split(' ')[0]) for x in cip_args_lines] + cip_args_n = (cip_args_prefixes.copy()) + for indx in np.arange(len(cip_args_n)): + if cip_args_prefixes[indx][0]=='Z': + cip_args_n[indx] = 1 # one nominal iteration; this workflow has no run-to-convergence CIP stage + elif cip_args_prefixes[indx][0] =='G': + cip_args_n[indx] = int(cip_args_prefixes[indx][1:]) # integer after G assumed + else: + cip_args_n[indx] = int(cip_args_n[indx]) cip_args_lines = [' '.join(x.split(' ')[1:]) for x in cip_args_lines] # pull off the integer cip_args_lines = [x.replace('[', ' \'[').replace(']', ']\'').rstrip() for x in cip_args_lines] cip_args_lines = [x.lstrip() for x in cip_args_lines] # remove leading whitespace @@ -542,6 +559,22 @@ if (opts.last_iteration_extrinsic): ile_args_extr = ile_args + " --save-P 0.01 --save-samples --n-eff " +str(2*n_points_per_ILE) # modify convergence criteria so output of reasonable size # - note we *disable* --no-adapt-after-first (if present), so each point is independent (e.g., in sky location) ile_args_extr = ile_args_extr.replace('--no-adapt-after-first','') + if opts.last_iteration_export_marginal_distance_grid: + ile_args_extr += " --export-marginal-distance-grid " + ile_args_extr = ile_args_extr.replace("--distance-marginalization ", ' ') + if opts.last_iteration_export_distance_slices and opts.last_iteration_export_distance_slices > 0: + ile_args_extr += " --export-distance-slices {} ".format(opts.last_iteration_export_distance_slices) + if opts.last_iteration_export_distance_slices_n_core: + ile_args_extr += " --n-distance-slice-core {} ".format(opts.last_iteration_export_distance_slices_n_core) + if opts.last_iteration_export_distance_slices_n_wing: + ile_args_extr += " --n-distance-slice-wing {} ".format(opts.last_iteration_export_distance_slices_n_wing) + if opts.last_iteration_export_distance_slices_wing_delta_lnL is not None: + ile_args_extr += " --distance-slice-wing-delta-lnL {} ".format(opts.last_iteration_export_distance_slices_wing_delta_lnL) + if opts.last_iteration_export_distance_slices_skip_threshold is not None: + ile_args_extr += " --distance-slice-skip-threshold {} ".format(opts.last_iteration_export_distance_slices_skip_threshold) + if "--internal-use-lnL" not in ile_args_extr: + ile_args_extr += " --internal-use-lnL " + ile_args_extr = ile_args_extr.replace("--distance-marginalization ", ' ') ileExtr_job, ileExtr_job_name = dag_utils.write_ILE_sub_simple(tag='ILE_extr',log_dir=None,arg_str=ile_args_extr,output_file="EXTR_out.xml",simple_unique=True,ncopies=1,exe=ile_exe,transfer_files=transfer_file_names,request_memory=opts.request_memory_ILE*2,request_gpu=opts.request_gpu_ILE,use_cvmfs_frames=opts.use_cvmfs_frames) ileExtr_job.add_condor_cmd("initialdir",opts.working_directory+"/approx_$(macroapprox)_iteration_$(macroiteration)_ile") ileExtr_job.set_log_file(opts.working_directory+"/approx_$(macroapprox)_iteration_$(macroiteration)_ile/logs/ILEextr-$(macroevent)-$(cluster)-$(process).log") diff --git a/MonteCarloMarginalizeCode/Code/bin/helper_LDG_Events.py b/MonteCarloMarginalizeCode/Code/bin/helper_LDG_Events.py index 900d43bd4..21b71b699 100755 --- a/MonteCarloMarginalizeCode/Code/bin/helper_LDG_Events.py +++ b/MonteCarloMarginalizeCode/Code/bin/helper_LDG_Events.py @@ -210,6 +210,7 @@ def get_observing_run(t): parser.add_argument("--assume-volumetric-spin",action='store_true',help="If present, the code will assume a volumetric spin prior in its last iterations. If *not* present, the code will adopt a uniform magnitude spin prior in its last iterations. If not present, generally more iterations are taken.") parser.add_argument("--assume-highq",action='store_true',help="If present, the code will adopt a strategy that drops spin2. Also the precessing strategy will allow perpendicular spin to play a role early on (rather than as a subdominant parameter later)") parser.add_argument("--assume-well-placed",action='store_true',help="If present, the code will adopt a strategy that assumes the initial grid is very well placed, and will minimize the number of early iterations performed. Not as extrme as --propose-flat-strategy") +parser.add_argument("--calmarg-first-cip-sigma-cut",default=None,type=float,help="In-loop calibration marginalization: relax the CIP --sigma-cut on the FIRST cip stage to this value. The cold-start iterations draw cal realizations from the broad PRIOR (no pilot proposal learned yet), so their per-point Monte-Carlo error is large and the default --sigma-cut 0.6 would strip every point. util_RIFT_pseudo_pipe.py passes this automatically when --calmarg-pilot is enabled.") parser.add_argument("--propose-ile-convergence-options",action='store_true',help="If present, the code will try to adjust the adaptation options, Nmax, etc based on experience") parser.add_argument("--internal-propose-ile-convergence-freezeadapt",action='store_true',help="If present, uses the --no-adapt-after-first --no-adapt-distance options (at one point default)") parser.add_argument("--internal-propose-ile-adapt-log",action='store_true',help="If present, uses the --adapt-log argument. Useful for very loud signals. Note only lnL information is used for adapting, not prior, so samples will be *uniform* in prior range if lnL is low") @@ -1741,6 +1742,14 @@ def lambda_m_estimate(m): helper_cip_arg_list += [helper_cip_last_it] +# In-loop calmarg cold start: the first CIP stage runs on iterations whose cal draws come +# from the broad PRIOR (the pilot has not yet learned/seeded a proposal), so their MC error +# is large. Relax that stage's --sigma-cut so the cold-start points are not all stripped +# (CIP default 0.6). Only the FIRST stage is relaxed; later stages run on pilot-seeded +# iterations with normal errors and keep the default cut. +if opts.calmarg_first_cip_sigma_cut is not None and len(helper_cip_arg_list) > 0: + helper_cip_arg_list[0] += " --sigma-cut {} ".format(opts.calmarg_first_cip_sigma_cut) + with open("helper_cip_arg_list.txt",'w+') as f: f.write("\n".join(helper_cip_arg_list)) diff --git a/MonteCarloMarginalizeCode/Code/bin/integrate_likelihood_extrinsic b/MonteCarloMarginalizeCode/Code/bin/integrate_likelihood_extrinsic index 347e0bce3..e55072b3b 100755 --- a/MonteCarloMarginalizeCode/Code/bin/integrate_likelihood_extrinsic +++ b/MonteCarloMarginalizeCode/Code/bin/integrate_likelihood_extrinsic @@ -39,6 +39,7 @@ import glue.lal #import pylal import RIFT.lalsimutils as lalsimutils +from RIFT.precision import RiftFloat import RIFT.likelihood.factored_likelihood as factored_likelihood import RIFT.integrators.mcsampler as mcsampler import RIFT.misc.sky_rotations as sky_rotations @@ -1047,7 +1048,7 @@ if not opts.time_marginalization: incl = numpy.arccos(incl) # use EXTREMELY many bits - lnL = numpy.zeros(right_ascension.shape,dtype=numpy.float128) + lnL = numpy.zeros(right_ascension.shape,dtype=RiftFloat) i = 0 for ph, th, tr, phr, ic, ps, di in zip(right_ascension, dec, t_ref, phi_orb, incl, psi, distance): @@ -1078,7 +1079,7 @@ else: # Sum over time for every point in other extrinsic params incl = numpy.arccos(incl) # use EXTREMELY many bits - lnL = numpy.zeros(right_ascension.shape,dtype=numpy.float128) + lnL = numpy.zeros(right_ascension.shape,dtype=RiftFloat) i = 0 tvals = numpy.linspace(-t_ref_wind,t_ref_wind,int((t_ref_wind)*2/P.deltaT)) # choose an array at the target sampling rate. P is inherited globally @@ -1140,7 +1141,7 @@ else: # Sum over time for every point in other extrinsic params incl = numpy.arccos(incl) # use EXTREMELY many bits - lnL = numpy.zeros(right_ascension.shape,dtype=numpy.float128) + lnL = numpy.zeros(right_ascension.shape,dtype=RiftFloat) P.phi = right_ascension.astype(float) # cast to float P.theta = dec #declination.astype(float) P.tref = float(fiducial_epoch) @@ -1278,7 +1279,7 @@ else: # Sum over time for every point in other extrinsic params if opts.inclination_cosine_sampler: incl = numpy.arccos(incl) - lnL = numpy.zeros(len(right_ascension),dtype=numpy.float128) + lnL = numpy.zeros(len(right_ascension),dtype=RiftFloat) i = 0 tvals = numpy.linspace(-t_ref_wind,t_ref_wind,int((t_ref_wind)*2/P.deltaT)) # choose an array at the target sampling rate. P is inherited globally @@ -1378,7 +1379,7 @@ if opts.maximize_only and opts.output_file: sampler._rvs["inclination"][indx_guess]/(numpy.pi),\ sampler._rvs["psi"][indx_guess]/numpy.pi,\ sampler._rvs["distance"][indx_guess]/dmax\ - ],dtype=numpy.float128) + ],dtype=RiftFloat) x0 = numpy.fmod(x0,numpy.ones(len(x0))) # had BETTER be defined on this range! # Pick the best starting time. BRUTE FORCE METHOD: use grid def fn_scaled_t(t,x0): diff --git a/MonteCarloMarginalizeCode/Code/bin/integrate_likelihood_extrinsic_batchmode b/MonteCarloMarginalizeCode/Code/bin/integrate_likelihood_extrinsic_batchmode index 88c7c1740..a4843f9aa 100755 --- a/MonteCarloMarginalizeCode/Code/bin/integrate_likelihood_extrinsic_batchmode +++ b/MonteCarloMarginalizeCode/Code/bin/integrate_likelihood_extrinsic_batchmode @@ -48,6 +48,7 @@ from igwn_ligolw import utils, ligolw import glue.lal import RIFT.lalsimutils as lalsimutils +from RIFT.precision import RiftFloat import RIFT.integrators.mcsampler as mcsampler import RIFT.misc.sky_rotations as sky_rotations try: @@ -237,6 +238,19 @@ optp.add_option("-l", "--distance-marginalization-lookup-table", default=None, h optp.add_option("--calibration-envelope-directory",default=None, help="Name of directory") optp.add_option("--calibration-n-realizations", default=100, type=int, help="Number of realizations to use for calmarg, recommend 100") optp.add_option("--calibration-spline-count", default=10,type=int) +optp.add_option("--calibration-fused-kernel", action="store_true", default=False, help="Opt-in: use the fused GPU kernel (Option C) for in-loop calibration marginalization. GPU only, no phase marginalization; with distance marginalization it uses the fused distmarg kernel. Falls back to the loop method (Option B) otherwise.") +optp.add_option("--calibration-proposal-breadcrumb",default=None, help="Opt-in (Option C / adaptive pilot): path to a breadcrumb .npz (RIFT.calmarg.breadcrumbs) carrying a LEARNED Gaussian proposal over cal spline nodes. When set, the cal realizations are drawn from that proposal instead of the broad prior, and the marginalization carries Phase-0 importance weights log(prior/proposal) so it stays unbiased. Requires --calibration-envelope-directory (the prior).") +optp.add_option("--calibration-dump-responsibilities",default=None, help="Opt-in (Option C / adaptive pilot): path to write per-cal-realization log-responsibilities (length n_cal), accumulated over the evaluated grid, plus the cal node draws. This is the pilot's output, fitted into a proposal by util_CalPilotFit.py. No effect on the returned likelihood.") +optp.add_option("--calibration-pilot-extrinsic",default=256,type=int, help="Pilot only: number of uniform-prior extrinsic samples used to extrinsic-marginalize the per-realization cal responsibility at each intrinsic point. Cal is ~extrinsic-independent, so a modest batch suffices.") +optp.add_option("--calibration-mc-error-extrinsic",default=8192,type=int, help="Calmarg error budget: CAP on the number of extrinsic-prior samples used to estimate the calibration Monte-Carlo contribution to the lnL error (per-realization responsibilities a_c -> Var(lnZ) ~= n_cal*Var_c(a_c)), added IN QUADRATURE to the reported sigma column. The batch is ADAPTIVE: it starts small and doubles until the estimate stabilizes or this cap is reached. Distance is drawn from the RUN'S distance prior (sampler prior / --d-prior; with a PINNED distance the probe runs at that fixed value and warns that the estimate is conservative). The extrinsic sampler's variance cannot see the spread over the (fixed) cal draw set, so without this term the reported error badly understates the truth whenever the cal n_eff is small. Set 0 to disable (restores the old, extrinsic-only sigma).") +optp.add_option("--calibration-neff-cal-target",default=10,type=float, help="Calmarg ADAPTIVE draw count: after the cal-block precompute, probe the effective number of contributing cal draws (neff_cal) at this intrinsic point; while it is below this target, DOUBLE the cal draw set (drawing fresh independent realizations and appending their precomputed blocks) up to --calibration-n-realizations-max. Set 0 to disable (fixed --calibration-n-realizations).") +optp.add_option("--calibration-n-realizations-max",default=0,type=int, help="Cap for the adaptive cal draw count (see --calibration-neff-cal-target). Default 0 = 8x --calibration-n-realizations.") +optp.add_option("--calibration-burn-in-neff",default=None,type=float, help="Opt-in: before the production cal-marginalized integration, BURN IN the extrinsic sampler on the cheap ZERO-CAL (n_cal=1) likelihood until this effective sample count, then switch to the full cal-marginalized likelihood. The extrinsic posterior is ~cal-independent. CAVEAT: the AV sampler RESETS between integrate() calls (no seedable AV yet), so this gives AV no speedup (correctness-safe only). It can warm-start GMM/portfolio (model reuse). Awaiting a seedable / boundary-shifting AV; see DESIGN_adaptive_driver.md. No effect unless calmarg is active.") +optp.add_option("--calibration-burn-in-nmax",default=None,type=int, help="Cap on the number of samples drawn during the zero-cal burn-in (default: the run's --n-max). Keeps the burn-in bounded if it cannot reach --calibration-burn-in-neff.") +optp.add_option("--calibration-export-posterior",action='store_true',default=False, help="Opt-in (final fairdraw export, calmarg active): for each fair-draw output sample, draw ONE calibration realization in proportion to its posterior weight (L_c * w_c, from the per-realization likelihood components) and write a SELF-CONTAINED sibling __cal.dat with the FULL draw -- intrinsic + extrinsic + the drawn realization's spline-node values as labeled columns cal__amp_/cal__phase_. The recovered cal posterior is then those columns, plottable with the standard tooling. Requires --calibration-envelope-directory; retains the cal node vectors.") +optp.add_option("--extrinsic-proposal-breadcrumb",default=None, help="Opt-in (GMM sampler): SEED the extrinsic GMM sampler from a learned proposal breadcrumb (RIFT.calmarg.extrinsic_handoff): the per-group GMMs from a previous iteration's posterior pre-fill gmm_dict, so the sampler starts on the posterior instead of cold. The extrinsic posterior barely moves iteration-to-iteration. Groups matched by parameter name; missing groups fall back to the default.") +optp.add_option("--extrinsic-proposal-output",default=None, help="Opt-in (GMM sampler): after the integration, fit the run's extrinsic posterior samples to a per-group GMM and WRITE it as a proposal breadcrumb (to seed a later iteration via --extrinsic-proposal-breadcrumb).") +optp.add_option("--extrinsic-proposal-adapt",action='store_true',default=False, help="With --extrinsic-proposal-breadcrumb: let the SEEDED extrinsic GMM groups keep adapting (re-fit each iteration). Default OFF = the seeded groups are FROZEN: a handed-off proposal (especially from a different, better-converged sampler) is trusted as-is, since the GMM's own adaptation is fragile on sharp ILE peaks (a bad batch re-fit triggers _reset and discards the seed). Freeze keeps the good seed; enable adapt only if the source posterior may have drifted.") optp.add_option("--vectorized", action="store_true", help="Perform manipulations of lm and timeseries using numpy arrays, not LAL data structures. (Combine with --gpu to enable GPU use, where available)") optp.add_option("--gpu", action="store_true", help="Perform manipulations of lm and timeseries using numpy arrays, CONVERTING TO GPU when available. You MUST use this option with --vectorized (otherwise it is a no-op). You MUST have a suitable version of cupy installed, your cuda operational, etc") optp.add_option("--force-gpu-only", action="store_true", help="Hard fail if no GPU present (assessed by cupy not loading)") @@ -280,6 +294,9 @@ integration_params.add_option("--adapt-weight-exponent", type=float, default=1.0 integration_params.add_option("--adapt-floor-level", type=float, default=0.1, help="Floor to use with weights (likelihood integrand) when doing adaptive sampling. This is necessary to ensure the *sampling* prior is non zero during adaptive sampling and to prevent overconvergence. Default is 0.1 (no floor)") integration_params.add_option("--adapt-adapt",action='store_true',help="Adapt the tempering exponent") integration_params.add_option("--adapt-log",action='store_true',help="Use a logarithmic tempering exponent") +integration_params.add_option("--internal-gmm-correlate-all",action='store_true',help="GMM sampler: use a SINGLE full-dimension GMM group instead of the default (sky)(distance,inclination)(psi,phi) pairing. The default pairing targets quadrupole-dominated binaries with a large sky ring; a product of per-group GMMs cannot represent cross-group correlations (e.g. sky-phase), and for a strongly-localized single-peak source the factored proposal can stall at the prior. Component count from --internal-gmm-sky-components (default 2 in this mode).") +integration_params.add_option("--internal-gmm-sky-components",type=int,default=None,help="GMM sampler: number of mixture components for the (ra,dec) group (default 4, sized for a large sky ring; use 1-2 for a well-localized single peak, e.g. 3+ IFOs / high SNR). With --internal-gmm-correlate-all, sets the single full-dimension group's component count.") +integration_params.add_option("--internal-gmm-phase-components",type=int,default=None,help="GMM sampler: number of mixture components for the (psi,phi_orb) group (default 4; use 1-2 for a single dominant phase peak).") integration_params.add_option("--interpolate-time", default=False,help="If using time marginalization, compute using a continuously-interpolated array. (Default=false)") integration_params.add_option("--d-prior",default='Euclidean' ,type=str,help="Distance prior for dL. Options are dL^2 (Euclidean), 'pseudo_cosmo', and 'cosmo' and 'cosmo_sourceframe' .") integration_params.add_option("--d-prior-redshift", action='store_true', help="If true, distance prior is computed in redshift. This option MAY be enforced for 'cosmo' sampling") @@ -315,6 +332,13 @@ intrinsic_params.add_option("--eff-lambda", type=float, help="Value of effective intrinsic_params.add_option("--deff-lambda", type=float, help="Value of second effective tidal parameter. Optional, ignored if not given") intrinsic_params.add_option("--export-eos-index",action='store_true') intrinsic_params.add_option("--export-marginal-distance-grid",action='store_true') +intrinsic_params.add_option("--export-distance-slices",type=int,default=0,help="If >0, after main extrinsic integration emit a per-event .dslice file with rows of fixed-d extrinsic-marginalized likelihoods. Total rows = --n-distance-slice-core + --n-distance-slice-wing. Requires --internal-use-lnL and no --distance-marginalization.") +intrinsic_params.add_option("--n-distance-slice-core",type=int,default=0,help="Core slices via importance-reweight on existing Omega samples (cheap). If 0 and --export-distance-slices>0, defaults to ceil(K*0.6).") +intrinsic_params.add_option("--n-distance-slice-wing",type=int,default=0,help="Wing slices via fresh Omega-only integrations at pinned distance (covers tails ~7 nats below peak). If 0 and --export-distance-slices>0, defaults to K - core.") +intrinsic_params.add_option("--distance-slice-wing-nmax",type=int,default=20000,help="Max samples per wing fresh integration.") +intrinsic_params.add_option("--distance-slice-wing-neff",type=int,default=30,help="n_eff target per wing fresh integration.") +intrinsic_params.add_option("--distance-slice-skip-threshold",type=float,default=1.0,help="Absolute lnL scale: if the PEAK lnL across core slices is below this many nats, treat the event as effectively undetected and skip wing integrations. (lnL is a likelihood ratio vs noise, so this is an absolute detectability cut, not a relative-spread test.)") +intrinsic_params.add_option("--distance-slice-wing-delta-lnL",type=float,default=7.0,help="Target lnL drop below peak used to place wing slice centers: wings span from the core edge out to where the parabolic lnL(1/d) model falls this many nats below peak (default 7 ~ prior weight <1e-3 outside). Falls back to log-uniform full-range placement if the parabolic fit is degenerate.") optp.add_option_group(intrinsic_params) @@ -789,19 +813,153 @@ for Psig in P_list: # calibration_marginalization=False calibration_realization_dict = {} +calibration_log_weights = None # Phase-0 importance weights log(prior/proposal); None => uniform (prior draws) +_calpilot = None # pilot bookkeeping (node draws + prior) when dumping responsibilities +_calpilot_logresp_list = [] # per-intrinsic-point per-realization log-responsibilities, accumulated +# Cal node vectors retained for --calibration-export-posterior (final fairdraw cal-posterior columns): +calibration_nodes = None # (n_cal, 2*n_nodes_amp*len(dets)) per-det [amp_0..,phase_0..] blocks +calibration_node_dets = None # detector order matching the node blocks +calibration_n_nodes_amp = None # spline nodes per detector per (amp|phase) +def _cal_setup_prior_with_nodes(psd_dict): + """Populate calibration_realization_dict from broad-PRIOR cal draws. When + --calibration-export-posterior is set, RETAIN the node vectors too (via + draw_prior_realizations_with_nodes, same prior as create_realizations) so the final + fairdraw can emit the recovered cal posterior; otherwise just build the realizations.""" + global calibration_realization_dict, calibration_nodes, calibration_node_dets, calibration_n_nodes_amp + import RIFT.calmarg.generate_realizations as _genr + if opts.calibration_export_posterior: + _ret = _genr.draw_prior_realizations_with_nodes( + opts.calibration_envelope_directory, list(psd_dict.keys()), 1./P.deltaF, P.deltaT, + opts.fmin_template, fmax, opts.calibration_spline_count, opts.calibration_n_realizations, + fmin_ifo=cal_fmin_ifo, rng=np.random.default_rng(getattr(opts,'seed',None))) + calibration_realization_dict = _ret['realizations'] + calibration_nodes = _ret['nodes']; calibration_node_dets = list(_ret['dets']); calibration_n_nodes_amp = int(_ret['n_nodes_amp']) + else: + for ifo in psd_dict: + fname = opts.calibration_envelope_directory + "/" + ifo + ".txt" + calibration_realization_dict[ifo] = _genr.create_realizations(fname, 1./P.deltaF, P.deltaT, cal_fmin_ifo[ifo], fmax, opts.calibration_spline_count, opts.calibration_n_realizations) if opts.calibration_envelope_directory: # t_ref_wind = opts.calibration_n_realizations * t_ref_wind # changes length of buffer, should produce longer window. DOES NOT WORK PROPERLY import RIFT.calmarg.generate_realizations calibration_marginalization=True + # per-detector low-frequency cutoff used to lay down the cal spline + cal_fmin_ifo = {} for ifo in psd_dict: - fname = opts.calibration_envelope_directory + "/" + ifo + ".txt" - fmin_here = opts.fmin_template - if opts.fmin_ifo: - fmin_here = flow_ifo_dict[ifo] - - calibration_realization_dict[ifo] = RIFT.calmarg.generate_realizations.create_realizations(fname, 1./P.deltaF, P.deltaT, fmin_here, fmax, opts.calibration_spline_count, opts.calibration_n_realizations) + cal_fmin_ifo[ifo] = flow_ifo_dict[ifo] if opts.fmin_ifo else opts.fmin_template + # Graceful fallback: a seeded run is given a per-iteration breadcrumb path, but the + # FIRST iteration (and any iteration before a pilot has run) has no breadcrumb yet. If + # the path is missing OR EMPTY, fall back to the broad prior -- so the SAME fixed ILE args + # work across all DAG iterations. The iteration-0 placeholder is a 0-byte file (created so + # OSG file transfer of cal_consolidated_$(macroiterationprev).npz does not fail); on OSG it + # IS transferred in, so it exists but is empty -> np.load raises EOFError. Treat empty (or + # otherwise unreadable) as "not present yet". + if opts.calibration_proposal_breadcrumb: + _bc_path = opts.calibration_proposal_breadcrumb + if not (os.path.exists(_bc_path) and os.path.getsize(_bc_path) > 0): + print(" Calibration proposal breadcrumb {} missing or empty (iteration-0 placeholder); falling back to PRIOR cal draws.".format(_bc_path)) + opts.calibration_proposal_breadcrumb = None + if opts.calibration_dump_responsibilities: + # PILOT (Option C): KEEP the cal node vectors so the per-realization responsibilities + # accumulated below can be fitted into a proposal (util_CalPilotFit.py). Deterministic + # (seeded) so node draws are identical across intrinsic points and the accumulation + # aligns by realization. + # - If an incoming proposal breadcrumb is given, draw the pilot's cal realizations + # FROM IT (refinement step: pilot_N seeded from consolidation_{N-1}); the fit then + # folds log_w so the refined proposal targets the posterior. This is the + # across-iteration climb (adaptive_cal unrolled over the DAG). + # - Otherwise draw from the broad prior (the N=0 cold start). + import RIFT.calmarg.breadcrumbs, RIFT.calmarg.adaptive + if opts.calibration_proposal_breadcrumb: + _bc = RIFT.calmarg.breadcrumbs.load(opts.calibration_proposal_breadcrumb) + calibration_realization_dict, calibration_log_weights, _pilot_nodes = \ + RIFT.calmarg.generate_realizations.seed_realizations_from_breadcrumb( + _bc, 1./P.deltaF, P.deltaT, opts.fmin_template, fmax, + opts.calibration_spline_count, opts.calibration_n_realizations, + fmin_ifo=cal_fmin_ifo, rng=np.random.default_rng(getattr(opts,'seed',None))) + _cal = _bc["cal"] + _calpilot = dict(nodes=_pilot_nodes, prior_mean=_cal["prior_mean"], prior_sigma=_cal["prior_sigma"], + node_log_f=_cal["node_log_f"], n_nodes_amp=int(_cal["n_nodes_amp"]), + dets=list(_cal["dets"]), log_w=np.asarray(calibration_log_weights)) + print(" Calibration PILOT mode (refine): seeded from {} ; responsibilities -> {}".format( + opts.calibration_proposal_breadcrumb, opts.calibration_dump_responsibilities)) + else: + _calpilot = RIFT.calmarg.generate_realizations.draw_prior_realizations_with_nodes( + opts.calibration_envelope_directory, list(psd_dict.keys()), 1./P.deltaF, P.deltaT, + opts.fmin_template, fmax, opts.calibration_spline_count, opts.calibration_n_realizations, + fmin_ifo=cal_fmin_ifo, rng=np.random.default_rng(getattr(opts,'seed',None))) + calibration_realization_dict = _calpilot['realizations'] + _calpilot['log_w'] = np.zeros(opts.calibration_n_realizations) # prior draws -> uniform + print(" Calibration PILOT mode (cold): prior cal draws; responsibilities -> {}".format( + opts.calibration_dump_responsibilities)) + elif opts.calibration_proposal_breadcrumb: + # Option C / adaptive pilot: draw cal realizations from the LEARNED proposal and + # carry importance weights log(prior/proposal) so the marginalization is unbiased. + import RIFT.calmarg.breadcrumbs, RIFT.calmarg.adaptive + try: + _bc = RIFT.calmarg.breadcrumbs.load(opts.calibration_proposal_breadcrumb) + _rng = np.random.default_rng(getattr(opts,'seed',None)) + calibration_realization_dict, calibration_log_weights, _seed_nodes = \ + RIFT.calmarg.generate_realizations.seed_realizations_from_breadcrumb( + _bc, 1./P.deltaF, P.deltaT, opts.fmin_template, fmax, + opts.calibration_spline_count, opts.calibration_n_realizations, + fmin_ifo=cal_fmin_ifo, rng=_rng) + if opts.calibration_export_posterior: + calibration_nodes = _seed_nodes + calibration_node_dets = list(_bc["cal"]["dets"]); calibration_n_nodes_amp = int(_bc["cal"]["n_nodes_amp"]) + print(" Calibration realizations SEEDED from proposal breadcrumb {} ; neff(cal weights)~{:.1f}/{}".format( + opts.calibration_proposal_breadcrumb, + RIFT.calmarg.adaptive.neff_from_logweights(calibration_log_weights), + opts.calibration_n_realizations)) + except Exception as _e_bc: + # robustness (esp. OSG file transfer): a missing/partial/invalid breadcrumb must NOT + # kill the job -- fall back to broad PRIOR cal draws (still unbiased, just unseeded). + print(" WARNING: could not seed from breadcrumb {} ({}); falling back to PRIOR cal draws.".format( + opts.calibration_proposal_breadcrumb, _e_bc)) + opts.calibration_proposal_breadcrumb = None + _cal_setup_prior_with_nodes(psd_dict) + else: + _cal_setup_prior_with_nodes(psd_dict) + +def _draw_more_calibration_draws(n_more, psd_dict): + """Draw n_more ADDITIONAL independent cal realizations from the same source as + the original set (broad prior / prior-with-nodes / proposal breadcrumb), EXTEND + the module-level bookkeeping in place (realization dict columns, importance + log-weights, node vectors), and return JUST the new realizations dict so the + caller can precompute only the new rholm blocks and append them. + + Fresh, unseeded randomness ON PURPOSE: cal draws must remain independent across + points/workers -- the variance is disclosed (cal MC error budget) and reduced by + growing the draw set, never by sharing draws.""" + global calibration_realization_dict, calibration_log_weights, calibration_nodes + import RIFT.calmarg.generate_realizations as _genr + new = {} + if opts.calibration_proposal_breadcrumb: + import RIFT.calmarg.breadcrumbs + _bc = RIFT.calmarg.breadcrumbs.load(opts.calibration_proposal_breadcrumb) + new, _lw, _nodes = _genr.seed_realizations_from_breadcrumb( + _bc, 1./P.deltaF, P.deltaT, opts.fmin_template, fmax, + opts.calibration_spline_count, n_more, fmin_ifo=cal_fmin_ifo, + rng=np.random.default_rng()) + calibration_log_weights = np.concatenate([np.asarray(calibration_log_weights), np.asarray(_lw)]) + if calibration_nodes is not None: + calibration_nodes = np.vstack([calibration_nodes, _nodes]) + elif opts.calibration_export_posterior: + _ret = _genr.draw_prior_realizations_with_nodes( + opts.calibration_envelope_directory, list(psd_dict.keys()), 1./P.deltaF, P.deltaT, + opts.fmin_template, fmax, opts.calibration_spline_count, n_more, + fmin_ifo=cal_fmin_ifo, rng=np.random.default_rng()) + new = _ret['realizations'] + calibration_nodes = np.vstack([calibration_nodes, _ret['nodes']]) if calibration_nodes is not None else _ret['nodes'] + else: + for ifo in psd_dict: + fname = opts.calibration_envelope_directory + "/" + ifo + ".txt" + new[ifo] = _genr.create_realizations(fname, 1./P.deltaF, P.deltaT, + cal_fmin_ifo[ifo], fmax, opts.calibration_spline_count, n_more) + for ifo in new: + calibration_realization_dict[ifo] = np.concatenate([calibration_realization_dict[ifo], new[ifo]], axis=1) + return new + - # # Set up parameters and bounds @@ -1291,6 +1449,12 @@ if opts.sampler_method=="GMM" and opts.internal_use_lnL: if opts.sampler_method =="adaptive_cartesian_gpu" and opts.internal_use_lnL: return_lnL=True pinned_params.update({"use_lnL":True}) +if opts.sampler_method =="AV" and opts.internal_use_lnL: + # AV integrates in log space natively (integrate() is a thin wrapper over integrate_log); + # without this, --internal-use-lnL --sampler-method AV passed the ok_lnL_methods check but + # silently did nothing, so exp(lnL) overflowed at high SNR when no logarithm offset was set. + return_lnL=True + pinned_params.update({"use_lnL":True}) if opts.sampler_method =="portfolio": return_lnL=True pinned_params.update({"use_lnL":True}) @@ -1321,15 +1485,56 @@ if opts.sampler_method == "GMM": pair_phi_psi = sampler_param_tuple(sampler, ['psi']) adapt_phase = True n_phase = 2 - gmm_dict = {pair_ra_dec:None,pair_d_incl:None,pair_phi_psi:default_phipsi} - gmm_adapt = {pair_ra_dec: True, pair_d_incl: True, pair_phi_psi: False} - if opts.internal_rotate_phase: - gmm_adapt[pair_phi_psi] = True + # user overrides for component counts (single-peak sources: the ring-sized + # defaults overfit a localized peak) + if opts.internal_gmm_sky_components: + n_sky = opts.internal_gmm_sky_components + if opts.internal_gmm_phase_components: + n_phase = opts.internal_gmm_phase_components + if opts.internal_gmm_correlate_all: + # SINGLE full-dimension group: a product of per-group GMMs cannot + # represent cross-group correlations (sky-phase etc.); for a localized + # single-peak source the factored proposal can stall at the prior + # (rank-elite refits included: the elite set looks broad in every + # per-group projection). One joint GMM can compound on any structure + # a Gaussian mixture can represent. + pair_all = tuple(range(len(sampler.params_ordered))) + n_all = opts.internal_gmm_sky_components if opts.internal_gmm_sky_components else 2 + gmm_dict = {pair_all: None} + gmm_adapt = {pair_all: True} + else: + gmm_dict = {pair_ra_dec:None,pair_d_incl:None,pair_phi_psi:default_phipsi} + gmm_adapt = {pair_ra_dec: True, pair_d_incl: True, pair_phi_psi: False} + if opts.internal_rotate_phase: + gmm_adapt[pair_phi_psi] = True + # Extrinsic handoff: SEED the per-group GMMs from a learned proposal breadcrumb (the + # previous iteration's posterior). Keys are dim-group index tuples, matched by name to + # sampler.params_ordered -- so they line up with pair_ra_dec / pair_d_incl / pair_phi_psi. + if opts.extrinsic_proposal_breadcrumb and os.path.exists(opts.extrinsic_proposal_breadcrumb) and os.path.getsize(opts.extrinsic_proposal_breadcrumb) > 0: + try: + import RIFT.calmarg.breadcrumbs as _ebcmod, RIFT.calmarg.extrinsic_handoff as _ehmod + _ebc = _ebcmod.load(opts.extrinsic_proposal_breadcrumb) + _ext_adapt = bool(getattr(opts, 'extrinsic_proposal_adapt', False)) + _seed = _ehmod.gmm_dict_from_breadcrumb(_ebc.get('extrinsic'), sampler.params_ordered, adapt=_ext_adapt, existing_keys=list(gmm_dict.keys())) + for _k, _m in _seed.items(): + if _k in gmm_dict: + gmm_dict[_k] = _m + # Default FROZEN (gmm_adapt False): _train skips these groups, so the handed-off + # proposal drives sampling unmodified. Re-fitting a seeded model on a bad first + # batch raises in the GMM init and triggers _reset -> the seed would be discarded. + gmm_adapt[_k] = _ext_adapt + print(" Extrinsic GMM SEEDED ({}) from {} for dim-groups {}".format( + "adapting" if _ext_adapt else "frozen", opts.extrinsic_proposal_breadcrumb, list(_seed.keys()))) + except Exception as _e_eh: + print(" WARNING: could not seed extrinsic GMM from {} ({}); using default proposal.".format(opts.extrinsic_proposal_breadcrumb, _e_eh)) # gmm_dict = {tuple([2]):None,tuple([4]):None,(3,5):None,tuple([0]):None,tuple([1]):None} # if len(psd_dict.keys()) > 2: # n_sky=2 # presumably we have localized better, avoid failure modes # comp_dict = {tuple([2]):1,tuple([4]):1,(3,5):3,tuple([0]):n_sky,tuple([1]):n_sky} - comp_dict = {pair_ra_dec:n_sky,pair_d_incl:n_d,pair_phi_psi:n_phase} + if opts.internal_gmm_correlate_all: + comp_dict = {pair_all: n_all} + else: + comp_dict = {pair_ra_dec:n_sky,pair_d_incl:n_d,pair_phi_psi:n_phase} extra_args = {'n_comp':comp_dict,'max_iter':n_max_blocks,'gmm_dict':gmm_dict, 'gmm_adapt':gmm_adapt} # made up for now, should adjust print("GMM:",extra_args) print("GMM:",sampler.params_ordered) @@ -1385,7 +1590,7 @@ if opts.sampler_method == 'adaptive_cartesian_gpu' and opts.skymap_file: def resample_samples(my_samples, - lookupNKDict=None, rholmArrayDict=None, ctUArrayDict=None, ctVArrayDict=None,epochDict=None): # uses LOTS of global variables, don't pass them all + lookupNKDict=None, rholmArrayDict=None, ctUArrayDict=None, ctVArrayDict=None,epochDict=None, n_cal=1, cal_log_weights=None): # uses LOTS of global variables, don't pass them all global fSample # access global sampling rate # will look a LOT like the likelihood function definitions, unfortunately if not opts.vectorized: @@ -1412,8 +1617,11 @@ def resample_samples(my_samples, for name in ['phi','theta', 'phiref','incl', 'psi','dist']: setattr(P,name, getattr(P,name).astype(float) ) + # With calibration marginalization (n_cal>1) this returns the cal-marginalized + # lnL(t) timeseries (weighted log-sum-exp over realizations per time bin), so the + # time resampling below operates on the marginalized likelihood. lnLt = factored_likelihood.DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop(tvals, - P, lookupNKDict, rholmArrayDict, ctUArrayDict, ctVArrayDict,epochDict,Lmax=opts.l_max,xpy=xpy_default,return_lnLt=True) + P, lookupNKDict, rholmArrayDict, ctUArrayDict, ctVArrayDict,epochDict,Lmax=opts.l_max,xpy=xpy_default,return_lnLt=True,n_cal=n_cal,cal_log_weights=cal_log_weights) lnLt = identity_convert(lnLt) # back to CPU. Note we have removed offsets if opts.zero_likelihood: @@ -1496,8 +1704,14 @@ def analyze_event(P_list, indx_event, data_dict, psd_dict, fmax, opts,inv_spec_t ignore_threshold=opts.internal_precompute_ignore_threshold print("IGNORE ACTIVE ", ignore_threshold) extra_kwargs ={} + n_cal_for_likelihood = 1 # >1 triggers in-loop calibration marginalization in the likelihood + # use the fused implementation (Option C) when requested; works on GPU (CUDA + # kernels) and CPU (numpy). Phase marginalization is not supported by the fused + # path, so it is disabled there below (that call site stays on the loop method). + use_fused_calmarg = bool(calibration_marginalization and opts.calibration_fused_kernel) if calibration_marginalization: extra_kwargs['calibration_realizations'] = calibration_realization_dict + n_cal_for_likelihood = opts.calibration_n_realizations rholms_intp, cross_terms, cross_terms_V, rholms, guess_snr, rest=factored_likelihood.PrecomputeLikelihoodTerms( fiducial_epoch, t_window, P, data_dict, psd_dict, opts.l_max, fmax, False, inv_spec_trunc_Q, T_spec, @@ -1549,6 +1763,157 @@ def analyze_event(P_list, indx_event, data_dict, psd_dict, fmax, opts,inv_spec_t ctVArrayDict[det] = cupy.asarray(ctVArrayDict[det]) epochDict[det] = cupy.asarray(epochDict[det]) + def _cal_error_probe(n_cal_now, n_start=256, n_cap=None, rel_tol=0.1): + """Estimate (sigma_lnZ_cal, neff_cal, n_used, dist_mode): the calibration + MC error of lnZ and the effective cal draw count, from per-realization + responsibilities on an extrinsic batch drawn from the RUN'S priors. + + * Distance comes from the run's distance prior -- the sampler's own + prior_pdf when distance is sampled (incl. cosmo/redshift variants, via + a uniform proposal + importance weight), the --d-prior pdf when distance + marginalization is active, or the PINNED value (with a warning: at fixed + distance the distance/amplitude degeneracy cannot absorb amplitude-like + cal perturbations, so the estimate is conservative). + * The batch is ADAPTIVE: doubled until sigma stabilizes (rel_tol) with + adequate weight support, up to n_cap. + Responsibilities are ~extrinsic-independent so modest batches converge.""" + import RIFT.calmarg.adaptive as _adapt + from scipy.special import logsumexp as _lse + _rng = np.random.default_rng() # fresh randomness: this is a diagnostic + if n_cap is None: + n_cap = max(int(opts.calibration_mc_error_extrinsic or 0), n_start) + _warned = [] + def _draw_dist(n): + if 'distance' in pinned_params: + if not _warned: + print(" [calmarg error] WARNING: distance is PINNED -- probe runs at the fixed value; without the distance/amplitude degeneracy the cal error estimate is conservative (an upper bound).") + _warned.append(1) + return np.full(n, float(pinned_params['distance'])), np.zeros(n), 'pinned' + if opts.distance_marginalization: + # distance is integrated on-board against the lookup-table prior; + # the probe varies it explicitly, drawing per --d-prior. + if opts.d_prior == 'pseudo_cosmo': + d = _rng.uniform(dmin, dmax, n) + _nm = priors_utils.dist_prior_pseudo_cosmo_eval_norm(dmin, dmax) + corr = np.log(np.clip(np.asarray(priors_utils.dist_prior_pseudo_cosmo(d, nm=_nm, xpy=np), dtype=float), 1e-300, None)) + return d, corr, 'pseudo_cosmo' + # Euclidean: exact inverse-CDF draw from the d^2 prior + u = _rng.uniform(0, 1, n) + d = (dmin**3 + u*(dmax**3 - dmin**3))**(1./3) + return d, np.zeros(n), 'Euclidean' + # distance is a sampler dimension: uniform proposal over its range, + # importance-weighted by the run's OWN prior_pdf (cosmo/redshift safe); + # redshift_to_distance maps the sampled coordinate to luminosity distance. + lo, hi = float(sampler.llim['distance']), float(sampler.rlim['distance']) + x = _rng.uniform(lo, hi, n) + pw = np.asarray(identity_convert(sampler.prior_pdf['distance'](identity_convert_togpu(x))), dtype=float) + corr = np.log(np.clip(pw, 1e-300, None)) + d = np.asarray(redshift_to_distance(x), dtype=float) + return d, corr, 'sampler prior ({})'.format(opts.d_prior) + _tv = xpy_default.linspace(-t_ref_wind, t_ref_wind, int((t_ref_wind)*2/P.deltaT)) + _calw_np = np.zeros(n_cal_now) if calibration_log_weights is None else np.asarray(identity_convert(calibration_log_weights), dtype=float)[:n_cal_now] + comp_list = []; corr_list = [] + sigma = None; neff_cal = None; sigma_prev = None + n_batch = int(n_start); n_tot = 0; dist_mode = None + while True: + P.phi = xpy_default.asarray(_rng.uniform(0, 2*np.pi, n_batch), dtype=np.float64) + P.theta = xpy_default.asarray(np.arcsin(_rng.uniform(-1, 1, n_batch)), dtype=np.float64) + P.incl = xpy_default.asarray(np.arccos(_rng.uniform(-1, 1, n_batch)), dtype=np.float64) + P.psi = xpy_default.asarray(_rng.uniform(0, np.pi, n_batch), dtype=np.float64) + P.phiref = xpy_default.asarray(_rng.uniform(0, 2*np.pi, n_batch), dtype=np.float64) + P.tref = float(fiducial_epoch) + _d, _corr, dist_mode = _draw_dist(n_batch) + P.dist = xpy_default.asarray(_d*1.e6*lalsimutils.lsu_PC, dtype=np.float64) + _comp = factored_likelihood.DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop( + _tv, P, lookupNKDict, rholmArrayDict, ctUArrayDict, ctVArrayDict, epochDict, + Lmax=opts.l_max, xpy=xpy_default, n_cal=n_cal_now, cal_method='loop', + return_cal_components=True) + comp_list.append(np.atleast_2d(np.asarray(identity_convert(_comp), dtype=float))) + corr_list.append(_corr) + comp_all = np.vstack(comp_list); corr_all = np.concatenate(corr_list) + slw = _lse(comp_all + _calw_np[None, :], axis=1) + corr_all + sigma, neff_cal, _a = _adapt.cal_mc_error_from_components( + comp_all, cal_log_weights=_calw_np, sample_log_weights=slw) + n_tot += n_batch + kish = float(np.exp(2*_lse(slw) - _lse(2*slw))) + if n_tot >= n_cap: + break + if (sigma_prev is not None) and (abs(sigma - sigma_prev) <= rel_tol*max(sigma, 0.02)) and kish >= 32: + break + sigma_prev = sigma + n_batch = min(n_tot, n_cap - n_tot) # double the total each pass + return float(sigma), float(neff_cal), int(n_tot), dist_mode + + # ---- ADAPTIVE cal draw count: instead of trusting a hardcoded + # --calibration-n-realizations, probe the effective number of contributing + # draws at THIS intrinsic point and grow the draw set (fresh independent + # draws; incremental precompute of only the new blocks) until the target + # neff_cal is met or the cap is reached. The realization dict and the + # importance-weight/node bookkeeping are extended in place, so later events + # in this job inherit the enlarged set. + if calibration_marginalization and n_cal_for_likelihood > 1 and (not opts.calibration_dump_responsibilities) and opts.calibration_neff_cal_target: + _ncal_cap = int(opts.calibration_n_realizations_max) if opts.calibration_n_realizations_max else 8*int(opts.calibration_n_realizations) + while True: + _sig0, _neff0, _npr0, _dmode0 = _cal_error_probe(n_cal_for_likelihood, n_start=256, n_cap=512) + print(" [calmarg adapt] n_cal={} : cal n_eff ~ {:.1f} (target {:g}), sigma_cal ~ {:.3f} (probe {} pts, distance: {})".format( + n_cal_for_likelihood, _neff0, opts.calibration_neff_cal_target, _sig0, _npr0, _dmode0)) + if _neff0 >= opts.calibration_neff_cal_target: + break + if n_cal_for_likelihood >= _ncal_cap: + print(" [calmarg adapt] WARNING: cal n_eff {:.1f} below target at the n_cal cap {} -- proceeding; the reported sigma will carry the (large) cal term.".format(_neff0, _ncal_cap)) + break + _n_more = min(n_cal_for_likelihood, _ncal_cap - n_cal_for_likelihood) + print(" [calmarg adapt] growing cal draw set by {} -> {} (incremental precompute)".format(_n_more, n_cal_for_likelihood + _n_more)) + _new_real = _draw_more_calibration_draws(_n_more, psd_dict) + _ek_more = dict(extra_kwargs); _ek_more['calibration_realizations'] = _new_real + _intp_more, _ct_more, _ctV_more, rholms_more, _snr_more, _rest_more = factored_likelihood.PrecomputeLikelihoodTerms( + fiducial_epoch, t_window, P, data_dict, psd_dict, opts.l_max, fmax, + False, inv_spec_trunc_Q, T_spec, + ignore_threshold=ignore_threshold, + NR_group=NR_template_group,NR_param=NR_template_param, + use_gwsignal=opts.use_gwsignal, + use_gwsignal_approx=opts.approximant, + use_external_EOB=opts.use_external_EOB,nr_lookup=opts.nr_lookup,nr_lookup_valid_groups=opts.nr_lookup_group,perturbative_extraction=opts.nr_perturbative_extraction,perturbative_extraction_full=opts.nr_perturbative_extraction_full,use_provided_strain=opts.nr_use_provided_strain,hybrid_use=opts.nr_hybrid_use,hybrid_method=opts.nr_hybrid_method,ROM_group=opts.rom_group,ROM_param=opts.rom_param,ROM_use_basis=opts.rom_use_basis,verbose=opts.verbose,quiet=not opts.verbose,ROM_limit_basis_size=opts.rom_limit_basis_size_to,no_memory=opts.no_memory,skip_interpolation=opts.vectorized, extra_waveform_kwargs=extra_waveform_kwargs,**_ek_more) + for det in rholms_more.keys(): + _,_,_,_,_, _rholmArray_more, _, _ = factored_likelihood.PackLikelihoodDataStructuresAsArrays( + rholms_more[det].keys(), _intp_more[det], rholms_more[det], _ct_more[det], _ctV_more[det]) + if opts.gpu and (not xpy_default is np): + _rholmArray_more = cupy.asarray(_rholmArray_more) + rholmArrayDict[det] = xpy_default.concatenate([rholmArrayDict[det], _rholmArray_more], axis=-1) + n_cal_for_likelihood += _n_more + opts.calibration_n_realizations = n_cal_for_likelihood # later events start consistent with the extended dict + + if opts.calibration_dump_responsibilities and calibration_marginalization: + # PILOT: accumulate the per-cal-realization extrinsic-marginalized log L at + # THIS intrinsic point. Cal is ~extrinsic-independent, so a uniform-prior + # extrinsic batch gives an unbiased Monte-Carlo estimate of int dOmega L_c + # (the cal posterior responsibility). Uses the simplest likelihood config + # (default helper, loop, fiducial distance) -- only the SHAPE in cal-node + # space matters, and the runtime importance weights keep the production + # marginalization unbiased regardless of pilot quality. + _Next = int(opts.calibration_pilot_extrinsic or 256) + _rng_ext = np.random.default_rng((getattr(opts,'seed',0) or 0) + 101 + int(indx_event)) + P.phi = xpy_default.asarray(_rng_ext.uniform(0, 2*np.pi, _Next), dtype=np.float64) + P.theta = xpy_default.asarray(np.arcsin(_rng_ext.uniform(-1, 1, _Next)), dtype=np.float64) # uniform in sin(dec) + P.incl = xpy_default.asarray(np.arccos(_rng_ext.uniform(-1, 1, _Next)), dtype=np.float64) + P.psi = xpy_default.asarray(_rng_ext.uniform(0, np.pi, _Next), dtype=np.float64) + P.phiref = xpy_default.asarray(_rng_ext.uniform(0, 2*np.pi, _Next), dtype=np.float64) + P.tref = float(fiducial_epoch) + P.dist = xpy_default.asarray(np.full(_Next, factored_likelihood.distMpcRef)*1.e6*lalsimutils.lsu_PC, dtype=np.float64) + _tvals = xpy_default.linspace(-t_ref_wind, t_ref_wind, int((t_ref_wind)*2/P.deltaT)) + _comp = factored_likelihood.DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop( + _tvals, P, lookupNKDict, rholmArrayDict, ctUArrayDict, ctVArrayDict, epochDict, + Lmax=opts.l_max, xpy=xpy_default, n_cal=n_cal_for_likelihood, cal_method='loop', + return_cal_components=True) + _comp = np.asarray(identity_convert(_comp)) # (Next, n_cal) -> CPU + from scipy.special import logsumexp as _logsumexp + _calpilot_logresp_list.append(_logsumexp(_comp, axis=0) - np.log(_Next)) + print(" pilot point {}: accumulated cal responsibilities ({} realizations, {} extrinsic)".format( + int(indx_event), n_cal_for_likelihood, _Next)) + # PILOT is cheap: skip the full extrinsic sampler integration -- we only needed + # the precompute + this small per-realization eval. Return a harmless lnL. + return 0.0 + # Likelihood @@ -1567,7 +1932,7 @@ def analyze_event(P_list, indx_event, data_dict, psd_dict, fmax, opts,inv_spec_t distance = redshift_to_distance(distance) # use EXTREMELY many bits - lnL = numpy.zeros(right_ascension.shape,dtype=numpy.float128) + lnL = numpy.zeros(right_ascension.shape,dtype=RiftFloat) i = 0 for ph, th, tr, phr, ic, ps, di in zip(right_ascension, dec, t_ref, phi_orb, incl, psi, distance): @@ -1600,7 +1965,7 @@ def analyze_event(P_list, indx_event, data_dict, psd_dict, fmax, opts,inv_spec_t distance = redshift_to_distance(distance) # use EXTREMELY many bits - lnL = numpy.zeros(right_ascension.shape,dtype=numpy.float128) + lnL = numpy.zeros(right_ascension.shape,dtype=RiftFloat) i = 0 tvals = numpy.linspace(-t_ref_wind,t_ref_wind,int((t_ref_wind)*2/P.deltaT)) # choose an array at the target sampling rate. P is inherited globally @@ -1643,7 +2008,7 @@ def analyze_event(P_list, indx_event, data_dict, psd_dict, fmax, opts,inv_spec_t distance = redshift_to_distance(distance) # use EXTREMELY many bits - lnL = numpy.zeros(right_ascension.shape,dtype=numpy.float128) + lnL = numpy.zeros(right_ascension.shape,dtype=RiftFloat) P.phi = right_ascension.astype(float) # cast to float P.theta = dec #declination.astype(float) P.tref = float(fiducial_epoch) @@ -1682,22 +2047,26 @@ def analyze_event(P_list, indx_event, data_dict, psd_dict, fmax, opts,inv_spec_t psi, distance): # global nEvals tvals = xpy_default.linspace(-t_ref_wind,t_ref_wind,int((t_ref_wind)*2/P.deltaT)) # choose an array at the target sampling rate. P is inherited globally - P.phi = xpy_asarray_already(right_ascension) # cast to float + # Use xpy_default.asarray (not the passthrough xpy_asarray_already): some + # samplers (e.g. AV) hand back numpy arrays, so on GPU we must convert + # them to cupy. asarray is a no-op for already-on-device arrays. This + # mirrors the distance-marginalization likelihood_function below. + P.phi = xpy_default.asarray(right_ascension, dtype=np.float64) if opts.declination_cosine_sampler: - P.theta = numpy.pi/2 - xpy_default.arccos(xpy_asarray_already(declination)) + P.theta = numpy.pi/2 - xpy_default.arccos(xpy_default.asarray(declination, dtype=np.float64)) else: - P.theta = xpy_asarray_already(declination) + P.theta = xpy_default.asarray(declination, dtype=np.float64) P.tref = float(fiducial_epoch) - P.phiref = xpy_asarray_already(phi_orb) + P.phiref = xpy_default.asarray(phi_orb, dtype=np.float64) if opts.inclination_cosine_sampler: - P.incl = xpy_default.arccos(xpy_asarray_already(inclination)) + P.incl = xpy_default.arccos(xpy_default.asarray(inclination, dtype=np.float64)) else: - P.incl = xpy_asarray_already(inclination) + P.incl = xpy_default.asarray(inclination, dtype=np.float64) if opts.d_prior_redshift: distance = redshift_to_distance(distance) - P.psi = xpy_asarray_already(psi) - P.dist = xpy_asarray_already(distance* 1.e6 * lalsimutils.lsu_PC) # luminosity distance + P.psi = xpy_default.asarray(psi, dtype=np.float64) + P.dist = xpy_default.asarray(distance* 1.e6 * lalsimutils.lsu_PC, dtype=np.float64) # luminosity distance # rotate sky if needed if opts.internal_sky_network_coordinates: @@ -1716,7 +2085,8 @@ def analyze_event(P_list, indx_event, data_dict, psd_dict, fmax, opts,inv_spec_t lnL = factored_likelihood.DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop(tvals, - P, lookupNKDict, rholmArrayDict, ctUArrayDict, ctVArrayDict,epochDict,Lmax=opts.l_max,xpy=xpy_default) + P, lookupNKDict, rholmArrayDict, ctUArrayDict, ctVArrayDict,epochDict,Lmax=opts.l_max,xpy=xpy_default,n_cal=n_cal_for_likelihood, + cal_method=('fused' if use_fused_calmarg else 'loop'), cal_log_weights=calibration_log_weights) # non-distmarg: default-helper fused kernel (cal_distmarg=None) # nEvals +=len(right_ascension) if supplemental_ln_likelihood: lnL += supplemental_ln_likelihood(P.phi, P.theta, P.phiref ,P.incl, P.psi, P.dist,xpy=xpy_default) # use these variables so they are already float-type @@ -1775,6 +2145,20 @@ def analyze_event(P_list, indx_event, data_dict, psd_dict, fmax, opts,inv_spec_t lnI[in_bounds] = intp(s[in_bounds], t[in_bounds]) return exponent_max(x0, rho_sq) + lnI + # Opt-in: package the distmarg table for the fused kernel (Option C). + # Only used (below) at the non-phase-marg distmarg call site; the loop + # method (Option B) remains the default and the fallback. Works on GPU + # and CPU (the fused path has a numpy backend), so no GPU gate here. + cal_distmarg_dict = None + if use_fused_calmarg: + cal_distmarg_dict = dict( + lnI_array=lnI_array, + s0=float(s_array[0]), ds=float(s_array[1] - s_array[0]), + smin=float(smin), smax=float(smax), + t0=float(t_array[0]), dt=float(t_array[1] - t_array[0]), tmax=float(tmax), + xmin=float(xmin), xmax=float(xmax), + sqrt_bmax=float(sqrt_bmax), bref=float(bref)) + if lookup_table["phase_marginalization"]: print( " Using direct phase marginalization ") @@ -1819,7 +2203,8 @@ def analyze_event(P_list, indx_event, data_dict, psd_dict, fmax, opts,inv_spec_t P.phiref = phi_orb_true lnL = factored_likelihood.DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop(tvals, - P, lookupNKDict, rholmArrayDict, ctUArrayDict, ctVArrayDict,epochDict,Lmax=opts.l_max,xpy=xpy_default, loglikelihood=distmarg_loglikelihood, phase_marginalization=True) + P, lookupNKDict, rholmArrayDict, ctUArrayDict, ctVArrayDict,epochDict,Lmax=opts.l_max,xpy=xpy_default, loglikelihood=distmarg_loglikelihood, phase_marginalization=True,n_cal=n_cal_for_likelihood, + cal_method=('fused' if cal_distmarg_dict is not None else 'loop'), cal_distmarg=cal_distmarg_dict, cal_log_weights=calibration_log_weights) # nEvals +=len(right_ascension) if supplemental_ln_likelihood: lnL += supplemental_ln_likelihood(P.phi, P.theta, P.phiref ,P.incl, P.psi, 0,xpy=xpy_default) # Same API @@ -1862,7 +2247,8 @@ def analyze_event(P_list, indx_event, data_dict, psd_dict, fmax, opts,inv_spec_t P.phiref = phi_orb_true lnL = factored_likelihood.DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop(tvals, - P, lookupNKDict, rholmArrayDict, ctUArrayDict, ctVArrayDict,epochDict,Lmax=opts.l_max,xpy=xpy_default, loglikelihood=distmarg_loglikelihood) + P, lookupNKDict, rholmArrayDict, ctUArrayDict, ctVArrayDict,epochDict,Lmax=opts.l_max,xpy=xpy_default, loglikelihood=distmarg_loglikelihood,n_cal=n_cal_for_likelihood, + cal_method=('fused' if cal_distmarg_dict is not None else 'loop'), cal_distmarg=cal_distmarg_dict, cal_log_weights=calibration_log_weights) # nEvals +=len(right_ascension) if supplemental_ln_likelihood: lnL += supplemental_ln_likelihood(P.phi, P.theta, P.phiref ,P.incl, P.psi, 0,xpy=xpy_default) # Same API @@ -1882,7 +2268,7 @@ def analyze_event(P_list, indx_event, data_dict, psd_dict, fmax, opts,inv_spec_t if opts.d_prior_redshift: distance = redshift_to_distance(distance) - lnL = numpy.zeros(len(right_ascension),dtype=numpy.float128) + lnL = numpy.zeros(len(right_ascension),dtype=RiftFloat) # i = 0 tvals = numpy.linspace(-t_ref_wind,t_ref_wind,int((t_ref_wind)*2/P.deltaT)) # choose an array at the target sampling rate. P is inherited globally @@ -1954,6 +2340,27 @@ def analyze_event(P_list, indx_event, data_dict, psd_dict, fmax, opts,inv_spec_t lnL_oracles = np.zeros(opts.n_chunk) sampler.update_sampling_prior(lnL_oracles, opts.n_chunk, external_rvs=rvs_train,log_scale_weights=True,floor_integrated_probability=opts.adapt_floor_level) + # Optional ZERO-CAL burn-in (generally useful; see RIFT/calmarg/DESIGN_adaptive_driver.md). + # Adapt the extrinsic sampler cheaply on the n_cal=1 baseline first, then run the full + # cal-marginalized integration reusing the adapted proposal. The likelihood closures + # read the enclosing n_cal_for_likelihood, so toggling it to 1 makes this same + # like_to_integrate evaluate the fast baseline. Correctness is preserved regardless of + # whether the sampler retains adaptation across the two integrate() calls -- worst case + # the burn-in is simply wasted; the production integral below is always the full cal one. + if opts.calibration_burn_in_neff and calibration_marginalization and n_cal_for_likelihood > 1: + _ncal_full = n_cal_for_likelihood + n_cal_for_likelihood = 1 + _burn_pinned = dict(pinned_params) + _burn_pinned['neff'] = float(opts.calibration_burn_in_neff) + _burn_pinned['nmax'] = int(opts.calibration_burn_in_nmax) if opts.calibration_burn_in_nmax else int(pinned_params.get('nmax', n_max)) + print(" [calmarg burn-in] adapting extrinsic sampler on ZERO-CAL likelihood -> neff>={:g} (nmax cap {})".format(_burn_pinned['neff'], _burn_pinned['nmax'])) + try: + _b = sampler.integrate(like_to_integrate, *unpinned_params, **_burn_pinned) + print(" [calmarg burn-in] done (burn-in neff ~ {}); switching to full cal marginalization".format(_b[2] if _b and len(_b) > 2 else '?')) + except Exception as _eb: + print(" [calmarg burn-in] integrate failed ({}); proceeding to production".format(_eb)) + n_cal_for_likelihood = _ncal_full # restore: production uses the full cal set + res, var, neff, dict_return = sampler.integrate(like_to_integrate, *unpinned_params, **pinned_params) if not(res): # no resut @@ -1966,6 +2373,70 @@ def analyze_event(P_list, indx_event, data_dict, psd_dict, fmax, opts,inv_spec_t log_res = res sqrt_var_over_res = numpy.exp(var/2 - log_res) + # Calibration MC error budget. The sampler's `var` is the EXTRINSIC sampling + # variance with the cal draw set held FIXED -- it is structurally blind to the + # Monte-Carlo error of the (1/n_cal) sum over realizations, which dominates + # whenever the cal n_eff is small (high SNR and/or broad envelopes). Estimate + # that term with the adaptive probe (extrinsic batch from the RUN'S priors, + # incl. its distance prior; see _cal_error_probe) and add it IN QUADRATURE to + # the reported sigma. Never fatal. + neff_cal = None; sigma_lnZ_cal = None + if calibration_marginalization and n_cal_for_likelihood > 1 and opts.vectorized and opts.calibration_mc_error_extrinsic: + try: + sigma_lnZ_cal, neff_cal, _npr_err, _dmode_err = _cal_error_probe(n_cal_for_likelihood) + _sigma_ext = float(sqrt_var_over_res) + sqrt_var_over_res = numpy.sqrt(_sigma_ext**2 + sigma_lnZ_cal**2) + print(" [calmarg error] sigma_lnZ: extrinsic {:.4f} (+) cal {:.4f} -> total {:.4f} ; cal n_eff {:.1f} / {} (probe {} pts, distance: {})".format( + _sigma_ext, sigma_lnZ_cal, float(sqrt_var_over_res), neff_cal, n_cal_for_likelihood, _npr_err, _dmode_err)) + if neff_cal < 10: + print(" [calmarg error] WARNING: cal n_eff < 10: the marginalization is dominated by a few draws and the quoted sigma is a LOWER BOUND. Increase --calibration-n-realizations / the adaptive cap (--calibration-n-realizations-max).") + except Exception as _e_cme: + print(" WARNING: calibration MC error estimate failed ({}); reported sigma is extrinsic-only.".format(_e_cme)) + + # Extrinsic handoff: after the integration, fit the run's extrinsic POSTERIOR to a + # per-group GMM and write it as a breadcrumb, so a later iteration can seed its extrinsic + # sampler (--extrinsic-proposal-breadcrumb). The extrinsic posterior barely moves + # iteration-to-iteration, so this lets the next run start on the answer. Wrapped so a + # harvest/fit failure can never break a production integration. + if opts.extrinsic_proposal_output: + try: + import RIFT.calmarg.extrinsic_handoff as _ehmod, RIFT.calmarg.breadcrumbs as _ebcmod + _rvs = sampler._rvs + # TRUE importance log-weight = lnL + ln(prior) - ln(sampling_prior). Build it from the + # raw, UNTEMPERED components and prefer them over any stored 'log_weights': the GPU/AV + # sampler (mcsamplerGPU) stores log_weights = tempering_exp*lnL + ln(prior) - ln(s_prior) + # (the adapt-weight-exponent, e.g. 0.1, baked in) -- fitting the GMM to those flattened + # weights places the proposal in the WRONG region. GMM's own _rvs has no tempering. + if 'log_integrand' in _rvs and 'log_joint_prior' in _rvs and 'log_joint_s_prior' in _rvs: + _lw = np.array(_rvs['log_integrand'] + _rvs['log_joint_prior'] - _rvs['log_joint_s_prior'], dtype=float) + elif 'integrand' in _rvs and 'joint_prior' in _rvs and 'joint_s_prior' in _rvs: + _ig = np.asarray(_rvs['integrand'], dtype=float); _jp = np.asarray(_rvs['joint_prior'], dtype=float); _jsp = np.asarray(_rvs['joint_s_prior'], dtype=float) + _keep = (_ig > 0) & (_jp > 0) & (_jsp > 0) + _lw = np.full(len(_ig), -np.inf); _lw[_keep] = np.log(_ig[_keep]) + np.log(_jp[_keep]) - np.log(_jsp[_keep]) + else: + raise Exception("no weights in sampler._rvs (keys={})".format(list(_rvs.keys()))) + # extrinsic samples + bounds for the standard groups that this run actually sampled. + _ext_params = [p for grp in _ehmod.STANDARD_GROUPS for p in grp] + _ext_samples = {p: np.array(_rvs[p], dtype=float).reshape(-1) for p in _ext_params if p in _rvs} + _ext_bounds = {p: (float(sampler.llim[p]), float(sampler.rlim[p])) for p in _ext_samples if p in sampler.llim} + # restrict to finite-weight, fully-bounded samples + _good = np.isfinite(_lw) + for _p in list(_ext_samples): + _good = _good & np.isfinite(_ext_samples[_p]) + _ext_samples = {p: v[_good] for p, v in _ext_samples.items() if p in _ext_bounds} + _ext = _ehmod.fit_extrinsic_proposal(_ext_samples, log_weights=_lw[_good], bounds=_ext_bounds) + # store the true lnL (peak-referenced) + sample count so a downstream consolidation + # can pick the most representative (near-peak / best-converged) proposal to hand on. + _ebcmod.save(opts.extrinsic_proposal_output, extrinsic=_ext, + meta=dict(event=int(indx_event), n_samples=int(_good.sum()), + lnL=float(log_res + manual_avoid_overflow_logarithm), + neff=float(neff), + groups=[g['params'] for g in _ext['groups']])) + print(" Extrinsic proposal WRITTEN to {} ({} groups: {})".format( + opts.extrinsic_proposal_output, len(_ext['groups']), [g['params'] for g in _ext['groups']])) + except Exception as _e_eho: + print(" WARNING: could not write extrinsic proposal to {} ({}); continuing.".format(opts.extrinsic_proposal_output, _e_eho)) + # Report results if opts.output_file and opts.sim_grid: fname_output_txt = opts.output_file +"_"+str(indx_event)+"_" + ".grid" @@ -2013,68 +2484,211 @@ def analyze_event(P_list, indx_event, data_dict, psd_dict, fmax, opts,inv_spec_t else: numpy.savetxt(fname_output_txt, numpy.array([[event_id, m1, m2, P.s1x, P.s1y, P.s1z, P.s2x, P.s2y, P.s2z, P.lambda1, P.lambda2, P.eos_table_index, log_res+manual_avoid_overflow_logarithm, sqrt_var_over_res,sampler.ntotal, neff ]])) #dict_return["convergence_test_results"]["normal_integral]" - # Distance marginal grid. Only if NOT marginalize distance AND use lnL + # Per-intrinsic likelihood-vs-distance grid. Pure extrinsic-marginalized + # likelihood as a function of d_L: divides out the distance sampling + # prior so downstream can re-marginalize with any prior of choice. if opts.output_file and opts.export_marginal_distance_grid and not(opts.distance_marginalization) and opts.internal_use_lnL: + from RIFT.misc.distance_grid import build_distance_grid, save_distance_grid fname_output_dgrid = opts.output_file +"_"+str(indx_event)+"_" + ".dgrid" - npts_dgrid_out = opts.n_eff # target samples - # compute CDF in distance and its inverse - # copy d grid and weights, make CDF - dL = np.array(sampler._rvs["distance"] ) - if 'log_weights' in sampler._rvs: - ln_wts = np.array(sampler._rvs['log_weights']) # assume present - elif 'log_integrand' in sampler._rvs: - ln_wts = np.array(sampler._rvs["log_integrand"] + sampler._rvs["log_joint_prior"] - sampler._rvs["log_joint_s_prior"]) + dL = np.array(sampler._rvs["distance"]) + rvs = sampler._rvs + if 'log_weights' in rvs: + ln_wts = np.array(rvs['log_weights']) + elif 'log_integrand' in rvs: + ln_wts = np.array(rvs['log_integrand'] + rvs['log_joint_prior'] - rvs['log_joint_s_prior']) + elif 'integrand' in rvs and 'joint_prior' in rvs and 'joint_s_prior' in rvs: + # mcsamplerEnsemble / GMM stores raw (non-log) integrand and priors. + # Drop rejected/out-of-support samples (zero integrand or zero prior + # contribution) by setting log weight to -inf. + integrand = np.asarray(rvs['integrand']) + jp = np.asarray(rvs['joint_prior']) + jsp = np.asarray(rvs['joint_s_prior']) + keep = (integrand > 0) & (jp > 0) & (jsp > 0) + ln_wts = np.full(len(integrand), -np.inf) + ln_wts[keep] = np.log(integrand[keep]) + np.log(jp[keep]) - np.log(jsp[keep]) + else: + raise Exception("distance grid export: cannot find weights in sampler._rvs (keys={})".format(list(rvs.keys()))) + # Distance prior at each sample. Use the sampler's stored prior_pdf + # callable; this matches whatever ILE actually integrated against + # (volumetric, pseudo_cosmo, redshift, ...). + prior_pdf_d = sampler.prior_pdf["distance"] + pi_d_samp = np.asarray(prior_pdf_d(dL), dtype=float) + # Guard: prior must be strictly positive at sampled points + pi_d_samp = np.where(pi_d_samp > 0, pi_d_samp, np.finfo(float).tiny) + ln_prior_d_samp = np.log(pi_d_samp) + params_out = { + "m1": P.m1/lal.MSUN_SI, + "m2": P.m2/lal.MSUN_SI, + "s1x": P.s1x, + "s1y": P.s1y, + "s1z": P.s1z, + "s2x": P.s2x, + "s2y": P.s2y, + "s2z": P.s2z, + "lambda1": P.lambda1, + "lambda2": P.lambda2, + "eccentricity": P.eccentricity, + "meanPerAno": P.meanPerAno, + "eos_index": getattr(P, "eos_table_index", 0), + } + dgrid = build_distance_grid( + dL, + ln_wts, + log_res + manual_avoid_overflow_logarithm, + sqrt_var_over_res, + params_out, + ln_prior_d_at_samples=ln_prior_d_samp, + n_grid=opts.n_eff, + ) + save_distance_grid(fname_output_dgrid, dgrid) + + # Plan-B distance slices: K independent fixed-d extrinsic integrals. + # Produces a (K rows x intrinsic cols) table per ILE job; target size + # ~10x .composite when K~=10. See RIFT/misc/distance_slices.py. + if (opts.output_file and opts.export_distance_slices and opts.export_distance_slices > 0 + and not(opts.distance_marginalization) and opts.internal_use_lnL): + from RIFT.misc import distance_slices + fname_output_dslice = opts.output_file + "_" + str(indx_event) + "_" + ".dslice" + K = int(opts.export_distance_slices) + # B2-reweight relies on a healthy main n_eff so that Omega samples + # are a good importance sample at every slice distance. GMM at low + # n_eff biases the reweighting silently; flag it so the user knows + # to switch sampler or raise n-max. + if opts.sampler_method == "GMM" and neff < 50: + print(" WARNING: --export-distance-slices with --sampler-method GMM at main n_eff={:.1f} (<50). ".format(neff) + + "B2-reweight may be biased; prefer --sampler-method AV or raise --n-max.") + dL_samp = np.array(sampler._rvs["distance"]) + # ln(pi_d) at samples uses the actual sampler prior (volumetric or + # pseudo-cosmo, whichever was registered). + prior_pdf_d = sampler.prior_pdf["distance"] + pi_d_samp = np.asarray(prior_pdf_d(dL_samp), float) + pi_d_samp = np.where(pi_d_samp > 0, pi_d_samp, np.finfo(float).tiny) + ln_pi_d_samp = np.log(pi_d_samp) + # ln(q_d) at samples; for the standard ILE path the proposal is the + # normalized sampler.pdf['distance'] divided by sampler._pdf_norm + # (for the basic mcsampler) or applied directly (Ensemble normalizes + # internally). For our purposes the joint_prior/joint_s_prior column + # already encodes the ratio across all dims, so we only need pi_d + # and q_d at the SAMPLES to isolate the Omega-only factor. Compute + # q_d as a normalized 1-D density on the supported range. + try: + q_d_raw = np.asarray(sampler.pdf["distance"](dL_samp), float) + except Exception: + q_d_raw = np.ones_like(dL_samp) + q_d_norm = float(getattr(sampler, "_pdf_norm", {}).get("distance", 1.0)) or 1.0 + q_d_samp = q_d_raw / q_d_norm + q_d_samp = np.where(q_d_samp > 0, q_d_samp, np.finfo(float).tiny) + ln_q_d_samp = np.log(q_d_samp) + # Pick slice centers from the full posterior on d. Recover the + # log-importance weights with the same fallback chain we use for + # .dgrid. + rvs = sampler._rvs + if 'log_weights' in rvs: + ln_w_full = np.array(rvs['log_weights']) + elif 'log_integrand' in rvs: + ln_w_full = np.array(rvs['log_integrand'] + rvs['log_joint_prior'] - rvs['log_joint_s_prior']) else: - raise Exception(" distance grid export: missing type") - indx_sort = np.argsort(dL); dL=dL[indx_sort]; ln_wts = ln_wts[indx_sort]; - from scipy.special import logsumexp - ln_wts += -logsumexp(ln_wts) # normalize - ln_sums = np.log(np.cumsum(np.exp(ln_wts))) #CDF log. - # Some summary statistics, to help later - ln_dL_av = np.average(np.log(dL), weights=np.exp(ln_wts)) - ln_dL_std = np.sqrt(np.average( (np.log(dL) - ln_dL_av)**2, weights=np.exp(ln_wts))) - # problem of CDF sometimes having zero derivative! Fix with small admixture of uniform probability - npts_grid = len(ln_sums) - eps_uniform = 1e-3 - # Check if gradient too small -# ln_sums = np.logaddexp(np.log(1-eps_uniform)+ ln_sums, np.log(eps_uniform) + (ln_sums[--1] + np.log( np.arange(npts_grid) /npts_grid) ) ) - # downselect grid, reduce to reasonalbe size of points points, chosen uniformly more or less - npts_target = opts.n_eff*10 - p_random = np.arange(npts_target)/(npts_target+1) - p_random.sort() - indx_downselect = np.array( list(set( [ np.sum( np.exp(ln_sums) < p ) for p in p_random] )) ) # can include duplicates! Remove! - indx_downselect.sort() - dL = dL[indx_downselect] # Selected d values for grid - ln_sums = ln_sums[indx_downselect] - # Interpolate CDF, then PDF - from scipy.interpolate import PchipInterpolator - intp_cdf = PchipInterpolator(dL, np.exp(ln_sums)) # not necessarily most stable ... - intp_pdf = intp_cdf.derivative() - # dL values to EXPORT can be anything. We don't have to use the same. - dL_new = np.exp( np.random.normal(loc=ln_dL_av, scale=ln_dL_std, size=opts.n_eff ) ) # cover target range, use logarithmic scaling - dL_new = np.minimum( dL_new, np.max(dL)) - dL_new = np.maximum( dL_new, np.min(dL)) - dL = dL_new - pdf_vals = intp_pdf(dL) - indx_ok = pdf_vals > 0; dL=dL[indx_ok]; pdf_vals = pdf_vals[indx_ok] # reject zero probabiliity points. - # compute revised lnL values. Note it is a constant - lnL_export=log_res + manual_avoid_overflow_logarithm + np.log(pdf_vals) - sigmaL = sqrt_var_over_res - # use labelled field names for export. Provide all by default - header = "lnL sigmaL m1 m2 s1x s1y s2z s2x s2y s2z lambda1 lambda2 eccentricity meanPerAno eos_index dist" - header_fields = header.split()[2:-2] # drop first two and last one (unit conversion) - dat = np.zeros( (len(dL), len(header.split()) ) ) - dat[:,0] = lnL_export - dat[:,1] = sigmaL - dat[:,-1] = dL # units are in Mpc by default, or should be - for indx, name in enumerate(header_fields): - fac_here = 1 - if name in ['m1', 'm2']: - fac_here = lal.MSUN_SI - dat[:,2+indx] = getattr(P, name)/fac_here - # Save field - np.savetxt(fname_output_dgrid, dat, header=header) - + integrand = np.asarray(rvs['integrand']) + jp = np.asarray(rvs['joint_prior']); jsp = np.asarray(rvs['joint_s_prior']) + keep = (integrand > 0) & (jp > 0) & (jsp > 0) + ln_w_full = np.full(len(integrand), -np.inf) + ln_w_full[keep] = np.log(integrand[keep]) + np.log(jp[keep]) - np.log(jsp[keep]) + # Split K into core (reweight) and wing (fresh) slices. + n_core = int(opts.n_distance_slice_core) or int(np.ceil(0.6 * K)) + n_wing = int(opts.n_distance_slice_wing) or (K - n_core) + n_core = max(1, min(n_core, K)) + n_wing = max(0, min(n_wing, K - n_core)) + + # Core: importance-reweight at quantile centers of the posterior. + d_core = distance_slices.quantile_slice_centers(dL_samp, ln_w_full, n_core) + lnL_core, sigmaL_core, neff_core, ntotal_core = distance_slices.importance_reweight_slices( + sampler, like_to_integrate, d_core, + ln_prior_d_at_samples=ln_pi_d_samp, + ln_proposal_d_at_samples=ln_q_d_samp, + manual_overflow=manual_avoid_overflow_logarithm, + return_lnL=return_lnL, + ) + ln_pi_d_core = np.log(np.maximum(prior_pdf_d(d_core), np.finfo(float).tiny)) + + if opts.sampler_method == "GMM" and neff < 50: + print(" WARNING: --export-distance-slices with --sampler-method GMM at main n_eff={:.1f} (<50). ".format(neff) + + "B2-reweight may be biased; prefer --sampler-method AV or raise --n-max.") + + # Wings: only run if (a) we asked for any, (b) core suggests a + # real distance posterior shape worth probing. Otherwise the + # likelihood is flat in d and fresh wings are wasted compute. + d_wings = np.array([]) + lnL_wings = np.array([]); sigmaL_wings = np.array([]); neff_wings = np.array([]); ntotal_wings = np.array([], dtype=int) + if n_wing > 0: + if distance_slices.is_uninformative(lnL_core, threshold=opts.distance_slice_skip_threshold): + print(" : peak core lnL < {:.2f} nats (effectively undetected); skipping {} wing fresh integrations".format( + opts.distance_slice_skip_threshold, n_wing)) + else: + # Place wings via the parabolic lnL(1/d) model fit to the core, + # so wing budget concentrates where the likelihood has support. + lnL_peak_core = float(np.nanmax(lnL_core)) if np.any(np.isfinite(lnL_core)) else None + d_wings = distance_slices.pick_wing_centers( + float(sampler.llim["distance"]), + float(sampler.rlim["distance"]), + d_core, n_wing, + lnL_core=lnL_core, lnL_peak=lnL_peak_core, + delta_lnL_target=opts.distance_slice_wing_delta_lnL, + ) + if len(d_wings) == 0: + print(" : no room outside core for wing slices; skipping") + else: + print(" : running {} wing fresh integrations".format(len(d_wings))) + lnL_wings_raw, sigmaL_wings, neff_wings, ntotal_wings = distance_slices.fresh_sample_slices( + sampler, like_to_integrate, d_wings, + n_max=int(opts.distance_slice_wing_nmax), + n_eff_target=int(opts.distance_slice_wing_neff), + return_lnL=return_lnL, + ) + # fresh_sample_slices returns ln integral of L_with_overflow; + # restore the overflow scale so wing lnL is on the same axis + # as the core slice (and as log_res). + lnL_wings = lnL_wings_raw + manual_avoid_overflow_logarithm + + # Combine core + wings, sort by distance. + d_all = np.concatenate([np.asarray(d_core, float), np.asarray(d_wings, float)]) + lnL_all = np.concatenate([lnL_core, lnL_wings]) + sigmaL_all = np.concatenate([sigmaL_core, sigmaL_wings]) + neff_all = np.concatenate([neff_core, neff_wings]) + ntotal_all = np.concatenate([ + np.full(len(d_core), ntotal_core, dtype=int), + np.asarray(ntotal_wings, dtype=int), + ]) + method_all = np.concatenate([ + np.full(len(d_core), distance_slices.METHOD_REWEIGHT, dtype=int), + np.full(len(d_wings), distance_slices.METHOD_FRESH, dtype=int), + ]) + ln_pi_d_all = np.log(np.maximum(prior_pdf_d(d_all), np.finfo(float).tiny)) + order = np.argsort(d_all) + + try: + params_out # noqa: F823 + except NameError: + params_out = { + "m1": P.m1/lal.MSUN_SI, "m2": P.m2/lal.MSUN_SI, + "s1x": P.s1x, "s1y": P.s1y, "s1z": P.s1z, + "s2x": P.s2x, "s2y": P.s2y, "s2z": P.s2z, + "lambda1": P.lambda1, "lambda2": P.lambda2, + "eccentricity": P.eccentricity, "meanPerAno": P.meanPerAno, + "eos_index": getattr(P, "eos_table_index", 0), + } + # Pass per-row method (build_distance_slice_table accepts a scalar + # method; we extend by writing the method field directly after). + slice_table = distance_slices.build_distance_slice_table( + d_all[order], lnL_all[order], sigmaL_all[order], neff_all[order], + 0, distance_slices.METHOD_REWEIGHT, params_out, + ln_prior_d_at_slices=ln_pi_d_all[order], + ) + slice_table["ntotal"] = ntotal_all[order].astype(float) + slice_table["method"] = method_all[order].astype(float) + distance_slices.save_distance_slice_table(fname_output_dslice, slice_table) + print(" : wrote distance slices to {} ({} core + {} wings)".format( + fname_output_dslice, len(d_core), len(d_wings))) + # Comprehensive output (not yet provided) # Convert declination, inclination parameters in sampler if needed if opts.save_samples and opts.output_file: @@ -2153,9 +2767,49 @@ def analyze_event(P_list, indx_event, data_dict, psd_dict, fmax, opts,inv_spec_t samples["alpha3"] = numpy.zeros(samples["psi"].shape) if opts.resample_time_marginalization: - samples = resample_samples(samples,lookupNKDict, rholmArrayDict, ctUArrayDict, ctVArrayDict,epochDict) + samples = resample_samples(samples,lookupNKDict, rholmArrayDict, ctUArrayDict, ctVArrayDict,epochDict, n_cal=n_cal_for_likelihood, cal_log_weights=calibration_log_weights) samples["loglikelihood" ] = samples["lnL_raw"] # export the non-time-marginalized likelihood, if we are in the final stages # print(samples['t_ref'] - fiducial_epoch, len(samples['t_ref'])) + # Recovered CALIBRATION posterior (opt-in): for each fair-draw sample, draw ONE cal + # realization in proportion to its posterior weight (per-realization L_c * importance + # weight w_c) and write a SELF-CONTAINED sibling __cal.dat with the FULL + # draw -- intrinsic + extrinsic + the drawn realization's spline nodes as labeled + # cal__amp_/cal__phase_ columns. (The main XML/.dat schema cannot carry + # arbitrary columns, so the cal posterior rides this sibling file, row-aligned.) + _cal_nodes = calibration_nodes; _cal_dets = calibration_node_dets; _cal_namp = calibration_n_nodes_amp + if (_cal_nodes is None) and (_calpilot is not None) and (_calpilot.get('nodes') is not None): + _cal_nodes = _calpilot['nodes']; _cal_dets = list(_calpilot['dets']); _cal_namp = int(_calpilot['n_nodes_amp']) + if opts.calibration_export_posterior and calibration_marginalization and n_cal_for_likelihood and n_cal_for_likelihood > 1 and _cal_nodes is not None: + try: + from scipy.special import logsumexp as _logsumexp # 'scipy' is shadowed as a local later in analyze_event + _tv = xpy_default.linspace(-t_ref_wind, t_ref_wind, int((t_ref_wind)*2/P.deltaT)) + # per-realization, time-integrated lnL at each fair-draw sample (P holds the sample + # extrinsic arrays, just set by resample_samples). return_cal_components forces the + # loop method and returns shape (n_samples, n_cal). + _comp = factored_likelihood.DiscreteFactoredLogLikelihoodViaArrayVectorNoLoop(_tv, + P, lookupNKDict, rholmArrayDict, ctUArrayDict, ctVArrayDict, epochDict, + Lmax=opts.l_max, xpy=xpy_default, n_cal=n_cal_for_likelihood, + cal_method='loop', return_cal_components=True) + _comp = np.atleast_2d(np.asarray(identity_convert(_comp), dtype=float)) # (n_samples, n_cal) + _calw = np.zeros(n_cal_for_likelihood) if calibration_log_weights is None else np.asarray(identity_convert(calibration_log_weights), dtype=float) + _logp = _comp + _calw[None, :] # posterior weight per (sample, realization) + _logp = _logp - _logsumexp(_logp, axis=1, keepdims=True) + _wp = np.exp(_logp); _ns = _comp.shape[0] + _idx = np.array([np.random.choice(n_cal_for_likelihood, p=_wp[_i]) for _i in range(_ns)]) + _nodes_drawn = np.asarray(_cal_nodes)[_idx] # (n_samples, 2*namp*ndet) + for _i_det, _ifo in enumerate(_cal_dets): + _base = _i_det * 2 * _cal_namp + for _k in range(_cal_namp): + samples["cal_%s_amp_%d" % (_ifo, _k)] = _nodes_drawn[:, _base + _k] + samples["cal_%s_phase_%d" % (_ifo, _k)] = _nodes_drawn[:, _base + _cal_namp + _k] + _ekeys = sorted(k for k in samples if isinstance(k, str) and np.ndim(samples[k]) == 1 and np.size(samples[k]) == _ns) + _mat = np.column_stack([np.asarray(samples[k], dtype=float) for k in _ekeys]) + _fn_cal = opts.output_file + "_" + str(indx_event) + "_cal.dat" + np.savetxt(_fn_cal, _mat, header=" ".join(_ekeys)) + print(" Calibration posterior (full fair draws + cal nodes) -> {} ; {} samples x {} cols ({} cal cols over {})".format( + _fn_cal, _ns, len(_ekeys), 2*_cal_namp*len(_cal_dets), _cal_dets)) + except Exception as _e_cep: + print(" WARNING: --calibration-export-posterior failed ({}); skipping cal-posterior export.".format(_e_cep)) xmlutils.append_samples_to_xmldoc(xmldoc, samples) # Extra metadata dict_out={"mass1": m1, "mass2": m2, "spin1z": P.s1z, "spin2z": P.s2z, "alpha4": P.eccentricity, "alpha": P.meanPerAno, "alpha5":P.lambda1, "alpha6":P.lambda2, "event_duration": sqrt_var_over_res, "ttotal": sampler.ntotal} @@ -2210,7 +2864,7 @@ def analyze_event(P_list, indx_event, data_dict, psd_dict, fmax, opts,inv_spec_t sampler._rvs["inclination"][indx_guess]/(numpy.pi),\ sampler._rvs["psi"][indx_guess]/numpy.pi,\ sampler._rvs["distance"][indx_guess]/dmax\ - ],dtype=numpy.float128) + ],dtype=RiftFloat) x0 = numpy.fmod(x0,numpy.ones(len(x0))) # had BETTER be defined on this range! # Pick the best starting time. BRUTE FORCE METHOD: use grid def fn_scaled_t(t,x0): @@ -2344,4 +2998,25 @@ for indx in numpy.arange(len(P_list)): # Zero out extrinsic parameters -- these are CUDA-populated / meaningless, but could cause errors if populated P_list[indx].incl = P_list[indx].tref = P_list[indx].dist = P_list[indx].phiref = P_list[indx].psi =P_list[indx].theta = P_list[indx].phi =0 P_list[indx].print_params() - + + +# ---- Calibration pilot output (Option C / adaptive driver) ------------------------- +# Write the per-realization cal responsibilities (accumulated over all analyzed intrinsic +# points) plus the prior cal node draws. util_CalPilotFit.py fits these into a Gaussian +# proposal breadcrumb that seeds the next iteration's wide ILE jobs. +if opts.calibration_dump_responsibilities and (_calpilot is not None) and len(_calpilot_logresp_list): + from scipy.special import logsumexp as _logsumexp + _allp = np.array(_calpilot_logresp_list) # (n_points, n_cal) + _logresp = _logsumexp(_allp, axis=0) # sum_points int dOmega L_c (unnormalized) + # fold the importance weight: responsibility for fitting the POSTERIOR is + # log_w + log L (= log prior + log L - log proposal). log_w==0 for prior draws. + _logresp = _logresp + np.asarray(_calpilot.get('log_w', np.zeros_like(_logresp))) + np.savez(opts.calibration_dump_responsibilities, + nodes=_calpilot['nodes'], log_resp=_logresp, + prior_mean=_calpilot['prior_mean'], prior_sigma=_calpilot['prior_sigma'], + node_log_f=_calpilot['node_log_f'], + n_nodes_amp=np.int64(_calpilot['n_nodes_amp']), + dets=np.array(list(_calpilot['dets']), dtype=object)) + print(" Calibration pilot responsibilities written to {} ({} points x {} realizations)".format( + opts.calibration_dump_responsibilities, len(_calpilot_logresp_list), _calpilot['nodes'].shape[0])) + diff --git a/MonteCarloMarginalizeCode/Code/bin/plot_posterior_corner.py b/MonteCarloMarginalizeCode/Code/bin/plot_posterior_corner.py index 66af83e35..f6c2b4666 100755 --- a/MonteCarloMarginalizeCode/Code/bin/plot_posterior_corner.py +++ b/MonteCarloMarginalizeCode/Code/bin/plot_posterior_corner.py @@ -254,6 +254,35 @@ def render_coordinates(coord_names,logparams=[]): parser.add_argument("--no-mod-psi",action="store_true",help="Default is to take psi mod pi. If present, does not do this") parser.add_argument("--downselect-parameter",action='append', help='Name of parameter to be used to eliminate grid points ') parser.add_argument("--downselect-parameter-range",action='append',type=str) +# ---- User-supplied coordinate-convert plugin (additive; the hardcoded RIFT +# conversion path is untouched). When --supplementary-coordinate-code is +# omitted these flags are a no-op and plotting behaves byte-identically +# to the legacy tool. When supplied, the plugin runs AFTER the existing +# per-file postprocessing -- it can only ADD columns to the record array, +# never override or shadow a name the RIFT path already produced. See +# RIFT.misc.coordinate_plugin for the plugin contract. +parser.add_argument("--supplementary-coordinate-code", default=None, type=str, + help="Coordinate conversion plugin spec. Accepts the literal 'rift_default', " + "a filesystem path to a .py file, or an importable dotted module name. " + "When set, the plugin is loaded and used to materialize additional " + "named columns on every loaded posterior / composite file -- columns " + "already produced by the RIFT hardcoded path (extract_combination_from_LI " + "etc.) are NOT overridden.") +parser.add_argument("--supplementary-coordinate-function", default=None, type=str, + help="Entry-point callable inside the plugin module. Defaults to 'convert_coordinates'.") +parser.add_argument("--supplementary-coordinate-ini", default=None, type=str, + help="Optional ini file parsed and handed to the plugin's prepare() hook.") +parser.add_argument("--supplementary-coordinate-chart", default=None, type=str, + help="Which chart (coordinate system) defined by the plugin to use. Required only " + "when the plugin defines multiple charts; otherwise auto-resolved.") +parser.add_argument("--supplementary-coordinate-input-parameter", action='append', default=None, + help="Existing posterior/composite column name to feed to the plugin as input. " + "Repeat for each input column. If omitted, the plugin's CHARTS[chart] " + "input_parameters / INPUT_PARAMETERS attribute is used.") +parser.add_argument("--supplementary-coordinate-output-parameter", action='append', default=None, + help="Name of a column the plugin should produce and add to the record arrays. " + "Repeat for each output column. If omitted, the plugin's CHARTS[chart] " + "parameters / OUTPUT_PARAMETERS attribute is used.") parser.add_argument("--verbose",action='store_true',help='print matplotlibrc data') opts= parser.parse_args() @@ -304,7 +333,131 @@ def render_coordinates(coord_names,logparams=[]): downselect_dict[dlist[indx]] = dlist_ranges[indx] if opts.downselect_parameter: print("Parameter downselect " , downselect_dict) - + + +# --------------------------------------------------------------------------- +# Optional coordinate-convert plugin. +# +# Loaded ONCE up front; the converter and the input/output name lists are +# captured as module-level closures so the per-file materialize step below +# is cheap. Skipped entirely when --supplementary-coordinate-code is unset, +# so the legacy invocation is byte-identical to the pre-plugin tool. +# +# Design note (don't break the hardcoded RIFT path): the plugin only ever +# ADDS columns to a record array. The `_materialize_plugin_columns` +# helper skips any output name that already exists in samples.dtype.names, +# so the converter cannot shadow a parameter produced by +# `extract_combination_from_LI` or by the file-load hot loop's +# `add_field`-based postprocessing. The standard `samples[param] ... else +# extract_combination_from_LI(...)` fallback at the plot-data extraction +# sites just transparently finds the new columns when the user asks for +# them. +# --------------------------------------------------------------------------- +_coord_plugin_converter = None +_coord_plugin_in_names = [] +_coord_plugin_out_names = [] +if opts.supplementary_coordinate_code: + from RIFT.misc.coordinate_plugin import load_coordinate_converter + + # Reuse the same loader util_ConstructEOSPosterior.py uses, so the + # plugin contract (CHARTS / prepare / register_priors / etc.) is + # exercised consistently across the codebase. prior_map/prior_range_map + # are unused here (plotting doesn't sample anything) -- pass None so the + # loader doesn't try to install priors we'd ignore. + _coord_plugin_converter, _coord_plugin_module = load_coordinate_converter( + spec=opts.supplementary_coordinate_code, + function_name=opts.supplementary_coordinate_function, + ini_path=opts.supplementary_coordinate_ini, + coord_names=opts.supplementary_coordinate_output_parameter, + low_level_coord_names=opts.supplementary_coordinate_input_parameter, + chart=opts.supplementary_coordinate_chart, + opts=opts, + prior_map=None, + prior_range_map=None, + ) + + # Resolve the input/output name lists. Priority: explicit CLI override + # > selected chart's declared names > module-level INPUT/OUTPUT_PARAMETERS. + _chart_spec = ( + getattr(_coord_plugin_module, "CHARTS", {}).get(opts.supplementary_coordinate_chart) + if opts.supplementary_coordinate_chart + else None + ) + if _chart_spec is None: + _charts = getattr(_coord_plugin_module, "CHARTS", None) or {} + if len(_charts) == 1: + _chart_spec = next(iter(_charts.values())) + + _coord_plugin_out_names = list( + opts.supplementary_coordinate_output_parameter + or (_chart_spec.get("parameters") if _chart_spec else None) + or getattr(_coord_plugin_module, "OUTPUT_PARAMETERS", []) + ) + _coord_plugin_in_names = list( + opts.supplementary_coordinate_input_parameter + or (_chart_spec.get("input_parameters") if _chart_spec else None) + or getattr(_coord_plugin_module, "INPUT_PARAMETERS", []) + ) + if not _coord_plugin_out_names or not _coord_plugin_in_names: + raise ValueError( + "Coordinate plugin loaded but the input/output column names " + "could not be determined from CHARTS / INPUT_PARAMETERS / " + "OUTPUT_PARAMETERS. Pass --supplementary-coordinate-input-parameter " + "and --supplementary-coordinate-output-parameter explicitly." + ) + print( + " plot_posterior_corner: coordinate plugin will materialize {} " + "from {} on every loaded posterior / composite file.".format( + _coord_plugin_out_names, _coord_plugin_in_names + ) + ) + + +def _materialize_plugin_columns(samples, source_label=""): + """Return ``samples`` with the plugin's output columns spliced in. + + Pure no-op when no plugin is loaded, when the plugin's required input + columns are missing from ``samples``, or when every output name is + already present (the legacy RIFT path wins by virtue of running first). + """ + if _coord_plugin_converter is None: + return samples + missing_in = [n for n in _coord_plugin_in_names if n not in samples.dtype.names] + if missing_in: + print( + " coordinate plugin: input column(s) {!r} missing from {!r}; " + "skipping conversion for this file.".format(missing_in, source_label or "samples") + ) + return samples + # Names already in samples are NOT overridden -- the RIFT hardcoded path + # (and any per-file postprocessing) wins. This is the only line that + # protects the legacy code path from a misconfigured plugin. + new_names = [n for n in _coord_plugin_out_names if n not in samples.dtype.names] + if not new_names: + return samples + x_in = np.column_stack( + [np.asarray(samples[n], dtype=float) for n in _coord_plugin_in_names] + ) + y = _coord_plugin_converter( + x_in, + coord_names=_coord_plugin_out_names, + low_level_coord_names=_coord_plugin_in_names, + ) + y = np.asarray(y, dtype=float) + if y.shape != (len(samples), len(_coord_plugin_out_names)): + raise ValueError( + "coordinate plugin returned shape {!r}, expected {!r}".format( + y.shape, (len(samples), len(_coord_plugin_out_names)) + ) + ) + out = samples + for name in new_names: + col_indx = _coord_plugin_out_names.index(name) + out = add_field(out, [(name, float)]) + out[name] = y[:, col_indx] + return out + + truth_P_list = None P_ref = None truth_dat = None @@ -481,6 +634,11 @@ def render_coordinates(coord_names,logparams=[]): samples = samples[indx_ok] + # Splice in any user-supplied coordinate-plugin output columns + # AFTER the legacy RIFT postprocessing has finished, never before. + # No-op when --supplementary-coordinate-code is unset. + samples = _materialize_plugin_columns(samples, source_label=fname) + # Save samples posterior_list.append(samples) @@ -658,6 +816,11 @@ def render_coordinates(coord_names,logparams=[]): samples = samples[indx_ok] + # Same plugin hook as the posterior loop above: only ADDS columns, + # never overrides what the composite-file postprocessing produced. + samples = _materialize_plugin_columns(samples, source_label=fname) + samples_orig = _materialize_plugin_columns(samples_orig, source_label=fname + " (pre-lnL-cut)") + composite_list.append(samples) composite_full_list.append(samples_orig) diff --git a/MonteCarloMarginalizeCode/Code/bin/util_CalConsolidate.py b/MonteCarloMarginalizeCode/Code/bin/util_CalConsolidate.py new file mode 100755 index 000000000..d9a214e09 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/bin/util_CalConsolidate.py @@ -0,0 +1,58 @@ +#! /usr/bin/env python +""" +util_CalConsolidate.py + +Consolidation step of the adaptive calibration driver (Option C; see +RIFT/calmarg/DESIGN_adaptive_driver.md). This is the per-iteration BARRIER between +the pilot jobs of iteration N and the wide ILE jobs of iteration N+1. + +Combines several pilot proposal breadcrumbs (one per harvested high-L point / pilot +job) into a single consolidated cal proposal breadcrumb, via a precision-weighted +(moment-matched) combination of the per-pilot Gaussians. The consolidated breadcrumb +is what wide_{N+1} consumes through --calibration-proposal-breadcrumb. + + util_CalConsolidate.py --breadcrumb pilot_a.npz --breadcrumb pilot_b.npz \ + --output-breadcrumb cal_consolidated.npz + +With a single input breadcrumb this is a pass-through (copy), which is the common case +once cal is learned and only one pilot remains active. +""" +from __future__ import division + +import sys +import argparse + +from RIFT.calmarg import pilot, breadcrumbs + + +def main(argv=None): + p = argparse.ArgumentParser() + p.add_argument("--breadcrumb", action="append", required=True, + help="input pilot proposal breadcrumb .npz (repeatable)") + p.add_argument("--output-breadcrumb", required=True, + help="output consolidated proposal breadcrumb .npz") + p.add_argument("--iteration", type=int, default=None, + help="iteration number (recorded in breadcrumb meta)") + opts = p.parse_args(argv) + + if len(opts.breadcrumb) == 1: + # pass-through: re-save with consolidated meta so downstream is uniform + g = breadcrumbs.load(opts.breadcrumb[0]) + meta = dict(g.get("meta", {})); meta["consolidated_from"] = 1 + if opts.iteration is not None: + meta["iteration"] = int(opts.iteration) + breadcrumbs.save(opts.output_breadcrumb, cal=g["cal"], meta=meta) + print(" Consolidation: single pilot -> pass-through to %s" % opts.output_breadcrumb) + return 0 + + out = pilot.consolidate(opts.breadcrumb, out_path=None) + meta = dict(consolidated_from=len(opts.breadcrumb)) + if opts.iteration is not None: + meta["iteration"] = int(opts.iteration) + breadcrumbs.save(opts.output_breadcrumb, cal=out, meta=meta) + print(" Consolidated %d pilot proposals -> %s" % (len(opts.breadcrumb), opts.output_breadcrumb)) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/MonteCarloMarginalizeCode/Code/bin/util_CalHarvestGrid.py b/MonteCarloMarginalizeCode/Code/bin/util_CalHarvestGrid.py new file mode 100755 index 000000000..aa28cffee --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/bin/util_CalHarvestGrid.py @@ -0,0 +1,95 @@ +#! /usr/bin/env python +""" +util_CalHarvestGrid.py + +Harvest the top-fraction (by lnL) intrinsic points from a RIFT *.composite file into a +small sim-xml grid, for the calibration PILOT to analyze (Option C; see +RIFT/calmarg/DESIGN_adaptive_driver.md). + +The cal posterior is ~extrinsic- and ~intrinsic-independent across the high-likelihood +region, so a handful of the best-fit points suffice to learn the cal proposal. This +writes those points as an xml grid that integrate_likelihood_extrinsic_batchmode can +analyze with --calibration-dump-responsibilities. + +Composite column convention (RIFT standard, headerless whitespace): + 0:indx 1:m1 2:m2 3:a1x 4:a1y 5:a1z 6:a2x 7:a2y 8:a2z 9:lnL 10:sigma/lnL 11:ntot ... +(masses in Msun). Override with --lnL-col / --mass-col if your composite differs. + + util_CalHarvestGrid.py --composite consolidated_3.composite \ + --output-grid cal_pilot_grid_3.xml.gz --top-fraction 0.05 --max-points 32 +""" +from __future__ import division + +import sys +import argparse +import numpy as np + +import lal +import RIFT.lalsimutils as lalsimutils + + +def harvest_top_rows(dat, lnL_col=9, top_fraction=0.05, max_points=32): + """Return the rows of `dat` (2D array) with the highest lnL: the top `top_fraction`, + capped at `max_points` (and at least 1).""" + dat = np.atleast_2d(dat) + lnL = dat[:, lnL_col] + n_keep = max(1, int(np.ceil(len(lnL) * top_fraction))) + if max_points: + n_keep = min(n_keep, max_points) + order = np.argsort(lnL)[::-1][:n_keep] + return dat[order] + + +def rows_to_P_list(rows, mass_col=1): + """Build ChooseWaveformParams from composite rows (masses Msun, spins dimensionless).""" + P_list = [] + for r in rows: + P = lalsimutils.ChooseWaveformParams() + P.m1 = float(r[mass_col]) * lal.MSUN_SI + P.m2 = float(r[mass_col + 1]) * lal.MSUN_SI + P.s1x, P.s1y, P.s1z = float(r[mass_col + 2]), float(r[mass_col + 3]), float(r[mass_col + 4]) + P.s2x, P.s2y, P.s2z = float(r[mass_col + 5]), float(r[mass_col + 6]), float(r[mass_col + 7]) + P.fmin = 0.0 + P_list.append(P) + return P_list + + +def main(argv=None): + p = argparse.ArgumentParser() + p.add_argument("--composite", required=True, help="input *.composite file") + p.add_argument("--output-grid", required=True, help="output sim-xml(.gz) grid") + p.add_argument("--top-fraction", type=float, default=0.05) + p.add_argument("--max-points", type=int, default=32, + help="cap on harvested points (the cal posterior is ~the same across " + "the high-L region, so a handful suffice)") + p.add_argument("--lnL-col", type=int, default=9) + p.add_argument("--mass-col", type=int, default=1, + help="column index of m1 (m2..spins follow)") + opts = p.parse_args(argv) + + # Robust read: composites are sometimes ragged (variable trailing columns, or a + # stray non-numeric line). Read only the columns we need (indx..lnL) and skip rows + # that don't parse, rather than failing the whole pilot. + n_need = max(opts.lnL_col, opts.mass_col + 7) + 1 + dat = np.genfromtxt(opts.composite, usecols=range(n_need), invalid_raise=False) + dat = np.atleast_2d(dat) + dat = dat[~np.isnan(dat).any(axis=1)] # drop any rows that failed to parse + if len(dat) == 0: + raise ValueError("no valid numeric rows in composite %s" % opts.composite) + rows = harvest_top_rows(dat, lnL_col=opts.lnL_col, top_fraction=opts.top_fraction, + max_points=opts.max_points) + P_list = rows_to_P_list(rows, mass_col=opts.mass_col) + fname = opts.output_grid + if fname.endswith(".xml.gz"): + fname = fname[:-len(".xml.gz")] + elif fname.endswith(".xml"): + fname = fname[:-len(".xml")] + lalsimutils.ChooseWaveformParams_array_to_xml(P_list, fname=fname) + print(" Harvested %d/%d points (top %.1f%%, lnL in [%.2f, %.2f]) -> %s.xml.gz" + % (len(P_list), len(np.atleast_2d(dat)), 100 * opts.top_fraction, + rows[:, opts.lnL_col].min(), rows[:, opts.lnL_col].max(), fname)) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/MonteCarloMarginalizeCode/Code/bin/util_CalMakePriorBreadcrumb.py b/MonteCarloMarginalizeCode/Code/bin/util_CalMakePriorBreadcrumb.py new file mode 100755 index 000000000..2b8a430ef --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/bin/util_CalMakePriorBreadcrumb.py @@ -0,0 +1,66 @@ +#! /usr/bin/env python +""" +util_CalMakePriorBreadcrumb.py + +Write a VALID "prior" calibration breadcrumb (RIFT.calmarg.breadcrumbs) whose learned proposal +IS the broad prior (proposal == prior). Seeding an ILE run from it +(--calibration-proposal-breadcrumb) therefore draws cal realizations from the prior with ZERO +importance weights -- i.e. it is exactly equivalent to the cold prior draws, but as a file +that LOADS cleanly. + +Use it as the iteration-0 placeholder `cal_consolidated_-1.npz` so that an ILE binary which +does NOT guard against an empty (0-byte) placeholder will not crash on it (EOFError). Newer +util_RIFT_pseudo_pipe.py already writes this automatically; this script lets you (re)generate +the placeholder for an ALREADY-built run directory in place -- no rebuild, no re-run: + + util_CalMakePriorBreadcrumb.py --calibration-envelope-directory rundir/cal_env \\ + --ifo H1 --ifo L1 --ifo V1 --fmin 10 --fmax 2047 --calibration-spline-count 10 \\ + --output rundir/cal_consolidated_-1.npz + +fmin/fmax/spline-count must match the wide-ILE cal settings (so the node dimension lines up: +dim = 2 * spline-count * n_ifo). Exact frequency values are not critical -- iteration 0 is a +cold start refined later -- but the IFO list and spline count MUST match. +""" +from __future__ import print_function + +import sys +import argparse + +import RIFT.calmarg.generate_realizations as genr +import RIFT.calmarg.breadcrumbs as breadcrumbs + + +def main(argv=None): + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + p.add_argument("--calibration-envelope-directory", required=True, + help="Directory with per-IFO envelope files .txt (the cal prior).") + p.add_argument("--ifo", action="append", required=True, + help="Detector (repeatable), IN THE NODE-BLOCK ORDER the wide ILE uses " + "(== the IFO order of the analysis).") + p.add_argument("--fmin", type=float, default=20.0, help="Spline fmin (all IFOs unless --fmin-ifo).") + p.add_argument("--fmin-ifo", action="append", default=[], + help="Per-detector fmin override, IFO=fmin (repeatable).") + p.add_argument("--fmax", type=float, required=True, help="Spline fmax (~ srate/2 - 1).") + p.add_argument("--calibration-spline-count", type=int, default=10, + help="Spline nodes per detector (MUST match the wide ILE --calibration-spline-count).") + p.add_argument("--output", required=True, help="Output breadcrumb path (e.g. cal_consolidated_-1.npz).") + opts = p.parse_args(argv) + + fmin_ifo = {} + for s in opts.fmin_ifo: + k, v = s.split("=") + fmin_ifo[k] = float(v) + + cal = genr.prior_cal_breadcrumb_dict( + opts.calibration_envelope_directory, list(opts.ifo), opts.fmin, opts.fmax, + opts.calibration_spline_count, fmin_ifo=(fmin_ifo or None)) + breadcrumbs.save(opts.output, cal=cal, meta=dict(placeholder=True, iteration=-1, + dets=list(opts.ifo))) + print("util_CalMakePriorBreadcrumb: wrote prior placeholder {} (dim {}, dets {})".format( + opts.output, cal["proposal_mean"].shape[0], list(opts.ifo))) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/MonteCarloMarginalizeCode/Code/bin/util_CalPilotFit.py b/MonteCarloMarginalizeCode/Code/bin/util_CalPilotFit.py new file mode 100755 index 000000000..2afc0de9f --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/bin/util_CalPilotFit.py @@ -0,0 +1,107 @@ +#! /usr/bin/env python +""" +util_CalPilotFit.py + +Pilot fit step of the adaptive calibration driver (Option C; see +RIFT/calmarg/DESIGN_adaptive_driver.md). + +Reads one or more calibration-responsibility dumps produced by +`integrate_likelihood_extrinsic_batchmode --calibration-dump-responsibilities` +(each an .npz with: nodes (n_cal, dim), log_resp (n_cal,), prior_mean, prior_sigma, +node_log_f, n_nodes_amp, dets) and fits a (tempered) Gaussian proposal over the cal +spline nodes, writing it as a breadcrumb (RIFT.calmarg.breadcrumbs) that seeds the +next iteration's wide ILE jobs via --calibration-proposal-breadcrumb. + +Multiple dumps (e.g. several harvested high-L intrinsic points run as separate pilot +jobs) are combined by pooling their responsibilities -- the node draws are identical +across them (the dump uses a fixed seed), so log_resp simply adds in log space. + + util_CalPilotFit.py --dump pilot_0.npz [--dump pilot_1.npz ...] \ + --output-breadcrumb cal_proposal.npz --beta 1.0 +""" +from __future__ import division + +import sys +import argparse +import numpy as np +from scipy.special import logsumexp + +from RIFT.calmarg import adaptive, breadcrumbs + + +def main(argv=None): + p = argparse.ArgumentParser() + p.add_argument("--dump", action="append", required=True, + help="responsibility dump .npz (repeatable; pooled)") + p.add_argument("--output-breadcrumb", required=True, + help="output proposal breadcrumb .npz") + p.add_argument("--beta", type=float, default=None, + help="tempering exponent for the responsibility weights (0= this fraction of n_cal. Guards the high-dim collapse where a " + "single prior draw dominates (neff->1). Default 0.3.") + p.add_argument("--cov-inflate", type=float, default=1.0, + help="multiply the fitted covariance by this factor (safety margin)") + p.add_argument("--iteration", type=int, default=None, + help="iteration number (recorded in breadcrumb meta)") + opts = p.parse_args(argv) + + dumps = [np.load(f, allow_pickle=True) for f in opts.dump] + nodes = dumps[0]["nodes"] + # pool responsibilities across dumps (identical node draws -> add in log space) + log_resp = dumps[0]["log_resp"].astype(float) + for z in dumps[1:]: + assert z["nodes"].shape == nodes.shape, "dump node grids differ; cannot pool" + assert np.allclose(z["nodes"], nodes), "dump node draws differ; cannot pool (re-run pilots with the same --seed)" + log_resp = np.logaddexp(log_resp, z["log_resp"].astype(float)) + + n_cal = nodes.shape[0] + neff = adaptive.neff_from_logweights(log_resp) + print(" Pilot fit: %d realizations, %d dump(s); neff(responsibility)=%.1f/%d" + % (n_cal, len(dumps), neff, n_cal)) + + # Choose the tempering exponent. In high dimensions a cold prior draw can collapse + # to neff~1 (one realization dominates); fitting at beta=1 then gives a degenerate + # (near-singular) proposal. Auto-temper: take the LARGEST beta<=1 whose tempered + # neff reaches target_neff_frac*n_cal, so the fit always uses a healthy sample. + beta = opts.beta + if beta is None: + target = max(1.0, opts.target_neff_frac * n_cal) + if adaptive.neff_from_logweights(log_resp) >= target: + beta = 1.0 + else: + lo, hi = 0.0, 1.0 + for _ in range(40): + mid = 0.5 * (lo + hi) + if adaptive.neff_from_logweights(mid * log_resp) >= target: + lo = mid # mid is healthy -> can we go higher (sharper)? + else: + hi = mid + beta = lo + print(" Auto-tempering: beta=%.3f (target neff>=%.0f; tempered neff=%.1f)" + % (beta, target, adaptive.neff_from_logweights(beta * log_resp))) + + # shrink toward the prior diagonal: in ~60-D cal node space a pilot with neff~tens + # cannot constrain the full covariance, so uninformed directions must default to + # ~prior width (else the proposal collapses to a near-delta and seeded weights blow up) + mean, cov = adaptive.fit_proposal(nodes, log_resp, beta, cov_inflate=opts.cov_inflate, + prior_sigma=dumps[0]["prior_sigma"]) + + cal = dict(proposal_mean=mean, proposal_cov=cov, + prior_mean=dumps[0]["prior_mean"], prior_sigma=dumps[0]["prior_sigma"], + node_log_f=dumps[0]["node_log_f"], n_nodes_amp=int(dumps[0]["n_nodes_amp"]), + dets=[str(x) for x in dumps[0]["dets"]]) + meta = dict(kind="pilot_fit", n_dumps=len(opts.dump), n_cal=int(n_cal), + beta=float(beta), neff_responsibility=float(neff)) + if opts.iteration is not None: + meta["iteration"] = int(opts.iteration) + breadcrumbs.save(opts.output_breadcrumb, cal=cal, meta=meta) + print(" Wrote cal proposal breadcrumb -> %s" % opts.output_breadcrumb) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/MonteCarloMarginalizeCode/Code/bin/util_CalPilotStage.py b/MonteCarloMarginalizeCode/Code/bin/util_CalPilotStage.py new file mode 100755 index 000000000..1baf3ac7d --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/bin/util_CalPilotStage.py @@ -0,0 +1,129 @@ +#! /usr/bin/env python +""" +util_CalPilotStage.py + +One-shot orchestrator for a calibration PILOT stage (Option C; see +RIFT/calmarg/DESIGN_adaptive_driver.md). This is the single executable the DAG runs as +`calpilot_N`, in parallel with iteration N's CIP/puff. It performs, in sequence: + + 1. HARVEST top-fraction high-lnL intrinsic points from iteration N's *.composite + -> a small sim-xml grid (util_CalHarvestGrid.py) + 2. DUMP run ILE on that grid with --calibration-dump-responsibilities (cheap: no + extrinsic sampler), optionally seeded from the previous consolidated + proposal (refinement) (integrate_likelihood_...) + 3. FIT fit a (auto-tempered) Gaussian cal proposal (util_CalPilotFit.py) + 4. CONSOLIDATE -> the proposal breadcrumb that seeds wide_{N+1} (util_CalConsolidate.py) + +The ILE command line is taken from the run's args_ile.txt (the same args the wide jobs +use); pilot-specific flags (--sim-xml, --n-events-to-analyze, --output-file, +--calibration-dump-responsibilities, --calibration-proposal-breadcrumb) are stripped and +re-supplied. Steps 1-4 run as subprocesses so this composes with the existing tools. + + util_CalPilotStage.py --composite consolidated_3.composite --ile-args-file args_ile.txt \ + --iteration 3 --output-breadcrumb cal_consolidated_3.npz \ + [--prev-breadcrumb cal_consolidated_2.npz] [--top-fraction 0.05] [--max-points 32] +""" +from __future__ import division + +import sys +import os +import shlex +import argparse +import subprocess + +# tokens we re-supply ourselves -> strip them (and their value) from the inherited ILE args +_STRIP_OPTS = {"--sim-xml", "--n-events-to-analyze", "--output-file", "--event", + "--calibration-dump-responsibilities", "--calibration-proposal-breadcrumb"} + + +def strip_opts(arg_str, strip=_STRIP_OPTS): + """Remove `--opt value` pairs (and bare flags) for opts in `strip` from an arg string.""" + toks = shlex.split(arg_str) + out, i = [], 0 + while i < len(toks): + t = toks[i] + if t in strip: + i += 1 + # skip a following value if it is not itself an option + if i < len(toks) and not toks[i].startswith("--"): + i += 1 + continue + out.append(t) + i += 1 + return " ".join(out) + + +def _which(name): + from shutil import which + return which(name) or name + + +def main(argv=None): + p = argparse.ArgumentParser() + p.add_argument("--composite", required=True) + p.add_argument("--ile-args-file", required=True, help="args_ile.txt (the wide ILE command)") + p.add_argument("--iteration", type=int, required=True) + p.add_argument("--output-breadcrumb", required=True, help="consolidated proposal for wide_{N+1}") + p.add_argument("--prev-breadcrumb", default=None, help="consolidation_{N-1} (refinement seed)") + p.add_argument("--top-fraction", type=float, default=0.05) + p.add_argument("--max-points", type=int, default=32) + p.add_argument("--beta", type=float, default=None, help="passed to util_CalPilotFit (default auto-temper)") + p.add_argument("--workdir", default=".", help="scratch dir for intermediate products") + p.add_argument("--ile-exe", default=None) + opts = p.parse_args(argv) + + wd = opts.workdir + it = opts.iteration + grid = os.path.join(wd, "cal_pilot_grid_%d.xml.gz" % it) + resp = os.path.join(wd, "cal_pilot_resp_%d.npz" % it) + prop = os.path.join(wd, "cal_proposal_%d.npz" % it) + + # --- 1. harvest ----------------------------------------------------------------- + subprocess.check_call([sys.executable, _which("util_CalHarvestGrid.py"), + "--composite", opts.composite, "--output-grid", grid, + "--top-fraction", str(opts.top_fraction), + "--max-points", str(opts.max_points)]) + + # how many points did we harvest? (drives --n-events-to-analyze) + import RIFT.lalsimutils as lalsimutils + n_pts = len(lalsimutils.xml_to_ChooseWaveformParams_array(grid)) + + # --- 2. ILE dump (cheap: skips the extrinsic sampler) --------------------------- + with open(opts.ile_args_file) as f: + ile_args = f.read().strip() + # args_ile.txt convention: the first token is a placeholder ('X', stripped by + # create_event_parameter_pipeline) -- or, in some hand-written files, the exe path. + # Drop it either way; otherwise it is passed to ILE as a stray positional arg. + toks = shlex.split(ile_args) + ile_exe = opts.ile_exe or _which("integrate_likelihood_extrinsic_batchmode") + if toks and (toks[0] == "X" or "integrate_likelihood" in toks[0]): + rest = " ".join(toks[1:]) + else: + rest = ile_args + rest = strip_opts(rest) + cmd = [ile_exe] + shlex.split(rest) + [ + "--sim-xml", grid, "--n-events-to-analyze", str(n_pts), + "--output-file", os.path.join(wd, "cal_pilot_out_%d" % it), + "--calibration-dump-responsibilities", resp] + if opts.prev_breadcrumb and os.path.exists(opts.prev_breadcrumb): + cmd += ["--calibration-proposal-breadcrumb", opts.prev_breadcrumb] # refinement + print(" [calpilot %d] DUMP: %s" % (it, " ".join(cmd))) + subprocess.check_call(cmd) + + # --- 3. fit ---------------------------------------------------------------------- + fit_cmd = [sys.executable, _which("util_CalPilotFit.py"), "--dump", resp, + "--output-breadcrumb", prop, "--iteration", str(it)] + if opts.beta is not None: + fit_cmd += ["--beta", str(opts.beta)] + subprocess.check_call(fit_cmd) + + # --- 4. consolidate ------------------------------------------------------------- + subprocess.check_call([sys.executable, _which("util_CalConsolidate.py"), + "--breadcrumb", prop, "--output-breadcrumb", opts.output_breadcrumb, + "--iteration", str(it)]) + print(" [calpilot %d] wrote consolidated proposal -> %s" % (it, opts.output_breadcrumb)) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/MonteCarloMarginalizeCode/Code/bin/util_ConsolidateDistanceGrids.py b/MonteCarloMarginalizeCode/Code/bin/util_ConsolidateDistanceGrids.py new file mode 100755 index 000000000..006855947 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/bin/util_ConsolidateDistanceGrids.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python +"""Consolidate per-event .dgrid (Plan A) or .dslice (Plan B) files. + +The ILE extrinsic stage emits one file per intrinsic point (e.g. +``EXTR_out.xml_0_.dgrid``, ``..._1_.dgrid`` ...). Each is a small ASCII +table with a ``# col1 col2 ...`` header line and one row per distance +sample/slice. Downstream tools (notably ``util_ConstructEOSPosterior.py``) +want a single concatenated table with one shared header. + +This script: +- reads each input file, +- verifies the headers match, +- writes one header line followed by the data rows from all files. + +Usage: + util_ConsolidateDistanceGrids.py --output all_dgrid.dat + util_ConsolidateDistanceGrids.py --output all_dgrid.dat --input-glob '*.dgrid' +""" +import argparse +import glob +import os +import sys + + +def _read_split(fname): + """Read a file, return (header_line, [data_lines]). + + ``header_line`` is the first line stripped of a leading ``#`` (or None if + the file has no header). Blank lines and other comment lines are + skipped in the data. + """ + with open(fname, 'r') as f: + lines = f.readlines() + if not lines: + return None, [] + header = None + data = [] + started = False + for line in lines: + s = line.strip() + if not s: + continue + if s.startswith('#'): + if not started and header is None: + header = s.lstrip('#').strip() + # subsequent comments are ignored + continue + started = True + data.append(line.rstrip('\n')) + return header, data + + +def main(argv=None): + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + p.add_argument("inputs", nargs='*', help="input files (.dgrid or .dslice)") + p.add_argument("--input-glob", default=None, + help="glob pattern for inputs (e.g. '*.dgrid')") + p.add_argument("--output", required=True, + help="output consolidated .dat file") + p.add_argument("--allow-empty", action='store_true', + help="exit 0 (writing a header-only output) if no inputs match") + opts = p.parse_args(argv) + + files = list(opts.inputs) + if opts.input_glob: + files += sorted(glob.glob(opts.input_glob)) + # preserve order, drop duplicates + seen = set() + files = [f for f in files if not (f in seen or seen.add(f))] + if not files: + msg = "no input files provided" + if opts.allow_empty: + print("util_ConsolidateDistanceGrids.py: {}; writing empty output".format(msg), + file=sys.stderr) + with open(opts.output, 'w') as f: + pass + return 0 + print("util_ConsolidateDistanceGrids.py: ERROR: {}".format(msg), + file=sys.stderr) + return 2 + + header = None + all_data = [] + n_files_used = 0 + for fname in files: + if not os.path.isfile(fname) or os.path.getsize(fname) == 0: + continue + h, d = _read_split(fname) + if h is None: + print("util_ConsolidateDistanceGrids.py: WARNING: no header in {} " + "(skipping)".format(fname), file=sys.stderr) + continue + if header is None: + header = h + elif h != header: + print("util_ConsolidateDistanceGrids.py: ERROR: header mismatch in {}\n" + " expected: {}\n got: {}".format(fname, header, h), + file=sys.stderr) + return 3 + all_data.extend(d) + n_files_used += 1 + + if header is None: + if opts.allow_empty: + with open(opts.output, 'w') as f: + pass + return 0 + print("util_ConsolidateDistanceGrids.py: ERROR: no usable inputs found", + file=sys.stderr) + return 2 + + with open(opts.output, 'w') as f: + f.write("# " + header + "\n") + for line in all_data: + f.write(line + "\n") + print("util_ConsolidateDistanceGrids.py: wrote {} rows from {} files to {}".format( + len(all_data), n_files_used, opts.output), file=sys.stderr) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/MonteCarloMarginalizeCode/Code/bin/util_ConstructEOSPosterior.py b/MonteCarloMarginalizeCode/Code/bin/util_ConstructEOSPosterior.py index 30cfcf0fb..aae18cc1a 100755 --- a/MonteCarloMarginalizeCode/Code/bin/util_ConstructEOSPosterior.py +++ b/MonteCarloMarginalizeCode/Code/bin/util_ConstructEOSPosterior.py @@ -1,902 +1,1007 @@ -#!/usr/bin/env python -# -# util_ConstructEOSPosterior.py -# - takes in *generic-format* hyperparameter likelihood data -# - uses *uniform* prior on hyperparameters. [non-uniform priors can be applied by the user with a supplementary function] -# - generates posterior distribution by weighted Monte Carlo -# -# EXAMPLE: -# python `which util_ConstructEOSPosterior.py` --fname fake_int_grid.dat --parameter gamma1 --parameter gamma2 --lnL-offset 50 - -import RIFT.interpolators.BayesianLeastSquares as BayesianLeastSquares - -import argparse -import sys -import numpy as np -import numpy.lib.recfunctions -import scipy -import scipy.stats -import functools -import itertools - -import joblib # http://scikit-learn.org/stable/modules/model_persistence.html - -# GPU acceleration: NOT YET, just do usual -xpy_default=numpy # just in case, to make replacement clear and to enable override -identity_convert = lambda x: x # trivial return itself -cupy_success=False - -no_plots = True -internal_dtype = np.float32 # only use 32 bit storage! Factor of 2 memory savings for GP code in high dimensions - - -try: - import matplotlib.pyplot as plt - from mpl_toolkits.mplot3d import Axes3D - import matplotlib.lines as mlines - import corner - - no_plots=False -except ImportError: - print(" - no matplotlib - ") - - -from sklearn.preprocessing import PolynomialFeatures -if True: -#try: - import RIFT.misc.ModifiedScikitFit as msf # altenative polynomialFeatures -else: -#except: - print(" - Faiiled ModifiedScikitFit : No polynomial fits - ") -from sklearn import linear_model - -from igwn_ligolw import lsctables, utils, ligolw -lsctables.use_in(ligolw.LIGOLWContentHandler) - -import RIFT.integrators.mcsampler as mcsampler -try: - import RIFT.integrators.mcsamplerEnsemble as mcsamplerEnsemble - mcsampler_gmm_ok = True -except: - print(" No mcsamplerEnsemble ") - mcsampler_gmm_ok = False -try: - import RIFT.integrators.mcsamplerGPU as mcsamplerGPU - mcsampler_gpu_ok = True - mcsamplerGPU.xpy_default =xpy_default # force consistent, in case GPU present - mcsamplerGPU.identity_convert = identity_convert -except: - print( " No mcsamplerGPU ") - mcsampler_gpu_ok = False -try: - import RIFT.integrators.mcsamplerAdaptiveVolume as mcsamplerAdaptiveVolume - mcsampler_AV_ok = True -except: - print(" No mcsamplerAV ") - mcsampler_AV_ok = False -try: - import RIFT.integrators.mcsamplerPortfolio as mcsamplerPortfolio - mcsampler_Portfolio_ok = True -except: - print(" No mcsamplerPortolfio ") - - - - - -def add_field(a, descr): - """Return a new array that is like "a", but has additional fields. - - Arguments: - a -- a structured numpy array - descr -- a numpy type description of the new fields - - The contents of "a" are copied over to the appropriate fields in - the new array, whereas the new fields are uninitialized. The - arguments are not modified. - - >>> sa = numpy.array([(1, 'Foo'), (2, 'Bar')], \ - dtype=[('id', int), ('name', 'S3')]) - >>> sa.dtype.descr == numpy.dtype([('id', int), ('name', 'S3')]) - True - >>> sb = add_field(sa, [('score', float)]) - >>> sb.dtype.descr == numpy.dtype([('id', int), ('name', 'S3'), \ - ('score', float)]) - True - >>> numpy.all(sa['id'] == sb['id']) - True - >>> numpy.all(sa['name'] == sb['name']) - True - """ - if a.dtype.fields is None: - raise ValueError("`A' must be a structured numpy array") - b = numpy.empty(a.shape, dtype=a.dtype.descr + descr) - for name in a.dtype.names: - b[name] = a[name] - return b - - -parser = argparse.ArgumentParser() -parser.add_argument("--fname",help="filename of *.dat file (EOS-format: lnL sigma_lnL p1 p2 ... . ASSUME any stacking over events already performed.") -parser.add_argument("--fname-output-samples",default="output-EOS-samples",help="output grid") -parser.add_argument("--fname-output-integral",default="output-EOS-integral",help="for evidencees and pipeline compatibility") -parser.add_argument("--n-output-samples",default=2000,type=int,help="output posterior samples (default 3000)") -parser.add_argument("--eos-param", type=str, default=None, help="parameterization of equation of state [spectral only, for now]") -parser.add_argument("--parameter", action='append', help="Parameters used as fitting parameters AND varied at a low level to make a posterior. Currently can only specify gamma1,gamma2, ..., and these MUST be columns in --fname. IF NOT PROVIDED, DEFAULTS TO LIST IN FILE. ") -parser.add_argument("--parameter-implied", action='append', help="Parameter used in fit, but not independently varied for Monte Carlo. For EOS objects, only possible for physical quantities like R1.4, etc. NOT YET PROVIDED") -#parser.add_argument("--no-adapt-parameter",action='append',help="Disable adaptive sampling in a parameter. Useful in cases where a parameter is not well-constrained, and the a prior sampler is well-chosen.") -parser.add_argument("--parameter-nofit", action='append', help="Parameter used to initialize the implied parameters, and varied at a low level, but NOT the fitting parameters.") -parser.add_argument("--integration-parameter-range",action='append', help="Integration parameter ranges. Syntax is name:[a,b]") -parser.add_argument("--downselect-parameter",action='append', help='Name of parameter to be used to eliminate grid points ') -parser.add_argument("--downselect-parameter-range",action='append',type=str) -parser.add_argument("--no-downselect",action='store_true') -parser.add_argument("--aligned-prior", default="uniform",help="Options are 'uniform', 'volumetric', and 'alignedspin-zprior'") -parser.add_argument("--cap-points",default=-1,type=int,help="Maximum number of points in the sample, if positive. Useful to cap the number of points ued for GP. See also lnLoffset. Note points are selected AT RANDOM") -parser.add_argument("--lambda-max", default=4000,type=float,help="Maximum range of 'Lambda' allowed. Minimum value is ZERO, not negative.") -parser.add_argument("--lnL-shift-prevent-overflow",default=None,type=float,help="Define this quantity to be a large positive number to avoid overflows. Note that we do *not* define this dynamically based on sample values, to insure reproducibility and comparable integral results. BEWARE: If you shift the result to be below zero, because the GP relaxes to 0, you will get crazy answers.") -parser.add_argument("--lnL-offset",type=float,default=np.inf,help="lnL offset") -parser.add_argument("--lnL-cut",type=float,default=None,help="lnL cut [MANUAL]") -parser.add_argument("--sigma-cut",type=float,default=0.6,help="Eliminate points with large error from the fit.") -parser.add_argument("--ignore-errors-in-data",action='store_true',help='Ignore reported error in lnL. Helpful for testing purposes (i.e., if the error is zero)') -parser.add_argument("--lnL-peak-insane-cut",type=float,default=np.inf,help="Throw away lnL greater than this value. Should not be necessary") -parser.add_argument("--verbose", action="store_true",default=False, help="Required to build post-frame-generating sanity-test plots") -parser.add_argument("--save-plots",default=False,action='store_true', help="Write plots to file (only useful for OSX, where interactive is default") -parser.add_argument("--n-max",default=3e5,type=float) -parser.add_argument("--n-step",default=1e5,type=int) -parser.add_argument("--n-eff",default=3e3,type=int) -parser.add_argument("--pool-size",default=3,type=int,help="Integer. Number of GPs to use (result is averaged)") -parser.add_argument("--fit-method",default="rf",help="rf (default) : rf|gp|quadratic|polynomial|gp_hyper|gp_lazy|cov|kde. Note 'polynomial' with --fit-order 0 will fit a constant") -parser.add_argument("--fit-load-gp",default=None,type=str,help="Filename of GP fit to load. Overrides fitting process, but user MUST correctly specify coordinate system to interpret the fit with. Does not override loading and converting the data.") -parser.add_argument("--fit-save-gp",default=None,type=str,help="Filename of GP fit to save. ") -parser.add_argument("--fit-order",type=int,default=2,help="Fit order (polynomial case: degree)") -parser.add_argument("--no-plots",action='store_true') -parser.add_argument("--using-eos-type", type=str, default=None, help="Name of EOS parameterization (must match what is used for inputs). Will use EOS parameterization to identify appropriate field headers") -parser.add_argument("--sampler-method",default="adaptive_cartesian",help="adaptive_cartesian|GMM|adaptive_cartesian_gpu") -parser.add_argument("--sampler-portfolio",default=None,action='append',type=str,help="comma-separated strings, matching sampler methods other than portfolio") -parser.add_argument("--sampler-portfolio-args",default=None, action='append', type=str, help='eval-able dictionary to be passed to that sampler_') -parser.add_argument("--internal-use-lnL",action='store_true',help="integrator internally manipulates lnL.. ") -parser.add_argument("--internal-correlate-parameters",default=None,type=str,help="comman-separated string indicating parameters that should be sampled allowing for correlations. Must be sampling parameters. Only implemented for gmm. If string is 'all', correlate *all* parameters") -parser.add_argument("--internal-n-comp",default=1,type=int,help="number of components to use for GMM sampling. Default is 1, because we expect a unimodal posterior in well-adapted coordinates. If you have crappy coordinates, use more") -parser.add_argument("--force-no-adapt",action='store_true',help="Disable adaptation, both of the tempering exponent *and* the individual sampling prior(s)") -parser.add_argument("--tripwire-fraction",default=0.05,type=float,help="Fraction of nmax of iterations after which n_eff needs to be greater than 1+epsilon for a small number epsilon") - -# Supplemental likelihood factors: convenient way to effectively change the mass/spin prior in arbitrary ways for example -# Note this supplemental factor is passed the *fitting* arguments, directly. Use with extreme caution, since we often change the dimension in a DAG -parser.add_argument("--supplementary-likelihood-factor-code", default=None,type=str,help="Import a module (in your pythonpath!) containing a supplementary factor for the likelihood. Used to impose supplementary external priors of arbitrary complexity and external dependence (e.g., imposing alternate EOS priors)") -parser.add_argument("--supplementary-likelihood-factor-function", default=None,type=str,help="With above option, specifies the specific function used as an external likelihood. EXPERTS ONLY") -parser.add_argument("--supplementary-likelihood-factor-ini", default=None,type=str,help="With above option, specifies an ini file that is parsed (here) and passed to the preparation code, called when the module is first loaded, to configure the module. EXPERTS ONLY") -parser.add_argument("--supplementary-coordinate-code", default=None,type=str,help="Coordinate conversion/prior code. Accepts: the literal 'rift_default' (use RIFT.lalsimutils.convert_waveform_coordinates plus RIFT-standard priors); a filesystem path ending in .py (loaded as a plugin); or any importable dotted module name. See RIFT.misc.coordinate_plugin for the interface plugins must implement.") -parser.add_argument("--supplementary-coordinate-function", default=None, type=str, help="Name of the entry-point callable inside the module named by --supplementary-coordinate-code. Defaults to 'convert_coordinates'.") -parser.add_argument("--supplementary-coordinate-ini", default=None, type=str, help="Optional ini file parsed and handed to the coordinate plugin's prepare() hook so it can read its own configuration block(s).") -parser.add_argument("--supplementary-coordinate-chart", default=None, type=str, help="Which chart (coordinate system) defined by the plugin to use for this run. Required when the plugin's CHARTS dict has more than one entry; ignored when the plugin doesn't define CHARTS. Different charts can share parameter names but imply different priors -- the chart name disambiguates which (name -> prior) mapping is installed.") -opts= parser.parse_args() - -#print(" WARNING: Always use internal_use_lnL for now ") -#opts.internal_use_lnL=True - -no_plots = no_plots | opts.no_plots -lnL_shift = 0 -lnL_default_large_negative = -500 -if opts.lnL_shift_prevent_overflow: - lnL_shift = opts.lnL_shift_prevent_overflow - - - -### Comparison data (from LI) -### - -downselect_dict = {} -dlist = [] -dlist_ranges=[] -if opts.downselect_parameter: - dlist = opts.downselect_parameter - dlist_ranges = map(eval,opts.downselect_parameter_range) -else: - dlist = [] - dlist_ranges = [] -if len(dlist) != len(dlist_ranges): - print(" downselect parameters inconsistent", dlist, dlist_ranges) -for indx in np.arange(len(dlist_ranges)): - downselect_dict[dlist[indx]] = dlist_ranges[indx] - -if opts.no_downselect: - downselect_dict={} - - -test_converged={} - -### -### Retrieve data -### -# int_sig sigma/L gamma1 gamma2 ... -col_lnL = 0 -dat_orig = dat = np.loadtxt(opts.fname) -dat_orig = dat[dat[:,col_lnL].argsort()] # sort http://stackoverflow.com/questions/2828059/sorting-arrays-in-numpy-by-column -print(" Original data size = ", len(dat), dat.shape) -dat_orig_names = None -with open(opts.fname,'r') as f: - header_str = f.readline() - header_str = header_str.rstrip() -dat_orig_names = header_str.replace('#','').split()[2:] - -### -### Parameters in use -### - -coord_names = opts.parameter # Used in fit -if coord_names is None: - coord_names = dat_orig_names -low_level_coord_names = coord_names # Used for Monte Carlo -if opts.parameter_implied: - coord_names = coord_names+opts.parameter_implied -if opts.parameter_nofit: - if opts.parameter is None: - low_level_coord_names = opts.parameter_nofit # Used for Monte Carlo - else: - low_level_coord_names = opts.parameter+opts.parameter_nofit # Used for Monte Carlo -error_factor = len(coord_names) -name_index_dict ={} -for name in dat_orig_names: - try: - name_index_dict[name] = 2+dat_orig_names.index(name) - except: - raise Exception(" Currently fitting parameter names must match columns in data file ") -# TeX dictionary -print(" Coordinate names for fit :, ", coord_names, " from ", dat_orig_names, " indexed as ", name_index_dict) -print(" Coordinate names for Monte Carlo :, ", low_level_coord_names) - - -### -### Integration ranges -### - -param_ranges = {} -for range_code in opts.integration_parameter_range: - name, range_str = range_code.split(':') - range_expr = eval(range_str) # define. Better to split on , for example - param_ranges[name] = np.array(range_expr) - -# Add in integration range for everything else, if nothing specified -for name in dat_orig_names: - if not name in param_ranges: - vals = dat_orig[:,name_index_dict[name]] - param_ranges[name] = [np.min(vals), np.max(vals)] - -### -### Prior functions : default is UNIFORM, since it is unmodeled and generic -### - -def uniform_prior(x): - return np.ones(x.shape) - -prior_map = {} -for name in low_level_coord_names: - prior_map[name] = uniform_prior - if not(name in param_ranges): - raise Exception(" {} not provided a parameter range ".format(name)) # change later, should fall back to using prior range from above - - -prior_range_map = param_ranges - -# prior_map = { 'gamma1':eos_param_uniform_prior, 'gamma2':eos_param_uniform_prior, -# } -# # Les: somewhat more aggressive: -# # gamma1: 0.2,2 -# # gamma2: -1.67, 1.7 -# prior_range_map = { 'gamma1': [0.707899,1.31], 'gamma2':[-1.6,1.7], 'gamma3':[-0.6,0.6], 'gamma4':[-0.02,0.02] -# } - - -### -### Supplemental likelihood: load (as in ILE) -### -supplemental_ln_likelihood= None -supplemental_ln_likelihood_prep =None -supplemental_ln_likelihood_parsed_ini=None -# Supplemental likelihood factor. Must have identical call sequence to 'likelihood_function'. Called with identical raw inputs (including cosines/etc) -if opts.supplementary_likelihood_factor_code and opts.supplementary_likelihood_factor_function: - print(" EXTERNAL SUPPLEMENTARY LIKELIHOOD FACTOR : {}.{} ".format(opts.supplementary_likelihood_factor_code,opts.supplementary_likelihood_factor_function)) - __import__(opts.supplementary_likelihood_factor_code) - external_likelihood_module = sys.modules[opts.supplementary_likelihood_factor_code] - supplemental_ln_likelihood = getattr(external_likelihood_module,opts.supplementary_likelihood_factor_function) - name_prep = "prepare_"+opts.supplementary_likelihood_factor_function - if hasattr(external_likelihood_module,name_prep): - supplemental_ln_likelhood_prep=getattr(external_likelihood_module,name_prep) - # Check for and load in ini file associated with external library - if opts.supplementary_likelihood_factor_ini: - import configparser as ConfigParser - config = ConfigParser.ConfigParser() - config.optionxform=str # force preserve case! - config.read(opts.supplementary_likelihood_factor_ini) - supplemental_ln_likelhood_parsed_ini=config - - # Call the ini file, tell it what coordinates we are using by name - supplemental_ln_likelihood_prep(config=supplemental_ln_likelihood_parsed_ini,coords=coord_names) - -supplemental_coordinate_convert = None -if opts.supplementary_coordinate_code: - # Resolve the user-supplied coordinate-convert plugin. The loader - # accepts three forms in --supplementary-coordinate-code: the literal - # 'rift_default', a filesystem path to a .py file, or an importable - # dotted module name. The plugin must expose a callable named by - # --supplementary-coordinate-function (default 'convert_coordinates') - # with the signature (x_in, coord_names, low_level_coord_names, **kwargs) - # returning a 2-D ndarray of shape (N, len(coord_names)). Plugins may - # optionally define prepare() (one-shot setup, gets the parsed ini and - # the active coord-name lists) and register_priors() (mutate prior_map - # in place). See RIFT.misc.coordinate_plugin for the full contract. - from RIFT.misc.coordinate_plugin import load_coordinate_converter - supplemental_coordinate_convert, _coord_plugin_module = load_coordinate_converter( - spec=opts.supplementary_coordinate_code, - function_name=opts.supplementary_coordinate_function, - ini_path=opts.supplementary_coordinate_ini, - coord_names=coord_names, - low_level_coord_names=low_level_coord_names, - chart=opts.supplementary_coordinate_chart, - opts=opts, - prior_map=prior_map, - prior_range_map=prior_range_map, - ) - -from sklearn.gaussian_process import GaussianProcessRegressor -from sklearn.gaussian_process.kernels import RBF, WhiteKernel, ConstantKernel as C - -def adderr(y): - val,err = y - return val+error_factor*err - -def fit_gp(x,y,x0=None,symmetry_list=None,y_errors=None,hypercube_rescale=False,fname_export="gp_fit"): - """ - x = array so x[0] , x[1], x[2] are points. - """ - - # If we are loading a fit, override everything else - if opts.fit_load_gp: - print(" WARNING: Do not re-use fits across architectures or versions : pickling is not transferrable ") - my_gp=joblib.load(opts.fit_load_gp) - return lambda x:my_gp.predict(x) - - # Amplitude: - # - We are fitting lnL. - # - We know the scale more or less: more than 2 in the log is bad - # Scale - # - because of strong correlations with chirp mass, the length scales can be very short - # - they are rarely very long, but at high mass can be long - # - I need to allow for a RANGE - - length_scale_est = [] - length_scale_bounds_est = [] - for indx in np.arange(len(x[0])): - # These length scales have been tuned by expereience - length_scale_est.append( 2*np.std(x[:,indx]) ) # auto-select range based on sampling retained - length_scale_min_here= np.max([1e-3,0.2*np.std(x[:,indx]/np.sqrt(len(x)))]) - length_scale_bounds_est.append( (length_scale_min_here , 5*np.std(x[:,indx]) ) ) # auto-select range based on sampling *RETAINED* (i.e., passing cut). Note that for the coordinates I usually use, it would be nonsensical to make the range in coordinate too small, as can occasionally happens - - print(" GP: Input sample size ", len(x), len(y)) - print(" GP: Estimated length scales ") - print(length_scale_est) - print(length_scale_bounds_est) - - if not (hypercube_rescale): - # These parameters have been hand-tuned by experience to try to set to levels comparable to typical lnL Monte Carlo error - kernel = WhiteKernel(noise_level=0.1,noise_level_bounds=(1e-2,1))+C(0.5, (1e-3,1e1))*RBF(length_scale=length_scale_est, length_scale_bounds=length_scale_bounds_est) - gp = GaussianProcessRegressor(kernel=kernel, n_restarts_optimizer=8) - - gp.fit(x,y) - - print(" Fit: std: ", np.std(y - gp.predict(x)), "using number of features ", len(y)) - - if opts.fit_save_gp: - print(" Attempting to save fit ", opts.fit_save_gp+".pkl") - joblib.dump(gp,opts.fit_save_gp+".pkl") - - return lambda x: gp.predict(x) - else: - x_scaled = np.zeros(x.shape) - x_center = np.zeros(len(length_scale_est)) - x_center = np.mean(x) - print(" Scaling data to central point ", x_center) - for indx in np.arange(len(x)): - x_scaled[indx] = (x[indx] - x_center)/length_scale_est # resize - - kernel = WhiteKernel(noise_level=0.1,noise_level_bounds=(1e-2,1))+C(0.5, (1e-3,1e1))*RBF( len(x_center), (1e-3,1e1)) - gp = GaussianProcessRegressor(kernel=kernel, n_restarts_optimizer=8) - - gp.fit(x_scaled,y) - print(" Fit: std: ", np.std(y - gp.predict(x_scaled)), "using number of features ", len(y)) # should NOT be perfect - - return lambda x,x0=x_center,scl=length_scale_est: gp.predict( (x-x0 )/scl) - -def map_funcs(func_list,obj): - return [func(obj) for func in func_list] -def fit_gp_pool(x,y,n_pool=10,**kwargs): - """ - Split the data into 10 parts, and return a GP that averages them - """ - x_copy = np.array(x) - y_copy = np.array(y) - indx_list =np.arange(len(x_copy)) - np.random.shuffle(indx_list) # acts in place - partition_list = np.array_split(indx_list,n_pool) - gp_fit_list =[] - for part in partition_list: - print(" Fitting partition ") - gp_fit_list.append(fit_gp(x[part],y[part],**kwargs)) - fn_out = lambda x: np.mean( map_funcs( gp_fit_list,x), axis=0) - print(" Testing ", fn_out([x[0]])) - return fn_out - - -def fit_rf(x,y,y_errors=None,fname_export='nn_fit'): -# from sklearn.ensemble import RandomForestRegressor - from sklearn.ensemble import ExtraTreesRegressor - # Instantiate model. Usually not that many structures to find, don't overcomplicate - # - should scale like number of samples - rf = ExtraTreesRegressor(n_estimators=100, verbose=True,n_jobs=-1) # no more than 5% of samples in a leaf - if y_errors is None: - rf.fit(x,y) - else: - rf.fit(x,y,sample_weight=1./y_errors**2) - - ### reject points with infinities : problems for inputs - def fn_return(x_in,rf=rf): - f_out = -lnL_default_large_negative*np.ones(len(x_in)) - # remove infinity or Nan - indx_ok = np.all(np.isfinite(np.array(x_in,dtype=float)),axis=-1) - # rf internally uses float32, so we need to remove points > 10^37 or so ! - # ... this *should* never happen due to bounds constraints, but ... - indx_ok_size = np.all( np.logical_not(np.greater(np.abs(x_in),1e37)), axis=-1) - indx_ok = np.logical_and(indx_ok, indx_ok_size) - f_out[indx_ok] = rf.predict(x_in[indx_ok]) - return f_out -# fn_return = lambda x_in: rf.predict(x_in) - - print( " Demonstrating RF") # debugging - residuals = rf.predict(x)-y - print( " std ", np.std(residuals), np.max(y), np.max(fn_return(x))) - return fn_return - - - - - -# initialize -dat_mass = [] -weights = [] -n_params = -1 - - - ### - ### Convert data. RIGHT NOW JUST DOWNSELECTING, no intermediate fitting parameters defined - ### - -# Naive convert: no downselect. -if (supplemental_coordinate_convert ==None): - - indx_of_orig_names = np.array([ dat_orig_names.index(coord_names[k]) for k in range(len(coord_names))]) - dat_out = [] - for line in dat: - dat_here= np.zeros(len(coord_names)+2) - if line[col_lnL+1] > opts.sigma_cut: - print("skipping", line) - continue - dat_here[:-2] = line[indx_of_orig_names+2]#line[2:len(coord_names)+2] # modify to use names! - dat_here[-2] = line[0] - dat_here[-1] = line[1] - dat_out.append(dat_here) - dat_out= np.array(dat_out) - - # Repack data, WHOLE SET - X =dat_out[:,0:len(coord_names)] - Y = dat_out[:,-2] - if np.max(Y)<0 and lnL_shift ==0: - lnL_shift = -100 - np.max(Y) # force it to be offset/positive -- may help some configurations. Remember our adaptivity is silly. - Y_err = dat_out[:,-1] - def convert_coords(x): - return x - -else: - # Pack data, using coordinate converter. Note later calculations MUST use the converter - X = supplemental_coordinate_convert(dat[:,2:], coord_names=coord_names, low_level_coord_names=dat_orig_names) # convert and generate X - Y = dat[:,0] - Y_err = dat[:,1] - if np.max(Y)<0 and lnL_shift ==0: - lnL_shift = -100 - np.max(Y) # force it to be offset/positive -- may help some configurations. Remember our adaptivity is silly. - def convert_coords(x_in): - return supplemental_coordinate_convert(x_in, coord_names=coord_names, low_level_coord_names=dat_orig_names) # convert and generate X -# Save copies for later (plots) -X_orig = X.copy() -Y_orig = Y.copy() - - - -# Eliminate values with Y too small -max_lnL = np.max(Y) -if np.isinf(opts.lnL_offset): - indx_ok= np.ones(len(Y),dtype=bool) # default case, we preserve all the data -else: - indx_ok = np.array(Y>np.max(Y)-opts.lnL_offset,dtype=bool) # force cast : sometimes indx_ok is a mappable object? -n_ok = np.sum(indx_ok) -# Provide some random points, to insure reasonable tapering behavior away from the sample -print(" Points used in fit : ", n_ok, " out of ", len(indx_ok), " given max lnL ", max_lnL) -if max_lnL < 10 and np.mean(Y) > -10: # second condition to allow synthetic tests not to fail, as these often have maxlnL not large - print(" Resetting to use ALL input data -- beware ! ") - # nothing matters, we will reject it anyways - indx_ok = np.ones(len(Y),dtype=bool) -elif n_ok < 10: # and max_lnL > 30: - # mark the top 10 elements and use them for fits - # this may be VERY VERY DANGEROUS if the peak is high and poorly sampled - idx_sorted_index = np.lexsort((np.arange(len(Y)), Y)) # Sort the array of Y, recovering index values - indx_list = np.array( [[k, Y[k]] for k in idx_sorted_index]) # pair up with the weights again - indx_list = indx_list[::-1] # reverse, so most significant are first - indx_ok = list(map(int,indx_list[:10,0])) - print(" Revised number of points for fit: ", np.sum(indx_ok), len(indx_ok), indx_list[:10]) -X_raw = X.copy() - - - -my_fit= None -if opts.fit_method =='gp': - print(" FIT METHOD : GP") - # some data truncation IS used for the GP, but beware - print(" Truncating data set used for GP, to reduce memory usage needed in matrix operations") - X=X[indx_ok] - Y=Y[indx_ok] - lnL_shift - Y_err = Y_err[indx_ok] - # Cap the total number of points retained, AFTER the threshold cut - if opts.cap_points< len(Y) and opts.cap_points> 100: - n_keep = opts.cap_points - indx = np.random.choice(np.arange(len(Y)),size=n_keep,replace=False) - Y=Y[indx] - X=X[indx] - Y_err=Y_err[indx] - if opts.ignore_errors_in_data: - Y_err=None - my_fit = fit_gp(X,Y,y_errors=Y_err) -elif opts.fit_method == 'rf': - print( " FIT METHOD ", opts.fit_method, " IS RF ") - # NO data truncation for NN needed? To be *consistent*, have the code function the same way as the others - X=X[indx_ok] - Y=Y[indx_ok] - lnL_shift - Y_err = Y_err[indx_ok] - # Cap the total number of points retained, AFTER the threshold cut - if opts.cap_points< len(Y) and opts.cap_points> 100: - n_keep = opts.cap_points - indx = np.random.choice(np.arange(len(Y)),size=n_keep,replace=False) - Y=Y[indx] - X=X[indx] - Y_err=Y_err[indx] - if opts.ignore_errors_in_data: - Y_err=None - my_fit = fit_rf(X,Y,y_errors=Y_err) - - -# Sort for later convenience (scatterplots, etc) -indx = Y.argsort()#[::-1] -X=X[indx] -Y=Y[indx] - - - -### -### Integrate posterior -### - - -sampler = mcsampler.MCSampler() -if opts.sampler_method == "adaptive_cartesian_gpu": - sampler = mcsamplerGPU.MCSampler() - sampler.xpy = xpy_default - sampler.identity_convert=identity_convert - mcsampler = mcsamplerGPU # force use of routines in that file, for properly configured GPU-accelerated code as needed - - # if opts.sampler_xpy == "numpy": - # mcsampler.set_xpy_to_numpy() - # sampler.xpy= numpy - # sampler.identity_convert= lambda x: x -if opts.sampler_method == "GMM": - sampler = mcsamplerEnsemble.MCSampler() -elif opts.sampler_method == "AV": - sampler = mcsamplerAdaptiveVolume.MCSampler() - opts.internal_use_lnL= True # required! -elif opts.sampler_method == "portfolio": - use_portfolio=True - sampler = None - sampler_list = [] - sampler_types = opts.sampler_portfolio - for name in sampler_types: - if name =='AV': - sampler = mcsamplerAdaptiveVolume.MCSampler() - if name =='GMM': - sampler = mcsamplerEnsemble.MCSampler() - opts.sampler_method = 'GMM' # this will force the creation/parsing of GMM-specific arguments below, so they are properly passed - if name == "adaptive_cartesian_gpu": - sampler = mcsamplerGPU.MCSampler() - sampler.xpy = xpy_default - sampler.identity_convert=identity_convert - if name == 'NFlow': - # expensive import, only do if requested - try: - import RIFT.integrators.mcsamplerNFlow as mcsamplerNFlow - mcsampler_NF_ok = True - except: - print(" No mcsamplerNFlow ") - continue - sampler = mcsamplerNFlow.MCSampler() - sampler.xpy = xpy_default - sampler.identity_convert=identity_convert - if sampler is None: - # Don't add unknown type - continue - print('PORTFOLIO: adding {} '.format(name)) - sampler_list.append(sampler) - sampler = mcsamplerPortfolio.MCSampler(portfolio=sampler_list) - - -## -## Loop over param names -## -for p in coord_names: - prior_here = prior_map[p] - range_here = prior_range_map[p] - - sampler.add_parameter(p, pdf=np.vectorize(lambda x:1), prior_pdf=prior_here,left_limit=range_here[0],right_limit=range_here[1],adaptive_sampling=True) - -likelihood_function = None -log_likelihood_function = None -def log_likelihood_function(*args): - return my_fit(convert_coords(np.array([*args]).T )) - -if len(coord_names) ==1: - def likelihood_function(x): - if isinstance(x,float): - return np.exp(my_fit([x])) - else: - return np.exp(my_fit(convert_coords(np.c_[x]))) -if len(coord_names) ==2: - def likelihood_function(x,y): - if isinstance(x,float): - return np.exp(my_fit([x,y])) - else: -# return np.exp(my_fit(convert_coords(np.array([x,y],dtype=internal_dtype).T))) - return np.exp(my_fit(convert_coords(np.c_[x,y]))) -if len(coord_names) ==3: - def likelihood_function(x,y,z): - if isinstance(x,float): - return np.exp(my_fit([x,y,z])) - else: -# return np.exp(my_fit(convert_coords(np.array([x,y,z],dtype=internal_dtype).T))) - return np.exp(my_fit(convert_coords(np.c_[x,y,z]))) -if len(coord_names) ==4: - def likelihood_function(x,y,z,a): - if isinstance(x,float): - return np.exp(my_fit([x,y,z,a])) - else: -# return np.exp(my_fit(convert_coords(np.array([x,y,z,a],dtype=internal_dtype).T))) - return np.exp(my_fit(convert_coords(np.c_[x,y,z,a]))) -if len(coord_names) ==5: - def likelihood_function(x,y,z,a,b): - if isinstance(x,float): - return np.exp(my_fit([x,y,z,a,b])) - else: -# return np.exp(my_fit(convert_coords(np.array([x,y,z,a,b],dtype=internal_dtype).T))) - return np.exp(my_fit(convert_coords(np.c_[x,y,z,a,b]))) -if len(coord_names) ==6: - def likelihood_function(x,y,z,a,b,c): - if isinstance(x,float): - return np.exp(my_fit([x,y,z,a,b,c])) - else: -# return np.exp(my_fit(convert_coords(np.array([x,y,z,a,b,c],dtype=internal_dtype).T))) - return np.exp(my_fit(convert_coords(np.c_[x,y,z,a,b,c]))) -if len(coord_names) ==7: - def likelihood_function(x,y,z,a,b,c,d): - if isinstance(x,float): - return np.exp(my_fit([x,y,z,a,b,c,d])) - else: - return np.exp(my_fit(convert_coords(np.c_[x,y,z,a,b,c,d]))) -if len(coord_names) ==8: - def likelihood_function(x,y,z,a,b,c,d,e): - if isinstance(x,float): - return np.exp(my_fit([x,y,z,a,b,c,d,e])) - else: - return np.exp(my_fit(convert_coords(np.c_[x,y,z,a,b,c,d,e]))) -if len(coord_names) ==9: - def likelihood_function(x,y,z,a,b,c,d,e,f): - if isinstance(x,float): - return np.exp(my_fit([x,y,z,a,b,c,d,e,f])) - else: - return np.exp(my_fit(convert_coords(np.c_[x,y,z,a,b,c,d,e,f]))) -if len(coord_names) ==10: - def likelihood_function(x,y,z,a,b,c,d,e,f,g): - if isinstance(x,float): - return np.exp(my_fit([x,y,z,a,b,c,d,e,f,g])) - else: - return np.exp(my_fit(convert_coords(np.c_[x,y,z,a,b,c,d,e,f,g]))) - - - - -n_step = opts.n_step -my_exp = np.min([1,0.8*np.log(n_step)/np.max(Y)]) # target value : scale to slightly sublinear to (n_step)^(0.8) for Ymax = 200. This means we have ~ n_step points, with peak value wt~ n_step^(0.8)/n_step ~ 1/n_step^(0.2), limiting contrast -if np.max(Y_orig) < 0: # for now, don't use a weight exponent if we are negative: can't use guess based from GW experience - my_exp = 1 -#my_exp = np.max([my_exp, 1/np.log(n_step)]) # do not allow extreme contrast in adaptivity, to the point that one iteration will dominate -print(" Weight exponent ", my_exp, " and peak contrast (exp)*lnL = ", my_exp*np.max(Y), "; exp(ditto) = ", np.exp(my_exp*np.max(Y)), " which should ideally be no larger than of order the number of trials in each epoch, to insure reweighting doesn't select a single preferred bin too strongly. Note also the floor exponent also constrains the peak, de-facto") - - -extra_args={} -if opts.sampler_method == "GMM": - n_max_blocks = ((1.0*int(opts.n_max))/n_step) - n_comp = opts.internal_n_comp # default - def parse_corr_params(my_str): - """ - Takes a string with no spaces, and returns a tuple - """ - corr_param_names = my_str.replace(',',' ').split() - corr_param_indexes = [] - for param in corr_param_names: - try: - indx = low_level_coord_names.index(param) - corr_param_indexes.append(indx) - except: - continue - return tuple(corr_param_indexes) - if opts.internal_correlate_parameters == 'all': - gmm_dict = {tuple(range(len(low_level_coord_names))):None} # integrate *jointly* in all parameters together - elif not (opts.internal_correlate_parameters is None): - # Correlate identified parameters - my_blocks = opts.internal_correlate_parameters.split() - my_tuples = list(map( parse_corr_params, my_blocks)) - gmm_dict = {x:None for x in my_tuples} - print(" GMM: Proposed correlated ", gmm_dict) - # What about un-labelled parameters? Make a null tuple for them as well - correlated_params = set(); correlated_params = correlated_params.union( *list(map(set,my_tuples))) - uncorrelated_params = set(np.arange(len(low_level_coord_names))); - uncorrelated_params = uncorrelated_params.difference(correlated_params) - for x in uncorrelated_params: - gmm_dict[(x,)] = None - print( " Using correlated GMM sampling on sampling variable indexes " , gmm_dict, " out of ", low_level_coord_names) - else: - param_indexes = range(len(low_level_coord_names)) - gmm_dict = {(k,):None for k in param_indexes} # no correlations -# lnL_offset_saving = opts.lnL_offset - lnL_offset_saving = -20 # for simplicity, hardcode for now for preserving points - print("GMM ", gmm_dict) - extra_args = {'n_comp':n_comp,'max_iter':n_max_blocks,'L_cutoff': None,'gmm_dict':gmm_dict,'max_err':50, 'lnw_failure_cut':-np.inf} # made up for now, should adjust -extra_args.update({ - "n_adapt": 100, # Number of chunks to allow adaption over - "history_mult": 10, # Multiplier on 'n' - number of samples to estimate marginalized 1D histograms with, - "force_no_adapt":opts.force_no_adapt, - "tripwire_fraction":opts.tripwire_fraction -}) - -fn_passed = likelihood_function -if supplemental_ln_likelihood: - fn_passed = lambda *x: likelihood_function(*x)*np.exp(supplemental_ln_likelihood(*x)) -if opts.internal_use_lnL: - fn_passed = log_likelihood_function # helps regularize large values - if supplemental_ln_likelihood: - fn_passed = lambda *x: log_likelihood_function(*x) + supplemental_ln_likelihood(*x) - extra_args.update({"use_lnL":True,"return_lnI":True}) - - - -res, var, neff, dict_return = sampler.integrate(fn_passed, *coord_names, verbose=True,nmax=int(opts.n_max),n=n_step,neff=opts.n_eff, save_intg=True,tempering_adapt=True, floor_level=1e-3,igrand_threshold_p=1e-3,convergence_tests=test_converged,adapt_weight_exponent=my_exp,no_protect_names=True,**extra_args) # weight ecponent needs better choice. We are using arbitrary-name functions - - -# Save result -- needed for odds ratios, etc. -np.savetxt(opts.fname_output_integral, [np.log(res)]) - -if neff < len(coord_names): - print(" PLOTS WILL FAIL ") - print(" Not enough independent Monte Carlo points to generate useful contours") - - -samples = sampler._rvs -print(samples.keys()) -n_params = len(coord_names) -dat_mass = np.zeros((len(samples[coord_names[0]]),n_params+3)) -if not(opts.internal_use_lnL): - dat_logL = np.log(samples["integrand"]) -else: - if 'log_integrand' in samples: - dat_logL = samples['log_integrand'] - else: - dat_logL = samples["integrand"] -lnLmax = np.max(dat_logL[np.isfinite(dat_logL)]) -print(" Max lnL ", np.max(dat_logL)) - -n_ESS = -1 -if True: - # Compute n_ESS. Should be done by integrator! - if 'log_joint_s_prior' in samples: - weights_scaled = np.exp(dat_logL - lnLmax + samples["log_joint_prior"] - samples["log_joint_s_prior"]) - # dictionary, write this to enable later use of it - samples["joint_s_prior"] = np.exp(samples["log_joint_s_prior"]) - samples["joint_prior"] = np.exp(samples["log_joint_prior"]) - else: - weights_scaled = np.exp(dat_logL - lnLmax)*sampler._rvs["joint_prior"]/sampler._rvs["joint_s_prior"] - weights_scaled = weights_scaled/np.max(weights_scaled) # try to reduce dynamic range - n_ESS = np.sum(weights_scaled)**2/np.sum(weights_scaled**2) - print(" n_eff n_ESS ", neff, n_ESS) - - -# Throw away stupid points that don't impact the posterior -indx_ok = np.ones(len(dat_logL),dtype=bool) -if not('log_joint_s_prior' in samples): - indx_ok=samples["joint_s_prior"]>0 -indx_ok = np.logical_and(dat_logL > np.max(dat_logL)-opts.lnL_offset ,indx_ok) -for p in coord_names: - samples[p] = samples[p][indx_ok] -dat_logL = dat_logL[indx_ok] -print(samples.keys()) -samples["joint_prior"] =samples["joint_prior"][indx_ok] -samples["joint_s_prior"] =samples["joint_s_prior"][indx_ok] - - - -### -### 1d posteriors of the coordinates used for sampling [EQUALLY WEIGHTED, BIASED because physics cuts aren't applied] -### - -p = samples["joint_prior"] -ps =samples["joint_s_prior"] -lnL = dat_logL -lnLmax = np.max(lnL) -weights = np.exp(lnL-lnLmax)*p/ps - - - -print(" ---- Subset for posterior samples (and further corner work) --- ") - - -p_norm = (weights/np.sum(weights)) -indx_list = np.random.choice(np.arange(len(weights)), p=p_norm.astype(np.float64),size=opts.n_output_samples) - - -dat_out = np.zeros( (opts.n_output_samples,2+len(dat_orig_names)) ) - -# Initialize fixed parameters -if len(coord_names) < len(dat_orig_names): # not needed if all params are in fit - - if len(dat) < opts.n_output_samples: - print(" NOTE: original data shorter than requested output; adding",opts.n_output_samples-len(dat),"duplicate fill lines from original data.") - newlines = None - if opts.n_output_samples > 2*len(dat): - newlines = dat[:] - newlen = len(newlines) - while newlen < opts.n_output_samples: - newerlines = dat[:opts.n_output_samples-newlen] #will only get up to len(dat) lines - newlines = np.concatenate((newlines,newerlines), axis=0) - newlen = len(newlines) - else: - newlines = dat[:opts.n_output_samples-len(dat)] #duplicate lines to fill - dat = np.concatenate((dat,newlines), axis=0) #should be fine since dat isn't used after this - - for c in np.arange(len(dat_orig_names)): - if dat_orig_names[c] not in coord_names: - print(" Not in coord_names:",dat_orig_names[c],"; adding to output as constant.") - outidx = name_index_dict[dat_orig_names[c]] # write in correct place - if len(dat) > opts.n_output_samples: - dat_out[:,outidx] = dat[:opts.n_output_samples,outidx] #truncate original data to fit (not ideal) - else: #len(dat) <= n_output_samples (if dat was <, should now be =) - dat_out[:,outidx] = dat[:,outidx] - -# Fill data from PE -for indx in np.arange(len(coord_names)): - vals = samples[coord_names[indx]][indx_list] # load in data for this column - outindx = name_index_dict[ coord_names[indx]] # write in correct place - dat_out[:,outindx] = vals - -# NOTE: if m1 or m2 is "constant" (i.e., not in samples), the possibility for m2 > m1 arises! Re-sort masses here to avoid; use below code. -#if ("m1" not in coord_names) or ("m2" not in coord_names): -# print(" NOTE: re-sorting masses so m1 > m2 (precaution)") -# m1dx = name_index_dict["m1"] -# m1 = np.maximum(dat_out[:,m1dx], dat_out[:,m1dx+1]) #N.B.: assumes m2 col index after m1 col -# m2 = np.minimum(dat_out[:,m1dx], dat_out[:,m1dx+1]) -# dat_out[:,m1dx] = m1 -# dat_out[:,m1dx+1] = m2 - -print(" Saving to ", opts.fname_output_samples+".dat") -np.savetxt(opts.fname_output_samples+".dat",dat_out,header=" lnL sigma_lnL " + ' '.join(dat_orig_names)) - +#!/usr/bin/env python +# +# util_ConstructEOSPosterior.py +# - takes in *generic-format* hyperparameter likelihood data +# - uses *uniform* prior on hyperparameters. [non-uniform priors can be applied by the user with a supplementary function] +# - generates posterior distribution by weighted Monte Carlo +# +# EXAMPLE: +# python `which util_ConstructEOSPosterior.py` --fname fake_int_grid.dat --parameter gamma1 --parameter gamma2 --lnL-offset 50 + +import RIFT.interpolators.BayesianLeastSquares as BayesianLeastSquares + +import argparse +import sys +import numpy as np +import numpy.lib.recfunctions +import scipy +import scipy.stats +import functools +import itertools + +import joblib # http://scikit-learn.org/stable/modules/model_persistence.html + +# GPU acceleration: NOT YET, just do usual +xpy_default=numpy # just in case, to make replacement clear and to enable override +identity_convert = lambda x: x # trivial return itself +cupy_success=False + +no_plots = True +internal_dtype = np.float32 # only use 32 bit storage! Factor of 2 memory savings for GP code in high dimensions + + +try: + import matplotlib.pyplot as plt + from mpl_toolkits.mplot3d import Axes3D + import matplotlib.lines as mlines + import corner + + no_plots=False +except ImportError: + print(" - no matplotlib - ") + + +from sklearn.preprocessing import PolynomialFeatures +if True: +#try: + import RIFT.misc.ModifiedScikitFit as msf # altenative polynomialFeatures +else: +#except: + print(" - Faiiled ModifiedScikitFit : No polynomial fits - ") +from sklearn import linear_model + +from igwn_ligolw import lsctables, utils, ligolw +lsctables.use_in(ligolw.LIGOLWContentHandler) + +import RIFT.integrators.mcsampler as mcsampler +try: + import RIFT.integrators.mcsamplerEnsemble as mcsamplerEnsemble + mcsampler_gmm_ok = True +except: + print(" No mcsamplerEnsemble ") + mcsampler_gmm_ok = False +try: + import RIFT.integrators.mcsamplerGPU as mcsamplerGPU + mcsampler_gpu_ok = True + mcsamplerGPU.xpy_default =xpy_default # force consistent, in case GPU present + mcsamplerGPU.identity_convert = identity_convert +except: + print( " No mcsamplerGPU ") + mcsampler_gpu_ok = False +try: + import RIFT.integrators.mcsamplerAdaptiveVolume as mcsamplerAdaptiveVolume + mcsampler_AV_ok = True +except: + print(" No mcsamplerAV ") + mcsampler_AV_ok = False +try: + import RIFT.integrators.mcsamplerPortfolio as mcsamplerPortfolio + mcsampler_Portfolio_ok = True +except: + print(" No mcsamplerPortolfio ") + + + + + +def add_field(a, descr): + """Return a new array that is like "a", but has additional fields. + + Arguments: + a -- a structured numpy array + descr -- a numpy type description of the new fields + + The contents of "a" are copied over to the appropriate fields in + the new array, whereas the new fields are uninitialized. The + arguments are not modified. + + >>> sa = numpy.array([(1, 'Foo'), (2, 'Bar')], \ + dtype=[('id', int), ('name', 'S3')]) + >>> sa.dtype.descr == numpy.dtype([('id', int), ('name', 'S3')]) + True + >>> sb = add_field(sa, [('score', float)]) + >>> sb.dtype.descr == numpy.dtype([('id', int), ('name', 'S3'), \ + ('score', float)]) + True + >>> numpy.all(sa['id'] == sb['id']) + True + >>> numpy.all(sa['name'] == sb['name']) + True + """ + if a.dtype.fields is None: + raise ValueError("`A' must be a structured numpy array") + b = numpy.empty(a.shape, dtype=a.dtype.descr + descr) + for name in a.dtype.names: + b[name] = a[name] + return b + + +parser = argparse.ArgumentParser() +parser.add_argument("--fname",help="filename of *.dat file (EOS-format: lnL sigma_lnL p1 p2 ... . ASSUME any stacking over events already performed.") +parser.add_argument("--fname-output-samples",default="output-EOS-samples",help="output grid") +parser.add_argument("--fname-output-integral",default="output-EOS-integral",help="for evidencees and pipeline compatibility") +parser.add_argument("--n-output-samples",default=2000,type=int,help="output posterior samples (default 3000)") +parser.add_argument("--eos-param", type=str, default=None, help="parameterization of equation of state [spectral only, for now]") +parser.add_argument("--parameter", action='append', help="Parameter used BOTH as a fit dimension (GP/RF input) AND as a Monte Carlo sampling dimension. Adds to coord_names AND low_level_coord_names. Must be a column in --fname unless --supplementary-coordinate-code is supplied. IF NEITHER --parameter NOR --parameter-implied IS PROVIDED, coord_names defaults to the data file's column list; IF NEITHER --parameter NOR --parameter-nofit IS PROVIDED, low_level_coord_names also defaults to the data file's column list.") +parser.add_argument("--parameter-implied", action='append', help="Parameter used as a fit dimension only -- added to coord_names but NOT sampled independently. The coordinate plugin is responsible for producing it from the data file's columns. Useful for fitting in a different basis (e.g. coord_names=[u,v,w]) than the data is stored in (e.g. dat_orig_names=[x,y,z]).") +#parser.add_argument("--no-adapt-parameter",action='append',help="Disable adaptive sampling in a parameter. Useful in cases where a parameter is not well-constrained, and the a prior sampler is well-chosen.") +parser.add_argument("--parameter-nofit", action='append', help="Parameter used as a sampling dimension only -- added to low_level_coord_names but NOT to the fit basis. Useful when the MC samples in the data-file basis (e.g. dat_orig_names=[x,y,z]) but the fit lives in a transformed basis routed through the coordinate plugin.") +parser.add_argument("--integration-parameter-range",action='append', help="Integration parameter ranges. Syntax is name:[a,b]") +parser.add_argument("--downselect-parameter",action='append', help='Name of parameter to be used to eliminate grid points ') +parser.add_argument("--downselect-parameter-range",action='append',type=str) +parser.add_argument("--no-downselect",action='store_true') +parser.add_argument("--aligned-prior", default="uniform",help="Options are 'uniform', 'volumetric', and 'alignedspin-zprior'") +parser.add_argument("--cap-points",default=-1,type=int,help="Maximum number of points in the sample, if positive. Useful to cap the number of points ued for GP. See also lnLoffset. Note points are selected AT RANDOM") +parser.add_argument("--lambda-max", default=4000,type=float,help="Maximum range of 'Lambda' allowed. Minimum value is ZERO, not negative.") +parser.add_argument("--lnL-shift-prevent-overflow",default=None,type=float,help="Define this quantity to be a large positive number to avoid overflows. Note that we do *not* define this dynamically based on sample values, to insure reproducibility and comparable integral results. BEWARE: If you shift the result to be below zero, because the GP relaxes to 0, you will get crazy answers.") +parser.add_argument("--lnL-offset",type=float,default=np.inf,help="lnL offset") +parser.add_argument("--lnL-cut",type=float,default=None,help="lnL cut [MANUAL]") +parser.add_argument("--sigma-cut",type=float,default=0.6,help="Eliminate points with large error from the fit.") +parser.add_argument("--ignore-errors-in-data",action='store_true',help='Ignore reported error in lnL. Helpful for testing purposes (i.e., if the error is zero)') +parser.add_argument("--lnL-peak-insane-cut",type=float,default=np.inf,help="Throw away lnL greater than this value. Should not be necessary") +parser.add_argument("--verbose", action="store_true",default=False, help="Required to build post-frame-generating sanity-test plots") +parser.add_argument("--save-plots",default=False,action='store_true', help="Write plots to file (only useful for OSX, where interactive is default") +parser.add_argument("--n-max",default=3e5,type=float) +parser.add_argument("--n-step",default=1e5,type=int) +parser.add_argument("--n-eff",default=3e3,type=int) +parser.add_argument("--pool-size",default=3,type=int,help="Integer. Number of GPs to use (result is averaged)") +parser.add_argument("--fit-method",default="rf",help="rf (default) : rf|gp|quadratic|polynomial|gp_hyper|gp_lazy|cov|kde. Note 'polynomial' with --fit-order 0 will fit a constant") +parser.add_argument("--fit-load-gp",default=None,type=str,help="Filename of GP fit to load. Overrides fitting process, but user MUST correctly specify coordinate system to interpret the fit with. Does not override loading and converting the data.") +parser.add_argument("--fit-save-gp",default=None,type=str,help="Filename of GP fit to save. ") +parser.add_argument("--fit-order",type=int,default=2,help="Fit order (polynomial case: degree)") +parser.add_argument("--no-plots",action='store_true') +parser.add_argument("--using-eos-type", type=str, default=None, help="Name of EOS parameterization (must match what is used for inputs). Will use EOS parameterization to identify appropriate field headers") +parser.add_argument("--sampler-method",default="adaptive_cartesian",help="adaptive_cartesian|GMM|adaptive_cartesian_gpu") +parser.add_argument("--sampler-portfolio",default=None,action='append',type=str,help="comma-separated strings, matching sampler methods other than portfolio") +parser.add_argument("--sampler-portfolio-args",default=None, action='append', type=str, help='eval-able dictionary to be passed to that sampler_') +parser.add_argument("--internal-use-lnL",action='store_true',help="integrator internally manipulates lnL.. ") +parser.add_argument("--internal-correlate-parameters",default=None,type=str,help="comman-separated string indicating parameters that should be sampled allowing for correlations. Must be sampling parameters. Only implemented for gmm. If string is 'all', correlate *all* parameters") +parser.add_argument("--internal-n-comp",default=1,type=int,help="number of components to use for GMM sampling. Default is 1, because we expect a unimodal posterior in well-adapted coordinates. If you have crappy coordinates, use more") +parser.add_argument("--force-no-adapt",action='store_true',help="Disable adaptation, both of the tempering exponent *and* the individual sampling prior(s)") +parser.add_argument("--tripwire-fraction",default=0.05,type=float,help="Fraction of nmax of iterations after which n_eff needs to be greater than 1+epsilon for a small number epsilon") + +# Supplemental likelihood factors: convenient way to effectively change the mass/spin prior in arbitrary ways for example +# Note this supplemental factor is passed the *fitting* arguments, directly. Use with extreme caution, since we often change the dimension in a DAG +parser.add_argument("--supplementary-likelihood-factor-code", default=None,type=str,help="Import a module (in your pythonpath!) containing a supplementary factor for the likelihood. Used to impose supplementary external priors of arbitrary complexity and external dependence (e.g., imposing alternate EOS priors)") +parser.add_argument("--supplementary-likelihood-factor-function", default=None,type=str,help="With above option, specifies the specific function used as an external likelihood. EXPERTS ONLY") +parser.add_argument("--supplementary-likelihood-factor-ini", default=None,type=str,help="With above option, specifies an ini file that is parsed (here) and passed to the preparation code, called when the module is first loaded, to configure the module. EXPERTS ONLY") +parser.add_argument("--supplementary-coordinate-code", default=None,type=str,help="Coordinate conversion/prior code. Accepts: the literal 'rift_default' (use RIFT.lalsimutils.convert_waveform_coordinates plus RIFT-standard priors); a filesystem path ending in .py (loaded as a plugin); or any importable dotted module name. See RIFT.misc.coordinate_plugin for the interface plugins must implement.") +parser.add_argument("--supplementary-coordinate-function", default=None, type=str, help="Name of the entry-point callable inside the module named by --supplementary-coordinate-code. Defaults to 'convert_coordinates'.") +parser.add_argument("--supplementary-coordinate-ini", default=None, type=str, help="Optional ini file parsed and handed to the coordinate plugin's prepare() hook so it can read its own configuration block(s).") +parser.add_argument("--supplementary-coordinate-chart", default=None, type=str, help="Which chart (coordinate system) defined by the plugin to use for this run. Required when the plugin's CHARTS dict has more than one entry; ignored when the plugin doesn't define CHARTS. Different charts can share parameter names but imply different priors -- the chart name disambiguates which (name -> prior) mapping is installed.") +opts= parser.parse_args() + +#print(" WARNING: Always use internal_use_lnL for now ") +#opts.internal_use_lnL=True + +no_plots = no_plots | opts.no_plots +lnL_shift = 0 +lnL_default_large_negative = -500 +if opts.lnL_shift_prevent_overflow: + lnL_shift = opts.lnL_shift_prevent_overflow + + + +### Comparison data (from LI) +### + +downselect_dict = {} +dlist = [] +dlist_ranges=[] +if opts.downselect_parameter: + dlist = opts.downselect_parameter + dlist_ranges = map(eval,opts.downselect_parameter_range) +else: + dlist = [] + dlist_ranges = [] +if len(dlist) != len(dlist_ranges): + print(" downselect parameters inconsistent", dlist, dlist_ranges) +for indx in np.arange(len(dlist_ranges)): + downselect_dict[dlist[indx]] = dlist_ranges[indx] + +if opts.no_downselect: + downselect_dict={} + + +test_converged={} + +### +### Retrieve data +### +# int_sig sigma/L gamma1 gamma2 ... +col_lnL = 0 +dat_orig = dat = np.loadtxt(opts.fname) +dat_orig = dat[dat[:,col_lnL].argsort()] # sort http://stackoverflow.com/questions/2828059/sorting-arrays-in-numpy-by-column +print(" Original data size = ", len(dat), dat.shape) +dat_orig_names = None +with open(opts.fname,'r') as f: + header_str = f.readline() + header_str = header_str.rstrip() +dat_orig_names = header_str.replace('#','').split()[2:] + +### +### Parameters in use +### + +# Decoupled fit basis vs Monte Carlo sampling basis -- mirrors the +# convention established by util_ConstructIntrinsicPosterior_GenericCoordinates.py +# and required for the new coordinate-plugin path: +# +# --parameter X -> X is BOTH a fit (GP/RF) and a sampling (MC) dim +# --parameter-implied X-> X is a fit dim ONLY (the plugin produces it from +# dat_orig_names; the MC integrator never sees it) +# --parameter-nofit X -> X is a sampling dim ONLY (the MC integrates over +# it; the fit never sees it). Typical use: MC in +# the data-file basis while the fit lives in a +# transformed basis routed through the plugin. +# +# Legacy fallback (preserves the pre-decoupling default): if the user +# supplies neither --parameter nor --parameter-implied, coord_names +# defaults to the data file's column list. Likewise low_level_coord_names +# defaults to the data file's columns when --parameter and --parameter-nofit +# are both absent. That way a bare invocation -- no flags -- still does +# "fit on every column in the file, MC sample in the same basis", which +# is what every existing hyperpipe / EOS-posterior demo relies on. +_user_params = list(opts.parameter) if opts.parameter else [] +_user_implied = list(opts.parameter_implied) if opts.parameter_implied else [] +_user_nofit = list(opts.parameter_nofit) if opts.parameter_nofit else [] + +if not _user_params and not _user_implied: + coord_names = list(dat_orig_names) # legacy default +else: + coord_names = _user_params + _user_implied # fit basis + +if not _user_params and not _user_nofit: + low_level_coord_names = list(dat_orig_names) # legacy default +else: + low_level_coord_names = _user_params + _user_nofit # MC basis + +error_factor = len(coord_names) +name_index_dict ={} +for name in dat_orig_names: + try: + name_index_dict[name] = 2+dat_orig_names.index(name) + except: + raise Exception(" Currently fitting parameter names must match columns in data file ") +# TeX dictionary +print(" Coordinate names for fit :, ", coord_names, " from ", dat_orig_names, " indexed as ", name_index_dict) +print(" Coordinate names for Monte Carlo :, ", low_level_coord_names) + + +### +### Integration ranges +### + +param_ranges = {} +for range_code in opts.integration_parameter_range: + name, range_str = range_code.split(':') + range_expr = eval(range_str) # define. Better to split on , for example + param_ranges[name] = np.array(range_expr) + +# Add in integration range for everything else, if nothing specified +for name in dat_orig_names: + if not name in param_ranges: + vals = dat_orig[:,name_index_dict[name]] + param_ranges[name] = [np.min(vals), np.max(vals)] + +### +### Prior functions : default is UNIFORM, since it is unmodeled and generic +### + +def uniform_prior(x): + return np.ones(x.shape) + +prior_map = {} +for name in low_level_coord_names: + prior_map[name] = uniform_prior + if not(name in param_ranges): + raise Exception(" {} not provided a parameter range ".format(name)) # change later, should fall back to using prior range from above + + +prior_range_map = param_ranges + +# prior_map = { 'gamma1':eos_param_uniform_prior, 'gamma2':eos_param_uniform_prior, +# } +# # Les: somewhat more aggressive: +# # gamma1: 0.2,2 +# # gamma2: -1.67, 1.7 +# prior_range_map = { 'gamma1': [0.707899,1.31], 'gamma2':[-1.6,1.7], 'gamma3':[-0.6,0.6], 'gamma4':[-0.02,0.02] +# } + + +### +### Supplemental likelihood: load (as in ILE) +### +supplemental_ln_likelihood= None +supplemental_ln_likelihood_prep =None +supplemental_ln_likelihood_parsed_ini=None +# Supplemental likelihood factor. Must have identical call sequence to 'likelihood_function'. Called with identical raw inputs (including cosines/etc) +if opts.supplementary_likelihood_factor_code and opts.supplementary_likelihood_factor_function: + print(" EXTERNAL SUPPLEMENTARY LIKELIHOOD FACTOR : {}.{} ".format(opts.supplementary_likelihood_factor_code,opts.supplementary_likelihood_factor_function)) + __import__(opts.supplementary_likelihood_factor_code) + external_likelihood_module = sys.modules[opts.supplementary_likelihood_factor_code] + supplemental_ln_likelihood = getattr(external_likelihood_module,opts.supplementary_likelihood_factor_function) + name_prep = "prepare_"+opts.supplementary_likelihood_factor_function + if hasattr(external_likelihood_module,name_prep): + supplemental_ln_likelhood_prep=getattr(external_likelihood_module,name_prep) + # Check for and load in ini file associated with external library + if opts.supplementary_likelihood_factor_ini: + import configparser as ConfigParser + config = ConfigParser.ConfigParser() + config.optionxform=str # force preserve case! + config.read(opts.supplementary_likelihood_factor_ini) + supplemental_ln_likelhood_parsed_ini=config + + # Call the ini file, tell it what coordinates we are using by name + supplemental_ln_likelihood_prep(config=supplemental_ln_likelihood_parsed_ini,coords=coord_names) + +supplemental_coordinate_convert = None +if opts.supplementary_coordinate_code: + # Resolve the user-supplied coordinate-convert plugin. The loader + # accepts three forms in --supplementary-coordinate-code: the literal + # 'rift_default', a filesystem path to a .py file, or an importable + # dotted module name. The plugin must expose a callable named by + # --supplementary-coordinate-function (default 'convert_coordinates') + # with the signature (x_in, coord_names, low_level_coord_names, **kwargs) + # returning a 2-D ndarray of shape (N, len(coord_names)). Plugins may + # optionally define prepare() (one-shot setup, gets the parsed ini and + # the active coord-name lists) and register_priors() (mutate prior_map + # in place). See RIFT.misc.coordinate_plugin for the full contract. + from RIFT.misc.coordinate_plugin import load_coordinate_converter + supplemental_coordinate_convert, _coord_plugin_module = load_coordinate_converter( + spec=opts.supplementary_coordinate_code, + function_name=opts.supplementary_coordinate_function, + ini_path=opts.supplementary_coordinate_ini, + coord_names=coord_names, + low_level_coord_names=low_level_coord_names, + chart=opts.supplementary_coordinate_chart, + opts=opts, + prior_map=prior_map, + prior_range_map=prior_range_map, + ) + +from sklearn.gaussian_process import GaussianProcessRegressor +from sklearn.gaussian_process.kernels import RBF, WhiteKernel, ConstantKernel as C + +def adderr(y): + val,err = y + return val+error_factor*err + +def fit_gp(x,y,x0=None,symmetry_list=None,y_errors=None,hypercube_rescale=False,fname_export="gp_fit"): + """ + x = array so x[0] , x[1], x[2] are points. + """ + + # If we are loading a fit, override everything else + if opts.fit_load_gp: + print(" WARNING: Do not re-use fits across architectures or versions : pickling is not transferrable ") + my_gp=joblib.load(opts.fit_load_gp) + return lambda x:my_gp.predict(x) + + # Amplitude: + # - We are fitting lnL. + # - We know the scale more or less: more than 2 in the log is bad + # Scale + # - because of strong correlations with chirp mass, the length scales can be very short + # - they are rarely very long, but at high mass can be long + # - I need to allow for a RANGE + + length_scale_est = [] + length_scale_bounds_est = [] + for indx in np.arange(len(x[0])): + # These length scales have been tuned by expereience + length_scale_est.append( 2*np.std(x[:,indx]) ) # auto-select range based on sampling retained + length_scale_min_here= np.max([1e-3,0.2*np.std(x[:,indx]/np.sqrt(len(x)))]) + length_scale_bounds_est.append( (length_scale_min_here , 5*np.std(x[:,indx]) ) ) # auto-select range based on sampling *RETAINED* (i.e., passing cut). Note that for the coordinates I usually use, it would be nonsensical to make the range in coordinate too small, as can occasionally happens + + print(" GP: Input sample size ", len(x), len(y)) + print(" GP: Estimated length scales ") + print(length_scale_est) + print(length_scale_bounds_est) + + if not (hypercube_rescale): + # These parameters have been hand-tuned by experience to try to set to levels comparable to typical lnL Monte Carlo error + kernel = WhiteKernel(noise_level=0.1,noise_level_bounds=(1e-2,1))+C(0.5, (1e-3,1e1))*RBF(length_scale=length_scale_est, length_scale_bounds=length_scale_bounds_est) + gp = GaussianProcessRegressor(kernel=kernel, n_restarts_optimizer=8) + + gp.fit(x,y) + + print(" Fit: std: ", np.std(y - gp.predict(x)), "using number of features ", len(y)) + + if opts.fit_save_gp: + print(" Attempting to save fit ", opts.fit_save_gp+".pkl") + joblib.dump(gp,opts.fit_save_gp+".pkl") + + return lambda x: gp.predict(x) + else: + x_scaled = np.zeros(x.shape) + x_center = np.zeros(len(length_scale_est)) + x_center = np.mean(x) + print(" Scaling data to central point ", x_center) + for indx in np.arange(len(x)): + x_scaled[indx] = (x[indx] - x_center)/length_scale_est # resize + + kernel = WhiteKernel(noise_level=0.1,noise_level_bounds=(1e-2,1))+C(0.5, (1e-3,1e1))*RBF( len(x_center), (1e-3,1e1)) + gp = GaussianProcessRegressor(kernel=kernel, n_restarts_optimizer=8) + + gp.fit(x_scaled,y) + print(" Fit: std: ", np.std(y - gp.predict(x_scaled)), "using number of features ", len(y)) # should NOT be perfect + + return lambda x,x0=x_center,scl=length_scale_est: gp.predict( (x-x0 )/scl) + +def map_funcs(func_list,obj): + return [func(obj) for func in func_list] +def fit_gp_pool(x,y,n_pool=10,**kwargs): + """ + Split the data into 10 parts, and return a GP that averages them + """ + x_copy = np.array(x) + y_copy = np.array(y) + indx_list =np.arange(len(x_copy)) + np.random.shuffle(indx_list) # acts in place + partition_list = np.array_split(indx_list,n_pool) + gp_fit_list =[] + for part in partition_list: + print(" Fitting partition ") + gp_fit_list.append(fit_gp(x[part],y[part],**kwargs)) + fn_out = lambda x: np.mean( map_funcs( gp_fit_list,x), axis=0) + print(" Testing ", fn_out([x[0]])) + return fn_out + + +def fit_rf(x,y,y_errors=None,fname_export='nn_fit'): +# from sklearn.ensemble import RandomForestRegressor + from sklearn.ensemble import ExtraTreesRegressor + # Instantiate model. Usually not that many structures to find, don't overcomplicate + # - should scale like number of samples + rf = ExtraTreesRegressor(n_estimators=100, verbose=True,n_jobs=-1) # no more than 5% of samples in a leaf + if y_errors is None: + rf.fit(x,y) + else: + rf.fit(x,y,sample_weight=1./y_errors**2) + + ### reject points with infinities : problems for inputs + def fn_return(x_in,rf=rf): + f_out = -lnL_default_large_negative*np.ones(len(x_in)) + # remove infinity or Nan + indx_ok = np.all(np.isfinite(np.array(x_in,dtype=float)),axis=-1) + # rf internally uses float32, so we need to remove points > 10^37 or so ! + # ... this *should* never happen due to bounds constraints, but ... + indx_ok_size = np.all( np.logical_not(np.greater(np.abs(x_in),1e37)), axis=-1) + indx_ok = np.logical_and(indx_ok, indx_ok_size) + f_out[indx_ok] = rf.predict(x_in[indx_ok]) + return f_out +# fn_return = lambda x_in: rf.predict(x_in) + + print( " Demonstrating RF") # debugging + residuals = rf.predict(x)-y + print( " std ", np.std(residuals), np.max(y), np.max(fn_return(x))) + return fn_return + + + + + +# initialize +dat_mass = [] +weights = [] +n_params = -1 + + + ### + ### Convert data. RIGHT NOW JUST DOWNSELECTING, no intermediate fitting parameters defined + ### + +# Naive convert: no downselect. +if (supplemental_coordinate_convert ==None): + + # The identity convert_coords below only makes sense when the fit + # basis equals the MC sampling basis equals a permutation of the + # data file's columns. Catch the new "split-basis" misconfiguration + # early, otherwise the integrator silently feeds samples in + # low_level_coord_names through an identity into a fit built on + # coord_names. + if list(low_level_coord_names) != list(coord_names): + raise ValueError( + " EOSPosterior: --parameter-implied / --parameter-nofit make " + "the fit basis ({coord!r}) differ from the MC sampling basis " + "({low!r}), but no --supplementary-coordinate-code was " + "supplied. The integrator cannot translate between the two " + "bases without a converter.".format( + coord=list(coord_names), + low=list(low_level_coord_names), + ) + ) + + indx_of_orig_names = np.array([ dat_orig_names.index(coord_names[k]) for k in range(len(coord_names))]) + dat_out = [] + for line in dat: + dat_here= np.zeros(len(coord_names)+2) + if line[col_lnL+1] > opts.sigma_cut: + print("skipping", line) + continue + dat_here[:-2] = line[indx_of_orig_names+2]#line[2:len(coord_names)+2] # modify to use names! + dat_here[-2] = line[0] + dat_here[-1] = line[1] + dat_out.append(dat_here) + dat_out= np.array(dat_out) + + # Repack data, WHOLE SET + X =dat_out[:,0:len(coord_names)] + Y = dat_out[:,-2] + if np.max(Y)<0 and lnL_shift ==0: + lnL_shift = -100 - np.max(Y) # force it to be offset/positive -- may help some configurations. Remember our adaptivity is silly. + Y_err = dat_out[:,-1] + def convert_coords(x): + return x + +else: + # Pack data, using coordinate converter. Note later calculations MUST use the converter. + # + # Two distinct call sites for the converter, with two different + # input bases -- this is the change that decouples the fit from + # the MC sampling basis: + # + # (1) The initial dat->X conversion below feeds rows whose + # columns are ordered by dat_orig_names (the data file's + # header). So we pass low_level_coord_names=dat_orig_names + # at this site. + # + # (2) The convert_coords closure is what the integrator calls + # on every Monte Carlo sample. The sampler operates in + # low_level_coord_names (we add_parameter() over that list + # below), so the closure must claim its inputs are in + # low_level_coord_names -- NOT dat_orig_names. Pre-fix this + # was hardcoded to dat_orig_names, which only happened to + # work when low_level_coord_names == dat_orig_names (i.e. + # the legacy case). For any non-trivial plugin where the + # MC samples in a different basis than the file's columns, + # the old behaviour applied the rotation an extra time and + # silently mis-evaluated lnL. + X = supplemental_coordinate_convert(dat[:,2:], coord_names=coord_names, low_level_coord_names=dat_orig_names) # convert and generate X + Y = dat[:,0] + Y_err = dat[:,1] + if np.max(Y)<0 and lnL_shift ==0: + lnL_shift = -100 - np.max(Y) # force it to be offset/positive -- may help some configurations. Remember our adaptivity is silly. + def convert_coords(x_in, _low=low_level_coord_names, _coord=coord_names): + # _low / _coord captured as defaults so the closure stays correct + # even if either list mutates later in the script. + return supplemental_coordinate_convert(x_in, coord_names=_coord, low_level_coord_names=_low) +# Save copies for later (plots) +X_orig = X.copy() +Y_orig = Y.copy() + + + +# Eliminate values with Y too small +max_lnL = np.max(Y) +if np.isinf(opts.lnL_offset): + indx_ok= np.ones(len(Y),dtype=bool) # default case, we preserve all the data +else: + indx_ok = np.array(Y>np.max(Y)-opts.lnL_offset,dtype=bool) # force cast : sometimes indx_ok is a mappable object? +n_ok = np.sum(indx_ok) +# Provide some random points, to insure reasonable tapering behavior away from the sample +print(" Points used in fit : ", n_ok, " out of ", len(indx_ok), " given max lnL ", max_lnL) +if max_lnL < 10 and np.mean(Y) > -10: # second condition to allow synthetic tests not to fail, as these often have maxlnL not large + print(" Resetting to use ALL input data -- beware ! ") + # nothing matters, we will reject it anyways + indx_ok = np.ones(len(Y),dtype=bool) +elif n_ok < 10: # and max_lnL > 30: + # mark the top 10 elements and use them for fits + # this may be VERY VERY DANGEROUS if the peak is high and poorly sampled + idx_sorted_index = np.lexsort((np.arange(len(Y)), Y)) # Sort the array of Y, recovering index values + indx_list = np.array( [[k, Y[k]] for k in idx_sorted_index]) # pair up with the weights again + indx_list = indx_list[::-1] # reverse, so most significant are first + indx_ok = list(map(int,indx_list[:10,0])) + print(" Revised number of points for fit: ", np.sum(indx_ok), len(indx_ok), indx_list[:10]) +X_raw = X.copy() + + + +my_fit= None +if opts.fit_method =='gp': + print(" FIT METHOD : GP") + # some data truncation IS used for the GP, but beware + print(" Truncating data set used for GP, to reduce memory usage needed in matrix operations") + X=X[indx_ok] + Y=Y[indx_ok] - lnL_shift + Y_err = Y_err[indx_ok] + # Cap the total number of points retained, AFTER the threshold cut + if opts.cap_points< len(Y) and opts.cap_points> 100: + n_keep = opts.cap_points + indx = np.random.choice(np.arange(len(Y)),size=n_keep,replace=False) + Y=Y[indx] + X=X[indx] + Y_err=Y_err[indx] + if opts.ignore_errors_in_data: + Y_err=None + my_fit = fit_gp(X,Y,y_errors=Y_err) +elif opts.fit_method == 'rf': + print( " FIT METHOD ", opts.fit_method, " IS RF ") + # NO data truncation for NN needed? To be *consistent*, have the code function the same way as the others + X=X[indx_ok] + Y=Y[indx_ok] - lnL_shift + Y_err = Y_err[indx_ok] + # Cap the total number of points retained, AFTER the threshold cut + if opts.cap_points< len(Y) and opts.cap_points> 100: + n_keep = opts.cap_points + indx = np.random.choice(np.arange(len(Y)),size=n_keep,replace=False) + Y=Y[indx] + X=X[indx] + Y_err=Y_err[indx] + if opts.ignore_errors_in_data: + Y_err=None + my_fit = fit_rf(X,Y,y_errors=Y_err) + + +# Sort for later convenience (scatterplots, etc) +indx = Y.argsort()#[::-1] +X=X[indx] +Y=Y[indx] + + + +### +### Integrate posterior +### + + +sampler = mcsampler.MCSampler() +if opts.sampler_method == "adaptive_cartesian_gpu": + sampler = mcsamplerGPU.MCSampler() + sampler.xpy = xpy_default + sampler.identity_convert=identity_convert + mcsampler = mcsamplerGPU # force use of routines in that file, for properly configured GPU-accelerated code as needed + + # if opts.sampler_xpy == "numpy": + # mcsampler.set_xpy_to_numpy() + # sampler.xpy= numpy + # sampler.identity_convert= lambda x: x +if opts.sampler_method == "GMM": + sampler = mcsamplerEnsemble.MCSampler() +elif opts.sampler_method == "AV": + sampler = mcsamplerAdaptiveVolume.MCSampler() + opts.internal_use_lnL= True # required! +elif opts.sampler_method == "portfolio": + use_portfolio=True + sampler = None + sampler_list = [] + sampler_types = opts.sampler_portfolio + for name in sampler_types: + if name =='AV': + sampler = mcsamplerAdaptiveVolume.MCSampler() + if name =='GMM': + sampler = mcsamplerEnsemble.MCSampler() + opts.sampler_method = 'GMM' # this will force the creation/parsing of GMM-specific arguments below, so they are properly passed + if name == "adaptive_cartesian_gpu": + sampler = mcsamplerGPU.MCSampler() + sampler.xpy = xpy_default + sampler.identity_convert=identity_convert + if name == 'NFlow': + # expensive import, only do if requested + try: + import RIFT.integrators.mcsamplerNFlow as mcsamplerNFlow + mcsampler_NF_ok = True + except: + print(" No mcsamplerNFlow ") + continue + sampler = mcsamplerNFlow.MCSampler() + sampler.xpy = xpy_default + sampler.identity_convert=identity_convert + if sampler is None: + # Don't add unknown type + continue + print('PORTFOLIO: adding {} '.format(name)) + sampler_list.append(sampler) + sampler = mcsamplerPortfolio.MCSampler(portfolio=sampler_list) + + +## +## Loop over param names +## +# IMPORTANT: iterate over low_level_coord_names, not coord_names. The +# sampler operates in the MC basis. coord_names is the FIT basis, which +# only the GP/RF and the convert_coords closure see. Pre-decoupling this +# loop used coord_names because the two lists were forced to be equal. +for p in low_level_coord_names: + prior_here = prior_map[p] + range_here = prior_range_map[p] + + sampler.add_parameter(p, pdf=np.vectorize(lambda x:1), prior_pdf=prior_here,left_limit=range_here[0],right_limit=range_here[1],adaptive_sampling=True) + +likelihood_function = None +log_likelihood_function = None +def log_likelihood_function(*args): + return my_fit(convert_coords(np.array([*args]).T )) + +# Fixed-arity wrappers around log_likelihood_function / likelihood_function. +# +# mcsampler's adaptive code introspects the wrapped function's argument +# count, so we generate one definition per supported dimensionality. The +# arity that matters is the MC SAMPLING dimensionality +# (len(low_level_coord_names)), not the fit dimensionality +# (len(coord_names)) -- the sampler passes one positional per +# low_level_coord_name and then convert_coords maps that batch into the +# fit basis. Pre-decoupling, the dispatch keyed on len(coord_names), +# which was only correct when low_level_coord_names == coord_names. +# +# Scalar branches also go through convert_coords so a non-trivial +# converter does not silently get bypassed when an internal caller hands +# in a single scalar sample. +def _scalar_to_lnL_input(args_tuple): + # Wrap an N-tuple of scalars into a (1, N) row, push it through the + # converter, and return -- shape (1, len(coord_names)) -- ready for my_fit. + return convert_coords(np.array([args_tuple], dtype=float)) + +_LN_LOW_DIM = len(low_level_coord_names) +if _LN_LOW_DIM == 1: + def likelihood_function(x): + if isinstance(x, float): + return np.exp(my_fit(_scalar_to_lnL_input((x,)))) + return np.exp(my_fit(convert_coords(np.c_[x]))) +elif _LN_LOW_DIM == 2: + def likelihood_function(x, y): + if isinstance(x, float): + return np.exp(my_fit(_scalar_to_lnL_input((x, y)))) + return np.exp(my_fit(convert_coords(np.c_[x, y]))) +elif _LN_LOW_DIM == 3: + def likelihood_function(x, y, z): + if isinstance(x, float): + return np.exp(my_fit(_scalar_to_lnL_input((x, y, z)))) + return np.exp(my_fit(convert_coords(np.c_[x, y, z]))) +elif _LN_LOW_DIM == 4: + def likelihood_function(x, y, z, a): + if isinstance(x, float): + return np.exp(my_fit(_scalar_to_lnL_input((x, y, z, a)))) + return np.exp(my_fit(convert_coords(np.c_[x, y, z, a]))) +elif _LN_LOW_DIM == 5: + def likelihood_function(x, y, z, a, b): + if isinstance(x, float): + return np.exp(my_fit(_scalar_to_lnL_input((x, y, z, a, b)))) + return np.exp(my_fit(convert_coords(np.c_[x, y, z, a, b]))) +elif _LN_LOW_DIM == 6: + def likelihood_function(x, y, z, a, b, c): + if isinstance(x, float): + return np.exp(my_fit(_scalar_to_lnL_input((x, y, z, a, b, c)))) + return np.exp(my_fit(convert_coords(np.c_[x, y, z, a, b, c]))) +elif _LN_LOW_DIM == 7: + def likelihood_function(x, y, z, a, b, c, d): + if isinstance(x, float): + return np.exp(my_fit(_scalar_to_lnL_input((x, y, z, a, b, c, d)))) + return np.exp(my_fit(convert_coords(np.c_[x, y, z, a, b, c, d]))) +elif _LN_LOW_DIM == 8: + def likelihood_function(x, y, z, a, b, c, d, e): + if isinstance(x, float): + return np.exp(my_fit(_scalar_to_lnL_input((x, y, z, a, b, c, d, e)))) + return np.exp(my_fit(convert_coords(np.c_[x, y, z, a, b, c, d, e]))) +elif _LN_LOW_DIM == 9: + def likelihood_function(x, y, z, a, b, c, d, e, f): + if isinstance(x, float): + return np.exp(my_fit(_scalar_to_lnL_input((x, y, z, a, b, c, d, e, f)))) + return np.exp(my_fit(convert_coords(np.c_[x, y, z, a, b, c, d, e, f]))) +elif _LN_LOW_DIM == 10: + def likelihood_function(x, y, z, a, b, c, d, e, f, g): + if isinstance(x, float): + return np.exp(my_fit(_scalar_to_lnL_input((x, y, z, a, b, c, d, e, f, g)))) + return np.exp(my_fit(convert_coords(np.c_[x, y, z, a, b, c, d, e, f, g]))) +else: + raise NotImplementedError( + " EOSPosterior currently only ships fixed-arity likelihood_function " + "wrappers for 1..10 sampling dimensions; got " + "{} (low_level_coord_names={!r}).".format(_LN_LOW_DIM, low_level_coord_names) + ) + + + + +n_step = opts.n_step +my_exp = np.min([1,0.8*np.log(n_step)/np.max(Y)]) # target value : scale to slightly sublinear to (n_step)^(0.8) for Ymax = 200. This means we have ~ n_step points, with peak value wt~ n_step^(0.8)/n_step ~ 1/n_step^(0.2), limiting contrast +if np.max(Y_orig) < 0: # for now, don't use a weight exponent if we are negative: can't use guess based from GW experience + my_exp = 1 +#my_exp = np.max([my_exp, 1/np.log(n_step)]) # do not allow extreme contrast in adaptivity, to the point that one iteration will dominate +print(" Weight exponent ", my_exp, " and peak contrast (exp)*lnL = ", my_exp*np.max(Y), "; exp(ditto) = ", np.exp(my_exp*np.max(Y)), " which should ideally be no larger than of order the number of trials in each epoch, to insure reweighting doesn't select a single preferred bin too strongly. Note also the floor exponent also constrains the peak, de-facto") + + +extra_args={} +if opts.sampler_method == "GMM": + n_max_blocks = ((1.0*int(opts.n_max))/n_step) + n_comp = opts.internal_n_comp # default + def parse_corr_params(my_str): + """ + Takes a string with no spaces, and returns a tuple + """ + corr_param_names = my_str.replace(',',' ').split() + corr_param_indexes = [] + for param in corr_param_names: + try: + indx = low_level_coord_names.index(param) + corr_param_indexes.append(indx) + except: + continue + return tuple(corr_param_indexes) + if opts.internal_correlate_parameters == 'all': + gmm_dict = {tuple(range(len(low_level_coord_names))):None} # integrate *jointly* in all parameters together + elif not (opts.internal_correlate_parameters is None): + # Correlate identified parameters + my_blocks = opts.internal_correlate_parameters.split() + my_tuples = list(map( parse_corr_params, my_blocks)) + gmm_dict = {x:None for x in my_tuples} + print(" GMM: Proposed correlated ", gmm_dict) + # What about un-labelled parameters? Make a null tuple for them as well + correlated_params = set(); correlated_params = correlated_params.union( *list(map(set,my_tuples))) + uncorrelated_params = set(np.arange(len(low_level_coord_names))); + uncorrelated_params = uncorrelated_params.difference(correlated_params) + for x in uncorrelated_params: + gmm_dict[(x,)] = None + print( " Using correlated GMM sampling on sampling variable indexes " , gmm_dict, " out of ", low_level_coord_names) + else: + param_indexes = range(len(low_level_coord_names)) + gmm_dict = {(k,):None for k in param_indexes} # no correlations +# lnL_offset_saving = opts.lnL_offset + lnL_offset_saving = -20 # for simplicity, hardcode for now for preserving points + print("GMM ", gmm_dict) + extra_args = {'n_comp':n_comp,'max_iter':n_max_blocks,'L_cutoff': None,'gmm_dict':gmm_dict,'max_err':50, 'lnw_failure_cut':-np.inf} # made up for now, should adjust +extra_args.update({ + "n_adapt": 100, # Number of chunks to allow adaption over + "history_mult": 10, # Multiplier on 'n' - number of samples to estimate marginalized 1D histograms with, + "force_no_adapt":opts.force_no_adapt, + "tripwire_fraction":opts.tripwire_fraction +}) + +fn_passed = likelihood_function +if supplemental_ln_likelihood: + fn_passed = lambda *x: likelihood_function(*x)*np.exp(supplemental_ln_likelihood(*x)) +if opts.internal_use_lnL: + fn_passed = log_likelihood_function # helps regularize large values + if supplemental_ln_likelihood: + fn_passed = lambda *x: log_likelihood_function(*x) + supplemental_ln_likelihood(*x) + extra_args.update({"use_lnL":True,"return_lnI":True}) + + + +res, var, neff, dict_return = sampler.integrate(fn_passed, *low_level_coord_names, verbose=True,nmax=int(opts.n_max),n=n_step,neff=opts.n_eff, save_intg=True,tempering_adapt=True, floor_level=1e-3,igrand_threshold_p=1e-3,convergence_tests=test_converged,adapt_weight_exponent=my_exp,no_protect_names=True,**extra_args) # MC integrates in the SAMPLING basis (low_level_coord_names); convert_coords routes each sample into the fit basis (coord_names) before evaluating the GP/RF + + +# Save result -- needed for odds ratios, etc. +np.savetxt(opts.fname_output_integral, [np.log(res)]) + +if neff < len(coord_names): + print(" PLOTS WILL FAIL ") + print(" Not enough independent Monte Carlo points to generate useful contours") + + +samples = sampler._rvs +print(samples.keys()) +# sampler._rvs is keyed by the SAMPLING basis (low_level_coord_names). +# Look up sample arrays by names that actually exist in the dict. +n_params = len(low_level_coord_names) +dat_mass = np.zeros((len(samples[low_level_coord_names[0]]),n_params+3)) +if not(opts.internal_use_lnL): + dat_logL = np.log(samples["integrand"]) +else: + if 'log_integrand' in samples: + dat_logL = samples['log_integrand'] + else: + dat_logL = samples["integrand"] +lnLmax = np.max(dat_logL[np.isfinite(dat_logL)]) +print(" Max lnL ", np.max(dat_logL)) + +n_ESS = -1 +if True: + # Compute n_ESS. Should be done by integrator! + if 'log_joint_s_prior' in samples: + weights_scaled = np.exp(dat_logL - lnLmax + samples["log_joint_prior"] - samples["log_joint_s_prior"]) + # dictionary, write this to enable later use of it + samples["joint_s_prior"] = np.exp(samples["log_joint_s_prior"]) + samples["joint_prior"] = np.exp(samples["log_joint_prior"]) + else: + weights_scaled = np.exp(dat_logL - lnLmax)*sampler._rvs["joint_prior"]/sampler._rvs["joint_s_prior"] + weights_scaled = weights_scaled/np.max(weights_scaled) # try to reduce dynamic range + n_ESS = np.sum(weights_scaled)**2/np.sum(weights_scaled**2) + print(" n_eff n_ESS ", neff, n_ESS) + + +# Throw away stupid points that don't impact the posterior +indx_ok = np.ones(len(dat_logL),dtype=bool) +if not('log_joint_s_prior' in samples): + indx_ok=samples["joint_s_prior"]>0 +indx_ok = np.logical_and(dat_logL > np.max(dat_logL)-opts.lnL_offset ,indx_ok) +# Mask in the sampling basis -- samples dict is keyed by low_level_coord_names. +for p in low_level_coord_names: + samples[p] = samples[p][indx_ok] +dat_logL = dat_logL[indx_ok] +print(samples.keys()) +samples["joint_prior"] =samples["joint_prior"][indx_ok] +samples["joint_s_prior"] =samples["joint_s_prior"][indx_ok] + + + +### +### 1d posteriors of the coordinates used for sampling [EQUALLY WEIGHTED, BIASED because physics cuts aren't applied] +### + +p = samples["joint_prior"] +ps =samples["joint_s_prior"] +lnL = dat_logL +lnLmax = np.max(lnL) +weights = np.exp(lnL-lnLmax)*p/ps + + + +print(" ---- Subset for posterior samples (and further corner work) --- ") + + +p_norm = (weights/np.sum(weights)) +indx_list = np.random.choice(np.arange(len(weights)), p=p_norm.astype(np.float64),size=opts.n_output_samples) + + +dat_out = np.zeros( (opts.n_output_samples,2+len(dat_orig_names)) ) + +# Initialize fixed parameters. +# +# Output columns are dat_orig_names (the file's basis). For any name in +# dat_orig_names that is NOT in the sampling basis, we fill the output +# column with the original grid's value -- it's a "constant" placeholder +# for that column. +# +# Pre-decoupling this test was against coord_names (the fit basis), which +# was the same list as low_level_coord_names whenever the fit basis equaled +# the sampling basis -- the only case the code supported. Now that +# coord_names and low_level_coord_names can differ, the right question +# for the output writer is "did we MC-sample this column?", not "did we +# fit on it?". Implied (fit-only) coords like R1.4 should NOT appear in +# the output file (they aren't grid columns and dat_orig_names doesn't +# carry them). +if len(low_level_coord_names) < len(dat_orig_names): # not needed if every grid column is sampled + + if len(dat) < opts.n_output_samples: + print(" NOTE: original data shorter than requested output; adding",opts.n_output_samples-len(dat),"duplicate fill lines from original data.") + newlines = None + if opts.n_output_samples > 2*len(dat): + newlines = dat[:] + newlen = len(newlines) + while newlen < opts.n_output_samples: + newerlines = dat[:opts.n_output_samples-newlen] #will only get up to len(dat) lines + newlines = np.concatenate((newlines,newerlines), axis=0) + newlen = len(newlines) + else: + newlines = dat[:opts.n_output_samples-len(dat)] #duplicate lines to fill + dat = np.concatenate((dat,newlines), axis=0) #should be fine since dat isn't used after this + + for c in np.arange(len(dat_orig_names)): + if dat_orig_names[c] not in low_level_coord_names: + print(" Not sampled:", dat_orig_names[c], "; adding to output as constant from initial grid.") + outidx = name_index_dict[dat_orig_names[c]] # write in correct place + if len(dat) > opts.n_output_samples: + dat_out[:,outidx] = dat[:opts.n_output_samples,outidx] #truncate original data to fit (not ideal) + else: #len(dat) <= n_output_samples (if dat was <, should now be =) + dat_out[:,outidx] = dat[:,outidx] + +# Fill data from PE. +# +# samples[k] keys are the SAMPLING basis (low_level_coord_names). We can +# only write a column to the output file when (a) we sampled it, and (b) +# its name is one of dat_orig_names (i.e. it has a column slot in the +# output schema). Skip sampled names that aren't in dat_orig_names with +# a warning -- they'd correspond to a --parameter-nofit name that isn't +# a data-file column, which is rare but valid and would only show up via +# the corner plot, not the output file. +for name in low_level_coord_names: + if name not in name_index_dict: + print(" Sampled coord {!r} is not a data-file column; not writing to output (would only appear in corner plots).".format(name)) + continue + outindx = name_index_dict[name] + dat_out[:, outindx] = samples[name][indx_list] + +# NOTE: if m1 or m2 is "constant" (i.e., not in samples), the possibility for m2 > m1 arises! Re-sort masses here to avoid; use below code. +#if ("m1" not in coord_names) or ("m2" not in coord_names): +# print(" NOTE: re-sorting masses so m1 > m2 (precaution)") +# m1dx = name_index_dict["m1"] +# m1 = np.maximum(dat_out[:,m1dx], dat_out[:,m1dx+1]) #N.B.: assumes m2 col index after m1 col +# m2 = np.minimum(dat_out[:,m1dx], dat_out[:,m1dx+1]) +# dat_out[:,m1dx] = m1 +# dat_out[:,m1dx+1] = m2 + +print(" Saving to ", opts.fname_output_samples+".dat") +np.savetxt(opts.fname_output_samples+".dat",dat_out,header=" lnL sigma_lnL " + ' '.join(dat_orig_names)) + diff --git a/MonteCarloMarginalizeCode/Code/bin/util_ConstructIntrinsicPosterior_GenericCoordinates.py b/MonteCarloMarginalizeCode/Code/bin/util_ConstructIntrinsicPosterior_GenericCoordinates.py index aaa43560e..a12eb885a 100755 --- a/MonteCarloMarginalizeCode/Code/bin/util_ConstructIntrinsicPosterior_GenericCoordinates.py +++ b/MonteCarloMarginalizeCode/Code/bin/util_ConstructIntrinsicPosterior_GenericCoordinates.py @@ -16,6 +16,7 @@ import RIFT.interpolators.BayesianLeastSquares as BayesianLeastSquares +from RIFT.precision import RiftFloat import argparse import sys @@ -3115,7 +3116,7 @@ def parse_corr_params(my_str): # Note also downselects NOT applied: no range cuts, unless applied as part of aligned_prior, etc. # - use for Bayes factors with GREAT CARE for this reason; should correct for with indx_ok log_res_reweighted = lnLmax + np.log(np.mean(weights)) -sigma_reweighted= np.std(weights,dtype=np.float128)/np.mean(weights) +sigma_reweighted= np.std(weights,dtype=RiftFloat)/np.mean(weights) neff_reweighted = np.sum(weights)/np.max(weights) np.savetxt(opts.fname_output_integral+"_withpriorchange.dat", [log_res_reweighted]) # should agree with the usual result, if no prior changes with open(opts.fname_output_integral+"_withpriorchange+annotation.dat", 'w') as file_out: @@ -3693,4 +3694,3 @@ def parse_corr_params(my_str): sys.exit(0) - diff --git a/MonteCarloMarginalizeCode/Code/bin/util_ExtrinsicConsolidate.py b/MonteCarloMarginalizeCode/Code/bin/util_ExtrinsicConsolidate.py new file mode 100755 index 000000000..49a7a6f8e --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/bin/util_ExtrinsicConsolidate.py @@ -0,0 +1,110 @@ +#! /usr/bin/env python +""" +util_ExtrinsicConsolidate.py + +Consolidate the per-event EXTRINSIC proposal breadcrumbs written by one iteration's wide +ILE jobs (each ILE job that ran with --extrinsic-proposal-output drops a small .npz holding +its run's extrinsic GMM proposal, see RIFT.calmarg.extrinsic_handoff) into ONE breadcrumb +that seeds the NEXT iteration's ILE jobs via --extrinsic-proposal-breadcrumb. + +The extrinsic posterior is nearly the same across intrinsic-grid points (it is set by the +data + best-fit template), so we do not need to merge mixtures across points -- we just pick +the single MOST REPRESENTATIVE per-event proposal and hand it forward. "Most representative" +defaults to the highest-lnL (near the peak) job, with effective-sample-count / sample-count +as tie-breaks; --select lets you change the key. + +Robust by design: unreadable / empty (placeholder) / extrinsic-less inputs are skipped, and +the output breadcrumb is ALWAYS written (empty if nothing valid was found) so that the +next iteration's OSG file-transfer for extr_consolidated_.npz never fails -- a downstream +empty/missing breadcrumb simply makes ILE fall back to its cold default proposal. +""" +from __future__ import print_function + +import sys +import glob +import argparse + +import numpy as np + +import RIFT.calmarg.breadcrumbs as breadcrumbs + + +def _load_one(path): + """Return (meta, extrinsic) from a breadcrumb, or None if it is unusable + (missing/empty placeholder/corrupt/no extrinsic payload).""" + try: + g = breadcrumbs.load(path) + except Exception as e: + print(" skip {} ({})".format(path, e)) + return None + ext = g.get("extrinsic") + if ext is None or not ext.get("groups"): + print(" skip {} (no extrinsic payload)".format(path)) + return None + return g.get("meta", {}) or {}, ext + + +def main(argv=None): + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + p.add_argument("--input-glob", default=None, + help="Glob for the per-event proposal breadcrumbs (e.g. 'extr_proposal_3_*.npz'). " + "Matched in the current working directory; on OSG the inputs are transferred " + "flat into the job scratch dir, so the same glob works there.") + p.add_argument("--input", action="append", default=[], + help="Explicit per-event proposal breadcrumb path (repeatable). Combined with --input-glob.") + p.add_argument("--output", required=True, + help="Output consolidated breadcrumb path (e.g. extr_consolidated_.npz).") + p.add_argument("--select", default="lnL", choices=["lnL", "neff", "n_samples"], + help="Per-event metric to rank by when picking the representative proposal. Default lnL.") + p.add_argument("--iteration", default=None, + help="Iteration index (recorded in the output meta; informational).") + opts = p.parse_args(argv) + + paths = list(opts.input) + if opts.input_glob: + paths += sorted(glob.glob(opts.input_glob)) + # de-duplicate, preserve order + seen = set(); paths = [x for x in paths if not (x in seen or seen.add(x))] + print("util_ExtrinsicConsolidate: {} candidate breadcrumb(s); ranking by '{}'".format(len(paths), opts.select)) + + candidates = [] + for path in paths: + loaded = _load_one(path) + if loaded is None: + continue + meta, ext = loaded + score = float(meta.get(opts.select, -np.inf)) + candidates.append((score, path, meta, ext)) + + if not candidates: + # Always emit an (empty) breadcrumb so the next iteration's transfer/seed never fails. + breadcrumbs.save(opts.output, extrinsic=None, + meta=dict(iteration=opts.iteration, n_candidates=0, source=None)) + print("util_ExtrinsicConsolidate: no usable extrinsic proposals; wrote EMPTY {} " + "(next iteration falls back to the cold default).".format(opts.output)) + return 0 + + # tie-break: primary --select metric, then neff, then n_samples + def _key(c): + _, _, m, _e = c + return (c[0], float(m.get("neff", -np.inf)), float(m.get("n_samples", -np.inf))) + best = max(candidates, key=_key) + best_score, best_path, best_meta, best_ext = best + + breadcrumbs.save(opts.output, extrinsic=best_ext, + meta=dict(iteration=opts.iteration, n_candidates=len(candidates), + source=best_path, select=opts.select, select_value=best_score, + source_event=best_meta.get("event"), + source_lnL=best_meta.get("lnL"), + source_neff=best_meta.get("neff"), + source_n_samples=best_meta.get("n_samples"), + groups=[g["params"] for g in best_ext["groups"]])) + print("util_ExtrinsicConsolidate: picked {} ({}={}, event={}, lnL={}) -> {} ({} groups)".format( + best_path, opts.select, best_score, best_meta.get("event"), best_meta.get("lnL"), + opts.output, len(best_ext["groups"]))) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/MonteCarloMarginalizeCode/Code/bin/util_GenerateMaxlnLWaveform.py b/MonteCarloMarginalizeCode/Code/bin/util_GenerateMaxlnLWaveform.py index b95eb26d4..49cfbee60 100755 --- a/MonteCarloMarginalizeCode/Code/bin/util_GenerateMaxlnLWaveform.py +++ b/MonteCarloMarginalizeCode/Code/bin/util_GenerateMaxlnLWaveform.py @@ -15,10 +15,12 @@ import subprocess from shutil import copyfile +from RIFT.precision import RiftFloat + parser = argparse.ArgumentParser() parser.add_argument("--l-max", type=int, default=2, help="Include all (l,m) modes with l less than or equal to this value.") parser.add_argument("--run-dir",default=None, help="directory code was run in") -parser.add_argument("--event-time", type=np.float128, default=None,help="Event time. If not present, will use event.log") +parser.add_argument("--event-time", type=RiftFloat, default=None,help="Event time. If not present, will use event.log") parser.add_argument("--save-plots", default=False, action='store_true',help="saves waveform plot and hoft data") parser.add_argument("--use-NR", default=False, action='store_true', help="generate plots using NR data") parser.add_argument("--no-memory",action='store_true') @@ -250,4 +252,3 @@ os.chdir(opts.run_dir) print(cmd) os.system(cmd) - diff --git a/MonteCarloMarginalizeCode/Code/bin/util_GenerateMaxlnLWaveform_NRFromIndex.py b/MonteCarloMarginalizeCode/Code/bin/util_GenerateMaxlnLWaveform_NRFromIndex.py index 6424445a6..2dcd40613 100644 --- a/MonteCarloMarginalizeCode/Code/bin/util_GenerateMaxlnLWaveform_NRFromIndex.py +++ b/MonteCarloMarginalizeCode/Code/bin/util_GenerateMaxlnLWaveform_NRFromIndex.py @@ -15,6 +15,8 @@ import subprocess from shutil import copyfile +from RIFT.precision import RiftFloat + import NRWaveformCatalogManager3 as nrwf import lal import RIFT.lalsimutils as lalsimutils @@ -26,7 +28,7 @@ parser.add_argument("--nr-param",default=None,help="param") parser.add_argument("--fname-indexed",default=None,help="File name of *.indexed file") parser.add_argument("--n-max",default=1e4,type=int,help="Override settings in command-single.sh") -parser.add_argument("--event-time", type=np.float128, default=None,help="Event time. If not present, will use event.log") +parser.add_argument("--event-time", type=RiftFloat, default=None,help="Event time. If not present, will use event.log") parser.add_argument("--save-plots", default=False, action='store_true',help="saves waveform plot and hoft data") parser.add_argument("--use-NR", default=False, action='store_true', help="generate plots using NR data") parser.add_argument("--no-memory",action='store_true') @@ -180,4 +182,3 @@ os.chdir(opts.run_dir) print(cmd) os.system(cmd) - diff --git a/MonteCarloMarginalizeCode/Code/bin/util_HyperparameterPuffball.py b/MonteCarloMarginalizeCode/Code/bin/util_HyperparameterPuffball.py index e4383bfcc..24e498c8a 100755 --- a/MonteCarloMarginalizeCode/Code/bin/util_HyperparameterPuffball.py +++ b/MonteCarloMarginalizeCode/Code/bin/util_HyperparameterPuffball.py @@ -25,6 +25,24 @@ parser.add_argument("--downselect-parameter",action='append', help='Name of parameter to be used to eliminate grid points ') parser.add_argument("--downselect-parameter-range",action='append',type=str) parser.add_argument("--regularize",action='store_true',help="Add some ad-hoc terms based on priors, to help with nearly-singular matricies") +# ---- Optional coordinate-convert plugin (additive; legacy path byte-identical when omitted) ---- +# When set, the puff lane operates in the PLUGIN basis: it forward-transforms the +# file columns named by --supplementary-coordinate-input-parameter into the +# basis named by --parameter, draws the puff displacement there, then +# INVERSE-transforms back to the file basis and writes those file columns. +# The plugin must implement both convert_coordinates AND inverse_convert_coordinates. +parser.add_argument("--supplementary-coordinate-code", default=None, type=str, + help="Coordinate plugin spec: 'rift_default', a .py path, or an importable dotted name. " + "See RIFT.misc.coordinate_plugin for the contract.") +parser.add_argument("--supplementary-coordinate-function", default=None, type=str, + help="Entry-point callable name. Defaults to 'convert_coordinates'.") +parser.add_argument("--supplementary-coordinate-ini", default=None, type=str, + help="Optional ini file handed to the plugin's prepare() hook.") +parser.add_argument("--supplementary-coordinate-chart", default=None, type=str, + help="Which chart in the plugin's CHARTS dict to use.") +parser.add_argument("--supplementary-coordinate-input-parameter", action='append', default=None, + help="File-column name to feed the plugin as an input dimension. Repeat per column. " + "If omitted, the plugin's CHARTS[chart] input_parameters / INPUT_PARAMETERS is used.") opts= parser.parse_args() if opts.random_parameter is None: @@ -73,14 +91,89 @@ +# ---- Optional coordinate-convert plugin ---------------------------------- # +# +# When --supplementary-coordinate-code is set, the puff displacement runs in +# the PLUGIN basis (coord_names) instead of directly in the file columns. +# This lets the user puff in a basis where the covariance is well-conditioned +# (e.g. axis-aligned with the underlying physics) even when the data file +# stores everything in a different basis. The legacy code path is +# byte-identical when no plugin is supplied: _coord_plugin_converter stays +# None, the existing `X[:, i] = dat_raw[p]` extraction runs untouched, and +# the file-column write-back at the bottom runs untouched too. +_coord_plugin_converter = None +_coord_plugin_inverse = None +_coord_plugin_in_names = None # the FILE-basis names we feed the plugin +if opts.supplementary_coordinate_code: + from RIFT.misc.coordinate_plugin import load_coordinate_converter + _coord_plugin_converter, _coord_plugin_module = load_coordinate_converter( + spec=opts.supplementary_coordinate_code, + function_name=opts.supplementary_coordinate_function, + ini_path=opts.supplementary_coordinate_ini, + coord_names=coord_names, + low_level_coord_names=opts.supplementary_coordinate_input_parameter, + chart=opts.supplementary_coordinate_chart, + opts=opts, + prior_map=None, + prior_range_map=None, + ) + # Resolve the FILE-basis input names: explicit CLI override > chart's + # input_parameters > module's INPUT_PARAMETERS. + _chart_spec = ( + getattr(_coord_plugin_module, "CHARTS", {}).get(opts.supplementary_coordinate_chart) + if opts.supplementary_coordinate_chart else None + ) + if _chart_spec is None: + _charts = getattr(_coord_plugin_module, "CHARTS", None) or {} + if len(_charts) == 1: + _chart_spec = next(iter(_charts.values())) + _coord_plugin_in_names = list( + opts.supplementary_coordinate_input_parameter + or (_chart_spec.get("input_parameters") if _chart_spec else None) + or getattr(_coord_plugin_module, "INPUT_PARAMETERS", []) + ) + if not _coord_plugin_in_names: + sys.exit("util_HyperparameterPuffball: plugin loaded but no file-basis " + "input columns are declared; pass --supplementary-coordinate-input-parameter " + "or define INPUT_PARAMETERS / CHARTS[chart].input_parameters in the plugin.") + # Round-trip requires an inverse. Bail out with a clear message if the + # plugin doesn't provide one -- silently using a pseudo-inverse here + # would produce subtly wrong puff displacements. + _coord_plugin_inverse = getattr(_coord_plugin_module, "inverse_convert_coordinates", None) + if not callable(_coord_plugin_inverse): + sys.exit("util_HyperparameterPuffball: --supplementary-coordinate-code set, but " + "the plugin does not define inverse_convert_coordinates. The puff lane " + "needs to round-trip through the plugin basis -- add an inverse or run " + "without the plugin.") + print(" util_HyperparameterPuffball: puffing in plugin basis {!r} (file columns {!r}).".format( + list(coord_names), _coord_plugin_in_names, + )) + # Load data, keep parameter names dat_raw = np.genfromtxt(opts.inj_file,names=True) X= np.zeros((len(dat_raw), len(coord_names))) -# Copy over the parameters we use. Note we have no way to create linear combinations or alternate coordinates here -for p in coord_names: -# indx_p = list(dat_raw.dtype.names).index(p) - indx_in = coord_names.index(p) - X[:,indx_in] = dat_raw[p] +if _coord_plugin_converter is None: + # Legacy path: --parameter names are file columns; copy directly. + for p in coord_names: + indx_in = coord_names.index(p) + X[:,indx_in] = dat_raw[p] +else: + # Plugin path: forward-transform the file's input-basis columns into + # the puff basis (coord_names). --parameter names need not exist as + # file columns at all. + missing_in = [n for n in _coord_plugin_in_names if n not in dat_raw.dtype.names] + if missing_in: + sys.exit("util_HyperparameterPuffball: plugin input column(s) {!r} not present in {!r}; " + "headers seen: {!r}".format(missing_in, opts.inj_file, list(dat_raw.dtype.names))) + X_in = np.column_stack([np.asarray(dat_raw[n], dtype=float) + for n in _coord_plugin_in_names]) + X = _coord_plugin_converter(X_in, + coord_names=coord_names, + low_level_coord_names=_coord_plugin_in_names) + X = np.asarray(X, dtype=float) + if X.shape != (len(dat_raw), len(coord_names)): + sys.exit("util_HyperparameterPuffball: plugin forward returned shape {!r}, " + "expected {!r}".format(X.shape, (len(dat_raw), len(coord_names)))) # Measure covariance matrix and generate random errors @@ -131,10 +224,30 @@ X_out = X_out[indx_ok] dat_raw = dat_raw[indx_ok] # must downselect here as well! -# Write data back into correct format and save -for p in coord_names: -# indx_p = dat_raw.dtype.names.index(p) - indx_in = coord_names.index(p) - dat_raw[p] = X_out[:,indx_in] +# Write data back into correct format and save. +# +# Legacy path: --parameter names are file columns; the puffed X_out columns +# go straight back into the matching dat_raw fields. +# +# Plugin path: X_out lives in the puff (plugin output) basis. Inverse- +# transform it to the file basis, then write each file column from the +# matching inverse-transformed column. --parameter names (coord_names) +# need not appear in dat_raw at all in this branch. +if _coord_plugin_converter is None: + for p in coord_names: + indx_in = coord_names.index(p) + dat_raw[p] = X_out[:,indx_in] +else: + X_in_out = _coord_plugin_inverse( + np.asarray(X_out, dtype=float), + coord_names=coord_names, + low_level_coord_names=_coord_plugin_in_names, + ) + X_in_out = np.asarray(X_in_out, dtype=float) + if X_in_out.shape != (len(X_out), len(_coord_plugin_in_names)): + sys.exit("util_HyperparameterPuffball: plugin inverse returned shape {!r}, " + "expected {!r}".format(X_in_out.shape, (len(X_out), len(_coord_plugin_in_names)))) + for j, name in enumerate(_coord_plugin_in_names): + dat_raw[name] = X_in_out[:, j] np.savetxt(opts.inj_file_out, dat_raw,header=" ".join(dat_raw.dtype.names)) diff --git a/MonteCarloMarginalizeCode/Code/bin/util_HyperparameterTracerUpdate.py b/MonteCarloMarginalizeCode/Code/bin/util_HyperparameterTracerUpdate.py index a7c2a1707..3ccdb520b 100755 --- a/MonteCarloMarginalizeCode/Code/bin/util_HyperparameterTracerUpdate.py +++ b/MonteCarloMarginalizeCode/Code/bin/util_HyperparameterTracerUpdate.py @@ -118,6 +118,25 @@ def build_parser(): p.add_argument("--rng-seed", default=None, type=int) p.add_argument("--state-in", default=None) p.add_argument("--state-out", default=None) + # ---- Optional coordinate-convert plugin -------------------------------- # + # When set, the tracer operates in the PLUGIN basis: forward-transform + # the file's input-basis columns into the basis named by --parameter, + # do SMC / birth-death / etc. in that basis, then inverse-transform back + # to the file basis to write the output .dat. Legacy code path is + # byte-identical when --supplementary-coordinate-code is unset. + # See RIFT.misc.coordinate_plugin for the plugin contract. The plugin + # MUST implement inverse_convert_coordinates: the tracer round-trips. + p.add_argument("--supplementary-coordinate-code", default=None, type=str, + help="Coordinate plugin spec: 'rift_default', a .py path, or an importable dotted name.") + p.add_argument("--supplementary-coordinate-function", default=None, type=str, + help="Entry-point callable name. Defaults to 'convert_coordinates'.") + p.add_argument("--supplementary-coordinate-ini", default=None, type=str, + help="Optional ini file handed to the plugin's prepare() hook.") + p.add_argument("--supplementary-coordinate-chart", default=None, type=str, + help="Which chart in the plugin's CHARTS dict to use.") + p.add_argument("--supplementary-coordinate-input-parameter", action='append', default=None, + help="File-column name to feed the plugin as an input dimension. Repeat per column. " + "If omitted, the plugin's CHARTS[chart] input_parameters / INPUT_PARAMETERS is used.") return p @@ -207,12 +226,87 @@ def _coord_box(parameter_order, downselect_dict, X): # ------------------------------ main --------------------------------------- # +def _load_coord_plugin(opts): + """Load the coordinate plugin if --supplementary-coordinate-code is set. + + Returns (forward, inverse, in_names) or (None, None, None) if no plugin + was requested. Bails out loudly if a plugin was requested but the + inverse callable isn't present -- the tracer needs to round-trip and + silently using a pseudo-inverse would produce subtly-wrong placements. + """ + if not getattr(opts, "supplementary_coordinate_code", None): + return None, None, None + from RIFT.misc.coordinate_plugin import load_coordinate_converter + forward, module = load_coordinate_converter( + spec=opts.supplementary_coordinate_code, + function_name=opts.supplementary_coordinate_function, + ini_path=opts.supplementary_coordinate_ini, + coord_names=opts.parameter, + low_level_coord_names=opts.supplementary_coordinate_input_parameter, + chart=opts.supplementary_coordinate_chart, + opts=opts, + prior_map=None, + prior_range_map=None, + ) + chart_spec = None + if opts.supplementary_coordinate_chart: + chart_spec = getattr(module, "CHARTS", {}).get(opts.supplementary_coordinate_chart) + if chart_spec is None: + charts = getattr(module, "CHARTS", None) or {} + if len(charts) == 1: + chart_spec = next(iter(charts.values())) + in_names = list( + opts.supplementary_coordinate_input_parameter + or (chart_spec.get("input_parameters") if chart_spec else None) + or getattr(module, "INPUT_PARAMETERS", []) + ) + if not in_names: + sys.exit("util_HyperparameterTracerUpdate: plugin loaded but no " + "file-basis input columns are declared; pass " + "--supplementary-coordinate-input-parameter or define " + "INPUT_PARAMETERS / CHARTS[chart].input_parameters.") + inverse = getattr(module, "inverse_convert_coordinates", None) + if not callable(inverse): + sys.exit("util_HyperparameterTracerUpdate: --supplementary-coordinate-code " + "set, but the plugin does not define inverse_convert_coordinates. " + "The tracer needs to round-trip through the plugin basis -- add an " + "inverse or run without the plugin.") + print(" util_HyperparameterTracerUpdate: operating in plugin basis {!r} " + "(file columns {!r}).".format(list(opts.parameter), in_names)) + return forward, inverse, in_names + + +def _extract_X_via_plugin(cols, rows, parameter_order, forward, in_names): + """Plugin-aware X extraction: read file-basis columns and forward-transform.""" + missing = [n for n in in_names if n not in cols] + if missing: + sys.exit("util_HyperparameterTracerUpdate: plugin input column(s) " + "{!r} not in dat header {!r}".format(missing, cols)) + in_idx = [cols.index(n) for n in in_names] + X_in = rows[:, in_idx].astype(float) + X = forward(X_in, coord_names=parameter_order, low_level_coord_names=in_names) + X = np.asarray(X, dtype=float) + if X.shape != (len(rows), len(parameter_order)): + sys.exit("util_HyperparameterTracerUpdate: plugin forward returned " + "shape {!r}, expected {!r}".format(X.shape, (len(rows), len(parameter_order)))) + return X + + def main(argv=None): opts = build_parser().parse_args(argv) rng = np.random.default_rng(opts.rng_seed) + # Load the optional coordinate plugin BEFORE any data extraction so the + # same forward/inverse pair is reused for the input grid, the previous- + # iteration grid (--inj-file-prev), and the final write-back. + forward, inverse, in_names = _load_coord_plugin(opts) + plugin_active = forward is not None + cols, rows = _read_dat(opts.inj_file) - X = _extract_X(cols, rows, opts.parameter) + if plugin_active: + X = _extract_X_via_plugin(cols, rows, opts.parameter, forward, in_names) + else: + X = _extract_X(cols, rows, opts.parameter) Y = rows[:, 0] # lnL column S = rows[:, 1] if rows.shape[1] >= 2 else None downselect = _build_downselect(opts) @@ -229,10 +323,20 @@ def main(argv=None): cov = cov + 1e-8 * np.eye(cov.shape[0]) delta = rng.multivariate_normal(np.zeros(X.shape[1]), cov, size=len(X)) X_out = X + delta - # write back into rows; zero lnL/sigma (puffball convention) + # write back; zero lnL/sigma (puffball convention) out_rows = rows.copy() - for i, name in enumerate(opts.parameter): - out_rows[:, cols.index(name)] = X_out[:, i] + if plugin_active: + # X_out is in the plugin basis -- inverse-transform back to the + # file basis and write each file-basis column. + X_in_out = np.asarray( + inverse(X_out, coord_names=opts.parameter, low_level_coord_names=in_names), + dtype=float, + ) + for j, name in enumerate(in_names): + out_rows[:, cols.index(name)] = X_in_out[:, j] + else: + for i, name in enumerate(opts.parameter): + out_rows[:, cols.index(name)] = X_out[:, i] out_rows[:, 0] = 0.0 out_rows[:, 1] = 0.0 _write_dat(opts.inj_file_out, cols, out_rows) @@ -250,7 +354,10 @@ def main(argv=None): if opts.inj_file_prev is not None and os.path.exists(opts.inj_file_prev): cols_p, rows_p = _read_dat(opts.inj_file_prev) - X_prev = _extract_X(cols_p, rows_p, opts.parameter) + if plugin_active: + X_prev = _extract_X_via_plugin(cols_p, rows_p, opts.parameter, forward, in_names) + else: + X_prev = _extract_X(cols_p, rows_p, opts.parameter) Y_prev = rows_p[:, 0] S_prev = rows_p[:, 1] if rows_p.shape[1] >= 2 else None fit_prev = _tracer_fits.build(opts.tracer_fit_method, @@ -328,8 +435,19 @@ def main(argv=None): out_rows = np.zeros((len(X_out), rows.shape[1])) # carry forward any extra columns from input rows (just zero them; marg driver overwrites) - for i, name in enumerate(opts.parameter): - out_rows[:, cols.index(name)] = X_out[:, i] + if plugin_active: + # X_out is in the plugin basis -- inverse-transform back to the file + # basis and write each file-basis column. --parameter names need + # not be file columns at all here. + X_in_out = np.asarray( + inverse(X_out, coord_names=opts.parameter, low_level_coord_names=in_names), + dtype=float, + ) + for j, name in enumerate(in_names): + out_rows[:, cols.index(name)] = X_in_out[:, j] + else: + for i, name in enumerate(opts.parameter): + out_rows[:, cols.index(name)] = X_out[:, i] out_rows[:, 0] = 0.0 out_rows[:, 1] = 0.0 _write_dat(opts.inj_file_out, cols, out_rows) diff --git a/MonteCarloMarginalizeCode/Code/bin/util_InitMargTable b/MonteCarloMarginalizeCode/Code/bin/util_InitMargTable old mode 100644 new mode 100755 diff --git a/MonteCarloMarginalizeCode/Code/bin/util_RIFT_pseudo_pipe.py b/MonteCarloMarginalizeCode/Code/bin/util_RIFT_pseudo_pipe.py index 01556f7c1..7afac2f6c 100755 --- a/MonteCarloMarginalizeCode/Code/bin/util_RIFT_pseudo_pipe.py +++ b/MonteCarloMarginalizeCode/Code/bin/util_RIFT_pseudo_pipe.py @@ -137,6 +137,7 @@ def unsafe_parse_arg_string_dict(my_argstr): parser.add_argument("--skip-reproducibility",action='store_true') parser.add_argument("--use-production-defaults",action='store_true',help="Use production defaults. Intended for use with tools like asimov or by nonexperts who just want something to run on a real event. Will require manual setting of other arguments!") parser.add_argument("--use-subdags",action='store_true',help="Use CEPP_Alternate instead of CEPP_BasicIteration. Note this writes an adaptively-sized DAG each iteration, but doesn't otherwise optimize yet.") +parser.add_argument("--pipeline-builder",default=None,choices=["BasicIteration","AlternateIteration"],help="Explicitly select the create_event_parameter_pipeline_* iteration builder, as a drop-in hot-swap for side-by-side A/B testing. Overrides the implicit --use-subdags routing. If unset, the builder is chosen by --use-subdags (Alternate) vs. the default (Basic).") parser.add_argument("--use-ile-subdags",action='store_true',help="Use ILE subdag system (new)") parser.add_argument("--bilby-ini-file",default=None,type=str,help="Pass ini file for parsing. Intended to use for calibration reweighting. Full path recommended") parser.add_argument("--bilby-pickle-file",default=None,type=str,help="Bilby Pickle file with event settings. Intended to use for calibration reweighting. Full path recommended") @@ -152,6 +153,23 @@ def unsafe_parse_arg_string_dict(my_argstr): parser.add_argument("--calibration-reweighting-initial-extra-args",type=str,default=None,help="If not 'None', pass through. One argument targets effective sample size, other duplicates inoutput") parser.add_argument("--calibration-reweighting-extra-args",type=str,default=None,help="If not 'None', pass through. One argument targets effective sample size, other duplicates inoutput") parser.add_argument("--calibration-reweighting-osg",action='store_true',help="Attempt to use settings for OSG for cal reweighting. Remove after developed") +# In-loop calibration marginalization (inside the ILE GPU loop), as opposed to the +# postprocessing --calibration-reweighting path above. Setting the envelope directory +# enables it and threads the corresponding flags into the ILE arguments (args_ile.txt). +parser.add_argument("--calmarg-envelope-directory",default=None,type=str,help="Enable IN-LOOP calibration marginalization in ILE. Directory with per-IFO calibration envelope files named .txt (e.g. H1.txt, L1.txt, V1.txt). Threaded to ILE as --calibration-envelope-directory (absolute path).") +parser.add_argument("--calmarg-n-realizations",default=100,type=int,help="Number of calibration realizations for in-loop calmarg. Threaded to ILE as --calibration-n-realizations.") +parser.add_argument("--calmarg-spline-count",default=10,type=int,help="Number of spline nodes for in-loop calmarg envelopes. Threaded to ILE as --calibration-spline-count.") +parser.add_argument("--calmarg-fused-kernel",action='store_true',help="Use the fused GPU kernel (Option C) for in-loop calmarg. GPU only; ILE falls back to the loop method otherwise. Threaded to ILE as --calibration-fused-kernel.") +parser.add_argument("--calmarg-pilot",action='store_true',help="Option C adaptive calibration: add per-iteration cal PILOT jobs that learn a cal proposal (harvest top-lnL composite points -> ILE --calibration-dump-responsibilities -> fit+consolidate) and SEED the next iteration's wide ILE jobs via --calibration-proposal-breadcrumb. Requires --calmarg-envelope-directory.") +parser.add_argument("--calmarg-pilot-cadence",default=1,type=int,help="Run a cal pilot every n iterations (default 1).") +parser.add_argument("--calmarg-pilot-max-it",default=3,type=int,help="Stop launching cal pilots after this iteration (cal is boring; freeze once learned). Default 3.") +parser.add_argument("--calmarg-pilot-top-fraction",default=0.05,type=float,help="Fraction of highest-lnL composite points the pilot harvests. Default 0.05.") +parser.add_argument("--calmarg-pilot-max-points",default=32,type=int,help="Cap on harvested pilot points per iteration. Default 32.") +parser.add_argument("--calmarg-first-cip-sigma-cut",default=100.0,type=float,help="With --calmarg-pilot: relax the first CIP stage's --sigma-cut to this value, so cold-start (prior-cal) iteration-0 points -- which have large MC error -- are not all stripped by CIP's default 0.6. Threaded to helper_LDG_Events.py. Default 100 (effectively keep all cold-start points).") +parser.add_argument("--calmarg-burn-in-neff",default=None,type=float,help="In-loop calmarg: burn the extrinsic sampler in on the cheap zero-cal likelihood to this n_eff before the full cal-marginalized integration (warm start; the extrinsic posterior is ~cal-independent). Threaded to ILE as --calibration-burn-in-neff.") +parser.add_argument("--calmarg-export-posterior",action='store_true',help="In-loop calmarg: at the final fairdraw export, also write the RECOVERED calibration posterior -- for each fair-draw sample, draw one cal realization in proportion to its posterior weight and write a self-contained sibling __cal.dat with the full draw (intrinsic + extrinsic + cal__amp_/cal__phase_ node columns). Threaded to ILE as --calibration-export-posterior (fires only at the extrinsic/fairdraw stage).") +parser.add_argument("--extrinsic-handoff",action='store_true',help="Extrinsic handoff (GMM sampler only): each iteration's wide ILE jobs write a per-event extrinsic GMM proposal (--extrinsic-proposal-output) of their extrinsic posterior; a per-iteration consolidation picks the most representative one and SEEDS the next iteration's wide ILE jobs via --extrinsic-proposal-breadcrumb, so the extrinsic sampler starts on the answer instead of cold. Requires --ile-sampler-method GMM. See RIFT/calmarg/DESIGN_extrinsic_handoff.md.") +parser.add_argument("--extrinsic-handoff-select",default="lnL",help="Metric the extrinsic consolidation ranks per-event proposals by (lnL|neff|n_samples). Default lnL (most peak-representative).") parser.add_argument("--distance-reweighting",action='store_true',help="Option to add job to DAG to reweight posterior samples due to different distance prior (LVK prod prior)") parser.add_argument("--extra-args-helper",action=None, help="Filename with arguments for the helper. Use to provide alternative channel names and other advanced configuration (--channel-name, data type)!") parser.add_argument("--manual-postfix",default='',type=str) @@ -294,6 +312,12 @@ def unsafe_parse_arg_string_dict(my_argstr): parser.add_argument("--internal-ile-adapt-log",action='store_true',help="Passthrough to ILE ") parser.add_argument("--internal-ile-auto-logarithm-offset",action='store_true',help="Passthrough to ILE") parser.add_argument("--internal-ile-use-lnL",action='store_true',help="Passthrough to ILE via helper. Will DISABLE auto-logarithm-offset and manual-logarithm-offset for ILE") +parser.add_argument("--export-marginal-distance-grid",action='store_true',help="Ask the ILE extrinsic stage to export per-intrinsic likelihood density grids in luminosity distance. Forces ILE lnL mode and disables distance marginalization. Requires the extrinsic stage (--add-extrinsic).") +parser.add_argument("--export-distance-slices",default=0,type=int,help="If >0, ask the ILE extrinsic stage to export K-row .dslice files (Plan-B fixed-distance extrinsic-marginalized likelihoods). Forces ILE lnL mode and disables distance marginalization. Requires the extrinsic stage (--add-extrinsic).") +parser.add_argument("--export-distance-slices-n-core",default=0,type=int,help="Passthrough: --n-distance-slice-core for the .dslice export.") +parser.add_argument("--export-distance-slices-n-wing",default=0,type=int,help="Passthrough: --n-distance-slice-wing for the .dslice export.") +parser.add_argument("--export-distance-slices-wing-delta-lnL",default=None,type=float,help="Passthrough: --distance-slice-wing-delta-lnL for the .dslice export (target lnL drop below peak for wing placement).") +parser.add_argument("--export-distance-slices-skip-threshold",default=None,type=float,help="Passthrough: --distance-slice-skip-threshold for the .dslice export (absolute peak-lnL detectability cut).") parser.add_argument("--ile-additional-files-to-transfer",default=None,help="Comma-separated list of filenames. To append to the transfer file list for ILE jobs (only). Intended for surrogates in LAL_DATA_PATH for wide-ranging use") parser.add_argument("--internal-cip-use-lnL",action='store_true') parser.add_argument("--manual-initial-grid",default=None,type=str,help="Filename (full path) to initial grid. Copied into proposed-grid.xml.gz, overwriting any grid assignment done here") @@ -740,6 +764,20 @@ def unsafe_parse_arg_string_dict(my_argstr): if opts.internal_ile_use_lnL: cmd+= " --internal-ile-use-lnL " +if opts.export_marginal_distance_grid or (opts.export_distance_slices and opts.export_distance_slices > 0): + # Per-distance likelihood export needs ILE lnL mode (forced here for the + # whole run, giving clean lnL-scaled helper args) and, *only at the export + # stage*, no distance marginalization. We deliberately do NOT disable + # distance marginalization globally: the intrinsic iterations keep it (it + # is a large speedup). Only the final extrinsic stage that emits the + # per-distance output has --distance-marginalization stripped, and that + # stripping is done by create_event_parameter_pipeline_* on the ILE_extr + # argument string -- not here. + opts.internal_ile_use_lnL = True + if "--internal-ile-use-lnL" not in cmd: + cmd += " --internal-ile-use-lnL " + if not opts.add_extrinsic: + print(" ==> WARNING: distance grid/slice export is emitted by the ILE extrinsic stage, but --add-extrinsic is not set; no per-distance output will be produced. <== ") if opts.internal_cip_use_lnL: cmd += " --internal-cip-use-lnL " if opts.internal_ile_data_tukey_window_time: @@ -789,6 +827,10 @@ def unsafe_parse_arg_string_dict(my_argstr): cmd += " --data-LI-seglen "+str(opts.data_LI_seglen) if opts.assume_well_placed: cmd += " --assume-well-placed " +if opts.calmarg_pilot: + # cold-start cal pilots draw cal from the broad PRIOR -> large MC error on iteration 0; + # relax the first CIP stage's sigma-cut so those points are not all stripped. + cmd += " --calmarg-first-cip-sigma-cut {} ".format(opts.calmarg_first_cip_sigma_cut) #if is_event_bns and not opts.no_matter: # cmd += " --assume-matter " # npts_it = 1000 @@ -969,6 +1011,15 @@ def unsafe_parse_arg_string_dict(my_argstr): line = line.replace('--declination-cosine-sampler', '') if opts.internal_ile_force_adapt_all: line += " --force-adapt-all " +# NOTE on per-distance export (grid/slices): the --last-iteration-export-* +# flags are *pipeline-builder* flags (consumed by +# create_event_parameter_pipeline_*), NOT ILE flags, so they are added to the +# CEPP command below -- not to this ILE argument string (args_ile.txt). The +# args_ile.txt here is the *intrinsic* ILE configuration and intentionally +# keeps --distance-marginalization (a speedup); lnL mode was already forced +# above, so --internal-use-lnL is present. create_event_parameter_pipeline_* +# strips --distance-marginalization only from the extrinsic (ILE_extr) stage +# that emits the per-distance output. if not(opts.ile_sampler_method is None): line += " --sampler-method {} ".format(opts.ile_sampler_method) if opts.internal_ile_sky_network_coordinates: @@ -990,6 +1041,85 @@ def unsafe_parse_arg_string_dict(my_argstr): # strictly the next argument only does anything at the extrinsic step, otherwis it is ignored if opts.internal_ile_srate_time_resampling: line += " --srate-resample-time-marginalization {} ".format(opts.internal_ile_srate_time_resampling) +# In-loop calibration marginalization (inside the ILE GPU loop). Engages on the +# distance-marginalization code path (kept in args_ile.txt); the fused kernel +# additionally requires GPU and falls back to the loop method otherwise. +if opts.calmarg_envelope_directory: + cal_dir = os.path.abspath(opts.calmarg_envelope_directory) + cal_dir_arg = cal_dir + if opts.use_osg_file_transfer: + # OSG file transfer: the worker has no shared filesystem, so an absolute + # --calibration-envelope-directory path is unreachable. The per-IFO .txt + # envelope files are transferred FLAT into the job scratch dir, so reference them + # relative to '.', and auto-append them to the ILE transfer list (the user should + # not have to remember --ile-additional-files-to-transfer for these). + cal_dir_arg = '.' + _cal_files = ",".join("{}/{}.txt".format(cal_dir, ifo) for ifo in event_dict["IFOs"]) + opts.ile_additional_files_to_transfer = (opts.ile_additional_files_to_transfer + "," + _cal_files) if opts.ile_additional_files_to_transfer else _cal_files + line += " --calibration-envelope-directory {} --calibration-n-realizations {} --calibration-spline-count {} ".format(cal_dir_arg, opts.calmarg_n_realizations, opts.calmarg_spline_count) + if opts.calmarg_fused_kernel: + line += " --calibration-fused-kernel " + if opts.calmarg_burn_in_neff: + line += " --calibration-burn-in-neff {} ".format(opts.calmarg_burn_in_neff) + if opts.calmarg_export_posterior: + # recovered cal posterior columns; harmless on the wide stage (only fires at the + # fairdraw/extrinsic stage, which has --save-samples + --resample-time-marginalization). + line += " --calibration-export-posterior " + if opts.calmarg_pilot: + # Option C: wide ILE jobs are SEEDED from the previous iteration's consolidated cal + # proposal. The $(macroiterationprev) condor macro resolves per node; ILE falls + # back to the broad prior when the file is absent/invalid (the first iterations). + if opts.use_osg_file_transfer: + # OSG: no shared FS -> reference the breadcrumb by BASENAME (it is transferred + # in from the submit node, produced at runtime by calpilot_{N-1}), and add it to + # the ILE transfer list. Also create a placeholder cal_consolidated_-1.npz so + # condor's transfer for the FIRST iteration (prev=-1, never produced) does not + # fail. Write a VALID 'prior' breadcrumb (proposal == prior -> seeding from it == + # cold prior draws, zero weights) rather than a 0-byte file: that way iteration 0 + # LOADS cleanly even on an older ILE binary that does not guard against an empty + # placeholder (belt-and-suspenders; the size-guard in the ILE is the other half). + line += " --calibration-proposal-breadcrumb cal_consolidated_$(macroiterationprev).npz " + _bc_xfer = os.getcwd() + "/cal_consolidated_$(macroiterationprev).npz" + opts.ile_additional_files_to_transfer = (opts.ile_additional_files_to_transfer + "," + _bc_xfer) if opts.ile_additional_files_to_transfer else _bc_xfer + _cal_ph_path = os.getcwd() + "/cal_consolidated_-1.npz" + try: + import RIFT.calmarg.generate_realizations as _genr_ph, RIFT.calmarg.breadcrumbs as _bcr_ph + _cal_ph = _genr_ph.prior_cal_breadcrumb_dict(cal_dir, list(event_dict["IFOs"]), + fmin_template, srate/2. - 1., opts.calmarg_spline_count) + _bcr_ph.save(_cal_ph_path, cal=_cal_ph, meta=dict(placeholder=True, iteration=-1)) + except Exception as _e_calph: + print(" WARNING: could not build prior cal placeholder ({}); writing 0-byte placeholder (needs the ILE empty-breadcrumb guard).".format(_e_calph)) + open(_cal_ph_path, "a").close() + else: + line += " --calibration-proposal-breadcrumb {}/cal_consolidated_$(macroiterationprev).npz ".format(os.getcwd()) + +# Extrinsic handoff (independent of calmarg). Each wide ILE job WRITES its run's extrinsic +# GMM proposal, and is SEEDED from the previous iteration's consolidated proposal. Only the +# GMM sampler builds the seedable gmm_dict, so warn if a different sampler is selected. +if opts.extrinsic_handoff: + if opts.ile_sampler_method != 'GMM': + print(" WARNING: --extrinsic-handoff seeds the ensemble (GMM) sampler's gmm_dict, but --ile-sampler-method is {}; the seed is a no-op for that sampler. Pass --ile-sampler-method GMM.".format(opts.ile_sampler_method)) + # output: per-event proposal breadcrumb (basename; written relative to the ILE initialdir + # on a shared FS, or to job scratch + transferred back on OSG). $(macroevent) is the + # per-node event macro, so each wide ILE job gets a distinct file. + line += " --extrinsic-proposal-output extr_proposal_$(macroiteration)_$(macroevent).npz " + # seed: from iteration N-1's consolidated proposal. Mirror the cal breadcrumb path + # (OSG basename + transfer + iteration-0 placeholder vs shared-FS absolute path). + if opts.use_osg_file_transfer: + line += " --extrinsic-proposal-breadcrumb extr_consolidated_$(macroiterationprev).npz " + _ext_bc_xfer = os.getcwd() + "/extr_consolidated_$(macroiterationprev).npz" + opts.ile_additional_files_to_transfer = (opts.ile_additional_files_to_transfer + "," + _ext_bc_xfer) if opts.ile_additional_files_to_transfer else _ext_bc_xfer + # valid EMPTY breadcrumb placeholder (loads cleanly -> extrinsic=None -> no seed/cold), + # rather than a 0-byte file that np.load chokes on. + _ext_ph_path = os.getcwd() + "/extr_consolidated_-1.npz" + try: + import RIFT.calmarg.breadcrumbs as _bcr_ph + _bcr_ph.save(_ext_ph_path, meta=dict(placeholder=True, iteration=-1)) + except Exception: + open(_ext_ph_path, "a").close() + else: + line += " --extrinsic-proposal-breadcrumb {}/extr_consolidated_$(macroiterationprev).npz ".format(os.getcwd()) + with open('args_ile.txt','w') as f: f.write(line) @@ -1415,6 +1545,12 @@ def unsafe_parse_arg_string_dict(my_argstr): cepp = "create_event_parameter_pipeline_BasicIteration" if opts.use_subdags: cepp = "create_event_parameter_pipeline_AlternateIteration" +if opts.pipeline_builder: # explicit override wins, for clean side-by-side A/B testing of the two builders + if opts.use_subdags and opts.pipeline_builder != "AlternateIteration": + # use_subdags is set either by the user or force-set by --internal-use-amr (which REQUIRES the Alternate builder) + print(" WARNING: --pipeline-builder {} overrides --use-subdags routing; AMR/subdag runs require AlternateIteration ".format(opts.pipeline_builder)) + cepp = "create_event_parameter_pipeline_" + opts.pipeline_builder +print(" Pipeline builder (create_event_parameter_pipeline_*): ", cepp) cmd =cepp+ " --ile-n-events-to-analyze {} --input-grid proposed-grid.xml.gz --ile-exe `which integrate_likelihood_extrinsic_batchmode` --ile-args `pwd`/args_ile.txt --cip-args-list args_cip_list.txt --test-args args_test.txt --request-memory-CIP {} --request-memory-ILE {} --n-samples-per-job ".format(n_jobs_per_worker,cip_mem,ile_mem) + str(npts_it) + " --working-directory `pwd` --n-iterations " + str(n_iterations) + " --n-iterations-subdag-max {} ".format(opts.internal_n_iterations_subdag_max) + " --n-copies {} ".format(opts.ile_copies) + " --ile-retries "+ str(opts.ile_retries) + " --general-retries " + str(opts.general_retries) if opts.ile_jobs_per_worker_first: cmd += " --ile-n-events-to-analyze-first {} ".format(opts.ile_jobs_per_worker_first) @@ -1424,6 +1560,11 @@ def unsafe_parse_arg_string_dict(my_argstr): cmd += " --ile-runtime-max-minutes {} ".format(opts.ile_runtime_max_minutes) if not(opts.internal_use_amr) or opts.internal_use_amr_puff: cmd+= " --puff-exe `which util_ParameterPuffball.py` --puff-cadence 1 --puff-max-it " + str(puff_max_it)+ " --puff-args `pwd`/args_puff.txt " +if opts.calmarg_pilot: + cmd += " --calmarg-pilot --calmarg-pilot-cadence {} --calmarg-pilot-max-it {} --calmarg-pilot-top-fraction {} --calmarg-pilot-max-points {} ".format( + opts.calmarg_pilot_cadence, opts.calmarg_pilot_max_it, opts.calmarg_pilot_top_fraction, opts.calmarg_pilot_max_points) +if opts.extrinsic_handoff: + cmd += " --extrinsic-handoff --extrinsic-handoff-select {} ".format(opts.extrinsic_handoff_select) if opts.assume_eccentric: cmd += " --use-eccentricity " if opts.sample_eccentricity_squared: @@ -1670,7 +1811,24 @@ def unsafe_parse_arg_string_dict(my_argstr): for key, val in ile_condor_commands: f.write(key+ ' ' + val + '\n') cmd += " --ile-condor-commands `pwd`/ile_condor_commands.txt " - + +# Per-distance likelihood export on the extrinsic stage. These are +# pipeline-builder flags consumed by create_event_parameter_pipeline_*, so +# they are added to the CEPP command (not the ILE args). They only take effect +# when the extrinsic stage exists (--add-extrinsic). +if opts.export_marginal_distance_grid: + cmd += " --last-iteration-export-marginal-distance-grid " +if opts.export_distance_slices and opts.export_distance_slices > 0: + cmd += " --last-iteration-export-distance-slices {} ".format(opts.export_distance_slices) + if opts.export_distance_slices_n_core: + cmd += " --last-iteration-export-distance-slices-n-core {} ".format(opts.export_distance_slices_n_core) + if opts.export_distance_slices_n_wing: + cmd += " --last-iteration-export-distance-slices-n-wing {} ".format(opts.export_distance_slices_n_wing) + if opts.export_distance_slices_wing_delta_lnL is not None: + cmd += " --last-iteration-export-distance-slices-wing-delta-lnL {} ".format(opts.export_distance_slices_wing_delta_lnL) + if opts.export_distance_slices_skip_threshold is not None: + cmd += " --last-iteration-export-distance-slices-skip-threshold {} ".format(opts.export_distance_slices_skip_threshold) + print(cmd) os.system(cmd) diff --git a/MonteCarloMarginalizeCode/Code/demo/hyperpipe/README.md b/MonteCarloMarginalizeCode/Code/demo/hyperpipe/README.md index 89e0c1bbb..2002cae57 100644 --- a/MonteCarloMarginalizeCode/Code/demo/hyperpipe/README.md +++ b/MonteCarloMarginalizeCode/Code/demo/hyperpipe/README.md @@ -1,14 +1,187 @@ -README for HyperPipe code +# `demo/hyperpipe` — HyperPipe example pipelines -The Makefile contains commands for running this HyperPipe code for a sample Gaussian distribution, and instructions for how the makefile can be modified for a new user supplied code run. -For more details, see the auto-generated documentation [here](https://rift-documentation.readthedocs.io/en/latest/hyperpipe.html) for a description of hyperpipe. +This directory holds runnable demos for the RIFT HyperPipe pipeline. Each +configuration drives the iterative `MARG → CON → UNIFY → EOS_POST → PUFF → +TEST → next iteration` loop on the same 3-D Gaussian toy, but each one +exercises a different slice of the pipeline so you can pick the one closest +to what you are testing. +The auto-generated documentation at + +covers the pipeline design. `technical_doc.txt` in this directory has a +pedagogical writeup of the procedure and implementation. -Similar content is also available in the supplementary document 'technical_doc.txt' for a pedagogical overview of the proceedure and implementation of the pipeline. -# Hyperpipe hydra pipeline writer -New tool +## The driver + +All hydra-style demos in this directory are launched with the same tool: + +```bash +util_RIFT_hyperpipe.py --config ./ +``` + +`util_RIFT_hyperpipe.py` reads the yaml, emits `args_*.txt` files into the +rundir specified by `general.rundir`, calls `create_eos_posterior_pipeline` +to assemble the condor DAG (`marginalize_hyperparameters.dag`), and submits +it. Inspect any rundir's `.sub` files to see exactly what every executable +was invoked with. + +Configs in this directory: + +| Config | Rundir | What it exercises | +|-------------------------------------|-----------------------|-------------------------------------------------------------------------------------| +| `hyperpipe_conf.yaml` | `rundir/` | Baseline pipeline: posterior-only resampling (no puff lane). | +| `hyperpipe_conf_tracer.yaml` | `rundir_tracer/` | Parsimonious-placement (tracer) variant — MARG_PUFF lane suppressed. | +| `hyperpipe_conf_osg.yaml` | `rundir_osg/` | OSG / IGWN submit-host setup (containers, OSG attributes). | +| `hyperpipe_conf_linear_uvw.yaml` | `rundir_linear_uvw/` | Coordinate transformation: fit in `(u,v,w)` while sampling in `(x,y,z)`. | + + +## `hyperpipe_conf.yaml` — baseline + +**What it exercises.** The simplest end-to-end loop: MARG evaluates +likelihoods on the initial grid, the EOS posterior fits a GP/RF, the next +iteration draws candidate grid points by resampling the posterior. There +is no PUFF lane (no `puff.exe` set) and no tracer placement — the +posterior alone seeds the next iteration's grid. + +**Build.** + +```bash +util_RIFT_hyperpipe.py --config ./hyperpipe_conf.yaml +# equivalent to: make rundir +``` + +**Inspect.** After the DAG finishes: + +```bash +(cd rundir; plot_posterior_corner.py \ + --posterior-file posterior-2.dat \ + --composite-file all.marg_net --composite-file-has-labels \ + --parameter x --parameter y --parameter z \ + --lnL-cut 15 --use-all-composite-but-grayscale) +``` + +You should see the recovered isotropic Gaussian centred where +`example_gaussian.py` placed it (default `[-5, 0, 0]`). + + +## `hyperpipe_conf_tracer.yaml` — parsimonious placement + +**What it exercises.** The tracer pathway (`util_HyperparameterTracerUpdate.py` +as `puff.exe`). This consumes `all.marg_net` directly and writes +`grid-{k+1}.dat`, suppressing the MARG_PUFF lane entirely. Per the comment +in the config file, the saving is about 1.7–1.8× for `N=5–6` iterations on +this toy — MARG still runs every iteration, but no separate MARG_PUFF +lane is built. + +The yaml exposes the SMC-MALA / birth-death sampler hyperparameters in +`puff.settings` so you can twiddle them without escaping into +`extra-args`. + +**Build.** + +```bash +util_RIFT_hyperpipe.py --config ./hyperpipe_conf_tracer.yaml +# equivalent to: make rundir_tracer +``` + +**Inspect.** Same plot command as above with `--posterior-file +rundir_tracer/posterior-3.dat`, or use the shell helper: + +```bash +make rundir_tracer_plots ``` -util_RIFT_hyperpipe.py --config-name hyperpipe_conf_tracer.yaml + + +## `hyperpipe_conf_osg.yaml` — OSG / IGWN setup + +**What it exercises.** Submit-host configuration for a condor pool with +OSG attributes and an IGWN-prefixed condor-local nonworker setup. Same +3-D Gaussian toy as the baseline, but `general.use-osg`, +`general.condor-local-nonworker`, and +`general.condor-local-nonworker-igwn-prefix` are all enabled. Use this +as a starting point when adapting one of the other demos for OSG or LIGO +clusters. + +**Build.** + +```bash +util_RIFT_hyperpipe.py --config ./hyperpipe_conf_osg.yaml ``` +> **Note.** As of writing the OSG yaml has a couple of stale keys +> (`explode marg jobs`, `puff factor`) that hydra would reject because +> they use spaces where the schema expects hyphens. Fix those locally +> before running (`explode-marg-jobs`, `puff-factor`) or copy from the +> baseline yaml. + + +## `hyperpipe_conf_linear_uvw.yaml` — coordinate transformation + +**What it exercises.** The decoupled-bases path in +`util_ConstructEOSPosterior.py`: the iteration (puff, marg evaluator, +convergence test) operates in the data-file basis `(x, y, z)`, but the +EOS posterior step *fits its GP/RF in a transformed basis `(u, v, w)`* +routed through the `linear_coordinate_convert.py` plugin. The +likelihood evaluator (`example_gaussian_uvw.py`) uses the same plugin +library to define a Gaussian whose principal axes lie along `(u, v, w)` +with deliberately unequal sigmas — so the rotation is observable in the +recovered posterior. + +Two pieces of the new machinery are exercised together: + +1. **Coordinate plugin contract.** `post.coord-module: + linear_coordinate_convert.py` (plus `--supplementary-coordinate-ini` + and `--supplementary-coordinate-chart uvw_rotated` via + `post.extra-args`) walks the loader at + `RIFT.misc.coordinate_plugin.load_coordinate_converter`. + +2. **`--parameter-implied` / `--parameter-nofit` semantics in + EOSPosterior.** `post.coords-implied: u v w` is the fit basis, + `post.coords-nofit: x y z` is the MC sampling basis, and + `post.coords-fit` is empty. This is the IntrinsicPosterior-style + coord-arg mechanism, now wired through to EOSPosterior. + +**Build.** + +```bash +util_RIFT_hyperpipe.py --config ./hyperpipe_conf_linear_uvw.yaml +``` + +**Inspect.** The recovered `(x, y, z)` posterior should be an +elongated ellipsoid pointing along the rotated `v`-axis +(`(-1, +1, 0) / sqrt(2)`), with the narrowest extent along the +`u`-axis (`(+1, +1, 0) / sqrt(2)`): + +```bash +(cd rundir_linear_uvw; plot_posterior_corner.py \ + --posterior-file posterior-4.dat \ + --composite-file all.marg_net --composite-file-has-labels \ + --parameter x --parameter y --parameter z \ + --lnL-cut 15 --use-all-composite-but-grayscale) +``` + + +## Pre-hydra Makefile-style demos + +The Makefile also carries two targets that bypass `util_RIFT_hyperpipe.py` +and drive `create_eos_posterior_pipeline` directly: + +- `make Gaussian_adaptive_unimodal` — single-event adaptive run with the + baseline puffball. Useful when you want to inspect the + `args_*.txt` files by hand or when hydra is in the way. +- `make Gaussian_adaptive_bimodal` — two events (`example_gaussian.py` + + `example_gaussian2.py`) into the same posterior, illustrating + multi-event heterogeneous-driver mode. + +These predate the hydra wrapper; the hydra yamls above are the +recommended entry point for new work. + + +## Initial grid + +Every demo above seeds from `blind_gaussian_3d_xy_plus.dat` (1000 uniform +points covering a corner of the `[-7, 7]^3` cube). Regenerate it with +`make blind_gaussian_3d_xy_plus.dat` if you delete it. The Gaussian's +centre is configurable in `example_gaussian.py` — `make +change_center_location` rewrites it via `sed`. diff --git a/MonteCarloMarginalizeCode/Code/demo/hyperpipe/hyperpipe_conf_linear_uvw.yaml b/MonteCarloMarginalizeCode/Code/demo/hyperpipe/hyperpipe_conf_linear_uvw.yaml index 5092208fa..fe5b28046 100644 --- a/MonteCarloMarginalizeCode/Code/demo/hyperpipe/hyperpipe_conf_linear_uvw.yaml +++ b/MonteCarloMarginalizeCode/Code/demo/hyperpipe/hyperpipe_conf_linear_uvw.yaml @@ -1,46 +1,48 @@ -# End-to-end demo for the linear_coordinate_convert plugin. +# End-to-end demo for the linear_coordinate_convert plugin with DECOUPLED +# fit / sampling bases inside util_ConstructEOSPosterior.py. # # Goal # ---- -# The pipeline iterates in (x, y, z) -- same basis as hyperpipe_conf_tracer.yaml -# -- but the likelihood is a 3-D Gaussian whose principal axes lie along -# (u, v, w), a 45-degree rotation of (x, y, z) in the xy-plane. Sigmas are -# deliberately unequal (sigma_u != sigma_v != sigma_w) so the rotation is -# observable in the resulting (x, y, z) posterior -- you should see an -# elongated, off-axis ellipsoid that points along the rotated v-axis. +# Iteration (puff lane, marg evaluator, condor DAG) lives in (x, y, z) -- +# the data file's column basis. The EOS posterior step FITS its GP/RF in +# (u, v, w), a 45-degree rotation of (x, y, z) in the xy-plane that +# axis-aligns the unequal-sigma Gaussian. The MC integrator inside the +# EOS posterior still SAMPLES in (x, y, z) so the output samples come out +# in the same basis the rest of the pipeline expects. # -# Why iteration stays in (x, y, z) -# -------------------------------- -# The hyperpipe yaml schema currently shares `coords-fit` between the puff -# stage and the post stage (see RIFT/hyperpipe/coords.py:to_puff_args and -# to_post_args). Having the puff lane and the EOS posterior operate in -# different bases would require either a schema extension or a Makefile- -# style direct invocation; both are out of scope for this demo. What this -# yaml DOES demonstrate is the most useful piece of the new plugin -# machinery: a user-supplied likelihood evaluator (example_gaussian_uvw.py) -# loading linear_coordinate_convert.py via the same plugin contract that -# util_ConstructEOSPosterior.py uses. The library is exercised -# end-to-end; the post-stage's `--supplementary-coordinate-code` path is -# covered by the unit tests in RIFT/misc/coordinate_plugin.py. +# This is the configuration the IntrinsicPosterior-style coordinate +# argument mechanism (--parameter-implied / --parameter-nofit) enables +# inside the EOS posterior: implied = fit dim only, nofit = sample dim +# only, the coordinate plugin routes between them. # # Reading this file # ----------------- # Differences from hyperpipe_conf_tracer.yaml: -# * `marg-list[0].exe` -> example_gaussian_uvw.py (uses linear plugin) -# * `marg-list[0].args` ships the absolute paths to the plugin / ini so -# the marg job finds them regardless of the condor scratch cwd. -# * `general.rundir` renamed to keep the two tracer rundirs side-by-side. -# Everything else mirrors hyperpipe_conf_tracer.yaml exactly. +# * post.coords-fit is EMPTY -- no parameter is both fit and sampled. +# * post.coords-implied: "u v w" -- the FIT basis. The coordinate +# plugin produces these from the data file's (x, y, z) columns; +# the MC integrator never sees them. +# * post.coords-nofit: "x y z" -- the MC SAMPLING basis. These are the +# data file's columns; the integrator samples them, the puff lane +# puffs them, and the output posterior is in this basis. +# * post.coord-module: linear_coordinate_convert.py with the +# uvw_rotated chart, plus the ini that carries A and b. +# * marg-list[0].exe: example_gaussian_uvw.py, which uses the same +# plugin library to define its Gaussian likelihood in (u, v, w). # # Run with: # util_RIFT_hyperpipe.py --config ./hyperpipe_conf_linear_uvw.yaml # -# Then plot with: +# Then plot the (x, y, z) posterior from rundir_linear_uvw/: # (cd rundir_linear_uvw; plot_posterior_corner.py \ # --posterior-file posterior-4.dat \ # --composite-file all.marg_net --composite-file-has-labels \ # --parameter x --parameter y --parameter z \ # --lnL-cut 15 --use-all-composite-but-grayscale) +# +# The recovered ellipsoid in (x, y, z) should point along the rotated +# v-axis ((-1, +1, 0)/sqrt(2)) with the narrowest extent along the +# u-axis ((+1, +1, 0)/sqrt(2)). arch: method: default @@ -50,32 +52,39 @@ arch: start-iteration: 0 post: - # Iteration basis (== puff basis == data-file column basis). The post - # stage's GP/RF fit happens here too; the rotated (u, v, w) basis only - # appears inside the likelihood evaluator below. - coords-fit: "x y z" - # Integration ranges in (x, y, z). Sized to comfortably enclose the - # initial grid plus a margin for the Gaussian tails after rotation: - # the Gaussian peaks at (u, v, w) = (0, 4.95, 3.5), which back-projects - # to (x, y, z) ~= (-3.5, 3.5, 3.5), and the broadest sigma (sigma_w = - # 1.5 along z) needs ~+/-5 in z to contain ~3 sigma. - coords-sample: "x:[-7,1] y:[0,7] z:[-1,8]" + # FIT basis -- only seen by the GP/RF. Plugin produces these from the + # data-file columns. Nothing about implied coords is sampled by the MC, + # so they don't need integration ranges. + coords-implied: "u v w" + # MC SAMPLING basis -- same as the data file's columns. The integrator + # samples these and the output posterior is written in this basis. + coords-nofit: "x y z" + # Sampling-basis integration ranges sized to enclose ~3 sigma of the + # rotated Gaussian (centred at (u, v, w) = (0, 4.95, 3.5), which back- + # projects to (x, y, z) ~= (-3.5, 3.5, 3.5); broadest extent is along + # z with sigma_w = 1.5). + coords-sample: "x:[-7,1] y:[0,7] z:[-1,8]" + # The coordinate plugin sits in this directory. ${hydra:runtime.cwd} + # resolves to where you invoked util_RIFT_hyperpipe.py from -- run it + # from demo/hyperpipe/ for the path below to work. + coord-module: ${hydra:runtime.cwd}/linear_coordinate_convert.py + # --supplementary-coordinate-{ini,chart,function} land here. function + # defaults to convert_coordinates so we don't have to set it. + extra-args: "--supplementary-coordinate-ini ${hydra:runtime.cwd}/linear_coordinate_convert.ini --supplementary-coordinate-chart uvw_rotated" settings: fit-method: rf marg-list: - name: Gaussian_uvw exe: example_gaussian_uvw.py - # The marg job lives inside a condor scratch dir, so the plugin / - # ini paths need to be absolute. ${hydra:runtime.cwd} is Hydra's - # original-invocation cwd resolver -- this assumes you run - # util_RIFT_hyperpipe.py from inside demo/hyperpipe/. If you invoke - # from elsewhere, edit the next two args to point at wherever you - # put linear_coordinate_convert.{py,ini}. args: "--outdir Gaussian_uvw_example --conforming-output-name --coord-plugin ${hydra:runtime.cwd}/linear_coordinate_convert.py --coord-ini ${hydra:runtime.cwd}/linear_coordinate_convert.ini --mu-uvw 0.0,4.95,3.5 --sigma-uvw 0.5,1.0,1.5" n-chunk: 100 puff: + # Tracer drop-in. Reads all.marg_net (which has columns (x, y, z)) and + # produces grid-{k+1}.dat in (x, y, z). Sees --parameter x y z because + # to_puff_args emits the SAMPLING basis (coords-fit + coords-nofit), + # not the fit basis. exe: util_HyperparameterTracerUpdate.py input-source: marg_net puff-factor: 0.5 @@ -89,10 +98,6 @@ puff: rng-seed: null init: - # Reuse the same initial grid as the baseline tracer demo. Columns are - # (x, y, z); the Gaussian centre at (u, v, w) = (0, 4.95, 3.5) lies near - # the centroid of this grid after rotation, so the iteration has - # something to lock onto right from iteration zero. file: blind_gaussian_3d_xy_plus.dat general: diff --git a/MonteCarloMarginalizeCode/Code/demo/hyperpipe/linear_coordinate_convert.py b/MonteCarloMarginalizeCode/Code/demo/hyperpipe/linear_coordinate_convert.py index d23261b62..0df0a9ea1 100644 --- a/MonteCarloMarginalizeCode/Code/demo/hyperpipe/linear_coordinate_convert.py +++ b/MonteCarloMarginalizeCode/Code/demo/hyperpipe/linear_coordinate_convert.py @@ -205,3 +205,81 @@ def convert_coordinates(x_in, coord_names, low_level_coord_names, chart=None, ** # Then pick out the columns the driver actually wants, in its order. out_perm = [OUTPUT_PARAMETERS.index(name) for name in coord_names] return y_full[:, out_perm] + + +def inverse_convert_coordinates(y_in, coord_names, low_level_coord_names, + chart=None, **kwargs): + """Inverse of ``convert_coordinates``: y -> x via x = A^{-1} (y - b). + + Required by RIFT stages that need to round-trip through the plugin + basis -- in particular the puff lane (util_HyperparameterPuffball.py + and util_HyperparameterTracerUpdate.py): they read the grid in the + file basis, forward-transform to do the displacement step in the + plugin basis, then inverse-transform the displaced points back to + the file basis so the output .dat preserves the file's column + structure. + + Requires A to be square and invertible. For affine maps that lose + information (non-square A, or square-but-singular A) there is no + closed-form inverse and we raise instead of silently using a + pseudo-inverse: the user is better served by a clear error than by + a quietly-wrong round-trip. + """ + if _A is None or _b is None: + raise RuntimeError( + "linear_coordinate_convert: prepare() was not called. This " + "means the loader didn't pass an ini file -- supply " + "--supplementary-coordinate-ini." + ) + if _A.shape[0] != _A.shape[1]: + raise ValueError( + "linear_coordinate_convert: cannot invert a non-square A " + f"(shape={_A.shape!r}). inverse_convert_coordinates only " + "supports square (#out == #in) maps." + ) + # Compute A^{-1} lazily and cache. numpy.linalg.inv will raise + # LinAlgError on singular A -- we let it propagate, the caller + # gets a useful traceback. + global _A_inv + try: + _A_inv # type: ignore[name-defined] + except NameError: + _A_inv = np.linalg.inv(_A) + + y = np.asarray(y_in, dtype=float) + if y.ndim != 2: + raise ValueError( + "linear_coordinate_convert.inverse: expected 2D y_in, got " + f"shape {y.shape}" + ) + + # Assemble a full-width (N, len(OUTPUT_PARAMETERS)) array in the + # plugin's canonical output order, padding missing columns with 0 + # only when the caller passed a strict subset. In practice the + # puff lane passes all OUTPUT_PARAMETERS, so this is a permutation. + y_full = np.zeros((y.shape[0], len(OUTPUT_PARAMETERS)), dtype=float) + seen = set() + for j, name in enumerate(coord_names): + if name not in OUTPUT_PARAMETERS: + raise ValueError( + "linear_coordinate_convert.inverse: coord_names contains " + f"{name!r}, not declared in ini's output_parameters " + f"{OUTPUT_PARAMETERS!r}" + ) + y_full[:, OUTPUT_PARAMETERS.index(name)] = y[:, j] + seen.add(name) + if len(seen) < len(OUTPUT_PARAMETERS): + missing = set(OUTPUT_PARAMETERS) - seen + raise ValueError( + "linear_coordinate_convert.inverse: input matrix does not span " + f"every output dimension; missing {sorted(missing)!r}. A " + "non-square partial inverse is ambiguous; pass all of " + f"{OUTPUT_PARAMETERS!r} via coord_names." + ) + + # Apply A^{-1} @ (y - b) row-wise. + x_aligned = (y_full - _b) @ _A_inv.T # shape (N, len(INPUT_PARAMETERS)) + + # Permute into the caller's requested low_level_coord_names order. + out_perm = [INPUT_PARAMETERS.index(name) for name in low_level_coord_names] + return x_aligned[:, out_perm] diff --git a/MonteCarloMarginalizeCode/Code/demo/pipeline/.gitignore b/MonteCarloMarginalizeCode/Code/demo/pipeline/.gitignore new file mode 100644 index 000000000..8666d8346 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/demo/pipeline/.gitignore @@ -0,0 +1,5 @@ +rundir_baseline/ +rundir_grid/ +rundir_slices/ +fake.cache +*.cache diff --git a/MonteCarloMarginalizeCode/Code/demo/pipeline/Makefile b/MonteCarloMarginalizeCode/Code/demo/pipeline/Makefile new file mode 100644 index 000000000..0a530c0ae --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/demo/pipeline/Makefile @@ -0,0 +1,118 @@ +# Standard pipeline-build demos for util_RIFT_pseudo_pipe.py +# +# These targets BUILD (do not submit) a RIFT DAG from a reference .ini + coinc +# using fake data, then verify the generated condor submit files. They are +# fast, need no real frames/GPUs, and double as regression tests that the +# per-distance likelihood export flags (Plan A grid, Plan B slices) thread all +# the way through util_RIFT_pseudo_pipe.py -> create_event_parameter_pipeline_* +# -> the ILE_extr submit file. +# +# Run inside the RIFT environment, e.g. +# pixi run --manifest-path ../../../../../pixi.toml make all +# or, with a pip-installed RIFT on PATH, +# make all + +RIFT_CODE_ROOT := $(abspath ../..) +REPO_ROOT := $(abspath ../../../..) + +REF_INI ?= $(REPO_ROOT)/.travis/ref_ini/GW150914.ini +COINC ?= $(REPO_ROOT)/.travis/ref_ini/coinc.xml +FAKE_CACHE ?= $(CURDIR)/fake.cache + +# Build-only environment: pretend we have a singularity image + datafind, use +# fake data, and put the in-tree bin/ on PATH so the pipeline builder and ILE +# executable resolve. +ENV = RIFT_LOWLATENCY=True \ + SINGULARITY_RIFT_IMAGE=foo \ + SINGULARITY_BASE_EXE_DIR=/usr/bin/ \ + GW_SURROGATE='' \ + PATH=$(RIFT_CODE_ROOT)/bin:$${PATH} \ + PYTHONPATH=$(RIFT_CODE_ROOT):$${PYTHONPATH:-} + +PIPE = util_RIFT_pseudo_pipe.py \ + --use-ini $(REF_INI) \ + --use-coinc $(COINC) \ + --fake-data-cache $(FAKE_CACHE) \ + --add-extrinsic + +.PHONY: help inputs baseline grid slices validate-grid validate-slices all clean zero-spin-phenomD + +help: + @echo "Targets:" + @echo " make inputs - verify reference ini/coinc are available" + @echo " make baseline - build a standard pipeline (no per-distance export)" + @echo " make grid - build + validate Plan-A distance-grid export" + @echo " make slices - build + validate Plan-B distance-slice export" + @echo " make all - run baseline, grid, and slices" + @echo " make zero-spin-phenomD - full end-to-end validation (build + run ILE locally + consolidate + posterior)" + @echo " make clean - remove generated run directories" + +zero-spin-phenomD: + $(MAKE) -C $(CURDIR)/zero_spin_phenomD all + +inputs: + @test -s "$(REF_INI)" || (echo "missing $(REF_INI)" && false) + @test -s "$(COINC)" || (echo "missing $(COINC)" && false) + @touch "$(FAKE_CACHE)" + @echo "Using ini: $(REF_INI)" + @echo "Using coinc: $(COINC)" + +baseline: inputs + rm -rf "$(CURDIR)/rundir_baseline" + $(ENV) $(PIPE) --use-rundir "$(CURDIR)/rundir_baseline" + @test -s "$(CURDIR)/rundir_baseline/ILE_extr.sub" + @! grep -q -- "--export-marginal-distance-grid" "$(CURDIR)/rundir_baseline/ILE_extr.sub" + @! grep -q -- "--export-distance-slices" "$(CURDIR)/rundir_baseline/ILE_extr.sub" + @echo "OK: baseline pipeline built; no per-distance export leaked into ILE_extr.sub" + +grid: inputs + rm -rf "$(CURDIR)/rundir_grid" + $(ENV) $(PIPE) --use-rundir "$(CURDIR)/rundir_grid" --export-marginal-distance-grid + $(MAKE) validate-grid + +validate-grid: + @test -s "$(CURDIR)/rundir_grid/ILE_extr.sub" + @grep -q -- "--export-marginal-distance-grid" "$(CURDIR)/rundir_grid/ILE_extr.sub" + @grep -q -- "--internal-use-lnL" "$(CURDIR)/rundir_grid/ILE_extr.sub" + @! grep -q -- "--export-marginal-distance-grid" "$(CURDIR)/rundir_grid/ILE.sub" + @grep -q -- "--distance-marginalization " "$(CURDIR)/rundir_grid/args_ile.txt" + @grep -q -- "--distance-marginalization " "$(CURDIR)/rundir_grid/ILE.sub" + @! grep -q -- "--distance-marginalization " "$(CURDIR)/rundir_grid/ILE_extr.sub" + @test -s "$(CURDIR)/rundir_grid/consolidate_dgrid.sub" + @test -s "$(CURDIR)/rundir_grid/consolidate_dgrid.sh" + @grep -q -- "EXTR_out.xml_\*_.dgrid" "$(CURDIR)/rundir_grid/consolidate_dgrid.sh" + @grep -q -- "all_dgrid.dat" "$(CURDIR)/rundir_grid/consolidate_dgrid.sh" + @grep -q "consolidate_dgrid" $(CURDIR)/rundir_grid/*.dag + @echo "OK: Plan-A grid export only on ILE_extr.sub; consolidate_dgrid job present; distance marginalization kept on intrinsic ILE.sub, disabled only at the extrinsic stage" + +slices: inputs + rm -rf "$(CURDIR)/rundir_slices" + $(ENV) $(PIPE) --use-rundir "$(CURDIR)/rundir_slices" \ + --export-distance-slices 10 \ + --export-distance-slices-wing-delta-lnL 7.0 \ + --export-distance-slices-skip-threshold 1.0 + $(MAKE) validate-slices + +validate-slices: + @test -s "$(CURDIR)/rundir_slices/ILE_extr.sub" + @grep -q -- "--export-distance-slices 10" "$(CURDIR)/rundir_slices/ILE_extr.sub" + @grep -q -- "--distance-slice-wing-delta-lnL 7.0" "$(CURDIR)/rundir_slices/ILE_extr.sub" + @grep -q -- "--distance-slice-skip-threshold 1.0" "$(CURDIR)/rundir_slices/ILE_extr.sub" + @grep -q -- "--internal-use-lnL" "$(CURDIR)/rundir_slices/ILE_extr.sub" + @! grep -q -- "--export-distance-slices" "$(CURDIR)/rundir_slices/ILE.sub" + @grep -q -- "--distance-marginalization " "$(CURDIR)/rundir_slices/args_ile.txt" + @grep -q -- "--distance-marginalization " "$(CURDIR)/rundir_slices/ILE.sub" + @! grep -q -- "--distance-marginalization " "$(CURDIR)/rundir_slices/ILE_extr.sub" + @test -s "$(CURDIR)/rundir_slices/consolidate_dslice.sub" + @test -s "$(CURDIR)/rundir_slices/consolidate_dslice.sh" + @grep -q -- "EXTR_out.xml_\*_.dslice" "$(CURDIR)/rundir_slices/consolidate_dslice.sh" + @grep -q -- "all_dslice.dat" "$(CURDIR)/rundir_slices/consolidate_dslice.sh" + @grep -q "consolidate_dslice" $(CURDIR)/rundir_slices/*.dag + @echo "OK: Plan-B slice export only on ILE_extr.sub; consolidate_dslice job present; distance marginalization kept on intrinsic ILE.sub, disabled only at the extrinsic stage" + +all: baseline grid slices + @echo "All pipeline-build demos passed." + +clean: + rm -rf "$(CURDIR)/rundir_baseline" "$(CURDIR)/rundir_grid" "$(CURDIR)/rundir_slices" "$(FAKE_CACHE)" + $(MAKE) -C $(CURDIR)/zero_spin_phenomD clean diff --git a/MonteCarloMarginalizeCode/Code/demo/pipeline/README.md b/MonteCarloMarginalizeCode/Code/demo/pipeline/README.md new file mode 100644 index 000000000..1f872eeac --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/demo/pipeline/README.md @@ -0,0 +1,57 @@ +# Pipeline-build demos (`util_RIFT_pseudo_pipe.py`) + +Fast, submission-free smoke tests of the end-to-end pipeline builder. Each +target runs `util_RIFT_pseudo_pipe.py` against a reference `.ini` + `coinc.xml` +with **fake data**, producing a complete RIFT run directory (helper output, +`args_*.txt`, and condor `*.sub` files) **without** submitting anything or +needing real frames, PSDs, or GPUs. + +These double as regression tests for argument threading: a flag set on +`util_RIFT_pseudo_pipe.py` must survive through +`create_event_parameter_pipeline_BasicIteration` (CEPP) and land in the correct +condor submit file. + +## Running + +Inside the RIFT environment: + +```bash +# with pixi +pixi run --manifest-path ../../../../../pixi.toml make all +# or, with a pip-installed RIFT on PATH +make all +``` + +Targets: + +| target | what it builds / checks | +| --- | --- | +| `baseline` | a standard pipeline; asserts no per-distance export leaks into `ILE_extr.sub` | +| `grid` | `--export-marginal-distance-grid` (Plan A); asserts `--export-marginal-distance-grid --internal-use-lnL` land in `ILE_extr.sub`, the flag does **not** appear in the intrinsic `ILE.sub`, and that distance marginalization is **kept on `ILE.sub` but stripped from `ILE_extr.sub`** | +| `slices` | `--export-distance-slices 10 ...` (Plan B); asserts `--export-distance-slices 10`, `--distance-slice-wing-delta-lnL`, `--distance-slice-skip-threshold`, `--internal-use-lnL` land in `ILE_extr.sub`, nothing leaks into `ILE.sub`, and distance marginalization is **kept on `ILE.sub` but stripped from `ILE_extr.sub`** | +| `all` | all three | +| `clean` | remove generated `rundir_*` and the fake cache | + +Inputs default to `.travis/ref_ini/GW150914.ini` and `.travis/ref_ini/coinc.xml`; +override with `make REF_INI=... COINC=... all`. + +## Why the extrinsic stage + +Per-distance likelihood export (both Plan A density grids and Plan B fixed-`d` +slices) is emitted by the **last-iteration extrinsic** ILE stage (`ILE_extr`), +not by the intrinsic ILE jobs that run every iteration. `util_RIFT_pseudo_pipe.py` +therefore: + +1. forces ILE `lnL` mode (for the whole run), and +2. passes the corresponding `--last-iteration-export-*` flag to CEPP, which + appends the ILE-level export flags to the `ILE_extr` argument string only + **and strips `--distance-marginalization` from that extrinsic stage only**. + +Distance marginalization is *not* disabled globally: the intrinsic iterations +keep it (a large speedup). Only the final extrinsic stage that integrates the +pure likelihood vs distance has it removed. + +These export flags require `--add-extrinsic` (so the extrinsic stage exists); +the demo passes it explicitly. See +`demo/rift/add_distance_grids/PLAN_B_DESIGN.md` for the Plan-B design and the +`.dslice` re-marginalization API. diff --git a/MonteCarloMarginalizeCode/Code/demo/pipeline/zero_spin_phenomD/.gitignore b/MonteCarloMarginalizeCode/Code/demo/pipeline/zero_spin_phenomD/.gitignore new file mode 100644 index 000000000..8d1944e4b --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/demo/pipeline/zero_spin_phenomD/.gitignore @@ -0,0 +1,3 @@ +rundir_build/ +rundir_extr/ +fake.cache diff --git a/MonteCarloMarginalizeCode/Code/demo/pipeline/zero_spin_phenomD/Makefile b/MonteCarloMarginalizeCode/Code/demo/pipeline/zero_spin_phenomD/Makefile new file mode 100644 index 000000000..52ec4ce15 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/demo/pipeline/zero_spin_phenomD/Makefile @@ -0,0 +1,165 @@ +# End-to-end validation: pseudo_pipe build + local ILE_extr run + .dgrid +# consolidation + util_ConstructEOSPosterior.py reconstruction. +# +# Uses the fake-data zero-noise BBH cache from .travis/ILE-GPU-Paper/demos with +# IMRPhenomD (zero spin) and the AV sampler, over a small mass grid. Designed +# to run in a few minutes on a single laptop -- no condor, no GPU. +# +# Run (inside the RIFT environment): +# pixi run --manifest-path ../../../../../pixi.toml make all +# or, with a pip-installed RIFT on PATH, +# make all + +RIFT_CODE_ROOT := $(abspath ../../..) +REPO_ROOT := $(abspath ../../../../..) +CI_DEMO := $(REPO_ROOT)/.travis/ILE-GPU-Paper/demos + +REF_INI ?= $(CURDIR)/zero_spin_phenomD.ini +COINC ?= $(REPO_ROOT)/.travis/ref_ini/coinc.xml +CACHE ?= $(CI_DEMO)/zero_noise.cache +PSD ?= $(CI_DEMO)/HLV-ILIGO_PSD.xml.gz +GRID ?= $(CI_DEMO)/overlap-grid.xml.gz + +ILE_EXE := $(RIFT_CODE_ROOT)/bin/integrate_likelihood_extrinsic_batchmode +CONS_EXE := $(RIFT_CODE_ROOT)/bin/util_ConsolidateDistanceGrids.py +EOS_EXE := $(RIFT_CODE_ROOT)/bin/util_ConstructEOSPosterior.py + +RUN_BUILD := $(CURDIR)/rundir_build +RUN_EXTR := $(CURDIR)/rundir_extr + +# Tiny test: 3 grid rows, AV with low n_eff target -> ~30 s/row on a laptop. +N_EVENTS ?= 3 +N_EFF ?= 50 +N_MAX ?= 30000 + +# Fake-data time window from the zero_noise cache (event at 1000000014.236...). +EVT_TIME := 1000000014.236547946 +DATA_T0 := 1000000008 +DATA_T1 := 1000000016 + +ENV = GW_SURROGATE='' \ + RIFT_LOWLATENCY=True \ + SINGULARITY_RIFT_IMAGE=foo \ + SINGULARITY_BASE_EXE_DIR=/usr/bin/ \ + PATH=$(RIFT_CODE_ROOT)/bin:$${PATH} \ + PYTHONPATH=$(RIFT_CODE_ROOT):$${PYTHONPATH:-} + +# ILE_extr arguments tuned for the zero-noise fake-data BBH demo (zero-spin +# IMRPhenomD, AV sampler, lnL mode, distance-grid export on). These match +# what util_RIFT_pseudo_pipe.py + create_event_parameter_pipeline_* would +# emit at the extrinsic stage for the same configuration. +EXTR_ARGS := \ + --n-chunk 10000 --time-marginalization \ + --reference-freq 100.0 --adapt-weight-exponent 0.1 \ + --event-time $(EVT_TIME) --save-P 0.1 \ + --cache-file $(CACHE) \ + --fmin-template 10 --n-max $(N_MAX) --fmax 1700.0 \ + --save-deltalnL inf --l-max 2 --n-eff $(N_EFF) \ + --approximant IMRPhenomD --adapt-floor-level 0.1 \ + --force-xpy --d-max 1000 \ + --psd-file H1=$(PSD) --psd-file L1=$(PSD) \ + --channel-name H1=FAKE-STRAIN --channel-name L1=FAKE-STRAIN \ + --inclination-cosine-sampler --declination-cosine-sampler \ + --data-start-time $(DATA_T0) --data-end-time $(DATA_T1) \ + --inv-spec-trunc-time 0 --no-adapt-after-first --no-adapt-distance \ + --srate 4096 --sampler-method AV --internal-use-lnL \ + --export-marginal-distance-grid \ + --sim-xml overlap-grid.xml.gz \ + --n-events-to-analyze $(N_EVENTS) \ + --output-file demo_extr + +.PHONY: help inputs build validate-build run-extr consolidate posterior all clean + +help: + @echo "Targets:" + @echo " make inputs - verify fake-data inputs are available" + @echo " make build - run util_RIFT_pseudo_pipe.py to build a pipeline (validates threading)" + @echo " make validate-build - check the build produced a .dgrid export and consolidation job" + @echo " make run-extr - run ILE_extr locally (no condor) on the first $(N_EVENTS) grid rows" + @echo " make consolidate - run util_ConsolidateDistanceGrids.py on the produced .dgrid files" + @echo " make posterior - run util_ConstructEOSPosterior.py on the consolidated .dgrid -> joint posterior" + @echo " make all - full chain" + @echo " make clean - remove rundir_build, rundir_extr, fake.cache" + +inputs: + @test -s "$(CACHE)" || (echo "missing fake cache $(CACHE) (run the ILE-GPU-Paper demo Makefile first)" && false) + @test -s "$(PSD)" || (echo "missing PSD $(PSD)" && false) + @test -s "$(GRID)" || (echo "missing input grid $(GRID)" && false) + @test -s "$(REF_INI)" && test -s "$(COINC)" || (echo "missing reference ini/coinc" && false) + @touch "$(CURDIR)/fake.cache" + @echo "All inputs present." + +# Build a RIFT pipeline with the per-distance grid export turned on (forces +# AV sampler + IMRPhenomD + zero spin via CLI overrides on the GW150914 +# reference ini). We use --fake-data-cache to keep the build offline; the +# resulting pipeline could run on the real fake-data cache by setting the +# right event-time/coinc, but that is beyond this build-validation target. +build: inputs + rm -rf "$(RUN_BUILD)" + $(ENV) util_RIFT_pseudo_pipe.py \ + --use-ini "$(REF_INI)" \ + --use-coinc "$(COINC)" \ + --use-rundir "$(RUN_BUILD)" \ + --fake-data-cache "$(CURDIR)/fake.cache" \ + --add-extrinsic \ + --export-marginal-distance-grid \ + --assume-nospin \ + --approx IMRPhenomD \ + --ile-sampler-method AV + $(MAKE) validate-build + +validate-build: + @test -s "$(RUN_BUILD)/ILE_extr.sub" + @grep -q -- "--export-marginal-distance-grid" "$(RUN_BUILD)/ILE_extr.sub" + @grep -q -- "--sampler-method AV" "$(RUN_BUILD)/ILE_extr.sub" + @grep -q -- "--internal-use-lnL" "$(RUN_BUILD)/ILE_extr.sub" + @! grep -q -- "--distance-marginalization " "$(RUN_BUILD)/ILE_extr.sub" + @grep -q -- "IMRPhenomD" "$(RUN_BUILD)/ILE_extr.sub" + @test -s "$(RUN_BUILD)/consolidate_dgrid.sub" + @test -s "$(RUN_BUILD)/consolidate_dgrid.sh" + @grep -q "consolidate_dgrid" $(RUN_BUILD)/*.dag + @echo "OK: pipeline built; ILE_extr.sub carries AV+IMRPhenomD+grid-export, distance marg disabled only at extrinsic stage, consolidate_dgrid wired into DAG." + +# Direct local run of the ILE_extr binary on a small subset of the grid -- no +# condor. Produces real *.dgrid files we can then consolidate and reconstruct +# the posterior from. Event-time/cache match the .travis/ILE-GPU-Paper fake +# data (the GW150914 ini used for the build step has a different event time +# and no matching frames; running directly here avoids that mismatch while +# still exercising the same ILE code path). +run-extr: inputs + rm -rf "$(RUN_EXTR)" + mkdir -p "$(RUN_EXTR)" + cp "$(GRID)" "$(RUN_EXTR)/overlap-grid.xml.gz" + @echo "Running ILE_extr on $(N_EVENTS) grid rows (AV, IMRPhenomD zero-spin, lnL mode, --export-marginal-distance-grid)..." + cd "$(RUN_EXTR)" && $(ENV) "$(ILE_EXE)" $(EXTR_ARGS) + @echo "Produced .dgrid files:"; ls "$(RUN_EXTR)"/demo_extr_*_.dgrid + +consolidate: run-extr + @echo "Consolidating per-event .dgrid files..." + cd "$(RUN_EXTR)" && $(ENV) "$(CONS_EXE)" --input-glob 'demo_extr_*_.dgrid' --output all_dgrid.dat + @test -s "$(RUN_EXTR)/all_dgrid.dat" + @head -1 "$(RUN_EXTR)/all_dgrid.dat" | grep -q "^# lnL sigmaL m1 m2" + @n_data=$$(grep -cv "^#" "$(RUN_EXTR)/all_dgrid.dat"); echo "all_dgrid.dat: $$n_data data rows" + +posterior: consolidate + @echo "Reconstructing joint (m1, m2, dist) posterior from consolidated grid..." + cd "$(RUN_EXTR)" && $(ENV) "$(EOS_EXE)" \ + --fname all_dgrid.dat \ + --parameter m1 --parameter m2 --parameter dist \ + --integration-parameter-range 'm1:[24,34]' \ + --integration-parameter-range 'm2:[24,34]' \ + --integration-parameter-range 'dist:[10,500]' \ + --lnL-offset 30 \ + --fname-output-samples joint_posterior \ + --fname-output-integral joint_evidence \ + --no-plots + @test -s "$(RUN_EXTR)/joint_posterior.dat" + @n_samples=$$(grep -cv "^#" "$(RUN_EXTR)/joint_posterior.dat"); echo "joint_posterior.dat: $$n_samples posterior samples" + @python -c "import numpy as np; d=np.genfromtxt('$(RUN_EXTR)/joint_posterior.dat', names=True); print(' summary:', {n: (round(float(d[n].mean()),3), round(float(d[n].std()),3)) for n in d.dtype.names if n in ('m1','m2','dist','lnL')})" + @echo "OK: end-to-end validation complete -- pipeline built, ILE_extr produced .dgrid output, consolidated, posterior reconstructed." + +all: build run-extr consolidate posterior + @echo "All validation steps passed." + +clean: + rm -rf "$(RUN_BUILD)" "$(RUN_EXTR)" "$(CURDIR)/fake.cache" diff --git a/MonteCarloMarginalizeCode/Code/demo/pipeline/zero_spin_phenomD/README.md b/MonteCarloMarginalizeCode/Code/demo/pipeline/zero_spin_phenomD/README.md new file mode 100644 index 000000000..a7da78d6e --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/demo/pipeline/zero_spin_phenomD/README.md @@ -0,0 +1,81 @@ +# Zero-spin IMRPhenomD .dgrid end-to-end validation + +End-to-end validation of the per-distance likelihood export pipeline using +**zero-spin IMRPhenomD**, the **AV** sampler, and a small mass grid of zero-noise +BBH points. The full chain runs in under a minute on a single laptop core -- +no condor, no GPU. + +## What it exercises + +1. **Build** (`make build`). Calls `util_RIFT_pseudo_pipe.py` with + `--add-extrinsic --export-marginal-distance-grid --assume-nospin --approx IMRPhenomD --ile-sampler-method AV` + to produce a complete RIFT run directory. Verifies the resulting + `ILE_extr.sub` carries the export flags, that distance marginalization is + disabled **only** at the extrinsic stage, and that the + `consolidate_dgrid.sub` consolidation job is wired into the DAG. + +2. **Run** (`make run-extr`). Bypasses condor and directly invokes + `integrate_likelihood_extrinsic_batchmode` on the first `N_EVENTS` rows of + the fake-data zero-noise BBH grid (`.travis/ILE-GPU-Paper/demos/overlap-grid.xml.gz`) + with arguments matching what the pipeline would emit at the extrinsic + stage. Produces one `.dgrid` file per intrinsic point. + +3. **Consolidate** (`make consolidate`). Runs + `util_ConsolidateDistanceGrids.py` over the per-event `.dgrid` files, + verifying header agreement and emitting a single `all_dgrid.dat` -- the + "net" intrinsic + distance grid that downstream tools consume. + +4. **Posterior** (`make posterior`). Feeds `all_dgrid.dat` into + `util_ConstructEOSPosterior.py` with `--parameter m1 --parameter m2 --parameter dist`, + reconstructing the joint (intrinsic + distance) posterior. Reports the + sample count and per-parameter mean / std as a sanity check. + +`make all` runs steps 1-4 sequentially; `make clean` removes the generated +run directories. + +## Inputs + +| input | source | notes | +| --- | --- | --- | +| `zero_spin_phenomD.ini` | local | minimal ini whose `[rift-pseudo-pipe]` section deliberately omits `approx`/`ile-sampler-method` so the CLI overrides win | +| coinc / fake cache / PSDs / grid | `.travis/...` | the same fake-data zero-noise BBH inputs used by the ILE-GPU-Paper demo and `.travis/test-build.sh` | + +## Tunables + +| variable | default | role | +| --- | --- | --- | +| `N_EVENTS` | 3 | number of grid rows to run locally in step 2 | +| `N_EFF` | 50 | ILE `--n-eff` target | +| `N_MAX` | 30000 | ILE `--n-max` cap | + +## Expected output + +``` +OK: pipeline built; ILE_extr.sub carries AV+IMRPhenomD+grid-export, ... +Produced .dgrid files: + rundir_extr/demo_extr_0_.dgrid + rundir_extr/demo_extr_1_.dgrid + rundir_extr/demo_extr_2_.dgrid +util_ConsolidateDistanceGrids.py: wrote 150 rows from 3 files to all_dgrid.dat +joint_posterior.dat: 2000 posterior samples + summary: {'lnL': (0.0, 0.0), 'm1': (...), 'm2': (...), 'dist': (...)} +OK: end-to-end validation complete -- pipeline built, ILE_extr produced .dgrid output, consolidated, posterior reconstructed. +All validation steps passed. +``` + +The injected true signal is m1 = m2 = 35 Msun at d = 200 Mpc; with `N_EVENTS=3` +the test only covers the lower edge of the mass grid (m1 = m2 ~ 26-29 Msun), +so the recovered posterior mean is biased toward those points -- this is by +design (fast test, not an accuracy demo). Increase `N_EVENTS` to cover the +full grid for a meaningful posterior. + +## Notes + +- Steps 2-4 use the same code paths the production pipeline does; only the + condor layer is bypassed. +- The .ini section `[rift-pseudo-pipe]` overrides CLI flags. The minimal + `zero_spin_phenomD.ini` here strips fields that would override + `--approx` / `--ile-sampler-method` / `--assume-nospin`. +- `util_ConstructEOSPosterior.py` requires `--integration-parameter-range` + for every fitted parameter; the Makefile supplies sensible ranges for + m1, m2, dist. diff --git a/MonteCarloMarginalizeCode/Code/demo/pipeline/zero_spin_phenomD/zero_spin_phenomD.ini b/MonteCarloMarginalizeCode/Code/demo/pipeline/zero_spin_phenomD/zero_spin_phenomD.ini new file mode 100644 index 000000000..230e8bb64 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/demo/pipeline/zero_spin_phenomD/zero_spin_phenomD.ini @@ -0,0 +1,63 @@ +# Minimal RIFT ini for the zero-spin IMRPhenomD validation demo. Derived from +# .travis/ref_ini/GW150914.ini but stripped to settings compatible with a fast +# zero-spin BBH test using the AV sampler. The [rift-pseudo-pipe] section +# avoids fields that would override --approx / --assume-nospin / --ile-sampler-method +# on the util_RIFT_pseudo_pipe.py command line. + +[analysis] +ifos=['H1','L1'] +singularity=False +osg=False + +[paths] + +[input] +max-psd-length=10000 + +[condor] +accounting_group=ligo.sim.o4.cbc.pe.rift +accounting_group_user=richard.oshaughnessy + +[datafind] +url-type=file +types = {'H1': 'H1_HOFT_C02', 'L1': 'L1_HOFT_C02', 'V1': ''} + +[data] +channels = {'H1': 'H1:DCS-CALIB_STRAIN_C02','L1': 'L1:DCS-CALIB_STRAIN_C02', 'V1': ''} + +[lalinference] +flow = {'H1': 20, 'L1': 20} +fhigh = { 'H1': 896, 'L1': 896 } + +[engine] +fref=20 +amporder = -1 +seglen = 4 +srate = 2048 +# zero spin -> compatible with IMRPhenomD +a_spin1-max = 0.0 +a_spin2-max = 0.0 +chirpmass-min = 23.0 +chirpmass-max = 35.0 +comp-min = 1 +comp-max = 1000 +distance-max = 1000 +aligned-spin = +alignedspin-zprior = + +[rift-pseudo-pipe] +# Keep this section minimal so the CLI args (--approx IMRPhenomD, +# --assume-nospin, --ile-sampler-method AV, --add-extrinsic, etc.) win. +internal-ile-request-disk="4M" +cip-fit-method="rf" +ile-n-eff=10 +l-max=2 +internal-distance-max=1000 +ile-runtime-max-minutes=60 +ile-jobs-per-worker=30 +internal-propose-converge-last-stage=True +force-eta-range="[0.20,0.24999]" +fmin-template=20 +event-time=1126259462.391 +n-output-samples=5000 +use-online-psd=False diff --git a/MonteCarloMarginalizeCode/Code/demo/rift/add_distance_grids/Makefile b/MonteCarloMarginalizeCode/Code/demo/rift/add_distance_grids/Makefile new file mode 100644 index 000000000..a19e9cf2e --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/demo/rift/add_distance_grids/Makefile @@ -0,0 +1,73 @@ +RIFT_CODE_ROOT := $(abspath ../../..) +REPO_ROOT := $(abspath ../../../../..) +CI_DEMO := $(REPO_ROOT)/.travis/ILE-GPU-Paper/demos + +INI ?= $(CURDIR)/add_distance_grids.ini +COINC ?= $(REPO_ROOT)/.travis/ref_ini/coinc.xml +CACHE ?= $(CI_DEMO)/zero_noise.cache +PSD ?= $(CI_DEMO)/HLV-ILIGO_PSD.xml.gz +INITIAL_GRID ?= $(CI_DEMO)/overlap-grid.xml.gz +RUN_DIR ?= $(CURDIR)/rundir + +ENV = GW_SURROGATE='' PYTHONPATH=$(RIFT_CODE_ROOT):$${PYTHONPATH:-} PATH=$(RIFT_CODE_ROOT)/bin:$${PATH} + +.PHONY: help inputs dag submit clean validate-args + +help: + @echo "Targets:" + @echo " make inputs - verify CI fake-data inputs are available" + @echo " make dag - build the RIFT DAG/run directory with distance-grid export enabled" + @echo " make submit - submit the generated DAG with condor_submit_dag" + @echo " make validate-args - confirm generated ILE args contain the distance-grid flags" + @echo " make clean - remove the generated run directory" + +inputs: + @test -s "$(INI)" + @test -s "$(COINC)" + @test -s "$(CACHE)" + @test -s "$(PSD)" + @test -s "$(INITIAL_GRID)" + @echo "Using INI: $(INI)" + @echo "Using coinc: $(COINC)" + @echo "Using fake cache: $(CACHE)" + @echo "Using PSD: $(PSD)" + @echo "Using initial grid: $(INITIAL_GRID)" + +dag: inputs + rm -rf "$(RUN_DIR)" + mkdir -p "$(RUN_DIR)" + cp "$(INITIAL_GRID)" "$(RUN_DIR)/overlap-grid.xml.gz" + printf '%s\n' 'X --mc-range [23,35] --eta-range [0.20,0.24999] --parameter mc --parameter-implied eta --parameter-nofit delta_mc --fit-method gp --verbose --lnL-offset 120 --cap-points 12000 --n-output-samples 1000 --no-plots --n-eff 1000' > "$(RUN_DIR)/args_cip.txt" + printf '%s\n' 'X --always-succeed --method lame --parameter m1' > "$(RUN_DIR)/args_test.txt" + printf '%s\n' 'X --parameter m1 --parameter m2' > "$(RUN_DIR)/args_plot.txt" + printf '%s\n' 'X --n-chunk 10000 --time-marginalization --sim-xml overlap-grid.xml.gz --reference-freq 100.0 --adapt-weight-exponent 0.1 --event-time 1000000014.236547946 --save-P 0.1 --cache-file $(CACHE) --fmin-template 10 --n-max 50000 --fmax 1700.0 --save-deltalnL inf --l-max 2 --n-eff 50 --approximant SEOBNRv4 --adapt-floor-level 0.1 --force-xpy --d-max 1000 --psd-file H1=$(PSD) --psd-file L1=$(PSD) --channel-name H1=FAKE-STRAIN --channel-name L1=FAKE-STRAIN --inclination-cosine-sampler --declination-cosine-sampler --data-start-time 1000000008 --data-end-time 1000000016 --inv-spec-trunc-time 0 --no-adapt-after-first --no-adapt-distance --srate 4096 --sampler-method GMM --internal-use-lnL ' > "$(RUN_DIR)/args_ile.txt" + cd "$(RUN_DIR)" && create_event_parameter_pipeline_BasicIteration \ + --ile-n-events-to-analyze 20 \ + --input-grid "$(INITIAL_GRID)" \ + --ile-exe "$(RIFT_CODE_ROOT)/bin/integrate_likelihood_extrinsic_batchmode" \ + --ile-args args_ile.txt \ + --last-iteration-export-marginal-distance-grid \ + --cip-args args_cip.txt \ + --test-args args_test.txt \ + --plot-args args_plot.txt \ + --request-memory-CIP 4096 \ + --request-memory-ILE 4096 \ + --n-samples-per-job 20 \ + --working-directory "$(RUN_DIR)" \ + --n-iterations 1 \ + --ile-retries 1 \ + --general-retries 1 + $(MAKE) validate-args + +validate-args: + @test -s "$(RUN_DIR)/args_ile.txt" + @grep -q -- "--export-marginal-distance-grid" "$(RUN_DIR)/args_ile.txt" + @grep -q -- "--internal-use-lnL" "$(RUN_DIR)/args_ile.txt" + @! grep -q -- "--distance-marginalization" "$(RUN_DIR)/args_ile.txt" + @echo "Distance-grid ILE args are present in $(RUN_DIR)/args_ile.txt" + +submit: validate-args + cd "$(RUN_DIR)" && condor_submit_dag marginalize_intrinsic_parameters_BasicIterationWorkflow.dag + +clean: + rm -rf "$(RUN_DIR)" diff --git a/MonteCarloMarginalizeCode/Code/demo/rift/add_distance_grids/PLAN_B_DESIGN.md b/MonteCarloMarginalizeCode/Code/demo/rift/add_distance_grids/PLAN_B_DESIGN.md new file mode 100644 index 000000000..76aea3ba4 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/demo/rift/add_distance_grids/PLAN_B_DESIGN.md @@ -0,0 +1,331 @@ +# Plan B: distance-as-parameter ILE export + +## Goal + +After a normal RIFT extrinsic run, for each intrinsic point produce a usable +estimate of + + L_pure(d) = integral L(d, Omega) pi_Omega(Omega) dOmega + +as a function of luminosity distance, populated densely enough that downstream +CIP can fit (intrinsic, d) jointly. Plan A (density-histogram export, the +existing `.dgrid` pathway) reconstructs the marginal but not the curve at +the n_eff RIFT typically runs at; Plan B instead does K independent +fixed-distance integrals per intrinsic point so each slice is its own +honest extrinsic-marginalized lnL. + +Deliverable target: <~10x the size of `.composite` files. K = 10 slices +per intrinsic point and ~20 columns per row hits that budget. + +## Hybrid core+wings architecture + +A single ILE job emits two kinds of slice rows in one `.dslice` file, +distinguished by the `method` column: + +* **Core (method = 0, reweight)** at the heart of the posterior, where + reweighting the main run's Omega samples is cheap and accurate. +* **Wings (method = 1, fresh)** in the low-probability tails, where + reweighting fails because the main Omega samples don't cover the + optimal Omega at far-from-peak distances. Each wing is its own fresh + AdaptiveVolume integration over Omega with distance pinned. + +### Core: B2-reweight + +After the main `sampler.integrate(...)` call: + +1. Choose `K_core` slice centers from equi-probable quantiles of the + posterior in d (uniform-in-log-d fallback for degenerate posteriors). +2. For each `d_k`, re-evaluate the existing `like_to_integrate` at + `(Omega_i, d_k)` for every sample i, reusing the already-precomputed + `rholms_intp` / `cross_terms`. Cost: `K_core * N` likelihood + evaluations on cached data; no waveform regeneration, no PSD reload. +3. Importance reweight: + + L(d_k) ~= (1/N) sum_i L(d_k, Omega_i) * pi_Omega(Omega_i) / q_Omega(Omega_i) + + The Omega-only IW factor `pi_Omega/q_Omega` is extracted from the + stored joint prior/proposal ratio with the distance piece divided out. + +This works well inside the posterior: Omega samples there are good +importance samples at every nearby slice distance. Falls apart in the +tails -- which is exactly where the wings step in. + +### Wings: B2-fresh + +For each wing slice `d_k`: + +1. Construct a fresh `mcsamplerAdaptiveVolume.MCSampler` over only the + Omega parameters by cloning the main sampler's per-parameter + `(pdf, prior, llim, rlim)` config. No distance dimension. +2. Wrap `like_to_integrate` so distance is fixed to `d_k`; Omega values + are clipped inward by ~1e-12 of their range to dodge boundary + `arccos(1+eps) = NaN` failures. +3. Call `sampler.integrate_log(...)` with a modest budget + (`--distance-slice-wing-nmax`, default 20k; `--distance-slice-wing-neff`, + default 30). AV is the canonical choice here -- it gives a real + adapted proposal in the wings without relying on the main run's + Omega samples. + +Wing centers are placed by fitting the core `(lnL, 1/d)` points to a +parabola in `1/d` (the natural form of the marginalized lnL near peak) +and spanning each side from the core edge out to where the model drops +`--distance-slice-wing-delta-lnL` nats below peak (default 7, i.e. +prior weight < 10^{-3} outside). This concentrates wing budget where +the likelihood actually has support. When the parabolic fit is +degenerate (fewer than 3 core points, no lnL variation, or a +non-downward fit) it falls back to log-uniform placement across the +full `[d_min, d_core_lo]` and `[d_core_hi, d_max]` spans. + +### Skip non-informative events + +`--distance-slice-skip-threshold` (default 1.0 nat) is an **absolute** +lnL cut: lnL is a likelihood ratio against the noise hypothesis, so if +the *peak* lnL across the core slices is below the threshold the event +is effectively undetected and wing integrations have nothing to learn +-- they are skipped and only the core rows are written. This is a +detectability cut, not a relative-spread test: a high-SNR event with a +flat distance profile (well-constrained inclination, unconstrained +distance) has a small spread but a large peak lnL and *does* get wings. +This guards the user's directive to "not waste time on noninformative +likelihoods". + +Code: +- `MonteCarloMarginalizeCode/Code/RIFT/misc/distance_slices.py`: + `importance_reweight_slices`, `fresh_sample_slices`, + `quantile_slice_centers`, `pick_wing_centers`, `is_uninformative`. +- `bin/integrate_likelihood_extrinsic_batchmode`: new flags + `--export-distance-slices K`, `--n-distance-slice-core`, + `--n-distance-slice-wing`, `--distance-slice-wing-nmax`, + `--distance-slice-wing-neff`, `--distance-slice-skip-threshold`, + `--distance-slice-wing-delta-lnL`. + +## Output format: `.dslice` + +One file per ILE job, K rows per intrinsic point. See +`DISTANCE_SLICE_FIELDS` in `distance_slices.py`. Key columns: + +| column | meaning | +| --- | --- | +| `lnL` | extrinsic-marginalized lnL at this `dist`, pure (no distance prior baked in) | +| `sigmaL` | per-slice MC standard error of lnL | +| `neff` | effective sample count contributing to this slice | +| `ntotal` | total samples consumed by the slice estimator | +| `method` | 0 = reweight, 1 = fresh | +| `dist` | slice center (Mpc) | +| `ln_prior_d_sampling` | log of the distance prior at `dist` under the ILE sampling prior | +| intrinsic columns | m1, m2, s1x..s2z, lambda1, lambda2, eccentricity, meanPerAno, eos_index | + +Re-marginalization: +```python +from RIFT.misc.distance_slices import load_distance_slice_table, reconstruct_marginal_lnL +table = load_distance_slice_table("CME_0_.dslice") +# Reproduce ILE's reported log_res (using stored sampling prior): +reconstruct_marginal_lnL(table) +# Or re-marginalize against any other prior: +reconstruct_marginal_lnL(table, ln_prior_d=lambda d: 2*np.log(d) - C) +``` + +## Workflow integration (non-destructive) + +The user's preferred path is to add a follow-on stage after a normal RIFT +run, **without** making `create_event_parameter_pipeline_BasicIteration` +("CEPP_basic") more baroque. Recommendation: + +### Recommended (no DAG changes) -- step 1 DONE + +1. **DONE.** Enable `--export-distance-slices K` on the **last iteration + only** of an otherwise-normal RIFT run. `util_RIFT_pseudo_pipe.py` now + exposes `--export-distance-slices K` (plus + `--export-distance-slices-{n-core,n-wing,wing-delta-lnL,skip-threshold}`), + sibling to `--export-marginal-distance-grid`. When set it forces ILE + lnL mode (whole run) and routes + `--last-iteration-export-distance-slices K ...` to the pipeline builder + (`create_event_parameter_pipeline_{Basic,Alternate,BasicMultiApprox}Iteration`), + which appends the ILE-level export flags to the **extrinsic** stage + (`ILE_extr.sub`) only. Distance marginalization is **not** disabled + globally -- the intrinsic iterations keep it (a speedup); the pipeline + builder strips `--distance-marginalization` from the extrinsic stage only. + Requires `--add-extrinsic`. + + While landing this we also fixed a latent bug: the Plan-A grid flag had + been appended to `args_ile.txt` (the ILE argument string) instead of the + CEPP command, so it would have been handed to the ILE executable and + rejected; both grid and slice flags now go to the CEPP command. + + End-to-end coverage: `demo/pipeline/` (Makefile + README) builds + baseline/grid/slices pipelines and asserts the flags land in + `ILE_extr.sub` (not the intrinsic `ILE.sub`) with no distance + marginalization; `.travis/test-build.sh` runs the same checks in CI. + +2. **DONE.** Add a consolidation step that concatenates `.dslice` (and + `.dgrid`) files into a single table per iteration. Landed as + `util_ConsolidateDistanceGrids.py` (header-checked concatenator with a + `--input-glob` mode) plus + `RIFT.misc.dag_utils_generic.write_consolidate_distance_grids_sub`. + When `--last-iteration-export-marginal-distance-grid` / + `--last-iteration-export-distance-slices` is set, CEPP_basic emits + `consolidate_dgrid.sub` / `consolidate_dslice.sub` and wires them as + children of every `ILE_extr` job in the DAG. Output lands at the run + root as `all_dgrid.dat` / `all_dslice.dat` -- the "net" intrinsic + + distance grid downstream tools consume. End-to-end validated by + `demo/pipeline/zero_spin_phenomD/` (zero-spin IMRPhenomD, AV sampler, + small mass grid, ~45 s on a laptop): builds the pipeline via + `util_RIFT_pseudo_pipe.py`, runs `ILE_extr` directly (no condor) on a + few grid rows, consolidates, and feeds `all_dgrid.dat` into + `util_ConstructEOSPosterior.py` to reconstruct the joint + (m1, m2, dist) posterior. + +3. (Optional, deferred) Teach CIP to ingest the `.dslice` table jointly: + either fit `lnL(intrinsic, dist)` directly, or marginalize over `dist` + per intrinsic point with a configurable prior and feed the marginal + into the existing intrinsic-only fitter. No change to RIFT structure + needed for the export deliverable -- the `.dslice` file *is* the + deliverable. + +### Why not "another CEPP_basic" call + +A second CEPP_basic invocation with an expanded sim_inspiral table +(K * N_intrinsic events, each pinned via `--pin-distance-to-sim`) was +considered. Costs: +- K x more ILE workers spun up (K x worker startup, PSD load, frame + read, waveform setup). RIFT already pays a fixed worker cost + dominated by setup at low n_eff; a Kx blowup is wasteful. +- Doubles the bookkeeping (two CEPP_basic invocations, two DAG roots, + two output trees to keep aligned with intrinsic ids). +- Requires generating the expanded sim_inspiral, which is itself + brittle (`xml_to_ChooseWaveformParams_array` is the very code path + that issue #136 has been biting). + +The B2-reweight pathway in-process pays only the K extra likelihood +evaluations per existing ILE job -- the expensive setup is reused. +Estimated cost: ~10% on top of a normal ILE job at K=10 (the main +integration uses ~50,000 likelihood evals; K=10 slice integrations on +the same sample set are ~10 * N_samples = ~5000 evaluations of the +*already cached* factored likelihood, dwarfed by the original +integration cost). + +### Lower-level pipeline extension (only if needed) + +If a `.dslice`-only iteration is needed (run the slice pass but skip a +fresh extrinsic integration -- say, because a prior run has good +adapted GMMs that we want to reuse), then we extend the low-level +pipeline with one new job class. The least baroque approach: + +- A new helper `util_RIFT_distance_slice_pass.py` that: + 1. Reads an existing `.composite` (intrinsic + ILE state) + 2. Builds a small sub-DAG of ILE jobs, each re-running on one + intrinsic point with `--export-distance-slices K --n-max `. + (Or, if we keep adapted-state pickles per intrinsic from the + parent run, restore those and skip the main integration.) + 3. Has its own unify step to concatenate the resulting `.dslice` files. +- This is a peer of `util_RIFT_pseudo_pipe.py` and shares its subdag + machinery, not embedded into CEPP_basic. + +Land that only if the recommended path can't carry the workflow. + +## Sampler choice: use AV (or any sampler with high main n_eff) + +B2-reweight relies on the existing Omega samples being a good importance +sample at every slice distance. The synthetic stress-test +(`validate_distance_slices.py`) confirms this works to <0.1 nat even with +strong d-Omega coupling, **provided the main run's n_eff is well above +~50**. + +Empirical findings on the fake-data demo (single ILE call, +`--n-max 50000`, n_eff target 100): + +| sampler | main n_eff | B2 reconstruct vs log_res | per-slice n_eff | +| --- | --- | --- | --- | +| GMM (`--sampler-method GMM`) | 1-2 | +2.7 nat bias | 16-25 (looks fine but isn't) | +| AV (`--sampler-method AV`) | 2.6-6.5 | within `sigmaL` (-0.3 to -1.0 nat) | 7-28 | + +GMM at default settings on this event ran out at n_eff=2 and B2-reweight +returned biased slice integrals without warning -- the per-slice n_eff +looked healthy because the same handful of high-weight samples dominate +every slice. **AV is the recommended default for runs that enable +distance slices.** GMM still works at high main n_eff (the synthetic +test confirms it); the issue is that GMM is unreliable at the n_eff +ranges RIFT routinely terminates at. + +## Validation strategy + +For a single ILE call (already wired into the demo): + +1. Run with `--export-distance-slices K --export-marginal-distance-grid` + so both Plan A and Plan B outputs exist side by side. +2. Check `reconstruct_marginal_lnL(slice_table)` agrees with `log_res` + from the `.dat` row within `sigmaL`. If yes, the slice + re-marginalization is unbiased. +3. Plot `lnL` from `.dslice` (K honest fixed-d integrals) vs + `lnL` reconstructed from `.dgrid` (the density histogram). The + slice version should be smoother and tighter at the same K. +4. If the main run's n_eff is below ~50 with GMM, fall back to AV (or + raise n-max) before trusting B2 output. + +`validate_distance_slices.py` provides a synthetic stress-test with a +known closed-form answer and an adjustable d-Omega coupling, so we can +keep the math honest as the prototype evolves. + +## Breadcrumbs for the next session + +These are deliberate next changes, not unknowns. Each one has a clear +spec; pick up here when work on `.dslice` resumes. + +### 1. Skip threshold should be an absolute lnL scale -- DONE + +**Status**: landed. `--distance-slice-skip-threshold` is now an +absolute cut. `is_uninformative(lnL_core, threshold)` returns True iff +the *peak* core lnL is below the threshold (default 1.0 nat); the old +`max - min` spread test is gone. Help text and the ILE skip message +("peak core lnL < ... (effectively undetected)") were updated to match. + +This skips undetected low-SNR events (low peak, flat profile) while +correctly *keeping* high-SNR events with a flat distance profile +(small spread but large peak lnL) -- exactly the case the relative +test got wrong. + +### 2. Wing-center placement via lnL ~ parabola in 1/dist -- DONE + +**Status**: landed. `pick_wing_centers` now accepts +`(d_min, d_max, d_core, n_wing, lnL_core=None, lnL_peak=None, +delta_lnL_target=7.0)`. When `lnL_core` is supplied it fits the core +`(lnL, 1/d)` points to a parabola in `1/d` + + lnL(d) ~= lnL_peak - 0.5 * A^2 * (1/d - 1/d_peak)^2 + +(`fit_lnL_parabola_in_inv_d`), solves for the two `1/d` where lnL drops +`delta_lnL_target` nats below peak (`_parabolic_wing_bounds`), and +spaces wings log-uniformly between the core edge and that boundary on +each side. The ILE binary passes `lnL_core`, the observed peak, and +`--distance-slice-wing-delta-lnL` (default 7.0). + +Robustness implemented: + +* Boundaries are clamped to `[d_min, d_max]` (the sampler's distance + support), honoring the distance-inclination-ridge caveat: the fresh + integration will honestly report low neff on any wing that catches an + unanticipated ridge. +* When the fit is degenerate (fewer than 3 finite core points, no lnL + variation, non-downward fit) or leaves no room outside the core, it + falls back to the original log-uniform full-range placement + (`_log_uniform_wings`). +* If the requested target lnL sits above the fitted vertex (observed + peak exceeds the fit), it uses the vertex-symmetric half-width + `sqrt(-delta/a)`, which always yields real roots for a downward + parabola. + +**Verified**: synthetic parabola recovers `A^2` exactly and places +wings inside the solved `[d_small, d_large]` bounds rather than spread +across the full prior range; degenerate inputs fall back cleanly. + +**Side benefit still open (deferred)**: `fit_lnL_parabola_in_inv_d` +exposes `A^2 = -2a` (the effective Fisher in `1/d`). Recording it in +`.dslice` metadata for downstream CIP would require a schema/header +addition to `DISTANCE_SLICE_FIELDS`; not done yet since it touches the +load/save/reconstruct path. + +## Older limitations (not high priority) + +* **GMM at low main n_eff** silently biases the reweight estimator. + The runtime warning is in place; the long-term fix is to default + RIFT to AV for any run that turns on `--export-distance-slices`. diff --git a/MonteCarloMarginalizeCode/Code/demo/rift/add_distance_grids/README.md b/MonteCarloMarginalizeCode/Code/demo/rift/add_distance_grids/README.md new file mode 100644 index 000000000..06bd4f008 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/demo/rift/add_distance_grids/README.md @@ -0,0 +1,54 @@ +# RIFT Distance-Grid Export Demo + +This demo builds a small zero-spin RIFT workflow with +`--export-marginal-distance-grid` enabled for ILE jobs. It reuses the fake +zero-noise frames, PSD, cache, and initial target grid from the CI assets in +`../../../../../.travis/ILE-GPU-Paper/demos`. + +`add_distance_grids.ini` records the corresponding zero-spin/fake-data +configuration. The Makefile builds the runnable DAG through the same +`create_event_parameter_pipeline_BasicIteration` path used by the CI demo, +because the higher-level pseudo-pipe ini path currently trips over an XML +compatibility issue when rereading its temporary target file in this +Python/lalsuite environment. + +Run: + +```bash +make dag +``` + +This creates `rundir/`, writes the normal RIFT DAG files, and verifies that +`rundir/args_ile.txt` contains: + +- `--export-marginal-distance-grid` +- `--internal-use-lnL` + +The demo intentionally does not submit automatically. To queue the generated +workflow: + +```bash +make submit +``` + +## Environment Note + +If the run prints messages like: + +```text +swig/python detected a memory leak of type 'struct tagLIGOTimeGPS *', no destructor found. +``` + +that is an environment compatibility warning from the LALSuite Python bindings, +not a distance-grid failure. It has been observed with the local `my_rift` +environment (`python 3.12.4`, `lal 7.7.0`, `lalsimulation 6.2.0`, +`lalmetaio 4.0.6`, `lalsuite 7.26`). Prefer a known-good RIFT/LALSuite +environment whose LAL packages were built with pre-SWIG-4.4 bindings. Pinning +`swig<4.4` only helps when rebuilding the LAL packages; installing an older SWIG +binary next to already-built LAL Python wheels/conda packages will not change +the generated wrapper code. + +After ILE jobs complete, each evaluated intrinsic point should have a companion +`*.dgrid` file. The grid is a likelihood density in luminosity distance; use +`RIFT.misc.distance_grid.reconstruct_marginal_lnL()` to check that integrating +the distance grid reconstructs the ordinary marginalized likelihood. diff --git a/MonteCarloMarginalizeCode/Code/demo/rift/add_distance_grids/add_distance_grids.ini b/MonteCarloMarginalizeCode/Code/demo/rift/add_distance_grids/add_distance_grids.ini new file mode 100644 index 000000000..0f85ce167 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/demo/rift/add_distance_grids/add_distance_grids.ini @@ -0,0 +1,83 @@ +[analysis] +ifos=['H1','L1'] +singularity=False +osg=False + +[paths] + +[input] +max-psd-length=10000 + +[condor] +accounting_group=ligo.sim.o4.cbc.pe.rift +accounting_group_user=oshaughn + +[datafind] +url-type=file +types = {'H1': 'FAKE', 'L1': 'FAKE'} + +[data] +channels = {'H1': 'H1:FAKE-STRAIN', 'L1': 'L1:FAKE-STRAIN'} + +[lalinference] +flow = {'H1': 20, 'L1': 20} +fhigh = {'H1': 1700, 'L1': 1700} + +[engine] +fref=100 +approx=SEOBNRv4 +amporder = -1 +seglen = 8 +srate = 4096 + +chirpmass-min = 23 +chirpmass-max = 35 +comp-min = 1 +comp-max = 1000 +distance-max = 1000 + +[rift-pseudo-pipe] +approx="SEOBNRv4" +assume-nospin=True +assume-nonprecessing=False +assume-precessing=False + +l-max=2 +fmin-template=10 +event-time=1000000014.236547946 +manual-postfix="_distance_grids" + +force-mc-range="[23,35]" +force-eta-range="[0.20,0.24999]" +force-initial-grid-size=100 +internal-force-iterations=2 +internal-n-iterations-subdag-max=2 +internal-truncate-cip-arg-list=1 + +ile-n-eff=50 +ile-jobs-per-worker=20 +ile-jobs-per-worker-first=20 +ile-copies=1 +ile-no-gpu=True +ile-sampler-method="GMM" +internal-ile-use-lnL=True +internal-ile-reset-adapt=True +internal-ile-inv-spec-trunc-time=0 +manual-extra-ile-args=" --data-start-time 1000000008 --data-end-time 1000000016 --no-adapt-after-first --no-adapt-distance --srate 4096 " + +export-marginal-distance-grid=True + +cip-fit-method="gp" +cip-sampler-method="GMM" +cip-explode-jobs=1 +cip-explode-jobs-last=1 +internal-cip-request-memory=4096 +n-output-samples=1000 +n-output-samples-last=2000 + +use_osg=False +use_osg_file_transfer=False +use_osg_cip=False +ile-retries=1 +general-retries=1 +skip-reproducibility=True diff --git a/MonteCarloMarginalizeCode/Code/demo/rift/add_distance_grids/validate_distance_grid.py b/MonteCarloMarginalizeCode/Code/demo/rift/add_distance_grids/validate_distance_grid.py new file mode 100644 index 000000000..9169996f5 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/demo/rift/add_distance_grids/validate_distance_grid.py @@ -0,0 +1,114 @@ +"""Stress-test the distance-grid export at realistic n_eff regimes. + +Goals +----- +1. Verify round-trip identity: reconstruct_marginal_lnL(grid) == lnL_marginal, + to machine precision. +2. Verify the *pure* likelihood interpretation: re-integrating against an + alternative distance prior yields the closed-form answer. +3. Quantify how badly the per-bin shape degrades as n_eff drops, since RIFT + routinely runs ILE with low n_eff (50-200) and the user is worried Plan A + may fail catastrophically. + +The synthetic problem mimics ILE's actual setup: + d ~ q(d) (uniform-in-d sampling proposal) + pi_d(d) = 3 d^2 / (d_max^3 - d_min^3) (volumetric prior) + L(d, Omega) = peak * exp(-0.5 ((d - d0)/sigma_d)^2) (no Omega dependence) +The marginal is exp(lnL) integrated against pi_d. +""" +import numpy as np + +from RIFT.misc.distance_grid import ( + build_distance_grid, + reconstruct_marginal_lnL, + _logsumexp, +) + + +def vol_log_prior(d, d_min=1.0, d_max=4000.0): + norm = (d_max**3 - d_min**3) / 3.0 + return 2.0*np.log(d) - np.log(norm) + + +def closed_form_marg(d0, sigma, lnL_peak, d_min, d_max): + """Marginal under volumetric prior, in the wide-support limit.""" + # E[d^2] ~ d0^2 + sigma^2 (for d well inside box) + norm = (d_max**3 - d_min**3) / 3.0 + return lnL_peak + np.log(np.sqrt(2*np.pi)*sigma * (d0**2 + sigma**2)) - np.log(norm) + + +def closed_form_flat(d0, sigma, lnL_peak, d_min, d_max): + """Marginal under flat-in-d prior.""" + return lnL_peak + np.log(np.sqrt(2*np.pi)*sigma / (d_max - d_min)) + + +def synth_trial(n_samp, n_grid, d0, sigma, lnL_peak, d_min, d_max, rng): + distance = rng.uniform(d_min, d_max, size=n_samp) + ln_L = lnL_peak - 0.5*((distance-d0)/sigma)**2 + ln_pi = vol_log_prior(distance, d_min, d_max) + ln_q = -np.log(d_max - d_min) + ln_w = ln_L + ln_pi - ln_q + lnL_marg_mc = _logsumexp(ln_w) - np.log(n_samp) + grid = build_distance_grid(distance, ln_w, lnL_marg_mc, 0.0, {}, + ln_prior_d_at_samples=ln_pi, n_grid=n_grid) + return distance, ln_w, lnL_marg_mc, grid + + +def neff_from_ln_weights(ln_w): + p = np.exp(ln_w - _logsumexp(ln_w)) + return 1.0 / np.sum(p**2) + + +def density_L2_error(grid, d0, sigma, lnL_peak): + """L2 relative error of exp(grid['lnL']) vs the true pure likelihood + L(d) at the bin centers (note: this is exp lnL on its own, not the + posterior density in d).""" + d_g = grid["dist"] + truth = np.exp(lnL_peak - 0.5*((d_g - d0)/sigma)**2) + recov = np.exp(grid["lnL"]) + denom = np.sqrt(np.mean(truth**2)) + return np.sqrt(np.mean((truth - recov)**2)) / max(denom, 1e-300) + + +def main(): + rng = np.random.default_rng(20260528) + d_min, d_max = 1.0, 4000.0 + d0, sigma, lnL_peak = 400.0, 80.0, 37.0 + truth_vol = closed_form_marg(d0, sigma, lnL_peak, d_min, d_max) + truth_flat = closed_form_flat(d0, sigma, lnL_peak, d_min, d_max) + + print(f"closed-form marg (volumetric): {truth_vol:.3f}") + print(f"closed-form marg (flat in d): {truth_flat:.3f}") + print() + print(f"{'N':>6} {'n_grid':>7} {'n_eff':>7} " + f"{'lnL_mc':>10} {'lnL_reco_vol':>13} {'lnL_reco_flat':>14} " + f"{'reco-mc':>9} {'flat-truth':>11} {'L2 shape':>10}") + for N in (50, 100, 200, 500, 2000, 10000, 50000): + n_grid = max(8, min(N//2, 200)) + errs, mc_errs, flat_errs, neffs = [], [], [], [] + for trial in range(50): + _, ln_w, lnL_mc, grid = synth_trial(N, n_grid, d0, sigma, lnL_peak, + d_min, d_max, rng) + lnL_reco_vol = reconstruct_marginal_lnL(grid) + flat = lambda d: -np.log(d_max-d_min) * np.ones_like(np.asarray(d, float)) + lnL_reco_flat = reconstruct_marginal_lnL(grid, ln_prior_d=flat) + errs.append(density_L2_error(grid, d0, sigma, lnL_peak)) + mc_errs.append(lnL_mc - truth_vol) + flat_errs.append(lnL_reco_flat - truth_flat) + neffs.append(neff_from_ln_weights(ln_w)) + last = (lnL_mc, lnL_reco_vol, lnL_reco_flat) + print(f"{N:6d} {n_grid:7d} {np.median(neffs):7.1f} " + f"{last[0]:10.4f} {last[1]:13.4f} {last[2]:14.4f} " + f"{last[1]-last[0]:+9.2e} {np.median(flat_errs):+11.3f} " + f"{np.median(errs):10.3f}") + + print() + print("Round-trip identity (lnL_reco_vol - lnL_mc, should be ~machine eps):") + for n_grid in (4, 8, 32, 200): + _, _, lnL_mc, grid = synth_trial(500, n_grid, d0, sigma, lnL_peak, + d_min, d_max, rng) + print(f" n_grid={n_grid:3d}: {reconstruct_marginal_lnL(grid) - lnL_mc:+.3e}") + + +if __name__ == "__main__": + main() diff --git a/MonteCarloMarginalizeCode/Code/demo/rift/add_distance_grids/validate_distance_slices.py b/MonteCarloMarginalizeCode/Code/demo/rift/add_distance_grids/validate_distance_slices.py new file mode 100644 index 000000000..0cd0f2412 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/demo/rift/add_distance_grids/validate_distance_slices.py @@ -0,0 +1,150 @@ +"""Synthetic stress-test for the B2 distance-slice estimator. + +We synthesize an ILE-like Monte Carlo with two parameters: distance d and a +single Omega-proxy x. Sample (d, x) from a known proposal q_joint(d, x) = +q_d(d) q_x(x); evaluate the joint likelihood L(d, x) = exp(-0.5*((d - d0(x))/ +sigma_d)^2 + lnL_peak); record per-sample integrand and the joint prior / +proposal arrays exactly as ILE's mcsamplerEnsemble does. Then run the +importance-reweight slice estimator and compare to the closed-form + + L_pure(d_target) = integral L(d_target, x) pi_x(x) dx + +For a Gaussian in d with mean d0(x) = d0_const + alpha * x and sigma_d +small, increasing alpha makes Omega couple more strongly to d -- that's the +regime where reweighting is expected to break, and where the bias should +appear. +""" +import numpy as np + +# Stand-in for sampler._rvs we'll mock up +class MockSampler: + pass + + +def _logsumexp(x): + m = np.max(x) + return m + np.log(np.sum(np.exp(x - m))) if np.isfinite(m) else m + + +def synth_run(N, alpha, sigma_d=80.0, d0_const=400.0, lnL_peak=30.0, + d_min=1.0, d_max=4000.0, x_min=-2.0, x_max=2.0, rng=None): + rng = rng or np.random.default_rng() + # Proposals: q_d uniform on [d_min,d_max], q_x uniform on [x_min,x_max] + d = rng.uniform(d_min, d_max, size=N) + x = rng.uniform(x_min, x_max, size=N) + q_d = 1.0/(d_max - d_min) * np.ones(N) + q_x = 1.0/(x_max - x_min) * np.ones(N) + # Priors: pi_d volumetric (d^2/(d_max^3/3)), pi_x uniform on [-2,2] + norm_d = (d_max**3 - d_min**3)/3.0 + pi_d = d**2 / norm_d + pi_x = 1.0/(x_max - x_min) * np.ones(N) + # Likelihood (no prior): peaks at d=d0(x) for each x + d0 = d0_const + alpha*x + lnL_at_sample = lnL_peak - 0.5 * ((d - d0)/sigma_d)**2 + integrand = np.exp(lnL_at_sample) + joint_prior = pi_d * pi_x + joint_s_prior = q_d * q_x + # Standard MC estimator of marg likelihood: + w_full = integrand * joint_prior / joint_s_prior + lnL_marg_mc = np.log(np.mean(w_full)) + # Closed-form marg (over both d and x): + # integral L(d,x) pi_d pi_x dd dx = (1/norm_d)(1/range_x) integral d^2 exp(-0.5((d-d0(x))/sigma_d)^2) dx dd + # for sigma_d much less than range, gaussian in d concentrates near d0(x); + # integrate d^2 against gaussian at d0(x) ~ (d0(x)^2 + sigma_d^2)*sqrt(2pi)*sigma_d. + # Then average over x uniform on [-2,2]: E[(d0_const + alpha*x)^2 + sigma_d^2] = d0_const^2 + alpha^2*var_x + sigma_d^2 + var_x_uniform = (x_max-x_min)**2 / 12.0 + E_d2 = d0_const**2 + alpha**2 * var_x_uniform + sigma_d**2 + marg_truth = np.exp(lnL_peak) * np.sqrt(2*np.pi) * sigma_d * E_d2 / norm_d + lnL_marg_truth = np.log(marg_truth) + + # For B2-slice we need a "like_to_integrate"-compatible function that + # takes (x, distance) arrays and returns lnL (mocking return_lnL=True) + def like_to_integrate(x, distance): + d0_arr = d0_const + alpha * np.asarray(x) + return lnL_peak - 0.5*((np.asarray(distance) - d0_arr)/sigma_d)**2 + + rvs = dict(distance=d, x=x, integrand=integrand, + joint_prior=joint_prior, joint_s_prior=joint_s_prior) + sampler = MockSampler() + sampler._rvs = rvs + sampler.prior_pdf = {"distance": lambda dd: dd**2 / norm_d} + sampler.pdf = {"distance": lambda dd: np.ones_like(dd) / (d_max - d_min)} + sampler._pdf_norm = {"distance": 1.0} + return sampler, like_to_integrate, lnL_marg_mc, lnL_marg_truth, dict( + d_min=d_min, d_max=d_max, sigma_d=sigma_d, d0_const=d0_const, + alpha=alpha, lnL_peak=lnL_peak, + ) + + +def test_wing_placement_and_skip(): + """Unit-check the absolute skip cut and the parabolic wing placement.""" + from RIFT.misc import distance_slices as ds + print("\n-- is_uninformative (absolute peak cut) --") + assert ds.is_uninformative(np.array([0.1, 0.3, 0.4, 0.2])) # undetected + assert not ds.is_uninformative(np.array([49.8, 50.0, 49.9, 49.85])) # hi-SNR flat + assert ds.is_uninformative(np.array([np.nan, np.nan])) # all nan + print(" ok: undetected skipped, high-SNR-flat kept, nan skipped") + + print("-- parabolic wing placement --") + A2, dpeak, peak = 8.0e6, 400.0, 30.0 + d_core = np.array([300., 350., 400., 450., 500.]) + lnL_core = peak - 0.5 * A2 * (1.0/d_core - 1.0/dpeak)**2 + a, b, c = ds.fit_lnL_parabola_in_inv_d(d_core, lnL_core) + assert abs(-2*a - A2) / A2 < 1e-6, "A^2 mis-recovered" + d_min, d_max = 1.0, 4000.0 + w = ds.pick_wing_centers(d_min, d_max, d_core, 6, lnL_core=lnL_core, + lnL_peak=peak, delta_lnL_target=7.0) + hw = np.sqrt(14.0 / A2) + d_small, d_large = 1.0/(1.0/dpeak + hw), 1.0/(1.0/dpeak - hw) + assert w.min() >= d_small - 1e-6 and w.max() <= d_large + 1e-6, \ + "wings escaped parabolic bounds" + assert np.all((w < d_core.min()) | (w > d_core.max())), "wing inside core" + print(" ok: A^2 recovered, wings within [{:.1f},{:.1f}] outside core".format( + d_small, d_large)) + + # degenerate -> log-uniform fallback spans the full prior range + w_fb = ds.pick_wing_centers(d_min, d_max, d_core, 6) + assert w_fb.min() < d_small and w_fb.max() > d_large, "fallback not full-range" + print(" ok: degenerate input falls back to full-range log-uniform") + + +def main(): + from RIFT.misc import distance_slices + test_wing_placement_and_skip() + rng = np.random.default_rng(20260528) + print("Mock 1-Omega problem: L(d, x) = peak exp(-0.5*((d - d0(x))/sigma)^2)") + print("'alpha' is the d-Omega coupling. alpha=0: separable. alpha=80: peak shifts ~1 sigma per unit x.") + print(f"\n{'alpha':>7} {'N':>6} {'lnL_truth':>10} {'lnL_mc':>10} " + f"{'B2_marg':>10} {'diff':>8} {'med slice n_eff':>16}") + for alpha in (0.0, 10.0, 40.0, 80.0, 160.0): + for N in (2000, 20000): + sampler, like, lnL_mc, lnL_truth, meta = synth_run(N, alpha, rng=rng) + # Run B2 slice + dL_samp = sampler._rvs["distance"] + # ln_w_full + rvs = sampler._rvs + keep = (rvs['integrand'] > 0) + ln_w_full = np.full(N, -np.inf) + ln_w_full[keep] = np.log(rvs['integrand'][keep]) + np.log(rvs['joint_prior'][keep]) - np.log(rvs['joint_s_prior'][keep]) + d_slices = distance_slices.quantile_slice_centers(dL_samp, ln_w_full, 20) + ln_pi_d_samp = np.log(sampler.prior_pdf['distance'](dL_samp)) + ln_q_d_samp = np.log(sampler.pdf['distance'](dL_samp)) + lnL_k, sigmaL_k, neff_k, _ = distance_slices.importance_reweight_slices( + sampler, like, d_slices, ln_pi_d_samp, ln_q_d_samp, + manual_overflow=0.0, return_lnL=True, + ) + # Build slice table to reconstruct marginal + ln_pi_d_slices = np.log(sampler.prior_pdf['distance'](d_slices)) + t = distance_slices.build_distance_slice_table( + d_slices, lnL_k, sigmaL_k, neff_k, N, + distance_slices.METHOD_REWEIGHT, {}, ln_pi_d_slices, + ) + lnL_marg_b2 = distance_slices.reconstruct_marginal_lnL(t) + med_neff = np.median(neff_k) + diff = lnL_marg_b2 - lnL_truth + print(f"{alpha:7.1f} {N:6d} {lnL_truth:10.4f} {lnL_mc:10.4f} " + f"{lnL_marg_b2:10.4f} {diff:+8.3f} {med_neff:16.1f}") + + +if __name__ == "__main__": + main() diff --git a/MonteCarloMarginalizeCode/Code/demo/rift/calmarg/Makefile b/MonteCarloMarginalizeCode/Code/demo/rift/calmarg/Makefile new file mode 100644 index 000000000..16ebada0e --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/demo/rift/calmarg/Makefile @@ -0,0 +1,703 @@ +# In-loop calibration marginalization demo (3 detectors: H1, L1, V1) +# +# Uses the zero-spin synthetic CI data shipped in .travis/ILE-GPU-Paper/demos. +# Runs the ILE binary directly (no condor) with distance marginalization and +# in-loop calibration marginalization, comparing: +# - baseline : no calibration marginalization +# - loop : in-loop calmarg, Option B (cal_method='loop'; CPU or GPU) +# - fused : in-loop calmarg, Option C (fused CUDA kernel; GPU only) +# +# See README.md for the physics and what each target demonstrates. + +RIFT_CODE_ROOT := $(abspath ../../..) +REPO_ROOT := $(abspath ../../../../..) +CI_DEMO := $(REPO_ROOT)/.travis/ILE-GPU-Paper/demos + +CACHE ?= $(CI_DEMO)/zero_noise.cache +PSD ?= $(CI_DEMO)/HLV-ILIGO_PSD.xml.gz +# Analyze the INJECTION itself as the (single) template, so the signal is actually +# present (matched template -> lnL ~ rho^2/2). Using a far-off grid point instead +# gives lnL ~ 0 ("no signal"), which makes the calmarg effect invisible. +SIM_XML ?= $(CI_DEMO)/mdc.xml.gz + +ILE := $(RIFT_CODE_ROOT)/bin/integrate_likelihood_extrinsic_batchmode +ENV := PYTHONPATH=$(RIFT_CODE_ROOT):$${PYTHONPATH:-} PATH=$(RIFT_CODE_ROOT)/bin:$${PATH} + +# Tunables. NCHUNK/NMAX are kept small so the demo runs on a modest GPU; raise +# them (and NEFF) for a production-quality run on a larger card. +NCAL ?= 100 # calibration realizations; >=100 required, 300 recommended (watch '[calmarg error] ... cal n_eff' in the ILE log) +NCHUNK ?= 1000 # extrinsic samples per evaluation block +NMAX ?= 2000 # max extrinsic samples +NEFF ?= 100 # target effective samples +SEED ?= 1234 # fixes BOTH the cal draws and the extrinsic draws +DMAX ?= 1000 +SAMPLER ?= AV # adaptive-volume sampler: mature, stable GPU code path + # (GMM is newer/heavier on GPU; override with SAMPLER=GMM) + +# Review matrix toggles: +# BACKEND=gpu (CUDA kernels) | cpu (numpy; runs on a laptop, no CUDA) +# DMARG=1 (distance marginalization on, fused distmarg kernel) +# | 0 (off, default-helper fused kernel) +BACKEND ?= gpu +DMARG ?= 1 + +ifeq ($(BACKEND),cpu) + # --gpu --force-xpy selects the xpy code path; on a machine WITHOUT cupy (e.g. a + # Mac) that path is numpy, i.e. CPU. (On a machine with cupy it stays on the GPU + # xpy path -- there is no separate force-numpy switch -- so use BACKEND=cpu where + # cupy is absent. The deterministic 'make verify-exact BACKEND=cpu' always uses + # numpy regardless, since the backtest selects the backend directly.) + ACCEL := --vectorized --gpu --force-xpy +else + ACCEL := --vectorized --gpu +endif + +ifeq ($(DMARG),1) + DMARG_FLAGS := --distance-marginalization --distance-marginalization-lookup-table $(CURDIR)/distance_marg.npz + DMARG_DEP := distance_marg.npz + VERIFY_LL := distmarg --real-table $(CURDIR)/distance_marg.npz +else + DMARG_FLAGS := + DMARG_DEP := + VERIFY_LL := default +endif + +# PHASE=1 adds analytic phase marginalization to the deterministic check (verify-exact). +PHASE ?= 0 +ifeq ($(PHASE),1) + VERIFY_PHASE := --phase-marginalization +else + VERIFY_PHASE := +endif + +# Detector / data configuration for the synthetic injection +EVENT_TIME := 1000000014.236547946 +IFO_PSD := --psd-file H1=$(PSD) --psd-file L1=$(PSD) --psd-file V1=$(PSD) +IFO_CHAN := --channel-name H1=FAKE-STRAIN --channel-name L1=FAKE-STRAIN --channel-name V1=FAKE-STRAIN + +# Common ILE arguments (3 detectors) +COMMON := $(ACCEL) \ + --n-chunk $(NCHUNK) --n-max $(NMAX) --n-eff $(NEFF) --seed $(SEED) \ + --time-marginalization --sim-xml $(SIM_XML) --n-events-to-analyze 1 \ + --reference-freq 100.0 --event-time $(EVENT_TIME) --approximant SEOBNRv4 --l-max 2 \ + --cache-file $(CACHE) --fmin-template 10 --fmax 1700.0 \ + --d-min 1 --d-max $(DMAX) $(DMARG_FLAGS) \ + $(IFO_PSD) $(IFO_CHAN) \ + --inclination-cosine-sampler --declination-cosine-sampler \ + --data-start-time 1000000008 --data-end-time 1000000016 --inv-spec-trunc-time 0 \ + --srate 4096 --sampler-method $(SAMPLER) + +CALMARG := --calibration-envelope-directory $(CURDIR)/cal_env --calibration-n-realizations $(NCAL) + +.PHONY: help inputs run-baseline run-loop run-fused compare verify-exact all clean + +help: + @echo "Targets:" + @echo " make inputs - build the distance-marginalization table + cal envelope files" + @echo " make verify-exact - DETERMINISTIC unit check: loop == fused == reference to ~1e-14" + @echo " make run-baseline - ILE, no calibration marginalization" + @echo " make run-loop - ILE + in-loop calmarg, Option B (loop; CPU or GPU)" + @echo " make run-fused - ILE + in-loop calmarg, Option C (fused GPU kernel)" + @echo " make compare - print marginalized lnL from the three runs" + @echo " make all - inputs + verify-exact + the three runs + compare" + @echo " make lowsnr-inputs- generate a fainter (~SNR 9) 3-IFO injection + cache" + @echo " make low-snr - lowsnr-inputs, then 'make all' on the quiet cache" + @echo " make clean - remove generated inputs/outputs" + @echo "" + @echo "Tunables (make VAR=...): NCAL=$(NCAL) NCHUNK=$(NCHUNK) NMAX=$(NMAX) NEFF=$(NEFF) SEED=$(SEED)" + @echo "Review matrix: BACKEND=gpu|cpu DMARG=1|0 PHASE=0|1" + @echo " e.g. make verify-exact BACKEND=cpu DMARG=0 PHASE=1 (laptop, no CUDA, no distance marg, phase marg)" + +inputs: $(DMARG_DEP) cal_env/H1.txt + +distance_marg.npz: + $(ENV) $(RIFT_CODE_ROOT)/bin/util_InitMargTable --d-min 1 --d-max $(DMAX) --out distance_marg.npz + +cal_env/H1.txt: + $(ENV) python tools/make_cal_envelopes.py --out-dir $(CURDIR)/cal_env --ifos H1,L1,V1 + +# Deterministic exact check on identical inputs (bypasses the stochastic sampler): +# loop (Option B), fused (Option C) and a brute-force reference must agree to ~1e-14. +# Honours BACKEND (gpu/cpu) and DMARG (distmarg/default helper). +verify-exact: $(DMARG_DEP) + $(ENV) python -m RIFT.calmarg.backtest_calmarg --backend $(BACKEND) \ + --loglikelihood $(VERIFY_LL) $(VERIFY_PHASE) \ + --dets H1,L1,V1 --n-cal $(NCAL) --npts-extrinsic 512 + +run-baseline: inputs + $(ENV) $(ILE) $(COMMON) --output-file out_baseline + @echo "baseline lnL:"; cat out_baseline*.dat + +run-loop: inputs + $(ENV) $(ILE) $(COMMON) $(CALMARG) --output-file out_loop + @echo "loop lnL:"; cat out_loop*.dat + +run-fused: inputs + $(ENV) $(ILE) $(COMMON) $(CALMARG) --calibration-fused-kernel --output-file out_fused + @echo "fused lnL:"; cat out_fused*.dat + +compare: + $(ENV) python tools/compare_lnL.py \ + baseline=out_baseline_0_.dat loop=out_loop_0_.dat fused=out_fused_0_.dat + +all: inputs verify-exact run-baseline run-loop run-fused compare + +# --- Low-SNR variant ------------------------------------------------------- +# The bundled CI injection is network SNR ~17.5 (m1=35,m2=30 at 200 Mpc). This +# generates a fainter copy (same source, larger distance -> ~SNR 9) on the fly so +# the full-sampler loop-vs-fused agreement is resolved well above Monte-Carlo +# noise. No binaries are committed; the frames/cache are regenerated locally, +# exactly as the CI data itself is made. +INJ_M1 ?= 35 +INJ_M2 ?= 30 +INJ_DIST ?= 400 # Mpc -> ~SNR 9 network (vs ~17.5 for the bundled 200 Mpc data) +INJ_APPROX ?= SEOBNRv4 +INJ_FMIN ?= 8 +LOWSNR_CACHE := $(CURDIR)/lowsnr.cache + +lowsnr-inputs: $(LOWSNR_CACHE) +$(LOWSNR_CACHE): + $(ENV) util_WriteInjectionFile.py --parameter m1 --parameter-value $(INJ_M1) \ + --parameter m2 --parameter-value $(INJ_M2) --fname mdc_lowsnr --approx $(INJ_APPROX) \ + --parameter tref --parameter-value $(EVENT_TIME) \ + --parameter dist --parameter-value $(INJ_DIST) --parameter fmin --parameter-value $(INJ_FMIN) + $(ENV) util_WriteFrameAndCacheFromXML.sh mdc_lowsnr.xml.gz 0 lowsnr $(INJ_APPROX) + @echo "# injected network SNR:"; $(ENV) util_FrameZeroNoiseSNR.py --cache $(LOWSNR_CACHE) \ + --psd-file H1=$(PSD) --psd-file L1=$(PSD) --psd-file V1=$(PSD) 2>/dev/null | tail -1 + +# Run the full comparison on the quiet injection (override CACHE + matched template). +low-snr: lowsnr-inputs + $(MAKE) all CACHE=$(LOWSNR_CACHE) SIM_XML=$(CURDIR)/mdc_lowsnr.xml.gz + +# --- Full DAG test (Option C adaptive cal pilot) on local condor ---------------- +# Builds and (optionally) submits a real RIFT DAG that exercises the per-iteration +# calibration PILOT wiring: calpilot_N runs in parallel with CIP_N, learns a cal +# proposal, and SEEDS the wide ILE jobs of iteration N+1 via a breadcrumb. Mirrors +# the known-good .travis/ILE-GPU-Paper test_workflow_batch_gpu_lowlatency target +# (calls create_event_parameter_pipeline_BasicIteration directly), 3-IFO + GPU + AV, +# with --calmarg-pilot added. Designed to run on a single machine with condor + GPU +# (e.g. cardassia); select the card with CUDA_VISIBLE_DEVICES if needed. +RUN_DAG := $(CURDIR)/rundir_dag +# Toggles: PILOT=1 adds the adaptive cal pilots (seed wide_{N+1}); PILOT=0 is VANILLA +# in-loop calmarg (no pilots -- the clean production path, best for faint sources). +# FUSED=1 uses the fused GPU kernel (Option C). E.g. the priority vanilla-fused demo: +# make dag-build PILOT=0 FUSED=1 && make dag-run PILOT=0 FUSED=1 +PILOT ?= 1 +FUSED ?= 0 +N_IT_DAG ?= 2 # >=2 so iteration 1 consumes the seed from calpilot_0 (PILOT=1) +NPTS_GRID_DAG ?= 80 # initial intrinsic grid size +NPTS_IT_DAG ?= 12 # intrinsic points / ILE jobs per iteration +NCAL_DAG ?= 100 # >=100 required, 300 recommended; 20 collapsed cal n_eff to O(1) and made the 2d lnL surface pure noise +NMAX_DAG ?= 40000 +NEFF_DAG ?= 15 +NCHUNK_DAG ?= 10000 +N_COPIES_DAG ?= 1 # ILE job duplicates (builder default 2); 1 -> no redundancy, + # ~2x faster turnaround on a single-card test +DMARG_DAG ?= 1 # distance marginalization in the wide ILE (default ON). With it, + # distance is integrated analytically (not sampled), the clean + # production path. (Aside: --no-adapt-distance is NOT a freeze -- + # distance is then UNIFORMLY sampled, fine for low-SNR with a + # sensible d-min/d-max; it is simply moot once distmarg is on.) + +ifeq ($(DMARG_DAG),1) + DAG_DMARG_FLAG := --distance-marginalization --distance-marginalization-lookup-table $(CURDIR)/distance_marg.npz + DAG_DMARG_DEP := distance_marg.npz +else + DAG_DMARG_FLAG := + DAG_DMARG_DEP := +endif + +ifeq ($(FUSED),1) + DAG_FUSED_FLAG := --calibration-fused-kernel +else + DAG_FUSED_FLAG := +endif +# Optional zero-cal extrinsic burn-in (task #24): set BURN_IN_NEFF to a target n_eff to +# adapt the sampler on the cheap zero-cal likelihood before the full cal-marg integral. +ifneq ($(BURN_IN_NEFF),) + DAG_BURNIN_FLAG := --calibration-burn-in-neff $(BURN_IN_NEFF) +else + DAG_BURNIN_FLAG := +endif +ifeq ($(PILOT),1) + DAG_PILOT_CEPP := --calmarg-pilot --calmarg-pilot-cadence 1 --calmarg-pilot-max-it 1 +else + DAG_PILOT_CEPP := +endif + +# NOTE: adapt-weight-exponent left at the ILE DEFAULT (1.0). For the AV sampler used here +# this option is a NO-OP (it matters for the OTHER integrators -- GMM/portfolio -- so do +# not drop it globally). n_eff is naturally low at small n-max (~3 at 100k) regardless: +# 100k samples is SHORT by RIFT standards; production runs use millions and let the AV +# integrator creep n_eff upward. Baseline (no cal) and calmarg give ~equal n_eff here, so +# cal is not the bottleneck -- the moderate SNR (~17.5; "loud" is 40+) x sample count is. +DAG_ILE_ARGS := --n-chunk $(NCHUNK_DAG) --time-marginalization --sim-xml overlap-grid.xml.gz \ + --reference-freq 100.0 --event-time $(EVENT_TIME) --save-P 0.1 \ + --cache-file $(CACHE) --fmin-template 10 --n-max $(NMAX_DAG) --fmax 1700.0 --save-deltalnL inf \ + --l-max 2 --n-eff $(NEFF_DAG) --approximant SEOBNRv4 --d-min 1 --d-max $(DMAX) \ + --psd-file H1=$(PSD) --psd-file L1=$(PSD) --psd-file V1=$(PSD) \ + --channel-name H1=FAKE-STRAIN --channel-name L1=FAKE-STRAIN --channel-name V1=FAKE-STRAIN \ + --inclination-cosine-sampler --declination-cosine-sampler --data-start-time 1000000008 \ + --data-end-time 1000000016 --inv-spec-trunc-time 0 \ + --srate 4096 --sampler-method AV --vectorized --gpu $(DAG_DMARG_FLAG) \ + --calibration-envelope-directory $(CURDIR)/cal_env --calibration-n-realizations $(NCAL_DAG) \ + --calibration-spline-count 10 $(DAG_FUSED_FLAG) $(DAG_BURNIN_FLAG) +# --sigma-cut 5: cal draws (PRIOR for vanilla every iteration; PRIOR at iteration 0 for the +# pilot cold start) have high per-point MC error (~0.7-0.9 here), which the CIP default +# --sigma-cut 0.6 would strip entirely. Relax it (cf. the helper_LDG_Events.py fix that +# relaxes the first CIP stage automatically when --calmarg-pilot is enabled). +DAG_CIP_ARGS := --mc-range [23,35] --eta-range [0.20,0.24999] --parameter mc --parameter-implied eta --parameter-nofit delta_mc --fit-method gp --lnL-offset 120 --sigma-cut 5 --cap-points 12000 --n-output-samples 5000 --no-plots --n-eff 5000 +DAG_TEST_ARGS := --always-succeed --method lame --parameter m1 +DAG_PLOT_ARGS := --parameter m1 --parameter m2 +DAG_FILE := marginalize_intrinsic_parameters_BasicIterationWorkflow.dag + +.PHONY: dag dag-build dag-validate dag-run tune-single tune-condor + +# Single-event ILE at the injection as ONE condor job (a full DAG is overkill for tuning): +# persistent logs, watchdog-isolated (condor, not an interactive launch), restartable, and +# UNBUFFERED via `python -u` so n_eff creep is watchable live. Submit from inside the env +# (getenv captures cupy): conda run -n rift_gpu2 make tune-condor FUSED=1 NMAX_DAG=4000000 +# watch: tail -f rundir_tune/tune_condor.out ; cat rundir_tune/tune_out*.dat +tune-condor: cal_env/H1.txt $(DAG_DMARG_DEP) + rm -rf $(CURDIR)/rundir_tune; mkdir -p $(CURDIR)/rundir_tune + @PYEXE=$$(command -v python); \ + { echo "universe = vanilla"; \ + echo "executable = $$PYEXE"; \ + echo "arguments = -u $(ILE) $(DAG_ILE_ARGS) --sim-xml $(SIM_XML) --n-events-to-analyze 1 --output-file tune_out"; \ + echo "request_GPUs = 1"; \ + echo "getenv = True"; \ + echo "initialdir = $(CURDIR)/rundir_tune"; \ + echo "output = tune_condor.out"; echo "error = tune_condor.err"; echo "log = tune_condor.log"; \ + echo "queue"; } > $(CURDIR)/rundir_tune/tune_condor.sub + cd $(CURDIR)/rundir_tune && $(ENV) condor_submit tune_condor.sub + @echo "Submitted. Watch live: tail -f $(CURDIR)/rundir_tune/tune_condor.out" + @echo "Result when done: cat $(CURDIR)/rundir_tune/tune_out*.dat (lnL col -4, n_eff col -1)" + +# Single-event ILE at the INJECTION (command-single style) using the SAME wide-ILE args +# the DAG will use -- the fast way to tune settings (n_eff!) for the event before paying +# for a full multi-iteration DAG. Reports lnL (col -4) and n_eff (col -1). +# make tune-single FUSED=1 DMARG_DAG=1 NMAX_DAG=200000 NEFF_DAG=200 +tune-single: cal_env/H1.txt $(DAG_DMARG_DEP) + rm -rf $(CURDIR)/rundir_tune; mkdir -p $(CURDIR)/rundir_tune + cd $(CURDIR)/rundir_tune && $(ENV) $(ILE) $(DAG_ILE_ARGS) \ + --sim-xml $(SIM_XML) --n-events-to-analyze 1 --output-file tune_out + @echo "=== tune-single result (lnL +- err, n_eff) ===" + @cat $(CURDIR)/rundir_tune/tune_out*.dat + @echo " (n_eff is the last column; aim for a healthy value before running the DAG)" + +# Build the DAG (no condor submission) and validate the wiring. PILOT/FUSED toggles above. +dag-build: cal_env/H1.txt $(DAG_DMARG_DEP) + rm -rf $(RUN_DAG); mkdir -p $(RUN_DAG) + cd $(RUN_DAG) && $(ENV) util_ManualOverlapGrid.py --parameter mc --parameter-range '[23,35]' \ + --parameter delta_mc --parameter-range '[0.0,0.5]' --grid-cartesian-npts $(NPTS_GRID_DAG) \ + --skip-overlap --approx SEOBNRv4 + cd $(RUN_DAG) && cp overlap-grid.xml.gz overlap-grid-0.xml.gz + cd $(RUN_DAG) && printf 'X %s\n' "$(DAG_CIP_ARGS)" > args_cip.txt + cd $(RUN_DAG) && printf 'X %s\n' "$(DAG_TEST_ARGS)" > args_test.txt + cd $(RUN_DAG) && printf 'X %s\n' "$(DAG_PLOT_ARGS)" > args_plot.txt + # args_ile.txt: the wide ILE config; for PILOT=1 also the per-iteration seed breadcrumb + # (the condor macro $(macroiterationprev) is emitted literally: $$ -> $ for Make, then + # single-quoted for the shell -- exactly as util_RIFT_pseudo_pipe.py injects it). + cd $(RUN_DAG) && { printf 'X %s' "$(DAG_ILE_ARGS)"; \ + [ "$(PILOT)" = "1" ] && printf ' --calibration-proposal-breadcrumb %s/cal_consolidated_$$(macroiterationprev).npz' "$(RUN_DAG)"; \ + printf '\n'; } > args_ile.txt + cd $(RUN_DAG) && $(ENV) create_event_parameter_pipeline_BasicIteration --request-gpu-ILE \ + --ile-n-events-to-analyze $(NPTS_IT_DAG) --input-grid overlap-grid-0.xml.gz \ + --ile-exe $(ILE) \ + --ile-args $(RUN_DAG)/args_ile.txt --cip-args $(RUN_DAG)/args_cip.txt \ + --test-args $(RUN_DAG)/args_test.txt --plot-args $(RUN_DAG)/args_plot.txt \ + --request-memory-CIP 2048 --request-memory-ILE 4096 --n-copies $(N_COPIES_DAG) \ + --input-grid $(RUN_DAG)/overlap-grid.xml.gz --n-samples-per-job $(NPTS_IT_DAG) \ + --working-directory $(RUN_DAG) --n-iterations $(N_IT_DAG) \ + $(DAG_PILOT_CEPP) + $(MAKE) dag-validate PILOT=$(PILOT) FUSED=$(FUSED) + +# Validate the produced DAG (no condor needed). Branch on PILOT. +dag-validate: + @grep -q -- "--calibration-envelope-directory" "$(RUN_DAG)/ILE.sub" || (echo "FAIL: wide ILE.sub missing calibration envelope" && false) +ifeq ($(FUSED),1) + @grep -q -- "--calibration-fused-kernel" "$(RUN_DAG)/ILE.sub" || (echo "FAIL: FUSED=1 but ILE.sub missing --calibration-fused-kernel" && false) +endif +ifeq ($(PILOT),1) + @test -s "$(RUN_DAG)/CALPILOT.sub" || (echo "FAIL: no CALPILOT.sub" && false) + @grep -q "util_CalPilotStage.py" "$(RUN_DAG)/CALPILOT.sub" || (echo "FAIL: CALPILOT.sub does not run util_CalPilotStage.py" && false) + @grep -q "CALPILOT" $(RUN_DAG)/$(DAG_FILE) || (echo "FAIL: no CALPILOT job in DAG" && false) + @grep -q -- "--calibration-proposal-breadcrumb" "$(RUN_DAG)/ILE.sub" || (echo "FAIL: wide ILE.sub missing seed breadcrumb" && false) + @grep -q "macroiterationprev" "$(RUN_DAG)/ILE.sub" || (echo "FAIL: ILE.sub missing macroiterationprev" && false) + @echo "OK: cal-PILOT DAG built -- CALPILOT runs util_CalPilotStage.py; wide ILE seeded via breadcrumb." +else + @! test -s "$(RUN_DAG)/CALPILOT.sub" || (echo "FAIL: PILOT=0 but a CALPILOT.sub was produced" && false) + @! grep -q -- "--calibration-proposal-breadcrumb" "$(RUN_DAG)/ILE.sub" || (echo "FAIL: PILOT=0 but ILE.sub has a seed breadcrumb" && false) + @echo "OK: VANILLA calmarg DAG built (no pilots; FUSED=$(FUSED)) -- ILE.sub carries the cal envelope, no CALPILOT." +endif + @echo " Submit with: make dag-run PILOT=$(PILOT) FUSED=$(FUSED)" + +# Submit the DAG to the local condor (GPU). Select the card with CUDA_VISIBLE_DEVICES. +dag-run: + cd $(RUN_DAG) && $(ENV) condor_submit_dag -f $(DAG_FILE) + @echo "Submitted. Watch with: condor_q ; tail -f $(RUN_DAG)/$(DAG_FILE).dagman.out" + @echo "After it finishes, the pilot products are $(RUN_DAG)/cal_consolidated_*.npz and" + @echo "$(RUN_DAG)/cal_pilot_resp_*.npz ; the seeded wide-ILE logs report 'SEEDED from proposal breadcrumb'." + +dag: dag-build + +# --- Top-level pipeline thread-through (util_RIFT_pseudo_pipe.py) ------------------- +# Unlike dag-build (which calls create_event_parameter_pipeline_BasicIteration directly +# with a hand-written args_ile.txt), this exercises the FULL top-level builder: +# util_RIFT_pseudo_pipe.py -> helper_LDG_Events.py -> args generation -> create_event_*. +# Goal: confirm the calmarg flags AND the time-sample (time-resampling) options thread all +# the way into args_ile.txt / the .sub files / the DAG. Offline build-validate (the +# established pattern: .travis/test-build.sh + demo/pipeline/zero_spin_phenomD), so no GPU +# run needed. Zero spin (reuses the zero-spin IMRPhenomD ini); iterations forced small. +PP_RUN := $(CURDIR)/rundir_pp +PP_INI ?= $(RIFT_CODE_ROOT)/demo/pipeline/zero_spin_phenomD/zero_spin_phenomD.ini +PP_COINC ?= $(REPO_ROOT)/.travis/ref_ini/coinc.xml +PP_NIT ?= 2 # forced iteration count (small, under control) +PP_SRATE ?= 4096 # time-resampling output rate (--srate-resample-time-marginalization) +# PP_PILOT=1 adds the adaptive cal PILOT jobs to pp-run (harvest->dump->fit->consolidate-> +# seed wide_{N+1}). Default 0 (vanilla). `make pp-run-pilot` is the convenience wrapper. +PP_PILOT ?= 0 +ifeq ($(PP_PILOT),1) + PP_PILOT_FLAGS := --calmarg-pilot --calmarg-pilot-cadence 1 --calmarg-pilot-max-it 1 +else + PP_PILOT_FLAGS := +endif + +.PHONY: pp pp-build pp-validate + +pp-build: cal_env/H1.txt + @test -s "$(PP_INI)" || (echo "missing zero-spin ini $(PP_INI)" && false) + @test -s "$(PP_COINC)" || (echo "missing coinc $(PP_COINC)" && false) + rm -rf $(PP_RUN); touch $(CURDIR)/foo.cache + $(ENV) util_RIFT_pseudo_pipe.py \ + --use-ini $(PP_INI) --use-coinc $(PP_COINC) --use-rundir $(PP_RUN) \ + --fake-data-cache $(CURDIR)/foo.cache \ + --assume-nospin --approx IMRPhenomD --ile-sampler-method AV \ + --add-extrinsic --add-extrinsic-time-resampling --internal-ile-srate-time-resampling $(PP_SRATE) \ + --calmarg-envelope-directory $(CURDIR)/cal_env --calmarg-n-realizations $(NCAL_DAG) --calmarg-fused-kernel \ + --internal-force-iterations $(PP_NIT) --ile-n-eff 30 + $(MAKE) pp-validate + +# Confirm everything threaded through into the generated pipeline (no condor needed). +pp-validate: + @test -s "$(PP_RUN)/args_ile.txt" || (echo "FAIL: pseudo_pipe produced no args_ile.txt" && false) + @echo "--- args_ile.txt ---"; cat $(PP_RUN)/args_ile.txt + @grep -q -- "--calibration-envelope-directory" $(PP_RUN)/args_ile.txt || (echo "FAIL: calmarg envelope not threaded to args_ile.txt" && false) + @grep -q -- "--calibration-n-realizations" $(PP_RUN)/args_ile.txt || (echo "FAIL: calmarg n-realizations not threaded" && false) + @grep -q -- "--calibration-fused-kernel" $(PP_RUN)/args_ile.txt || (echo "FAIL: calmarg fused-kernel not threaded" && false) + @grep -q -- "--time-marginalization" $(PP_RUN)/args_ile.txt || (echo "FAIL: --time-marginalization absent (time integral)" && false) + @grep -q -- "--srate-resample-time-marginalization" $(PP_RUN)/args_ile.txt || (echo "FAIL: TIME SAMPLES (--srate-resample-time-marginalization) not threaded to ILE" && false) + @ls $(PP_RUN)/*.dag >/dev/null 2>&1 || (echo "FAIL: no top-level DAG produced" && false) + @test -s "$(PP_RUN)/ILE_extr.sub" || (echo "FAIL: no extrinsic (time-sample) stage ILE_extr.sub" && false) + @echo "--- ILE_extr.sub (the extrinsic / time-sample output stage) ---" + @grep -oE -- "--calibration-fused-kernel|--calibration-envelope-directory|--srate-resample-time-marginalization [0-9]+|--time-marginalization" $(PP_RUN)/ILE_extr.sub | sort -u + @grep -q -- "--calibration-fused-kernel" $(PP_RUN)/ILE_extr.sub || (echo "FAIL: extrinsic stage missing calmarg" && false) + @grep -q -- "--srate-resample-time-marginalization" $(PP_RUN)/ILE_extr.sub || (echo "FAIL: extrinsic stage missing TIME SAMPLES resampling" && false) + @echo "OK: top-level util_RIFT_pseudo_pipe.py threaded EVERYTHING --" + @echo " calmarg (envelope + n-realizations + fused-kernel) into args_ile.txt, ILE.sub AND ILE_extr.sub;" + @echo " TIME SAMPLES (--srate-resample-time-marginalization) into the wide AND extrinsic (ILE_extr) stages" + @echo " [--add-extrinsic-time-resampling drives the extrinsic stage at build time]; zero-spin IMRPhenomD;" + @echo " $(PP_NIT) iterations; full DAG produced." + @echo " (Offline build-validate -- the established pattern; no GPU/data run needed for the threading check.)" + +pp: pp-build + +# --- EXTRINSIC HANDOFF offline build-validate (separate rundir; never touches pp/run dirs) --- +# Builds a pipeline with the extrinsic handoff (GMM sampler) and confirms the per-event +# proposal output, the seed breadcrumb, and the EXTRCONSOLIDATE consolidation node all thread +# through. Offline -- no GPU/condor/data run needed for the threading check. +# See RIFT/calmarg/DESIGN_extrinsic_handoff.md. +PP_EXTR_RUN := $(CURDIR)/rundir_pp_extr + +# OPTIONAL distance marginalization for the pseudo_pipe calmarg path. The fused calmarg kernel +# does NOT require distmarg -- it has both a non-distmarg kernel (Q_fused_calmarg_cupy) and a +# distmarg kernel (Q_fused_calmarg_distmarg_cupy), and the ILE binary wires whichever applies. +# PP_DMARG=1 adds --internal-marginalize-distance, so the helper builds the lookup table +# (util_InitMargTable) and distance is marginalized ANALYTICALLY -- it leaves the extrinsic +# sampler entirely. RECOMMENDED with --extrinsic-handoff: it removes the distance dimension +# (and its hard [1,PP_DMAX] bound) from the GMM proposal, so a seeded distance Gaussian can't +# spill past the bound into NaN likelihood. (Distinct from the direct-ILE dag-build DMARG knob, +# which uses a pre-built --distance-marginalization-lookup-table.) +PP_DMARG ?= 0 +PP_DMAX ?= 1000 +ifeq ($(PP_DMARG),1) + PP_DMARG_FLAGS := --internal-marginalize-distance --internal-distance-max $(PP_DMAX) +else + PP_DMARG_FLAGS := +endif + +# Recovered CALIBRATION posterior in the final fairdraw output (opt-in; default ON for the +# runnable targets so it gets tested). Writes a self-contained sibling __cal.dat +# with the full draw (intrinsic + extrinsic + cal__amp_/cal__phase_) -- the +# recovered cal posterior is those columns. PP_CALPOST=0 to disable. +PP_CALPOST ?= 1 +ifeq ($(PP_CALPOST),1) + PP_CALPOST_FLAGS := --calmarg-export-posterior +else + PP_CALPOST_FLAGS := +endif + +# CIP throughput: by default RIFT runs ONE CIP worker per iteration targeting a large n_eff +# (~500, the cip-cap-neff), which is slow. Spread the work over PP_CIP_EXPLODE parallel CIP +# workers and lower the n_eff target so EACH worker is light: with PP_CIP_EXPLODE=8 and +# PP_CIP_NEFF=160 the cap is split ~160/8 -> n_eff ~20 per worker (8x parallel, ~20/job vs ~500). +PP_CIP_EXPLODE ?= 8 +PP_CIP_NEFF ?= 160 +PP_CIP_FLAGS := --cip-explode-jobs $(PP_CIP_EXPLODE) --internal-cip-cap-neff $(PP_CIP_NEFF) + +.PHONY: extr extr-build extr-validate + +extr-build: cal_env/H1.txt + @test -s "$(PP_INI)" || (echo "missing zero-spin ini $(PP_INI)" && false) + @test -s "$(PP_COINC)" || (echo "missing coinc $(PP_COINC)" && false) + rm -rf $(PP_EXTR_RUN); touch $(CURDIR)/foo.cache + $(ENV) util_RIFT_pseudo_pipe.py \ + --use-ini $(PP_INI) --use-coinc $(PP_COINC) --use-rundir $(PP_EXTR_RUN) \ + --fake-data-cache $(CURDIR)/foo.cache \ + --assume-nospin --approx IMRPhenomD --ile-sampler-method GMM \ + --add-extrinsic --add-extrinsic-time-resampling --internal-ile-srate-time-resampling $(PP_SRATE) \ + --calmarg-envelope-directory $(CURDIR)/cal_env --calmarg-n-realizations $(NCAL_DAG) --calmarg-fused-kernel \ + --extrinsic-handoff $(PP_DMARG_FLAGS) \ + --internal-force-iterations $(PP_NIT) --ile-n-eff 30 + $(MAKE) extr-validate PP_DMARG=$(PP_DMARG) + +extr-validate: + @test -s "$(PP_EXTR_RUN)/args_ile.txt" || (echo "FAIL: pseudo_pipe produced no args_ile.txt" && false) + @echo "--- args_ile.txt (extrinsic handoff flags) ---" + @grep -oE -- "--sampler-method [A-Za-z_]+|--extrinsic-proposal-output [^ ]+|--extrinsic-proposal-breadcrumb [^ ]+" $(PP_EXTR_RUN)/args_ile.txt | sort -u + @grep -q -- "--sampler-method GMM" $(PP_EXTR_RUN)/args_ile.txt || (echo "FAIL: extrinsic handoff needs the GMM sampler (--sampler-method GMM absent)" && false) + @grep -q -- "--extrinsic-proposal-output" $(PP_EXTR_RUN)/args_ile.txt || (echo "FAIL: per-event --extrinsic-proposal-output not threaded to ILE" && false) + @grep -q -- "--extrinsic-proposal-breadcrumb" $(PP_EXTR_RUN)/args_ile.txt || (echo "FAIL: seed --extrinsic-proposal-breadcrumb not threaded to ILE" && false) + @if [ "$(PP_DMARG)" = "1" ]; then \ + grep -q -- "--distance-marginalization" $(PP_EXTR_RUN)/args_ile.txt || (echo "FAIL: PP_DMARG=1 but --distance-marginalization not threaded" && false); \ + grep -q -- "--distance-marginalization-lookup-table" $(PP_EXTR_RUN)/args_ile.txt || (echo "FAIL: PP_DMARG=1 but no distance-marginalization lookup table" && false); \ + echo " [PP_DMARG=1] distance marginalization ON: distance is marginalized analytically and removed from the GMM sampler"; \ + else echo " [PP_DMARG=0] distance SAMPLED (fused kernel without distmarg -- valid; pass PP_DMARG=1 to marginalize)"; fi + @test -s "$(PP_EXTR_RUN)/EXTRCONSOLIDATE.sub" || (echo "FAIL: no EXTRCONSOLIDATE.sub consolidation stage" && false) + @echo "--- EXTRCONSOLIDATE.sub ---" + @grep -E "universe|executable|arguments|initialdir" $(PP_EXTR_RUN)/EXTRCONSOLIDATE.sub | head + @grep -q "util_ExtrinsicConsolidate.py" $(PP_EXTR_RUN)/EXTRCONSOLIDATE.sub || (echo "FAIL: EXTRCONSOLIDATE.sub does not run util_ExtrinsicConsolidate.py" && false) + @grep -q "universe = local" $(PP_EXTR_RUN)/EXTRCONSOLIDATE.sub || (echo "FAIL: EXTRCONSOLIDATE should be a local-universe job" && false) + @DAG=$$(ls $(PP_EXTR_RUN)/*BasicIterationWorkflow.dag 2>/dev/null | head -1); \ + test -n "$$DAG" || (echo "FAIL: no per-iteration DAG produced" && false); \ + grep -q "util_ExtrinsicConsolidate_py" $$DAG || (echo "FAIL: no EXTRCONSOLIDATE node in the DAG" && false); \ + EC=$$(grep -oE "util_ExtrinsicConsolidate_py-[0-9a-f]+" $$DAG | head -1); \ + grep -qE "PARENT unify_sh-[0-9a-f]+ CHILD $$EC" $$DAG || (echo "FAIL: EXTRCONSOLIDATE not gated behind the iteration's unify (ILE barrier)" && false); \ + grep -qE "PARENT $$EC CHILD integrate_likelihood_extrinsic_batchmode" $$DAG || (echo "FAIL: next-iteration ILE jobs do not depend on EXTRCONSOLIDATE (seed barrier missing)" && false) + @echo "OK: extrinsic handoff threaded EVERYTHING --" + @echo " --sampler-method GMM + per-event --extrinsic-proposal-output + seed --extrinsic-proposal-breadcrumb in args_ile.txt;" + @echo " EXTRCONSOLIDATE.sub (local universe, util_ExtrinsicConsolidate.py);" + @echo " DAG wiring: unify_{it} -> EXTRCONSOLIDATE_{it} -> wide ILE_{it+1} (consolidate barrier + seed barrier)." + @echo " (Offline build-validate; NOTE the ILE binary change is execute-point -- rebuild the container before an OSG/CIT run.)" + +extr: extr-build + +# --- RUNNABLE top-level pipeline on the CI fake data (breadcrumb for later) ---------- +# Unlike pp-build (offline threading check), this builds a pipeline that ACTUALLY RUNS on +# the zero-noise CI data and SUBMITS it to condor: real coinc (util_SimInspiralToCoinc.py +# from the injection), the real zero_noise.cache + CI PSD + FAKE-STRAIN channels (a +# CI-matched ini), calmarg + time-resampling, zero spin, small forced iterations. Produces +# extrinsic-stage TIME SAMPLES with calmarg on. Single GPU on cardassia (CUDA_VISIBLE_DEVICES=0). +PP_RUN_REAL := $(CURDIR)/rundir_pp_run +PP_DAG := marginalize_intrinsic_parameters_BasicIterationWorkflow.dag + +# --- CIT / OSG container mode (auto-activates when SINGULARITY_RIFT_IMAGE is set) ----- +# CIT is container-only: you must `export SINGULARITY_RIFT_IMAGE=/path/to/rift.sif` and run +# on OSG. Exporting that env var (or passing OSG=1) auto-populates the OSG flags on pp-run: +# --use-osg -> the builder adds --use-singularity (reads SINGULARITY_RIFT_IMAGE) +# --use-osg-cip -> CIP jobs run in-container on OSG too +# --use-osg-file-transfer + a frames_dir -> transfer the CI frames (no CVMFS for fake data) +# --ile-additional-files-to-transfer -> ride the cal envelopes + PSD to the workers (no +# shared filesystem on OSG) +# On a local shared-FS run (cardassia) leave SINGULARITY_RIFT_IMAGE unset / OSG=0 -> no-op. +# CIT: export SINGULARITY_RIFT_IMAGE=/cvmfs/.../rift.sif ; make pp-run +ifneq ($(SINGULARITY_RIFT_IMAGE),) + OSG ?= 1 +endif +OSG ?= 0 +ifeq ($(OSG),1) + # Container: ~4 GB disk for the .sif transfer + unpack (vs the MB-scale shared-FS baseline) + PP_DISK ?= 4G + # --condor-local-nonworker: at CIT the fast 'internal' jobs (consolidate/unify/puff/cal- + # consolidation/etc.) have no execute-point access, so vanilla-universe submission fails; + # this runs them locally (non-NFS / flock_local) instead. Required whenever OSG is active. + PP_OSG_FLAGS := --use-osg --use-osg-cip --use-osg-file-transfer --condor-local-nonworker + # On OSG the cal envelopes are auto-added to the transfer list by util_RIFT_pseudo_pipe.py + # (referenced as '.'); the PSD is provided as run-dir -psd.xml.gz (copied below), which + # RIFT lists + transfers by basename. So do NOT pass --use-online-psd-file (it would force + # an unreachable absolute path into the args). + PP_PSD_FLAG := +else + PP_DISK ?= 4M + PP_OSG_FLAGS := + # Local shared FS: reference the CI PSD by absolute path (no transfer needed). + PP_PSD_FLAG := --use-online-psd-file $(PSD) +endif +# Disk requests for ALL THREE job types come from the CLI here, ALWAYS (baseline 4M local, +# 4G container). Must NOT also be set in the ini -- a [rift-pseudo-pipe] value there +# OVERRIDES the CLI, which previously left ILE stuck at 4M while cip/general got bumped. +PP_DISK_FLAGS := --internal-ile-request-disk $(PP_DISK) --internal-cip-request-disk $(PP_DISK) --internal-general-request-disk $(PP_DISK) + +.PHONY: pp-run pp-run-build pp-coinc + +pp-coinc: ci_coinc.xml +ci_coinc.xml: + $(ENV) util_SimInspiralToCoinc.py --sim-xml $(SIM_XML) --event 0 \ + --ifo H1 --ifo L1 --ifo V1 --output $(CURDIR)/ci_coinc.xml --injected-snr 17.5 + +# Build the runnable pipeline (no submit) and validate the runnable bits threaded through. +# OSG=1 (or SINGULARITY_RIFT_IMAGE set) layers on the container/OSG flags for CIT. +pp-run-build: cal_env/H1.txt ci_coinc.xml +ifeq ($(OSG),1) + @test -n "$(SINGULARITY_RIFT_IMAGE)" || (echo "OSG=1 but SINGULARITY_RIFT_IMAGE is not set (CIT is container-only: export it first)" && false) + @echo " [OSG] container mode: SINGULARITY_RIFT_IMAGE=$(SINGULARITY_RIFT_IMAGE)" +endif + rm -rf $(PP_RUN_REAL) + $(ENV) util_RIFT_pseudo_pipe.py \ + --use-ini $(CURDIR)/calmarg_ci.ini --use-coinc $(CURDIR)/ci_coinc.xml --use-rundir $(PP_RUN_REAL) \ + --fake-data-cache $(CACHE) $(PP_PSD_FLAG) \ + --assume-nospin --approx IMRPhenomD --ile-sampler-method AV \ + --add-extrinsic --add-extrinsic-time-resampling --internal-ile-srate-time-resampling 4096 \ + --calmarg-envelope-directory $(CURDIR)/cal_env --calmarg-n-realizations $(NCAL_DAG) --calmarg-fused-kernel \ + $(PP_DMARG_FLAGS) $(PP_CALPOST_FLAGS) $(PP_CIP_FLAGS) \ + --internal-force-iterations $(PP_NIT) --ile-n-eff 30 \ + $(PP_DISK_FLAGS) $(PP_OSG_FLAGS) $(PP_PILOT_FLAGS) +ifeq ($(OSG),1) + @# --use-osg-file-transfer expects a frames_dir of .gwf in the rundir. Must be a FULL + @# COPY, not a symlink: condor refuses "Transfer of symlinks to directories is not supported". + rm -rf $(PP_RUN_REAL)/frames_dir; cp -r $(CI_DEMO)/zero_noise_mdc $(PP_RUN_REAL)/frames_dir + @# The args reference per-IFO -psd.xml.gz (basename, transferred); populate them from + @# the CI PSD so the files exist in the run dir AND carry the real PSD (not fiducial). + for ifo in H1 L1 V1; do cp $(PSD) $(PP_RUN_REAL)/$$ifo-psd.xml.gz; done + @echo " [OSG] copied CI frames -> frames_dir (full copy), CI PSD -> {H1,L1,V1}-psd.xml.gz" +endif +ifeq ($(OSG),1) + @grep -q "local.cache" $(PP_RUN_REAL)/args_ile.txt 2>/dev/null || echo " [OSG] note: cache is local.cache (built on the remote worker), not zero_noise.cache" +else + @grep -q "zero_noise.cache" $(PP_RUN_REAL)/args_ile.txt || (echo "FAIL: not the real CI cache" && false) +endif + @grep -q "FAKE-STRAIN" $(PP_RUN_REAL)/args_ile.txt || (echo "FAIL: not the FAKE-STRAIN channels" && false) + @grep -q "1000000014" $(PP_RUN_REAL)/args_ile.txt || (echo "FAIL: not the CI event time" && false) + @grep -q -- "--srate-resample-time-marginalization" $(PP_RUN_REAL)/ILE_extr.sub || (echo "FAIL: extrinsic stage missing time resampling" && false) + @grep -q -- "--calibration-fused-kernel" $(PP_RUN_REAL)/ILE_extr.sub || (echo "FAIL: extrinsic stage missing calmarg" && false) + @test -s "$(PP_RUN_REAL)/$(PP_DAG)" || (echo "FAIL: no top-level DAG produced" && false) +ifeq ($(PP_PILOT),1) + @test -s "$(PP_RUN_REAL)/CALPILOT.sub" || (echo "FAIL: PP_PILOT=1 but no CALPILOT.sub" && false) + @# the stage exe is named in CALPILOT.sub (local) OR in calpilot_pre.sh (OSG: the sub's + @# executable is the prescript, which builds local.cache then runs util_CalPilotStage.py) + @grep -qs "util_CalPilotStage.py" "$(PP_RUN_REAL)/CALPILOT.sub" "$(PP_RUN_REAL)/calpilot_pre.sh" || (echo "FAIL: neither CALPILOT.sub nor calpilot_pre.sh runs util_CalPilotStage.py" && false) + @grep -q "CALPILOT" "$(PP_RUN_REAL)/$(PP_DAG)" || (echo "FAIL: no CALPILOT job in DAG" && false) + @grep -q -- "--calibration-proposal-breadcrumb" "$(PP_RUN_REAL)/args_ile.txt" || (echo "FAIL: wide ILE args missing seed breadcrumb" && false) + @echo "OK: runnable cal-PILOT pipeline built on CI data -- CALPILOT (harvest->dump->fit->" + @echo " consolidate) wired; wide ILE seeded via --calibration-proposal-breadcrumb; $(PP_NIT) iters." +else + @echo "OK: runnable calmarg pipeline built on the CI fake data (real cache + PSD + FAKE-STRAIN," + @echo " event 1000000014.236, calmarg+fused, time-resampling, zero-spin, $(PP_NIT) iters)." +endif + +# Build + SUBMIT to condor. Watch: condor_q ; tail -f $(PP_RUN_REAL)/$(PP_DAG).dagman.out +# Extrinsic time-sample output lands in the last iteration's extrinsic stage. +pp-run: pp-run-build + cd $(PP_RUN_REAL) && $(ENV) condor_submit_dag -f $(PP_DAG) + @echo "Submitted. Watch: condor_q ; tail -f $(PP_RUN_REAL)/$(PP_DAG).dagman.out" + @# CIT requires accounting identity in the environment, else jobs never queue. + @test -n "$$LIGO_USER_NAME" || echo " *** WARNING: LIGO_USER_NAME is unset -- condor jobs may not queue (required at CIT). export it. ***" + @{ test -n "$$LIGO_ACCOUNTING" || test -n "$$LIGO_ACCOUNTING_GROUP"; } || echo " *** WARNING: LIGO_ACCOUNTING (a.k.a. LIGO_ACCOUNTING_GROUP) is unset -- condor jobs may not queue (required at CIT). export it. ***" + +# Runnable cal-PILOT pipeline: pp-run with the adaptive pilots enabled (harvest->dump-> +# fit->consolidate->seed wide_{N+1}). Needs >=2 iterations so the seed is consumed. +# Honours OSG (CIT) the same way as pp-run. +# NOTE: uses a SEPARATE run directory (rundir_pp_pilot) so it does NOT clobber a vanilla +# pp-run already running in rundir_pp_run. (pp-run-build starts with `rm -rf`.) +.PHONY: pp-run-pilot pp-run-pilot-build +pp-run-pilot-build: + $(MAKE) pp-run-build PP_PILOT=1 PP_NIT=2 PP_RUN_REAL=$(CURDIR)/rundir_pp_pilot +pp-run-pilot: + $(MAKE) pp-run PP_PILOT=1 PP_NIT=2 PP_RUN_REAL=$(CURDIR)/rundir_pp_pilot + +# --- EXTRINSIC HANDOFF: small real GPU+condor smoke run on cardassia -------------------- +# Confirms the seeded extrinsic handoff actually runs: GMM sampler (so gmm_dict seeding is +# live), --extrinsic-handoff on, on the CI fake data. Deliberately TINY so it gets through +# many intrinsic points per condor job (few restarts) and reaches the seeded 2nd iteration +# fast: 300 initial intrinsic points, 200/generation, 50 intrinsic evals PER ILE job +# (IMRPhenomD), >=2 iterations so iteration-1 ILE is seeded by iteration-0's consolidation. +# Separate rundir (rundir_pp_extr_run) -- never clobbers pp-run / pilot runs. +# Single GPU on cardassia: CUDA_VISIBLE_DEVICES=0 make extr-run +EXTR_RUN := $(CURDIR)/rundir_pp_extr_run +EXTR_NIT ?= 2 # need >=2 so the iteration-0 proposal seeds iteration-1 +EXTR_GRID0 ?= 300 # initial intrinsic grid points +EXTR_GRIDN ?= 200 # intrinsic points proposed per generation (CIP output) +EXTR_PER_JOB ?= 50 # intrinsic evaluations per ILE condor job (minimize restarts) +EXTR_NCHUNK ?= 4000 # extrinsic block size; small keeps the NVS 510 off its watchdog +EXTR_NMAX ?= 40000 # HARD cap on extrinsic samples/point -- the helper sets 4,000,000 + # (production); with slow calmarg convergence that is hours/point on + # the NVS 510. 40000 -> ~20 s/point (measured), enough to fit+hand off. +EXTR_NEFF ?= 30 # n_eff target -- smoke just confirms seeding; jobs stop at n-max +# The extrinsic handoff forces --sampler-method GMM on the WIDE stages (the gmm_dict seed needs +# GMM). The FINAL extrinsic stage merely EXTRACTS samples and is much more efficient with the AV +# sampler, so swap it via a post-build search/replace on the generated ILE_extr.sub (a deploy +# step here -- the pipeline source stays sampler-neutral). EXTR_EXTRINSIC_AV=0 to keep GMM. +EXTR_EXTRINSIC_AV ?= 1 + +.PHONY: extr-run extr-run-build + +# Several knobs live in calmarg_ci.ini's [rift-pseudo-pipe] section, and an ini value +# OVERRIDES the CLI -- so the per-job / per-generation / n_eff sizes must be set IN the ini, +# not on the command line. Derive a run-specific ini from calmarg_ci.ini, overriding only +# those three lines (sed), leaving the shared ini untouched (so pp-run is unaffected). +EXTR_INI := $(CURDIR)/extr_calmarg_ci.ini + +extr-run-build: cal_env/H1.txt ci_coinc.xml + rm -rf $(EXTR_RUN) + sed -e 's/^ile-jobs-per-worker=.*/ile-jobs-per-worker=$(EXTR_PER_JOB)/' \ + -e 's/^n-output-samples=.*/n-output-samples=$(EXTR_GRIDN)/' \ + -e 's/^ile-n-eff=.*/ile-n-eff=$(EXTR_NEFF)/' \ + $(CURDIR)/calmarg_ci.ini > $(EXTR_INI) + $(ENV) util_RIFT_pseudo_pipe.py \ + --use-ini $(EXTR_INI) --use-coinc $(CURDIR)/ci_coinc.xml --use-rundir $(EXTR_RUN) \ + --fake-data-cache $(CACHE) --use-online-psd-file $(PSD) \ + --assume-nospin --approx IMRPhenomD --ile-sampler-method GMM \ + --add-extrinsic --add-extrinsic-time-resampling --internal-ile-srate-time-resampling 4096 \ + --calmarg-envelope-directory $(CURDIR)/cal_env --calmarg-n-realizations $(NCAL_DAG) --calmarg-fused-kernel \ + --extrinsic-handoff $(PP_DMARG_FLAGS) $(PP_CALPOST_FLAGS) $(PP_CIP_FLAGS) \ + --force-initial-grid-size $(EXTR_GRID0) --n-output-samples-last $(EXTR_GRIDN) \ + --manual-extra-ile-args "--n-chunk $(EXTR_NCHUNK) --n-max $(EXTR_NMAX)" \ + --internal-force-iterations $(EXTR_NIT) \ + --internal-ile-request-disk 4M --internal-cip-request-disk 4M --internal-general-request-disk 4M + @# AV for the FINAL extrinsic stage (efficiency): post-build search/replace on the generated + @# ILE_extr.sub -- the wide stages keep GMM for the handoff seed, the extraction stage uses AV. + @if [ "$(EXTR_EXTRINSIC_AV)" = "1" ]; then \ + find $(EXTR_RUN) -name 'ILE_extr.sub' -exec sed -i 's/--sampler-method GMM/--sampler-method AV/g' {} + ; \ + echo " [extr] final extrinsic stage -> AV (post-build sed on ILE_extr.sub; wide stages stay GMM)" ; \ + fi + @# the n-max bound must land AFTER the helper's production --n-max 4000000 (argparse last-wins); + @# the manual-extra args are appended last, so the effective n-max is the bounded value. + @awk '{for(i=1;i<=NF;i++)if($$i=="--n-max")v=$$(i+1)} END{print " effective --n-max =",v}' $(EXTR_RUN)/args_ile.txt + @awk '/--n-max/{n=split($$0,a," "); for(i=1;i<=n;i++)if(a[i]=="--n-max")last=a[i+1]} END{exit (last==$(EXTR_NMAX))?0:1}' $(EXTR_RUN)/args_ile.txt \ + || (echo "FAIL: effective --n-max is not the bounded $(EXTR_NMAX) (jobs would run to the 4,000,000 production cap)" && false) + @# confirm the per-job size took (ini value is overridden by the derived ini to $(EXTR_PER_JOB)) + @grep -q -- "--ile-n-events-to-analyze $(EXTR_PER_JOB)" $(EXTR_RUN)/command-single.sh 2>/dev/null \ + || echo " NOTE: verify --ile-n-events-to-analyze=$(EXTR_PER_JOB) in the builder invocation above" + @# confirm the handoff threaded through into the runnable pipeline + @grep -q -- "--sampler-method GMM" $(EXTR_RUN)/args_ile.txt || (echo "FAIL: GMM sampler not set" && false) + @grep -q -- "--extrinsic-proposal-output" $(EXTR_RUN)/args_ile.txt || (echo "FAIL: per-event extrinsic output not threaded" && false) + @grep -q -- "--extrinsic-proposal-breadcrumb" $(EXTR_RUN)/args_ile.txt || (echo "FAIL: extrinsic seed breadcrumb not threaded" && false) + @grep -q -- "--n-chunk $(EXTR_NCHUNK)" $(EXTR_RUN)/args_ile.txt || (echo "FAIL: n-chunk not threaded (NVS 510 watchdog guard)" && false) + @test -s "$(EXTR_RUN)/EXTRCONSOLIDATE.sub" || (echo "FAIL: no EXTRCONSOLIDATE.sub" && false) + @test -s "$(EXTR_RUN)/$(PP_DAG)" || (echo "FAIL: no top-level DAG" && false) + @echo "OK: tiny GMM extrinsic-handoff pipeline built on CI data --" + @echo " $(EXTR_GRID0) initial / $(EXTR_GRIDN) per-gen intrinsic, $(EXTR_PER_JOB) evals/ILE job, n-chunk $(EXTR_NCHUNK)," + @echo " $(EXTR_NIT) iterations (iter-1 ILE seeded by iter-0's EXTRCONSOLIDATE). Submit with: make extr-run" + +# Build + SUBMIT. After it runs, the iteration-1 ILE *.out logs should print +# ' Extrinsic GMM SEEDED from .../extr_consolidated_0.npz for dim-groups [...]' +# and extr_consolidated_0.npz / extr_proposal_0_*.npz should appear in the rundir. +extr-run: extr-run-build + cd $(EXTR_RUN) && $(ENV) condor_submit_dag -f $(PP_DAG) + @echo "Submitted. Watch: condor_q ; tail -f $(EXTR_RUN)/$(PP_DAG).dagman.out" + @echo "Confirm seeding: grep -l 'Extrinsic GMM SEEDED' $(EXTR_RUN)/iteration_1_ile/logs/*.out" + @test -n "$$LIGO_USER_NAME" || echo " *** WARNING: LIGO_USER_NAME unset (jobs may not queue at CIT). ***" + +clean: + rm -f distance_marg.npz out_*.dat *.xml.gz lowsnr.cache snr_*.dat snr-report.txt foo.cache ci_coinc.xml extr_calmarg_ci.ini + rm -rf cal_env lowsnr_mdc rundir_dag rundir_tune rundir_pp rundir_pp_run rundir_pp_pilot rundir_pp_extr rundir_pp_extr_run diff --git a/MonteCarloMarginalizeCode/Code/demo/rift/calmarg/README.md b/MonteCarloMarginalizeCode/Code/demo/rift/calmarg/README.md new file mode 100644 index 000000000..0cf518a6a --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/demo/rift/calmarg/README.md @@ -0,0 +1,293 @@ +# In-loop calibration marginalization demo (H1 · L1 · V1) + +This demo exercises RIFT's **in-loop calibration marginalization** — marginalizing +over detector calibration uncertainty *inside* the ILE GPU likelihood loop, instead +of as a postprocessing reweighting step (`calibration_reweighting.py`). + +It uses the zero-spin synthetic data shipped with the CI +(`.travis/ILE-GPU-Paper/demos/`): a zero-noise injection (`m1=35, m2=30` at 200 Mpc, +network SNR ≈ 17.5) observed in **three detectors (H1, L1, V1)**, with the bundled +`HLV-ILIGO_PSD.xml.gz` PSDs. No real data or proprietary frames are needed. + +The template analyzed is the **injection itself** (`mdc.xml.gz`, a single matched +point), so the signal is actually present and `lnL ≈ ρ²/2 ~ 150` — large enough for the +calibration-marginalization effect to be visible. (Analyzing a far-off intrinsic grid +point instead would give `lnL ~ 0` and hide the effect.) + +## Why in-loop, and what it does + +The calibration model multiplies each detector's data by an uncertain, +frequency-dependent complex factor `C(f)`. Marginalizing over it in postprocessing +reweights extrinsic samples that were drawn *without* calibration knowledge — which +is very inefficient for high-SNR sources or broad calibration priors. + +In RIFT's factored likelihood, applying `C(f)` to the **data** changes only the +data–template term `Q_lm(t) = (t)`; the template–template terms `U, V` +(`rho_sq`) are calibration-independent. So we draw `N` calibration realizations once, +build the per-realization `Q_lm` blocks, and Monte-Carlo marginalize over them on the +GPU while the extrinsic likelihood is evaluated: + +``` +Z_cal(theta) = (1/N) * sum_c integral dt exp( lnL_t(theta, c) ) +``` + +Two implementations are demonstrated (both validated to agree to ~1e-14 on identical +inputs — see `make verify-exact`): + +| path | flag | where it runs | +|-------------|-------------------------------------|--------------------------| +| **baseline**| *(none)* | no calibration marginalization | +| **loop** | `--calibration-envelope-directory` | Option B: Python loop over realizations reusing the existing kernel (CPU or GPU) | +| **fused** | `+ --calibration-fused-kernel` | Option C: a single fused CUDA kernel does Q + distmarg loglikelihood + cal/time log-sum-exp on-board (GPU only) | + +The demo runs with **distance marginalization** on, so the fused path uses the +dedicated fused distmarg kernel. + +## Running + +```bash +make inputs # build the distance-marginalization table + per-IFO cal envelopes +make verify-exact # DETERMINISTIC: loop == fused == reference to ~1e-14 (the rigorous check) +make run-baseline # ILE, no calibration marginalization +make run-loop # ILE + in-loop calmarg, Option B +make run-fused # ILE + in-loop calmarg, Option C (fused kernel) +make compare # print the marginalized lnL from the three runs +# or simply: +make all +``` + +## Make targets reference + +The demo has grown from the single-ILE correctness check into a ladder that goes all the way +to a runnable condor pipeline. Targets, grouped by what they exercise: + +**A. Numerical correctness + single-ILE (no condor)** — the original demo: +| target | what | +|---|---| +| `inputs` | build the distmarg table + per-IFO cal envelopes | +| `verify-exact` | DETERMINISTIC loop == fused == reference to ~1e-14 (the rigorous proof) | +| `run-baseline` / `run-loop` / `run-fused` | one ILE: no cal / loop calmarg / fused calmarg | +| `compare` | print the marginalized lnL from the three runs | +| `all` | inputs + verify-exact + the three runs + compare | +| `lowsnr-inputs` / `low-snr` | fainter copy of the source (~SNR 9) for a robust full-sampler check | + +**B. Direct-ILE DAG + n-max tuning (condor on one GPU, e.g. cardassia)** — hand-rolled DAG, no +pseudo_pipe. `DMARG_DAG`/`NCAL_DAG`/`NMAX_DAG`/`NEFF_DAG`/`NCHUNK_DAG` tunables; `FUSED=1`/`PILOT=1`: +| target | what | +|---|---| +| `dag-build` / `dag-validate` / `dag-run` / `dag` | build / check / submit / build+submit a vanilla fused-calmarg DAG | +| `tune-single` / `tune-condor` | one big ILE (python -u / a single condor submit) to push n_eff up at large `NMAX_DAG` | + +**C. Top-level pipeline via `util_RIFT_pseudo_pipe.py` — OFFLINE build-validate** (no GPU/condor; +confirms everything threads through incl. TIME SAMPLES): +| target | what | +|---|---| +| `pp-build` / `pp-validate` / `pp` | build + validate a full pipeline (AV sampler, calmarg+fused, time-resampling) | +| `extr-build` / `extr-validate` / `extr` | same, with the **extrinsic handoff** (GMM seed) — checks the EXTRCONSOLIDATE node + seed wiring | + +**D. RUNNABLE pipeline on the CI fake data (condor; one GPU on cardassia, or OSG/CIT)**: +| target | what | +|---|---| +| `pp-coinc` | build `ci_coinc.xml` from the injection (`util_SimInspiralToCoinc.py`) | +| `pp-run-build` / `pp-run` | build / build+submit the real pipeline (real cache + PSD + FAKE-STRAIN, calmarg, time-resampling) | +| `pp-run-pilot-build` / `pp-run-pilot` | same with the adaptive cal **pilots** enabled (separate `rundir_pp_pilot`) | +| `extr-run-build` / `extr-run` | tiny GMM **extrinsic-handoff** GPU+condor run (separate `rundir_pp_extr_run`) | + +Runnable-target toggles (override on the make line): +| toggle | default | meaning | +|---|---|---| +| `OSG` | `0` (auto `1` if `SINGULARITY_RIFT_IMAGE` set) | layer on `--use-osg*` + container + frame transfer for CIT (container-only) | +| `PP_PILOT` | `0` | enable the cal pilots on `pp-run` | +| `PP_DMARG` | `0` | OPTIONAL distance marginalization with the fused kernel (`--internal-marginalize-distance`); recommended with `--extrinsic-handoff` | +| `PP_CALPOST` | `1` | write the recovered **calibration posterior** (`__cal.dat`) at the final fairdraw | +| `PP_NIT` | `2` | forced iteration count | + +> The runnable targets each start with their own `rm -rf ` and use **separate** run +> directories, so launching one never clobbers another that is still running. + +**Helper utilities** (also runnable standalone): `util_ExtrinsicConsolidate.py` (pick the best +per-event extrinsic proposal), `util_CalMakePriorBreadcrumb.py` (write/patch a valid iteration-0 +`cal_consolidated_-1.npz` prior placeholder — see "Pilot / OSG notes" below). + +### Backends and review matrix + +The fused path has two interchangeable backends and works with or without distance +marginalization, selected by `BACKEND` and `DMARG`: + +| toggle | values | meaning | +|---|---|---| +| `BACKEND` | `gpu` (default) / `cpu` | CUDA kernels / pure-numpy (laptop, no CUDA) | +| `DMARG` | `1` (default) / `0` | distance-marginalization (fused distmarg kernel) / off (default-helper kernel) | + +The deterministic check covers the whole matrix; e.g. on a laptop with no CUDA: + +```bash +make verify-exact BACKEND=cpu DMARG=1 # CPU, distance marginalization +make verify-exact BACKEND=cpu DMARG=0 # CPU, no distance marginalization +make all DMARG=0 # full non-distmarg end-to-end run +``` + +`BACKEND=cpu` runs on numpy where cupy is absent (a Mac); the full ILE runs use +`--gpu --force-xpy`, which is the numpy code path only on a machine without cupy. +`verify-exact BACKEND=cpu` always uses numpy (the backtest picks the backend directly), +so it is the portable cross-check. + +Generated inputs: +* `distance_marg.npz` — distance-marginalization lookup table (`util_InitMargTable`). +* `cal_env/{H1,L1,V1}.txt` — synthetic calibration envelopes at **GWTC-4/O4a scale** + (amplitude 1-sigma 1–1.6%, phase 1-sigma 1–1.6°, slightly different per IFO; the + LIGO O4a strain calibration error is bounded at ≲2% / ≲2° below 2 kHz, + arXiv:2508.18079). `tools/make_cal_envelopes.py --wide` restores the original + deliberately-broad 5–8% / 3–4.8° envelopes — a STRESS TEST that collapses the cal + n_eff to O(1) at any practical `NCAL` (raw prior-draw marginalization cannot + resolve it; use it only to exercise the error diagnostics or the adaptive pilot). + +### Calibration MC error in the reported sigma + +The sigma column of `out_*.dat` now **includes the calibration MC error in +quadrature** with the extrinsic sampling error (`--calibration-mc-error-extrinsic`, +default on; the probe batch is adaptive and draws distance from the run's own +distance prior). The extrinsic integrator's variance is structurally blind to the +spread over the fixed cal draw set, so the old sigma badly understated the truth +whenever the per-point cal n_eff was small — at `NCAL_DAG=20` with wide envelopes +this produced a 2d lnL surface with ~1.0 point-to-point noise quoted at sigma~0.18. +Each ILE log now prints +`[calmarg error] sigma_lnZ: extrinsic ... (+) cal ... -> total ... ; cal n_eff X / N` +and warns when `cal n_eff < 10` (where the quoted sigma is only a lower bound). + +### Adaptive cal draw count + +The number of cal realizations is no longer trusted as given: after the cal-block +precompute, ILE probes the effective draw count at each intrinsic point +(`[calmarg adapt]` log lines) and **doubles the draw set** (fresh independent +draws, incremental precompute of only the new blocks) until +`--calibration-neff-cal-target` (default 10) is met or +`--calibration-n-realizations-max` (default 8× the initial count) is reached. +`NCAL` therefore sets the *starting* size; 100 is a sensible start with +GWTC-4-scale envelopes, with headroom to 800 by default. + +Each `out_*_0_.dat` row is one intrinsic point. The columns end with +`... lnL sqrt_var ntotal neff`, so the **marginalized lnL is column `[-4]`** and +the **last column is `neff`** (effective sample count, a sampler diagnostic — *not* +the result). `make compare` reads the right column and also prints `neff` and the +sampling error; don't compare the last column. + +## Convergence caveat (read before trusting a single full-sampler lnL) + +This demo analyzes **one** intrinsic point with the matched template, so the extrinsic +likelihood is a single narrow peak. RIFT's adaptive extrinsic sampler can struggle to +lock such a peak robustly from a single point (the full pipeline normally seeds from a +grid of points), and the convergence is sensitive to `NCHUNK`, SNR, and even the GPU +environment. Symptoms of a *non-converged* run: `neff` of order 1 (one draw dominates), +or `neff` large but lnL near 0 (the sampler spread into an off-peak region and missed +the signal). Always sanity-check `sqrt(2*lnLmax)` in the run log — it should be ~the +injected network SNR (≈17.5 here, ≈9 for the low-SNR variant); if it is, the signal was +found and any oddness is marginalization/convergence, not a missing signal. + +Practical guidance: for a robust full-sampler run use the **low-SNR variant** (`make +low-snr`, broad peak) and a modest `NCHUNK` (~1000). For the *rigorous* loop-vs-fused +correctness check that does **not** depend on sampler convergence, use **`make +verify-exact`** (deterministic, ~1e-14). The calibration-marginalization correctness +claim rests on `verify-exact`; the full-sampler runs are an end-to-end illustration. + +## How to read the results + +* **`make verify-exact` is the exact numerical proof.** It bypasses the stochastic + sampler and evaluates loop, fused, and a brute-force reference on identical inputs + using this demo's real lookup table: they agree to ~1e-14. + +* **The full ILE runs are stochastic.** RIFT's GPU Monte-Carlo integrator is **not + bit-reproducible even with `--seed`** (cupy reductions and the sampler are not fully + deterministic). At the small sample counts used here the run-to-run scatter in the + marginalized lnL is a few tenths; loop and fused agree *within that Monte-Carlo + noise*, as does a repeat of either one. Raise `NEFF`/`NMAX` (on a GPU with enough + memory) to shrink the scatter and to resolve the calibration penalty (in-loop + calmarg lowers and broadens the marginalized lnL relative to baseline). + +## Quiet-source variant (low SNR) + +The bundled CI injection is **network SNR ≈ 17.5** (`m1=35, m2=30` at 200 Mpc). For a +full-sampler sanity where loop-vs-fused agreement sits clearly above Monte-Carlo noise, +generate a fainter copy of the same source at larger distance (~SNR 9) and run the +comparison on it: + +```bash +make lowsnr-inputs # writes mdc_lowsnr.xml.gz + lowsnr.cache (3 IFOs), prints the injected SNR +make low-snr # = make all CACHE=$(CURDIR)/lowsnr.cache +# tune the loudness with INJ_DIST (Mpc): larger = quieter +make lowsnr-inputs INJ_DIST=600 # ~SNR 6 +``` + +Nothing binary is committed — the frames/cache are regenerated locally, exactly as the +CI data itself is built (`util_WriteInjectionFile.py` → `util_WriteFrameAndCacheFromXML.sh`). + +## Tunables + +```bash +make all NCAL=100 NCHUNK=4000 NMAX=20000 NEFF=1000 # production-ish (needs a larger GPU) +``` + +`NCAL` (calibration realizations), `NCHUNK`/`NMAX` (extrinsic samples per block / max), +`NEFF` (target effective samples), `SEED`, `DMAX`, `SAMPLER`. + +The demo uses the **adaptive-volume sampler** (`SAMPLER=AV`), the mature/stable GPU +code path. The GMM sampler (`mcsamplerEnsemble`) is newer and heavier on the GPU; if you +hit `CUDA_ERROR_ILLEGAL_ADDRESS` or other GPU instability, stay on AV (override with +`SAMPLER=GMM` only if you specifically want to test it). The fused kernels themselves +also guard against out-of-range window offsets, so a pathological draw can't trigger an +illegal memory access. + +> **Memory note.** The fused/loop precompute holds `N` calibration realizations, so GPU +> memory scales with `NCAL` and `NCHUNK`. On a small card (≈2 GB) keep `NCHUNK` ≲ 1000; +> if you see `Out of memory ...` from cupy, lower `NCHUNK`/`NCAL`. + +## Using this from the full pipeline + +`util_RIFT_pseudo_pipe.py` threads the same options down to ILE: + +``` +--calmarg-envelope-directory DIR # enables in-loop calmarg (per-IFO .txt files) +--calmarg-n-realizations N # default 100 +--calmarg-spline-count M # default 10 +--calmarg-fused-kernel # use the fused GPU kernel (else the loop method) +--calmarg-export-posterior # write the recovered cal posterior at the final fairdraw (see below) +--internal-marginalize-distance # OPTIONAL distance marginalization (composes with the fused kernel) +--calmarg-pilot # adaptive cal pilots: learn a cal proposal and SEED wide_{N+1} +--extrinsic-handoff # GMM-seed handoff: carry the extrinsic posterior between iterations +``` + +These append the corresponding `--calibration-*` / `--extrinsic-*` flags to the ILE arguments. +To live-test on real data, copy your coinc, frames, PSD, and ini, then launch the pipe with +these flags. Design notes for the advanced paths live next to the code: +`RIFT/calmarg/DESIGN_adaptive_driver.md` (cal pilots) and +`RIFT/calmarg/DESIGN_extrinsic_handoff.md` (extrinsic handoff). + +### Recovered calibration posterior + +With `--calmarg-export-posterior` (pipeline) / `--calibration-export-posterior` (ILE), the final +fairdraw stage draws, per output sample, one calibration realization in proportion to its +posterior weight and writes a **self-contained sibling `__cal.dat`** with the +FULL draw — intrinsic + extrinsic + the drawn realization's spline nodes as labeled columns +`cal__amp_` / `cal__phase_`. The recovered cal posterior is just those columns, +plottable with the standard tooling (it should sit inside, and no wider than, the input envelope +band). In the demo this is `PP_CALPOST=1` (default on). + +### Pilot / OSG notes + +- **Iteration-0 placeholder.** On OSG the cal pilots seed wide_{N+1} from a transferred + breadcrumb; iteration 0 has none yet, so pseudo_pipe writes a `cal_consolidated_-1.npz` + *placeholder*. It is a **valid "prior" breadcrumb** (proposal == prior → seeding from it == + cold prior draws, zero weights) so it loads cleanly on any ILE binary. To (re)generate one for + an already-built run dir (e.g. to patch a run launched before this fix, without rebuilding the + container): + ```bash + util_CalMakePriorBreadcrumb.py --calibration-envelope-directory rundir/cal_env \ + --ifo H1 --ifo L1 --ifo V1 --fmin 10 --fmax 2047 --calibration-spline-count 10 \ + --output rundir/cal_consolidated_-1.npz + ``` + (`--ifo` order / `--fmin` / `--fmax` / `--calibration-spline-count` must match the wide-ILE cal + settings so the node dimension `2·spline·n_ifo` lines up.) + +- **Execute-point vs pipeline-writer.** Changes to the ILE binary / likelihood / `RIFT/calmarg` + need a **container rebuild** to take effect on OSG/CIT; changes to `util_RIFT_pseudo_pipe.py` / + `create_event_*` / `dag_utils*` / the Makefile are pipeline-writer only (no rebuild). diff --git a/MonteCarloMarginalizeCode/Code/demo/rift/calmarg/calmarg_ci.ini b/MonteCarloMarginalizeCode/Code/demo/rift/calmarg/calmarg_ci.ini new file mode 100644 index 000000000..a21e4fda9 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/demo/rift/calmarg/calmarg_ci.ini @@ -0,0 +1,67 @@ +# CI-matched RIFT ini for the in-loop calmarg pseudo_pipe RUN target (pp-run). +# Matches the zero-noise synthetic CI data in .travis/ILE-GPU-Paper/demos: 3 IFOs +# (H1,L1,V1) with FAKE-STRAIN channels, srate 4096, seglen 8 (the 8 s frame +# [1000000008,1000000016]; event 1000000014 + buffer 2 - seglen 8 = that frame), +# fmin 10, zero spin (IMRPhenomD-compatible), mc in [23,35]. CLI args on the +# util_RIFT_pseudo_pipe.py command line (--approx, --assume-nospin, calmarg flags, +# time-resampling, ...) win, so keep [rift-pseudo-pipe] minimal. + +[analysis] +ifos=['H1','L1','V1'] +singularity=False +osg=False + +[paths] + +[input] +max-psd-length=10000 + +[condor] +accounting_group=ligo.sim.o4.cbc.pe.rift +accounting_group_user=richard.oshaughnessy + +[datafind] +url-type=file +types = {'H1': 'fake_strain', 'L1': 'fake_strain', 'V1': 'fake_strain'} + +[data] +channels = {'H1': 'H1:FAKE-STRAIN','L1': 'L1:FAKE-STRAIN', 'V1': 'V1:FAKE-STRAIN'} + +[lalinference] +flow = {'H1': 10, 'L1': 10, 'V1': 10} +fhigh = { 'H1': 1700, 'L1': 1700, 'V1': 1700 } + +[engine] +fref=20 +amporder = -1 +seglen = 8 +srate = 4096 +a_spin1-max = 0.0 +a_spin2-max = 0.0 +chirpmass-min = 23.0 +chirpmass-max = 35.0 +comp-min = 1 +comp-max = 1000 +distance-max = 1000 +aligned-spin = +alignedspin-zprior = + +[rift-pseudo-pipe] +# NOTE: disk requests are set from the util_RIFT_pseudo_pipe.py CLI (the Makefile), NOT +# here -- a value in this [rift-pseudo-pipe] section OVERRIDES the CLI, which would defeat +# the container +4GB disk bump. So keep internal-*-request-disk OUT of this ini. +cip-fit-method="rf" +ile-n-eff=10 +l-max=2 +internal-distance-max=1000 +ile-runtime-max-minutes=60 +# ILE jobs are fast here (~1-3 min each), so pack many intrinsic points per condor job to +# minimize per-job startup/queue overhead (50 -> --ile-n-events-to-analyze 50). NOTE: an ini +# value in [rift-pseudo-pipe] OVERRIDES the CLI, so set it here, not on the make line. +ile-jobs-per-worker=50 +internal-propose-converge-last-stage=True +force-eta-range="[0.20,0.24999]" +fmin-template=10 +event-time=1000000014.236547946 +n-output-samples=2000 +use-online-psd=False diff --git a/MonteCarloMarginalizeCode/Code/demo/rift/calmarg/tools/compare_lnL.py b/MonteCarloMarginalizeCode/Code/demo/rift/calmarg/tools/compare_lnL.py new file mode 100644 index 000000000..79ccbb31c --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/demo/rift/calmarg/tools/compare_lnL.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +""" +Compare the marginalized lnL produced by ILE runs in this demo. + +The ILE per-event .dat row ends with four columns: + ... , lnL (=log_res), sqrt_var_over_res, ntotal, neff +so the marginalized lnL is column [-4], its error is [-3], and the LAST column is +neff (effective sample count) -- NOT lnL. (A common foot-gun: grabbing [-1] gives +neff, which scatters run-to-run and is meaningless to compare.) + +This prints lnL (with its sampling error) and neff for each file, and the pairwise +lnL differences (loop vs fused should agree to within ~the sampling error). +""" +import sys +import numpy as np + +if len(sys.argv) < 2: + print("usage: compare_lnL.py label=file.dat [label=file.dat ...]") + sys.exit(1) + +lnL, err, neff = {}, {}, {} +for arg in sys.argv[1:]: + label, fname = arg.split("=", 1) + a = np.atleast_2d(np.genfromtxt(fname)) + lnL[label] = a[:, -4] # log_res = marginalized lnL + err[label] = a[:, -3] # sqrt_var_over_res (sampling error on the integral) + neff[label] = a[:, -1] # effective sample count (diagnostic, NOT a result) + print("{:10s} lnL = {} +- {} (neff = {})".format( + label, + np.array2string(lnL[label], precision=4), + np.array2string(err[label], precision=4), + np.array2string(neff[label], precision=0))) + +labels = list(lnL) +print("\npairwise max|delta lnL| (compare to the sampling errors above):") +for i in range(len(labels)): + for j in range(i + 1, len(labels)): + a, b = lnL[labels[i]], lnL[labels[j]] + n = min(len(a), len(b)) + d = float(np.max(np.abs(a[:n] - b[:n]))) + print(" {:10s} vs {:10s} {:.4f}".format(labels[i], labels[j], d)) diff --git a/MonteCarloMarginalizeCode/Code/demo/rift/calmarg/tools/make_cal_envelopes.py b/MonteCarloMarginalizeCode/Code/demo/rift/calmarg/tools/make_cal_envelopes.py new file mode 100644 index 000000000..577caaa58 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/demo/rift/calmarg/tools/make_cal_envelopes.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +""" +Generate synthetic per-IFO calibration envelope files for the in-loop +calibration-marginalization demo. + +Output format (one file per IFO, named .txt), matching what +RIFT.calmarg.generate_realizations.retrieve_envelope_from_file expects: + + frequency median_mag median_phase 16_mag 16_phase 84_mag 84_phase + +Amplitude factors are centered on 1 (fractional), phase on 0 (radians). +The 16/84 columns are the 1-sigma band edges; RIFT turns (84-16)/2 into a 1-sigma +width per spline node. +""" +import argparse +import os +import numpy as np + +p = argparse.ArgumentParser() +p.add_argument("--out-dir", required=True, help="directory to write .txt files") +p.add_argument("--ifos", default="H1,L1,V1", help="comma-separated IFO names") +p.add_argument("--fmin", type=float, default=5.0) +p.add_argument("--fmax", type=float, default=2048.0) +p.add_argument("--n-freq", type=int, default=60) +p.add_argument("--amp-sigma", type=float, default=0.01, + help="1-sigma fractional amplitude uncertainty (e.g. 0.01 = 1%%). " + "Default is GWTC-4/O4a-scale: LIGO O4a strain calibration error is " + "bounded at <~2%% in amplitude and <~2 deg in phase below 2 kHz " + "(arXiv:2508.18079); with the per-IFO spread below the default gives " + "1-sigma 1.0/1.3/1.6%%.") +p.add_argument("--phase-sigma-deg", type=float, default=1.0, + help="1-sigma phase uncertainty in degrees (GWTC-4-scale default; see --amp-sigma)") +p.add_argument("--wide", action="store_true", + help="STRESS TEST: restore the original deliberately-broad envelopes " + "(5%% amplitude / 3 deg phase base, i.e. 5-8%% / 3-4.8 deg per IFO). " + "At SNR ~17 these collapse the cal n_eff to O(1) at any practical " + "n_cal -- useful only for exercising the error diagnostics, NOT " + "for prior-draw marginalization.") +args = p.parse_args() +if args.wide: + args.amp_sigma, args.phase_sigma_deg = 0.05, 3.0 + +os.makedirs(args.out_dir, exist_ok=True) +f = np.geomspace(args.fmin, args.fmax, args.n_freq) +ph_sigma = np.deg2rad(args.phase_sigma_deg) + +# Give each IFO a slightly different uncertainty so the demo is not degenerate. +ifos = [s.strip() for s in args.ifos.split(",") if s.strip()] +scale = {ifo: 1.0 + 0.3 * i for i, ifo in enumerate(ifos)} + +for ifo in ifos: + a_sig = args.amp_sigma * scale[ifo] + p_sig = ph_sigma * scale[ifo] + med_mag = np.ones_like(f) + med_ph = np.zeros_like(f) + out = np.column_stack([ + f, + med_mag, med_ph, + med_mag - a_sig, med_ph - p_sig, # 16th percentile + med_mag + a_sig, med_ph + p_sig, # 84th percentile + ]) + path = os.path.join(args.out_dir, ifo + ".txt") + np.savetxt(path, out) + print("wrote {} (amp 1-sigma {:.1%}, phase 1-sigma {:.2f} deg)".format( + path, a_sig, np.rad2deg(p_sig))) diff --git a/MonteCarloMarginalizeCode/Code/test/test_container_manifest.py b/MonteCarloMarginalizeCode/Code/test/test_container_manifest.py new file mode 100644 index 000000000..29cdbc489 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/test/test_container_manifest.py @@ -0,0 +1,209 @@ +""" +Tests for container family manifest parsing and the expression-valued +SingularityImage / selective-transfer / require_gpus wiring. + +These run without a real HTCondor pool: the parser + expression builders are +pure, and the integration test inspects the generated ``condor_cmds`` on the +job object returned by ``write_ILE_sub_simple`` (no .sub file or condor needed). + +Run directly: python test/test_container_manifest.py +Or via pytest: pytest test/test_container_manifest.py +""" + +import os +import sys +import textwrap + +import pytest + +yaml = pytest.importorskip("yaml") # manifest parsing requires PyYAML + +import RIFT.misc.container_manifest as cm + + +# --------------------------------------------------------------------------- +# helpers +# --------------------------------------------------------------------------- + +MIXED_MANIFEST = textwrap.dedent( + """ + version: 1 + fallback: ancient + containers: + - label: ancient + image: /cvmfs/sw/rift_ancient_cuda11.sif + cuda_capability_min: 3.0 + cuda_capability_max: 7.0 + - label: modern + image: osdf:///igwn/rift_modern_cuda12.sif + cuda_capability_min: 7.0 + """ +) + +ALL_CVMFS_MANIFEST = textwrap.dedent( + """ + version: 1 + fallback: ancient + containers: + - label: ancient + image: /cvmfs/sw/rift_ancient.sif + cuda_capability_min: 3.0 + - label: modern + image: /cvmfs/sw/rift_modern.sif + cuda_capability_min: 7.0 + """ +) + + +def _write(tmp_path, text, name="fam.yaml"): + p = tmp_path / name + p.write_text(text) + return str(p) + + +# --------------------------------------------------------------------------- +# 1. parser +# --------------------------------------------------------------------------- + +def test_parser_sorts_and_resolves_fallback(tmp_path): + m = cm.load_container_manifest(_write(tmp_path, MIXED_MANIFEST)) + # sorted by capability descending + assert [c["label"] for c in m["containers"]] == ["modern", "ancient"] + assert m["fallback"] == "ancient" + assert m["capability_attr"] == cm.DEFAULT_CAPABILITY_ATTR + + +def test_parser_default_fallback_is_lowest(tmp_path): + # no explicit fallback -> most-compatible (lowest-min) container + text = MIXED_MANIFEST.replace("fallback: ancient\n", "") + m = cm.load_container_manifest(_write(tmp_path, text)) + assert m["fallback"] == "ancient" + + +def test_parser_rejects_unknown_fallback(tmp_path): + text = MIXED_MANIFEST.replace("fallback: ancient", "fallback: nope") + with pytest.raises(cm.ContainerManifestError): + cm.load_container_manifest(_write(tmp_path, text)) + + +def test_parser_rejects_empty(tmp_path): + with pytest.raises(cm.ContainerManifestError): + cm.load_container_manifest(_write(tmp_path, "version: 1\ncontainers: []\n")) + + +def test_parser_rejects_missing_image(tmp_path): + text = "containers:\n - label: x\n cuda_capability_min: 5.0\n" + with pytest.raises(cm.ContainerManifestError): + cm.load_container_manifest(_write(tmp_path, text)) + + +# --------------------------------------------------------------------------- +# 2. expressions +# --------------------------------------------------------------------------- + +def test_image_expression(tmp_path): + m = cm.load_container_manifest(_write(tmp_path, MIXED_MANIFEST)) + expr = cm.build_singularity_image_expr(m) + assert expr == ( + 'ifThenElse(TARGET.GPUs_Capability >= 7.0, ' + '"./rift_modern_cuda12.sif", "/cvmfs/sw/rift_ancient_cuda11.sif")' + ) + # an expression must NOT be a quoted string literal + assert not expr.startswith('"') + + +def test_transfer_expression_is_comma_free_ternary(tmp_path): + m = cm.load_container_manifest(_write(tmp_path, MIXED_MANIFEST)) + expr = cm.build_transfer_input_expr(m) + assert expr == ( + '$$([ (TARGET.GPUs_Capability >= 7.0 ? ' + '"osdf:///igwn/rift_modern_cuda12.sif" : "") ])' + ) + # the token sits inside a comma-separated transfer_input_files list, so it + # must contain no commas of its own + assert "," not in expr + + +def test_transfer_expression_none_when_all_in_place(tmp_path): + m = cm.load_container_manifest(_write(tmp_path, ALL_CVMFS_MANIFEST)) + assert cm.build_transfer_input_expr(m) is None + + +def test_require_gpus_floor(tmp_path): + m = cm.load_container_manifest(_write(tmp_path, MIXED_MANIFEST)) + assert cm.build_require_gpus_floor(m) == "Capability >= 3.0" + + +def test_capability_attr_env_override(tmp_path, monkeypatch): + monkeypatch.setenv("RIFT_GPU_CAPABILITY_ATTR", "CUDACapability") + m = cm.load_container_manifest(_write(tmp_path, MIXED_MANIFEST)) + assert "TARGET.CUDACapability >=" in cm.build_singularity_image_expr(m) + + +# --------------------------------------------------------------------------- +# 3-5. integration with write_ILE_sub_simple (inspect generated condor_cmds) +# --------------------------------------------------------------------------- + +def _make_ile_job(tmp_path, monkeypatch, singularity_image): + """Call write_ILE_sub_simple in an isolated cwd; return its condor_cmds dict. + + Skips if the dag_utils_generic backend cannot be imported in this env. + """ + dag = pytest.importorskip("RIFT.misc.dag_utils_generic") + monkeypatch.chdir(tmp_path) + job, _ = dag.write_ILE_sub_simple( + tag="ILE", + log_dir=str(tmp_path) + "/", + exe="/usr/bin/true", + arg_str="--foo bar", + transfer_files=["../all.net"], + use_singularity=True, + singularity_image=singularity_image, + request_gpu=True, + cache_file="local.cache", + ) + return dict(job.condor_cmds) + + +def test_integration_family_mixed(tmp_path, monkeypatch): + monkeypatch.setenv( + "RIFT_REQUIRE_GPUS", '(DeviceName=!="Tesla K10.G1.8GB")' + ) + cmds = _make_ile_job(tmp_path, monkeypatch, _write(tmp_path, MIXED_MANIFEST)) + + img = cmds["MY.SingularityImage"] + assert img.startswith("ifThenElse(") # expression, not a literal + assert not img.startswith('"') + + # selective transfer: exactly one $$() token, whole family NOT dumped + tif = cmds["transfer_input_files"] + assert tif.count("$$([") == 1 + assert "/cvmfs/sw/rift_ancient_cuda11.sif" not in tif # cvmfs image not transferred + assert tif.count("osdf:///igwn/rift_modern_cuda12.sif") == 1 + + # floor composed with (not replacing) the user's RIFT_REQUIRE_GPUS + rg = cmds["require_gpus"] + assert "Capability >= 3.0" in rg + assert 'DeviceName=!="Tesla K10.G1.8GB"' in rg + assert "&&" in rg + + +def test_integration_all_cvmfs_no_transfer_token(tmp_path, monkeypatch): + cmds = _make_ile_job(tmp_path, monkeypatch, _write(tmp_path, ALL_CVMFS_MANIFEST)) + assert "$$([" not in cmds.get("transfer_input_files", "") + # still an expression-valued image + a capability floor + assert cmds["MY.SingularityImage"].startswith("ifThenElse(") + assert "Capability >= 3.0" in cmds["require_gpus"] + + +def test_backward_compat_single_sif(tmp_path, monkeypatch): + monkeypatch.delenv("RIFT_REQUIRE_GPUS", raising=False) + cmds = _make_ile_job(tmp_path, monkeypatch, "./foo.sif") + # byte-identical legacy behavior: quoted literal, no $$() token, no floor + assert cmds["MY.SingularityImage"] == '"./foo.sif"' + assert "$$([" not in cmds.get("transfer_input_files", "") + assert "require_gpus" not in cmds + + +if __name__ == "__main__": + sys.exit(pytest.main([os.path.abspath(__file__), "-v"])) diff --git a/MonteCarloMarginalizeCode/Code/test/test_distance_grid.py b/MonteCarloMarginalizeCode/Code/test/test_distance_grid.py new file mode 100644 index 000000000..497da59d2 --- /dev/null +++ b/MonteCarloMarginalizeCode/Code/test/test_distance_grid.py @@ -0,0 +1,95 @@ +import numpy as np + +from RIFT.misc.distance_grid import ( + DISTANCE_GRID_FIELDS, + build_distance_grid, + load_distance_grid, + reconstruct_marginal_lnL, + _logsumexp, +) + + +def _volumetric_log_prior(d, d_min=1.0, d_max=4000.0): + norm = (d_max**3 - d_min**3) / 3.0 + return 2.0*np.log(d) - np.log(norm) + + +def test_distance_grid_reconstructs_marginal_lnL_with_sampling_prior(): + rng = np.random.default_rng(1234) + distance = rng.lognormal(mean=np.log(450.0), sigma=0.22, size=200) + ln_weights = -0.5 * ((distance - 430.0) / 35.0) ** 2 + lnL_marginal = 37.25 + + grid = build_distance_grid( + distance, + ln_weights, + lnL_marginal, + sigmaL=0.012, + params={"m1": 35.0, "m2": 28.0, "s1z": 0.1, "s2z": -0.2}, + ln_prior_d_at_samples=_volumetric_log_prior(distance), + n_grid=40, + ) + + assert grid.dtype.names == DISTANCE_GRID_FIELDS + assert np.all(np.diff(grid["dist"]) >= 0) + assert np.all(grid["dist_weight"] > 0) + assert np.isclose(reconstruct_marginal_lnL(grid), lnL_marginal) + assert np.all(grid["m1"] == 35.0) + assert np.all(grid["s1z"] == 0.1) + + +def test_distance_grid_roundtrip_preserves_reconstruction(tmp_path): + distance = np.linspace(100.0, 900.0, 21) + ln_weights = -0.5 * ((distance - 500.0) / 120.0) ** 2 + lnL_marginal = -12.5 + grid = build_distance_grid( + distance, ln_weights, lnL_marginal, 0.2, {}, + ln_prior_d_at_samples=_volumetric_log_prior(distance), + n_grid=10, + ) + + from pathlib import Path + fname = Path(str(tmp_path)) / "event_0_.dgrid" + from RIFT.misc.distance_grid import save_distance_grid + save_distance_grid(fname, grid) + loaded = load_distance_grid(fname) + + assert loaded.dtype.names == DISTANCE_GRID_FIELDS + assert np.isclose(reconstruct_marginal_lnL(loaded), lnL_marginal) + + +def test_exported_lnL_is_pure_likelihood(): + """exp(lnL) is the pure extrinsic-marginalized likelihood density in d; + integrating it against a different distance prior gives a different + marginal.""" + rng = np.random.default_rng(7) + n = 4000 + d_min, d_max = 100.0, 1500.0 + distance = rng.uniform(d_min, d_max, size=n) + ln_L_pure = -0.5 * ((distance - 600.0)/80.0)**2 + 5.0 + ln_pi = _volumetric_log_prior(distance, d_min, d_max) + ln_q = -np.log(d_max - d_min) + ln_w = ln_L_pure + ln_pi - ln_q + lnL_marg_mc = _logsumexp(ln_w) - np.log(n) + grid = build_distance_grid(distance, ln_w, lnL_marg_mc, 0.0, {}, + ln_prior_d_at_samples=ln_pi, n_grid=40) + # default reconstruction matches the original marginal + assert np.isclose(reconstruct_marginal_lnL(grid), lnL_marg_mc) + # reconstruction with a flat-in-d prior gives the pure-likelihood integral + flat_log_prior = lambda d: np.full_like(np.asarray(d, float), -np.log(d_max - d_min)) + lnL_flat = reconstruct_marginal_lnL(grid, ln_prior_d=flat_log_prior) + expected = np.log(np.sqrt(2*np.pi)*80.0*np.exp(5.0)/(d_max - d_min)) + assert abs(lnL_flat - expected) < 0.1, (lnL_flat, expected) + # and is meaningfully different from the volumetric answer + # closed-form ratio at d~600 over [100,1500]: log(d^2*(d_max-d_min)*3/(d_max^3-d_min^3)) + expected_ratio = np.log(600.0**2 * (d_max-d_min) * 3.0 / (d_max**3 - d_min**3)) + assert abs((lnL_flat - lnL_marg_mc) - (-expected_ratio)) < 0.1 + + +def test_legacy_distance_grid_without_weights_uses_trapezoid(): + dtype = [("lnL", float), ("dist", float)] + grid = np.zeros(5, dtype=dtype) + grid["dist"] = np.linspace(0.0, 1.0, 5) + grid["lnL"] = 0.0 + + assert np.isclose(reconstruct_marginal_lnL(grid), 0.0) diff --git a/MonteCarloMarginalizeCode/Code/test/test_like_and_samp.py b/MonteCarloMarginalizeCode/Code/test/test_like_and_samp.py index 895a764ec..d8eb68095 100755 --- a/MonteCarloMarginalizeCode/Code/test/test_like_and_samp.py +++ b/MonteCarloMarginalizeCode/Code/test/test_like_and_samp.py @@ -81,6 +81,7 @@ import pickle import numpy as np +from RIFT.precision import RiftFloat try: import cupy @@ -846,7 +847,7 @@ def likelihood_function(right_ascension, declination,t_ref, phi_orb, inclination global nEvals global lnLOffsetValue # use EXTREMELY many bits - lnL = np.zeros(right_ascension.shape,dtype=np.float128) + lnL = np.zeros(right_ascension.shape,dtype=RiftFloat) i = 0 # if opts.rotate_sky_coordinates: # print " -Sky ring width ", np.std(declination), " note contribution from floor is of order p_floor*(pi)/sqrt(12) ~ 0.9 pfloor" @@ -887,7 +888,7 @@ def likelihood_function(right_ascension, declination,t_ref, phi_orb, inclination global nEvals global lnLOffsetValue # use EXTREMELY many bits - lnL = np.zeros(right_ascension.shape,dtype=np.float128) + lnL = np.zeros(right_ascension.shape,dtype=RiftFloat) i = 0 # if opts.rotate_sky_coordinates: # print " -Sky ring width ", np.std(declination), " note contribution from floor is of order p_floor*(pi)/sqrt(12) ~ 0.9 pfloor" @@ -964,7 +965,7 @@ def likelihood_function(right_ascension, declination,t_ref, phi_orb, inclination global nEvals global lnLOffsetValue # use EXTREMELY many bits - lnL = np.zeros(right_ascension.shape,dtype=np.float128) + lnL = np.zeros(right_ascension.shape,dtype=RiftFloat) i = 0 # if opts.rotate_sky_coordinates: # print " -Sky ring width ", np.std(declination), " note contribution from floor is of order p_floor*(pi)/sqrt(12) ~ 0.9 pfloor" diff --git a/MonteCarloMarginalizeCode/Code/test/test_like_and_samp_simplified.py b/MonteCarloMarginalizeCode/Code/test/test_like_and_samp_simplified.py index 4f7477648..4bcd3845e 100755 --- a/MonteCarloMarginalizeCode/Code/test/test_like_and_samp_simplified.py +++ b/MonteCarloMarginalizeCode/Code/test/test_like_and_samp_simplified.py @@ -65,6 +65,7 @@ import sys import numpy as np +from RIFT.precision import RiftFloat from glue.lal import Cache from glue.ligolw import utils, lsctables, table, ligolw, git_version @@ -639,7 +640,7 @@ def likelihood_function(right_ascension, declination,t_ref, phi_orb, inclination global nEvals global lnLOffsetValue # use EXTREMELY many bits - lnL = np.zeros(right_ascension.shape,dtype=np.float128) + lnL = np.zeros(right_ascension.shape,dtype=RiftFloat) i = 0 # if opts.rotate_sky_coordinates: # print " -Sky ring width ", np.std(declination), " note contribution from floor is of order p_floor*(pi)/sqrt(12) ~ 0.9 pfloor" @@ -824,4 +825,3 @@ def likelihood_function(right_ascension, declination,t_ref, phi_orb, inclination # utils.write_fileobj(xmldoc,sys.stdout) xmlutils.append_likelihood_result_to_xmldoc(xmldoc, np.log(res), **{"mass1": m1, "mass2": m2}) utils.write_filename(xmldoc, opts.points_file_base+".xml.gz", gz=True) - diff --git a/README.md b/README.md index 7e9e46709..62af2336c 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,28 @@ Repository for the rapid_PE / RIFT code, developed at RIT (forked long ago from Please see INSTALL.md +### Pixi development environments + +The root `pixi.toml` provides reproducible local-development environments for +the RIFT science stack. The default environment intentionally keeps `swig` +below 4.4.0 so local installs do not accidentally pick up the SWIG 4.4.x +binding-generation behavior tracked in issue #136: + +```bash +pixi install +pixi run install-rift +pixi run import-check +``` + +CI also resolves a comparison environment with `swig >=4.4.0`: + +```bash +pixi run -e swig-pre44 swig-version +pixi run -e swig-post44 swig-version +pixi run -e swig-pre44 import-check +pixi run -e swig-post44 import-check +``` + ## Science If you are using this code for a production analysis, please contact us to make sure you follow the instructions included here! diff --git a/containers/README.md b/containers/README.md new file mode 100644 index 000000000..56eaa9e81 --- /dev/null +++ b/containers/README.md @@ -0,0 +1,201 @@ +# RIFT containers + +This directory holds the multi-architecture container build and the "container +family" deployment mechanism. It has two related but independent pieces: + +1. **Multi-target build** — build a *family* of RIFT containers (different base + image + cupy/CUDA variant, targeting different GPU compute capabilities) from + one template. +2. **Family deployment** — let `SINGULARITY_RIFT_IMAGE` point at a YAML + *manifest* describing that family, so each Condor job picks the right image + for the machine it lands on. + +The top-level [`rift_container.def`](../rift_container.def) is unchanged and +remains the default single-image build. + +--- + +## 1. Building a family + +``` +containers/build_family.sh [--render-only] [OUTPUT_DIR] +``` + +- [`rift_container.def.in`](rift_container.def.in) is a template with + `@@BASE_IMAGE@@` / `@@CUPY_PKG@@` placeholders (apptainer `.def` files take no + build args, so we render then build). +- [`build_family.sh`](build_family.sh) holds the build `MATRIX`. The **first** + entry is the default and uses the current production base image, so the family + always includes a broadly-compatible image for older machines. Add rows to + target more architectures. +- `--render-only` writes the per-entry `.def` files without invoking apptainer + (useful in CI or on a machine without apptainer). +- Each build also emits a `rift_container_family.generated.yaml` stub — fill in + each `image:` with where you published the `.sif` (a CVMFS path or `osdf://` + URL), and you have a deployable manifest. + +All matrix entries share the pip set in +[`requirements-container.txt`](requirements-container.txt) (the cupy wheel is the +only per-entry difference). That file is the **single source of truth** also +consumed by the CI dependency canary (below). `build_family.sh` stages it into +each image via the `.def`'s `%files` section, so the build does **not** depend on +the cloned RIFT branch shipping the file. + +### Build troubleshooting + +**`proot error: ptrace(TRACEME): Operation not permitted` / +`mksquashfs command failed`** (seen on shared clusters such as CIT). Apptainer +has no usable user namespaces or setuid install, so it falls back to its +unprivileged `proot` build engine — which cannot run the `mksquashfs` helper. +**Setting `PROOT_NO_SECCOMP=1` is not sufficient** (it silences the seccomp +message but proot still fails to exec mksquashfs). Avoid the proot path instead: + +1. **Build with `--fakeroot`** (recommended; the IGWN/CIT path): + + ```console + containers/build_family.sh --fakeroot ./container_family + ``` + + Requires `/etc/subuid` + `/etc/subgid` entries for your user and unprivileged + user namespaces enabled (check: `grep $USER /etc/subuid` and + `apptainer build --fakeroot` on a tiny def). This produces a real `.sif` + without proot. + +2. **If even `--fakeroot` is unavailable, build a `--sandbox`** (a directory). + This skips `mksquashfs` entirely, so it sidesteps the failing step: + + ```console + containers/build_family.sh --sandbox ./container_family + # later, on a host where apptainer can make a SIF: + apptainer build rift_container_default.sif ./container_family/rift_container_default/ + ``` + +3. **Or build elsewhere** — on a node/registry with proper apptainer (or build + the OCI image with Docker/podman, push to a registry, then + `apptainer pull`/`build` the `.sif` on a capable host). + +`build_family.sh` still exports `PROOT_NO_SECCOMP=1` as a harmless best-effort, +and passes any extra `--flag` you give it straight through to `apptainer build`. +If a build runs out of space mid-way, point `APPTAINER_TMPDIR` at a large local +disk. + +--- + +## 2. Deploying a family via a manifest + +Set `SINGULARITY_RIFT_IMAGE` to a `.yaml`/`.yml` manifest instead of a single +`.sif`. Everything else (pseudo_pipe, `--use-singularity`, etc.) is unchanged — +the manifest is detected by file extension. A plain `.sif` path or single +`osdf://` URL keeps the **exact** legacy single-image behavior; the manifest path +is never consulted in that case. + +See [`rift_container_family.yaml`](rift_container_family.yaml) for a worked +example. Schema: + +| field | meaning | +|-------------------|---------| +| `version` | manifest schema version (currently `1`) | +| `capability_attr` | machine ClassAd attribute the selection expression tests (default `GPUs_Capability`) | +| `fallback` | label of the catch-all image (innermost `else`); **must be CPU-safe** | +| `containers[]` | the family | +| ↳ `label` | human id; also referenced by `fallback` | +| ↳ `image` | a CVMFS/local path (referenced in place, lazy-fetched) **or** an `osdf://` URL (selectively transferred) | +| ↳ `cuda_capability_min` | inclusive lower capability bound for this image | +| ↳ `cuda_capability_max` | informational upper bound (`null` = open-ended) | +| ↳ `note` | free-text | + +> **Keep the family consistent.** A *single* `SINGULARITY_BASE_EXE_DIR` is +> applied to **every** image in the family — the ILE/CIP jobs locate the +> executable as `SINGULARITY_BASE_EXE_DIR + `, with no per-image +> override. So all images in a manifest **must install RIFT's executables at the +> same in-container path** (and share a common layout/Python/entrypoints). Build +> them from the same `rift_container.def.in` template (`build_family.sh` does +> this) and do **not** hand-mix images with different internal layouts. The same +> applies to `SINGULARITY_BASE_EXE_DIR_HYPERPIPE` if you use hyperpipe. + +### What the pipeline generates + +For the ILE (and CIP) Condor submit, a manifest produces: + +- **`MY.SingularityImage`** — an *unquoted* `ifThenElse(...)` expression that + selects the highest-capability image the matched machine can run, defaulting to + the `fallback` image (also used when the capability attribute is `undefined`, + e.g. on a CPU-only CIP slot — hence the fallback must be CPU-safe): + + ``` + ifThenElse(TARGET.GPUs_Capability >= 8.0, "./rift_container_modern.sif", "/cvmfs/.../rift_container_default.sif") + ``` + +- **Selective transfer** — only `osdf://` images get fetched, and only on the + machine that selected them, via one HTCondor `$$()` match-time token appended + to `transfer_input_files` (CVMFS/local images are referenced in place and never + transferred, so the *whole family is never pulled*): + + ``` + $$([ (TARGET.GPUs_Capability >= 8.0 ? "osdf:///.../rift_container_modern.sif" : "") ]) + ``` + + `request_disk` is **not** auto-sized (image sizes are unknown at submit time) — + size it to your largest single transferred image. + +- **`require_gpus` floor** — `Capability >= `, + composed (`&&`) with any user-supplied `RIFT_REQUIRE_GPUS` (which today you use + to block incompatible hosts by `DeviceName`). Both apply; neither is dropped. + +### HTCondor GPU attribute names — important + +Two different namespaces are in play and are kept separate: + +- The **image-selection `ifThenElse`** reads the *machine* ClassAd. Default + `GPUs_Capability` (advertised on the OSG; some pools differ). Override per-run + with `RIFT_GPU_CAPABILITY_ATTR`, or per-manifest with `capability_attr`. Verify + on your pool: + + ``` + condor_status -constraint 'TotalGPUs > 0' -autoformat GPUs_DeviceName GPUs_Capability GPUs_GlobalMemoryMb + ``` + + Not every GPU host advertises this; on such hosts the expression collapses to + the fallback image and the `require_gpus` floor does the steering. + +- The **`require_gpus` floor** uses the require_gpus sub-ad attribute + `Capability` (unprefixed — *not* `TARGET.`, *not* `GPUs_`). + +### Requirements + +- `PyYAML` must be importable wherever the pipeline is built (only when a + manifest is actually used). Single-`.sif` runs never require it. + +### Validation status + +Validated on a real HTCondor pool + GPU (a cap-3.0 machine): + +- The advertised attributes are `GPUs_Capability` (machine ad) and `Capability` + (require_gpus sub-ad) — matching the defaults above. +- The `require_gpus` capability floor matches a compatible GPU and correctly + *excludes* an incompatible one (`Capability >= 7.0` did not match a cap-3.0 + GPU), so the floor steers GPU selection as intended. +- The `$$([ ifThenElse(TARGET.GPUs_Capability >= …, …) ])` transfer token is + honored at match time: only the matched image's URL is selected/transferred. +- The empty-result case — when a manifest *mixes* CVMFS and osdf entries and a + CVMFS branch is selected, the `$$()` token expands to `""` — is **tolerated**: + the empty entry is skipped and the job runs clean. (So mixed manifests are + safe; you do *not* need uniform all-osdf / all-cvmfs retrieval.) + +One item still needs a real **OSG/GWMS** pilot (a local pool has no singularity +wrapper to exercise it): that the pilot evaluates the expression-valued +`MY.SingularityImage` and honors a relative `./name.sif` produced by it. + +--- + +## 3. CI dependency-resolution canary + +The default container build uses *unpinned* deps, so a fresh upstream release +(e.g. `swig>=4.4.0`, see issue #136) can silently break RIFT and we only find out +when a container rebuild fails. The `container-dep-canary` job in +[`.github/workflows/ci.yml`](../.github/workflows/ci.yml) installs the unpinned +[`requirements-container.txt`](requirements-container.txt) set (minus the +GPU-only cupy wheel) and the pixi `swig-post44` lane, then runs the import check — +on every push/PR **and weekly** — to flag such breakage early. It is +non-blocking (advisory): it tracks upstream changes outside any PR author's +control. diff --git a/containers/build_family.sh b/containers/build_family.sh new file mode 100755 index 000000000..3b9348c8b --- /dev/null +++ b/containers/build_family.sh @@ -0,0 +1,129 @@ +#!/bin/bash +# Build a *family* of RIFT containers from containers/rift_container.def.in, one +# per build-matrix entry (different base image + cupy/CUDA variant, targeting +# different GPU compute capabilities). +# +# Usage: +# containers/build_family.sh [--render-only] [--fakeroot] [--sandbox] \ +# [other apptainer build flags] [OUTPUT_DIR] +# +# --render-only render the per-entry .def files but do NOT run apptainer +# (useful on machines without apptainer, or in CI) +# --fakeroot pass --fakeroot to `apptainer build` (RECOMMENDED on shared +# clusters such as CIT: avoids the unprivileged `proot` engine, +# whose mksquashfs step fails. Needs /etc/subuid + /etc/subgid +# entries for your user and unprivileged user namespaces.) +# --sandbox build a writable directory instead of a .sif. This SKIPS the +# mksquashfs step entirely, so it sidesteps the proot squashfs +# failure when even --fakeroot is unavailable. Convert to .sif +# later on a capable host: apptainer build out.sif sandbox_dir/ +# any other --flag is passed straight through to `apptainer build`. +# OUTPUT_DIR where rendered .def and built images land (default: ./container_family) +# +# The DEFAULT (first) matrix entry keeps the current production base image, so +# the family always includes a broadly-compatible image for older machines. +# Add rows to MATRIX to target more architectures. +# +# After building, publish the .sif files to CVMFS or osdf and edit +# containers/rift_container_family.yaml so SINGULARITY_RIFT_IMAGE can point at it. +set -euo pipefail + +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +TEMPLATE="${HERE}/rift_container.def.in" + +RENDER_ONLY=0 +SANDBOX=0 +BUILD_OPTS=() +OUTPUT_DIR="./container_family" +for arg in "$@"; do + case "$arg" in + --render-only) RENDER_ONLY=1 ;; + --sandbox) SANDBOX=1 ;; + --fakeroot) BUILD_OPTS+=(--fakeroot) ;; + --*) BUILD_OPTS+=("$arg") ;; # passthrough to apptainer build + *) OUTPUT_DIR="$arg" ;; + esac +done + +# Build matrix: "label|base_image|cupy_pkg|cuda_capability_min|cuda_capability_max" +# - The first entry is the DEFAULT and uses the current production base image. +# - cuda_capability_max may be empty (open-ended); it is informational and is +# echoed into the manifest stub for convenience. +MATRIX=( + "default|nvidia/cuda:11.8.0-runtime-ubuntu22.04|cupy-cuda11x|3.5|8.0" + "modern|nvidia/cuda:12.4.1-runtime-ubuntu22.04|cupy-cuda12x|8.0|" +) + +# Workaround for unprivileged apptainer builds that fall back to the `proot` +# engine (no usable user namespaces / setuid apptainer, common on shared +# clusters like CIT): proot's mksquashfs step is blocked by seccomp on ptrace +# ("proot error: ptrace(TRACEME): Operation not permitted"). PROOT_NO_SECCOMP=1 +# disables proot's seccomp filtering and lets the build finish. Harmless when +# proot is not used; override by exporting it yourself before running. +# A fully privileged / fakeroot / userns-capable build does not need this. +export PROOT_NO_SECCOMP="${PROOT_NO_SECCOMP:-1}" + +mkdir -p "${OUTPUT_DIR}" +MANIFEST_STUB="${OUTPUT_DIR}/rift_container_family.generated.yaml" +{ + echo "# Auto-generated manifest stub from containers/build_family.sh." + echo "# Edit 'image:' to the published CVMFS path or osdf:// URL of each .sif." + echo "# IMPORTANT: a single SINGULARITY_BASE_EXE_DIR is applied to the whole" + echo "# family -- all images must install RIFT executables at the SAME" + echo "# in-container path. These are built from one template, so they are" + echo "# consistent; do not hand-swap in images with a different layout." + echo "version: 1" + echo "capability_attr: GPUs_Capability" + echo "fallback: default" + echo "containers:" +} > "${MANIFEST_STUB}" + +for row in "${MATRIX[@]}"; do + IFS='|' read -r label base cupy cap_min cap_max <<< "$row" + rendered="${OUTPUT_DIR}/rift_container_${label}.def" + sif="${OUTPUT_DIR}/rift_container_${label}.sif" + + echo ">>> Rendering ${label}: base=${base} cupy=${cupy}" + sed -e "s#@@BASE_IMAGE@@#${base}#g" \ + -e "s#@@CUPY_PKG@@#${cupy}#g" \ + -e "s#@@REQFILE@@#${HERE}/requirements-container.txt#g" \ + "${TEMPLATE}" > "${rendered}" + + { + echo " - label: ${label}" + echo " image: REPLACE_ME/rift_container_${label}.sif # publish to CVMFS or osdf" + echo " cuda_capability_min: ${cap_min}" + if [ -n "${cap_max}" ]; then + echo " cuda_capability_max: ${cap_max}" + else + echo " cuda_capability_max: null" + fi + echo " note: \"base=${base}, ${cupy}\"" + } >> "${MANIFEST_STUB}" + + if [ "${RENDER_ONLY}" -eq 1 ]; then + echo " (render-only) wrote ${rendered}" + continue + fi + if ! command -v apptainer >/dev/null 2>&1; then + echo " apptainer not found; wrote ${rendered} (build skipped)" >&2 + continue + fi + + if [ "${SANDBOX}" -eq 1 ]; then + target="${OUTPUT_DIR}/rift_container_${label}" # writable directory (no mksquashfs) + sandbox_opt=(--sandbox) + else + target="${sif}" + sandbox_opt=() + fi + echo ">>> Building ${target}${BUILD_OPTS[*]:+ (opts: ${BUILD_OPTS[*]})}" + # ${arr[@]+"${arr[@]}"} expands safely even when the array is empty under set -u + apptainer build ${sandbox_opt[@]+"${sandbox_opt[@]}"} ${BUILD_OPTS[@]+"${BUILD_OPTS[@]}"} "${target}" "${rendered}" +done + +echo +echo "Done. Rendered defs (and any built .sif) are in ${OUTPUT_DIR}/" +echo "Manifest stub: ${MANIFEST_STUB}" +echo "Next: publish the .sif images, fill in their 'image:' locations, and point" +echo "SINGULARITY_RIFT_IMAGE at the resulting .yaml manifest." diff --git a/containers/requirements-container.txt b/containers/requirements-container.txt new file mode 100644 index 000000000..e90cf5e6b --- /dev/null +++ b/containers/requirements-container.txt @@ -0,0 +1,27 @@ +# Shared pip dependency set for the RIFT container builds. +# +# SINGLE SOURCE OF TRUTH: the multi-target build (containers/rift_container.def.in, +# which stages this file into the image via its %files section) and the CI +# "dependency-resolution canary" (.github/workflows/ci.yml :: container-dep-canary) +# both install from this file, so the canary exercises the same unpinned set the +# family containers ship. (The top-level rift_container.def keeps an equivalent +# inline list for the default single build.) +# +# NOTE: the GPU-specific cupy wheel (cupy-cuda11x vs cupy-cuda12x) is NOT listed +# here -- it varies per build-matrix entry and is installed by the .def itself. +# The canary has no GPU, so it skips cupy entirely. +# +# Intentionally UNPINNED (mirrors rift_container.def). The canary's whole job is +# to catch when a fresh upstream release of one of these (e.g. swig>=4.4.0 via a +# transitive build, lalsuite, numpy) breaks RIFT -- see issue #136 -- before it +# surprises a container rebuild. +asimov>=0.5.6 +asimov-gwdata>=0.4.0 +gwdatafind==1.2.0 +gwosc>=0.7.1 +lalsuite>=7.26 +numpy>=1.24.4 +natsort +pybind11>=2.12 +scipy>=1.9.3 +pyseobnr diff --git a/containers/rift_container.def.in b/containers/rift_container.def.in new file mode 100644 index 000000000..3d881c426 --- /dev/null +++ b/containers/rift_container.def.in @@ -0,0 +1,74 @@ +# Parameterized apptainer definition for the RIFT container *family*. +# +# This is a TEMPLATE. Apptainer .def files take no build args, so +# containers/build_family.sh renders a concrete .def per build-matrix entry by +# substituting the @@PLACEHOLDERS@@ below, then runs `apptainer build`. +# +# Placeholders: +# @@BASE_IMAGE@@ - docker base image (e.g. nvidia/cuda:11.8.0-runtime-ubuntu22.04) +# @@CUPY_PKG@@ - cupy wheel matched to the base CUDA version (cupy-cuda11x / cupy-cuda12x) +# +# The top-level rift_container.def is left in place as the default single build; +# this template + build_family.sh is the multi-target path. +Bootstrap: docker +From: @@BASE_IMAGE@@ + +%files + # Stage the shared dependency list from the HOST build tree into the image, + # so the build does NOT depend on the cloned RIFT branch carrying this file + # (the clone below may be a branch/release that predates it). build_family.sh + # fills in the absolute host path of containers/requirements-container.txt. + @@REQFILE@@ /opt/requirements-container.txt + +%post + # Update the system and install essential libraries + apt-get update -y + apt-get install -y \ + build-essential \ + cmake \ + g++ \ + wget \ + python3.10 \ + python3.10-venv \ + python3-pip \ + curl \ + bc \ + locales \ + git \ + libkrb5-dev \ + libgsl-dev + + # Configure locale + locale-gen en_US.UTF-8 + + # Ensure Python symlink is in place + ln -s /usr/bin/python3.10 /usr/local/bin/python3 + ln -s /usr/bin/python3 /usr/local/bin/python + + # Set up RIFT installation, using MAIN SOURCE. Modify if you want a release version, or a different branch! + cd /opt + mkdir installed_RIFT + cd installed_RIFT + git clone https://github.com/oshaughn/research-projects-RIT.git + cd research-projects-RIT + #git checkout rift_O4c + pip3 install --upgrade pip + pip3 install --upgrade setuptools --break-system-packages + pip3 install -e . + + # GPU-specific cupy variant, matched to the base image CUDA version. + pip3 install @@CUPY_PKG@@ + + # Shared dependency set -- single source of truth, also exercised by the CI + # dependency-resolution canary (containers/requirements-container.txt). + # Staged into the image via the %files section above (independent of the + # cloned branch). + pip3 install -r /opt/requirements-container.txt + +%environment + # Set environment variables + alias python=python3 + +%labels + org.rift.base @@BASE_IMAGE@@ + org.rift.cupy @@CUPY_PKG@@ diff --git a/containers/rift_container_family.yaml b/containers/rift_container_family.yaml new file mode 100644 index 000000000..1a87b1a5e --- /dev/null +++ b/containers/rift_container_family.yaml @@ -0,0 +1,52 @@ +# Example RIFT container *family* manifest. +# +# Point SINGULARITY_RIFT_IMAGE at a copy of this file (a .yaml / .yml path) to +# deploy a family of containers instead of a single .sif. The pipeline turns it +# into an expression-valued MY.SingularityImage that picks the right image per +# matched machine's GPU capability, a selective ($$()) transfer for osdf images, +# and a require_gpus capability floor. +# +# A plain .sif path or single osdf:// URL keeps the legacy single-image behavior +# (this file is NOT consulted in that case). +# +# See containers/README.md for the full schema and the HTCondor GPU-attribute +# caveat. +# +# IMPORTANT -- keep the family CONSISTENT. A single SINGULARITY_BASE_EXE_DIR is +# applied to *every* image in the family (the ILE/CIP jobs locate the executable +# as SINGULARITY_BASE_EXE_DIR + , with no per-image override). So all +# images listed below MUST install RIFT's executables at the SAME in-container +# path (and otherwise share a common layout/Python/entrypoints). Build them from +# the same containers/rift_container.def.in template (build_family.sh does this) +# -- do NOT mix images with different internal layouts. Same goes for +# SINGULARITY_BASE_EXE_DIR_HYPERPIPE if you use hyperpipe. + +version: 1 + +# Machine ClassAd attribute the image-selection ifThenElse tests. Default +# GPUs_Capability (advertised on the OSG; verify on your pool with e.g. +# condor_status -constraint 'TotalGPUs > 0' -af GPUs_DeviceName GPUs_Capability +# ). Overridable per-run via the RIFT_GPU_CAPABILITY_ATTR env var. +capability_attr: GPUs_Capability + +# Innermost else-branch of the selection expression: used when the machine +# advertises no/low capability (and on CPU-only CIP slots). MUST be the +# CPU-safe / most broadly compatible image. +fallback: default + +containers: + # Default, broadly-compatible image for older machines. Referenced in place + # on CVMFS -- never transferred; CVMFS lazy-fetches it only when selected. + - label: default + image: /cvmfs/singularity.opensciencegrid.org/oshaughn/rift_container_default.sif + cuda_capability_min: 3.5 + cuda_capability_max: 8.0 + note: "base=nvidia/cuda:11.8.0-runtime-ubuntu22.04, cupy-cuda11x" + + # Newer image for higher-capability GPUs. Delivered via osdf: only the + # matched machine fetches it (selective $$() transfer). + - label: modern + image: osdf:///igwn/staging/oshaughn/rift_containers/rift_container_modern.sif + cuda_capability_min: 8.0 + cuda_capability_max: null + note: "base=nvidia/cuda:12.4.1-runtime-ubuntu22.04, cupy-cuda12x" diff --git a/docs/source/containers.rst b/docs/source/containers.rst new file mode 100644 index 000000000..59f5cf4ac --- /dev/null +++ b/docs/source/containers.rst @@ -0,0 +1,224 @@ +Containers and multi-architecture deployment +============================================= + +RIFT runs its compute jobs (ILE, CIP) inside a Singularity/Apptainer container +on HTCondor pools such as the OSG. Historically the environment variable +``SINGULARITY_RIFT_IMAGE`` names a **single** image, and every job is pinned to +it:: + + export SINGULARITY_RIFT_IMAGE=/cvmfs/singularity.opensciencegrid.org/.../rift:production + +That still works exactly as before. This page documents two additions: + +* a **container *family*** — point ``SINGULARITY_RIFT_IMAGE`` at a YAML + *manifest* describing several images that target different GPU compute + capabilities, and let HTCondor pick the right one per matched machine; and +* a **multi-target build** that produces such a family from one template. + +.. note:: + + If ``SINGULARITY_RIFT_IMAGE`` is a plain ``.sif`` path or a single + ``osdf://`` URL, behavior is **unchanged** — the manifest machinery is never + engaged. A manifest is recognized purely by its ``.yaml`` / ``.yml`` suffix. + + +Deploying a container family +---------------------------- + +Set ``SINGULARITY_RIFT_IMAGE`` to a manifest file instead of a single image:: + + export SINGULARITY_RIFT_IMAGE=`pwd`/rift_container_family.yaml + +Everything else — ``util_RIFT_pseudo_pipe.py``, ``--use-singularity``, +``--use-osg`` — is identical. When the pipeline builds the ILE/CIP submit +files it reads the manifest and emits an *expression-valued* container +selection (see `What the pipeline generates`_ below). + +Manifest format +~~~~~~~~~~~~~~~ + +.. code-block:: yaml + + version: 1 + + # Machine ClassAd attribute the selection expression tests. + # Default GPUs_Capability (see "GPU attribute names" below). + capability_attr: GPUs_Capability + + # Catch-all image (innermost else of the selection); MUST be CPU-safe, + # since it is also used when no GPU capability is advertised. + fallback: default + + containers: + # Broadly-compatible image for older machines. On CVMFS: referenced in + # place and lazy-fetched (only the selected image is ever pulled), never + # transferred. + - label: default + image: /cvmfs/singularity.opensciencegrid.org/oshaughn/rift_container_default.sif + cuda_capability_min: 3.5 # inclusive lower bound for this image + cuda_capability_max: 8.0 # informational; null = open-ended + note: "cupy-cuda11x, ubuntu22.04/cuda11.8" + + # Newer image for higher-capability GPUs. Delivered via osdf: only the + # matched machine fetches it (selective transfer). + - label: modern + image: osdf:///igwn/staging/oshaughn/rift_containers/rift_container_modern.sif + cuda_capability_min: 8.0 + cuda_capability_max: null + note: "cupy-cuda12x, ubuntu22.04/cuda12.4" + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - Field + - Meaning + * - ``version`` + - Manifest schema version (currently ``1``). + * - ``capability_attr`` + - Machine ClassAd attribute the selection expression tests (default + ``GPUs_Capability``). + * - ``fallback`` + - ``label`` of the catch-all image (innermost ``else``); **must be + CPU-safe**. + * - ``containers[].label`` + - Human id; also referenced by ``fallback``. + * - ``containers[].image`` + - A CVMFS/local path (referenced in place, lazy-fetched) **or** an + ``osdf://`` URL (selectively transferred). + * - ``containers[].cuda_capability_min`` + - Inclusive lower capability bound for this image. + * - ``containers[].cuda_capability_max`` + - Informational upper bound (``null`` = open-ended). + * - ``containers[].note`` + - Free text. + +A starting manifest lives at :code:`containers/rift_container_family.yaml` in the +source tree. + +.. warning:: + + **Keep the family consistent.** A *single* ``SINGULARITY_BASE_EXE_DIR`` is + applied to **every** image in the family — the ILE/CIP jobs locate the + executable as ``SINGULARITY_BASE_EXE_DIR + ``, with no per-image + override. Every image in a manifest **must install RIFT's executables at the + same in-container path** (and share a common layout / Python / entrypoints). + Build them from the same ``rift_container.def.in`` template + (``build_family.sh`` does this); do **not** hand-mix images with different + internal layouts. The same applies to ``SINGULARITY_BASE_EXE_DIR_HYPERPIPE`` + if you use hyperpipe. + + +What the pipeline generates +--------------------------- + +For a manifest, the ILE (and CIP) Condor submit files get: + +* **``MY.SingularityImage``** — an *unquoted* ``ifThenElse`` expression that + selects the highest-capability image the matched machine can run, falling back + to the ``fallback`` image (also used when the capability attribute is + ``undefined``, e.g. on a CPU-only CIP slot — hence the fallback must be + CPU-safe):: + + ifThenElse(TARGET.GPUs_Capability >= 8.0, "./rift_container_modern.sif", "/cvmfs/.../rift_container_default.sif") + +* **Selective transfer** — only ``osdf://`` images are fetched, and only on the + machine that selected them, via a single HTCondor ``$$()`` match-time token + appended to ``transfer_input_files``. CVMFS/local images are referenced in + place and never transferred, so the **whole family is never pulled**:: + + $$([ (TARGET.GPUs_Capability >= 8.0 ? "osdf:///.../rift_container_modern.sif" : "") ]) + + ``request_disk`` is **not** auto-sized — set it to your largest single + transferred image. + +* **``require_gpus`` floor** — ``Capability >= ``, + combined (``&&``) with any ``RIFT_REQUIRE_GPUS`` you set. Both apply; neither + is dropped. This stops jobs matching a GPU that *no* image in the family + supports. + + +GPU attribute names +------------------- + +Two different ClassAd namespaces are involved, and they are kept separate: + +* The **image selection** ``ifThenElse`` reads the *machine* ad. The default + attribute is ``GPUs_Capability``. Override it per run with the environment + variable ``RIFT_GPU_CAPABILITY_ATTR``, or per manifest with ``capability_attr``. + Verify what your pool advertises:: + + condor_status -constraint 'TotalGPUs > 0' -autoformat GPUs_DeviceName GPUs_Capability GPUs_GlobalMemoryMb + + Not every GPU host advertises this; on such hosts the expression collapses to + the fallback image and the ``require_gpus`` floor does the steering. + +* The **``require_gpus`` floor** uses the require_gpus sub-ad attribute + ``Capability`` (unprefixed — *not* ``TARGET.``, *not* ``GPUs_``). + +.. note:: + + These mechanisms have been validated on a real HTCondor pool + GPU: the + attribute names, the ``require_gpus`` floor (matching a compatible GPU and + excluding an incompatible one), the ``$$()`` match-time image selection, and + tolerance of the empty-result case for a manifest that mixes CVMFS and + ``osdf`` entries. The remaining item for a first real OSG run is that the + pilot evaluates the expression-valued ``MY.SingularityImage`` and honors a + relative ``./name.sif`` produced by it. + + +Building a container family +--------------------------- + +The build lives under :code:`containers/`: + +* :code:`rift_container.def.in` — an Apptainer definition template with + ``@@BASE_IMAGE@@`` / ``@@CUPY_PKG@@`` placeholders. +* :code:`build_family.sh` — renders one ``.def`` per build-matrix entry and runs + ``apptainer build``. The **first** matrix entry keeps the current production + base image, so the family always includes a broadly-compatible image for older + machines. +* :code:`requirements-container.txt` — the shared, unpinned pip dependency set + (the cupy wheel is the only per-entry difference). + +.. code-block:: console + + # render the per-entry .def files only (no apptainer needed) + containers/build_family.sh --render-only ./container_family + + # render and build each .sif (requires apptainer) + containers/build_family.sh ./container_family + + # on shared clusters (e.g. CIT), build with --fakeroot to avoid the + # unprivileged proot engine (whose mksquashfs step fails): + containers/build_family.sh --fakeroot ./container_family + +Each run also writes a ``rift_container_family.generated.yaml`` stub: fill in +each ``image:`` with where you published the ``.sif`` (a CVMFS path or +``osdf://`` URL) and you have a deployable manifest. + +.. note:: + + On clusters without setuid apptainer or unprivileged user namespaces, a plain + build falls back to the ``proot`` engine and fails at ``mksquashfs`` + (``ptrace(TRACEME): Operation not permitted``). Use ``--fakeroot`` (needs + ``/etc/subuid`` + ``/etc/subgid`` entries), or ``--sandbox`` to build a + directory that skips ``mksquashfs`` and convert it to a ``.sif`` later on a + capable host. See the build-troubleshooting section of + :code:`containers/README.md`. + +The top-level :code:`rift_container.def` is unchanged and remains the default +single-image build. + + +Catching dependency breakage early (CI canary) +---------------------------------------------- + +The container ships an *unpinned* dependency set and clones RIFT at build time, +so a fresh upstream release (for example ``swig>=4.4.0``) can silently break +RIFT and only surface when a container rebuild fails. Two **non-blocking** CI +jobs guard against this — ``container-dep-canary`` (installs the same unpinned +``containers/requirements-container.txt`` set and runs the import check) and +``container-swig-canary`` (exercises the ``pixi`` ``swig-post44`` lane). They +run on every push/PR and on a weekly schedule, so a breaking upstream release is +flagged even with no RIFT commit. diff --git a/docs/source/index.rst b/docs/source/index.rst index fb3cdd005..36a4080aa 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -21,6 +21,7 @@ Rapid inference via Iterative FiTting: this algorithm provides a framework for e examples-ini examples-noini osg + containers injections plotting hyperpipe diff --git a/docs/source/osg.rst b/docs/source/osg.rst index 04b29d456..c2e671f7f 100644 --- a/docs/source/osg.rst +++ b/docs/source/osg.rst @@ -33,6 +33,12 @@ absolutely essential to extremely helpful: RIFT_GETENV=LD_LIBRARY_PATH,PATH,PYTHONPATH,*RIFT*,LIBRARY_PATH SINGULARITY_RIFT_IMAGE=/cvmfs/singularity.opensciencegrid.org/james-clark/research-projects-rit/rift:production +.. note:: + + ``SINGULARITY_RIFT_IMAGE`` may also point at a YAML *container family* + manifest (instead of a single image) to deploy images matched to each + machine's GPU capability. See :doc:`containers`. + Additionally, if you are using a waveform model implemented in `gwsignal`, you must export an extra environment variable: diff --git a/pixi.lock b/pixi.lock new file mode 100644 index 000000000..33cdd9a5a --- /dev/null +++ b/pixi.lock @@ -0,0 +1,18127 @@ +version: 7 +platforms: +- name: linux-64 +- name: osx-64 +- name: osx-arm64 +environments: + default: + channels: + - url: https://conda.anaconda.org/conda-forge/ + indexes: + - https://pypi.org/simple + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.15.3-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aom-3.9.1-hac33072_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/astropy-base-7.2.0-py312h4f23490_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/astropy-healpix-1.1.3-py312h4f23490_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.9.6-hb9c0fe4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.13-h2c9d079_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.12.6-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.2-h8b1a151_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.9-h841be55_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.10.10-hf621c6d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.26.1-hc87160b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.14.0-ha25ca29_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.11.5-h9b5df67_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.4-h8b1a151_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.10-h8b1a151_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.37.3-hb153662_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.747-h133b1ee_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.16.2-h206d751_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.13.3-hed0cdb0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.16.0-hf824e48_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.13.0-ha7a2c86_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.14.0-h539c000_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.5.0-py312h90b7ffd_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bcrypt-5.0.0-py312h868fb18_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-hed03a55_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312hdb49522_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brunsli-0.1-hd1e3526_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-blosc2-3.0.3-hc31b594_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py312h460c074_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cfitsio-4.6.2-ha0b56bc_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/charls-2.4.3-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/chealpix-3.31.0-ha55a0e1_8.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312h0a2e395_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.14.0-py312h8a5da7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cryptography-48.0.0-py312ha4b625e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.1.0-py312h4c3975b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dav1d-1.2.1-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fftw-3.3.11-nompi_h3b011a4_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.0-h27c8c51_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py312h8a5da7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h480dda7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/google-crc32c-1.8.0-py312h03f33d3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-hecca717_2.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/h5py-3.13.0-nompi_py312hedeef09_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-12.2.0-h15599e2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.3-nompi_h2d575fe_109.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/healpy-1.18.1-py312he9d304c_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/htcondor-24.12.4-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/htcondor-classads-24.12.4-hf1419ba_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/htcondor-cli-24.12.4-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/htcondor-utils-24.12.4-h4a68bad_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/htgettoken-2.6-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/igwn-ligolw-2.1.1-py312h53c857e_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/igwn-segments-2.1.1-py312h53c857e_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/imagecodecs-2026.5.10-py312he34094b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/jq-1.8.1-h73b1eb8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/jxrlib-1.1-hd590300_3.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/kiwisolver-1.5.0-py312h0a2e395_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lal-7.6.1-fftw_py312ha96d5c7_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalapps-10.0.2-py312h760c983_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalburst-2.0.6-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalframe-3.0.6-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalinference-4.1.8-nompi_py312h1d0e7ed_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalinference-data-4.1.8-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalinspiral-5.0.2-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalmetaio-4.0.5-py312h66e93f0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalpulsar-7.1.0-py312he8860a6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalpulsar-data-7.1.0-ha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalsimulation-6.1.0-py312h09b83b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalsimulation-data-6.1.0-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h0c24ade_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ldas-tools-al-2.7.0-hd827ec0_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ldas-tools-framecpp-2.9.3-he85ded8_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.1.0-hdb68285_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_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/libarrow-23.0.1-hf605819_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-23.0.1-h635bf11_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-compute-23.0.1-h53684a4_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-23.0.1-h635bf11_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-23.0.1-hb4dd7c2_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libavif16-1.4.1-hcfa2d63_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-7_h4a7cf45_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libboost-1.88.0-hed09d94_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libboost-python-1.88.0-py312hf890105_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-7_h0358290_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp21.1-21.1.8-default_h99862b1_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-22.1.6-default_h746c552_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcondor_utils-24.12.4-he8e5dd1_0.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-hb8b1518_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.18.0-h4e3cde8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.127-hb03c661_0.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/libegl-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libframel-8.48.5-ha02e5fa_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h73754d4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.2-h32235b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.39.0-h9d11ab5_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.39.0-hdbdcf42_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.78.1-h1d1128b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.4.0-h10be129_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.1.4.1-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.11.2-h174a0a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblal-7.6.1-fftw_hcbe9c14_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblalburst-2.0.6-h3773ae6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblalframe-3.0.6-h7c287a6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblalinference-4.1.8-h3773ae6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblalinspiral-5.0.2-h3773ae6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblalmetaio-4.0.5-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblalpulsar-7.1.0-h9345056_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblalsimulation-6.1.0-py312h09b83b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-7_h47877c9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.8-hf7376ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm22-22.1.6-hf7376ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmetaio-8.5.1-h0b0be96_1003.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/libntlm-1.8-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-1.21.0-h9692893_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-headers-1.21.0-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-23.0.1-h7376487_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_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/libpq-18.1-h5c52fec_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.33.5-h2b00c02_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h0dc7533_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.22-h280c20c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.1-h0c1763c_0.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-15.2.0-h934c35e_19.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_19.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.1-h9d88235_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.1-h5347b49_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.341.0-h5279c79_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.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.36-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.1-hca5e8e5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.1-ha9997c6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.1-h26afc86_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzopfli-1.0.3-h9c3ff4c_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/ligo-segments-1.4.0-py312h66e93f0_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ligo.skymap-2.3.0-py312h36415c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/llvmlite-0.47.0-py312h7424e68_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-4.4.5-py312h3d67a73_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.9-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.9-py312he3d6523_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.1.2-py312hd9148b4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/munge-0.5.16-h63a00c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numba-0.65.1-py312hd1dde6f_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-1.26.4-py312heda63a1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/oniguruma-6.9.10-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjph-0.27.3-h8d634f6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.3.0-h21090e2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.3-py312h8ecdadd_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.46-h1321c63_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.2.0-py312h50c33e8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.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/psutil-7.2.2-py312h5253ce2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-23.0.1-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-23.0.1-py312h2054cf2_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyerfa-2.0.1.5-py310h32771cd_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pynacl-1.6.2-py312hf34ed73_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.10.1-py312h9da60e5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.13-hd63d673_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-gssapi-1.11.1-py312h7cea900_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-htcondor-24.12.4-py312h1e35698_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lal-7.6.1-fftw_py312heccaa44_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalburst-2.0.6-py312h3684b61_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalframe-3.0.6-py312hc0a28a1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalinference-4.1.8-py312hc0a28a1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalinspiral-5.0.2-py312hc0a28a1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalmetaio-4.0.5-py312hc0a28a1_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalpulsar-7.1.0-py312hc0a28a1_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalsimulation-6.1.0-py312h09b83b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-ligo-lw-1.8.3-py312h66e93f0_4.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/qhull-2020.2-h434a139_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.10.1-hca0d9c9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rav1e-0.8.1-h1fbca29_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h5301d42_1.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/regex-2026.5.9-py312h4c3975b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/reproject-0.19.0-py312h4f23490_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.1-h1cbb8d7_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.8.0-np2py312h3226591_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py312h54fa4ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scitokens-cpp-1.4.0-h096d96b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py312h383787d_2.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/svt-av1-4.0.1-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/swig-4.3.1-hf1419ba_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.5-py312h4c3975b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py312h4c3975b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.25.0-hd6090a7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-2.2.1-py312h4c3975b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.47-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.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_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.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-libxtst-1.2.5-hb9d3cd8_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zfp-1.0.1-h909a3a2_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.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/antlr-python-runtime-4.9.3-pyhd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/arviz-0.23.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/astroplan-0.10.1-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/astropy-iers-data-0.2026.5.25.1.14.13-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.0-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/corner-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.3.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.3.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-image-2025.11.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dateparser-1.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_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/distributed-2026.3.0-pyhc364b38_0.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/dqsegdb2-1.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/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/fsspec-2026.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/future-1.0.0-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwdatafind-2.1.1-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwosc-0.8.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwpy-4.0.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h5netcdf-1.8.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hydra-core-1.3.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.15-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/igwn-auth-utils-1.4.0-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/imageio-2.37.0-pyhfb79c49_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_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/invoke-3.0.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jplephem-2.24-pyha4b2019_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/lalsuite-7.25-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ligo-gracedb-2.15.7-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ligotimegps-2.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/lscsoft-glue-4.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mpmath-1.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.21.2-pyhcf101f3_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/networkx-3.6.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/omegaconf-2.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/paramiko-5.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pims-0.7-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.1.1-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.6-pyhcf101f3_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/ptemcee-1.0.0-py_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/pyavm-0.9.9-pyhc455866_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.3-pyhfe8187e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-global-3.0.3-pyh648e204_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.13.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.0.3-pyhc364b38_1.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_abi-3.12-8_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/safe-netrc-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/scitokens-1.9.7-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_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/slicerator-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tifffile-2026.3.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.3-pyh8f84b5b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzlocal-5.3.1-pyh8f84b5b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.47.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-einstats-0.9.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zarr-3.1.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda + - pypi: https://files.pythonhosted.org/packages/00/e2/1cb7cfb88fd3866062977a484bc0dda4e165361e949cc8e270302d05b007/spinsfast-2022.4.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/07/40/4058220b3d60890b62e0a2e8212e2546695827cf85e6186405ecf8ef33f1/numpy_quaternion-2024.0.13-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/09/c9/a9271d39c86d28d8bfacec831e416e42eb6d154f03605429631c4c08138b/pygsl_lite-0.1.8.tar.gz + - pypi: https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/20/7a/1c6e3562dfd8950adbb11ffbc65d21e7c89d01a6e4f137fa981056de25c5/gitpython-3.1.50-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/26/96/92119e6b279a88547a51eb99726ecae1ada839bf4fbcc2856503e2558e80/blosc2-4.3.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/27/ae/defd665dbbeb2fffa077491365ed160acaec49274ce8d4b979f55db71f18/ndindex-1.10.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/42/c2/b602d2a5bbaaf9db1cff035ee6ddea76938c0b25ac1e6ac8ca288a073b47/otter_report-0.4.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/46/75/505cd72dfde3a1d7e719f840a2aa656a8c6b4c7b1b8c604a2d587cb1fc52/precession-2.1.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/59/91/aa6bde563e0085a02a435aa99b49ef75b0a4b062635e606dab23ce18d720/inflection-0.5.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/59/f3/77f85bfac22ee84b231ee8b147cdd948f679edf7d5a622acf9169d2a9b63/juliacall-0.9.34-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/66/8f/2515bd2f110f6316f39568e25943577effffa5283b6376969e8277ba330d/sxs-2025.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/66/92/8e7104d2cf40ba2f88528c01e8a063aba7b223a21c0310fcf043b441f5da/sxscatalog-3.0.28-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6d/a3/e2d6b17b02b5c52ce6c68fce7f2f190e796c2c1c5419e675f6a947fdb78c/qnm-0.4.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6f/33/81570e825bdb2c0d7fa705087b704007bb7898c7dbb7a64f868da59252b3/pyseobnr-0.3.6.tar.gz + - pypi: https://files.pythonhosted.org/packages/78/17/853354204e1ca022d6b7d011ca7f3206c4f8faa3cc743e92609b49c1d83f/tinydb-4.8.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7d/4a/75aadc3b17430d4d9f3ad7bf0332a22b7ef78312e2b5b70774d2a301b9ec/spherical-1.0.18-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/81/68/dddd76117df2ef14c943c6bbb6618be5c9401280046f4ddfc9fb4596a1b8/statsmodels-0.14.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/86/9d/1f4495bf047e61e904a69242941aca04ad3c7453639d9019c5d8facb3862/juliapkg-0.1.23-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/f1/2033813456213ecfe8ccab18d78ef7b00af70d049c916cb167d592bdd6da/scri-2022.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/90/ad/cba91b3bcf04073e4d1655a5c1710ef3f457f56f7d1b79dcc3d72f4dd912/plotly-6.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/97/8d/17e56f2208b885aa8462277ae75a9bc82f7877bb52d88690636246853032/requests_pelican-0.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/99/29/c2dc674ea70fa9a4819417289a9c0d3e4780835beeed573eb66964cfb763/tables-3.11.1-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a6/24/4d91e05817e92e3a61c8a21e08fd0f390f5301f1c448b137c57c4bc6e543/semver-3.0.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ad/28/9f3700cb784c0f0475de7074ed489702d5c6fb63a3df56b4cb4333b055fc/liquidpy-0.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/8a/eafc962e29c7fe800c702b4ae09cc358f1cbe40089e7a8cdac4f5b4c8c7a/requests_scitokens-0.2.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bc/b2/d213f02254956b40f70dfaf2ba21ed0d1b7ed54b7cf361865d9d95fae867/asimov-0.6.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bd/b1/6bc28d9441c48744b6ecfb46af928fca427a3213282efa0faa5bea7eefd8/pesummary-1.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/9b/7a0e11d7858feac24372ae770575802833436a050f93dfd012d8025e4ae9/deepdish-0.3.7-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d6/c6/7a6d8f56d3efe2ddb2bc659c2b1f9ba01b8a27174733526b57e181ba23b0/spherical_functions-2023.0.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d9/43/560e9ba23c02c904b5934496486d061bcb14cd3ebba2e3cf0e2dccb6c22b/numexpr-2.14.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/df/b1/3e3144c3ada2ae1a60a378396629064ef14e425e356c8e7c1db3a50ef351/python_gitlab-8.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e0/a9/023730ba63db1e494a271cb018dcd361bd2c917ba7004c3e49d5daf795a2/py_cpuinfo-9.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e3/47/a8d68aed3f6cb55bae90f504cd5ea3698a923c5fb6e9690726359e28eb1c/asimov_gwdata-0.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/70/ba4b949bdc0490ab78d545459acd7702b211dfccf7eb89bbc1060f52818d/patsy-1.0.2-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fd/86/e74734fcc564c7c9dfb47758ea9699c5294c68979fe0d0017454f3ca3f54/quaternionic-1.0.17-py3-none-any.whl + osx-64: + - conda: https://conda.anaconda.org/conda-forge/noarch/antlr-python-runtime-4.9.3-pyhd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/arviz-0.23.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/astroplan-0.10.1-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/astropy-iers-data-0.2026.5.25.1.14.13-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.0-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/corner-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.3.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.3.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-image-2025.11.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dateparser-1.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_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/distributed-2026.3.0-pyhc364b38_0.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/dqsegdb2-1.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/future-1.0.0-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwdatafind-2.1.1-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwosc-0.8.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwpy-4.0.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h5netcdf-1.8.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hydra-core-1.3.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.15-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/igwn-auth-utils-1.4.0-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/imageio-2.37.0-pyhfb79c49_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_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/invoke-3.0.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jplephem-2.24-pyha4b2019_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/lalsuite-7.25-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ligo-gracedb-2.15.7-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ligotimegps-2.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/lscsoft-glue-4.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mpmath-1.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.21.2-pyhcf101f3_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/networkx-3.6.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/omegaconf-2.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/paramiko-5.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pims-0.7-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.1.1-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.6-pyhcf101f3_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/ptemcee-1.0.0-py_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/pyavm-0.9.9-pyhc455866_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.3-pyhfe8187e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-global-3.0.3-pyh648e204_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.13.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.0.3-pyhc364b38_1.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_abi-3.12-8_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/safe-netrc-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/scitokens-1.9.7-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_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/slicerator-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tifffile-2026.3.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.3-pyh8f84b5b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzlocal-5.3.1-pyh8f84b5b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.47.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-einstats-0.9.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zarr-3.1.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aom-3.9.1-hf036a51_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/astropy-base-7.2.0-py312h391ab28_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/astropy-healpix-1.1.3-py312h8ab2c85_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-auth-0.9.6-hbd79662_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-cal-0.9.13-hea39f9f_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-common-0.12.6-h8616949_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-compression-0.3.2-hb9ea233_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-event-stream-0.5.9-h8efd969_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-http-0.10.10-h8f73dec_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-io-0.26.1-hc95b61d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-mqtt-0.14.0-h2b5127a_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-s3-0.11.5-hafc236b_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-sdkutils-0.2.4-h901532c_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-checksums-0.2.10-h31279ed_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-crt-cpp-0.37.3-h4bfe737_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-sdk-cpp-1.11.747-h5d703ad_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/azure-core-cpp-1.16.2-h87f1c7e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/azure-identity-cpp-1.13.3-h1135191_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/azure-storage-blobs-cpp-12.16.0-hefc3566_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/azure-storage-common-cpp-12.13.0-h74781cd_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/azure-storage-files-datalake-cpp-12.14.0-h2303994_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/backports.zstd-1.5.0-py312h5f4ecc6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/bcrypt-5.0.0-py312h8a6388b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/blosc-1.21.6-hd145fbb_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-1.2.0-hf139dec_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-bin-1.2.0-h8616949_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-python-1.2.0-py312h4b46afd_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/brunsli-0.1-ha00ef93_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/c-ares-1.34.6-hb5e19a0_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/c-blosc2-3.0.3-h32e32c0_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/cffi-2.0.0-py312he90777b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/cfitsio-4.6.2-ha7f915d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/charls-2.4.3-hcc62823_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/chealpix-3.31.0-h93aae7a_8.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/contourpy-1.3.3-py312hb0c38da_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.14.0-py312heb39f77_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/cryptography-48.0.0-py312h1af399d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/cytoolz-1.1.0-py312h1a1c95f_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/dav1d-1.2.1-h0dc2134_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/fftw-3.3.11-nompi_h54214ab_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/fonttools-4.63.0-py312heb39f77_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/freetype-2.14.3-h694c41f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/geos-3.14.1-he483b9e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/gflags-2.2.2-hac325c4_1005.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/giflib-5.2.2-h10d778d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/glog-0.7.1-h2790a97_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/google-crc32c-1.8.0-py312hb9001e9_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/gsl-2.7-h93259b0_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-64/h5py-3.13.0-nompi_py312hea5ca7c_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-1.14.3-nompi_h1607680_109.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/healpy-1.18.1-py312h9f11423_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/htcondor-24.12.4-py312hb401068_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/htcondor-classads-24.12.4-hf470585_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/htcondor-cli-24.12.4-py312hb401068_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/htcondor-utils-24.12.4-h1cc2291_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/htgettoken-2.6-py312hb401068_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-75.1-h120a0e1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/igwn-ligolw-2.1.1-py312h0b9663a_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/igwn-segments-2.1.1-py312h0b9663a_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/imagecodecs-2026.5.10-py312hc8b613d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/jq-1.8.1-h2287256_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/jxrlib-1.1-h10d778d_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/kiwisolver-1.5.0-py312hb1dc2e7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/krb5-1.21.3-h37d8d59_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lal-7.6.1-fftw_py312h6f020d4_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalapps-10.0.2-py312h72dd72b_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalburst-2.0.6-py312h01d7ebd_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalframe-3.0.6-py312h01d7ebd_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalinference-4.1.8-nompi_py312h34ae946_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalinference-data-4.1.8-h694c41f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalinspiral-5.0.2-py312h01d7ebd_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalmetaio-4.0.5-py312h01d7ebd_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalpulsar-7.1.0-py312h5a434c1_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalpulsar-data-7.1.0-h694c41f_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalsimulation-6.1.0-py312haa07450_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalsimulation-data-6.1.0-h694c41f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lcms2-2.19.1-h5ea7634_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/ldas-tools-al-2.7.0-hb7f9d93_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/ldas-tools-framecpp-2.9.3-hf716561_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lerc-4.1.0-h35c7297_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20260107.1-cxx17_h7ed6875_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libaec-1.1.5-he7c3a48_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-23.0.1-h47227bc_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-acero-23.0.1-hc9ab1f6_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-compute-23.0.1-h3b2c5b4_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-dataset-23.0.1-hc9ab1f6_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-substrait-23.0.1-h613493e_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libavif16-1.4.1-h9d3eb37_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-7_he492b99_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libboost-1.88.0-hf9ddd82_6.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libboost-python-1.88.0-py312h83679cb_6.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlicommon-1.2.0-h8616949_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlidec-1.2.0-h8616949_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlienc-1.2.0-h8616949_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-7_h9b27e0a_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libcondor_utils-24.12.4-h2da22b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libcrc32c-1.1.2-he49afe7_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.18.0-h9348e2b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.6-h19cb2f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libdeflate-1.25-h517ebb2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libedit-3.1.20250104-pl5321ha958ccf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libev-4.33-h10d778d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libevent-2.1.12-ha90c15b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.8.1-hcc62823_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libframel-8.48.5-hbd657fc_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libfreetype-2.14.3-h694c41f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libfreetype6-2.14.3-h58fbd8d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_19.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_19.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_19.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libgoogle-cloud-2.39.0-h11ac9da_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libgoogle-cloud-storage-2.39.0-hea209c6_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libgrpc-1.78.1-h147dede_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libhwy-1.4.0-hca42a69_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libiconv-1.18-h57a12c2_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libjpeg-turbo-3.1.4.1-ha1e9b39_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libjxl-0.11.2-h473410d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblal-7.6.1-fftw_ha907fd1_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblalburst-2.0.6-h8fa4168_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblalframe-3.0.6-h6e76fb1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblalinference-4.1.8-h506f03e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblalinspiral-5.0.2-h8fa4168_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblalmetaio-4.0.5-h6e16a3a_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblalpulsar-7.1.0-h0f29fec_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblalsimulation-6.1.0-py312h1b08b07_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-7_h859234e_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.3-hbb4bfdb_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libmetaio-8.5.1-h97fe558_1003.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.68.1-h70048d4_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.33-openmp_h9e49c7b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libopentelemetry-cpp-1.21.0-h7a0a166_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libopentelemetry-cpp-headers-1.21.0-h694c41f_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libparquet-23.0.1-hb3ef814_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libpng-1.6.58-he930e7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libprotobuf-6.33.5-h29d92e8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libre2-11-2025.11.05-h6e8c311_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libsodium-1.0.22-ha3d0635_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.53.1-h77d7759_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libssh2-1.11.1-hed3591d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libthrift-0.22.0-hebea4ca_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libtiff-4.7.1-ha0a348c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libutf8proc-2.11.3-hc282952_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libwebp-base-1.6.0-hb807250_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libxcb-1.17.0-hf1f96e2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libxml2-16-2.15.1-ha1d9b0f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libxml2-2.15.1-h7b7ecba_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libzopfli-1.0.3-h046ec9c_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-64/ligo-segments-1.4.0-py312h01d7ebd_6.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/ligo.skymap-2.3.0-py312h2e055c9_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.6-h0d3cbff_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/llvmlite-0.47.0-py312ha5a82fe_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lz4-4.4.5-py312ha706d14_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lz4-c-1.10.0-h240833e_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/markupsafe-3.0.3-py312heb39f77_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-3.10.9-py312hb401068_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-base-3.10.9-py312h7609456_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/msgpack-python-1.1.2-py312hd099df3_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.6-hcc0dc9a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/nlohmann_json-3.12.0-h06076ce_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/numba-0.65.1-py312h704f9c4_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/numcodecs-0.16.5-py312h86abcb1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-1.26.4-py312he3a82b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/oniguruma-6.9.10-h6e16a3a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/openjpeg-2.5.4-h52bb76a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/openjph-0.27.3-h2c0e27e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.2-hc881268_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/orc-2.3.0-hb9b210e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pandas-3.0.3-py312h8e27051_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pcre2-10.46-ha3e7e28_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pillow-12.2.0-py312he84af14_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/prometheus-cpp-1.3.0-h7802330_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/psutil-7.2.2-py312hf7082af_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pthread-stubs-0.4-h00291cd_1002.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pyarrow-23.0.1-py312hb401068_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pyarrow-core-23.0.1-py312h3987635_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pyerfa-2.0.1.5-py310hcbffc5d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pynacl-1.6.2-py312h3bc9c61_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.12.13-ha9537fe_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-gssapi-1.11.1-py312h972ca57_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-htcondor-24.12.4-py312h564a4e3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lal-7.6.1-fftw_py312ha78c7a8_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalburst-2.0.6-py312h44bc254_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalframe-3.0.6-py312h025c719_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalinference-4.1.8-py312h025c719_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalinspiral-5.0.2-py312h025c719_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalmetaio-4.0.5-py312h025c719_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalpulsar-7.1.0-py312h025c719_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalsimulation-6.1.0-py312h1b08b07_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-ligo-lw-1.8.3-py312h01d7ebd_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py312h51361c1_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/qhull-2020.2-h3c5361c_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/rav1e-0.8.1-h618d828_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/re2-2025.11.05-h77e0585_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/regex-2026.5.9-py312h933eb07_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/reproject-0.19.0-py312h391ab28_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.8.0-np2py312h47bbdc5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.17.1-py312h6309490_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/scitokens-cpp-1.4.0-h1c2ca81_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/shapely-2.1.2-py312hd8edc82_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/snappy-1.2.2-h01f5ddf_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/svt-av1-4.0.1-h991f03e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/swig-4.3.1-hf470585_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/tornado-6.5.5-py312h933eb07_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/unicodedata2-17.0.1-py312h1a1c95f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/wrapt-2.2.1-py312h933eb07_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/xorg-libxau-1.0.12-h8616949_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/xorg-libxdmcp-1.1.5-h8616949_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/yaml-0.2.5-h4132b18_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/zfp-1.0.1-h1b13a81_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/zlib-1.3.2-hbb4bfdb_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/zlib-ng-2.3.3-h8bce59a_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda + - pypi: https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/09/c9/a9271d39c86d28d8bfacec831e416e42eb6d154f03605429631c4c08138b/pygsl_lite-0.1.8.tar.gz + - pypi: https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/19/cb/d31459da1f482479512c642c8463347b828004ecfa5bcd0a26ab57317add/spinsfast-2022.4.10-cp312-cp312-macosx_13_0_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/20/7a/1c6e3562dfd8950adbb11ffbc65d21e7c89d01a6e4f137fa981056de25c5/gitpython-3.1.50-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/25/ce/308e5e5da57515dd7cab3ec37ea2d5b8ff50bef1fcc8e6d31456f9fae08e/statsmodels-0.14.6-cp312-cp312-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/36/e1/84f6ede106afadc0e2c6a43d35b96ed6909149023f9a6a929175ad13a503/blosc2-4.3.3-cp312-cp312-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/42/c2/b602d2a5bbaaf9db1cff035ee6ddea76938c0b25ac1e6ac8ca288a073b47/otter_report-0.4.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/46/75/505cd72dfde3a1d7e719f840a2aa656a8c6b4c7b1b8c604a2d587cb1fc52/precession-2.1.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/59/91/aa6bde563e0085a02a435aa99b49ef75b0a4b062635e606dab23ce18d720/inflection-0.5.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/59/f3/77f85bfac22ee84b231ee8b147cdd948f679edf7d5a622acf9169d2a9b63/juliacall-0.9.34-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/65/90/774ddd08b2a1b41faa56da111f0fbfeb4f17ee537214c938ef41d61af949/ndindex-1.10.1-cp312-cp312-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/66/8f/2515bd2f110f6316f39568e25943577effffa5283b6376969e8277ba330d/sxs-2025.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/66/92/8e7104d2cf40ba2f88528c01e8a063aba7b223a21c0310fcf043b441f5da/sxscatalog-3.0.28-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6d/a3/e2d6b17b02b5c52ce6c68fce7f2f190e796c2c1c5419e675f6a947fdb78c/qnm-0.4.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6f/33/81570e825bdb2c0d7fa705087b704007bb7898c7dbb7a64f868da59252b3/pyseobnr-0.3.6.tar.gz + - pypi: https://files.pythonhosted.org/packages/78/17/853354204e1ca022d6b7d011ca7f3206c4f8faa3cc743e92609b49c1d83f/tinydb-4.8.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7d/4a/75aadc3b17430d4d9f3ad7bf0332a22b7ef78312e2b5b70774d2a301b9ec/spherical-1.0.18-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/86/9d/1f4495bf047e61e904a69242941aca04ad3c7453639d9019c5d8facb3862/juliapkg-0.1.23-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/f1/2033813456213ecfe8ccab18d78ef7b00af70d049c916cb167d592bdd6da/scri-2022.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/90/ad/cba91b3bcf04073e4d1655a5c1710ef3f457f56f7d1b79dcc3d72f4dd912/plotly-6.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/97/8d/17e56f2208b885aa8462277ae75a9bc82f7877bb52d88690636246853032/requests_pelican-0.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9d/20/c473fc04a371f5e2f8c5749e04505c13e7a8ede27c09e9f099b2ad6f43d6/numexpr-2.14.1-cp312-cp312-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a6/24/4d91e05817e92e3a61c8a21e08fd0f390f5301f1c448b137c57c4bc6e543/semver-3.0.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ad/28/9f3700cb784c0f0475de7074ed489702d5c6fb63a3df56b4cb4333b055fc/liquidpy-0.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/8a/eafc962e29c7fe800c702b4ae09cc358f1cbe40089e7a8cdac4f5b4c8c7a/requests_scitokens-0.2.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ba/b2/138065f2c50fd66b89623a5ee45d43d2341cfede4d7e0f7292075953ff12/numpy_quaternion-2024.0.13-cp312-cp312-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/bc/b2/d213f02254956b40f70dfaf2ba21ed0d1b7ed54b7cf361865d9d95fae867/asimov-0.6.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bd/b1/6bc28d9441c48744b6ecfb46af928fca427a3213282efa0faa5bea7eefd8/pesummary-1.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/d5/9b/7a0e11d7858feac24372ae770575802833436a050f93dfd012d8025e4ae9/deepdish-0.3.7-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d6/c6/7a6d8f56d3efe2ddb2bc659c2b1f9ba01b8a27174733526b57e181ba23b0/spherical_functions-2023.0.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/df/b1/3e3144c3ada2ae1a60a378396629064ef14e425e356c8e7c1db3a50ef351/python_gitlab-8.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e0/a9/023730ba63db1e494a271cb018dcd361bd2c917ba7004c3e49d5daf795a2/py_cpuinfo-9.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e3/47/a8d68aed3f6cb55bae90f504cd5ea3698a923c5fb6e9690726359e28eb1c/asimov_gwdata-0.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/70/ba4b949bdc0490ab78d545459acd7702b211dfccf7eb89bbc1060f52818d/patsy-1.0.2-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fa/bb/4a9cde6628563388db26fa86c64adb0f2475a757e72af0ec185fd520b72f/tables-3.11.1-cp311-abi3-macosx_10_9_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fd/86/e74734fcc564c7c9dfb47758ea9699c5294c68979fe0d0017454f3ca3f54/quaternionic-1.0.17-py3-none-any.whl + osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/noarch/antlr-python-runtime-4.9.3-pyhd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/arviz-0.23.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/astroplan-0.10.1-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/astropy-iers-data-0.2026.5.25.1.14.13-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.0-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/corner-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.3.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.3.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-image-2025.11.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dateparser-1.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_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/distributed-2026.3.0-pyhc364b38_0.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/dqsegdb2-1.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/future-1.0.0-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwdatafind-2.1.1-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwosc-0.8.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwpy-4.0.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h5netcdf-1.8.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hydra-core-1.3.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.15-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/igwn-auth-utils-1.4.0-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/imageio-2.37.0-pyhfb79c49_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_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/invoke-3.0.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jplephem-2.24-pyha4b2019_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/lalsuite-7.25-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ligo-gracedb-2.15.7-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ligotimegps-2.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/lscsoft-glue-4.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mpmath-1.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.21.2-pyhcf101f3_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/networkx-3.6.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/omegaconf-2.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/paramiko-5.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pims-0.7-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.1.1-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.6-pyhcf101f3_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/ptemcee-1.0.0-py_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/pyavm-0.9.9-pyhc455866_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.3-pyhfe8187e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-global-3.0.3-pyh648e204_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.13.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.0.3-pyhc364b38_1.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_abi-3.12-8_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/safe-netrc-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/scitokens-1.9.7-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_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/slicerator-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tifffile-2026.3.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.3-pyh8f84b5b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzlocal-5.3.1-pyh8f84b5b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.47.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-einstats-0.9.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zarr-3.1.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_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/aom-3.9.1-h7bae524_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/astropy-base-7.2.0-py312ha11c99a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/astropy-healpix-1.1.3-py312hf57c059_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.9.6-ha02d361_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.13-h6ee9776_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.12.6-hc919400_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.2-h3e7f9b5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.5.9-hd533cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.10.10-ha1850f6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.26.1-h4137820_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.14.0-h5721393_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.11.5-h7d214dc_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.4-h16f91aa_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.10-h3e7f9b5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.37.3-hcfbc53e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.747-h35a1687_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.16.2-he5ae378_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.13.3-h810541e_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.16.0-h5446563_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.13.0-he467506_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.14.0-hdc9d693_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.5.0-py312h87c4bb7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bcrypt-5.0.0-py312h6ef9ec0_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/blosc-1.21.6-h7dd00d9_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-h7d5ae5b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py312h0dfefe5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brunsli-0.1-he0dfb12_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-blosc2-3.0.3-hf9886e1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py312h1b4d9a2_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cfitsio-4.6.2-h7200ff5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/charls-2.4.3-hf6b4638_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/chealpix-3.31.0-he71fa57_8.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py312h3093aea_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.14.0-py312h04c11ed_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cryptography-48.0.0-py312h1238841_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cytoolz-1.1.0-py312h2bbb03f_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/dav1d-1.2.1-hb547adb_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fftw-3.3.11-nompi_haf1500d_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.63.0-py312h04c11ed_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.14.1-h5afe852_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-h93a5062_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/google-crc32c-1.8.0-py312h090f823_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/h5py-3.13.0-nompi_py312hd7c5113_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-1.14.3-nompi_ha698983_109.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/healpy-1.18.1-py312hb9c4046_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/htcondor-24.12.4-py312h1f38498_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/htcondor-classads-24.12.4-h0c2a548_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/htcondor-cli-24.12.4-py312h81bd7bf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/htcondor-utils-24.12.4-h9e88eaa_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/htgettoken-2.6-py312h81bd7bf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/igwn-ligolw-2.1.1-py312h8d938ae_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/igwn-segments-2.1.1-py312h8d938ae_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/imagecodecs-2026.5.10-py312ha5a633e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/jq-1.8.1-hbc156a2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/jxrlib-1.1-h93a5062_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.0-py312h3093aea_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lal-7.6.1-fftw_py312h3b12eeb_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalapps-10.0.2-py312he91bef5_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalburst-2.0.6-py312h028adb6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalframe-3.0.6-py312h028adb6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalinference-4.1.8-nompi_py312h7a6eb69_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalinference-data-4.1.8-hce30654_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalinspiral-5.0.2-py312h028adb6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalmetaio-4.0.5-py312h028adb6_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalpulsar-7.1.0-py312hf4cc3a4_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalpulsar-data-7.1.0-hce30654_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalsimulation-6.1.0-py312hd4754c9_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalsimulation-data-6.1.0-hce30654_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.19.1-hdfa7624_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ldas-tools-al-2.7.0-he7ec6a4_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ldas-tools-framecpp-2.9.3-ha7f91e6_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.1.0-h1eee2c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260107.1-cxx17_h2062a1b_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/libarrow-23.0.1-h5390cfe_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-23.0.1-hbf36091_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-compute-23.0.1-h4dbefc3_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-23.0.1-hbf36091_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-23.0.1-h05be00f_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libavif16-1.4.1-hfce71f6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-7_h51639a9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libboost-1.88.0-h18cd856_6.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libboost-python-1.88.0-py312h4c080bd_6.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-7_hb0561ab_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcondor_utils-24.12.4-h4785e8b_0.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.18.0-he38603e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.6-h55c6f16_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-hc11a715_0.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-h93a5062_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libframel-8.48.5-h2f1a0bb_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-hdfa99f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.39.0-h2f60c08_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.39.0-ha114238_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.78.1-h3e3f78d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libhwy-1.4.0-ha332bbd_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/libjpeg-turbo-3.1.4.1-h84a0fba_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjxl-0.11.2-h934fa54_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblal-7.6.1-fftw_h8fcc6d2_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalburst-2.0.6-h2786bc8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalframe-3.0.6-h63b17c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalinference-4.1.8-h4d5f05e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalinspiral-5.0.2-h2786bc8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalmetaio-4.0.5-h5505292_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalpulsar-7.1.0-h8480f09_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalsimulation-6.1.0-py312h0975e73_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-7_hd9741b5_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmetaio-8.5.1-h57f5043_1003.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.33-openmp_he657e61_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-1.21.0-h08d5cc3_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-headers-1.21.0-hce30654_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-23.0.1-h7a13205_4_cpu.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/libprotobuf-6.33.5-h4a5acfd_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h4c27e2a_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.22-h1a92334_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.1-h1b79a29_0.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/libthrift-0.22.0-h1fb9c8a_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.1-h4030677_1.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/libwebp-base-1.6.0-h07db88b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.1-h0ff4647_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.1-h9329255_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzopfli-1.0.3-h9f76cd9_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ligo-segments-1.4.0-py312hea69d52_6.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ligo.skymap-2.3.0-py312hd0a6ca1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.6-hc7d1edf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvmlite-0.47.0-py312h7ca588d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-4.4.5-py312h2b25a0d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py312h04c11ed_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.9-py312h1f38498_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.9-py312hf3defc7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.1.2-py312h84eede6_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h784d473_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numba-0.65.1-py312h2d3d6e9_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-1.26.4-py312h8442bc7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/oniguruma-6.9.10-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjph-0.27.3-h2a4d681_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.2-hd24854e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.3.0-hd11884d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.3-py312h6510ced_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.46-h7125dd6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.2.0-py312h4e908a4_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/psutil-7.2.2-py312hb3ab3e3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-23.0.1-py312h1f38498_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-23.0.1-py312h21b41d0_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyerfa-2.0.1.5-py310hbb12772_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pynacl-1.6.2-py312h8fd69cb_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.13-h8561d8f_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-gssapi-1.11.1-py312h72ca3cf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-htcondor-24.12.4-py312hd1c112e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lal-7.6.1-fftw_py312h0f8b06b_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalburst-2.0.6-py312heec191f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalframe-3.0.6-py312he0011b7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalinference-4.1.8-py312he0011b7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalinspiral-5.0.2-py312he0011b7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalmetaio-4.0.5-py312he0011b7_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalpulsar-7.1.0-py312he0011b7_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalsimulation-6.1.0-py312he758b9a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-ligo-lw-1.8.3-py312hea69d52_4.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/qhull-2020.2-h420ef59_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rav1e-0.8.1-h8246384_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-ha480c28_1.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/regex-2026.5.9-py312h2bbb03f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproject-0.19.0-py312ha11c99a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.8.0-np2py312he5ca3e3_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py312h0f234b1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scitokens-cpp-1.4.0-h608d757_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/shapely-2.1.2-py312h35cd81b_2.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/svt-av1-4.0.1-h0cb729a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/swig-4.3.1-h0c2a548_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.5-py312h2bbb03f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py312h2bbb03f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-2.2.1-py312h2bbb03f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h925e9cb_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zfp-1.0.1-ha86207d_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda + - pypi: https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/05/30/affbabf3c27fb501ec7b5808230c619d4d1a4525c07301074eb4bda92fa9/statsmodels-0.14.6-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/09/c9/a9271d39c86d28d8bfacec831e416e42eb6d154f03605429631c4c08138b/pygsl_lite-0.1.8.tar.gz + - pypi: https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/20/7a/1c6e3562dfd8950adbb11ffbc65d21e7c89d01a6e4f137fa981056de25c5/gitpython-3.1.50-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/42/c2/b602d2a5bbaaf9db1cff035ee6ddea76938c0b25ac1e6ac8ca288a073b47/otter_report-0.4.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/45/93/b6760dd1904c2a498e5f43d1bb436f59383c3ddea3815f1461dfaa259373/numexpr-2.14.1-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/46/75/505cd72dfde3a1d7e719f840a2aa656a8c6b4c7b1b8c604a2d587cb1fc52/precession-2.1.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/59/91/aa6bde563e0085a02a435aa99b49ef75b0a4b062635e606dab23ce18d720/inflection-0.5.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/59/f3/77f85bfac22ee84b231ee8b147cdd948f679edf7d5a622acf9169d2a9b63/juliacall-0.9.34-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/66/8f/2515bd2f110f6316f39568e25943577effffa5283b6376969e8277ba330d/sxs-2025.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/66/92/8e7104d2cf40ba2f88528c01e8a063aba7b223a21c0310fcf043b441f5da/sxscatalog-3.0.28-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6d/a3/e2d6b17b02b5c52ce6c68fce7f2f190e796c2c1c5419e675f6a947fdb78c/qnm-0.4.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6f/33/81570e825bdb2c0d7fa705087b704007bb7898c7dbb7a64f868da59252b3/pyseobnr-0.3.6.tar.gz + - pypi: https://files.pythonhosted.org/packages/78/17/853354204e1ca022d6b7d011ca7f3206c4f8faa3cc743e92609b49c1d83f/tinydb-4.8.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/78/74/6568c8d3aabf9982ab89fe3e378afbd7aad4894bde4570991a3246169ef4/tables-3.11.1-cp311-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7d/4a/75aadc3b17430d4d9f3ad7bf0332a22b7ef78312e2b5b70774d2a301b9ec/spherical-1.0.18-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/86/9d/1f4495bf047e61e904a69242941aca04ad3c7453639d9019c5d8facb3862/juliapkg-0.1.23-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/f1/2033813456213ecfe8ccab18d78ef7b00af70d049c916cb167d592bdd6da/scri-2022.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/90/ad/cba91b3bcf04073e4d1655a5c1710ef3f457f56f7d1b79dcc3d72f4dd912/plotly-6.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/97/8d/17e56f2208b885aa8462277ae75a9bc82f7877bb52d88690636246853032/requests_pelican-0.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a6/24/4d91e05817e92e3a61c8a21e08fd0f390f5301f1c448b137c57c4bc6e543/semver-3.0.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a8/30/12c87de87d6a3b523996737b97ba2e75972afac3c804249e5e61036e03d7/spinsfast-2022.4.10.tar.gz + - pypi: https://files.pythonhosted.org/packages/ad/28/9f3700cb784c0f0475de7074ed489702d5c6fb63a3df56b4cb4333b055fc/liquidpy-0.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/8a/eafc962e29c7fe800c702b4ae09cc358f1cbe40089e7a8cdac4f5b4c8c7a/requests_scitokens-0.2.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bc/b2/d213f02254956b40f70dfaf2ba21ed0d1b7ed54b7cf361865d9d95fae867/asimov-0.6.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bd/b1/6bc28d9441c48744b6ecfb46af928fca427a3213282efa0faa5bea7eefd8/pesummary-1.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/9b/7a0e11d7858feac24372ae770575802833436a050f93dfd012d8025e4ae9/deepdish-0.3.7-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d6/c6/7a6d8f56d3efe2ddb2bc659c2b1f9ba01b8a27174733526b57e181ba23b0/spherical_functions-2023.0.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/da/cc/ca6129956980fc6148d6b1983bc5dfc302f73bc734b376069eb560d170b6/blosc2-4.3.3-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/dc/9c/6a64269df4f032d883c5da72052850ef94fb79743b70606719c1ca579c79/numpy_quaternion-2024.0.13-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/df/b1/3e3144c3ada2ae1a60a378396629064ef14e425e356c8e7c1db3a50ef351/python_gitlab-8.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e0/a9/023730ba63db1e494a271cb018dcd361bd2c917ba7004c3e49d5daf795a2/py_cpuinfo-9.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e3/47/a8d68aed3f6cb55bae90f504cd5ea3698a923c5fb6e9690726359e28eb1c/asimov_gwdata-0.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ed/ee/a423e857f5b45da3adc8ddbcfbfd4a0e9a047edce3915d3e3d6e189b6bd9/ndindex-1.10.1-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/f1/70/ba4b949bdc0490ab78d545459acd7702b211dfccf7eb89bbc1060f52818d/patsy-1.0.2-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fd/86/e74734fcc564c7c9dfb47758ea9699c5294c68979fe0d0017454f3ca3f54/quaternionic-1.0.17-py3-none-any.whl + swig-post44: + channels: + - url: https://conda.anaconda.org/conda-forge/ + indexes: + - https://pypi.org/simple + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.15.3-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aom-3.9.1-hac33072_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/astropy-base-7.2.0-py312h4f23490_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/astropy-healpix-1.1.3-py312h4f23490_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.9.6-hb9c0fe4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.13-h2c9d079_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.12.6-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.2-h8b1a151_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.9-h841be55_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.10.10-hf621c6d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.26.1-hc87160b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.14.0-ha25ca29_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.11.5-h9b5df67_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.4-h8b1a151_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.10-h8b1a151_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.37.3-hb153662_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.747-h133b1ee_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.16.2-h206d751_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.13.3-hed0cdb0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.16.0-hf824e48_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.13.0-ha7a2c86_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.14.0-h539c000_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.5.0-py312h90b7ffd_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bcrypt-5.0.0-py312h868fb18_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-hed03a55_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312hdb49522_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brunsli-0.1-hd1e3526_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-blosc2-3.0.3-hc31b594_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py312h460c074_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cfitsio-4.6.2-ha0b56bc_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/charls-2.4.3-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/chealpix-3.31.0-ha55a0e1_8.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312h0a2e395_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.14.0-py312h8a5da7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cryptography-48.0.0-py312ha4b625e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.1.0-py312h4c3975b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dav1d-1.2.1-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.4.0-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fftw-3.3.11-nompi_h3b011a4_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.0-h27c8c51_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py312h8a5da7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h480dda7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/google-crc32c-1.8.0-py312h03f33d3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-hecca717_2.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/h5py-3.13.0-nompi_py312hedeef09_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-12.2.0-h15599e2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.3-nompi_h2d575fe_109.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/healpy-1.18.1-py312he9d304c_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/htcondor-25.6.1-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/htcondor-classads-25.6.1-h793e66c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/htcondor-cli-25.6.1-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/htcondor-utils-25.6.1-h41609d3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/htgettoken-2.6-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/igwn-ligolw-2.1.1-py312h53c857e_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/igwn-segments-2.1.1-py312h53c857e_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/imagecodecs-2026.5.10-py312he34094b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/jq-1.8.1-h73b1eb8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/jxrlib-1.1-hd590300_3.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/kiwisolver-1.5.0-py312h0a2e395_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lal-7.6.1-fftw_py312ha96d5c7_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalapps-10.0.2-py312h760c983_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalburst-2.0.6-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalframe-3.0.6-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalinference-4.1.8-nompi_py312h1d0e7ed_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalinference-data-4.1.8-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalinspiral-5.0.2-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalmetaio-4.0.5-py312h66e93f0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalpulsar-7.1.0-py312he8860a6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalpulsar-data-7.1.0-ha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalsimulation-6.1.0-py312h09b83b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalsimulation-data-6.1.0-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h0c24ade_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ldas-tools-al-2.7.0-hd827ec0_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ldas-tools-framecpp-2.9.3-he85ded8_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.1.0-hdb68285_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_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/libarrow-23.0.1-hf605819_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-23.0.1-h635bf11_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-compute-23.0.1-h53684a4_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-23.0.1-h635bf11_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-23.0.1-hb4dd7c2_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libavif16-1.4.1-hcfa2d63_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-7_h4a7cf45_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libboost-1.88.0-hed09d94_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-7_h0358290_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp21.1-21.1.8-default_h99862b1_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-22.1.6-default_h746c552_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcondor_utils-25.6.1-h9f200e7_0.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-hb8b1518_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.18.0-h4e3cde8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.127-hb03c661_0.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/libegl-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libframel-8.48.5-ha02e5fa_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h73754d4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.1-h0d30a3d_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.39.0-h9d11ab5_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.39.0-hdbdcf42_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.78.1-h1d1128b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.4.0-h10be129_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.1.4.1-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.11.2-h174a0a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblal-7.6.1-fftw_hcbe9c14_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblalburst-2.0.6-h3773ae6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblalframe-3.0.6-h7c287a6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblalinference-4.1.8-h3773ae6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblalinspiral-5.0.2-h3773ae6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblalmetaio-4.0.5-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblalpulsar-7.1.0-h9345056_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblalsimulation-6.1.0-py312h09b83b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-7_h47877c9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.8-hf7376ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm22-22.1.6-hf7376ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmetaio-8.5.1-h0b0be96_1003.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/libntlm-1.8-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-1.21.0-h9692893_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-headers-1.21.0-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-23.0.1-h7376487_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_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/libpq-18.1-h5c52fec_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.33.5-h2b00c02_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h0dc7533_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.22-h280c20c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.1-h0c1763c_0.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-15.2.0-h934c35e_19.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_19.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.1-h9d88235_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.1-h5347b49_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.341.0-h5279c79_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.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.36-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.1-hca5e8e5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.1-ha9997c6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.1-h26afc86_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzopfli-1.0.3-h9c3ff4c_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/ligo-segments-1.4.0-py312h66e93f0_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ligo.skymap-2.3.0-py312h36415c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/llvmlite-0.47.0-py312h7424e68_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-4.4.5-py312h3d67a73_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.9-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.9-py312he3d6523_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.1.2-py312hd9148b4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/munge-0.5.16-h63a00c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numba-0.65.1-py312hd1dde6f_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-1.26.4-py312heda63a1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/oniguruma-6.9.10-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjph-0.27.3-h8d634f6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.3.0-h21090e2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.3-py312h8ecdadd_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/pillow-12.2.0-py312h50c33e8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.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/psutil-7.2.2-py312h5253ce2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-23.0.1-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-23.0.1-py312h2054cf2_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyerfa-2.0.1.5-py310h32771cd_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pynacl-1.6.2-py312hf34ed73_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.10.1-py312h9da60e5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.13-hd63d673_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-gssapi-1.11.1-py312h7cea900_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-htcondor-25.6.1-py312h40fa4ac_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lal-7.6.1-fftw_py312heccaa44_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalburst-2.0.6-py312h3684b61_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalframe-3.0.6-py312hc0a28a1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalinference-4.1.8-py312hc0a28a1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalinspiral-5.0.2-py312hc0a28a1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalmetaio-4.0.5-py312hc0a28a1_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalpulsar-7.1.0-py312hc0a28a1_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalsimulation-6.1.0-py312h09b83b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-ligo-lw-1.8.3-py312h66e93f0_4.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/qhull-2020.2-h434a139_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.10.1-h6f76662_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rav1e-0.8.1-h1fbca29_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h5301d42_1.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/regex-2026.5.9-py312h4c3975b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/reproject-0.19.0-py312h4f23490_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.1-h1cbb8d7_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.8.0-np2py312h3226591_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py312h54fa4ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scitokens-cpp-1.4.0-h096d96b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py312h383787d_2.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/svt-av1-4.0.1-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/swig-4.4.1-h7a96c5f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.5-py312h4c3975b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py312h4c3975b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.25.0-hd6090a7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-2.2.1-py312h4c3975b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.47-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.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_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.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-libxtst-1.2.5-hb9d3cd8_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zfp-1.0.1-h909a3a2_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.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/antlr-python-runtime-4.9.3-pyhd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/arviz-0.23.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/astroplan-0.10.1-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/astropy-iers-data-0.2026.5.25.1.14.13-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.0-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/corner-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.3.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.3.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-image-2025.11.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dateparser-1.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_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/distributed-2026.3.0-pyhc364b38_0.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/dqsegdb2-1.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/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/fsspec-2026.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/future-1.0.0-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwdatafind-2.1.1-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwosc-0.8.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwpy-4.0.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h5netcdf-1.8.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hydra-core-1.3.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.15-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/igwn-auth-utils-1.4.0-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/imageio-2.37.0-pyhfb79c49_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_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/invoke-3.0.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jplephem-2.24-pyha4b2019_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/lalsuite-7.25-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ligo-gracedb-2.15.7-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ligotimegps-2.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/lscsoft-glue-4.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mpmath-1.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.21.2-pyhcf101f3_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/networkx-3.6.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/omegaconf-2.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/paramiko-5.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pims-0.7-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.1.1-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.6-pyhcf101f3_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/ptemcee-1.0.0-py_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/pyavm-0.9.9-pyhc455866_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.3-pyhfe8187e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-global-3.0.3-pyh648e204_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.13.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.0.3-pyhc364b38_1.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_abi-3.12-8_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/safe-netrc-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/scitokens-1.9.7-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_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/slicerator-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tifffile-2026.3.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.3-pyh8f84b5b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzlocal-5.3.1-pyh8f84b5b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.47.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-einstats-0.9.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zarr-3.1.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda + - pypi: https://files.pythonhosted.org/packages/00/e2/1cb7cfb88fd3866062977a484bc0dda4e165361e949cc8e270302d05b007/spinsfast-2022.4.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/07/40/4058220b3d60890b62e0a2e8212e2546695827cf85e6186405ecf8ef33f1/numpy_quaternion-2024.0.13-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/09/c9/a9271d39c86d28d8bfacec831e416e42eb6d154f03605429631c4c08138b/pygsl_lite-0.1.8.tar.gz + - pypi: https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/20/7a/1c6e3562dfd8950adbb11ffbc65d21e7c89d01a6e4f137fa981056de25c5/gitpython-3.1.50-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/26/96/92119e6b279a88547a51eb99726ecae1ada839bf4fbcc2856503e2558e80/blosc2-4.3.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/27/ae/defd665dbbeb2fffa077491365ed160acaec49274ce8d4b979f55db71f18/ndindex-1.10.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/42/c2/b602d2a5bbaaf9db1cff035ee6ddea76938c0b25ac1e6ac8ca288a073b47/otter_report-0.4.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/46/75/505cd72dfde3a1d7e719f840a2aa656a8c6b4c7b1b8c604a2d587cb1fc52/precession-2.1.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/59/91/aa6bde563e0085a02a435aa99b49ef75b0a4b062635e606dab23ce18d720/inflection-0.5.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/59/f3/77f85bfac22ee84b231ee8b147cdd948f679edf7d5a622acf9169d2a9b63/juliacall-0.9.34-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/66/8f/2515bd2f110f6316f39568e25943577effffa5283b6376969e8277ba330d/sxs-2025.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/66/92/8e7104d2cf40ba2f88528c01e8a063aba7b223a21c0310fcf043b441f5da/sxscatalog-3.0.28-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6d/a3/e2d6b17b02b5c52ce6c68fce7f2f190e796c2c1c5419e675f6a947fdb78c/qnm-0.4.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6f/33/81570e825bdb2c0d7fa705087b704007bb7898c7dbb7a64f868da59252b3/pyseobnr-0.3.6.tar.gz + - pypi: https://files.pythonhosted.org/packages/78/17/853354204e1ca022d6b7d011ca7f3206c4f8faa3cc743e92609b49c1d83f/tinydb-4.8.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7d/4a/75aadc3b17430d4d9f3ad7bf0332a22b7ef78312e2b5b70774d2a301b9ec/spherical-1.0.18-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/81/68/dddd76117df2ef14c943c6bbb6618be5c9401280046f4ddfc9fb4596a1b8/statsmodels-0.14.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/86/9d/1f4495bf047e61e904a69242941aca04ad3c7453639d9019c5d8facb3862/juliapkg-0.1.23-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/f1/2033813456213ecfe8ccab18d78ef7b00af70d049c916cb167d592bdd6da/scri-2022.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/90/ad/cba91b3bcf04073e4d1655a5c1710ef3f457f56f7d1b79dcc3d72f4dd912/plotly-6.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/97/8d/17e56f2208b885aa8462277ae75a9bc82f7877bb52d88690636246853032/requests_pelican-0.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/99/29/c2dc674ea70fa9a4819417289a9c0d3e4780835beeed573eb66964cfb763/tables-3.11.1-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a6/24/4d91e05817e92e3a61c8a21e08fd0f390f5301f1c448b137c57c4bc6e543/semver-3.0.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ad/28/9f3700cb784c0f0475de7074ed489702d5c6fb63a3df56b4cb4333b055fc/liquidpy-0.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/8a/eafc962e29c7fe800c702b4ae09cc358f1cbe40089e7a8cdac4f5b4c8c7a/requests_scitokens-0.2.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bc/b2/d213f02254956b40f70dfaf2ba21ed0d1b7ed54b7cf361865d9d95fae867/asimov-0.6.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bd/b1/6bc28d9441c48744b6ecfb46af928fca427a3213282efa0faa5bea7eefd8/pesummary-1.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/9b/7a0e11d7858feac24372ae770575802833436a050f93dfd012d8025e4ae9/deepdish-0.3.7-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d6/c6/7a6d8f56d3efe2ddb2bc659c2b1f9ba01b8a27174733526b57e181ba23b0/spherical_functions-2023.0.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d9/43/560e9ba23c02c904b5934496486d061bcb14cd3ebba2e3cf0e2dccb6c22b/numexpr-2.14.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/df/b1/3e3144c3ada2ae1a60a378396629064ef14e425e356c8e7c1db3a50ef351/python_gitlab-8.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e0/a9/023730ba63db1e494a271cb018dcd361bd2c917ba7004c3e49d5daf795a2/py_cpuinfo-9.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e3/47/a8d68aed3f6cb55bae90f504cd5ea3698a923c5fb6e9690726359e28eb1c/asimov_gwdata-0.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/70/ba4b949bdc0490ab78d545459acd7702b211dfccf7eb89bbc1060f52818d/patsy-1.0.2-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fd/86/e74734fcc564c7c9dfb47758ea9699c5294c68979fe0d0017454f3ca3f54/quaternionic-1.0.17-py3-none-any.whl + osx-64: + - conda: https://conda.anaconda.org/conda-forge/noarch/antlr-python-runtime-4.9.3-pyhd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/arviz-0.23.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/astroplan-0.10.1-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/astropy-iers-data-0.2026.5.25.1.14.13-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.0-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/corner-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.3.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.3.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-image-2025.11.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dateparser-1.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_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/distributed-2026.3.0-pyhc364b38_0.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/dqsegdb2-1.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/future-1.0.0-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwdatafind-2.1.1-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwosc-0.8.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwpy-4.0.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h5netcdf-1.8.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hydra-core-1.3.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.15-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/igwn-auth-utils-1.4.0-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/imageio-2.37.0-pyhfb79c49_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_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/invoke-3.0.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jplephem-2.24-pyha4b2019_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/lalsuite-7.25-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ligo-gracedb-2.15.7-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ligotimegps-2.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/lscsoft-glue-4.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mpmath-1.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.21.2-pyhcf101f3_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/networkx-3.6.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/omegaconf-2.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/paramiko-5.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pims-0.7-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.1.1-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.6-pyhcf101f3_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/ptemcee-1.0.0-py_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/pyavm-0.9.9-pyhc455866_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.3-pyhfe8187e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-global-3.0.3-pyh648e204_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.13.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.0.3-pyhc364b38_1.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_abi-3.12-8_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/safe-netrc-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/scitokens-1.9.7-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_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/slicerator-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tifffile-2026.3.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.3-pyh8f84b5b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzlocal-5.3.1-pyh8f84b5b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.47.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-einstats-0.9.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zarr-3.1.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aom-3.9.1-hf036a51_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/astropy-base-7.2.0-py312h391ab28_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/astropy-healpix-1.1.3-py312h8ab2c85_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-auth-0.10.1-ha3f0692_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-cal-0.9.13-hea39f9f_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-common-0.12.6-h8616949_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-compression-0.3.2-hb9ea233_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-event-stream-0.7.0-ha9bd753_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-http-0.10.13-h1037d30_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-io-0.26.3-hc95b61d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-mqtt-0.15.2-h377fd20_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-s3-0.12.2-hfe16a33_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-sdkutils-0.2.4-h901532c_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-checksums-0.2.10-h31279ed_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-crt-cpp-0.38.3-heb35453_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-sdk-cpp-1.11.747-h9890d28_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/azure-core-cpp-1.16.2-h87f1c7e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/azure-identity-cpp-1.13.3-h1135191_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/azure-storage-blobs-cpp-12.16.0-hefc3566_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/azure-storage-common-cpp-12.13.0-h74781cd_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/azure-storage-files-datalake-cpp-12.14.0-h2303994_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/backports.zstd-1.5.0-py312h5f4ecc6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/bcrypt-5.0.0-py312h8a6388b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/blosc-1.21.6-hd145fbb_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-1.2.0-hf139dec_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-bin-1.2.0-h8616949_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-python-1.2.0-py312h4b46afd_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/brunsli-0.1-ha00ef93_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/c-ares-1.34.6-hb5e19a0_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/c-blosc2-3.0.3-h32e32c0_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/cffi-2.0.0-py312he90777b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/cfitsio-4.6.2-ha7f915d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/charls-2.4.3-hcc62823_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/chealpix-3.31.0-h93aae7a_8.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/contourpy-1.3.3-py312hb0c38da_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.14.0-py312heb39f77_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/cryptography-48.0.0-py312h1af399d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/cytoolz-1.1.0-py312h1a1c95f_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/dav1d-1.2.1-h0dc2134_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/fftw-3.3.11-nompi_h54214ab_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/fonttools-4.63.0-py312heb39f77_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/freetype-2.14.3-h694c41f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/geos-3.14.1-he483b9e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/gflags-2.2.2-hac325c4_1005.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/giflib-5.2.2-h10d778d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/glog-0.7.1-h2790a97_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/google-crc32c-1.8.0-py312hb9001e9_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/gsl-2.7-h93259b0_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-64/h5py-3.13.0-nompi_py312hea5ca7c_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-1.14.3-nompi_h1607680_109.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/healpy-1.18.1-py312h9f11423_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/htcondor-25.10.1-py312hb401068_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/htcondor-classads-25.10.1-h4086b99_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/htcondor-cli-25.10.1-py312hb401068_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/htcondor-utils-25.10.1-h712aeec_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/htgettoken-2.6-py312hb401068_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-75.1-h120a0e1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/igwn-ligolw-2.1.1-py312h0b9663a_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/igwn-segments-2.1.1-py312h0b9663a_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/imagecodecs-2026.5.10-py312hc8b613d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/jq-1.8.1-h2287256_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/jxrlib-1.1-h10d778d_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/kiwisolver-1.5.0-py312hb1dc2e7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/krb5-1.22.2-h207b36a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lal-7.6.1-fftw_py312h6f020d4_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalapps-10.0.2-py312h72dd72b_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalburst-2.0.6-py312h01d7ebd_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalframe-3.0.6-py312h01d7ebd_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalinference-4.1.8-nompi_py312h34ae946_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalinference-data-4.1.8-h694c41f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalinspiral-5.0.2-py312h01d7ebd_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalmetaio-4.0.5-py312h01d7ebd_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalpulsar-7.1.0-py312h5a434c1_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalpulsar-data-7.1.0-h694c41f_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalsimulation-6.1.0-py312haa07450_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalsimulation-data-6.1.0-h694c41f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lcms2-2.19.1-h5ea7634_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/ldas-tools-al-2.7.0-hb7f9d93_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/ldas-tools-framecpp-2.9.3-hf716561_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lerc-4.1.0-h35c7297_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20260107.1-cxx17_h7ed6875_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libaec-1.1.5-he7c3a48_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-24.0.0-h7252e1b_2_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-acero-24.0.0-h66151e4_2_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-compute-24.0.0-h5d4fa73_2_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-dataset-24.0.0-h66151e4_2_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-substrait-24.0.0-h613493e_2_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libavif16-1.4.1-h9d3eb37_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-7_he492b99_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libboost-1.88.0-hf9ddd82_6.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlicommon-1.2.0-h8616949_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlidec-1.2.0-h8616949_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlienc-1.2.0-h8616949_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-7_h9b27e0a_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libcondor_utils-25.10.1-hcd6a731_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libcrc32c-1.1.2-he49afe7_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.20.0-h8f0b9e4_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.6-h19cb2f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libdeflate-1.25-h517ebb2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libedit-3.1.20250104-pl5321ha958ccf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libev-4.33-h10d778d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libevent-2.1.12-ha90c15b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.8.1-hcc62823_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libframel-8.48.5-hbd657fc_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libfreetype-2.14.3-h694c41f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libfreetype6-2.14.3-h58fbd8d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_19.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_19.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_19.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libgoogle-cloud-3.5.0-h10ed7cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libgoogle-cloud-storage-3.5.0-hea209c6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libgrpc-1.78.1-h147dede_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libhwy-1.4.0-hca42a69_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libiconv-1.18-h57a12c2_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libjpeg-turbo-3.1.4.1-ha1e9b39_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libjxl-0.11.2-h473410d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblal-7.6.1-fftw_ha907fd1_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblalburst-2.0.6-h8fa4168_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblalframe-3.0.6-h6e76fb1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblalinference-4.1.8-h506f03e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblalinspiral-5.0.2-h8fa4168_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblalmetaio-4.0.5-h6e16a3a_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblalpulsar-7.1.0-h0f29fec_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblalsimulation-6.1.0-py312h1b08b07_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-7_h859234e_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.3-hbb4bfdb_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libmetaio-8.5.1-h97fe558_1003.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.68.1-h70048d4_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.33-openmp_h9e49c7b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libopentelemetry-cpp-1.26.0-h7a0a166_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libopentelemetry-cpp-headers-1.26.0-h694c41f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libparquet-24.0.0-h527dc83_2_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libpng-1.6.58-he930e7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libprotobuf-6.33.5-h29d92e8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libre2-11-2025.11.05-h6e8c311_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libsodium-1.0.22-ha3d0635_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.53.1-h77d7759_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libssh2-1.11.1-hed3591d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libthrift-0.22.0-hebea4ca_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libtiff-4.7.1-ha0a348c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libutf8proc-2.11.3-hc282952_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libwebp-base-1.6.0-hb807250_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libxcb-1.17.0-hf1f96e2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libxml2-16-2.15.1-ha1d9b0f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libxml2-2.15.1-h7b7ecba_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libzopfli-1.0.3-h046ec9c_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-64/ligo-segments-1.4.0-py312h01d7ebd_6.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/ligo.skymap-2.3.0-py312h2e055c9_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.6-h0d3cbff_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/llvmlite-0.47.0-py312ha5a82fe_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lz4-4.4.5-py312ha706d14_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lz4-c-1.10.0-h240833e_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/markupsafe-3.0.3-py312heb39f77_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-3.10.9-py312hb401068_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-base-3.10.9-py312h7609456_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/msgpack-python-1.1.2-py312hd099df3_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.6-hcc0dc9a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/nlohmann_json-3.12.0-h06076ce_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/numba-0.65.1-py312h704f9c4_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/numcodecs-0.16.5-py312h86abcb1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-1.26.4-py312he3a82b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/oniguruma-6.9.10-h6e16a3a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/openjpeg-2.5.4-h52bb76a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/openjph-0.27.3-h2c0e27e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.2-hc881268_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/orc-2.3.0-hb9b210e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pandas-3.0.3-py312h8e27051_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pcre2-10.47-h13923f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pillow-12.2.0-py312he84af14_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/prometheus-cpp-1.3.0-h7802330_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/psutil-7.2.2-py312hf7082af_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pthread-stubs-0.4-h00291cd_1002.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pyarrow-24.0.0-py312hb401068_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pyarrow-core-24.0.0-py312h3987635_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pyerfa-2.0.1.5-py310hcbffc5d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pynacl-1.6.2-py312h3bc9c61_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.12.13-ha9537fe_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-gssapi-1.11.1-py312h479078b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-htcondor-25.10.1-py312haf40f37_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lal-7.6.1-fftw_py312ha78c7a8_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalburst-2.0.6-py312h44bc254_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalframe-3.0.6-py312h025c719_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalinference-4.1.8-py312h025c719_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalinspiral-5.0.2-py312h025c719_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalmetaio-4.0.5-py312h025c719_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalpulsar-7.1.0-py312h025c719_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalsimulation-6.1.0-py312h1b08b07_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-ligo-lw-1.8.3-py312h01d7ebd_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py312h51361c1_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/qhull-2020.2-h3c5361c_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/rav1e-0.8.1-h618d828_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/re2-2025.11.05-h77e0585_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/regex-2026.5.9-py312h933eb07_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/reproject-0.19.0-py312h391ab28_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.8.0-np2py312h47bbdc5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.17.1-py312h6309490_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/scitokens-cpp-1.4.0-h1c2ca81_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/shapely-2.1.2-py312hd8edc82_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/snappy-1.2.2-h01f5ddf_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/svt-av1-4.0.1-h991f03e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/swig-4.4.1-hdac4ec2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/tornado-6.5.5-py312h933eb07_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/unicodedata2-17.0.1-py312h1a1c95f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/wrapt-2.2.1-py312h933eb07_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/xorg-libxau-1.0.12-h8616949_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/xorg-libxdmcp-1.1.5-h8616949_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/yaml-0.2.5-h4132b18_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/zfp-1.0.1-h1b13a81_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/zlib-1.3.2-hbb4bfdb_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/zlib-ng-2.3.3-h8bce59a_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda + - pypi: https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/09/c9/a9271d39c86d28d8bfacec831e416e42eb6d154f03605429631c4c08138b/pygsl_lite-0.1.8.tar.gz + - pypi: https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/19/cb/d31459da1f482479512c642c8463347b828004ecfa5bcd0a26ab57317add/spinsfast-2022.4.10-cp312-cp312-macosx_13_0_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/20/7a/1c6e3562dfd8950adbb11ffbc65d21e7c89d01a6e4f137fa981056de25c5/gitpython-3.1.50-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/25/ce/308e5e5da57515dd7cab3ec37ea2d5b8ff50bef1fcc8e6d31456f9fae08e/statsmodels-0.14.6-cp312-cp312-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/36/e1/84f6ede106afadc0e2c6a43d35b96ed6909149023f9a6a929175ad13a503/blosc2-4.3.3-cp312-cp312-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/42/c2/b602d2a5bbaaf9db1cff035ee6ddea76938c0b25ac1e6ac8ca288a073b47/otter_report-0.4.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/46/75/505cd72dfde3a1d7e719f840a2aa656a8c6b4c7b1b8c604a2d587cb1fc52/precession-2.1.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/59/91/aa6bde563e0085a02a435aa99b49ef75b0a4b062635e606dab23ce18d720/inflection-0.5.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/59/f3/77f85bfac22ee84b231ee8b147cdd948f679edf7d5a622acf9169d2a9b63/juliacall-0.9.34-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/65/90/774ddd08b2a1b41faa56da111f0fbfeb4f17ee537214c938ef41d61af949/ndindex-1.10.1-cp312-cp312-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/66/8f/2515bd2f110f6316f39568e25943577effffa5283b6376969e8277ba330d/sxs-2025.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/66/92/8e7104d2cf40ba2f88528c01e8a063aba7b223a21c0310fcf043b441f5da/sxscatalog-3.0.28-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6d/a3/e2d6b17b02b5c52ce6c68fce7f2f190e796c2c1c5419e675f6a947fdb78c/qnm-0.4.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6f/33/81570e825bdb2c0d7fa705087b704007bb7898c7dbb7a64f868da59252b3/pyseobnr-0.3.6.tar.gz + - pypi: https://files.pythonhosted.org/packages/78/17/853354204e1ca022d6b7d011ca7f3206c4f8faa3cc743e92609b49c1d83f/tinydb-4.8.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7d/4a/75aadc3b17430d4d9f3ad7bf0332a22b7ef78312e2b5b70774d2a301b9ec/spherical-1.0.18-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/86/9d/1f4495bf047e61e904a69242941aca04ad3c7453639d9019c5d8facb3862/juliapkg-0.1.23-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/f1/2033813456213ecfe8ccab18d78ef7b00af70d049c916cb167d592bdd6da/scri-2022.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/90/ad/cba91b3bcf04073e4d1655a5c1710ef3f457f56f7d1b79dcc3d72f4dd912/plotly-6.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/97/8d/17e56f2208b885aa8462277ae75a9bc82f7877bb52d88690636246853032/requests_pelican-0.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9d/20/c473fc04a371f5e2f8c5749e04505c13e7a8ede27c09e9f099b2ad6f43d6/numexpr-2.14.1-cp312-cp312-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a6/24/4d91e05817e92e3a61c8a21e08fd0f390f5301f1c448b137c57c4bc6e543/semver-3.0.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ad/28/9f3700cb784c0f0475de7074ed489702d5c6fb63a3df56b4cb4333b055fc/liquidpy-0.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/8a/eafc962e29c7fe800c702b4ae09cc358f1cbe40089e7a8cdac4f5b4c8c7a/requests_scitokens-0.2.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ba/b2/138065f2c50fd66b89623a5ee45d43d2341cfede4d7e0f7292075953ff12/numpy_quaternion-2024.0.13-cp312-cp312-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/bc/b2/d213f02254956b40f70dfaf2ba21ed0d1b7ed54b7cf361865d9d95fae867/asimov-0.6.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bd/b1/6bc28d9441c48744b6ecfb46af928fca427a3213282efa0faa5bea7eefd8/pesummary-1.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/d5/9b/7a0e11d7858feac24372ae770575802833436a050f93dfd012d8025e4ae9/deepdish-0.3.7-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d6/c6/7a6d8f56d3efe2ddb2bc659c2b1f9ba01b8a27174733526b57e181ba23b0/spherical_functions-2023.0.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/df/b1/3e3144c3ada2ae1a60a378396629064ef14e425e356c8e7c1db3a50ef351/python_gitlab-8.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e0/a9/023730ba63db1e494a271cb018dcd361bd2c917ba7004c3e49d5daf795a2/py_cpuinfo-9.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e3/47/a8d68aed3f6cb55bae90f504cd5ea3698a923c5fb6e9690726359e28eb1c/asimov_gwdata-0.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/70/ba4b949bdc0490ab78d545459acd7702b211dfccf7eb89bbc1060f52818d/patsy-1.0.2-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fa/bb/4a9cde6628563388db26fa86c64adb0f2475a757e72af0ec185fd520b72f/tables-3.11.1-cp311-abi3-macosx_10_9_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fd/86/e74734fcc564c7c9dfb47758ea9699c5294c68979fe0d0017454f3ca3f54/quaternionic-1.0.17-py3-none-any.whl + osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/noarch/antlr-python-runtime-4.9.3-pyhd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/arviz-0.23.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/astroplan-0.10.1-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/astropy-iers-data-0.2026.5.25.1.14.13-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.0-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/corner-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.3.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.3.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-image-2025.11.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dateparser-1.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_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/distributed-2026.3.0-pyhc364b38_0.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/dqsegdb2-1.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/future-1.0.0-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwdatafind-2.1.1-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwosc-0.8.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwpy-4.0.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h5netcdf-1.8.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hydra-core-1.3.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.15-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/igwn-auth-utils-1.4.0-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/imageio-2.37.0-pyhfb79c49_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_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/invoke-3.0.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jplephem-2.24-pyha4b2019_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/lalsuite-7.25-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ligo-gracedb-2.15.7-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ligotimegps-2.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/lscsoft-glue-4.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mpmath-1.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.21.2-pyhcf101f3_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/networkx-3.6.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/omegaconf-2.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/paramiko-5.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pims-0.7-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.1.1-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.6-pyhcf101f3_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/ptemcee-1.0.0-py_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/pyavm-0.9.9-pyhc455866_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.3-pyhfe8187e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-global-3.0.3-pyh648e204_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.13.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.0.3-pyhc364b38_1.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_abi-3.12-8_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/safe-netrc-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/scitokens-1.9.7-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_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/slicerator-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tifffile-2026.3.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.3-pyh8f84b5b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzlocal-5.3.1-pyh8f84b5b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.47.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-einstats-0.9.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zarr-3.1.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_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/aom-3.9.1-h7bae524_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/astropy-base-7.2.0-py312ha11c99a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/astropy-healpix-1.1.3-py312hf57c059_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.10.1-ha7d4cc1_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.13-h6ee9776_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.12.6-hc919400_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.2-h3e7f9b5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.7.0-h351c84d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.10.13-h95cdebe_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.26.3-h4137820_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.15.2-h8860bc9_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.12.2-h07b101a_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.4-h16f91aa_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.10-h3e7f9b5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.38.3-hba17502_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.747-h30a6df1_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.16.2-he5ae378_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.13.3-h810541e_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.16.0-h5446563_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.13.0-he467506_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.14.0-hdc9d693_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.5.0-py312h87c4bb7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bcrypt-5.0.0-py312h6ef9ec0_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/blosc-1.21.6-h7dd00d9_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-h7d5ae5b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py312h0dfefe5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brunsli-0.1-he0dfb12_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-blosc2-3.0.3-hf9886e1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py312h1b4d9a2_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cfitsio-4.6.2-h7200ff5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/charls-2.4.3-hf6b4638_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/chealpix-3.31.0-he71fa57_8.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py312h3093aea_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.14.0-py312h04c11ed_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cryptography-48.0.0-py312h1238841_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cytoolz-1.1.0-py312h2bbb03f_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/dav1d-1.2.1-hb547adb_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fftw-3.3.11-nompi_haf1500d_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.63.0-py312h04c11ed_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.14.1-h5afe852_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-h93a5062_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/google-crc32c-1.8.0-py312h090f823_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/h5py-3.13.0-nompi_py312hd7c5113_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-1.14.3-nompi_ha698983_109.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/healpy-1.18.1-py312hb9c4046_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/htcondor-25.10.1-py312h1f38498_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/htcondor-classads-25.10.1-h7a491f7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/htcondor-cli-25.10.1-py312h81bd7bf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/htcondor-utils-25.10.1-h91f37e5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/htgettoken-2.6-py312h81bd7bf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/igwn-ligolw-2.1.1-py312h8d938ae_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/igwn-segments-2.1.1-py312h8d938ae_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/imagecodecs-2026.5.10-py312ha5a633e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/jq-1.8.1-hbc156a2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/jxrlib-1.1-h93a5062_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.0-py312h3093aea_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h385eeb1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lal-7.6.1-fftw_py312h3b12eeb_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalapps-10.0.2-py312he91bef5_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalburst-2.0.6-py312h028adb6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalframe-3.0.6-py312h028adb6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalinference-4.1.8-nompi_py312h7a6eb69_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalinference-data-4.1.8-hce30654_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalinspiral-5.0.2-py312h028adb6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalmetaio-4.0.5-py312h028adb6_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalpulsar-7.1.0-py312hf4cc3a4_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalpulsar-data-7.1.0-hce30654_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalsimulation-6.1.0-py312hd4754c9_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalsimulation-data-6.1.0-hce30654_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.19.1-hdfa7624_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ldas-tools-al-2.7.0-he7ec6a4_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ldas-tools-framecpp-2.9.3-ha7f91e6_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.1.0-h1eee2c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260107.1-cxx17_h2062a1b_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/libarrow-24.0.0-h4c70b79_2_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-24.0.0-hee8fe31_2_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-compute-24.0.0-h3b6a98a_2_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-24.0.0-hee8fe31_2_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-24.0.0-h05be00f_2_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libavif16-1.4.1-hfce71f6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-7_h51639a9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libboost-1.88.0-h18cd856_6.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-7_hb0561ab_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcondor_utils-25.10.1-h3669c8c_0.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.20.0-hd5a2499_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.6-h55c6f16_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-hc11a715_0.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-h93a5062_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libframel-8.48.5-h2f1a0bb_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-hdfa99f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-3.5.0-he41eb1d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-3.5.0-ha114238_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.78.1-h3e3f78d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libhwy-1.4.0-ha332bbd_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/libjpeg-turbo-3.1.4.1-h84a0fba_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjxl-0.11.2-h934fa54_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblal-7.6.1-fftw_h8fcc6d2_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalburst-2.0.6-h2786bc8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalframe-3.0.6-h63b17c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalinference-4.1.8-h4d5f05e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalinspiral-5.0.2-h2786bc8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalmetaio-4.0.5-h5505292_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalpulsar-7.1.0-h8480f09_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalsimulation-6.1.0-py312h0975e73_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-7_hd9741b5_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmetaio-8.5.1-h57f5043_1003.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.33-openmp_he657e61_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-1.26.0-h08d5cc3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-headers-1.26.0-hce30654_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-24.0.0-h16c0493_2_cpu.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/libprotobuf-6.33.5-h4a5acfd_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h4c27e2a_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.22-h1a92334_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.1-h1b79a29_0.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/libthrift-0.22.0-h1fb9c8a_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.1-h4030677_1.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/libwebp-base-1.6.0-h07db88b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.1-h0ff4647_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.1-h9329255_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzopfli-1.0.3-h9f76cd9_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ligo-segments-1.4.0-py312hea69d52_6.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ligo.skymap-2.3.0-py312hd0a6ca1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.6-hc7d1edf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvmlite-0.47.0-py312h7ca588d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-4.4.5-py312h2b25a0d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py312h04c11ed_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.9-py312h1f38498_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.9-py312hf3defc7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.1.2-py312h84eede6_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h784d473_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numba-0.65.1-py312h2d3d6e9_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-1.26.4-py312h8442bc7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/oniguruma-6.9.10-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjph-0.27.3-h2a4d681_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.2-hd24854e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.3.0-hd11884d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.3-py312h6510ced_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/pillow-12.2.0-py312h4e908a4_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/psutil-7.2.2-py312hb3ab3e3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-24.0.0-py312h1f38498_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-24.0.0-py312h21b41d0_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyerfa-2.0.1.5-py310hbb12772_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pynacl-1.6.2-py312h8fd69cb_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.13-h8561d8f_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-gssapi-1.11.1-py312h858ef95_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-htcondor-25.10.1-py312hacc1691_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lal-7.6.1-fftw_py312h0f8b06b_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalburst-2.0.6-py312heec191f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalframe-3.0.6-py312he0011b7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalinference-4.1.8-py312he0011b7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalinspiral-5.0.2-py312he0011b7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalmetaio-4.0.5-py312he0011b7_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalpulsar-7.1.0-py312he0011b7_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalsimulation-6.1.0-py312he758b9a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-ligo-lw-1.8.3-py312hea69d52_4.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/qhull-2020.2-h420ef59_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rav1e-0.8.1-h8246384_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-ha480c28_1.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/regex-2026.5.9-py312h2bbb03f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproject-0.19.0-py312ha11c99a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.8.0-np2py312he5ca3e3_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py312h0f234b1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scitokens-cpp-1.4.0-h608d757_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/shapely-2.1.2-py312h35cd81b_2.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/svt-av1-4.0.1-h0cb729a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/swig-4.4.1-h4366dc5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.5-py312h2bbb03f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py312h2bbb03f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-2.2.1-py312h2bbb03f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h925e9cb_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zfp-1.0.1-ha86207d_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda + - pypi: https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/05/30/affbabf3c27fb501ec7b5808230c619d4d1a4525c07301074eb4bda92fa9/statsmodels-0.14.6-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/09/c9/a9271d39c86d28d8bfacec831e416e42eb6d154f03605429631c4c08138b/pygsl_lite-0.1.8.tar.gz + - pypi: https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/20/7a/1c6e3562dfd8950adbb11ffbc65d21e7c89d01a6e4f137fa981056de25c5/gitpython-3.1.50-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/42/c2/b602d2a5bbaaf9db1cff035ee6ddea76938c0b25ac1e6ac8ca288a073b47/otter_report-0.4.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/45/93/b6760dd1904c2a498e5f43d1bb436f59383c3ddea3815f1461dfaa259373/numexpr-2.14.1-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/46/75/505cd72dfde3a1d7e719f840a2aa656a8c6b4c7b1b8c604a2d587cb1fc52/precession-2.1.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/59/91/aa6bde563e0085a02a435aa99b49ef75b0a4b062635e606dab23ce18d720/inflection-0.5.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/59/f3/77f85bfac22ee84b231ee8b147cdd948f679edf7d5a622acf9169d2a9b63/juliacall-0.9.34-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/66/8f/2515bd2f110f6316f39568e25943577effffa5283b6376969e8277ba330d/sxs-2025.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/66/92/8e7104d2cf40ba2f88528c01e8a063aba7b223a21c0310fcf043b441f5da/sxscatalog-3.0.28-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6d/a3/e2d6b17b02b5c52ce6c68fce7f2f190e796c2c1c5419e675f6a947fdb78c/qnm-0.4.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6f/33/81570e825bdb2c0d7fa705087b704007bb7898c7dbb7a64f868da59252b3/pyseobnr-0.3.6.tar.gz + - pypi: https://files.pythonhosted.org/packages/78/17/853354204e1ca022d6b7d011ca7f3206c4f8faa3cc743e92609b49c1d83f/tinydb-4.8.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/78/74/6568c8d3aabf9982ab89fe3e378afbd7aad4894bde4570991a3246169ef4/tables-3.11.1-cp311-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7d/4a/75aadc3b17430d4d9f3ad7bf0332a22b7ef78312e2b5b70774d2a301b9ec/spherical-1.0.18-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/86/9d/1f4495bf047e61e904a69242941aca04ad3c7453639d9019c5d8facb3862/juliapkg-0.1.23-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/f1/2033813456213ecfe8ccab18d78ef7b00af70d049c916cb167d592bdd6da/scri-2022.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/90/ad/cba91b3bcf04073e4d1655a5c1710ef3f457f56f7d1b79dcc3d72f4dd912/plotly-6.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/97/8d/17e56f2208b885aa8462277ae75a9bc82f7877bb52d88690636246853032/requests_pelican-0.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a6/24/4d91e05817e92e3a61c8a21e08fd0f390f5301f1c448b137c57c4bc6e543/semver-3.0.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a8/30/12c87de87d6a3b523996737b97ba2e75972afac3c804249e5e61036e03d7/spinsfast-2022.4.10.tar.gz + - pypi: https://files.pythonhosted.org/packages/ad/28/9f3700cb784c0f0475de7074ed489702d5c6fb63a3df56b4cb4333b055fc/liquidpy-0.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/8a/eafc962e29c7fe800c702b4ae09cc358f1cbe40089e7a8cdac4f5b4c8c7a/requests_scitokens-0.2.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bc/b2/d213f02254956b40f70dfaf2ba21ed0d1b7ed54b7cf361865d9d95fae867/asimov-0.6.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bd/b1/6bc28d9441c48744b6ecfb46af928fca427a3213282efa0faa5bea7eefd8/pesummary-1.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/9b/7a0e11d7858feac24372ae770575802833436a050f93dfd012d8025e4ae9/deepdish-0.3.7-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d6/c6/7a6d8f56d3efe2ddb2bc659c2b1f9ba01b8a27174733526b57e181ba23b0/spherical_functions-2023.0.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/da/cc/ca6129956980fc6148d6b1983bc5dfc302f73bc734b376069eb560d170b6/blosc2-4.3.3-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/dc/9c/6a64269df4f032d883c5da72052850ef94fb79743b70606719c1ca579c79/numpy_quaternion-2024.0.13-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/df/b1/3e3144c3ada2ae1a60a378396629064ef14e425e356c8e7c1db3a50ef351/python_gitlab-8.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e0/a9/023730ba63db1e494a271cb018dcd361bd2c917ba7004c3e49d5daf795a2/py_cpuinfo-9.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e3/47/a8d68aed3f6cb55bae90f504cd5ea3698a923c5fb6e9690726359e28eb1c/asimov_gwdata-0.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ed/ee/a423e857f5b45da3adc8ddbcfbfd4a0e9a047edce3915d3e3d6e189b6bd9/ndindex-1.10.1-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/f1/70/ba4b949bdc0490ab78d545459acd7702b211dfccf7eb89bbc1060f52818d/patsy-1.0.2-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fd/86/e74734fcc564c7c9dfb47758ea9699c5294c68979fe0d0017454f3ca3f54/quaternionic-1.0.17-py3-none-any.whl + swig-pre44: + channels: + - url: https://conda.anaconda.org/conda-forge/ + indexes: + - https://pypi.org/simple + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.15.3-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aom-3.9.1-hac33072_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/astropy-base-7.2.0-py312h4f23490_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/astropy-healpix-1.1.3-py312h4f23490_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.9.6-hb9c0fe4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.13-h2c9d079_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.12.6-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.2-h8b1a151_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.9-h841be55_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.10.10-hf621c6d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.26.1-hc87160b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.14.0-ha25ca29_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.11.5-h9b5df67_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.4-h8b1a151_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.10-h8b1a151_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.37.3-hb153662_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.747-h133b1ee_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.16.2-h206d751_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.13.3-hed0cdb0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.16.0-hf824e48_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.13.0-ha7a2c86_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.14.0-h539c000_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.5.0-py312h90b7ffd_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bcrypt-5.0.0-py312h868fb18_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-hed03a55_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312hdb49522_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brunsli-0.1-hd1e3526_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-blosc2-3.0.3-hc31b594_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py312h460c074_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cfitsio-4.6.2-ha0b56bc_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/charls-2.4.3-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/chealpix-3.31.0-ha55a0e1_8.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312h0a2e395_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.14.0-py312h8a5da7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cryptography-48.0.0-py312ha4b625e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.1.0-py312h4c3975b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dav1d-1.2.1-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fftw-3.3.11-nompi_h3b011a4_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.0-h27c8c51_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py312h8a5da7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h480dda7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/google-crc32c-1.8.0-py312h03f33d3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-hecca717_2.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/h5py-3.13.0-nompi_py312hedeef09_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-12.2.0-h15599e2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.3-nompi_h2d575fe_109.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/healpy-1.18.1-py312he9d304c_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/htcondor-24.12.4-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/htcondor-classads-24.12.4-hf1419ba_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/htcondor-cli-24.12.4-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/htcondor-utils-24.12.4-h4a68bad_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/htgettoken-2.6-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/igwn-ligolw-2.1.1-py312h53c857e_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/igwn-segments-2.1.1-py312h53c857e_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/imagecodecs-2026.5.10-py312he34094b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/jq-1.8.1-h73b1eb8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/jxrlib-1.1-hd590300_3.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/kiwisolver-1.5.0-py312h0a2e395_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lal-7.6.1-fftw_py312ha96d5c7_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalapps-10.0.2-py312h760c983_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalburst-2.0.6-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalframe-3.0.6-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalinference-4.1.8-nompi_py312h1d0e7ed_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalinference-data-4.1.8-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalinspiral-5.0.2-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalmetaio-4.0.5-py312h66e93f0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalpulsar-7.1.0-py312he8860a6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalpulsar-data-7.1.0-ha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalsimulation-6.1.0-py312h09b83b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lalsimulation-data-6.1.0-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h0c24ade_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ldas-tools-al-2.7.0-hd827ec0_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ldas-tools-framecpp-2.9.3-he85ded8_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.1.0-hdb68285_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_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/libarrow-23.0.1-hf605819_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-23.0.1-h635bf11_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-compute-23.0.1-h53684a4_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-23.0.1-h635bf11_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-23.0.1-hb4dd7c2_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libavif16-1.4.1-hcfa2d63_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-7_h4a7cf45_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libboost-1.88.0-hed09d94_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libboost-python-1.88.0-py312hf890105_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-7_h0358290_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp21.1-21.1.8-default_h99862b1_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-22.1.6-default_h746c552_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcondor_utils-24.12.4-he8e5dd1_0.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-hb8b1518_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.18.0-h4e3cde8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.127-hb03c661_0.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/libegl-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libframel-8.48.5-ha02e5fa_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h73754d4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.2-h32235b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.39.0-h9d11ab5_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.39.0-hdbdcf42_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.78.1-h1d1128b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.4.0-h10be129_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.1.4.1-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.11.2-h174a0a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblal-7.6.1-fftw_hcbe9c14_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblalburst-2.0.6-h3773ae6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblalframe-3.0.6-h7c287a6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblalinference-4.1.8-h3773ae6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblalinspiral-5.0.2-h3773ae6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblalmetaio-4.0.5-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblalpulsar-7.1.0-h9345056_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblalsimulation-6.1.0-py312h09b83b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-7_h47877c9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.8-hf7376ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm22-22.1.6-hf7376ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmetaio-8.5.1-h0b0be96_1003.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/libntlm-1.8-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-1.21.0-h9692893_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-headers-1.21.0-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-23.0.1-h7376487_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_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/libpq-18.1-h5c52fec_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.33.5-h2b00c02_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h0dc7533_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.22-h280c20c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.1-h0c1763c_0.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-15.2.0-h934c35e_19.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_19.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.1-h9d88235_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.1-h5347b49_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.341.0-h5279c79_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.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.36-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.1-hca5e8e5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.1-ha9997c6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.1-h26afc86_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzopfli-1.0.3-h9c3ff4c_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/ligo-segments-1.4.0-py312h66e93f0_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ligo.skymap-2.3.0-py312h36415c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/llvmlite-0.47.0-py312h7424e68_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-4.4.5-py312h3d67a73_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.9-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.9-py312he3d6523_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.1.2-py312hd9148b4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/munge-0.5.16-h63a00c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numba-0.65.1-py312hd1dde6f_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-1.26.4-py312heda63a1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/oniguruma-6.9.10-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjph-0.27.3-h8d634f6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.3.0-h21090e2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.3-py312h8ecdadd_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.46-h1321c63_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.2.0-py312h50c33e8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.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/psutil-7.2.2-py312h5253ce2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-23.0.1-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-23.0.1-py312h2054cf2_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyerfa-2.0.1.5-py310h32771cd_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pynacl-1.6.2-py312hf34ed73_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.10.1-py312h9da60e5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.13-hd63d673_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-gssapi-1.11.1-py312h7cea900_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-htcondor-24.12.4-py312h1e35698_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lal-7.6.1-fftw_py312heccaa44_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalburst-2.0.6-py312h3684b61_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalframe-3.0.6-py312hc0a28a1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalinference-4.1.8-py312hc0a28a1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalinspiral-5.0.2-py312hc0a28a1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalmetaio-4.0.5-py312hc0a28a1_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalpulsar-7.1.0-py312hc0a28a1_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalsimulation-6.1.0-py312h09b83b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-ligo-lw-1.8.3-py312h66e93f0_4.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/qhull-2020.2-h434a139_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.10.1-hca0d9c9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rav1e-0.8.1-h1fbca29_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h5301d42_1.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/regex-2026.5.9-py312h4c3975b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/reproject-0.19.0-py312h4f23490_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.1-h1cbb8d7_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.8.0-np2py312h3226591_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py312h54fa4ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scitokens-cpp-1.4.0-h096d96b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py312h383787d_2.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/svt-av1-4.0.1-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/swig-4.3.1-hf1419ba_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.5-py312h4c3975b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py312h4c3975b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.25.0-hd6090a7_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-2.2.1-py312h4c3975b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.47-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.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_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.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-libxtst-1.2.5-hb9d3cd8_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zfp-1.0.1-h909a3a2_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.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/antlr-python-runtime-4.9.3-pyhd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/arviz-0.23.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/astroplan-0.10.1-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/astropy-iers-data-0.2026.5.25.1.14.13-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.0-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/corner-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.3.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.3.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-image-2025.11.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dateparser-1.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_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/distributed-2026.3.0-pyhc364b38_0.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/dqsegdb2-1.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/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/fsspec-2026.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/future-1.0.0-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwdatafind-2.1.1-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwosc-0.8.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwpy-4.0.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h5netcdf-1.8.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hydra-core-1.3.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.15-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/igwn-auth-utils-1.4.0-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/imageio-2.37.0-pyhfb79c49_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_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/invoke-3.0.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jplephem-2.24-pyha4b2019_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/lalsuite-7.25-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ligo-gracedb-2.15.7-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ligotimegps-2.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/lscsoft-glue-4.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mpmath-1.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.21.2-pyhcf101f3_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/networkx-3.6.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/omegaconf-2.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/paramiko-5.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pims-0.7-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.1.1-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.6-pyhcf101f3_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/ptemcee-1.0.0-py_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/pyavm-0.9.9-pyhc455866_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.3-pyhfe8187e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-global-3.0.3-pyh648e204_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.13.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.0.3-pyhc364b38_1.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_abi-3.12-8_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/safe-netrc-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/scitokens-1.9.7-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_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/slicerator-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tifffile-2026.3.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.3-pyh8f84b5b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzlocal-5.3.1-pyh8f84b5b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.47.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-einstats-0.9.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zarr-3.1.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda + - pypi: https://files.pythonhosted.org/packages/00/e2/1cb7cfb88fd3866062977a484bc0dda4e165361e949cc8e270302d05b007/spinsfast-2022.4.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/07/40/4058220b3d60890b62e0a2e8212e2546695827cf85e6186405ecf8ef33f1/numpy_quaternion-2024.0.13-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/09/c9/a9271d39c86d28d8bfacec831e416e42eb6d154f03605429631c4c08138b/pygsl_lite-0.1.8.tar.gz + - pypi: https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/20/7a/1c6e3562dfd8950adbb11ffbc65d21e7c89d01a6e4f137fa981056de25c5/gitpython-3.1.50-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/26/96/92119e6b279a88547a51eb99726ecae1ada839bf4fbcc2856503e2558e80/blosc2-4.3.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/27/ae/defd665dbbeb2fffa077491365ed160acaec49274ce8d4b979f55db71f18/ndindex-1.10.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/42/c2/b602d2a5bbaaf9db1cff035ee6ddea76938c0b25ac1e6ac8ca288a073b47/otter_report-0.4.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/46/75/505cd72dfde3a1d7e719f840a2aa656a8c6b4c7b1b8c604a2d587cb1fc52/precession-2.1.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/59/91/aa6bde563e0085a02a435aa99b49ef75b0a4b062635e606dab23ce18d720/inflection-0.5.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/59/f3/77f85bfac22ee84b231ee8b147cdd948f679edf7d5a622acf9169d2a9b63/juliacall-0.9.34-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/66/8f/2515bd2f110f6316f39568e25943577effffa5283b6376969e8277ba330d/sxs-2025.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/66/92/8e7104d2cf40ba2f88528c01e8a063aba7b223a21c0310fcf043b441f5da/sxscatalog-3.0.28-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6d/a3/e2d6b17b02b5c52ce6c68fce7f2f190e796c2c1c5419e675f6a947fdb78c/qnm-0.4.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6f/33/81570e825bdb2c0d7fa705087b704007bb7898c7dbb7a64f868da59252b3/pyseobnr-0.3.6.tar.gz + - pypi: https://files.pythonhosted.org/packages/78/17/853354204e1ca022d6b7d011ca7f3206c4f8faa3cc743e92609b49c1d83f/tinydb-4.8.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7d/4a/75aadc3b17430d4d9f3ad7bf0332a22b7ef78312e2b5b70774d2a301b9ec/spherical-1.0.18-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/81/68/dddd76117df2ef14c943c6bbb6618be5c9401280046f4ddfc9fb4596a1b8/statsmodels-0.14.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/86/9d/1f4495bf047e61e904a69242941aca04ad3c7453639d9019c5d8facb3862/juliapkg-0.1.23-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/f1/2033813456213ecfe8ccab18d78ef7b00af70d049c916cb167d592bdd6da/scri-2022.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/90/ad/cba91b3bcf04073e4d1655a5c1710ef3f457f56f7d1b79dcc3d72f4dd912/plotly-6.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/97/8d/17e56f2208b885aa8462277ae75a9bc82f7877bb52d88690636246853032/requests_pelican-0.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/99/29/c2dc674ea70fa9a4819417289a9c0d3e4780835beeed573eb66964cfb763/tables-3.11.1-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a6/24/4d91e05817e92e3a61c8a21e08fd0f390f5301f1c448b137c57c4bc6e543/semver-3.0.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ad/28/9f3700cb784c0f0475de7074ed489702d5c6fb63a3df56b4cb4333b055fc/liquidpy-0.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/8a/eafc962e29c7fe800c702b4ae09cc358f1cbe40089e7a8cdac4f5b4c8c7a/requests_scitokens-0.2.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bc/b2/d213f02254956b40f70dfaf2ba21ed0d1b7ed54b7cf361865d9d95fae867/asimov-0.6.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bd/b1/6bc28d9441c48744b6ecfb46af928fca427a3213282efa0faa5bea7eefd8/pesummary-1.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/9b/7a0e11d7858feac24372ae770575802833436a050f93dfd012d8025e4ae9/deepdish-0.3.7-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d6/c6/7a6d8f56d3efe2ddb2bc659c2b1f9ba01b8a27174733526b57e181ba23b0/spherical_functions-2023.0.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d9/43/560e9ba23c02c904b5934496486d061bcb14cd3ebba2e3cf0e2dccb6c22b/numexpr-2.14.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/df/b1/3e3144c3ada2ae1a60a378396629064ef14e425e356c8e7c1db3a50ef351/python_gitlab-8.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e0/a9/023730ba63db1e494a271cb018dcd361bd2c917ba7004c3e49d5daf795a2/py_cpuinfo-9.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e3/47/a8d68aed3f6cb55bae90f504cd5ea3698a923c5fb6e9690726359e28eb1c/asimov_gwdata-0.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/70/ba4b949bdc0490ab78d545459acd7702b211dfccf7eb89bbc1060f52818d/patsy-1.0.2-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fd/86/e74734fcc564c7c9dfb47758ea9699c5294c68979fe0d0017454f3ca3f54/quaternionic-1.0.17-py3-none-any.whl + osx-64: + - conda: https://conda.anaconda.org/conda-forge/noarch/antlr-python-runtime-4.9.3-pyhd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/arviz-0.23.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/astroplan-0.10.1-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/astropy-iers-data-0.2026.5.25.1.14.13-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.0-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/corner-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.3.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.3.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-image-2025.11.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dateparser-1.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_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/distributed-2026.3.0-pyhc364b38_0.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/dqsegdb2-1.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/future-1.0.0-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwdatafind-2.1.1-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwosc-0.8.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwpy-4.0.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h5netcdf-1.8.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hydra-core-1.3.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.15-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/igwn-auth-utils-1.4.0-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/imageio-2.37.0-pyhfb79c49_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_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/invoke-3.0.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jplephem-2.24-pyha4b2019_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/lalsuite-7.25-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ligo-gracedb-2.15.7-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ligotimegps-2.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/lscsoft-glue-4.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mpmath-1.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.21.2-pyhcf101f3_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/networkx-3.6.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/omegaconf-2.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/paramiko-5.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pims-0.7-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.1.1-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.6-pyhcf101f3_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/ptemcee-1.0.0-py_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/pyavm-0.9.9-pyhc455866_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.3-pyhfe8187e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-global-3.0.3-pyh648e204_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.13.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.0.3-pyhc364b38_1.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_abi-3.12-8_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/safe-netrc-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/scitokens-1.9.7-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_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/slicerator-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tifffile-2026.3.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.3-pyh8f84b5b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzlocal-5.3.1-pyh8f84b5b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.47.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-einstats-0.9.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zarr-3.1.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aom-3.9.1-hf036a51_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/astropy-base-7.2.0-py312h391ab28_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/astropy-healpix-1.1.3-py312h8ab2c85_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-auth-0.9.6-hbd79662_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-cal-0.9.13-hea39f9f_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-common-0.12.6-h8616949_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-compression-0.3.2-hb9ea233_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-event-stream-0.5.9-h8efd969_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-http-0.10.10-h8f73dec_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-io-0.26.1-hc95b61d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-mqtt-0.14.0-h2b5127a_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-s3-0.11.5-hafc236b_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-sdkutils-0.2.4-h901532c_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-checksums-0.2.10-h31279ed_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-crt-cpp-0.37.3-h4bfe737_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-sdk-cpp-1.11.747-h5d703ad_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/azure-core-cpp-1.16.2-h87f1c7e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/azure-identity-cpp-1.13.3-h1135191_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/azure-storage-blobs-cpp-12.16.0-hefc3566_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/azure-storage-common-cpp-12.13.0-h74781cd_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/azure-storage-files-datalake-cpp-12.14.0-h2303994_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/backports.zstd-1.5.0-py312h5f4ecc6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/bcrypt-5.0.0-py312h8a6388b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/blosc-1.21.6-hd145fbb_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-1.2.0-hf139dec_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-bin-1.2.0-h8616949_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-python-1.2.0-py312h4b46afd_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/brunsli-0.1-ha00ef93_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/c-ares-1.34.6-hb5e19a0_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/c-blosc2-3.0.3-h32e32c0_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/cffi-2.0.0-py312he90777b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/cfitsio-4.6.2-ha7f915d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/charls-2.4.3-hcc62823_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/chealpix-3.31.0-h93aae7a_8.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/contourpy-1.3.3-py312hb0c38da_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.14.0-py312heb39f77_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/cryptography-48.0.0-py312h1af399d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/cytoolz-1.1.0-py312h1a1c95f_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/dav1d-1.2.1-h0dc2134_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/fftw-3.3.11-nompi_h54214ab_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/fonttools-4.63.0-py312heb39f77_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/freetype-2.14.3-h694c41f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/geos-3.14.1-he483b9e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/gflags-2.2.2-hac325c4_1005.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/giflib-5.2.2-h10d778d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/glog-0.7.1-h2790a97_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/google-crc32c-1.8.0-py312hb9001e9_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/gsl-2.7-h93259b0_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-64/h5py-3.13.0-nompi_py312hea5ca7c_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-1.14.3-nompi_h1607680_109.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/healpy-1.18.1-py312h9f11423_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/htcondor-24.12.4-py312hb401068_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/htcondor-classads-24.12.4-hf470585_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/htcondor-cli-24.12.4-py312hb401068_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/htcondor-utils-24.12.4-h1cc2291_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/htgettoken-2.6-py312hb401068_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-75.1-h120a0e1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/igwn-ligolw-2.1.1-py312h0b9663a_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/igwn-segments-2.1.1-py312h0b9663a_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/imagecodecs-2026.5.10-py312hc8b613d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/jq-1.8.1-h2287256_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/jxrlib-1.1-h10d778d_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/kiwisolver-1.5.0-py312hb1dc2e7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/krb5-1.21.3-h37d8d59_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lal-7.6.1-fftw_py312h6f020d4_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalapps-10.0.2-py312h72dd72b_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalburst-2.0.6-py312h01d7ebd_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalframe-3.0.6-py312h01d7ebd_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalinference-4.1.8-nompi_py312h34ae946_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalinference-data-4.1.8-h694c41f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalinspiral-5.0.2-py312h01d7ebd_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalmetaio-4.0.5-py312h01d7ebd_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalpulsar-7.1.0-py312h5a434c1_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalpulsar-data-7.1.0-h694c41f_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalsimulation-6.1.0-py312haa07450_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lalsimulation-data-6.1.0-h694c41f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lcms2-2.19.1-h5ea7634_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/ldas-tools-al-2.7.0-hb7f9d93_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/ldas-tools-framecpp-2.9.3-hf716561_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lerc-4.1.0-h35c7297_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20260107.1-cxx17_h7ed6875_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libaec-1.1.5-he7c3a48_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-23.0.1-h47227bc_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-acero-23.0.1-hc9ab1f6_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-compute-23.0.1-h3b2c5b4_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-dataset-23.0.1-hc9ab1f6_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-substrait-23.0.1-h613493e_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libavif16-1.4.1-h9d3eb37_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-7_he492b99_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libboost-1.88.0-hf9ddd82_6.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libboost-python-1.88.0-py312h83679cb_6.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlicommon-1.2.0-h8616949_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlidec-1.2.0-h8616949_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlienc-1.2.0-h8616949_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-7_h9b27e0a_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libcondor_utils-24.12.4-h2da22b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libcrc32c-1.1.2-he49afe7_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.18.0-h9348e2b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.6-h19cb2f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libdeflate-1.25-h517ebb2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libedit-3.1.20250104-pl5321ha958ccf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libev-4.33-h10d778d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libevent-2.1.12-ha90c15b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.8.1-hcc62823_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libframel-8.48.5-hbd657fc_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libfreetype-2.14.3-h694c41f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libfreetype6-2.14.3-h58fbd8d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_19.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_19.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_19.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libgoogle-cloud-2.39.0-h11ac9da_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libgoogle-cloud-storage-2.39.0-hea209c6_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libgrpc-1.78.1-h147dede_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libhwy-1.4.0-hca42a69_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libiconv-1.18-h57a12c2_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libjpeg-turbo-3.1.4.1-ha1e9b39_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libjxl-0.11.2-h473410d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblal-7.6.1-fftw_ha907fd1_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblalburst-2.0.6-h8fa4168_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblalframe-3.0.6-h6e76fb1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblalinference-4.1.8-h506f03e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblalinspiral-5.0.2-h8fa4168_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblalmetaio-4.0.5-h6e16a3a_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblalpulsar-7.1.0-h0f29fec_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblalsimulation-6.1.0-py312h1b08b07_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-7_h859234e_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.3-hbb4bfdb_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libmetaio-8.5.1-h97fe558_1003.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.68.1-h70048d4_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.33-openmp_h9e49c7b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libopentelemetry-cpp-1.21.0-h7a0a166_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libopentelemetry-cpp-headers-1.21.0-h694c41f_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libparquet-23.0.1-hb3ef814_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libpng-1.6.58-he930e7c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libprotobuf-6.33.5-h29d92e8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libre2-11-2025.11.05-h6e8c311_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libsodium-1.0.22-ha3d0635_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.53.1-h77d7759_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libssh2-1.11.1-hed3591d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libthrift-0.22.0-hebea4ca_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libtiff-4.7.1-ha0a348c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libutf8proc-2.11.3-hc282952_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libwebp-base-1.6.0-hb807250_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libxcb-1.17.0-hf1f96e2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libxml2-16-2.15.1-ha1d9b0f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libxml2-2.15.1-h7b7ecba_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libzopfli-1.0.3-h046ec9c_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-64/ligo-segments-1.4.0-py312h01d7ebd_6.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/ligo.skymap-2.3.0-py312h2e055c9_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.6-h0d3cbff_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/llvmlite-0.47.0-py312ha5a82fe_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lz4-4.4.5-py312ha706d14_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/lz4-c-1.10.0-h240833e_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/markupsafe-3.0.3-py312heb39f77_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-3.10.9-py312hb401068_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-base-3.10.9-py312h7609456_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/msgpack-python-1.1.2-py312hd099df3_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.6-hcc0dc9a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/nlohmann_json-3.12.0-h06076ce_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/numba-0.65.1-py312h704f9c4_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/numcodecs-0.16.5-py312h86abcb1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-1.26.4-py312he3a82b2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/oniguruma-6.9.10-h6e16a3a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/openjpeg-2.5.4-h52bb76a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/openjph-0.27.3-h2c0e27e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.2-hc881268_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/orc-2.3.0-hb9b210e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pandas-3.0.3-py312h8e27051_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pcre2-10.46-ha3e7e28_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pillow-12.2.0-py312he84af14_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/prometheus-cpp-1.3.0-h7802330_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/psutil-7.2.2-py312hf7082af_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pthread-stubs-0.4-h00291cd_1002.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pyarrow-23.0.1-py312hb401068_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pyarrow-core-23.0.1-py312h3987635_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pyerfa-2.0.1.5-py310hcbffc5d_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pynacl-1.6.2-py312h3bc9c61_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.12.13-ha9537fe_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-gssapi-1.11.1-py312h972ca57_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-htcondor-24.12.4-py312h564a4e3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lal-7.6.1-fftw_py312ha78c7a8_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalburst-2.0.6-py312h44bc254_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalframe-3.0.6-py312h025c719_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalinference-4.1.8-py312h025c719_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalinspiral-5.0.2-py312h025c719_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalmetaio-4.0.5-py312h025c719_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalpulsar-7.1.0-py312h025c719_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalsimulation-6.1.0-py312h1b08b07_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-ligo-lw-1.8.3-py312h01d7ebd_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py312h51361c1_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/qhull-2020.2-h3c5361c_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/rav1e-0.8.1-h618d828_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/re2-2025.11.05-h77e0585_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/regex-2026.5.9-py312h933eb07_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/reproject-0.19.0-py312h391ab28_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.8.0-np2py312h47bbdc5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.17.1-py312h6309490_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/scitokens-cpp-1.4.0-h1c2ca81_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/shapely-2.1.2-py312hd8edc82_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/snappy-1.2.2-h01f5ddf_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/svt-av1-4.0.1-h991f03e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/swig-4.3.1-hf470585_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/tornado-6.5.5-py312h933eb07_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/unicodedata2-17.0.1-py312h1a1c95f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/wrapt-2.2.1-py312h933eb07_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/xorg-libxau-1.0.12-h8616949_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/xorg-libxdmcp-1.1.5-h8616949_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/yaml-0.2.5-h4132b18_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/zfp-1.0.1-h1b13a81_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/zlib-1.3.2-hbb4bfdb_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/zlib-ng-2.3.3-h8bce59a_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda + - pypi: https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/09/c9/a9271d39c86d28d8bfacec831e416e42eb6d154f03605429631c4c08138b/pygsl_lite-0.1.8.tar.gz + - pypi: https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/19/cb/d31459da1f482479512c642c8463347b828004ecfa5bcd0a26ab57317add/spinsfast-2022.4.10-cp312-cp312-macosx_13_0_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/20/7a/1c6e3562dfd8950adbb11ffbc65d21e7c89d01a6e4f137fa981056de25c5/gitpython-3.1.50-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/25/ce/308e5e5da57515dd7cab3ec37ea2d5b8ff50bef1fcc8e6d31456f9fae08e/statsmodels-0.14.6-cp312-cp312-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/36/e1/84f6ede106afadc0e2c6a43d35b96ed6909149023f9a6a929175ad13a503/blosc2-4.3.3-cp312-cp312-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/42/c2/b602d2a5bbaaf9db1cff035ee6ddea76938c0b25ac1e6ac8ca288a073b47/otter_report-0.4.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/46/75/505cd72dfde3a1d7e719f840a2aa656a8c6b4c7b1b8c604a2d587cb1fc52/precession-2.1.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/59/91/aa6bde563e0085a02a435aa99b49ef75b0a4b062635e606dab23ce18d720/inflection-0.5.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/59/f3/77f85bfac22ee84b231ee8b147cdd948f679edf7d5a622acf9169d2a9b63/juliacall-0.9.34-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/65/90/774ddd08b2a1b41faa56da111f0fbfeb4f17ee537214c938ef41d61af949/ndindex-1.10.1-cp312-cp312-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/66/8f/2515bd2f110f6316f39568e25943577effffa5283b6376969e8277ba330d/sxs-2025.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/66/92/8e7104d2cf40ba2f88528c01e8a063aba7b223a21c0310fcf043b441f5da/sxscatalog-3.0.28-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6d/a3/e2d6b17b02b5c52ce6c68fce7f2f190e796c2c1c5419e675f6a947fdb78c/qnm-0.4.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6f/33/81570e825bdb2c0d7fa705087b704007bb7898c7dbb7a64f868da59252b3/pyseobnr-0.3.6.tar.gz + - pypi: https://files.pythonhosted.org/packages/78/17/853354204e1ca022d6b7d011ca7f3206c4f8faa3cc743e92609b49c1d83f/tinydb-4.8.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7d/4a/75aadc3b17430d4d9f3ad7bf0332a22b7ef78312e2b5b70774d2a301b9ec/spherical-1.0.18-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/86/9d/1f4495bf047e61e904a69242941aca04ad3c7453639d9019c5d8facb3862/juliapkg-0.1.23-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/f1/2033813456213ecfe8ccab18d78ef7b00af70d049c916cb167d592bdd6da/scri-2022.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/90/ad/cba91b3bcf04073e4d1655a5c1710ef3f457f56f7d1b79dcc3d72f4dd912/plotly-6.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/97/8d/17e56f2208b885aa8462277ae75a9bc82f7877bb52d88690636246853032/requests_pelican-0.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9d/20/c473fc04a371f5e2f8c5749e04505c13e7a8ede27c09e9f099b2ad6f43d6/numexpr-2.14.1-cp312-cp312-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a6/24/4d91e05817e92e3a61c8a21e08fd0f390f5301f1c448b137c57c4bc6e543/semver-3.0.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ad/28/9f3700cb784c0f0475de7074ed489702d5c6fb63a3df56b4cb4333b055fc/liquidpy-0.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/8a/eafc962e29c7fe800c702b4ae09cc358f1cbe40089e7a8cdac4f5b4c8c7a/requests_scitokens-0.2.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ba/b2/138065f2c50fd66b89623a5ee45d43d2341cfede4d7e0f7292075953ff12/numpy_quaternion-2024.0.13-cp312-cp312-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/bc/b2/d213f02254956b40f70dfaf2ba21ed0d1b7ed54b7cf361865d9d95fae867/asimov-0.6.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bd/b1/6bc28d9441c48744b6ecfb46af928fca427a3213282efa0faa5bea7eefd8/pesummary-1.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/d5/9b/7a0e11d7858feac24372ae770575802833436a050f93dfd012d8025e4ae9/deepdish-0.3.7-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d6/c6/7a6d8f56d3efe2ddb2bc659c2b1f9ba01b8a27174733526b57e181ba23b0/spherical_functions-2023.0.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/df/b1/3e3144c3ada2ae1a60a378396629064ef14e425e356c8e7c1db3a50ef351/python_gitlab-8.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e0/a9/023730ba63db1e494a271cb018dcd361bd2c917ba7004c3e49d5daf795a2/py_cpuinfo-9.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e3/47/a8d68aed3f6cb55bae90f504cd5ea3698a923c5fb6e9690726359e28eb1c/asimov_gwdata-0.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/70/ba4b949bdc0490ab78d545459acd7702b211dfccf7eb89bbc1060f52818d/patsy-1.0.2-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fa/bb/4a9cde6628563388db26fa86c64adb0f2475a757e72af0ec185fd520b72f/tables-3.11.1-cp311-abi3-macosx_10_9_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fd/86/e74734fcc564c7c9dfb47758ea9699c5294c68979fe0d0017454f3ca3f54/quaternionic-1.0.17-py3-none-any.whl + osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/noarch/antlr-python-runtime-4.9.3-pyhd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/arviz-0.23.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/astroplan-0.10.1-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/astropy-iers-data-0.2026.5.25.1.14.13-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.0-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/corner-2.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.3.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.3.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dask-image-2025.11.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dateparser-1.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_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/distributed-2026.3.0-pyhc364b38_0.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/dqsegdb2-1.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/future-1.0.0-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwdatafind-2.1.1-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwosc-0.8.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gwpy-4.0.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h5netcdf-1.8.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hydra-core-1.3.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.15-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/igwn-auth-utils-1.4.0-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/imageio-2.37.0-pyhfb79c49_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_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/invoke-3.0.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jplephem-2.24-pyha4b2019_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/lalsuite-7.25-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ligo-gracedb-2.15.7-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ligotimegps-2.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/lscsoft-glue-4.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mpmath-1.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.21.2-pyhcf101f3_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/networkx-3.6.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/omegaconf-2.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/paramiko-5.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pims-0.7-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.1.1-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.6-pyhcf101f3_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/ptemcee-1.0.0-py_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/pyavm-0.9.9-pyhc455866_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.3-pyhfe8187e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-global-3.0.3-pyh648e204_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.13.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.0.3-pyhc364b38_1.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_abi-3.12-8_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/safe-netrc-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/scitokens-1.9.7-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_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/slicerator-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tifffile-2026.3.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.3-pyh8f84b5b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzlocal-5.3.1-pyh8f84b5b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.47.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-einstats-0.9.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zarr-3.1.5-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_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/aom-3.9.1-h7bae524_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/astropy-base-7.2.0-py312ha11c99a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/astropy-healpix-1.1.3-py312hf57c059_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.9.6-ha02d361_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.13-h6ee9776_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.12.6-hc919400_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.2-h3e7f9b5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.5.9-hd533cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.10.10-ha1850f6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.26.1-h4137820_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.14.0-h5721393_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.11.5-h7d214dc_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.4-h16f91aa_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.10-h3e7f9b5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.37.3-hcfbc53e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.747-h35a1687_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.16.2-he5ae378_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.13.3-h810541e_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.16.0-h5446563_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.13.0-he467506_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.14.0-hdc9d693_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.5.0-py312h87c4bb7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bcrypt-5.0.0-py312h6ef9ec0_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/blosc-1.21.6-h7dd00d9_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-h7d5ae5b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py312h0dfefe5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brunsli-0.1-he0dfb12_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-blosc2-3.0.3-hf9886e1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py312h1b4d9a2_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cfitsio-4.6.2-h7200ff5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/charls-2.4.3-hf6b4638_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/chealpix-3.31.0-he71fa57_8.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py312h3093aea_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.14.0-py312h04c11ed_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cryptography-48.0.0-py312h1238841_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cytoolz-1.1.0-py312h2bbb03f_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/dav1d-1.2.1-hb547adb_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fftw-3.3.11-nompi_haf1500d_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.63.0-py312h04c11ed_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.14.1-h5afe852_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-h93a5062_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/google-crc32c-1.8.0-py312h090f823_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/h5py-3.13.0-nompi_py312hd7c5113_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-1.14.3-nompi_ha698983_109.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/healpy-1.18.1-py312hb9c4046_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/htcondor-24.12.4-py312h1f38498_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/htcondor-classads-24.12.4-h0c2a548_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/htcondor-cli-24.12.4-py312h81bd7bf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/htcondor-utils-24.12.4-h9e88eaa_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/htgettoken-2.6-py312h81bd7bf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/igwn-ligolw-2.1.1-py312h8d938ae_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/igwn-segments-2.1.1-py312h8d938ae_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/imagecodecs-2026.5.10-py312ha5a633e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/jq-1.8.1-hbc156a2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/jxrlib-1.1-h93a5062_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.0-py312h3093aea_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lal-7.6.1-fftw_py312h3b12eeb_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalapps-10.0.2-py312he91bef5_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalburst-2.0.6-py312h028adb6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalframe-3.0.6-py312h028adb6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalinference-4.1.8-nompi_py312h7a6eb69_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalinference-data-4.1.8-hce30654_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalinspiral-5.0.2-py312h028adb6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalmetaio-4.0.5-py312h028adb6_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalpulsar-7.1.0-py312hf4cc3a4_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalpulsar-data-7.1.0-hce30654_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalsimulation-6.1.0-py312hd4754c9_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalsimulation-data-6.1.0-hce30654_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.19.1-hdfa7624_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ldas-tools-al-2.7.0-he7ec6a4_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ldas-tools-framecpp-2.9.3-ha7f91e6_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.1.0-h1eee2c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260107.1-cxx17_h2062a1b_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/libarrow-23.0.1-h5390cfe_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-23.0.1-hbf36091_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-compute-23.0.1-h4dbefc3_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-23.0.1-hbf36091_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-23.0.1-h05be00f_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libavif16-1.4.1-hfce71f6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-7_h51639a9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libboost-1.88.0-h18cd856_6.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libboost-python-1.88.0-py312h4c080bd_6.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-7_hb0561ab_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcondor_utils-24.12.4-h4785e8b_0.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.18.0-he38603e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.6-h55c6f16_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-hc11a715_0.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-h93a5062_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libframel-8.48.5-h2f1a0bb_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-hdfa99f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.39.0-h2f60c08_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.39.0-ha114238_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.78.1-h3e3f78d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libhwy-1.4.0-ha332bbd_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/libjpeg-turbo-3.1.4.1-h84a0fba_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjxl-0.11.2-h934fa54_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblal-7.6.1-fftw_h8fcc6d2_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalburst-2.0.6-h2786bc8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalframe-3.0.6-h63b17c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalinference-4.1.8-h4d5f05e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalinspiral-5.0.2-h2786bc8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalmetaio-4.0.5-h5505292_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalpulsar-7.1.0-h8480f09_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalsimulation-6.1.0-py312h0975e73_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-7_hd9741b5_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmetaio-8.5.1-h57f5043_1003.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.33-openmp_he657e61_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-1.21.0-h08d5cc3_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-headers-1.21.0-hce30654_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-23.0.1-h7a13205_4_cpu.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/libprotobuf-6.33.5-h4a5acfd_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h4c27e2a_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.22-h1a92334_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.1-h1b79a29_0.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/libthrift-0.22.0-h1fb9c8a_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.1-h4030677_1.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/libwebp-base-1.6.0-h07db88b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.1-h0ff4647_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.1-h9329255_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzopfli-1.0.3-h9f76cd9_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ligo-segments-1.4.0-py312hea69d52_6.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ligo.skymap-2.3.0-py312hd0a6ca1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.6-hc7d1edf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvmlite-0.47.0-py312h7ca588d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-4.4.5-py312h2b25a0d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py312h04c11ed_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.9-py312h1f38498_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.9-py312hf3defc7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.1.2-py312h84eede6_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h784d473_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numba-0.65.1-py312h2d3d6e9_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-1.26.4-py312h8442bc7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/oniguruma-6.9.10-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjph-0.27.3-h2a4d681_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.2-hd24854e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.3.0-hd11884d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.3-py312h6510ced_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.46-h7125dd6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.2.0-py312h4e908a4_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/psutil-7.2.2-py312hb3ab3e3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-23.0.1-py312h1f38498_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-23.0.1-py312h21b41d0_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyerfa-2.0.1.5-py310hbb12772_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pynacl-1.6.2-py312h8fd69cb_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.13-h8561d8f_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-gssapi-1.11.1-py312h72ca3cf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-htcondor-24.12.4-py312hd1c112e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lal-7.6.1-fftw_py312h0f8b06b_100.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalburst-2.0.6-py312heec191f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalframe-3.0.6-py312he0011b7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalinference-4.1.8-py312he0011b7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalinspiral-5.0.2-py312he0011b7_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalmetaio-4.0.5-py312he0011b7_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalpulsar-7.1.0-py312he0011b7_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalsimulation-6.1.0-py312he758b9a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-ligo-lw-1.8.3-py312hea69d52_4.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/qhull-2020.2-h420ef59_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rav1e-0.8.1-h8246384_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-ha480c28_1.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/regex-2026.5.9-py312h2bbb03f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproject-0.19.0-py312ha11c99a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.8.0-np2py312he5ca3e3_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py312h0f234b1_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scitokens-cpp-1.4.0-h608d757_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/shapely-2.1.2-py312h35cd81b_2.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/svt-av1-4.0.1-h0cb729a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/swig-4.3.1-h0c2a548_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.5-py312h2bbb03f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py312h2bbb03f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-2.2.1-py312h2bbb03f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h925e9cb_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zfp-1.0.1-ha86207d_5.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda + - pypi: https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/05/30/affbabf3c27fb501ec7b5808230c619d4d1a4525c07301074eb4bda92fa9/statsmodels-0.14.6-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/09/c9/a9271d39c86d28d8bfacec831e416e42eb6d154f03605429631c4c08138b/pygsl_lite-0.1.8.tar.gz + - pypi: https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/20/7a/1c6e3562dfd8950adbb11ffbc65d21e7c89d01a6e4f137fa981056de25c5/gitpython-3.1.50-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/42/c2/b602d2a5bbaaf9db1cff035ee6ddea76938c0b25ac1e6ac8ca288a073b47/otter_report-0.4.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/45/93/b6760dd1904c2a498e5f43d1bb436f59383c3ddea3815f1461dfaa259373/numexpr-2.14.1-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/46/75/505cd72dfde3a1d7e719f840a2aa656a8c6b4c7b1b8c604a2d587cb1fc52/precession-2.1.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/59/91/aa6bde563e0085a02a435aa99b49ef75b0a4b062635e606dab23ce18d720/inflection-0.5.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/59/f3/77f85bfac22ee84b231ee8b147cdd948f679edf7d5a622acf9169d2a9b63/juliacall-0.9.34-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/66/8f/2515bd2f110f6316f39568e25943577effffa5283b6376969e8277ba330d/sxs-2025.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/66/92/8e7104d2cf40ba2f88528c01e8a063aba7b223a21c0310fcf043b441f5da/sxscatalog-3.0.28-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6d/a3/e2d6b17b02b5c52ce6c68fce7f2f190e796c2c1c5419e675f6a947fdb78c/qnm-0.4.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6f/33/81570e825bdb2c0d7fa705087b704007bb7898c7dbb7a64f868da59252b3/pyseobnr-0.3.6.tar.gz + - pypi: https://files.pythonhosted.org/packages/78/17/853354204e1ca022d6b7d011ca7f3206c4f8faa3cc743e92609b49c1d83f/tinydb-4.8.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/78/74/6568c8d3aabf9982ab89fe3e378afbd7aad4894bde4570991a3246169ef4/tables-3.11.1-cp311-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7d/4a/75aadc3b17430d4d9f3ad7bf0332a22b7ef78312e2b5b70774d2a301b9ec/spherical-1.0.18-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/86/9d/1f4495bf047e61e904a69242941aca04ad3c7453639d9019c5d8facb3862/juliapkg-0.1.23-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/f1/2033813456213ecfe8ccab18d78ef7b00af70d049c916cb167d592bdd6da/scri-2022.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/90/ad/cba91b3bcf04073e4d1655a5c1710ef3f457f56f7d1b79dcc3d72f4dd912/plotly-6.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/97/8d/17e56f2208b885aa8462277ae75a9bc82f7877bb52d88690636246853032/requests_pelican-0.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a6/24/4d91e05817e92e3a61c8a21e08fd0f390f5301f1c448b137c57c4bc6e543/semver-3.0.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a8/30/12c87de87d6a3b523996737b97ba2e75972afac3c804249e5e61036e03d7/spinsfast-2022.4.10.tar.gz + - pypi: https://files.pythonhosted.org/packages/ad/28/9f3700cb784c0f0475de7074ed489702d5c6fb63a3df56b4cb4333b055fc/liquidpy-0.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/8a/eafc962e29c7fe800c702b4ae09cc358f1cbe40089e7a8cdac4f5b4c8c7a/requests_scitokens-0.2.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bc/b2/d213f02254956b40f70dfaf2ba21ed0d1b7ed54b7cf361865d9d95fae867/asimov-0.6.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bd/b1/6bc28d9441c48744b6ecfb46af928fca427a3213282efa0faa5bea7eefd8/pesummary-1.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/9b/7a0e11d7858feac24372ae770575802833436a050f93dfd012d8025e4ae9/deepdish-0.3.7-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d6/c6/7a6d8f56d3efe2ddb2bc659c2b1f9ba01b8a27174733526b57e181ba23b0/spherical_functions-2023.0.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/da/cc/ca6129956980fc6148d6b1983bc5dfc302f73bc734b376069eb560d170b6/blosc2-4.3.3-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/dc/9c/6a64269df4f032d883c5da72052850ef94fb79743b70606719c1ca579c79/numpy_quaternion-2024.0.13-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/df/b1/3e3144c3ada2ae1a60a378396629064ef14e425e356c8e7c1db3a50ef351/python_gitlab-8.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e0/a9/023730ba63db1e494a271cb018dcd361bd2c917ba7004c3e49d5daf795a2/py_cpuinfo-9.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e3/47/a8d68aed3f6cb55bae90f504cd5ea3698a923c5fb6e9690726359e28eb1c/asimov_gwdata-0.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ed/ee/a423e857f5b45da3adc8ddbcfbfd4a0e9a047edce3915d3e3d6e189b6bd9/ndindex-1.10.1-cp312-cp312-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/f1/70/ba4b949bdc0490ab78d545459acd7702b211dfccf7eb89bbc1060f52818d/patsy-1.0.2-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fd/86/e74734fcc564c7c9dfb47758ea9699c5294c68979fe0d0017454f3ca3f54/quaternionic-1.0.17-py3-none-any.whl +packages: +- conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda + build_number: 20 + sha256: 1dd3fffd892081df9726d7eb7e0dea6198962ba775bd88842135a4ddb4deb3c9 + md5: a9f577daf3de00bca7c3c76c0ecbd1de + depends: + - __glibc >=2.17,<3.0.a0 + - libgomp >=7.5.0 + constrains: + - openmp_impl <0.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 28948 + timestamp: 1770939786096 +- conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.15.3-hb03c661_0.conda + sha256: d88aa7ae766cf584e180996e92fef2aa7d8e0a0a5ab1d4d49c32390c1b5fff31 + md5: dcdc58c15961dbf17a0621312b01f5cb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: LGPL-2.1-or-later + license_family: GPL + purls: [] + size: 584660 + timestamp: 1768327524772 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aom-3.9.1-hac33072_0.conda + sha256: b08ef033817b5f9f76ce62dfcac7694e7b6b4006420372de22494503decac855 + md5: 346722a0be40f6edc53f12640d301338 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 2706396 + timestamp: 1718551242397 +- conda: https://conda.anaconda.org/conda-forge/linux-64/astropy-base-7.2.0-py312h4f23490_0.conda + sha256: e427fdcc692ac4c2c5be3d39886c5c86479ac924d07196a2375b66467d2f7ba7 + md5: a2826f4e9101450669fcfbe1b3b16820 + depends: + - __glibc >=2.17,<3.0.a0 + - astropy-iers-data >=0.2025.10.27.0.39.10 + - libgcc >=14 + - numpy >=1.23,<3 + - numpy >=1.24 + - packaging >=22.0.0 + - pyerfa >=2.0.1.1 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - pyyaml >=6.0.0 + constrains: + - astropy >=7.0.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/astropy?source=hash-mapping + size: 9530649 + timestamp: 1764120772709 +- conda: https://conda.anaconda.org/conda-forge/linux-64/astropy-healpix-1.1.3-py312h4f23490_0.conda + sha256: 382272564eba823850e8132c38fcfed7eecf3273d9c614822e7d29c3efa87423 + md5: c9280b8322e17ae119b457c7f39b222d + depends: + - __glibc >=2.17,<3.0.a0 + - astropy-base >=3 + - libgcc >=14 + - numpy >=1.23,<3 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/astropy-healpix?source=hash-mapping + size: 116115 + timestamp: 1768880212636 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.9.6-hb9c0fe4_1.conda + sha256: 84f9e2f83d9d93da551e0058c651015dd4bfd84256c6293db01130911c5e0f12 + md5: b1143a5b5a03ee174b3f3f7c49df3c09 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-http >=0.10.10,<0.10.11.0a0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-io >=0.26.1,<0.26.2.0a0 + - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 133452 + timestamp: 1771494128397 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.13-h2c9d079_1.conda + sha256: f21d648349a318f4ae457ea5403d542ba6c0e0343b8642038523dd612b2a5064 + md5: 3c3d02681058c3d206b562b2e3bc337f + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - libgcc >=14 + - openssl >=3.5.4,<4.0a0 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 56230 + timestamp: 1764593147526 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.12.6-hb03c661_0.conda + sha256: 926a5b9de0a586e88669d81de717c8dd3218c51ce55658e8a16af7e7fe87c833 + md5: e36ad70a7e0b48f091ed6902f04c23b8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 239605 + timestamp: 1763585595898 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.2-h8b1a151_0.conda + sha256: 1838bdc077b77168416801f4715335b65e9223f83641a2c28644f8acd8f9db0e + md5: f16f498641c9e05b645fe65902df661a + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 22278 + timestamp: 1767790836624 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.9-h841be55_2.conda + sha256: 179610f3c76238ca5fc4578384381bfd297e0ae1b96f6be52220c51f66b38131 + md5: 7e1ea1a67435a32e04305fda877acd1e + depends: + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-checksums >=0.2.10,<0.2.11.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-io >=0.26.1,<0.26.2.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 58801 + timestamp: 1771380394434 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.10.10-hf621c6d_0.conda + sha256: c61272aaff8aec10bb6a2afa62a7181e4ab00f4577350a8023431c74b9e91a72 + md5: 977e7d3cba1ef84fc088869b292672fe + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-io >=0.26.1,<0.26.2.0a0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + - aws-c-compression >=0.3.2,<0.3.3.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 225671 + timestamp: 1771421336421 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.26.1-hc87160b_2.conda + sha256: f224ba83bba90744cb8a85ce63075b2cd940cb8e232bc3e3f32d7aac833ab61c + md5: 3a7d90d34895728f0b69107602b6e189 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + - s2n >=1.7.1,<1.7.2.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 181558 + timestamp: 1773409398408 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.14.0-ha25ca29_1.conda + sha256: 2e9f2fc6ca8aa993b4962dbae711df69e8091b6a691bdcef8c8398dc81f923d7 + md5: a827b063719f5aac504d06ac77cc3125 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-http >=0.10.10,<0.10.11.0a0 + - aws-c-io >=0.26.1,<0.26.2.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 220029 + timestamp: 1771458032786 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.11.5-h9b5df67_3.conda + sha256: 4ec226a26aa1971d739f8600310b98f6ce8c24b93d88f8acb8387e9de0f4361e + md5: 1f130ac4eb7f1dea1ae4b5f53683e3aa + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-checksums >=0.2.10,<0.2.11.0a0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + - openssl >=3.5.5,<4.0a0 + - aws-c-http >=0.10.10,<0.10.11.0a0 + - aws-c-auth >=0.9.6,<0.9.7.0a0 + - aws-c-io >=0.26.1,<0.26.2.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 151354 + timestamp: 1771586299371 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.4-h8b1a151_4.conda + sha256: 9d62c5029f6f8219368a8665f0a549da572dc777f52413b7d75609cacdbc02cc + md5: c7e3e08b7b1b285524ab9d74162ce40b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - aws-c-common >=0.12.6,<0.12.7.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 59383 + timestamp: 1764610113765 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.10-h8b1a151_0.conda + sha256: 09472dd5fa4473cffd44741ee4c1112f2c76d7168d1343de53c2ad283dc1efa6 + md5: f8e1bcc5c7d839c5882e94498791be08 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 101435 + timestamp: 1771063496927 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.37.3-hb153662_0.conda + sha256: 25897c312a2fb52a1c36083d810ce11a3bb69bae23c31cd572d9629857547a56 + md5: 9ce778ddbd927385bf145224e291e2a1 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 + - aws-c-io >=0.26.1,<0.26.2.0a0 + - aws-c-s3 >=0.11.5,<0.11.6.0a0 + - aws-c-event-stream >=0.5.9,<0.5.10.0a0 + - aws-c-auth >=0.9.6,<0.9.7.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-mqtt >=0.14.0,<0.14.1.0a0 + - aws-c-http >=0.10.10,<0.10.11.0a0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 410093 + timestamp: 1771983327389 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.747-h133b1ee_1.conda + sha256: ac6090e6ab8cc2c927e7f62d90918de169cdd35e580fab8a95dc5d5ba8515fd0 + md5: 36afc05aac7c7f516749cdd3b5e978d9 + depends: + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-crt-cpp >=0.37.3,<0.37.4.0a0 + - libcurl >=8.18.0,<9.0a0 + - aws-c-event-stream >=0.5.9,<0.5.10.0a0 + - libzlib >=1.3.1,<2.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 3624539 + timestamp: 1772084530342 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.16.2-h206d751_0.conda + sha256: 321d1070905e467b6bc6f5067b97c1868d7345c272add82b82e08a0224e326f0 + md5: 5492abf806c45298ae642831c670bba0 + depends: + - __glibc >=2.17,<3.0.a0 + - libcurl >=8.18.0,<9.0a0 + - libgcc >=14 + - libstdcxx >=14 + - openssl >=3.5.4,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 348729 + timestamp: 1768837519361 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.13.3-hed0cdb0_1.conda + sha256: 2beb6ae8406f946b8963a67e72fe74453e1411c5ae7e992978340de6c512d13c + md5: 68bfb556bdf56d56e9f38da696e752ca + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.16.2,<1.16.3.0a0 + - libgcc >=14 + - libstdcxx >=14 + - openssl >=3.5.5,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 250511 + timestamp: 1770344967948 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.16.0-hf824e48_2.conda + sha256: ec278ffc9785cffeed097f57483fd0bc32c9083f56d7e6d95de46e560e4b49d1 + md5: 315c1c09f02a1efeb1b4d3dbcd2aa26a + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.16.2,<1.16.3.0a0 + - azure-storage-common-cpp >=12.13.0,<12.13.1.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + size: 580752 + timestamp: 1778727162545 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.13.0-ha7a2c86_0.conda + sha256: 67fa6937bc2f6400f5ff19727f5d926fdc68d7fce3aaeab4016f49bb93d89cbb + md5: a7e8cca395e0a1616b389749580b7804 + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.16.2,<1.16.3.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libxml2 + - libxml2-16 >=2.14.6 + - openssl >=3.5.6,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 159140 + timestamp: 1778661935076 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.14.0-h539c000_2.conda + sha256: 7765e9082b544555f74473ec21e366d92bb7688635d42d200860798e8b792a25 + md5: 245b61f9baef23f8f6cf04ccda928521 + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.16.2,<1.16.3.0a0 + - azure-storage-blobs-cpp >=12.16.0,<12.16.1.0a0 + - azure-storage-common-cpp >=12.13.0,<12.13.1.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + size: 302771 + timestamp: 1778763856084 +- conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.5.0-py312h90b7ffd_0.conda + sha256: a2b08a4e5e549b5f67c38edffd175437e2208547a7e67b5fa5373b67ef419e50 + md5: b31dba71fe091e7201826e57e0f7b261 + depends: + - python + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - zstd >=1.5.7,<1.6.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause AND MIT AND EPL-2.0 + purls: + - pkg:pypi/backports-zstd?source=hash-mapping + size: 239928 + timestamp: 1778594049826 +- conda: https://conda.anaconda.org/conda-forge/linux-64/bcrypt-5.0.0-py312h868fb18_1.conda + sha256: 22020286e3d27eba7c9efef79c1020782885992aea0e7d28d7274c4405001521 + md5: 8fbbd949c452efde5a75b62b22a88938 + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/bcrypt?source=hash-mapping + size: 292835 + timestamp: 1762497719397 +- conda: https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda + sha256: e7af5d1183b06a206192ff440e08db1c4e8b2ca1f8376ee45fb2f3a85d4ee45d + md5: 2c2fae981fd2afd00812c92ac47d023d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.1,<1.3.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 48427 + timestamp: 1733513201413 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-hed03a55_1.conda + sha256: e511644d691f05eb12ebe1e971fd6dc3ae55a4df5c253b4e1788b789bdf2dfa6 + md5: 8ccf913aaba749a5496c17629d859ed1 + depends: + - __glibc >=2.17,<3.0.a0 + - brotli-bin 1.2.0 hb03c661_1 + - libbrotlidec 1.2.0 hb03c661_1 + - libbrotlienc 1.2.0 hb03c661_1 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 20103 + timestamp: 1764017231353 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-hb03c661_1.conda + sha256: 64b137f30b83b1dd61db6c946ae7511657eead59fdf74e84ef0ded219605aa94 + md5: af39b9a8711d4a8d437b52c1d78eb6a1 + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlidec 1.2.0 hb03c661_1 + - libbrotlienc 1.2.0 hb03c661_1 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 21021 + timestamp: 1764017221344 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312hdb49522_1.conda + sha256: 49df13a1bb5e388ca0e4e87022260f9501ed4192656d23dc9d9a1b4bf3787918 + md5: 64088dffd7413a2dd557ce837b4cbbdb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - libbrotlicommon 1.2.0 hb03c661_1 + license: MIT + license_family: MIT + purls: + - pkg:pypi/brotli?source=hash-mapping + size: 368300 + timestamp: 1764017300621 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brunsli-0.1-hd1e3526_2.conda + sha256: b4831ac06bb65561342cedf3d219cf9b096f20b8d62cda74f0177dffed79d4d5 + md5: 5948f4fead433c6e5c46444dbfb01162 + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon >=1.2.0,<1.3.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + size: 168501 + timestamp: 1761758949420 +- conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda + sha256: 0b75d45f0bba3e95dc693336fa51f40ea28c980131fec438afb7ce6118ed05f6 + md5: d2ffd7602c02f2b316fd921d39876885 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: bzip2-1.0.6 + license_family: BSD + purls: [] + size: 260182 + timestamp: 1771350215188 +- conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda + sha256: cc9accf72fa028d31c2a038460787751127317dcfa991f8d1f1babf216bb454e + md5: 920bb03579f15389b9e512095ad995b7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 207882 + timestamp: 1765214722852 +- conda: https://conda.anaconda.org/conda-forge/linux-64/c-blosc2-3.0.3-hc31b594_0.conda + sha256: f02a289837c31d43405915b6efbe64f925dfb23e4ca2949f604fa0ab8a9094cc + md5: 4393b048c1e819f5262c05ccffb47265 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - lz4-c >=1.10.0,<1.11.0a0 + - zlib-ng >=2.3.3,<2.4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 378858 + timestamp: 1778843534911 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda + sha256: 3bd6a391ad60e471de76c0e9db34986c4b5058587fbf2efa5a7f54645e28c2c7 + md5: 09262e66b19567aff4f592fb53b28760 + depends: + - __glibc >=2.17,<3.0.a0 + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - freetype >=2.12.1,<3.0a0 + - icu >=75.1,<76.0a0 + - libexpat >=2.6.4,<3.0a0 + - libgcc >=13 + - libglib >=2.82.2,<3.0a0 + - libpng >=1.6.47,<1.7.0a0 + - libstdcxx >=13 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - pixman >=0.44.2,<1.0a0 + - xorg-libice >=1.1.2,<2.0a0 + - xorg-libsm >=1.2.5,<2.0a0 + - xorg-libx11 >=1.8.11,<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: [] + size: 978114 + timestamp: 1741554591855 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py312h460c074_1.conda + sha256: 7dafe8173d5f94e46cf9cd597cc8ff476a8357fbbd4433a8b5697b2864845d9c + md5: 648ee28dcd4e07a1940a17da62eccd40 + depends: + - __glibc >=2.17,<3.0.a0 + - libffi >=3.5.2,<3.6.0a0 + - libgcc >=14 + - pycparser + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + purls: + - pkg:pypi/cffi?source=hash-mapping + size: 295716 + timestamp: 1761202958833 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cfitsio-4.6.2-ha0b56bc_1.conda + sha256: 7f36c319c2821b53628a455ecf276bde7b5519f795fdf180c7fad264c7c68744 + md5: f91b4da04d6e52178e420f8dc871bbfc + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - libcurl >=8.14.1,<9.0a0 + - libgcc >=14 + - libzlib >=1.3.1,<2.0a0 + license: LicenseRef-fitsio + purls: [] + size: 765594 + timestamp: 1753286825348 +- conda: https://conda.anaconda.org/conda-forge/linux-64/charls-2.4.3-hecca717_0.conda + sha256: 53504e965499b4845ca3dc63d5905d5a1e686fcb9ab17e83c018efa479e787d0 + md5: 937ca49a245fcf2b88d51b6b52959426 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 161768 + timestamp: 1772712510770 +- conda: https://conda.anaconda.org/conda-forge/linux-64/chealpix-3.31.0-ha55a0e1_8.conda + sha256: 9a7760fbd4fb74079114e31b1723526474c6e3cc3c4d0915c2e62742a38e1283 + md5: cdc00718e8732ccc223cf95b7323e4fb + depends: + - __glibc >=2.17,<3.0.a0 + - cfitsio >=4.6.2,<4.6.3.0a0 + - libgcc >=14 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 40483 + timestamp: 1756896844897 +- conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312h0a2e395_4.conda + sha256: 62447faf7e8eb691e407688c0b4b7c230de40d5ecf95bf301111b4d05c5be473 + md5: 43c2bc96af3ae5ed9e8a10ded942aa50 + depends: + - numpy >=1.25 + - python + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/contourpy?source=hash-mapping + size: 320386 + timestamp: 1769155979897 +- conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.14.0-py312h8a5da7c_0.conda + sha256: 6ed9b689e92c5e202d834d5a4eeba990ecbfda104ef40ac455f3d006d439a926 + md5: c78de13127e71cb1e581f473ac76f360 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - tomli + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/coverage?source=hash-mapping + size: 390409 + timestamp: 1778444934285 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cryptography-48.0.0-py312ha4b625e_0.conda + sha256: 16d3d1e8df34a36430a28f423380fbd93abe5670ca7b52e9f4a64c091fd3ddd9 + md5: c5a8e173200adf567dc2818d8bf1325f + depends: + - __glibc >=2.17,<3.0.a0 + - cffi >=2.0 + - libgcc >=14 + - openssl >=3.5.6,<4.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - __glibc >=2.17 + license: Apache-2.0 AND BSD-3-Clause AND PSF-2.0 AND MIT + license_family: BSD + purls: + - pkg:pypi/cryptography?source=hash-mapping + size: 1912222 + timestamp: 1777966300032 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda + sha256: ee09ad7610c12c7008262d713416d0b58bf365bc38584dce48950025850bdf3f + md5: cae723309a49399d2949362f4ab5c9e4 + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libntlm >=1.8,<2.0a0 + - libstdcxx >=13 + - libxcrypt >=4.4.36 + - openssl >=3.5.0,<4.0a0 + license: BSD-3-Clause-Attribution + license_family: BSD + purls: [] + size: 209774 + timestamp: 1750239039316 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.1.0-py312h4c3975b_2.conda + sha256: 75b3d3c9497cded41e029b7a0ce4cc157334bbc864d6701221b59bb76af4396d + md5: 29fd0bdf551881ab3d2801f7deaba528 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - toolz >=0.10.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/cytoolz?source=hash-mapping + size: 623770 + timestamp: 1771855837505 +- conda: https://conda.anaconda.org/conda-forge/linux-64/dav1d-1.2.1-hd590300_0.conda + sha256: 22053a5842ca8ee1cf8e1a817138cdb5e647eb2c46979f84153f6ad7bde73020 + md5: 418c6ca5929a611cbd69204907a83995 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 760229 + timestamp: 1685695754230 +- conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda + sha256: 8bb557af1b2b7983cf56292336a1a1853f26555d9c6cecf1e5b2b96838c9da87 + md5: ce96f2f470d39bd96ce03945af92e280 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - libglib >=2.86.2,<3.0a0 + - libexpat >=2.7.3,<3.0a0 + license: AFL-2.1 OR GPL-2.0-or-later + purls: [] + size: 447649 + timestamp: 1764536047944 +- conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda + sha256: 1bcc132fbcc13f9ad69da7aa87f60ea41de7ed4d09f3a00ff6e0e70e1c690bc2 + md5: bfd56492d8346d669010eccafe0ba058 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 69544 + timestamp: 1739569648873 +- conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.4.0-hecca717_0.conda + sha256: 40cdd1b048444d3235069d75f9c8e1f286db567f6278a93b4f024e5642cfaecc + md5: dbe3ec0f120af456b3477743ffd99b74 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 71809 + timestamp: 1765193127016 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fftw-3.3.11-nompi_h3b011a4_100.conda + sha256: 6fd5d681fba20adaca771f138ac52dbf0a52e0dc2ac31b9ce7406068d102a9a7 + md5: 0717f4eb3d18259358a1fa77edb18917 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.3.0 + - libstdcxx >=14 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 2195198 + timestamp: 1776781721834 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.0-h27c8c51_0.conda + sha256: e798086d8a65d55dc4c51f5746705639c9a5f2eeb0b8fc50e6152cfc0d69a4e8 + md5: 06965b2f9854d0b15e0443ee81fe83dc + depends: + - __glibc >=2.17,<3.0.a0 + - libexpat >=2.8.1,<3.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libgcc >=14 + - libuuid >=2.42.1,<3.0a0 + - libzlib >=1.3.2,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 280882 + timestamp: 1779421631622 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py312h8a5da7c_0.conda + sha256: d235ae7075642044ceb3d922ef2a710a82665755ac9bbb7e8dad7daa72bc6d87 + md5: 294fb524171e2a2748cb7fe708aba826 + depends: + - __glibc >=2.17,<3.0.a0 + - brotli + - libgcc >=14 + - munkres + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - unicodedata2 >=15.1.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/fonttools?source=hash-mapping + size: 3007892 + timestamp: 1778770568019 +- conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_0.conda + sha256: c934c385889c7836f034039b43b05ccfa98f53c900db03d8411189892ced090b + md5: 8462b5322567212beeb025f3519fb3e2 + depends: + - libfreetype 2.14.3 ha770c72_0 + - libfreetype6 2.14.3 h73754d4_0 + license: GPL-2.0-only OR FTL + purls: [] + size: 173839 + timestamp: 1774298173462 +- conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h480dda7_0.conda + sha256: 08896dcd94e14a83f247e91748444e610f344ab42d80cbf2b6082b481c3f8f4b + md5: 4d4efd0645cd556fab54617c4ad477ef + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: LGPL-2.1-only + purls: [] + size: 1974942 + timestamp: 1761593471198 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda + sha256: 6c33bf0c4d8f418546ba9c250db4e4221040936aef8956353bc764d4877bc39a + md5: d411fc29e338efb48c5fd4576d71d881 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 119654 + timestamp: 1726600001928 +- conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda + sha256: aac402a8298f0c0cc528664249170372ef6b37ac39fdc92b40601a6aed1e32ff + md5: 3bf7b9fd5a7136126e0234db4b87c8b6 + depends: + - libgcc-ng >=12 + license: MIT + license_family: MIT + purls: [] + size: 77248 + timestamp: 1712692454246 +- conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda + sha256: dc824dc1d0aa358e28da2ecbbb9f03d932d976c8dca11214aa1dcdfcbd054ba2 + md5: ff862eebdfeb2fd048ae9dc92510baca + depends: + - gflags >=2.2.2,<2.3.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 143452 + timestamp: 1718284177264 +- conda: https://conda.anaconda.org/conda-forge/linux-64/google-crc32c-1.8.0-py312h03f33d3_1.conda + sha256: 3f962b2cbdc2aac3089a5c708477657266c0a665f0eed09981a34d1ab6793065 + md5: 68f704ea294dcec9e09edd9c3d233846 + depends: + - __glibc >=2.17,<3.0.a0 + - libcrc32c >=1.1.2,<1.2.0a0 + - libgcc >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/google-crc32c?source=hash-mapping + size: 24900 + timestamp: 1768549198202 +- conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-hecca717_2.conda + sha256: 25ba37da5c39697a77fce2c9a15e48cf0a84f1464ad2aafbe53d8357a9f6cc8c + md5: 2cd94587f3a401ae05e03a6caf09539d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: LGPL-2.0-or-later + license_family: LGPL + purls: [] + size: 99596 + timestamp: 1755102025473 +- 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 + purls: [] + size: 3376423 + timestamp: 1626369596591 +- conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.13.0-nompi_py312hedeef09_100.conda + sha256: 76bb853325f0c756599edb0be014723b01fea61e24817fd2f0b9ddfe4c570c0f + md5: ed73cf6f5e1ce5e823e6efcf54cbdc51 + depends: + - __glibc >=2.17,<3.0.a0 + - cached-property + - hdf5 >=1.14.3,<1.14.4.0a0 + - libgcc >=13 + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/h5py?source=hash-mapping + size: 1388165 + timestamp: 1739952623855 +- conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-12.2.0-h15599e2_0.conda + sha256: 6bd8b22beb7d40562b2889dc68232c589ff0d11a5ad3addd41a8570d11f039d9 + md5: b8690f53007e9b5ee2c2178dd4ac778c + depends: + - __glibc >=2.17,<3.0.a0 + - cairo >=1.18.4,<2.0a0 + - graphite2 >=1.3.14,<2.0a0 + - icu >=75.1,<76.0a0 + - libexpat >=2.7.1,<3.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libgcc >=14 + - libglib >=2.86.1,<3.0a0 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 2411408 + timestamp: 1762372726141 +- conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.3-nompi_h2d575fe_109.conda + sha256: e8669a6d76d415f4fdbe682507ac3a3b39e8f493d2f2bdc520817f80b7cc0753 + md5: e7a7a6e6f70553a31e6e79c65768d089 + depends: + - __glibc >=2.17,<3.0.a0 + - libaec >=1.1.3,<2.0a0 + - libcurl >=8.11.1,<9.0a0 + - libgcc >=13 + - libgfortran + - libgfortran5 >=13.3.0 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 3930078 + timestamp: 1737516601132 +- conda: https://conda.anaconda.org/conda-forge/linux-64/healpy-1.18.1-py312he9d304c_2.conda + sha256: 7f0663eaa865687d15d661d9ed21fa5d971fd01eb87df7cc94c91a8e0249193e + md5: 7a4d9a6043e509f64995f6ec6bd24ae7 + depends: + - __glibc >=2.17,<3.0.a0 + - astropy-base + - cfitsio >=4.6.2,<4.6.3.0a0 + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.3.0 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - matplotlib-base + - numpy >=1.23,<3 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - scipy + license: GPL-2.0-only + license_family: GPL + purls: + - pkg:pypi/healpy?source=hash-mapping + size: 2818682 + timestamp: 1757172185669 +- conda: https://conda.anaconda.org/conda-forge/linux-64/htcondor-24.12.4-py312h7900ff3_0.conda + sha256: a75dd3e93e5c9d6e097392adfc0849f4e7639b01a5f19aa88a026dd8d53ee02e + md5: 92495faf88b9faf255b9355229216b85 + depends: + - htcondor-classads 24.12.4 hf1419ba_0 + - htcondor-cli 24.12.4 py312h7900ff3_0 + - htcondor-utils 24.12.4 h4a68bad_0 + - libcondor_utils 24.12.4 he8e5dd1_0 + - python >=3.12,<3.13.0a0 + - python-htcondor 24.12.4 py312h1e35698_0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 23452 + timestamp: 1759416970099 +- conda: https://conda.anaconda.org/conda-forge/linux-64/htcondor-25.6.1-py312h7900ff3_0.conda + sha256: dd86f67163b26600b5dfbd298181c51544822da196100c2ad114a338a74b1109 + md5: bc786d8b18589585b49da9a56b4dac4e + depends: + - htcondor-classads 25.6.1 h793e66c_0 + - htcondor-cli 25.6.1 py312h7900ff3_0 + - htcondor-utils 25.6.1 h41609d3_0 + - libcondor_utils 25.6.1 h9f200e7_0 + - python >=3.12,<3.13.0a0 + - python-htcondor 25.6.1 py312h40fa4ac_0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 22697 + timestamp: 1769821218938 +- conda: https://conda.anaconda.org/conda-forge/linux-64/htcondor-classads-24.12.4-hf1419ba_0.conda + sha256: e1dfb61b1b8252ea5451601e9088e0080bd8e2a41aeaccf3b04c6e76cce512a0 + md5: 0771e88823ac06decd5b3e3d762202ac + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - pcre2 >=10.46,<10.47.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 11890389 + timestamp: 1759415794736 +- conda: https://conda.anaconda.org/conda-forge/linux-64/htcondor-classads-25.6.1-h793e66c_0.conda + sha256: f38207b06a5007416838b08d11ec5256669778597311854dc164041ca05e5e0b + md5: 6c3643ee222688f65d61b0d0debd2678 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - pcre2 >=10.47,<10.48.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 11933761 + timestamp: 1769820747387 +- conda: https://conda.anaconda.org/conda-forge/linux-64/htcondor-cli-24.12.4-py312h7900ff3_0.conda + sha256: 918add18c5a956ede11105bf703f82db6154593145a1114c6dc2656844bb1c93 + md5: d3ca4377b069c9e1dd15276595ff2ade + depends: + - python >=3.12,<3.13.0a0 + - python-htcondor 24.12.4 py312h1e35698_0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/htcondor-cli?source=hash-mapping + size: 153921 + timestamp: 1759416911673 +- conda: https://conda.anaconda.org/conda-forge/linux-64/htcondor-cli-25.6.1-py312h7900ff3_0.conda + sha256: fa5e32900829c8682062ce1d86b28988f2230fc61274237fe8acc408e44217a0 + md5: c3763e69f850d2be9272279b53304bb6 + depends: + - python >=3.12,<3.13.0a0 + - python-htcondor 25.6.1 py312h40fa4ac_0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/htcondor-cli?source=hash-mapping + size: 154972 + timestamp: 1769821144309 +- conda: https://conda.anaconda.org/conda-forge/linux-64/htcondor-utils-24.12.4-h4a68bad_0.conda + sha256: 1f9d644542720037a81bc5b5a31d49b274f67415e80270cdcc8b1975dce2756f + md5: 9cbb6b4b95410165adb24090d739bd67 + depends: + - __glibc >=2.17,<3.0.a0 + - htcondor-classads 24.12.4 hf1419ba_0 + - libcondor_utils 24.12.4 he8e5dd1_0 + - libcurl >=8.14.1,<9.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libuuid >=2.41.2,<3.0a0 + - openssl >=3.5.4,<4.0a0 + - scitokens-cpp >=1.1.3,<2.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 25774213 + timestamp: 1759415888312 +- conda: https://conda.anaconda.org/conda-forge/linux-64/htcondor-utils-25.6.1-h41609d3_0.conda + sha256: b28ab849e2b394e52af839965cf8962e13c9ce078cb51be00ec244f7513aa9b6 + md5: 847f563afbc1522cbc55c6456d8ece4a + depends: + - __glibc >=2.17,<3.0.a0 + - htcondor-classads 25.6.1 h793e66c_0 + - libcondor_utils 25.6.1 h9f200e7_0 + - libcurl >=8.18.0,<9.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libuuid >=2.41.3,<3.0a0 + - openssl >=3.5.5,<4.0a0 + - scitokens-cpp >=1.3.0,<2.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 26837458 + timestamp: 1769820828993 +- conda: https://conda.anaconda.org/conda-forge/linux-64/htgettoken-2.6-py312h7900ff3_0.conda + sha256: ed166a6c3b811acfd2269a218da9784e718975e6fe48b2fd9411d682c50510ac + md5: d341279ba84525b9ac1ea09e28c9022c + depends: + - jq + - paramiko + - python >=3.12,<3.13.0a0 + - python-gssapi + - python_abi 3.12.* *_cp312 + - urllib3 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/htgettoken?source=hash-mapping + size: 52468 + timestamp: 1768653480844 +- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda + sha256: 71e750d509f5fa3421087ba88ef9a7b9be11c53174af3aa4d06aff4c18b38e8e + md5: 8b189310083baabfb622af68fd9d3ae3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: MIT + license_family: MIT + purls: [] + size: 12129203 + timestamp: 1720853576813 +- conda: https://conda.anaconda.org/conda-forge/linux-64/igwn-ligolw-2.1.1-py312h53c857e_1.conda + sha256: c2fbe2a1ed34588dcf251ae4a2d8a74a589ae11ef5816e09e25a2a34f6f4c57c + md5: 441adf613cc02135af099f76402829b3 + depends: + - igwn-segments + - numpy + - python + - python-dateutil + - pyyaml + - tqdm + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - python_abi 3.12.* *_cp312 + license: GPL-3.0-or-later + license_family: GPL + purls: + - pkg:pypi/igwn-ligolw?source=hash-mapping + size: 2420527 + timestamp: 1771054285142 +- conda: https://conda.anaconda.org/conda-forge/linux-64/igwn-segments-2.1.1-py312h53c857e_2.conda + sha256: 0bb2fa6032dd90857774bcfab883e3384ad2aaf5ddc381514ea795355b937fbd + md5: d859fd276966f13a9b95397256ea45c0 + depends: + - python + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - python_abi 3.12.* *_cp312 + license: GPL-3.0-or-later + license_family: GPL + purls: + - pkg:pypi/igwn-segments?source=hash-mapping + size: 95108 + timestamp: 1770797826962 +- conda: https://conda.anaconda.org/conda-forge/linux-64/imagecodecs-2026.5.10-py312he34094b_0.conda + sha256: 413b83783b25864c46a1decd18f214505a439784ed7d38fc14832bc6c854e536 + md5: c20aa00cb836f0d13ea810d36c41fec9 + depends: + - __glibc >=2.17,<3.0.a0 + - blosc >=1.21.6,<2.0a0 + - brunsli >=0.1,<1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - c-blosc2 >=3.0.2,<3.1.0a0 + - charls >=2.4.3,<2.5.0a0 + - giflib >=5.2.2,<5.3.0a0 + - jxrlib >=1.1,<1.2.0a0 + - lcms2 >=2.19.1,<3.0a0 + - lerc >=4.1.0,<5.0a0 + - libaec >=1.1.5,<2.0a0 + - libavif16 >=1.4.1,<2.0a0 + - libbrotlicommon >=1.2.0,<1.3.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libdeflate >=1.25,<1.26.0a0 + - libgcc >=14 + - libjpeg-turbo >=3.1.4.1,<4.0a0 + - libjxl >=0.11,<1.0a0 + - liblzma >=5.8.3,<6.0a0 + - libpng >=1.6.58,<1.7.0a0 + - libstdcxx >=14 + - libtiff >=4.7.1,<4.8.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - libzopfli >=1.0.3,<1.1.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - numpy >=1.23,<3 + - openjpeg >=2.5.4,<3.0a0 + - openjph >=0.27.2,<0.28.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - snappy >=1.2.2,<1.3.0a0 + - zfp >=1.0.1,<2.0a0 + - zlib-ng >=2.3.3,<2.4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/imagecodecs?source=hash-mapping + size: 2288211 + timestamp: 1778501112233 +- conda: https://conda.anaconda.org/conda-forge/linux-64/jq-1.8.1-h73b1eb8_0.conda + sha256: ab26cb11ad0d10f5c6637d925b044c74a3eacb5825686d3720313b3cb6d40cef + md5: 2714e43bfc035f7ef26796632aa1b523 + depends: + - oniguruma 6.9.* + - libgcc >=13 + - __glibc >=2.17,<3.0.a0 + - oniguruma >=6.9.10,<6.10.0a0 + license: MIT + license_family: MIT + purls: [] + size: 313184 + timestamp: 1751447310552 +- conda: https://conda.anaconda.org/conda-forge/linux-64/jxrlib-1.1-hd590300_3.conda + sha256: 2057ca87b313bde5b74b93b0e696f8faab69acd4cb0edebb78469f3f388040c0 + md5: 5aeabe88534ea4169d4c49998f293d6c + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 239104 + timestamp: 1703333860145 +- conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda + sha256: 0960d06048a7185d3542d850986d807c6e37ca2e644342dd0c72feefcf26c2a4 + md5: b38117a3c920364aff79f870c984b4a3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: LGPL-2.1-or-later + purls: [] + size: 134088 + timestamp: 1754905959823 +- conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.0-py312h0a2e395_0.conda + sha256: eec7654c2d68f06590862c6e845cc70987b6d6559222b6f0e619dea4268f5dd5 + md5: cd74a9525dc74bbbf93cf8aa2fa9eb5b + depends: + - python + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/kiwisolver?source=hash-mapping + size: 77120 + timestamp: 1773067050308 +- conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + sha256: 99df692f7a8a5c27cd14b5fb1374ee55e756631b9c3d659ed3ee60830249b238 + md5: 3f43953b7d3fb3aaa1d0d0723d91e368 + depends: + - keyutils >=1.6.1,<2.0a0 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 1370023 + timestamp: 1719463201255 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lal-7.6.1-fftw_py312ha96d5c7_100.conda + sha256: c70ebfc1f85ff7a09d7667e6784b1c2aad80b70fd79e975e7e28d4dcdaa575fe + md5: 4b4ac92d2ad8037dc2b8bab94911c473 + depends: + - __glibc >=2.17,<3.0.a0 + - fftw >=3.3.10,<4.0a0 + - libgcc >=13 + - liblal 7.6.1 fftw_hcbe9c14_100 + - ligo-segments + - numpy + - python >=3.12,<3.13.0a0 + - python-lal 7.6.1 fftw_py312heccaa44_100 + - python-ligo-lw + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 83058 + timestamp: 1734696072240 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lalapps-10.0.2-py312h760c983_2.conda + sha256: 9545e7ce03526625cef73b8ea981d0c7f9273e6870cdb04ec4e8cec9ec07875c + md5: 97d6f5be8467b5cf6ef3d750297968d6 + depends: + - __glibc >=2.17,<3.0.a0 + - cfitsio >=4.6.2,<4.6.3.0a0 + - gsl >=2.7,<2.8.0a0 + - h5py + - lal >=7.6.0 + - lalburst >=2.0.0 + - lalframe >=3.0.0 + - lalinference >=4.1.0 + - lalinspiral >=5.0.0 + - lalmetaio >=4.0.0 + - lalpulsar >=7.1.0 + - lalsimulation >=6.1.0 + - libframel >=8.39.2 + - libframel >=8.41.3,<9.0a0 + - libgcc >=13 + - liblal >=7.6.1,<8.0a0 + - liblalburst >=2.0.6,<3.0a0 + - liblalframe >=3.0.6,<4.0a0 + - liblalinference >=4.1.8,<5.0a0 + - liblalinspiral >=5.0.2,<6.0a0 + - liblalmetaio >=4.0.5,<5.0a0 + - liblalpulsar >=7.1.0,<8.0a0 + - liblalsimulation >=6.1.0,<7.0a0 + - libmetaio >=8.4.0 + - libmetaio >=8.5.1,<9.0a0 + - ligo-segments + - numpy + - pillow + - python >=3.12,<3.13.0a0 + - python-ligo-lw >=1.7.0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 2100126 + timestamp: 1744228831801 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lalburst-2.0.6-py312h66e93f0_0.conda + sha256: df82091036013f14425593376e24cad5daf9256c6d9c952a41ba213b96ca49a7 + md5: ddb63c216e5314801600ea0d6f1bbfe4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalburst 2.0.6 h3773ae6_0 + - pillow + - python >=3.12,<3.13.0a0 + - python-lalburst 2.0.6 py312h3684b61_0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 55680 + timestamp: 1734709342842 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lalframe-3.0.6-py312h66e93f0_0.conda + sha256: 5c0c425a2b7939be2fc1eaf6ce2b4b17e040ce3c8e8e5cfc234fb6b3a16809ad + md5: 4186e287b6755e12d5e3f87c55e3662b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - liblal >=7.6.0,<8.0a0 + - liblalframe 3.0.6 h7c287a6_0 + - python >=3.12,<3.13.0a0 + - python-lalframe 3.0.6 py312hc0a28a1_0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 390097 + timestamp: 1734696415634 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lalinference-4.1.8-nompi_py312h1d0e7ed_100.conda + sha256: e663614560c069fac522d854de01828d3268b3a4878a9e284622999ffeaa836e + md5: 4dff3d43fde4faeabd234ac6a6e843eb + depends: + - __glibc >=2.17,<3.0.a0 + - astropy-base >=1.1.1 + - h5py + - icu >=75.1,<76.0a0 + - libgcc >=13 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalinference 4.1.8 h3773ae6_0 + - ligo-gracedb + - lscsoft-glue >=1.54.1 + - matplotlib-base >=1.2.0 + - python >=3.12,<3.13.0a0 + - python-lal >=7.6.0 + - python-lalinference 4.1.8 py312hc0a28a1_0 + - python-lalsimulation >=6.1.0 + - python-ligo-lw >=1.7.0 + - python_abi 3.12.* *_cp312 + - scipy >=0.9.0 + - six + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 141220 + timestamp: 1734713000016 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lalinference-data-4.1.8-ha770c72_0.conda + sha256: 40983befc7938fdc61421813e86a9994a5630e34c4639cf2f9cb29cf7a2005d7 + md5: eb6caf2436d2d69f3f65550bd6f8c1d2 + constrains: + - liblalinference >=3.0.3 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 31854 + timestamp: 1734712638698 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lalinspiral-5.0.2-py312h66e93f0_0.conda + sha256: b0e63d667d2c34aaad9b4124d932c4d1b617b1b425d2d7e048d65547af44dfeb + md5: 5c31999806fe276ee735aab649ed67d2 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalinspiral 5.0.2 h3773ae6_0 + - ligo-segments + - python >=3.12,<3.13.0a0 + - python-lalinspiral 5.0.2 py312hc0a28a1_0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 27425 + timestamp: 1734709092019 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lalmetaio-4.0.5-py312h66e93f0_2.conda + sha256: a1f6b79764b8b5a89ea5678edbf7a7d072bcf675d1801bdd7aed78c8391a831f + md5: 5dccf786ab17b4a5342621f5184b0aae + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - liblal >=7.6.0 + - liblal >=7.6.0,<8.0a0 + - liblalmetaio 4.0.5 hb9d3cd8_2 + - python >=3.12,<3.13.0a0 + - python-lalmetaio 4.0.5 py312hc0a28a1_2 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 22779 + timestamp: 1734275397248 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lalpulsar-7.1.0-py312he8860a6_1.conda + sha256: df5689a3cac43d1790c7c0c70152eab7a22d29d90767bb09fb6c6a8c0ebfa7dd + md5: a2ce619f3b4bd911a84f58c61b42c6f5 + depends: + - __glibc >=2.17,<3.0.a0 + - astropy-base + - cfitsio >=4.6.2,<4.6.3.0a0 + - fftw >=3.3.10,<4.0a0 + - gsl >=2.7,<2.8.0a0 + - jplephem + - lalinference >=4.1.0 + - libgcc >=13 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalframe >=3.0.0 + - liblalframe >=3.0.6,<4.0a0 + - liblalinference >=4.1.0 + - liblalinference >=4.1.8,<5.0a0 + - liblalpulsar 7.1.0 h9345056_1 + - liblalsimulation >=6.1.0 + - liblalsimulation >=6.1.0,<7.0a0 + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python-lal >=7.6.0 + - python-lalframe >=3.0.0 + - python-lalinference >=4.1.0 + - python-lalpulsar 7.1.0 py312hc0a28a1_1 + - python-lalsimulation >=6.1.0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 1736691 + timestamp: 1744218602788 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lalpulsar-data-7.1.0-ha770c72_1.conda + sha256: 413de5cfcb560f218275ea99dc6b1ace3e2335aa35167c842f5ac6cae51faeae + md5: 8eafdb67f9e00a6ddbf18d3640f970c7 + constrains: + - liblalpulsar >=4.0.0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 89997557 + timestamp: 1744215088937 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lalsimulation-6.1.0-py312h09b83b2_0.conda + sha256: 22dee83778bab61f36b2fa079bc266699f0ec79df20a77911f169c4602c8f9b6 + md5: a80afd999f3b7f29ff3f1c257c5ef194 + depends: + - __glibc >=2.17,<3.0.a0 + - gsl >=2.7,<2.8.0a0 + - libgcc >=13 + - liblal >=7.6.0 + - liblal >=7.6.0,<8.0a0 + - liblalsimulation 6.1.0 py312h09b83b2_0 + - mpmath >=1.0.0 + - python >=3.12,<3.13.0a0 + - python-lalsimulation 6.1.0 py312h09b83b2_0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 187962 + timestamp: 1734695978550 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lalsimulation-data-6.1.0-ha770c72_0.conda + sha256: 7040c7c0ec09a852b61236f7aae2d471f8ae3e2fd092cf1b3c42a5747fa2ef3e + md5: 91ea9f0cdd63b20be4e96e4e43025026 + constrains: + - liblalsimulation >=3.1.2 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 3662005 + timestamp: 1734695801709 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h0c24ade_0.conda + sha256: eb89c6c39f2f6a93db55723dbb2f6bba8c8e63e6312bf1abf13e6e9ff45849c8 + md5: f92f984b558e6e6204014b16d212b271 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libjpeg-turbo >=3.1.4.1,<4.0a0 + - libtiff >=4.7.1,<4.8.0a0 + license: MIT + license_family: MIT + purls: [] + size: 251086 + timestamp: 1778079286384 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda + sha256: 3d584956604909ff5df353767f3a2a2f60e07d070b328d109f30ac40cd62df6c + md5: 18335a698559cdbcd86150a48bf54ba6 + depends: + - __glibc >=2.17,<3.0.a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - binutils_impl_linux-64 2.45.1 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 728002 + timestamp: 1774197446916 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ldas-tools-al-2.7.0-hd827ec0_3.conda + sha256: cb41035f9c7bfa456409c06b7f093105cf5c8ccbb1876937f5767c2ad7ce83bb + md5: 754b9e851afaea050c27ee93cfb2efaa + depends: + - __glibc >=2.17,<3.0.a0 + - libboost >=1.88.0,<1.89.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 2493268 + timestamp: 1764932544027 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ldas-tools-framecpp-2.9.3-he85ded8_4.conda + sha256: abbee4227ac52d8d86ee48ac6b72a557087c4f4c3db744e792320d3db100e567 + md5: 41b300c2be384755cc193016f4eadb0c + depends: + - __glibc >=2.17,<3.0.a0 + - ldas-tools-al >=2.6.7 + - ldas-tools-al >=2.7.0,<2.8.0a0 + - libboost >=1.88.0,<1.89.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.2,<4.0a0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 37619803 + timestamp: 1756907139608 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.1.0-hdb68285_0.conda + sha256: f84cb54782f7e9cea95e810ea8fef186e0652d0fa73d3009914fa2c1262594e1 + md5: a752488c68f2e7c456bcbd8f16eec275 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 261513 + timestamp: 1773113328888 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda + sha256: a7a4481a4d217a3eadea0ec489826a69070fcc3153f00443aa491ed21527d239 + md5: 6f7b4302263347698fd24565fbf11310 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + constrains: + - libabseil-static =20260107.1=cxx17* + - abseil-cpp =20260107.1 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 1384817 + timestamp: 1770863194876 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda + sha256: 822e4ae421a7e9c04e841323526321185f6659222325e1a9aedec811c686e688 + md5: 86f7414544ae606282352fa1e116b41f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 36544 + timestamp: 1769221884824 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-23.0.1-hf605819_4_cpu.conda + build_number: 4 + sha256: ab98e6f69161ea682c57d081c756c2ebf32377af00f8935470146f31a7ea2671 + md5: 67a646e10ae89bca5ea51784160f6e2f + depends: + - __glibc >=2.17,<3.0.a0 + - aws-crt-cpp >=0.37.3,<0.37.4.0a0 + - aws-sdk-cpp >=1.11.747,<1.11.748.0a0 + - azure-core-cpp >=1.16.2,<1.16.3.0a0 + - azure-identity-cpp >=1.13.3,<1.13.4.0a0 + - azure-storage-blobs-cpp >=12.16.0,<12.16.1.0a0 + - azure-storage-files-datalake-cpp >=12.14.0,<12.14.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libgcc >=14 + - libgoogle-cloud >=2.39.0,<2.40.0a0 + - libgoogle-cloud-storage >=2.39.0,<2.40.0a0 + - libopentelemetry-cpp >=1.21.0,<1.22.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.3.0,<2.3.1.0a0 + - snappy >=1.2.2,<1.3.0a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - arrow-cpp <0.0a0 + - apache-arrow-proc =*=cpu + - parquet-cpp <0.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 6476655 + timestamp: 1773271630169 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-23.0.1-h635bf11_4_cpu.conda + build_number: 4 + sha256: 7839fbede8048f23e4ff2af07a9a434b522b5cc80e075e07c276e289d96fdc87 + md5: 710409d5a7908333633a845f9f665488 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 23.0.1 hf605819_4_cpu + - libarrow-compute 23.0.1 h53684a4_4_cpu + - libgcc >=14 + - libstdcxx >=14 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 608862 + timestamp: 1773271881163 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-compute-23.0.1-h53684a4_4_cpu.conda + build_number: 4 + sha256: 189d3b7f7292f19f37320b016feacd8f3636cc7377c49653da56141b158691ad + md5: c1c5caac6cd765606e5f3285d8fc3a40 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 23.0.1 hf605819_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: [] + size: 3004719 + timestamp: 1773271711321 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-23.0.1-h635bf11_4_cpu.conda + build_number: 4 + sha256: 6b8416458e69697196fd03bbf6642855f32a1735d8bd92127b51634b68b88bf7 + md5: 9b42e3a38fa716174aeeefa36b3c0e24 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 23.0.1 hf605819_4_cpu + - libarrow-acero 23.0.1 h635bf11_4_cpu + - libarrow-compute 23.0.1 h53684a4_4_cpu + - libgcc >=14 + - libparquet 23.0.1 h7376487_4_cpu + - libstdcxx >=14 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 608094 + timestamp: 1773271988669 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-23.0.1-hb4dd7c2_4_cpu.conda + build_number: 4 + sha256: bbffc6cc6deafe3993e66d5a7ccfbe520855e7c2e3797e5c54fa3f11cdd46c25 + md5: 466afa302c7781270b75dc504165ddc6 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libarrow 23.0.1 hf605819_4_cpu + - libarrow-acero 23.0.1 h635bf11_4_cpu + - libarrow-dataset 23.0.1 h635bf11_4_cpu + - libgcc >=14 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libstdcxx >=14 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 518669 + timestamp: 1773272024297 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libavif16-1.4.1-hcfa2d63_0.conda + sha256: e29d8ed0334305c6bafecb32f9a1967cfc0a081eac916e947a1f2f7c4bb41947 + md5: f79415aee8862b3af85ea55dea37e46b + depends: + - __glibc >=2.17,<3.0.a0 + - aom >=3.9.1,<3.10.0a0 + - dav1d >=1.2.1,<1.2.2.0a0 + - libgcc >=14 + - rav1e >=0.8.1,<0.9.0a0 + - svt-av1 >=4.0.1,<4.0.2.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 148710 + timestamp: 1774042709303 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-7_h4a7cf45_openblas.conda + build_number: 7 + sha256: 081c850f99bc355821fac9c6e3727d40b3f8ce3beb50a5437cf03726b611ff39 + md5: 955b44e8b00b7f7ef4ce0130cef12394 + depends: + - libopenblas >=0.3.33,<0.3.34.0a0 + - libopenblas >=0.3.33,<1.0a0 + constrains: + - libcblas 3.11.0 7*_openblas + - blas 2.307 openblas + - liblapack 3.11.0 7*_openblas + - liblapacke 3.11.0 7*_openblas + - mkl <2027 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 18716 + timestamp: 1778489854108 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libboost-1.88.0-hed09d94_6.conda + sha256: 40b334d77229fcceb51d911a153d7ab9ff4f6a6f90e938387bf29129ab956c58 + md5: 70675d70a76e1b5539b1f464fd5f02ba + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - icu >=75.1,<76.0a0 + - libgcc >=14 + - liblzma >=5.8.1,<6.0a0 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - boost-cpp <0.0a0 + license: BSL-1.0 + purls: [] + size: 2978265 + timestamp: 1763017293494 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libboost-python-1.88.0-py312hf890105_6.conda + sha256: 2892a87a3b29582d82aa1a949825adb436fe6454a12f502f1ee2cde5f8da6dee + md5: 595192338e11b3cf5041b4ca978142b9 + depends: + - __glibc >=2.17,<3.0.a0 + - libboost 1.88.0 hed09d94_6 + - libgcc >=14 + - libstdcxx >=14 + - numpy >=1.23,<3 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - boost <0.0a0 + - py-boost <0.0a0 + license: BSL-1.0 + purls: [] + size: 130214 + timestamp: 1763017581996 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda + sha256: 318f36bd49ca8ad85e6478bd8506c88d82454cc008c1ac1c6bf00a3c42fa610e + md5: 72c8fd1af66bd67bf580645b426513ed + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 79965 + timestamp: 1764017188531 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda + sha256: 12fff21d38f98bc446d82baa890e01fd82e3b750378fedc720ff93522ffb752b + md5: 366b40a69f0ad6072561c1d09301c886 + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.2.0 hb03c661_1 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 34632 + timestamp: 1764017199083 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda + sha256: a0c15c79997820bbd3fbc8ecf146f4fe0eca36cc60b62b63ac6cf78857f1dd0d + md5: 4ffbb341c8b616aa2494b6afb26a0c5f + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.2.0 hb03c661_1 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 298378 + timestamp: 1764017210931 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-7_h0358290_openblas.conda + build_number: 7 + sha256: 956ae0bb1ec8b0c3663d75b151aceb0521b54e513bf97f621a035f9c87037970 + md5: 0675639dc24cb0032f199e7ff68e4633 + depends: + - libblas 3.11.0 7_h4a7cf45_openblas + constrains: + - liblapacke 3.11.0 7*_openblas + - blas 2.307 openblas + - liblapack 3.11.0 7*_openblas + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 18675 + timestamp: 1778489861559 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp21.1-21.1.8-default_h99862b1_4.conda + sha256: aa61ed39af5944d05cd27672d2b520dbf2b3428575fbc7192acede709bed974a + md5: 80edae9c0a5feaf93fa2996c266d1ce7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libllvm21 >=21.1.8,<21.2.0a0 + - libstdcxx >=14 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 21066414 + timestamp: 1777010859192 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-22.1.6-default_h746c552_1.conda + sha256: 4f91ada190f6e78efd9179fd995c9c7fe2f4bb00aef977a164b1cba8d49973bb + md5: bf306e7b1c8c2c204b28138a08666bbd + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libllvm22 >=22.1.6,<22.2.0a0 + - libstdcxx >=14 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 12828360 + timestamp: 1779397396725 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcondor_utils-24.12.4-he8e5dd1_0.conda + sha256: 1adbf97f6ff94f80f3998e9399fc453c23a657b4e17943f55020bf54e21f10a1 + md5: 9a0a0ee03fd0e3363efa59a9880d5c88 + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + - htcondor-classads 24.12.4 hf1419ba_0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libuuid >=2.41.2,<3.0a0 + - munge + - openssl >=3.5.4,<4.0a0 + - pcre2 >=10.46,<10.47.0a0 + - scitokens-cpp >=0.5.0 + - scitokens-cpp >=1.1.3,<2.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 28064884 + timestamp: 1759415826899 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcondor_utils-25.6.1-h9f200e7_0.conda + sha256: aef6f663d5e3278de234ffd77e2ac9899800b0b4477f72567dd74e21ee45614f + md5: e0a13fcfec34991a111ccbb19529e5a9 + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + - htcondor-classads 25.6.1 h793e66c_0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libuuid >=2.41.3,<3.0a0 + - munge + - openssl >=3.5.5,<4.0a0 + - pcre2 >=10.47,<10.48.0a0 + - scitokens-cpp >=0.5.0 + - scitokens-cpp >=1.3.0,<2.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 28775724 + timestamp: 1769820778309 +- 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: [] + size: 20440 + timestamp: 1633683576494 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda + sha256: cb83980c57e311783ee831832eb2c20ecb41e7dee6e86e8b70b8cef0e43eab55 + md5: d4a250da4737ee127fb1fa6452a9002e + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 4523621 + timestamp: 1749905341688 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.18.0-h4e3cde8_0.conda + sha256: 5454709d9fb6e9c3dd6423bc284fa7835a7823bfa8323f6e8786cdd555101fab + md5: 0a5563efed19ca4461cf927419b6eb73 + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=14 + - libnghttp2 >=1.67.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.4,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: curl + license_family: MIT + purls: [] + size: 462942 + timestamp: 1767821743793 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda + sha256: aa8e8c4be9a2e81610ddf574e05b64ee131fab5e0e3693210c9d6d2fba32c680 + md5: 6c77a605a7a689d17d4819c0f8ac9a00 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 73490 + timestamp: 1761979956660 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.127-hb03c661_0.conda + sha256: 7d3187c11b7ae66c5595a8afd5a7ce352a490527fdf6614cab129bc7f2c16ba3 + md5: d8d16b9b32a3c5df7e5b3350e2cbe058 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libpciaccess >=0.19,<0.20.0a0 + license: MIT + license_family: MIT + purls: [] + size: 311505 + timestamp: 1778975798004 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda + sha256: d789471216e7aba3c184cd054ed61ce3f6dac6f87a50ec69291b9297f8c18724 + md5: c277e0a4d549b03ac1e9d6cbbe3d017b + depends: + - ncurses + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - ncurses >=6.5,<7.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 134676 + timestamp: 1738479519902 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_3.conda + sha256: 9a25ea93e8272785405a21d30f84e620befb1d545f6dfaae18f06103b5df0443 + md5: 75e9f795be506c96dd43cb09c7c8d557 + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_3 + license: LicenseRef-libglvnd + purls: [] + size: 46500 + timestamp: 1779728188901 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + sha256: 1cd6048169fa0395af74ed5d8f1716e22c19a81a8a36f934c110ca3ad4dd27b4 + md5: 172bf1cd1ff8629f2b1179945ed45055 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 112766 + timestamp: 1702146165126 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda + sha256: 2e14399d81fb348e9d231a82ca4d816bf855206923759b69ad006ba482764131 + md5: a1cfcc585f0c42bf8d5546bb1dfb668d + depends: + - libgcc-ng >=12 + - openssl >=3.1.1,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 427426 + timestamp: 1685725977222 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda + sha256: 363018b25fdb5534c79783d912bd4b685a3547f4fc5996357ad548899b0ee8e7 + md5: 93764a5ca80616e9c10106cdaec92f74 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - expat 2.8.1.* + license: MIT + license_family: MIT + purls: [] + size: 77294 + timestamp: 1779278686680 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda + sha256: 31f19b6a88ce40ebc0d5a992c131f57d919f73c0b92cd1617a5bec83f6e961e6 + md5: a360c33a5abe61c07959e449fa1453eb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 58592 + timestamp: 1769456073053 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libframel-8.48.5-ha02e5fa_0.conda + sha256: eff718a2ec302a5168a023e8b10e538edc27dc81e75d5fed57680a34ecf565e6 + md5: 75cb7a46d09c6c48494b3239751b9d20 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: LGPL-2.1-or-later + license_family: LGPL + purls: [] + size: 482048 + timestamp: 1767613080978 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_0.conda + sha256: 38f014a7129e644636e46064ecd6b1945e729c2140e21d75bb476af39e692db2 + md5: e289f3d17880e44b633ba911d57a321b + depends: + - libfreetype6 >=2.14.3 + license: GPL-2.0-only OR FTL + purls: [] + size: 8049 + timestamp: 1774298163029 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h73754d4_0.conda + sha256: 16f020f96da79db1863fcdd8f2b8f4f7d52f177dd4c58601e38e9182e91adf1d + md5: fb16b4b69e3f1dcfe79d80db8fd0c55d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libpng >=1.6.55,<1.7.0a0 + - libzlib >=1.3.2,<2.0a0 + constrains: + - freetype >=2.14.3 + license: GPL-2.0-only OR FTL + purls: [] + size: 384575 + timestamp: 1774298162622 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda + sha256: 8e0a3b5e41272e5678499b5dfc4cddb673f9e935de01eb0767ce857001229f46 + md5: 57736f29cc2b0ec0b6c2952d3f101b6a + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + constrains: + - libgcc-ng ==15.2.0=*_19 + - libgomp 15.2.0 he0feb66_19 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 1041084 + timestamp: 1778269013026 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda + sha256: 9dcf54adfaa5e861123c2da4f2f0451a685464ea7e5a41ad91cf67b31d658d98 + md5: 331ee9b72b9dff570d56b1302c5ab37d + depends: + - libgcc 15.2.0 he0feb66_19 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 27694 + timestamp: 1778269016987 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda + sha256: 561a42758ef25b9ce308c4e2cf56daee4f06138385a17e29a492cd928e00be6f + md5: 42bf7eca1a951735fa06c0e3c0d5c8e6 + depends: + - libgfortran5 15.2.0 h68bc16d_19 + constrains: + - libgfortran-ng ==15.2.0=*_19 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 27655 + timestamp: 1778269042954 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda + sha256: 057978bb69fea29ed715a9b98adf71015c31baecc4aeb2bfc20d4fd5d83579d4 + md5: 85072b0ad177c966294f129b7c04a2d5 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15.2.0 + constrains: + - libgfortran 15.2.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 2483673 + timestamp: 1778269025089 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_3.conda + sha256: ec353b3076ed8e357ed961d0e9ff6997491cade0e603de5bd18a2e301ac78ebd + md5: f25206d7322c0e9648e8b83694d143ab + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_3 + - libglx 1.7.0 ha4b6fd6_3 + license: LicenseRef-libglvnd + purls: [] + size: 133469 + timestamp: 1779728207669 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.2-h32235b2_0.conda + sha256: 918306d6ed211ab483e4e19368e5748b265d24e75c88a1c66a61f72b9fa30b29 + md5: 0cb0612bc9cb30c62baf41f9d600611b + depends: + - __glibc >=2.17,<3.0.a0 + - libffi >=3.5.2,<3.6.0a0 + - libgcc >=14 + - libiconv >=1.18,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - pcre2 >=10.46,<10.47.0a0 + constrains: + - glib 2.86.2 *_0 + license: LGPL-2.1-or-later + purls: [] + size: 3974801 + timestamp: 1763672326986 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.1-h0d30a3d_2.conda + sha256: 33eb5d5310a5c2c0a4707a0afa644801c2e08c8f70c45e1f62f354116dfe0970 + md5: 17d484ab9c8179c6a6e5b7dbb5065afc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libffi >=3.5.2,<3.6.0a0 + - pcre2 >=10.47,<10.48.0a0 + - libzlib >=1.3.2,<2.0a0 + - libiconv >=1.18,<2.0a0 + constrains: + - glib >2.66 + license: LGPL-2.1-or-later + purls: [] + size: 4754097 + timestamp: 1778508800134 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_3.conda + sha256: e019ebe4e3f5cdf23e2f5e58ddf7ade27988c53820115b17b98f218ebcc87748 + md5: eb83f3f8cecc3e9bff9e250817fc69b6 + depends: + - __glibc >=2.17,<3.0.a0 + license: LicenseRef-libglvnd + purls: [] + size: 133586 + timestamp: 1779728183422 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_3.conda + sha256: 2f74713c9ca408ea84e88a30a9028153e7b553e8bb42e06139eac9a753c27da9 + md5: ec3c4350aa0261bf7f87b8ca15c8e80e + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_3 + - xorg-libx11 >=1.8.13,<2.0a0 + license: LicenseRef-libglvnd + purls: [] + size: 76586 + timestamp: 1779728199059 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda + sha256: 5abe4ab9d93f6c9757d654f1969ae2267d4505315c1f2f8fe705fd60af084f1b + md5: faac990cb7aedc7f3a2224f2c9b0c26c + depends: + - __glibc >=2.17,<3.0.a0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 603817 + timestamp: 1778268942614 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.39.0-h9d11ab5_1.conda + sha256: 44f8e354431d2336475465ec8d71df7f3dea1397e70df0718c2ac75137976c63 + md5: cd398eb8374fb626a710b7a35b7ffa98 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20260107.0,<20260108.0a0 + - libcurl >=8.18.0,<9.0a0 + - libgcc >=14 + - libgrpc >=1.78.0,<1.79.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libstdcxx >=14 + - openssl >=3.5.5,<4.0a0 + constrains: + - libgoogle-cloud 2.39.0 *_1 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 1307253 + timestamp: 1770461665848 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.39.0-hdbdcf42_1.conda + sha256: 2cce946ebf40b0b5fdb3e82c8a9f90ca28cd62abd281b20713067cc69a75c441 + md5: 384a1730ea66a72692e377cb45996d61 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libgcc >=14 + - libgoogle-cloud 2.39.0 h9d11ab5_1 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + purls: [] + size: 803453 + timestamp: 1770461856392 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.78.1-h1d1128b_0.conda + sha256: 5bb935188999fd70f67996746fd2dca85ec6204289e11695c316772e19451eb8 + md5: b5fb6d6c83f63d83ef2721dca6ff7091 + depends: + - __glibc >=2.17,<3.0.a0 + - c-ares >=1.34.6,<2.0a0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libgcc >=14 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libre2-11 >=2025.11.5 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.5,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.78.1 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 7021360 + timestamp: 1774020290672 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.4.0-h10be129_0.conda + sha256: 8b70955d5e9a49d08945d4f8e2eab855b2efa5fce9cb9bc5e75d86764e6f2f38 + md5: 3a9428b74c403c71048104d38437b48c + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: Apache-2.0 OR BSD-3-Clause + purls: [] + size: 1435782 + timestamp: 1776989559668 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda + sha256: c467851a7312765447155e071752d7bf9bf44d610a5687e32706f480aad2833f + md5: 915f5995e94f60e9a4826e0b0920ee88 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: LGPL-2.1-only + purls: [] + size: 790176 + timestamp: 1754908768807 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.4.1-hb03c661_0.conda + sha256: 10056646c28115b174de81a44e23e3a0a3b95b5347d2e6c45cc6d49d35294256 + md5: 6178c6f2fb254558238ef4e6c56fb782 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + purls: [] + size: 633831 + timestamp: 1775962768273 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.11.2-h174a0a3_1.conda + sha256: 0c8a78c6a42a6e4c6de3a5e82d692f60400d43f4cc80591745f28b37daad9c70 + md5: 850f48943d6b4589800a303f0de6a816 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libhwy >=1.4.0,<1.5.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 1846962 + timestamp: 1777065125966 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblal-7.6.1-fftw_hcbe9c14_100.conda + sha256: 0fa386ec61e09def59133f2e8dcfe77158eba2756ced6f04843bdeaab68891ab + md5: 554e9aae3a43164b277d33c8ca4158be + depends: + - __glibc >=2.17,<3.0.a0 + - fftw >=3.3.10,<4.0a0 + - gsl >=2.7,<2.8.0a0 + - hdf5 >=1.14.3,<1.14.4.0a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + constrains: + - python-lal >=7.1.1 + - lal >=7.1.1 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 2299753 + timestamp: 1734695039343 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblalburst-2.0.6-h3773ae6_0.conda + sha256: 88740674d5886cef146335f32894adaed93ec5ff618e441f2ac1270bce580ac4 + md5: 659626e5a5c67aed774ffd6953ef2aa2 + depends: + - __glibc >=2.17,<3.0.a0 + - gsl >=2.7,<2.8.0a0 + - libgcc >=13 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalmetaio >=4.0.0 + - liblalmetaio >=4.0.5,<5.0a0 + - liblalsimulation >=6.1.0 + - liblalsimulation >=6.1.0,<7.0a0 + constrains: + - python-lalburst >=1.5.7 + - lalburst >=1.5.7 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 74163 + timestamp: 1734708924070 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblalframe-3.0.6-h7c287a6_0.conda + sha256: 181648c7be402a4ae7056029b6cde5c1c40da18b4354187dc8f951d669cc56e3 + md5: bd02d9a68d610a4e8f4c3cd620124d04 + depends: + - __glibc >=2.17,<3.0.a0 + - ldas-tools-framecpp >=2.9.3,<2.10.0a0 + - libframel >=8.41.3,<9.0a0 + - libgcc >=13 + - liblal >=7.6.0,<8.0a0 + constrains: + - python-lalframe >=1.5.3 + - lalframe >=1.5.3 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 251017 + timestamp: 1734696134142 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblalinference-4.1.8-h3773ae6_0.conda + sha256: c1973fb5c47df8fa8018ea55054f6c4b7219c89bd541fb10724a3be9df4ba1cc + md5: e5b19fb805bd8983dec5bdc7b4a3ad9c + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + - gsl >=2.7,<2.8.0a0 + - lalinference-data 4.1.8 ha770c72_0 + - libgcc >=13 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalburst >=2.0.0 + - liblalburst >=2.0.6,<3.0a0 + - liblalframe >=3.0.0 + - liblalframe >=3.0.6,<4.0a0 + - liblalinspiral >=5.0.0 + - liblalinspiral >=5.0.2,<6.0a0 + - liblalmetaio >=4.0.0 + - liblalmetaio >=4.0.5,<5.0a0 + - liblalsimulation >=6.1.0 + - liblalsimulation >=6.1.0,<7.0a0 + constrains: + - lalinference >=2.0.6 + - python-lalinference >=2.0.6 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 961882 + timestamp: 1734712662114 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblalinspiral-5.0.2-h3773ae6_0.conda + sha256: fc1c3df1394fcf933d56a72a324c59b1e41acc0f9e084c7b86295c18e8afef7e + md5: 3ed9ee3c2cdb2c32d869ec1cb92ed1ff + depends: + - __glibc >=2.17,<3.0.a0 + - gsl >=2.7,<2.8.0a0 + - libgcc >=13 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalburst >=2.0.0 + - liblalburst >=2.0.5,<3.0a0 + - liblalframe >=3.0.0 + - liblalframe >=3.0.6,<4.0a0 + - liblalmetaio >=4.0.0 + - liblalmetaio >=4.0.5,<5.0a0 + - liblalsimulation >=6.1.0 + - liblalsimulation >=6.1.0,<7.0a0 + constrains: + - lalinspiral >=2.0.1 + - python-lalinspiral >=2.0.1 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 1195733 + timestamp: 1734708662377 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblalmetaio-4.0.5-hb9d3cd8_2.conda + sha256: 291e16f53a9b7275d844cc34e3af632b139e2242a90f3201263b731e088ad04f + md5: dae474381bb3c92aa62237a15d44d5d3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - liblal >=7.6.0 + - liblal >=7.6.0,<8.0a0 + - libmetaio >=8.4.0 + - libmetaio >=8.5.1,<9.0a0 + constrains: + - python-lalmetaio >=2.0.1 + - lalmetaio >=2.0.1 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 117381 + timestamp: 1734275050936 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblalpulsar-7.1.0-h9345056_1.conda + sha256: 0c75f743fe9ccad2f70a155a555eb0fbd0df38dfebe1cb5912f71bfc962118ea + md5: 4f28fcb039f20661ae6e7d5f9a4e582d + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + - cfitsio >=4.6.2,<4.6.3.0a0 + - fftw + - gsl >=2.7,<2.8.0a0 + - lalpulsar-data 7.1.0 ha770c72_1 + - libgcc >=13 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalframe >=3.0.0 + - liblalframe >=3.0.6,<4.0a0 + - liblalinference >=4.1.0 + - liblalinference >=4.1.8,<5.0a0 + - liblalsimulation >=6.1.0 + - liblalsimulation >=6.1.0,<7.0a0 + constrains: + - lalpulsar >=3.0.0 + - python-lalpulsar >=3.0.0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 1761960 + timestamp: 1744215201125 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblalsimulation-6.1.0-py312h09b83b2_0.conda + sha256: c153371f813ab569b802e10f47aeac11d9cad34a3a020af104e586df823be3df + md5: 2f1ae2e5f481f0ce44c44fecf6c198b6 + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + - gsl >=2.7,<2.8.0a0 + - lalsimulation-data 6.1.0 ha770c72_0 + - libgcc >=13 + - liblal >=7.6.0 + - liblal >=7.6.0,<8.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - lalsimulation >=2.5.0 + - python-lalsimulation >=2.5.0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 7919946 + timestamp: 1734695922103 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-7_h47877c9_openblas.conda + build_number: 7 + sha256: 96962084921f197c9ad13fb7f8b324f2351d50ff3d8d962148751ad532f54a01 + md5: 6569b4f273740e25dc0dc7e3232c2a6c + depends: + - libblas 3.11.0 7_h4a7cf45_openblas + constrains: + - liblapacke 3.11.0 7*_openblas + - libcblas 3.11.0 7*_openblas + - blas 2.307 openblas + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 18694 + timestamp: 1778489869038 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.8-hf7376ad_0.conda + sha256: 91bb4f5be1601b40b4995911d785e29387970f0b3c80f33f7f9028f95335399f + md5: 1a2708a460884d6861425b7f9a7bef99 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libxml2 + - libxml2-16 >=2.14.6 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 44333366 + timestamp: 1765959132513 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm22-22.1.6-hf7376ad_0.conda + sha256: c883814c109f6f1d3b159d6366a5d39dd1e160ce1cf48b27b6d7c4ee8d804232 + md5: 605a337de427d14e51adb39f8a07b282 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - 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 + purls: [] + size: 44235433 + timestamp: 1779261596488 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda + sha256: ec30e52a3c1bf7d0425380a189d209a52baa03f22fb66dd3eb587acaa765bd6d + md5: b88d90cad08e6bc8ad540cb310a761fb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - xz 5.8.3.* + license: 0BSD + purls: [] + size: 113478 + timestamp: 1775825492909 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libmetaio-8.5.1-h0b0be96_1003.conda + sha256: ece2d119d1f5743aeb40814308b2a9a989412e9f43f9e28fac05e289fef20fa9 + md5: 96380b4f61bbd8946f38fd1ecf54eb4e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + - libzlib >=1.3.1,<2.0a0 + constrains: + - metaio >=8.5.1 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 47894 + timestamp: 1721742221704 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda + sha256: 663444d77a42f2265f54fb8b48c5450bfff4388d9c0f8253dd7855f0d993153f + md5: 2a45e7f8af083626f009645a6481f12d + depends: + - __glibc >=2.17,<3.0.a0 + - c-ares >=1.34.6,<2.0a0 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.5,<4.0a0 + license: MIT + license_family: MIT + purls: [] + 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: [] + size: 33731 + timestamp: 1750274110928 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda + sha256: 3b3f19ced060013c2dd99d9d46403be6d319d4601814c772a3472fe2955612b0 + md5: 7c7927b404672409d9917d49bff5f2d6 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: LGPL-2.1-or-later + purls: [] + size: 33418 + timestamp: 1734670021371 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda + sha256: 3d9aa85648e5e18a6d66db98b8c4317cc426721ad7a220aa86330d1ccedc8903 + md5: 2d3278b721e40468295ca755c3b84070 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.3.0 + constrains: + - openblas >=0.3.33,<0.3.34.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 5931919 + timestamp: 1776993658641 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_3.conda + sha256: 90777039b48529283df5f16383fc399866024257a8bd93de583f4730db1ab30a + md5: c2bd8055a2e2dce7a7f32cfd02101fb6 + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_3 + license: LicenseRef-libglvnd + purls: [] + size: 51767 + timestamp: 1779728204026 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-1.21.0-h9692893_2.conda + sha256: 59663bdd97ac6d8ce8a83bf80e18c14c4ac5ca536ef1a2de4bc9080a45dc501a + md5: c3de1cc30bc11edbc98aed352381449d + depends: + - libabseil * cxx17* + - libabseil >=20260107.0,<20260108.0a0 + - libcurl >=8.18.0,<9.0a0 + - libgrpc >=1.78.0,<1.79.0a0 + - libopentelemetry-cpp-headers 1.21.0 ha770c72_2 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libzlib >=1.3.1,<2.0a0 + - nlohmann_json + - prometheus-cpp >=1.3.0,<1.4.0a0 + constrains: + - cpp-opentelemetry-sdk =1.21.0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 896630 + timestamp: 1770452315175 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-headers-1.21.0-ha770c72_2.conda + sha256: b2b2122f214c417851ba280009aea040e546665c43de737690c2610055a255e3 + md5: 253e70376a8ae74f9d99d44712b3e087 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 362214 + timestamp: 1770452273268 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-23.0.1-h7376487_4_cpu.conda + build_number: 4 + sha256: c1c6f612b8cd50abff3ca345d2fb650c621723ceac4a7a98ff059599cf53ecbc + md5: ecc6a87df60b48824fca2f3027eaaa8f + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 23.0.1 hf605819_4_cpu + - libgcc >=14 + - libstdcxx >=14 + - libthrift >=0.22.0,<0.22.1.0a0 + - openssl >=3.5.5,<4.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 1390208 + timestamp: 1773271844211 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_0.conda + sha256: f41721636a7c2e51bc2c642e1127955ab9c81145470714fdaac44d4d09e4af41 + md5: 33082e13b4769b48cfeb648e15bfe3fc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 29147 + timestamp: 1773533027610 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda + sha256: 377cfe037f3eeb3b1bf3ad333f724a64d32f315ee1958581fc671891d63d3f89 + md5: eba48a68a1a2b9d3c0d9511548db85db + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libzlib >=1.3.2,<2.0a0 + license: zlib-acknowledgement + purls: [] + size: 317729 + timestamp: 1776315175087 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.1-h5c52fec_2.conda + sha256: bbab2c3e6f650f2bd1bc84d88e6a20fefa6a401fa445bb4b97c509c1b3a89fa8 + md5: a8ac9a6342569d1714ae1b53ae2fcadb + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=75.1,<76.0a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=14 + - openldap >=2.6.10,<2.7.0a0 + - openssl >=3.5.4,<4.0a0 + license: PostgreSQL + purls: [] + size: 2711480 + timestamp: 1764345810429 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.33.5-h2b00c02_0.conda + sha256: afbf195443269ae10a940372c1d37cda749355d2bd96ef9587a962abd87f2429 + md5: 11ac478fa72cf12c214199b8a96523f4 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20260107.0,<20260108.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 3638698 + timestamp: 1769749419271 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h0dc7533_1.conda + sha256: 138fc85321a8c0731c1715688b38e2be4fb71db349c9ab25f685315095ae70ff + md5: ced7f10b6cfb4389385556f47c0ad949 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20260107.0,<20260108.0a0 + - libgcc >=14 + - libstdcxx >=14 + constrains: + - re2 2025.11.05.* + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 213122 + timestamp: 1768190028309 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.22-h280c20c_1.conda + sha256: b677bbf1c339d894757c3dcfbb2f88649e499e4991d70ae09a1466da9a6c92d6 + md5: 965e4d531b588b2e42f66fd8e48b056c + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: ISC + purls: [] + size: 269272 + timestamp: 1779163468406 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.1-h0c1763c_0.conda + sha256: 54cdcd3214313b62c2a8ee277e6f42150d9b748264c1b70d958bf735e420ef8d + md5: 7dc38adcbf71e6b38748e919e16e0dce + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libzlib >=1.3.2,<2.0a0 + license: blessing + purls: [] + size: 954962 + timestamp: 1777986471789 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda + sha256: fa39bfd69228a13e553bd24601332b7cfeb30ca11a3ca50bb028108fe90a7661 + md5: eecce068c7e4eddeb169591baac20ac4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 304790 + timestamp: 1745608545575 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda + sha256: dff1058c76ec6b8759e41cefa2508162d00e4a5e6721aa68ec3fd10094e702dc + md5: 5794b3bdc38177caf969dabd3af08549 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc 15.2.0 he0feb66_19 + constrains: + - libstdcxx-ng ==15.2.0=*_19 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 5852044 + timestamp: 1778269036376 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_19.conda + sha256: 0672b6b6e1791c92e8eccad58081a99d614fcf82bca5841f9dfa3c3e658f83b9 + md5: e5ce228e579726c07255dbf90dc62101 + depends: + - libstdcxx 15.2.0 h934c35e_19 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 27776 + timestamp: 1778269074600 +- 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: [] + size: 423861 + timestamp: 1777018957474 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda + sha256: e5f8c38625aa6d567809733ae04bb71c161a42e44a9fa8227abe61fa5c60ebe0 + md5: cd5a90476766d53e901500df9215e927 + depends: + - __glibc >=2.17,<3.0.a0 + - lerc >=4.0.0,<5.0a0 + - libdeflate >=1.25,<1.26.0a0 + - libgcc >=14 + - libjpeg-turbo >=3.1.0,<4.0a0 + - liblzma >=5.8.1,<6.0a0 + - libstdcxx >=14 + - libwebp-base >=1.6.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: HPND + purls: [] + size: 435273 + timestamp: 1762022005702 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.11.3-hfe17d71_0.conda + sha256: ecbf4b7520296ed580498dc66a72508b8a79da5126e1d6dc650a7087171288f9 + md5: 1247168fe4a0b8912e3336bccdbf98a5 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 85969 + timestamp: 1768735071295 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.1-h5347b49_0.conda + sha256: 3f0edf1280e2f6684a986f821eaa3e123d2694a00b31b96ca0d4a4c12c129231 + md5: 7d0a66598195ef00b6efc55aefc7453b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 40163 + timestamp: 1779118517630 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.341.0-h5279c79_0.conda + sha256: a68280d57dfd29e3d53400409a39d67c4b9515097eba733aa6fe00c880620e2b + md5: 31ad065eda3c2d88f8215b1289df9c89 + depends: + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxrandr >=1.5.5,<2.0a0 + constrains: + - libvulkan-headers 1.4.341.0.* + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 199795 + timestamp: 1770077125520 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda + sha256: 3aed21ab28eddffdaf7f804f49be7a7d701e8f0e46c856d801270b470820a37b + md5: aea31d2e5b1091feca96fcfe945c3cf9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - libwebp 1.6.0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 429011 + timestamp: 1752159441324 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + sha256: 666c0c431b23c6cec6e492840b176dde533d48b7e6fb8883f5071223433776aa + md5: 92ed62436b625154323d40d5f2f11dd7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + purls: [] + size: 395888 + timestamp: 1727278577118 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + sha256: 6ae68e0b86423ef188196fff6207ed0c8195dd84273cb5623b85aa08033a410c + md5: 5aa797f8787fe7a17d1b0821485b5adc + depends: + - libgcc-ng >=12 + license: LGPL-2.1-or-later + purls: [] + size: 100393 + timestamp: 1702724383534 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.1-hca5e8e5_0.conda + sha256: d2195b5fbcb0af1ff7b345efdf89290c279b8d1d74f325ae0ac98148c375863c + md5: 2bca1fbb221d9c3c8e3a155784bbc2e9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libxcb >=1.17.0,<2.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - xkeyboard-config + - xorg-libxau >=1.0.12,<2.0a0 + license: MIT/X11 Derivative + license_family: MIT + purls: [] + size: 837922 + timestamp: 1764794163823 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.1-ha9997c6_0.conda + sha256: 71436e72a286ef8b57d6f4287626ff91991eb03c7bdbe835280521791efd1434 + md5: e7733bc6785ec009e47a224a71917e84 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=75.1,<76.0a0 + - libgcc >=14 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.1,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + constrains: + - libxml2 2.15.1 + license: MIT + license_family: MIT + purls: [] + size: 556302 + timestamp: 1761015637262 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.1-h26afc86_0.conda + sha256: ec0735ae56c3549149eebd7dc22c0bed91fd50c02eaa77ff418613ddda190aa8 + md5: e512be7dc1f84966d50959e900ca121f + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=75.1,<76.0a0 + - libgcc >=14 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.1,<6.0a0 + - libxml2-16 2.15.1 ha9997c6_0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 45283 + timestamp: 1761015644057 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda + sha256: 0694760a3e62bdc659d90a14ae9c6e132b525a7900e59785b18a08bb52a5d7e5 + md5: 87e6096ec6d542d1c1f8b33245fe8300 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libxml2 + - libxml2-16 >=2.14.6 + license: MIT + license_family: MIT + purls: [] + size: 245434 + timestamp: 1757963724977 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda + sha256: 55044c403570f0dc26e6364de4dc5368e5f3fc7ff103e867c487e2b5ab2bcda9 + md5: d87ff7921124eccd67248aa483c23fec + depends: + - __glibc >=2.17,<3.0.a0 + constrains: + - zlib 1.3.2 *_2 + license: Zlib + license_family: Other + purls: [] + size: 63629 + timestamp: 1774072609062 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libzopfli-1.0.3-h9c3ff4c_0.tar.bz2 + sha256: ff94f30b2e86cbad6296cf3e5804d442d9e881f7ba8080d92170981662528c6e + md5: c66fe2d123249af7651ebde8984c51c2 + depends: + - libgcc-ng >=9.3.0 + - libstdcxx-ng >=9.3.0 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 168074 + timestamp: 1607309189989 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ligo-segments-1.4.0-py312h66e93f0_6.conda + sha256: b4d622c53fc839ecd5798bb0e0e60977973fe0aba6e744928f4470c2ab9ce559 + md5: 8dbb1268e79c44b25f6745af7b3ff452 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - six + license: GPL-3.0-or-later + license_family: GPL + purls: + - pkg:pypi/ligo-segments?source=hash-mapping + size: 78098 + timestamp: 1733851880296 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ligo.skymap-2.3.0-py312h36415c3_0.conda + sha256: 4ae12ed4778dcc7e0345bc95f78ddc77f18998cc481e0219077de5a909ba74f9 + md5: d955bf259aef301e37b97e39afe53981 + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + - astroplan >=0.7 + - astropy-base >=6.0 + - astropy-healpix >=0.3 + - chealpix >=3.31.0,<3.32.0a0 + - gsl >=2.7,<2.8.0a0 + - h5py + - healpy + - libcblas >=3.9.0,<4.0a0 + - libgcc >=13 + - ligo-gracedb >=2.0.1 + - ligo-segments >=1.2.0 + - matplotlib-base >=3.9.1 + - networkx + - numpy >=1.19,<3 + - numpy >=1.23.0 + - pillow >=2.5.0 + - ptemcee + - python >=3.12,<3.13.0a0 + - python-lal >=7.6.0 + - python-lalinspiral >=5.0.1 + - python-lalmetaio >=4.0.5 + - python-lalsimulation >=6.0.0 + - python-ligo-lw >=1.8.0 + - python_abi 3.12.* *_cp312 + - pytz + - reproject >=0.3.2 + - scipy >=0.14,!=1.10.0 + - shapely >=2.0.0 + - tqdm >=4.27.0 + license: GPL-3.0-or-later + license_family: GPL + purls: + - pkg:pypi/ligo-skymap?source=hash-mapping + size: 1929162 + timestamp: 1745758318090 +- conda: https://conda.anaconda.org/conda-forge/linux-64/llvmlite-0.47.0-py312h7424e68_1.conda + sha256: 27b34a24cc4c872d328b07319ae5ab6673d5c94ec923cde5b1f3ac7f59b95dd0 + md5: 49f23211559c82cf8c5ffac112fe73b4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.2,<2.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - zstd >=1.5.7,<1.6.0a0 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/llvmlite?source=hash-mapping + size: 34127488 + timestamp: 1776076739766 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-4.4.5-py312h3d67a73_1.conda + sha256: e8ae9141c7afcc95555fca7ff5f91d7a84f094536715211e750569fd4bb2caa4 + md5: a669145a2c834895bdf3fcba1f1e5b9c + depends: + - python + - lz4-c + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python_abi 3.12.* *_cp312 + - lz4-c >=1.10.0,<1.11.0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/lz4?source=hash-mapping + size: 44154 + timestamp: 1765026394687 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + sha256: 47326f811392a5fd3055f0f773036c392d26fdb32e4d8e7a8197eed951489346 + md5: 9de5350a85c4a20c685259b889aa6393 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 167055 + timestamp: 1733741040117 +- conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_1.conda + sha256: 5f3aad1f3a685ed0b591faad335957dbdb1b73abfd6fc731a0d42718e0653b33 + md5: 93a4752d42b12943a355b682ee43285b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/markupsafe?source=hash-mapping + size: 26057 + timestamp: 1772445297924 +- conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.9-py312h7900ff3_0.conda + sha256: cdd59bb1a52b09979f11c68909d53120b2e749edd1992853a74e1604db19c8b0 + md5: 579c6a324b197594fabc9240bddf2d8b + depends: + - matplotlib-base >=3.10.9,<3.10.10.0a0 + - pyside6 >=6.7.2 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - tornado >=5 + license: PSF-2.0 + license_family: PSF + purls: [] + size: 17831 + timestamp: 1777000588302 +- conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.9-py312he3d6523_0.conda + sha256: c7e133837376e53e6a52719c205a3067c42f05769bc3e8307417f8d817dfc63e + md5: 7d499b5b6d150f133800dc3a582771c7 + depends: + - __glibc >=2.17,<3.0.a0 + - contourpy >=1.0.1 + - cycler >=0.10 + - fonttools >=4.22.0 + - freetype + - kiwisolver >=1.3.1 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libgcc >=14 + - libstdcxx >=14 + - numpy >=1.23 + - numpy >=1.23,<3 + - packaging >=20.0 + - pillow >=8 + - pyparsing >=2.3.1 + - python >=3.12,<3.13.0a0 + - python-dateutil >=2.7 + - python_abi 3.12.* *_cp312 + - qhull >=2020.2,<2020.3.0a0 + - tk >=8.6.13,<8.7.0a0 + license: PSF-2.0 + license_family: PSF + purls: + - pkg:pypi/matplotlib?source=hash-mapping + size: 8336056 + timestamp: 1777000573501 +- conda: https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.1.2-py312hd9148b4_1.conda + sha256: 94068fd39d1a672f8799e3146a18ba4ef553f0fcccefddb3c07fbdabfd73667a + md5: 2e489969e38f0b428c39492619b5e6e5 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/msgpack?source=hash-mapping + size: 102525 + timestamp: 1762504116832 +- conda: https://conda.anaconda.org/conda-forge/linux-64/munge-0.5.16-h63a00c3_0.conda + sha256: 5a9964eab34510cead90d6db2f1ef4c5c5e99df898324aa811a6b68aa0b5c818 + md5: 64b3e80aa79b823d54862872aa4edd23 + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - libgcc-ng >=12 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.3.1,<4.0a0 + license: LGPL-3.0-or-later + license_family: GPL + purls: [] + size: 130717 + timestamp: 1720627584980 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda + sha256: fc89f74bbe362fb29fa3c037697a89bec140b346a2469a90f7936d1d7ea4d8a3 + md5: fc21868a1a5aacc937e7a18747acb8a5 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: X11 AND BSD-3-Clause + purls: [] + size: 918956 + timestamp: 1777422145199 +- conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda + sha256: fd2cbd8dfc006c72f45843672664a8e4b99b2f8137654eaae8c3d46dca776f63 + md5: 16c2a0e9c4a166e53632cfca4f68d020 + constrains: + - nlohmann_json-abi ==3.12.0 + license: MIT + license_family: MIT + purls: [] + size: 136216 + timestamp: 1758194284857 +- conda: https://conda.anaconda.org/conda-forge/linux-64/numba-0.65.1-py312hd1dde6f_1.conda + sha256: 6c758c97b06e0bb99e425edce981610b2db9f95c0996f48288a031d847981193 + md5: acd0d3547b3cb443a5ce9124412b6295 + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + - libgcc >=14 + - libstdcxx >=14 + - llvmlite >=0.47.0,<0.48.0a0 + - numpy >=1.22.3,<2.5 + - numpy >=1.23,<3 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - cudatoolkit >=11.2 + - tbb >=2021.6.0 + - cuda-version >=11.2 + - cuda-python >=11.6 + - libopenblas !=0.3.6 + - scipy >=1.0 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/numba?source=hash-mapping + size: 5707782 + timestamp: 1778390479325 +- 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 + size: 813229 + timestamp: 1764782491676 +- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py312heda63a1_0.conda + sha256: fe3459c75cf84dcef6ef14efcc4adb0ade66038ddd27cadb894f34f4797687d8 + md5: d8285bea2a350f63fab23bf460221f3f + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libgcc-ng >=12 + - liblapack >=3.9.0,<4.0a0 + - libstdcxx-ng >=12 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/numpy?source=hash-mapping + size: 7484186 + timestamp: 1707225809722 +- conda: https://conda.anaconda.org/conda-forge/linux-64/oniguruma-6.9.10-hb9d3cd8_0.conda + sha256: bbff8a60f70d5ebab138b564554f28258472e1e63178614562d4feee29d10da2 + md5: 6ce853cb231f18576d2db5c2d4cb473e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 248670 + timestamp: 1735727084819 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda + sha256: 3900f9f2dbbf4129cf3ad6acf4e4b6f7101390b53843591c53b00f034343bc4d + md5: 11b3379b191f63139e29c0d19dee24cd + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libpng >=1.6.50,<1.7.0a0 + - libstdcxx >=14 + - libtiff >=4.7.1,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 355400 + timestamp: 1758489294972 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openjph-0.27.3-h8d634f6_0.conda + sha256: fb2600253b6ab9f53cd0334ae77642f5a71e6d42848b2f9a350a0d3861ff00cc + md5: c6c0190e1995c47f4221a889ac3bde3e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libtiff >=4.7.1,<4.8.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 283239 + timestamp: 1778775149306 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda + sha256: cb0b07db15e303e6f0a19646807715d28f1264c6350309a559702f4f34f37892 + md5: 2e5bf4f1da39c0b32778561c3c4e5878 + depends: + - __glibc >=2.17,<3.0.a0 + - cyrus-sasl >=2.1.27,<3.0a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=13 + - libstdcxx >=13 + - openssl >=3.5.0,<4.0a0 + license: OLDAP-2.8 + license_family: BSD + purls: [] + size: 780253 + timestamp: 1748010165522 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda + sha256: c0ef482280e38c71a08ad6d71448194b719630345b0c9c60744a2010e8a8e0cb + md5: da1b85b6a87e141f5140bb9924cecab0 + depends: + - __glibc >=2.17,<3.0.a0 + - ca-certificates + - libgcc >=14 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 3167099 + timestamp: 1775587756857 +- conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.3.0-h21090e2_0.conda + sha256: a60c2578c8422e0c67206d269767feb4d3e627511558b6866e5daf2231d5214d + md5: 8027fce94fdfdf2e54f9d18cbae496df + depends: + - tzdata + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.2,<1.3.0a0 + - libabseil >=20260107.1,<20260108.0a0 + - libabseil * cxx17* + - libprotobuf >=6.33.5,<6.33.6.0a0 + - zstd >=1.5.7,<1.6.0a0 + - libzlib >=1.3.1,<2.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 1468651 + timestamp: 1773230208923 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.3-py312h8ecdadd_0.conda + sha256: 009408dcfdc789b8a1748d6a63fd2134ea2edc8474231ea7beba0ac3ad772a37 + md5: 15c437bfa4cbddd379b95357c9aa4150 + depends: + - python + - numpy >=1.26.0 + - python-dateutil >=2.8.2 + - libgcc >=14 + - libstdcxx >=14 + - __glibc >=2.17,<3.0.a0 + - python_abi 3.12.* *_cp312 + - numpy >=1.23,<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 + size: 14872605 + timestamp: 1778602625175 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.46-h1321c63_0.conda + sha256: 5c7380c8fd3ad5fc0f8039069a45586aa452cf165264bc5a437ad80397b32934 + md5: 7fa07cb0fb1b625a089ccc01218ee5b1 + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - libgcc >=14 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 1209177 + timestamp: 1756742976157 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-haa7fec5_0.conda + sha256: 5e6f7d161356fefd981948bea5139c5aa0436767751a6930cb1ca801ebb113ff + md5: 7a3bff861a6583f1889021facefc08b1 + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - libgcc >=14 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 1222481 + timestamp: 1763655398280 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.2.0-py312h50c33e8_0.conda + sha256: fa291f8915114733dc1df9f1627b8c63c517217c1eee1a6ede2ceb5e368cf27a + md5: 9e5609720e31213d4f39afe377f6217e + depends: + - python + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - lcms2 >=2.18,<3.0a0 + - libxcb >=1.17.0,<2.0a0 + - libjpeg-turbo >=3.1.2,<4.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - openjpeg >=2.5.4,<3.0a0 + - python_abi 3.12.* *_cp312 + - tk >=8.6.13,<8.7.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - zlib-ng >=2.3.3,<2.4.0a0 + license: HPND + purls: + - pkg:pypi/pillow?source=hash-mapping + size: 1039561 + timestamp: 1775060059882 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.conda + sha256: 43d37bc9ca3b257c5dd7bf76a8426addbdec381f6786ff441dc90b1a49143b6a + md5: c01af13bdc553d1a8fbfff6e8db075f0 + depends: + - libgcc >=14 + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: MIT + license_family: MIT + purls: [] + size: 450960 + timestamp: 1754665235234 +- 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: [] + size: 199544 + timestamp: 1730769112346 +- conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h5253ce2_0.conda + sha256: d834fd656133c9e4eaf63ffe9a117c7d0917d86d89f7d64073f4e3a0020bd8a7 + md5: dd94c506b119130aef5a9382aed648e7 + depends: + - python + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/psutil?source=hash-mapping + size: 225545 + timestamp: 1769678155334 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + sha256: 9c88f8c64590e9567c6c80823f0328e58d3b1efb0e1c539c0315ceca764e0973 + md5: b3c17d95b5a10c6e64a21fa17573e70e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + purls: [] + size: 8252 + timestamp: 1726802366959 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-23.0.1-py312h7900ff3_0.conda + sha256: 05ac953b934135cf63343ddc6cbea56abfd167af118f22a47fc8984f9158beb8 + md5: 58710f6789e9a893472922a9dcd03f4f + depends: + - libarrow-acero 23.0.1.* + - libarrow-dataset 23.0.1.* + - libarrow-substrait 23.0.1.* + - libparquet 23.0.1.* + - pyarrow-core 23.0.1 *_0_* + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 28639 + timestamp: 1771307345680 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-23.0.1-py312h2054cf2_0_cpu.conda + sha256: e023133b8d24bada11fcf57b80aca98cf253a09ce996393949c006d236ac87b7 + md5: 9ad4bfc6f8ca7cdf4acf857fa0c9a91f + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 23.0.1.* *cpu + - libarrow-compute 23.0.1.* *cpu + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<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 + size: 4776752 + timestamp: 1771307276253 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyerfa-2.0.1.5-py310h32771cd_2.conda + noarch: python + sha256: a3f25f921be09e15ed6ff46a1ec99ce9cca6affa4a086f6f39ad630e21e48fb7 + md5: e6efd9593a25d093b4ce9dd8053c4af7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - numpy >=1.21,<3 + - python + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pyerfa?source=hash-mapping + size: 295617 + timestamp: 1756821497270 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pynacl-1.6.2-py312hf34ed73_2.conda + sha256: 2b35f29ea0e01c711749b29b0a09144df2e4cd7bdf8b29d264ef749951a75a0e + md5: aad7f28cd7236932f9c9c27c9b28280c + depends: + - __glibc >=2.17,<3.0.a0 + - cffi >=1.4.1 + - libgcc >=14 + - libsodium >=1.0.22,<1.0.23.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - six + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/pynacl?source=hash-mapping + size: 1180950 + timestamp: 1778984876791 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.10.1-py312h9da60e5_0.conda + sha256: dccbc2674aaae31711933942fd16d87b127e6335556d5701cb760f27986f0375 + md5: dda0a61b6186fc914cf6c1581f64229d + depends: + - __glibc >=2.17,<3.0.a0 + - libclang13 >=21.1.7 + - libegl >=1.7.0,<2.0a0 + - libgcc >=14 + - libgl >=1.7.0,<2.0a0 + - libopengl >=1.7.0,<2.0a0 + - libstdcxx >=14 + - libvulkan-loader >=1.4.328.1,<2.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - libxslt >=1.1.43,<2.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - qt6-main 6.10.1.* + - qt6-main >=6.10.1,<6.11.0a0 + license: LGPL-3.0-only + license_family: LGPL + purls: + - pkg:pypi/pyside6?source=hash-mapping + - pkg:pypi/shiboken6?source=hash-mapping + size: 11606305 + timestamp: 1765811838817 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.13-hd63d673_0_cpython.conda + sha256: a44655c1c3e1d43ed8704890a91e12afd68130414ea2c0872e154e5633a13d7e + md5: 7eccb41177e15cc672e1babe9056018e + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-64 >=2.36.1 + - libexpat >=2.7.4,<3.0a0 + - libffi >=3.5.2,<3.6.0a0 + - libgcc >=14 + - liblzma >=5.8.2,<6.0a0 + - libnsl >=2.0.1,<2.1.0a0 + - libsqlite >=3.51.2,<4.0a0 + - libuuid >=2.41.3,<3.0a0 + - libxcrypt >=4.4.36 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.5.5,<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: [] + size: 31608571 + timestamp: 1772730708989 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-gssapi-1.11.1-py312h7cea900_0.conda + sha256: 6111460af5c1860c03ef68b4a94fd7ba541687c1b3fe91e170bf8eb007272a60 + md5: 1a0ae53a0853e54d1ac8448fdeba9f01 + depends: + - __glibc >=2.17,<3.0.a0 + - decorator + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: ISC + purls: + - pkg:pypi/gssapi?source=hash-mapping + size: 559459 + timestamp: 1769842649997 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-htcondor-24.12.4-py312h1e35698_0.conda + sha256: 8908ab85bf345b919b4faa16b07a771a5ca945842cc4e2556e4e46844c52c37c + md5: cbd4ef4b341950969d3a776172449fe7 + depends: + - __glibc >=2.17,<3.0.a0 + - htcondor-classads 24.12.4 hf1419ba_0 + - libboost-python >=1.88.0,<1.89.0a0 + - libcondor_utils 24.12.4 he8e5dd1_0 + - libgcc >=14 + - libstdcxx >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/htcondor?source=hash-mapping + size: 10307972 + timestamp: 1759416481221 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-htcondor-25.6.1-py312h40fa4ac_0.conda + sha256: 8563d63eba6871f279d0f161c5b80a4d0b0be62c914e43b54ddf812e5100dd42 + md5: 2f01552b30ce7adf8019936863913cd2 + depends: + - __glibc >=2.17,<3.0.a0 + - htcondor-classads 25.6.1 h793e66c_0 + - libcondor_utils 25.6.1 h9f200e7_0 + - libgcc >=14 + - libstdcxx >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/htcondor?source=hash-mapping + size: 1826998 + timestamp: 1769820971106 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-lal-7.6.1-fftw_py312heccaa44_100.conda + sha256: 1122cd48a8250069b8816686382666d75b9c58544a07e7f44a02ebdc8dfb0893 + md5: 0fff865c055ebdc694627a822b634f88 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - liblal 7.6.1 fftw_hcbe9c14_100 + - ligo-segments + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python-dateutil + - python_abi 3.12.* *_cp312 + - scipy + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 977688 + timestamp: 1734695257317 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalburst-2.0.6-py312h3684b61_0.conda + sha256: c1a0f90714bae2128e979c0b56c4550fe061f32922901ac077c5c13a3e44cb63 + md5: 9c6426b360a7f3f1e64694513c55ef0d + depends: + - __glibc >=2.17,<3.0.a0 + - gsl >=2.7,<2.8.0a0 + - libgcc >=13 + - liblalburst 2.0.6 h3773ae6_0 + - ligo-segments + - lscsoft-glue + - matplotlib-base + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python-lal >=7.6.0 + - python-lalmetaio >=4.0.0 + - python-lalsimulation >=6.1.0 + - python-ligo-lw >=1.7.0 + - python_abi 3.12.* *_cp312 + - scipy + - tqdm + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 339127 + timestamp: 1734709124792 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalframe-3.0.6-py312hc0a28a1_0.conda + sha256: 52db22f1a08d577e870be5812cffbe81f941c2ad04471464095c91a77daca611 + md5: 83e0a790d1e81548e896ba858587407f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - liblalframe 3.0.6 h7c287a6_0 + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python-lal >=7.6.0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 763304 + timestamp: 1734696319935 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalinference-4.1.8-py312hc0a28a1_0.conda + sha256: 67f989910970b4494bbdef4dead78501e5a66448a0253a55fc23284acfc1675f + md5: 0788dbc9e7cc85665438c60e47244282 + depends: + - __glibc >=2.17,<3.0.a0 + - astropy-base >=1.1.1 + - h5py + - healpy >=1.17.3 + - libgcc >=13 + - liblalinference 4.1.8 h3773ae6_0 + - ligo-segments + - lscsoft-glue >=1.54.1 + - matplotlib-base >=1.2.0 + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python-lal >=7.6.0 + - python-lalburst >=2.0.0 + - python-lalinspiral >=5.0.0 + - python-lalmetaio >=4.0.0 + - python-lalsimulation >=6.1.0 + - python-ligo-lw >=1.7.0 + - python_abi 3.12.* *_cp312 + - scipy >=0.9.0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 852173 + timestamp: 1734712848143 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalinspiral-5.0.2-py312hc0a28a1_0.conda + sha256: 482b5c766fb73da9afea65447e36aa49a823528795a9e97b1fb47f6e061500a4 + md5: 1e3b133c631134b5ce62ef9f9367c013 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - liblalinspiral 5.0.2 h3773ae6_0 + - lscsoft-glue + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python-lal >=7.6.0 + - python-lalburst >=2.0.0 + - python-lalframe >=3.0.0 + - python-lalmetaio >=4.0.0 + - python-lalsimulation >=6.1.0 + - python-ligo-lw + - python_abi 3.12.* *_cp312 + - tqdm + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 61725079 + timestamp: 1734708922981 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalmetaio-4.0.5-py312hc0a28a1_2.conda + sha256: 0ba0552788165a55e10194b341288552f11506f23c1c93ed8924aa3ed4b5bf7d + md5: e07e287556b3a8d49c999c7fc0b12fd2 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - liblalmetaio 4.0.5 hb9d3cd8_2 + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python-lal >=7.6.0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 161685 + timestamp: 1734275293301 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalpulsar-7.1.0-py312hc0a28a1_1.conda + sha256: 1d05958d1cf12ddc3ffb6bf2a405a835d0d22eb457a87b5f96e8f61fd5b117c2 + md5: b4328900fedc8e9ae926604928370433 + depends: + - __glibc >=2.17,<3.0.a0 + - astropy-base + - libgcc >=13 + - liblalpulsar 7.1.0 h9345056_1 + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python-lal >=7.6.0 + - python-lalframe >=3.0.0 + - python-lalinference >=4.1.0 + - python-lalsimulation >=6.1.0 + - python_abi 3.12.* *_cp312 + - six + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 96801307 + timestamp: 1744215898111 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-lalsimulation-6.1.0-py312h09b83b2_0.conda + sha256: af389d42ec5a23f119d5e0447d5bbc9e0c5a1ed36deacf0d7b38a53d751364d1 + md5: d05d0a75983655eede7638949a6d4080 + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + - astropy-base + - gsl >=2.7,<2.8.0a0 + - gwpy + - libgcc >=13 + - liblal >=7.6.0 + - liblal >=7.6.0,<8.0a0 + - liblalsimulation 6.1.0 py312h09b83b2_0 + - mpmath >=1.0.0 + - numpy + - python >=3.12,<3.13.0a0 + - python-lal >=7.6.0 + - python_abi 3.12.* *_cp312 + - scipy + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 8479505 + timestamp: 1734695954067 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-ligo-lw-1.8.3-py312h66e93f0_4.conda + sha256: c7a772efddc6095c7ef5d2c426f9907af47779b77567167c63cb23253c2ea94b + md5: 1bda2f338b4931db2314d48b76e5861a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - ligo-segments + - lscsoft-glue >=2.0.0 + - numpy + - python >=3.12,<3.13.0a0 + - python-dateutil + - python-lal >=6.19.0 + - python_abi 3.12.* *_cp312 + - pyyaml + - six + - tqdm + license: GPL-3.0-or-later + license_family: GPL + purls: + - pkg:pypi/python-ligo-lw?source=hash-mapping + size: 192801 + timestamp: 1733996334698 +- 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 + - 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 + size: 198293 + timestamp: 1770223620706 +- conda: https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda + sha256: 776363493bad83308ba30bcb88c2552632581b143e8ee25b1982c8c743e73abc + md5: 353823361b1d27eb3960efb076dfcaf6 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: LicenseRef-Qhull + purls: [] + size: 552937 + timestamp: 1720813982144 +- conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.10.1-h6f76662_3.conda + sha256: 8269ca1fc02dbd419f77ed30b6ec205897efd12813607ecb0630f075f8c5f01f + md5: f134a496ef494f2b6c5a26e5d739acc6 + depends: + - __glibc >=2.17,<3.0.a0 + - alsa-lib >=1.2.15.1,<1.3.0a0 + - dbus >=1.16.2,<2.0a0 + - double-conversion >=3.4.0,<3.5.0a0 + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - harfbuzz >=12.2.0 + - icu >=75.1,<76.0a0 + - krb5 >=1.21.3,<1.22.0a0 + - libclang-cpp21.1 >=21.1.7,<21.2.0a0 + - libclang13 >=21.1.7 + - libcups >=2.3.3,<2.4.0a0 + - libdrm >=2.4.125,<2.5.0a0 + - libegl >=1.7.0,<2.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libgcc >=14 + - libgl >=1.7.0,<2.0a0 + - libglib >=2.86.3,<3.0a0 + - libjpeg-turbo >=3.1.2,<4.0a0 + - libllvm21 >=21.1.7,<21.2.0a0 + - libpng >=1.6.53,<1.7.0a0 + - libpq >=18.1,<19.0a0 + - libsqlite >=3.51.1,<4.0a0 + - libstdcxx >=14 + - libtiff >=4.7.1,<4.8.0a0 + - libvulkan-loader >=1.4.328.1,<2.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libxkbcommon >=1.13.1,<2.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.4,<4.0a0 + - pcre2 >=10.47,<10.48.0a0 + - wayland >=1.24.0,<2.0a0 + - xcb-util >=0.4.1,<0.5.0a0 + - xcb-util-cursor >=0.1.6,<0.2.0a0 + - xcb-util-image >=0.4.0,<0.5.0a0 + - xcb-util-keysyms >=0.4.1,<0.5.0a0 + - xcb-util-renderutil >=0.3.10,<0.4.0a0 + - xcb-util-wm >=0.4.2,<0.5.0a0 + - xorg-libice >=1.1.2,<2.0a0 + - xorg-libsm >=1.2.6,<2.0a0 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxcomposite >=0.4.6,<1.0a0 + - xorg-libxcursor >=1.2.3,<2.0a0 + - xorg-libxdamage >=1.1.6,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxrandr >=1.5.4,<2.0a0 + - xorg-libxtst >=1.2.5,<2.0a0 + - xorg-libxxf86vm >=1.1.6,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - qt 6.10.1 + license: LGPL-3.0-only + license_family: LGPL + purls: [] + size: 56636216 + timestamp: 1766349442902 +- conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.10.1-hca0d9c9_0.conda + sha256: 9b6d229110b0af7d80270cd14dc89acb8264128a29079bea58cee3fef0c5f8d3 + md5: 2ab549dfb24cfbac4cf53ed9ef3d8dfb + depends: + - __glibc >=2.17,<3.0.a0 + - alsa-lib >=1.2.14,<1.3.0a0 + - dbus >=1.16.2,<2.0a0 + - double-conversion >=3.3.1,<3.4.0a0 + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - harfbuzz >=12.2.0 + - icu >=75.1,<76.0a0 + - krb5 >=1.21.3,<1.22.0a0 + - libclang-cpp21.1 >=21.1.6,<21.2.0a0 + - libclang13 >=21.1.6 + - libcups >=2.3.3,<2.4.0a0 + - libdrm >=2.4.125,<2.5.0a0 + - libegl >=1.7.0,<2.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libgcc >=14 + - libgl >=1.7.0,<2.0a0 + - libglib >=2.86.2,<3.0a0 + - libjpeg-turbo >=3.1.2,<4.0a0 + - libllvm21 >=21.1.6,<21.2.0a0 + - libpng >=1.6.50,<1.7.0a0 + - libpq >=18.1,<19.0a0 + - libsqlite >=3.51.0,<4.0a0 + - libstdcxx >=14 + - libtiff >=4.7.1,<4.8.0a0 + - libvulkan-loader >=1.4.328.1,<2.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libxkbcommon >=1.13.0,<2.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.4,<4.0a0 + - pcre2 >=10.46,<10.47.0a0 + - wayland >=1.24.0,<2.0a0 + - xcb-util >=0.4.1,<0.5.0a0 + - xcb-util-cursor >=0.1.6,<0.2.0a0 + - xcb-util-image >=0.4.0,<0.5.0a0 + - xcb-util-keysyms >=0.4.1,<0.5.0a0 + - xcb-util-renderutil >=0.3.10,<0.4.0a0 + - xcb-util-wm >=0.4.2,<0.5.0a0 + - xorg-libice >=1.1.2,<2.0a0 + - xorg-libsm >=1.2.6,<2.0a0 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxcomposite >=0.4.6,<1.0a0 + - xorg-libxcursor >=1.2.3,<2.0a0 + - xorg-libxdamage >=1.1.6,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxrandr >=1.5.4,<2.0a0 + - xorg-libxtst >=1.2.5,<2.0a0 + - xorg-libxxf86vm >=1.1.6,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - qt 6.10.1 + license: LGPL-3.0-only + license_family: LGPL + purls: [] + size: 56495332 + timestamp: 1763747754806 +- conda: https://conda.anaconda.org/conda-forge/linux-64/rav1e-0.8.1-h1fbca29_0.conda + sha256: cf550bbc8e5ebedb6dba9ccaead3e07bd1cb86b183644a4c853e06e4b3ad5ac7 + md5: d83958768626b3c8471ce032e28afcd3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - __glibc >=2.17 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 5595970 + timestamp: 1772540833621 +- conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h5301d42_1.conda + sha256: 3fc684b81631348540e9a42f6768b871dfeab532d3f47d5c341f1f83e2a2b2b2 + md5: 66a715bc01c77d43aca1f9fcb13dde3c + depends: + - libre2-11 2025.11.05 h0dc7533_1 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 27469 + timestamp: 1768190052132 +- 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 + purls: [] + size: 345073 + timestamp: 1765813471974 +- conda: https://conda.anaconda.org/conda-forge/linux-64/regex-2026.5.9-py312h4c3975b_0.conda + sha256: 2d1d20f24cd3274c91ce62215fd86b28c24c33a9381699b00fd95cffe11c1dc4 + md5: 0cee21f9702469ebdd93b4ddc4a2dc3f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 AND CNRI-Python + license_family: PSF + purls: + - pkg:pypi/regex?source=hash-mapping + size: 411061 + timestamp: 1778374143589 +- conda: https://conda.anaconda.org/conda-forge/linux-64/reproject-0.19.0-py312h4f23490_0.conda + sha256: e449b4718d8434c0e9638023118bc77c32539b026bd565046c75b61014ed0bb8 + md5: db0a812a5ebdd7f71a75f34f20069b63 + depends: + - __glibc >=2.17,<3.0.a0 + - astropy-base >=5.0 + - astropy-healpix >=1.0 + - dask >=2024.4.1 + - dask-image >=2025.11.0 + - fsspec >=2021.9 + - libgcc >=14 + - numpy >=1.23 + - numpy >=1.23,<3 + - pillow >=10.0 + - pyavm >=0.9.6 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - scipy >=1.9 + - shapely + - zarr >=2.17.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/reproject?source=hash-mapping + size: 989593 + timestamp: 1763206506874 +- conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.1-h1cbb8d7_1.conda + sha256: dbbe4ab36b90427f12d69fc14a8b601b6bca4185c6c4dd67b8046a8da9daec03 + md5: 9d978822b57bafe72ebd3f8b527bba71 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - openssl >=3.5.5,<4.0a0 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 395083 + timestamp: 1773251675551 +- conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.8.0-np2py312h3226591_1.conda + sha256: 23c643c37fafa14ba3f2b7a407126ea5e732a3655ea8157cf9f977098f863448 + md5: 38decbeae260892040709cafc0514162 + depends: + - python + - numpy >=1.24.1 + - scipy >=1.10.0 + - joblib >=1.3.0 + - threadpoolctl >=3.2.0 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - _openmp_mutex >=4.5 + - numpy >=1.23,<3 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scikit-learn?source=hash-mapping + size: 9726193 + timestamp: 1765801245538 +- conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py312h54fa4ab_0.conda + sha256: e3ad577361d67f6c078a6a7a3898bf0617b937d44dc4ccd57aa3336f2b5778dd + md5: 3e38daeb1fb05a95656ff5af089d2e4c + depends: + - __glibc >=2.17,<3.0.a0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.3.0 + - liblapack >=3.9.0,<4.0a0 + - libstdcxx >=14 + - numpy <2.7 + - numpy >=1.23,<3 + - numpy >=1.25.2 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scipy?source=hash-mapping + size: 17109648 + timestamp: 1771880675810 +- conda: https://conda.anaconda.org/conda-forge/linux-64/scitokens-cpp-1.4.0-h096d96b_0.conda + sha256: 48b6a8088232220d69949916a64c3e8b5fe0f763a47d8a530246cf233181545a + md5: d0b9190c5937652677d4822d8f9abba9 + depends: + - __glibc >=2.17,<3.0.a0 + - libcurl >=8.18.0,<9.0a0 + - libgcc >=14 + - libsqlite >=3.51.2,<4.0a0 + - libstdcxx >=14 + - libuuid >=2.41.3,<3.0a0 + - openssl >=3.5.5,<4.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 2278960 + timestamp: 1771576161131 +- conda: https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py312h383787d_2.conda + sha256: da100ac0210f52399faf814f701165058fa2e2f65f5c036cdf2bf99a40223373 + md5: 69e400d3deca12ee7afd4b73a5596905 + depends: + - __glibc >=2.17,<3.0.a0 + - geos >=3.14.1,<3.14.2.0a0 + - libgcc >=14 + - numpy >=1.23,<3 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/shapely?source=hash-mapping + size: 631649 + timestamp: 1762523699384 +- conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda + sha256: 48f3f6a76c34b2cfe80de9ce7f2283ecb55d5ed47367ba91e8bb8104e12b8f11 + md5: 98b6c9dc80eb87b2519b97bcf7e578dd + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 45829 + timestamp: 1762948049098 +- conda: https://conda.anaconda.org/conda-forge/linux-64/svt-av1-4.0.1-hecca717_0.conda + sha256: 4a1d2005153b9454fc21c9bad1b539df189905be49e851ec62a6212c2e045381 + md5: 2a2170a3e5c9a354d09e4be718c43235 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 2619743 + timestamp: 1769664536467 +- conda: https://conda.anaconda.org/conda-forge/linux-64/swig-4.3.1-hf1419ba_4.conda + sha256: 004d184fc0f03addb56801677efc840275b4af13c5a171528041464621dad457 + md5: fe76a95e3e0d1cc4f9e23bedc54d8f39 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - pcre2 >=10.46,<10.47.0a0 + constrains: + - swig-abi ==4 + license: GPL-3.0-or-later + license_family: GPL + purls: [] + size: 1258554 + timestamp: 1761740324989 +- conda: https://conda.anaconda.org/conda-forge/linux-64/swig-4.4.1-h7a96c5f_0.conda + sha256: 45ec1eedd1de2d7985955290015773a4adc9b8ea95d0f839aaabda2ed075d83c + md5: ce50bd18ea2a92833be8b62881929e23 + depends: + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - pcre2 >=10.47,<10.48.0a0 + constrains: + - swig-abi ==5 + license: GPL-3.0-or-later + license_family: GPL + purls: [] + size: 1329174 + timestamp: 1773251886390 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda + sha256: cafeec44494f842ffeca27e9c8b0c27ed714f93ac77ddadc6aaf726b5554ebac + md5: cffd3bdd58090148f4cfcd831f4b26ab + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libzlib >=1.3.1,<2.0a0 + constrains: + - xorg-libx11 >=1.8.12,<2.0a0 + license: TCL + license_family: BSD + purls: [] + size: 3301196 + timestamp: 1769460227866 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.5-py312h4c3975b_0.conda + sha256: 4629b1c9139858fb08bb357df917ffc12e4d284c57ff389806bb3ae476ef4e0a + md5: 2b37798adbc54fd9e591d24679d2133a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/tornado?source=hash-mapping + size: 859665 + timestamp: 1774358032165 +- conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py312h4c3975b_0.conda + sha256: 895bbfe9ee25c98c922799de901387d842d7c01cae45c346879865c6a907f229 + md5: 0b6c506ec1f272b685240e70a29261b8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/unicodedata2?source=hash-mapping + size: 410641 + timestamp: 1770909099497 +- conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.25.0-hd6090a7_0.conda + sha256: ea374d57a8fcda281a0a89af0ee49a2c2e99cc4ac97cf2e2db7064e74e764bdb + md5: 996583ea9c796e5b915f7d7580b51ea6 + depends: + - __glibc >=2.17,<3.0.a0 + - libexpat >=2.7.4,<3.0a0 + - libffi >=3.5.2,<3.6.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + size: 334139 + timestamp: 1773959575393 +- conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-2.2.1-py312h4c3975b_0.conda + sha256: 3d36b297c5f0c1ab0e598760f0033377334a3bfb5c12f4129c2abcb884e2d632 + md5: a4d41bb00f252b99b4b903b3a8fa3ecc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/wrapt?source=compressed-mapping + size: 114800 + timestamp: 1779477451511 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda + sha256: ad8cab7e07e2af268449c2ce855cbb51f43f4664936eff679b1f3862e6e4b01d + md5: fdc27cb255a7a2cc73b7919a968b48f0 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libxcb >=1.17.0,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 20772 + timestamp: 1750436796633 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0.conda + sha256: c2be9cae786fdb2df7c2387d2db31b285cf90ab3bfabda8fa75a596c3d20fc67 + md5: 4d1fc190b99912ed557a8236e958c559 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libxcb >=1.13 + - libxcb >=1.17.0,<2.0a0 + - xcb-util-image >=0.4.0,<0.5.0a0 + - xcb-util-renderutil >=0.3.10,<0.4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 20829 + timestamp: 1763366954390 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda + sha256: 94b12ff8b30260d9de4fd7a28cca12e028e572cbc504fd42aa2646ec4a5bded7 + md5: a0901183f08b6c7107aab109733a3c91 + depends: + - libgcc-ng >=12 + - libxcb >=1.16,<2.0.0a0 + - xcb-util >=0.4.1,<0.5.0a0 + license: MIT + license_family: MIT + purls: [] + size: 24551 + timestamp: 1718880534789 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda + sha256: 546e3ee01e95a4c884b6401284bb22da449a2f4daf508d038fdfa0712fe4cc69 + md5: ad748ccca349aec3e91743e08b5e2b50 + depends: + - libgcc-ng >=12 + - libxcb >=1.16,<2.0.0a0 + license: MIT + license_family: MIT + purls: [] + size: 14314 + timestamp: 1718846569232 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda + sha256: 2d401dadc43855971ce008344a4b5bd804aca9487d8ebd83328592217daca3df + md5: 0e0cbe0564d03a99afd5fd7b362feecd + depends: + - libgcc-ng >=12 + - libxcb >=1.16,<2.0.0a0 + license: MIT + license_family: MIT + purls: [] + size: 16978 + timestamp: 1718848865819 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda + sha256: 31d44f297ad87a1e6510895740325a635dd204556aa7e079194a0034cdd7e66a + md5: 608e0ef8256b81d04456e8d211eee3e8 + depends: + - libgcc-ng >=12 + - libxcb >=1.16,<2.0.0a0 + license: MIT + license_family: MIT + purls: [] + size: 51689 + timestamp: 1718844051451 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.47-hb03c661_0.conda + sha256: 19c2bb14bec84b0e995b56b752369775c75f1589314b43733948bb5f471a6915 + md5: b56e0c8432b56decafae7e78c5f29ba5 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - xorg-libx11 >=1.8.13,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 399291 + timestamp: 1772021302485 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda + sha256: c12396aabb21244c212e488bbdc4abcdef0b7404b15761d9329f5a4a39113c4b + md5: fb901ff28063514abb6046c9ec2c4a45 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + purls: [] + size: 58628 + timestamp: 1734227592886 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda + sha256: 277841c43a39f738927145930ff963c5ce4c4dacf66637a3d95d802a64173250 + md5: 1c74ff8c35dcadf952a16f752ca5aa49 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libuuid >=2.38.1,<3.0a0 + - xorg-libice >=1.1.2,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 27590 + timestamp: 1741896361728 +- 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 + purls: [] + size: 839652 + timestamp: 1770819209719 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda + sha256: 6bc6ab7a90a5d8ac94c7e300cc10beb0500eeba4b99822768ca2f2ef356f731b + md5: b2895afaf55bf96a8c8282a2e47a5de0 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 15321 + timestamp: 1762976464266 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda + sha256: 048c103000af9541c919deef03ae7c5e9c570ffb4024b42ecb58dbde402e373a + md5: f2ba4192d38b6cef2bb2c25029071d90 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxfixes >=6.0.2,<7.0a0 + license: MIT + license_family: MIT + purls: [] + size: 14415 + timestamp: 1770044404696 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda + sha256: 832f538ade441b1eee863c8c91af9e69b356cd3e9e1350fff4fe36cc573fc91a + md5: 2ccd714aa2242315acaf0a67faea780b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxfixes >=6.0.1,<7.0a0 + - xorg-libxrender >=0.9.11,<0.10.0a0 + license: MIT + license_family: MIT + purls: [] + size: 32533 + timestamp: 1730908305254 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda + sha256: 43b9772fd6582bf401846642c4635c47a9b0e36ca08116b3ec3df36ab96e0ec0 + md5: b5fcc7172d22516e1f965490e65e33a4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxfixes >=6.0.1,<7.0a0 + license: MIT + license_family: MIT + purls: [] + size: 13217 + timestamp: 1727891438799 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda + sha256: 25d255fb2eef929d21ff660a0c687d38a6d2ccfbcbf0cc6aa738b12af6e9d142 + md5: 1dafce8548e38671bea82e3f5c6ce22f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 20591 + timestamp: 1762976546182 +- 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 + license: MIT + license_family: MIT + purls: [] + size: 50326 + timestamp: 1769445253162 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda + sha256: 83c4c99d60b8784a611351220452a0a85b080668188dce5dfa394b723d7b64f4 + md5: ba231da7fccf9ea1e768caf5c7099b84 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - xorg-libx11 >=1.8.12,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 20071 + timestamp: 1759282564045 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-hb03c661_0.conda + sha256: 495f99c8eacfa4ae2d8fed2a7f2105777af89acdc204df145d2bbbc380ac631b + md5: adba2e334082bb218db806d4c12277c9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - xorg-libx11 >=1.8.13,<2.0a0 + - xorg-libxext >=1.3.7,<2.0a0 + - xorg-libxfixes >=6.0.2,<7.0a0 + license: MIT + license_family: MIT + purls: [] + size: 47717 + timestamp: 1779111857071 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-hb03c661_0.conda + sha256: 80ed047a5cb30632c3dc5804c7716131d767089f65877813d4ae855ee5c9d343 + md5: e192019153591938acf7322b6459d36e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxrender >=0.9.12,<0.10.0a0 + license: MIT + license_family: MIT + purls: [] + size: 30456 + timestamp: 1769445263457 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda + sha256: 044c7b3153c224c6cedd4484dd91b389d2d7fd9c776ad0f4a34f099b3389f4a1 + md5: 96d57aba173e878a2089d5638016dc5e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 33005 + timestamp: 1734229037766 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda + sha256: 752fdaac5d58ed863bbf685bb6f98092fe1a488ea8ebb7ed7b606ccfce08637a + md5: 7bbe9a0cc0df0ac5f5a8ad6d6a11af2f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - xorg-libx11 >=1.8.10,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxi >=1.7.10,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 32808 + timestamp: 1727964811275 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda + sha256: 64db17baaf36fa03ed8fae105e2e671a7383e22df4077486646f7dbf12842c9f + md5: 665d152b9c6e78da404086088077c844 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 18701 + timestamp: 1769434732453 +- conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda + sha256: 6d9ea2f731e284e9316d95fa61869fe7bbba33df7929f82693c121022810f4ad + md5: a77f85f77be52ff59391544bfe73390a + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: MIT + license_family: MIT + purls: [] + size: 85189 + timestamp: 1753484064210 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zfp-1.0.1-h909a3a2_5.conda + sha256: 5fabe6cccbafc1193038862b0b0d784df3dae84bc48f12cac268479935f9c8b7 + md5: 6a0eb48e58684cca4d7acc8b7a0fd3c7 + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + - libgcc >=14 + - libstdcxx >=14 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 277694 + timestamp: 1766549572069 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_2.conda + sha256: 245c9ee8d688e23661b95e3c6dd7272ca936fabc03d423cdb3cdee1bbcf9f2f2 + md5: c2a01a08fc991620a74b32420e97868a + depends: + - __glibc >=2.17,<3.0.a0 + - libzlib 1.3.2 h25fd6f3_2 + license: Zlib + license_family: Other + purls: [] + size: 95931 + timestamp: 1774072620848 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda + sha256: ea4e50c465d70236408cb0bfe0115609fd14db1adcd8bd30d8918e0291f8a75f + md5: 2aadb0d17215603a82a2a6b0afd9a4cb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: Zlib + license_family: Other + purls: [] + size: 122618 + timestamp: 1770167931827 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda + sha256: 68f0206ca6e98fea941e5717cec780ed2873ffabc0e1ed34428c061e2c6268c7 + md5: 4a13eeac0b5c8e5b8ab496e6c4ddd829 + depends: + - __glibc >=2.17,<3.0.a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 601375 + timestamp: 1764777111296 +- conda: https://conda.anaconda.org/conda-forge/noarch/antlr-python-runtime-4.9.3-pyhd8ed1ab_1.tar.bz2 + sha256: b91f8ab4ac2b48972fbee1fc8e092cc452fdf59156e4ff2322c94bbf73650f94 + md5: c88eaec8de9ae1fa161205aa18e7a5b1 + depends: + - python >=3.6 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/antlr4-python3-runtime?source=hash-mapping + size: 101065 + timestamp: 1638309284042 +- conda: https://conda.anaconda.org/conda-forge/noarch/arviz-0.23.4-pyhcf101f3_0.conda + sha256: d4e638ca192077e0dd7f60d33ae416f34d1d2b1f032e775eaeb38f07556b79ed + md5: f64907fda280c6f731d240572ca7956c + depends: + - python >=3.10 + - setuptools >=60.0.0 + - matplotlib-base >=3.8 + - numpy >=1.26.0 + - scipy >=1.11.0 + - packaging + - pandas >=2.1.0 + - xarray >=2023.7.0 + - h5netcdf >=1.0.2 + - typing_extensions >=4.1.0 + - xarray-einstats >=0.3 + - h5py + - platformdirs + - python + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/arviz?source=hash-mapping + size: 1499441 + timestamp: 1770281563537 +- conda: https://conda.anaconda.org/conda-forge/noarch/astroplan-0.10.1-pyhd8ed1ab_2.conda + sha256: cc2ae4924d4ca8e5d411cb40d686c698e570ed80b96f9a3b247e97d85de91182 + md5: a1539d8a811402e8f5e1f9ee69197fd4 + depends: + - astropy-base >=4 + - matplotlib-base + - numpy >=1.17 + - python >=3.9 + - pytz + - six + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/astroplan?source=hash-mapping + size: 71853 + timestamp: 1734484572825 +- conda: https://conda.anaconda.org/conda-forge/noarch/astropy-iers-data-0.2026.5.25.1.14.13-pyhd8ed1ab_0.conda + sha256: b2e2c6f4451388ca22e9a6ea57aadfc2dae7b380c5ccf7a676ad689ff5aff089 + md5: bc2a4cd601a4d4675d5f726c2195934c + depends: + - python >=3.10 + license: BSD-3-Clause + purls: + - pkg:pypi/astropy-iers-data?source=compressed-mapping + size: 1233982 + timestamp: 1779676762952 +- conda: https://conda.anaconda.org/conda-forge/noarch/bokeh-3.9.0-pyhd8ed1ab_0.conda + sha256: 96a6486d4fe27c02c1092a40096dfd82043929b3a7da156a49b28d851159c551 + md5: b9a6da57e94cd12bd71e7ab0713ef052 + depends: + - contourpy >=1.2 + - jinja2 >=2.9 + - narwhals >=1.13 + - numpy >=1.16 + - packaging >=16.8 + - pillow >=7.1.0 + - python >=3.10 + - pyyaml >=3.10 + - tornado >=6.2 + - xyzservices >=2021.09.1 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/bokeh?source=hash-mapping + size: 4240579 + timestamp: 1773302678722 +- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda + sha256: 9812a303a1395e1dafbd92e5bc8a1ff6013bcbba0a09c7f03a8d23e43560aa9b + md5: 489b8e97e666c93f68fdb35c3c9b957f + depends: + - __unix + license: ISC + purls: [] + size: 129868 + timestamp: 1779289852439 +- conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 + noarch: python + sha256: 561e6660f26c35d137ee150187d89767c988413c978e1b712d53f27ddf70ea17 + md5: 9b347a7ec10940d3f7941ff6c460b551 + depends: + - cached_property >=1.5.2,<1.5.3.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 4134 + timestamp: 1615209571450 +- conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 + sha256: 6dbf7a5070cc43d90a1e4c2ec0c541c69d8e30a0e25f50ce9f6e4a432e42c5d7 + md5: 576d629e47797577ab0f1b351297ef4a + depends: + - python >=3.6 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/cached-property?source=hash-mapping + size: 11065 + timestamp: 1615209567874 +- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda + sha256: 645655a3510e38e625da136595f3f16f2130c3263630cc3bc8f60f619ddbe490 + md5: 9fefff2f745ea1cc2ef15211a20c054a + depends: + - python >=3.10 + license: ISC + purls: + - pkg:pypi/certifi?source=compressed-mapping + size: 134201 + timestamp: 1779285131141 +- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda + sha256: 3f9483d62ce24ecd063f8a5a714448445dc8d9e201147c46699fc0033e824457 + md5: a9167b9571f3baa9d448faa2139d1089 + depends: + - python >=3.10 + license: MIT + license_family: MIT + purls: + - pkg:pypi/charset-normalizer?source=hash-mapping + size: 58872 + timestamp: 1775127203018 +- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.0-pyhc90fa1f_0.conda + sha256: 99ab8ef815c4520cce3a7482c2513f377c14348206857661d84c76a55e030f97 + md5: 003767c47f1f0a474c4de268b57839c3 + depends: + - __unix + - python + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/click?source=compressed-mapping + size: 104631 + timestamp: 1779108494556 +- conda: https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda + sha256: 4c287c2721d8a34c94928be8fe0e9a85754e90189dd4384a31b1806856b50a67 + md5: 61b8078a0905b12529abc622406cb62c + depends: + - python >=3.10 + - python + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/cloudpickle?source=hash-mapping + size: 27353 + timestamp: 1765303462831 +- 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 + size: 27011 + timestamp: 1733218222191 +- conda: https://conda.anaconda.org/conda-forge/noarch/corner-2.2.3-pyhd8ed1ab_1.conda + sha256: 0cd1c3b4d7a2ba10c34752d48ca7a5d758618dabc58e459f5126701672ff33cf + md5: cacf4ba53cf0341b2aa9d2755a5f5a98 + depends: + - arviz >=0.9 + - python >=3.9 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/corner?source=hash-mapping + size: 20423 + timestamp: 1734278657058 +- conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda + sha256: bb47aec5338695ff8efbddbc669064a3b10fe34ad881fb8ad5d64fbfa6910ed1 + md5: 4c2a8fef270f6c69591889b93f9f55c1 + depends: + - python >=3.10 + - python + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/cycler?source=hash-mapping + size: 14778 + timestamp: 1764466758386 +- conda: https://conda.anaconda.org/conda-forge/noarch/dask-2026.3.0-pyhc364b38_0.conda + sha256: fe59c26dc20a47aa56fc38a2326f2a62403f3eed366837c1a0fba166a220d2b7 + md5: f9761ef056ba0ccef16e01cfceee62c2 + depends: + - python >=3.10 + - dask-core >=2026.3.0,<2026.3.1.0a0 + - distributed >=2026.3.0,<2026.3.1.0a0 + - cytoolz >=0.11.2 + - lz4 >=4.3.2 + - numpy >=1.26 + - pandas >=2.0 + - bokeh >=3.1.0 + - jinja2 >=2.10.3 + - pyarrow >=16.0 + - python + constrains: + - openssl !=1.1.1e + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 11383 + timestamp: 1773913283482 +- conda: https://conda.anaconda.org/conda-forge/noarch/dask-core-2026.3.0-pyhc364b38_0.conda + sha256: 5497e56b12b8a07921668f6469d725be9826ffe5ae8a2f6f71d26369400b41ca + md5: 809f4cde7c853f437becc43415a2ecdf + depends: + - python >=3.10 + - click >=8.1 + - cloudpickle >=3.0.0 + - fsspec >=2021.9.0 + - packaging >=20.0 + - partd >=1.4.0 + - pyyaml >=5.3.1 + - toolz >=0.12.0 + - importlib-metadata >=4.13.0 + - python + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/dask?source=hash-mapping + size: 1066502 + timestamp: 1773823162829 +- conda: https://conda.anaconda.org/conda-forge/noarch/dask-image-2025.11.0-pyhd8ed1ab_0.conda + sha256: e20bfbd456d6e4d52756d191803033f414a7fe1c6afa9c7311fb52b60b74555e + md5: 6be108f5a63f7ba1c2cb9b15a0836b6c + depends: + - dask >=2024.4.1 + - dask-core >=2024.4.1 + - numpy >=1.18 + - pandas >=2.0.0 + - pims >=0.4.1 + - python >=3.9 + - scipy >=1.7.0 + - tifffile >=2018.10.18 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/dask-image?source=hash-mapping + size: 69018 + timestamp: 1763075350614 +- conda: https://conda.anaconda.org/conda-forge/noarch/dateparser-1.4.0-pyhd8ed1ab_0.conda + sha256: 9ccdd848db68efc03afbf5fc67e92accc912c0b609a4f4ba54b720f0c27c41a2 + md5: 4d8650857be15983a33032bf18059787 + depends: + - python >=3.10 + - python-dateutil >=2.7.0 + - pytz >=2024.2 + - regex >=2024.9.11 + - tzlocal >=0.2 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/dateparser?source=hash-mapping + size: 180549 + timestamp: 1774525022824 +- conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_0.conda + sha256: 430bd9d731b265f0bedb3183ac3ecfaa1656390c092b6e864ff8cc1229843c8c + md5: 61dcf784d59ef0bd62c57d982b154ace + depends: + - python >=3.10 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/decorator?source=compressed-mapping + size: 16102 + timestamp: 1779115228886 +- 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 + size: 15896 + timestamp: 1768934186726 +- conda: https://conda.anaconda.org/conda-forge/noarch/distributed-2026.3.0-pyhc364b38_0.conda + sha256: 49cbb318f7a1797b9f17c135c9b5c48ba2086570a58c99054d3b40bf13a5b815 + md5: 8efb90a27e3b948514a428cb99f3fc70 + depends: + - python >=3.10 + - click >=8.0 + - cloudpickle >=3.0.0 + - cytoolz >=0.12.0 + - dask-core >=2026.3.0,<2026.3.1.0a0 + - jinja2 >=2.10.3 + - locket >=1.0.0 + - msgpack-python >=1.0.2 + - packaging >=20.0 + - psutil >=5.8.0 + - pyyaml >=5.4.1 + - sortedcontainers >=2.0.5 + - tblib >=1.6.0 + - toolz >=0.12.0 + - tornado >=6.2.0 + - urllib3 >=1.26.5 + - zict >=3.0.0 + - python + constrains: + - openssl !=1.1.1e + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/distributed?source=hash-mapping + size: 845608 + timestamp: 1773858577032 +- 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 + size: 22491 + timestamp: 1734368817583 +- conda: https://conda.anaconda.org/conda-forge/noarch/dqsegdb2-1.3.0-pyhd8ed1ab_0.conda + sha256: 943d9927e3201123136379cbad784905f7657b631880f7b8fdaca197095d4baa + md5: 8e8f1ccdd98ef1eed28063e0b14d488d + depends: + - click >=6.7 + - igwn-auth-utils >=1.0.0 + - igwn-segments >=2.0.0 + - python >=3.9 + license: GPL-3.0-or-later + license_family: GPL + purls: + - pkg:pypi/dqsegdb2?source=hash-mapping + size: 28326 + timestamp: 1736356382409 +- 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 + 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: [] + size: 397370 + timestamp: 1566932522327 +- conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 + sha256: c52a29fdac682c20d252facc50f01e7c2e7ceac52aa9817aaf0bb83f7559ec5c + md5: 34893075a5c9e55cdafac56607368fc6 + license: OFL-1.1 + license_family: Other + purls: [] + size: 96530 + timestamp: 1620479909603 +- conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 + sha256: 00925c8c055a2275614b4d983e1df637245e19058d79fc7dd1a93b8d9fb4b139 + md5: 4d59c254e01d9cde7957100457e2d5fb + license: OFL-1.1 + license_family: Other + purls: [] + size: 700814 + timestamp: 1620479612257 +- conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda + sha256: 2821ec1dc454bd8b9a31d0ed22a7ce22422c0aef163c59f49dfdf915d0f0ca14 + md5: 49023d73832ef61042f6a237cb2687e7 + license: LicenseRef-Ubuntu-Font-Licence-Version-1.0 + license_family: Other + purls: [] + size: 1620504 + timestamp: 1727511233259 +- conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 + sha256: a997f2f1921bb9c9d76e6fa2f6b408b7fa549edd349a77639c9fe7a23ea93e61 + md5: fee5683a3f04bd15cbd8318b096a27ab + depends: + - fonts-conda-forge + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 3667 + timestamp: 1566974674465 +- conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda + sha256: 54eea8469786bc2291cc40bca5f46438d3e062a399e8f53f013b6a9f50e98333 + md5: a7970cd949a077b7cb9696379d338681 + depends: + - font-ttf-ubuntu + - font-ttf-inconsolata + - font-ttf-dejavu-sans-mono + - font-ttf-source-code-pro + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 4059 + timestamp: 1762351264405 +- conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2026.4.0-pyhd8ed1ab_0.conda + sha256: 079701b4ff3b317a1a158cabd48cf2b856b8b8d3ef44f152809d9acf20cc8e10 + md5: 2c11aa96ea85ced419de710c1c3a78ff + depends: + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/fsspec?source=hash-mapping + size: 149694 + timestamp: 1777547807038 +- conda: https://conda.anaconda.org/conda-forge/noarch/future-1.0.0-pyhd8ed1ab_2.conda + sha256: 45dfd037889b7075c5eb46394f93172de0be0b1624c7f802dd3ecc94b814d8e0 + md5: 1054c53c95d85e35b88143a3eda66373 + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/future?source=hash-mapping + size: 364561 + timestamp: 1738926525117 +- conda: https://conda.anaconda.org/conda-forge/noarch/gwdatafind-2.1.1-pyh707e725_0.conda + sha256: 3fc49a834431d7fbe40579a9577422c5d820d3cca027da9d4b054013944b2713 + md5: a001fbdeb771cafde9c2e1e6c3ba66fd + depends: + - __unix + - igwn-auth-utils >=0.3.1 + - igwn-segments + - python >=3.10 + constrains: + - lscsoft-glue >=2.0.0 + license: GPL-3.0-or-later + license_family: GPL + purls: + - pkg:pypi/gwdatafind?source=hash-mapping + size: 40668 + timestamp: 1762163422217 +- conda: https://conda.anaconda.org/conda-forge/noarch/gwosc-0.8.2-pyhd8ed1ab_0.conda + sha256: e4c2f6b1c389fb551585cb7ba28d5830b8d91c991108f98202aec12f95ee6b63 + md5: 62fb0f6327a3c8cfc64f222aa3c012b0 + depends: + - python >=3.10 + - requests >=1.0.0 + - tenacity + license: MIT + license_family: MIT + purls: + - pkg:pypi/gwosc?source=hash-mapping + size: 32622 + timestamp: 1775579303307 +- conda: https://conda.anaconda.org/conda-forge/noarch/gwpy-4.0.1-pyhcf101f3_0.conda + sha256: ee29b7f28126612f313212d0852e4bac062839cab82d950ea9ab676ccb3b9f58 + md5: ac9dec4c551b055e5b86d9b1fca0356b + depends: + - astropy-base >=6.1.6 + - dateparser >=1.2.0 + - dqsegdb2 >=1.3.0 + - gwdatafind >=2.0.0 + - gwosc >=0.6.0 + - h5py >=3.11.0 + - igwn-segments >=2.0.0 + - ligotimegps >=2.1.0 + - matplotlib-base >=3.8.0 + - numpy >=1.24.4 + - packaging >=24.2 + - python >=3.11 + - python-dateutil >=2.9.0 + - requests >=2.32.3 + - scipy >=1.12.0 + - tqdm >=4.67.0 + - python + license: GPL-3.0-or-later + license_family: GPL + purls: + - pkg:pypi/gwpy?source=hash-mapping + size: 1260379 + timestamp: 1770114269741 +- conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + sha256: 84c64443368f84b600bfecc529a1194a3b14c3656ee2e832d15a20e0329b6da3 + md5: 164fc43f0b53b6e3a7bc7dce5e4f1dc9 + depends: + - python >=3.10 + - hyperframe >=6.1,<7 + - hpack >=4.1,<5 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/h2?source=hash-mapping + size: 95967 + timestamp: 1756364871835 +- conda: https://conda.anaconda.org/conda-forge/noarch/h5netcdf-1.8.1-pyhd8ed1ab_0.conda + sha256: 5bf081c0f21a57fc84b5000d53f043d63638b77dcc2137f87511a4581838c9f6 + md5: ca7f9ba8762d3e360e47917a10e23760 + depends: + - h5py + - numpy + - packaging + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/h5netcdf?source=hash-mapping + size: 57732 + timestamp: 1769241877548 +- conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + sha256: 6ad78a180576c706aabeb5b4c8ceb97c0cb25f1e112d76495bff23e3779948ba + md5: 0a802cb9888dd14eeefc611f05c40b6e + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/hpack?source=hash-mapping + size: 30731 + timestamp: 1737618390337 +- conda: https://conda.anaconda.org/conda-forge/noarch/hydra-core-1.3.2-pyhd8ed1ab_1.conda + sha256: 40b4469bd65e0156de1136ae8b265f5d2d72f14b8d431e009836d59438339ee8 + md5: a189dd36bcaaf4c7647deb2dcb4e1b05 + depends: + - antlr-python-runtime 4.9.* + - omegaconf >=2.2,<2.4 + - packaging + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/hydra-core?source=hash-mapping + size: 110015 + timestamp: 1736934833060 +- conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + sha256: 77af6f5fe8b62ca07d09ac60127a30d9069fdc3c68d6b256754d0ffb1f7779f8 + md5: 8e6923fc12f1fe8f8c4e5c9f343256ac + depends: + - python >=3.9 + license: MIT + license_family: MIT + purls: + - pkg:pypi/hyperframe?source=hash-mapping + size: 17397 + timestamp: 1737618427549 +- conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.15-pyhcf101f3_0.conda + sha256: 3d25f9f6f7ab3e1ce6429fc8c8aae0335cf446692e715068488536d220cc43de + md5: 1b9083b7f00609605d1483dbc6071a81 + depends: + - python >=3.10 + - python + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/idna?source=compressed-mapping + size: 62642 + timestamp: 1779294335905 +- conda: https://conda.anaconda.org/conda-forge/noarch/igwn-auth-utils-1.4.0-pyh707e725_0.conda + sha256: 6ac42aea820d0c5f5d240f330d6f591d100c22e56800790aab89ee85d9de8e12 + md5: ece2973a2bc0a03fbae8107f0feb7d48 + depends: + - __unix + - cryptography >=44.0.1 + - htgettoken >=2.1 + - python >=3.9 + - python-gssapi >=1.9.0 + - requests >=2.32.0 + - safe-netrc >=1.0.0 + - scitokens >=1.8.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/igwn-auth-utils?source=hash-mapping + size: 33429 + timestamp: 1748854840055 +- conda: https://conda.anaconda.org/conda-forge/noarch/imageio-2.37.0-pyhfb79c49_0.conda + sha256: 8ef69fa00c68fad34a3b7b260ea774fda9bd9274fd706d3baffb9519fd0063fe + md5: b5577bc2212219566578fd5af9993af6 + depends: + - numpy + - pillow >=8.3.2 + - python >=3.9 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/imageio?source=hash-mapping + size: 293226 + timestamp: 1738273949742 +- conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda + sha256: 43e2a5497cad1598ff88a3e69f69bc88b7b8f141fa63c60eab5db296317318b8 + md5: ffc17e785d64e12fc311af9184221839 + depends: + - python >=3.10 + - zipp >=3.20 + - python + license: Apache-2.0 + purls: + - pkg:pypi/importlib-metadata?source=compressed-mapping + size: 34766 + timestamp: 1779714582554 +- conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda + sha256: e1a9e3b1c8fe62dc3932a616c284b5d8cbe3124bbfbedcf4ce5c828cb166ee19 + md5: 9614359868482abba1bd15ce465e3c42 + depends: + - python >=3.10 + license: MIT + license_family: MIT + purls: + - pkg:pypi/iniconfig?source=hash-mapping + size: 13387 + timestamp: 1760831448842 +- conda: https://conda.anaconda.org/conda-forge/noarch/invoke-3.0.3-pyhd8ed1ab_0.conda + sha256: d7421c54944dec04d2671e23196a15583d48f309d06fbcf995b5dfa6d586a5e9 + md5: 4dee387356d58bdc4b686ae3424fbd9e + depends: + - python >=3.10 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/invoke?source=hash-mapping + size: 133811 + timestamp: 1775579645825 +- conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + sha256: fc9ca7348a4f25fed2079f2153ecdcf5f9cf2a0bc36c4172420ca09e1849df7b + md5: 04558c96691bed63104678757beb4f8d + depends: + - markupsafe >=2.0 + - python >=3.10 + - python + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/jinja2?source=hash-mapping + size: 120685 + timestamp: 1764517220861 +- conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda + sha256: 301539229d7be6420c084490b8145583291123f0ce6b92f56be5948a2c83a379 + md5: 615de2a4d97af50c350e5cf160149e77 + depends: + - python >=3.10 + - setuptools + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/joblib?source=hash-mapping + size: 226448 + timestamp: 1765794135253 +- conda: https://conda.anaconda.org/conda-forge/noarch/jplephem-2.24-pyha4b2019_1.conda + sha256: 0a3b690b4c0601319607b7fd4ba2eefcd87dc6a4b39d5ad6b50d8e4cb16f7442 + md5: d14d799f3aec31b2eb761e3d394f7bab + depends: + - numpy + - python >=3.10 + license: MIT + license_family: MIT + purls: + - pkg:pypi/jplephem?source=hash-mapping + size: 41950 + timestamp: 1772477050871 +- conda: https://conda.anaconda.org/conda-forge/noarch/lalsuite-7.25-pyhd8ed1ab_0.conda + sha256: 2dff776a231ea5416ec88ffb9ac59816e11df0a2ebca960afb0bfca61abb1538 + md5: d655403fd4837dd4a33ae9e780ab6b82 + depends: + - lal 7.6.1.* + - lalapps 10.0.2.* + - lalburst 2.0.6.* + - lalframe 3.0.6.* + - lalinference 4.1.8.* + - lalinspiral 5.0.2.* + - lalmetaio 4.0.5.* + - lalpulsar 7.1.0.* + - lalsimulation 6.1.0.* + - python >=3.10 + license: GPL-3.0-or-later + license_family: GPL + purls: + - pkg:pypi/lalsuite?source=hash-mapping + size: 16292 + timestamp: 1734789301887 +- conda: https://conda.anaconda.org/conda-forge/noarch/ligo-gracedb-2.15.7-pyhcf101f3_0.conda + sha256: 13efdfebcdbd05b753f0731364200ba34e217211610ed1390e32bb4f228762f8 + md5: 495229a27540f4a8a74458d03509ecb6 + depends: + - cryptography >=1.7.2 + - future >=0.15.0 + - igwn-auth-utils >=1.0.0 + - python >=3.10 + - requests >=2.6.0 + - python + license: GPL-3.0-or-later + license_family: GPL + purls: + - pkg:pypi/ligo-gracedb?source=hash-mapping + size: 2286879 + timestamp: 1775146909865 +- conda: https://conda.anaconda.org/conda-forge/noarch/ligotimegps-2.1.0-pyhd8ed1ab_0.conda + sha256: 15f32bee2a8e2dee908f06a2737ffa73bdcace49fae85b5fb33a02a8f178cb9c + md5: 48ffbc346313b1b98f5fc4bdee8fdc52 + depends: + - python >=3.11 + license: GPL-3.0-or-later + license_family: GPL + purls: + - pkg:pypi/ligotimegps?source=hash-mapping + size: 26877 + timestamp: 1759852472917 +- conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 + sha256: 9afe0b5cfa418e8bdb30d8917c5a6cec10372b037924916f1f85b9f4899a67a6 + md5: 91e27ef3d05cc772ce627e51cff111c4 + depends: + - python >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.* + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/locket?source=hash-mapping + size: 8250 + timestamp: 1650660473123 +- conda: https://conda.anaconda.org/conda-forge/noarch/lscsoft-glue-4.1.1-pyhd8ed1ab_0.conda + sha256: 7274143fbb34f473bca5f8a1c72c76c9845e00617b6cc9679903b6a574ee6a1b + md5: fc8ec1081b314a802e3905b5d0ac2689 + depends: + - igwn-segments + - python >=3.9 + license: GPL-3.0-or-later + license_family: GPL + purls: + - pkg:pypi/lscsoft-glue?source=hash-mapping + size: 50615 + timestamp: 1759328954284 +- conda: https://conda.anaconda.org/conda-forge/noarch/mpmath-1.4.1-pyhd8ed1ab_0.conda + sha256: 5bbf2f8179ec43d34d67ca8e4989d216c1bdb4b749fe6cb40e86ebf88c1b5300 + md5: 2e81b32b805f406d23ba61938a184081 + depends: + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/mpmath?source=hash-mapping + size: 464918 + timestamp: 1773662068273 +- conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + sha256: d09c47c2cf456de5c09fa66d2c3c5035aa1fa228a1983a433c47b876aa16ce90 + md5: 37293a85a0f4f77bbd9cf7aaefc62609 + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/munkres?source=hash-mapping + size: 15851 + timestamp: 1749895533014 +- conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.21.2-pyhcf101f3_0.conda + sha256: 70f43d62450927d51673eecd8823e14f5b3cfebdb43cda1d502eba97162bab42 + md5: 6687827c332121727ce383919e1ec8c2 + depends: + - python >=3.10 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/narwhals?source=compressed-mapping + size: 284323 + timestamp: 1778929680962 +- conda: https://conda.anaconda.org/conda-forge/noarch/natsort-8.4.0-pyhcf101f3_2.conda + sha256: aeb1548eb72e4f198e72f19d242fb695b35add2ac7b2c00e0d83687052867680 + md5: e941e85e273121222580723010bd4fa2 + depends: + - python >=3.9 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/natsort?source=hash-mapping + size: 39262 + timestamp: 1770905275632 +- conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda + sha256: f6a82172afc50e54741f6f84527ef10424326611503c64e359e25a19a8e4c1c6 + md5: a2c1eeadae7a309daed9d62c96012a2b + depends: + - python >=3.11 + - python + constrains: + - numpy >=1.25 + - scipy >=1.11.2 + - matplotlib-base >=3.8 + - pandas >=2.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/networkx?source=hash-mapping + size: 1587439 + timestamp: 1765215107045 +- conda: https://conda.anaconda.org/conda-forge/noarch/omegaconf-2.3.0-pyhd8ed1ab_0.conda + sha256: df806841be847e5287b22b6ae7f380874f81ea51f1b51ae14a570f3385c7b133 + md5: 23cc056834cab53849b91f78d6ee3ea0 + depends: + - antlr-python-runtime 4.9.* + - python >=3.7 + - pyyaml >=5.1.0 + - typing_extensions + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/omegaconf?source=hash-mapping + size: 166453 + timestamp: 1670575519562 +- conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda + sha256: 3906abfb6511a3bb309e39b9b1b7bc38f50a723971de2395489fd1f379255890 + md5: 4c06a92e74452cfa53623a81592e8934 + depends: + - python >=3.8 + - python + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/packaging?source=hash-mapping + size: 91574 + timestamp: 1777103621679 +- conda: https://conda.anaconda.org/conda-forge/noarch/paramiko-5.0.0-pyhd8ed1ab_0.conda + sha256: 1b64a1b91b57c2a4c42a9e73b9a544e22fcdac55a953d2c09644b184edde7c3f + md5: e7fa4da3b74ededb2fa7ab4171f63d21 + depends: + - bcrypt >=3.2 + - cryptography >=3.3 + - invoke >=2.0 + - pynacl >=1.5 + - python >=3.10 + license: LGPL-2.1-or-later + license_family: LGPL + purls: + - pkg:pypi/paramiko?source=hash-mapping + size: 152584 + timestamp: 1778365181112 +- conda: https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda + sha256: 472fc587c63ec4f6eba0cc0b06008a6371e0a08a5986de3cf4e8024a47b4fe6c + md5: 0badf9c54e24cecfb0ad2f99d680c163 + depends: + - locket + - python >=3.9 + - toolz + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/partd?source=hash-mapping + size: 20884 + timestamp: 1715026639309 +- conda: https://conda.anaconda.org/conda-forge/noarch/pims-0.7-pyhd8ed1ab_1.conda + sha256: cc9521b3a517c9c0f5097a96ed2285b89ba3ee291320a26100261fea2130f8bf + md5: 146adfd93cac5e7c6b5def8f39c917cd + depends: + - imageio + - jinja2 + - numpy >=1.19 + - packaging + - pillow + - python >=3.9 + - slicerator >=1.1.0 + - tifffile + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pims?source=hash-mapping + size: 71357 + timestamp: 1734051228623 +- conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.1.1-pyh8b19718_0.conda + sha256: 1bd94ef1ae08fd811ef3b26857e46ba460c7430bf1f3ccd94a4d6614fd619bd5 + md5: 35870d32aed92041d31cbb15e822dca3 + depends: + - python >=3.10,<3.13.0a0 + - setuptools + - wheel + license: MIT + license_family: MIT + purls: + - pkg:pypi/pip?source=hash-mapping + size: 1201616 + timestamp: 1777924080196 +- conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.6-pyhcf101f3_0.conda + sha256: 8f29915c172f1f7f4f7c9391cd5dac3ebf5d13745c8b7c8006032615246345a5 + md5: 89c0b6d1793601a2a3a3f7d2d3d8b937 + depends: + - python >=3.10 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/platformdirs?source=hash-mapping + size: 25862 + timestamp: 1775741140609 +- conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda + sha256: e14aafa63efa0528ca99ba568eaf506eb55a0371d12e6250aaaa61718d2eb62e + md5: d7585b6550ad04c8c5e21097ada2888e + depends: + - python >=3.9 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/pluggy?source=hash-mapping + size: 25877 + timestamp: 1764896838868 +- conda: https://conda.anaconda.org/conda-forge/noarch/ptemcee-1.0.0-py_0.tar.bz2 + sha256: 0f76c1869194d3246673361a2f1e21a38ea275609e6d240183bcbc7b32caac8d + md5: d7eb1dc7fb71bbcc6ff3f1f2239f73e2 + depends: + - numpy + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/ptemcee?source=hash-mapping + size: 19208 + timestamp: 1545353618172 +- conda: https://conda.anaconda.org/conda-forge/noarch/pyavm-0.9.9-pyhc455866_0.conda + sha256: f232f80b489147cf447bbaa20bf3cf42f6df3059a92f5a4643f7422260e70771 + md5: 559e5051ce00d853e61f3f47e0b47115 + depends: + - python >=3.10 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pyavm?source=hash-mapping + size: 212409 + timestamp: 1773694994752 +- conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.3-pyhfe8187e_0.conda + sha256: 71a9524f44d6ac6304feae71e2bbe8d8ce0816f0be7a0271c15681ad1040965d + md5: e0f4549ccb507d4af8ed5c5345210673 + depends: + - python >=3.8 + - pybind11-global ==3.0.3 *_0 + - python + constrains: + - pybind11-abi ==11 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pybind11?source=hash-mapping + size: 247963 + timestamp: 1775004608640 +- conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-global-3.0.3-pyh648e204_0.conda + sha256: 97a0fbd2a81d95e90d714e5c628fe860b29a3caad53abcfb90add1965ad85bef + md5: 7fdc3e18c14b862ae5f064c1ea8e2636 + depends: + - python >=3.8 + - __unix + - python + constrains: + - pybind11-abi ==11 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pybind11-global?source=hash-mapping + size: 243898 + timestamp: 1775004520432 +- conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda + sha256: e27e0473fc6723311a0bd48b89b616fa1b996a2f7a2b555338cbbcfb9c640568 + md5: 9c5491066224083c41b6d5635ed7107b + depends: + - python >=3.10 + - python + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pycparser?source=compressed-mapping + size: 55886 + timestamp: 1779293633166 +- conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + sha256: cf70b2f5ad9ae472b71235e5c8a736c9316df3705746de419b59d442e8348e86 + md5: 16c18772b340887160c79a6acc022db0 + depends: + - python >=3.10 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/pygments?source=hash-mapping + size: 893031 + timestamp: 1774796815820 +- conda: https://conda.anaconda.org/conda-forge/noarch/pyjwt-2.13.0-pyhcf101f3_0.conda + sha256: 58cda7489477fecb859ec95bf73cba7e4634db1a517e29e0d3086d7ec71733ae + md5: edb808d7f7396478ab6e457e697b8ec5 + depends: + - python >=3.10 + - typing_extensions >=4.0 + - python + constrains: + - cryptography >=3.4.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pyjwt?source=hash-mapping + size: 33417 + timestamp: 1779400286454 +- conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda + sha256: 417fba4783e528ee732afa82999300859b065dc59927344b4859c64aae7182de + md5: 3687cc0b82a8b4c17e1f0eb7e47163d5 + depends: + - python >=3.10 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/pyparsing?source=hash-mapping + size: 110893 + timestamp: 1769003998136 +- conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + sha256: ba3b032fa52709ce0d9fd388f63d330a026754587a2f461117cac9ab73d8d0d8 + md5: 461219d1a5bd61342293efa2c0c90eac + depends: + - __unix + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pysocks?source=hash-mapping + size: 21085 + timestamp: 1733217331982 +- conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.0.3-pyhc364b38_1.conda + sha256: 960f59442173eee0731906a9077bd5ccf60f4b4226f05a22d1728ab9a21a879c + md5: 6a991452eadf2771952f39d43615bb3e + depends: + - colorama >=0.4 + - 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 + purls: + - pkg:pypi/pytest?source=hash-mapping + size: 299984 + timestamp: 1775644472530 +- 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 + size: 233310 + timestamp: 1751104122689 +- conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda + build_number: 8 + sha256: 80677180dd3c22deb7426ca89d6203f1c7f1f256f2d5a94dc210f6e758229809 + md5: c3efd25ac4d74b1584d2f7a57195ddf1 + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 6958 + timestamp: 1752805918820 +- conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda + sha256: 5020863d629f584b5c057333a67a7aed43e3ed013ba15dd70f353501ccb5aff6 + md5: 03cb60f505ad3ada0a95277af5faeb1a + depends: + - python >=3.10 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/pytz?source=hash-mapping + size: 201747 + timestamp: 1777892201250 +- conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda + sha256: 1715246b19c9f85ee022933b4845f2fc14ac9184981b7b7d9b728bec8e9588da + md5: 4a85203c1d80c1059086ae860836ffb9 + depends: + - python >=3.10 + - certifi >=2023.5.7 + - charset-normalizer >=2,<4 + - idna >=2.5,<4 + - urllib3 >=1.26,<3 + - python + constrains: + - chardet >=3.0.2,<8 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/requests?source=compressed-mapping + size: 68709 + timestamp: 1778851103479 +- conda: https://conda.anaconda.org/conda-forge/noarch/safe-netrc-1.0.1-pyhd8ed1ab_1.conda + sha256: 644be784d3e33521464c7bb5d8830e2537f7a1feb1d618d36f05b7548e0852b5 + md5: fe7b13f5c99a4d31ca9f3f8de2c781d4 + depends: + - python >=3.10 + license: GPL-2.0-or-later + license_family: GPL + purls: + - pkg:pypi/safe-netrc?source=hash-mapping + size: 16997 + timestamp: 1764943298569 +- conda: https://conda.anaconda.org/conda-forge/noarch/scitokens-1.9.7-pyhcf101f3_0.conda + sha256: 46b0d97e9faea846013818d300bb45650522909fc44bef974e370131922c5982 + md5: e3e616e3d47c4956869a6c54b02ed2a3 + depends: + - cryptography + - pyjwt >=1.6.1 + - python >=3.10 + - requests + - python + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/scitokens?source=hash-mapping + size: 54753 + timestamp: 1773654272163 +- conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda + sha256: 82088a6e4daa33329a30bc26dc19a98c7c1d3f05c0f73ce9845d4eab4924e9e1 + md5: 8e194e7b992f99a5015edbd4ebd38efd + depends: + - python >=3.10 + license: MIT + license_family: MIT + purls: + - pkg:pypi/setuptools?source=hash-mapping + size: 639697 + timestamp: 1773074868565 +- 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 + size: 18455 + timestamp: 1753199211006 +- conda: https://conda.anaconda.org/conda-forge/noarch/slicerator-1.1.0-pyhd8ed1ab_1.conda + sha256: 5340c36cb62b7c8a22c267254c037302fea2670a4fb9d29e10ba36565e2a5510 + md5: 102f1100ad3dcbcf57f789600c9c015a + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/slicerator?source=hash-mapping + size: 15755 + timestamp: 1734051114500 +- conda: https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda + sha256: d1e3e06b5cf26093047e63c8cc77b70d970411c5cbc0cb1fad461a8a8df599f7 + md5: 0401a17ae845fa72c7210e206ec5647d + depends: + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/sortedcontainers?source=hash-mapping + size: 28657 + timestamp: 1738440459037 +- conda: https://conda.anaconda.org/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda + sha256: 6b549360f687ee4d11bf85a6d6a276a30f9333df1857adb0fe785f0f8e9bcd60 + md5: f88bb644823094f436792f80fba3207e + depends: + - python >=3.10 + - python + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/tblib?source=hash-mapping + size: 19397 + timestamp: 1762956379123 +- conda: https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.4-pyhcf101f3_0.conda + sha256: 32e75900d6a094ffe4290a8c9f1fa15744d9da8ff617aba4acaa0f057a065c34 + md5: 043f0599dc8aa023369deacdb5ac24eb + depends: + - python >=3.10 + - python + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/tenacity?source=hash-mapping + size: 31404 + timestamp: 1770510172846 +- conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda + sha256: 6016672e0e72c4cf23c0cf7b1986283bd86a9c17e8d319212d78d8e9ae42fdfd + md5: 9d64911b31d57ca443e9f1e36b04385f + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/threadpoolctl?source=hash-mapping + size: 23869 + timestamp: 1741878358548 +- conda: https://conda.anaconda.org/conda-forge/noarch/tifffile-2026.3.3-pyhd8ed1ab_0.conda + sha256: da24795c3000566167fbb51c0933acd7a46fe2661375ad4d8a1748aab2bc9537 + md5: cecacab21bc8f4ed17fac11bc8b08cf0 + depends: + - imagecodecs >=2025.11.11 + - numpy >=1.19.2 + - python >=3.11 + constrains: + - matplotlib-base >=3.3 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/tifffile?source=hash-mapping + size: 193137 + timestamp: 1772701074188 +- conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda + sha256: 91cafdb64268e43e0e10d30bd1bef5af392e69f00edd34dfaf909f69ab2da6bd + md5: b5325cf06a000c5b14970462ff5e4d58 + depends: + - python >=3.10 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/tomli?source=hash-mapping + size: 21561 + timestamp: 1774492402955 +- conda: https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda + sha256: 4e379e1c18befb134247f56021fdf18e112fb35e64dd1691858b0a0f3bea9a45 + md5: c07a6153f8306e45794774cf9b13bd32 + depends: + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/toolz?source=hash-mapping + size: 53978 + timestamp: 1760707830681 +- conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.3-pyh8f84b5b_0.conda + sha256: 9ef8e47cf00e4d6dcc114eb32a1504cc18206300572ef14d76634ba29dfe1eb6 + md5: e5ce43272193b38c2e9037446c1d9206 + depends: + - python >=3.10 + - __unix + - python + license: MPL-2.0 and MIT + purls: + - pkg:pypi/tqdm?source=hash-mapping + size: 94132 + timestamp: 1770153424136 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + sha256: 032271135bca55aeb156cee361c81350c6f3fb203f57d024d7e5a1fc9ef18731 + md5: 0caa1af407ecff61170c9437a808404d + depends: + - python >=3.10 + - python + license: PSF-2.0 + license_family: PSF + purls: + - pkg:pypi/typing-extensions?source=hash-mapping + size: 51692 + timestamp: 1756220668932 +- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda + sha256: 1d30098909076af33a35017eed6f2953af1c769e273a0626a04722ac4acaba3c + md5: ad659d0a2b3e47e38d829aa8cad2d610 + license: LicenseRef-Public-Domain + purls: [] + size: 119135 + timestamp: 1767016325805 +- conda: https://conda.anaconda.org/conda-forge/noarch/tzlocal-5.3.1-pyh8f84b5b_0.conda + sha256: 6447388bd870ab0a2b38af5aa64185cd71028a2a702f0935e636a01d81fba7fc + md5: 369f3170d6f727d3102d83274e403b66 + depends: + - python >=3.10 + - __unix + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/tzlocal?source=hash-mapping + size: 23880 + timestamp: 1756227235167 +- conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda + sha256: feff959a816f7988a0893201aa9727bbb7ee1e9cec2c4f0428269b489eb93fb4 + md5: cbb88288f74dbe6ada1c6c7d0a97223e + depends: + - backports.zstd >=1.0.0 + - brotli-python >=1.2.0 + - h2 >=4,<5 + - pysocks >=1.5.6,<2.0,!=1.5.7 + - python >=3.10 + license: MIT + license_family: MIT + purls: + - pkg:pypi/urllib3?source=hash-mapping + size: 103560 + timestamp: 1778188657149 +- conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.47.0-pyhd8ed1ab_0.conda + sha256: 9e156ffaefb8463437144326ada4b85d1de17961b9997ac5f1cbbaf747bd8bed + md5: d0e3b2f0030cf4fca58bde71d246e94c + depends: + - packaging >=24.0 + - python >=3.10 + license: MIT + license_family: MIT + purls: + - pkg:pypi/wheel?source=hash-mapping + size: 33491 + timestamp: 1776878563806 +- conda: https://conda.anaconda.org/conda-forge/noarch/xarray-2026.4.0-pyhc364b38_0.conda + sha256: 9f5f5905afea6e0188aa6887aed0809033143343a7af06983f4b390c2286d2d7 + md5: 099794df685f800c3f319ff4742dc1bb + depends: + - python >=3.11 + - numpy >=1.26 + - packaging >=24.2 + - pandas >=2.2 + - python + constrains: + - bottleneck >=1.4 + - cartopy >=0.23 + - cftime >=1.6 + - dask-core >=2024.6 + - distributed >=2024.6 + - flox >=0.9 + - h5netcdf >=1.3 + - h5py >=3.11 + - hdf5 >=1.14 + - iris >=3.9 + - matplotlib-base >=3.8 + - nc-time-axis >=1.4 + - netcdf4 >=1.6.0 + - numba >=0.60 + - numbagg >=0.8 + - pint >=0.24 + - pydap >=3.5.0 + - scipy >=1.13 + - seaborn-base >=0.13 + - sparse >=0.15 + - toolz >=0.12 + - zarr >=2.18 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/xarray?source=hash-mapping + size: 1017999 + timestamp: 1776122774298 +- conda: https://conda.anaconda.org/conda-forge/noarch/xarray-einstats-0.9.1-pyhd8ed1ab_0.conda + sha256: 3fefcdb5520c9f7127d67904894cccdc917449a3376f1ccf84127f02ad3aa61b + md5: 18860b32ac96f7e9d8be1c91eb601462 + depends: + - numpy >=1.25 + - python >=3.11 + - scipy >=1.11 + - xarray >=2023.06.0 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/xarray-einstats?source=hash-mapping + size: 37867 + timestamp: 1750279091345 +- conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda + sha256: 663ea9b00d68c2da309114923924686ab6d3f59ef1b196c5029ba16799e7bb07 + md5: 4487b9c371d0161d54b5c7bbd890c0fc + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/xyzservices?source=hash-mapping + size: 51732 + timestamp: 1774900074457 +- conda: https://conda.anaconda.org/conda-forge/noarch/zarr-3.1.5-pyhcf101f3_0.conda + sha256: c36bec7d02d2f227409fcc4cf586cf3a658af068b58374de7f8f2d0b5c1c84f9 + md5: c1844a94b2be61bb03bbb71574a0abfc + depends: + - python >=3.11 + - packaging >=22.0 + - numpy >=1.26 + - numcodecs >=0.14 + - typing_extensions >=4.9 + - donfig >=0.8 + - google-crc32c >=1.5 + - python + constrains: + - fsspec >=2023.10.0 + - obstore >=0.5.1 + license: MIT + license_family: MIT + purls: + - pkg:pypi/zarr?source=hash-mapping + size: 305998 + timestamp: 1763742695201 +- conda: https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda + sha256: 5488542dceeb9f2874e726646548ecc5608060934d6f9ceaa7c6a48c61f9cc8d + md5: e52c2ef711ccf31bb7f70ca87d144b9e + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/zict?source=hash-mapping + size: 36341 + timestamp: 1733261642963 +- conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda + sha256: 210bd31c22bb88f5e2a167df24c95bb5f152b2ada7502f9b8c49d1f5366db423 + md5: ba3dcdc8584155c97c648ae9c044b7a3 + depends: + - python >=3.10 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/zipp?source=compressed-mapping + size: 24190 + timestamp: 1779159948016 +- conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda + build_number: 7 + sha256: 30006902a9274de8abdad5a9f02ef7c8bb3d69a503486af0c1faee30b023e5b7 + md5: eaac87c21aff3ed21ad9656697bb8326 + depends: + - llvm-openmp >=9.0.1 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 8328 + timestamp: 1764092562779 +- conda: https://conda.anaconda.org/conda-forge/osx-64/aom-3.9.1-hf036a51_0.conda + sha256: 3032f2f55d6eceb10d53217c2a7f43e1eac83603d91e21ce502e8179e63a75f5 + md5: 3f17bc32cb7fcb2b4bf3d8d37f656eb8 + depends: + - __osx >=10.13 + - libcxx >=16 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 2749186 + timestamp: 1718551450314 +- conda: https://conda.anaconda.org/conda-forge/osx-64/astropy-base-7.2.0-py312h391ab28_0.conda + sha256: f3aa65e2913ca569adcaba8e10a4c956dedcd794953ffea80267140944aeeb96 + md5: bd42b0ef0e3f318a5c4ef9ff81644302 + depends: + - __osx >=10.13 + - astropy-iers-data >=0.2025.10.27.0.39.10 + - numpy >=1.23,<3 + - numpy >=1.24 + - packaging >=22.0.0 + - pyerfa >=2.0.1.1 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - pyyaml >=6.0.0 + constrains: + - astropy >=7.0.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/astropy?source=hash-mapping + size: 9405459 + timestamp: 1764120956355 +- conda: https://conda.anaconda.org/conda-forge/osx-64/astropy-healpix-1.1.3-py312h8ab2c85_0.conda + sha256: 3216d6447e9069ed71999a0924bc91981827c147afcf405883e48726f45b0549 + md5: 2d3524f373519d99d6d1ad34a01a0be9 + depends: + - __osx >=10.13 + - astropy-base >=3 + - numpy >=1.23,<3 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/astropy-healpix?source=hash-mapping + size: 111906 + timestamp: 1768880468035 +- conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-auth-0.10.1-ha3f0692_3.conda + sha256: 7cef042368d4e576b4f2cab16c964d989fba81a31b6bba9a70e8bb056d052775 + md5: a86f1fa5a9479cf6a11df72083408626 + depends: + - __osx >=11.0 + - aws-c-http >=0.10.13,<0.10.14.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + - aws-c-io >=0.26.3,<0.26.4.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 120729 + timestamp: 1777489539645 +- conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-auth-0.9.6-hbd79662_1.conda + sha256: 0e57c6ab849ed2dc17c0479779402e4a2febda55a547920ede353fb89da3bfd4 + md5: 6eac869db7e36861b52706a84b62adbb + depends: + - __osx >=11.0 + - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 + - aws-c-http >=0.10.10,<0.10.11.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-io >=0.26.1,<0.26.2.0a0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 119960 + timestamp: 1771494173039 +- conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-cal-0.9.13-hea39f9f_1.conda + sha256: c085b749572ca7c137dfbf8a2a4fd505657f8f7f8a7b374d5f41bf4eb2dd9214 + md5: cbf7be9e03e8b5e38ec60b6dbdf3a649 + depends: + - __osx >=10.13 + - aws-c-common >=0.12.6,<0.12.7.0a0 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 45262 + timestamp: 1764593359925 +- conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-common-0.12.6-h8616949_0.conda + sha256: 66fb2710898bb3e25cb4af52ee88a0559dcde5e56e6bd09b31b98a346a89b2e3 + md5: c7f2d588a6d50d170b343f3ae0b72e62 + depends: + - __osx >=10.13 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 230785 + timestamp: 1763585852531 +- conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-compression-0.3.2-hb9ea233_0.conda + sha256: 599eff2c7b6d2e4e2ed1594e330f5f4f06f0fbe21d20d53beb78e3443344555c + md5: da394e3dc9c78278c8bdbd3a81fdbdb2 + depends: + - __osx >=10.13 + - aws-c-common >=0.12.6,<0.12.7.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 21769 + timestamp: 1767790884673 +- conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-event-stream-0.5.9-h8efd969_2.conda + sha256: 15f2228ecb30aaf96856a2a3f5991e496a8e9b0fd428090c9f1ebb9a349a17be + md5: c17ce609af703addf9aa5627bee9abe9 + depends: + - __osx >=11.0 + - libcxx >=19 + - aws-c-io >=0.26.1,<0.26.2.0a0 + - aws-checksums >=0.2.10,<0.2.11.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 53601 + timestamp: 1771380412957 +- conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-event-stream-0.7.0-ha9bd753_0.conda + sha256: f8127169c450667c802ffe5cfc38eee2291ce253aa2b6bf244bc09d74e4edbac + md5: a7f76898deba16f0b70782ad3c0f841a + depends: + - __osx >=11.0 + - libcxx >=19 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-checksums >=0.2.10,<0.2.11.0a0 + - aws-c-io >=0.26.3,<0.26.4.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 53830 + timestamp: 1774479986246 +- conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-http-0.10.10-h8f73dec_0.conda + sha256: ed5b9375d4cadf5fc2633722185662c3a09e80b2e669ef97785b41521b931d36 + md5: 1e24e3a1577f3308d38b1b840b79a78e + depends: + - __osx >=11.0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + - aws-c-compression >=0.3.2,<0.3.3.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-io >=0.26.1,<0.26.2.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 193259 + timestamp: 1771421371021 +- conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-http-0.10.13-h1037d30_0.conda + sha256: 5dd30efffb930499b30b0c0ad98cae9c62179143d07645e18823e04e50667d90 + md5: 52af2e201214cbd7bc6282f01c535879 + depends: + - __osx >=11.0 + - aws-c-io >=0.26.3,<0.26.4.0a0 + - aws-c-compression >=0.3.2,<0.3.3.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 193468 + timestamp: 1777483091300 +- conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-io-0.26.1-hc95b61d_2.conda + sha256: 2068bd26f7edebde73ddb5e8c621f180b6ec3d1add5689e32610b5947888c116 + md5: e8f6d38042ecf60daa190d2577cd1cee + depends: + - __osx >=11.0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 182851 + timestamp: 1773328980145 +- conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-io-0.26.3-hc95b61d_2.conda + sha256: e3b6b0b68b1f8708ebbc1c4b8486939cdc26cbd1d8e9af51a719e41b9860f1bb + md5: 6056103306b72ddc7f85fe8579764c07 + depends: + - __osx >=11.0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 182725 + timestamp: 1779133075397 +- conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-mqtt-0.14.0-h2b5127a_1.conda + sha256: 3ec986cbc20e2320243bc81752807601d4e203dddb0cdb55c34d88c4c3df4065 + md5: 348c5b73925a44a5f66111d20f245e68 + depends: + - __osx >=11.0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-io >=0.26.1,<0.26.2.0a0 + - aws-c-http >=0.10.10,<0.10.11.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 191622 + timestamp: 1771458106157 +- conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-mqtt-0.15.2-h377fd20_2.conda + sha256: 7f13b421167b55d296a92cf95f7c0793ec54a7da34da4749ea3eee9cb9e44306 + md5: 636c0b11b63f8ee234388624caa971fb + depends: + - __osx >=11.0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-io >=0.26.3,<0.26.4.0a0 + - aws-c-http >=0.10.13,<0.10.14.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 193355 + timestamp: 1777488219057 +- conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-s3-0.11.5-hafc236b_3.conda + sha256: c52910e453a9f95a76b49ffd469568c9b1b42af97b68a5a572e36521a7c8aa3d + md5: a7909e0fd744693b22ae9adba17ac1aa + depends: + - __osx >=11.0 + - aws-c-auth >=0.9.6,<0.9.7.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + - aws-c-io >=0.26.1,<0.26.2.0a0 + - aws-c-http >=0.10.10,<0.10.11.0a0 + - aws-checksums >=0.2.10,<0.2.11.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 134299 + timestamp: 1771586339084 +- conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-s3-0.12.2-hfe16a33_1.conda + sha256: df4040780a6331295563fa9e251266d150920bcf5359dd6d6960f07403480b31 + md5: 84be59d0b0107cc0ba06983a2a05070d + depends: + - __osx >=11.0 + - aws-c-io >=0.26.3,<0.26.4.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + - aws-c-http >=0.10.13,<0.10.14.0a0 + - aws-c-auth >=0.10.1,<0.10.2.0a0 + - aws-checksums >=0.2.10,<0.2.11.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 134870 + timestamp: 1777824857364 +- conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-sdkutils-0.2.4-h901532c_4.conda + sha256: 468629dbf52fee6dcabda1fcb0c0f2f29941b9001dcc75a57ebfbe38d0bde713 + md5: b384fb05730f549a55cdb13c484861eb + depends: + - __osx >=10.13 + - aws-c-common >=0.12.6,<0.12.7.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 55664 + timestamp: 1764610141049 +- conda: https://conda.anaconda.org/conda-forge/osx-64/aws-checksums-0.2.10-h31279ed_0.conda + sha256: 8776d3d51e03ba373a13e4cd4adaf70fd15323c50f1dde85669dc4e379c10dbd + md5: 28a458ade86d135a90951d816760cc5c + depends: + - __osx >=11.0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 95954 + timestamp: 1771063481230 +- conda: https://conda.anaconda.org/conda-forge/osx-64/aws-crt-cpp-0.37.3-h4bfe737_0.conda + sha256: a999e49690418ab1a58a36af0c1546f6b16006535dae4f7716ad25a2924f7c4d + md5: f28fc0586a01af5aa8963e3bb885bfe4 + depends: + - libcxx >=19 + - __osx >=11.0 + - aws-c-io >=0.26.1,<0.26.2.0a0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + - aws-c-s3 >=0.11.5,<0.11.6.0a0 + - aws-c-mqtt >=0.14.0,<0.14.1.0a0 + - aws-c-event-stream >=0.5.9,<0.5.10.0a0 + - aws-c-auth >=0.9.6,<0.9.7.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 + - aws-c-http >=0.10.10,<0.10.11.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 346889 + timestamp: 1771983363260 +- conda: https://conda.anaconda.org/conda-forge/osx-64/aws-crt-cpp-0.38.3-heb35453_1.conda + sha256: 693a235526a4bb2f533d55926640403299846def598f69007e38f8d406bc0eba + md5: 1a605fd4a7d4e70d5a26b9f8068360af + depends: + - libcxx >=19 + - __osx >=11.0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-s3 >=0.12.2,<0.12.3.0a0 + - aws-c-mqtt >=0.15.2,<0.15.3.0a0 + - aws-c-auth >=0.10.1,<0.10.2.0a0 + - aws-c-http >=0.10.13,<0.10.14.0a0 + - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + - aws-c-io >=0.26.3,<0.26.4.0a0 + - aws-c-event-stream >=0.7.0,<0.7.1.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 348359 + timestamp: 1778019141858 +- conda: https://conda.anaconda.org/conda-forge/osx-64/aws-sdk-cpp-1.11.747-h5d703ad_1.conda + sha256: 0ba35d639628f7f08150fd5a525da57fa5f567098fe22ebad0f3f76d63227030 + md5: 5ad302589c4e42f984cde8124ae932fa + depends: + - libcxx >=19 + - __osx >=11.0 + - aws-c-event-stream >=0.5.9,<0.5.10.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - libzlib >=1.3.1,<2.0a0 + - aws-crt-cpp >=0.37.3,<0.37.4.0a0 + - libcurl >=8.18.0,<9.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 3476896 + timestamp: 1772084563334 +- conda: https://conda.anaconda.org/conda-forge/osx-64/aws-sdk-cpp-1.11.747-h9890d28_4.conda + sha256: e167de27b143ce0ded8dd7601e7f25ebe5664c51e5e90c6e972cca0219b937f1 + md5: 7e6e4e6253356bdd3dbe52370c01ea1a + depends: + - __osx >=11.0 + - libcxx >=19 + - aws-c-event-stream >=0.7.0,<0.7.1.0a0 + - aws-crt-cpp >=0.38.3,<0.38.4.0a0 + - libcurl >=8.20.0,<9.0a0 + - libzlib >=1.3.2,<2.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 3477268 + timestamp: 1778156316324 +- conda: https://conda.anaconda.org/conda-forge/osx-64/azure-core-cpp-1.16.2-h87f1c7e_0.conda + sha256: bc2cde0d7204b3574084de1d83d80bceb7eb1550a17a0f0ccedbb312145475d3 + md5: 24997c4c96d1875956abd9ce37f262eb + depends: + - __osx >=10.13 + - libcurl >=8.18.0,<9.0a0 + - libcxx >=19 + - openssl >=3.5.4,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 298273 + timestamp: 1768837905794 +- conda: https://conda.anaconda.org/conda-forge/osx-64/azure-identity-cpp-1.13.3-h1135191_1.conda + sha256: 182769c18c23e2b29bb35f6fca4c233f0125f84418dacb2c36912298dafbe42e + md5: 14d2491d2dfcbb127fa0ff6219704ab5 + depends: + - __osx >=10.13 + - azure-core-cpp >=1.16.2,<1.16.3.0a0 + - libcxx >=19 + - openssl >=3.5.5,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 175167 + timestamp: 1770345309347 +- conda: https://conda.anaconda.org/conda-forge/osx-64/azure-storage-blobs-cpp-12.16.0-hefc3566_2.conda + sha256: 7353b04b2aff8c34610011710be614733df343986f7b15d825dbed27005ef742 + md5: 5bf9d81a733e5864a723bd308a473c6b + depends: + - __osx >=11.0 + - azure-core-cpp >=1.16.2,<1.16.3.0a0 + - azure-storage-common-cpp >=12.13.0,<12.13.1.0a0 + - libcxx >=19 + license: MIT + license_family: MIT + purls: [] + size: 434697 + timestamp: 1778727610755 +- conda: https://conda.anaconda.org/conda-forge/osx-64/azure-storage-common-cpp-12.13.0-h74781cd_0.conda + sha256: 21cf4bc77e20a4a4874452dc5438fdae86f2cccfa2ffa29e920b2be0450e906b + md5: 7d4ec20278fbc5159c0899787a8afea3 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.16.2,<1.16.3.0a0 + - libcxx >=19 + - libxml2 + - libxml2-16 >=2.14.6 + - openssl >=3.5.6,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 133457 + timestamp: 1778662369219 +- conda: https://conda.anaconda.org/conda-forge/osx-64/azure-storage-files-datalake-cpp-12.14.0-h2303994_2.conda + sha256: 040025c0dc8e338dc1038572748ffc2d1bb6f646b2c4a95daae3a4468a74af0f + md5: b3e6633ee0101fb99316de21754ec448 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.16.2,<1.16.3.0a0 + - azure-storage-blobs-cpp >=12.16.0,<12.16.1.0a0 + - azure-storage-common-cpp >=12.13.0,<12.13.1.0a0 + - libcxx >=19 + license: MIT + license_family: MIT + purls: [] + size: 204849 + timestamp: 1778764549811 +- conda: https://conda.anaconda.org/conda-forge/osx-64/backports.zstd-1.5.0-py312h5f4ecc6_0.conda + sha256: f6166347ee8dd7e5c71029c27e89cc5d4a84ccb3f374761363d5bce08ce92227 + md5: c987aba92ae6e528ed495f1ea71d4834 + depends: + - python + - __osx >=11.0 + - python_abi 3.12.* *_cp312 + - zstd >=1.5.7,<1.6.0a0 + license: BSD-3-Clause AND MIT AND EPL-2.0 + purls: + - pkg:pypi/backports-zstd?source=hash-mapping + size: 240621 + timestamp: 1778594129022 +- conda: https://conda.anaconda.org/conda-forge/osx-64/bcrypt-5.0.0-py312h8a6388b_1.conda + sha256: 38923f0ca303c603404f6a6468d53d7222678a4858356c0912879680f914ac29 + md5: 7fbc2ba672662d9fd4dbfebc3c9acd9b + depends: + - python + - __osx >=10.13 + - python_abi 3.12.* *_cp312 + constrains: + - __osx >=10.13 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/bcrypt?source=hash-mapping + size: 278137 + timestamp: 1762497774810 +- conda: https://conda.anaconda.org/conda-forge/osx-64/blosc-1.21.6-hd145fbb_1.conda + sha256: 876bdb1947644b4408f498ac91c61f1f4987d2c57eb47c0aba0d5ee822cd7da9 + md5: 717852102c68a082992ce13a53403f9d + depends: + - __osx >=10.13 + - libcxx >=18 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.1,<1.3.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 46990 + timestamp: 1733513422834 +- conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-1.2.0-hf139dec_1.conda + sha256: c838c71ded28ada251589f6462fc0f7c09132396799eea2701277566a1a863bf + md5: 149d8ee7d6541a02a6117d8814fd9413 + depends: + - __osx >=10.13 + - brotli-bin 1.2.0 h8616949_1 + - libbrotlidec 1.2.0 h8616949_1 + - libbrotlienc 1.2.0 h8616949_1 + license: MIT + license_family: MIT + purls: [] + size: 20194 + timestamp: 1764017661405 +- conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-bin-1.2.0-h8616949_1.conda + sha256: dcb5a2b29244b82af2545efad13dfdf8dddb86f88ce64ff415be9e7a10cc0383 + md5: 34803b20dfec7af32ba675c5ccdbedbf + depends: + - __osx >=10.13 + - libbrotlidec 1.2.0 h8616949_1 + - libbrotlienc 1.2.0 h8616949_1 + license: MIT + license_family: MIT + purls: [] + size: 18589 + timestamp: 1764017635544 +- conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-python-1.2.0-py312h4b46afd_1.conda + sha256: 8854a80360128157e8d05eb57c1c7e7c1cb10977e4c4557a77d29c859d1f104b + md5: 01fdbccc39e0a7698e9556e8036599b7 + depends: + - __osx >=10.13 + - libcxx >=19 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - libbrotlicommon 1.2.0 h8616949_1 + license: MIT + license_family: MIT + purls: + - pkg:pypi/brotli?source=hash-mapping + size: 389534 + timestamp: 1764017976737 +- conda: https://conda.anaconda.org/conda-forge/osx-64/brunsli-0.1-ha00ef93_2.conda + sha256: 8267fa351967ffb2587ea58f93226abe57d68a020758cab56591dc7fa4eb455d + md5: 44c70b2db8d9b7be20a86bd40a2aa9e9 + depends: + - __osx >=10.13 + - libbrotlicommon >=1.2.0,<1.3.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libcxx >=19 + license: MIT + license_family: MIT + purls: [] + size: 147378 + timestamp: 1761759461091 +- conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda + sha256: 9f242f13537ef1ce195f93f0cc162965d6cc79da578568d6d8e50f70dd025c42 + md5: 4173ac3b19ec0a4f400b4f782910368b + depends: + - __osx >=10.13 + license: bzip2-1.0.6 + license_family: BSD + purls: [] + size: 133427 + timestamp: 1771350680709 +- conda: https://conda.anaconda.org/conda-forge/osx-64/c-ares-1.34.6-hb5e19a0_0.conda + sha256: 2f5bc0292d595399df0d168355b4e9820affc8036792d6984bd751fdda2bcaea + md5: fc9a153c57c9f070bebaa7eef30a8f17 + depends: + - __osx >=10.13 + license: MIT + license_family: MIT + purls: [] + size: 186122 + timestamp: 1765215100384 +- conda: https://conda.anaconda.org/conda-forge/osx-64/c-blosc2-3.0.3-h32e32c0_0.conda + sha256: 5162c640cae2be1555fd6582ad9ce14875ba762f925035c98c144ed0a7a7d66b + md5: 3ad9b814c5a67bbae42e883f1dd3cb72 + depends: + - __osx >=11.0 + - libcxx >=19 + - lz4-c >=1.10.0,<1.11.0a0 + - zlib-ng >=2.3.3,<2.4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 310107 + timestamp: 1778844643033 +- conda: https://conda.anaconda.org/conda-forge/osx-64/cffi-2.0.0-py312he90777b_1.conda + sha256: e2888785e50ef99c63c29fb3cfbfb44cdd50b3bb7cd5f8225155e362c391936f + md5: cf70c8244e7ceda7e00b1881ad7697a9 + depends: + - __osx >=10.13 + - libffi >=3.5.2,<3.6.0a0 + - pycparser + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + purls: + - pkg:pypi/cffi?source=hash-mapping + size: 288241 + timestamp: 1761203170357 +- conda: https://conda.anaconda.org/conda-forge/osx-64/cfitsio-4.6.2-ha7f915d_1.conda + sha256: a1a73d388be57366d056225cfb6551dd65f11b31897e55d5ce671deb87fbecdd + md5: 01d54d9f3d6ccfe0d2b900a73bb72431 + depends: + - __osx >=10.13 + - bzip2 >=1.0.8,<2.0a0 + - libcurl >=8.14.1,<9.0a0 + - libzlib >=1.3.1,<2.0a0 + license: LicenseRef-fitsio + purls: [] + size: 697242 + timestamp: 1753286975548 +- conda: https://conda.anaconda.org/conda-forge/osx-64/charls-2.4.3-hcc62823_0.conda + sha256: 8755decbc126ed207da4618b104ee2a8a3336c1511264d5549b22dd21d356667 + md5: 04d7337a89a1def07cf2b2bd96e2b04a + depends: + - __osx >=11.0 + - libcxx >=19 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 135256 + timestamp: 1772713129777 +- conda: https://conda.anaconda.org/conda-forge/osx-64/chealpix-3.31.0-h93aae7a_8.conda + sha256: a35e752285e8953f30b1e9200e71985665af7f2754adea28601a6a6a91061d25 + md5: 65b5d5adcfb7758caa9bd6b0c7b37ebe + depends: + - __osx >=10.13 + - cfitsio >=4.6.2,<4.6.3.0a0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 36724 + timestamp: 1756897326802 +- conda: https://conda.anaconda.org/conda-forge/osx-64/contourpy-1.3.3-py312hb0c38da_4.conda + sha256: 6c03943009b07c6deb3a64afa094b6ca694062b58127a4da6f656a13d508c340 + md5: 625f08687ba33cc9e57865e7bf8e8123 + depends: + - numpy >=1.25 + - python + - __osx >=10.13 + - libcxx >=19 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/contourpy?source=hash-mapping + size: 298198 + timestamp: 1769156053873 +- conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.14.0-py312heb39f77_0.conda + sha256: 2015247c03005286e2bc86c99c5d73dc41a11b25e9d821b08879a228157aaebc + md5: e8cd6d5f41bbb4afa787a223d9b71a7d + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - tomli + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/coverage?source=hash-mapping + size: 387001 + timestamp: 1778445412937 +- conda: https://conda.anaconda.org/conda-forge/osx-64/cryptography-48.0.0-py312h1af399d_0.conda + sha256: 3c5f681f1916a653bc5d2b5304f13d5853a2fecc9be3ca4b88cb0c5f35d77d58 + md5: e4583dba46cd24ff9903630a567e56b2 + depends: + - __osx >=11.0 + - cffi >=2.0 + - openssl >=3.5.6,<4.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - __osx >=10.13 + license: Apache-2.0 AND BSD-3-Clause AND PSF-2.0 AND MIT + license_family: BSD + purls: + - pkg:pypi/cryptography?source=hash-mapping + size: 1855412 + timestamp: 1777966414011 +- conda: https://conda.anaconda.org/conda-forge/osx-64/cytoolz-1.1.0-py312h1a1c95f_2.conda + sha256: 7718e3415123f406e23e88b9a2e03db94984211c07c98a1dc20dc677a624c42e + md5: db9f538ce2d80fce3d42c7b67308f3d2 + depends: + - __osx >=10.13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - toolz >=0.10.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/cytoolz?source=hash-mapping + size: 593863 + timestamp: 1771856019545 +- conda: https://conda.anaconda.org/conda-forge/osx-64/dav1d-1.2.1-h0dc2134_0.conda + sha256: ec71a835866b42e946cd2039a5f7a6458851a21890d315476f5e66790ac11c96 + md5: 9d88733c715300a39f8ca2e936b7808d + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 668439 + timestamp: 1685696184631 +- conda: https://conda.anaconda.org/conda-forge/osx-64/fftw-3.3.11-nompi_h54214ab_100.conda + sha256: c234b8f1be5b630236675a006172edd768ca2685fbf0713ec007f3d373f5ee27 + md5: 5174eb59171c77781c90fbff9eaf5c89 + depends: + - __osx >=11.0 + - libcxx >=19 + - libgfortran + - libgfortran5 >=14.3.0 + - llvm-openmp >=19.1.7 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 1810437 + timestamp: 1776783050946 +- conda: https://conda.anaconda.org/conda-forge/osx-64/fonttools-4.63.0-py312heb39f77_0.conda + sha256: eb76350b1653a7e6e61c0fdc7dde79e32a0ba662c6c9471b2557720fb7db802d + md5: 86857c4f51ca02be0aa72fb98ea80ef9 + depends: + - __osx >=11.0 + - brotli + - munkres + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - unicodedata2 >=15.1.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/fonttools?source=compressed-mapping + size: 2914614 + timestamp: 1778770861388 +- conda: https://conda.anaconda.org/conda-forge/osx-64/freetype-2.14.3-h694c41f_0.conda + sha256: 5ddd46a88a0b6483e3dec52cabb62414504c94ee0e77369a4717f61a656c535a + md5: 6ab1403cc6cb284d56d0464f19251075 + depends: + - libfreetype 2.14.3 h694c41f_0 + - libfreetype6 2.14.3 h58fbd8d_0 + license: GPL-2.0-only OR FTL + purls: [] + size: 174060 + timestamp: 1774298809296 +- conda: https://conda.anaconda.org/conda-forge/osx-64/geos-3.14.1-he483b9e_0.conda + sha256: 4d95fd55a9e649620b4e50ddafff064c4ec52d87e1ed64aa4cad13e643b32baf + md5: d83030a79ce1276edc2332c1730efa17 + depends: + - __osx >=10.13 + - libcxx >=19 + license: LGPL-2.1-only + purls: [] + size: 1631280 + timestamp: 1761593838143 +- conda: https://conda.anaconda.org/conda-forge/osx-64/gflags-2.2.2-hac325c4_1005.conda + sha256: c0bea66f71a6f4baa8d4f0248e17f65033d558d9e882c0af571b38bcca3e4b46 + md5: a26de8814083a6971f14f9c8c3cb36c2 + depends: + - __osx >=10.13 + - libcxx >=17 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 84946 + timestamp: 1726600054963 +- conda: https://conda.anaconda.org/conda-forge/osx-64/giflib-5.2.2-h10d778d_0.conda + sha256: 2c825df829097536314a195ae5cacaa8695209da6b4400135a65d8e23c008ff8 + md5: 03e8c9b4d3da5f3d6eabdd020c2d63ac + license: MIT + license_family: MIT + purls: [] + size: 74516 + timestamp: 1712692686914 +- conda: https://conda.anaconda.org/conda-forge/osx-64/glog-0.7.1-h2790a97_0.conda + sha256: dd56547db8625eb5c91bb0a9fbe8bd6f5c7fbf5b6059d46365e94472c46b24f9 + md5: 06cf91665775b0da395229cd4331b27d + depends: + - __osx >=10.13 + - gflags >=2.2.2,<2.3.0a0 + - libcxx >=16 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 117017 + timestamp: 1718284325443 +- conda: https://conda.anaconda.org/conda-forge/osx-64/google-crc32c-1.8.0-py312hb9001e9_1.conda + sha256: ffd90aaf431a1a1d45f1b852aef16e56c1ce73bd443fae96d83c9d8d2b8a9af2 + md5: 558f8364bc7ccb5be86f035dadab7981 + depends: + - __osx >=10.13 + - libcrc32c >=1.1.2,<1.2.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/google-crc32c?source=hash-mapping + size: 24287 + timestamp: 1768549357803 +- conda: https://conda.anaconda.org/conda-forge/osx-64/gsl-2.7-h93259b0_0.tar.bz2 + sha256: 8550d64004810fa0b5f552d1f21f9fe51483cd30d2d3200d7b0c5e324f7e6995 + md5: b4942b1ee2a52fd67f446074488d774d + depends: + - libblas >=3.8.0,<4.0a0 + - libcblas >=3.8.0,<4.0a0 + license: GPL-3.0-or-later + license_family: GPL + purls: [] + size: 3221488 + timestamp: 1626369980688 +- conda: https://conda.anaconda.org/conda-forge/osx-64/h5py-3.13.0-nompi_py312hea5ca7c_100.conda + sha256: 99ef28fdfa99c75f95edebb5d468250e4395a5bc823682ef3542bda19f6f90c2 + md5: 55bc071459c1de6abc4d02133a540021 + depends: + - __osx >=10.13 + - cached-property + - hdf5 >=1.14.3,<1.14.4.0a0 + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/h5py?source=hash-mapping + size: 1205893 + timestamp: 1739952559909 +- conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-1.14.3-nompi_h1607680_109.conda + sha256: b1882c1d26cd854c980dd64f97ed27f55bbbf413b39ade43fe6cdb2514f8a747 + md5: aa2b87330df24a89585b9d3e4d70c4d4 + depends: + - __osx >=10.13 + - libaec >=1.1.3,<2.0a0 + - libcurl >=8.11.1,<9.0a0 + - libcxx >=18 + - libgfortran >=5 + - libgfortran5 >=13.2.0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 3735253 + timestamp: 1737517248573 +- conda: https://conda.anaconda.org/conda-forge/osx-64/healpy-1.18.1-py312h9f11423_2.conda + sha256: a6863f1ea42fa7be18b2d38b93bbd959d0804e1b6bd85ad7955248ef047023c2 + md5: 5933a04052a74ba60dd5035da391d155 + depends: + - __osx >=10.13 + - astropy-base + - cfitsio >=4.6.2,<4.6.3.0a0 + - libcxx >=19 + - libgfortran + - libgfortran5 >=14.3.0 + - libgfortran5 >=15.1.0 + - libzlib >=1.3.1,<2.0a0 + - matplotlib-base + - numpy >=1.23,<3 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - scipy + license: GPL-2.0-only + license_family: GPL + purls: + - pkg:pypi/healpy?source=hash-mapping + size: 2728977 + timestamp: 1757172669294 +- conda: https://conda.anaconda.org/conda-forge/osx-64/htcondor-24.12.4-py312hb401068_0.conda + sha256: c8b954a80737d8070b4414c74f144ccd57f4edffdc7580221c48e82ffa3707ba + md5: 556582715c2dd1495a8c61dff039f640 + depends: + - htcondor-classads 24.12.4 hf470585_0 + - htcondor-cli 24.12.4 py312hb401068_0 + - htcondor-utils 24.12.4 h1cc2291_0 + - libcondor_utils 24.12.4 h2da22b3_0 + - python >=3.12,<3.13.0a0 + - python-htcondor 24.12.4 py312h564a4e3_0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 23608 + timestamp: 1759418928559 +- conda: https://conda.anaconda.org/conda-forge/osx-64/htcondor-25.10.1-py312hb401068_0.conda + sha256: fb709483dd369888a66299f94aa5d6e2d7bc9a40c63bb24d43404bb8c21ec50e + md5: 1fb9d768e3095fc5cf37eb65c811021a + depends: + - htcondor-classads 25.10.1 h4086b99_0 + - htcondor-cli 25.10.1 py312hb401068_0 + - htcondor-utils 25.10.1 h712aeec_0 + - libcondor_utils 25.10.1 hcd6a731_0 + - python >=3.12,<3.13.0a0 + - python-htcondor 25.10.1 py312haf40f37_0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 22426 + timestamp: 1778794422743 +- conda: https://conda.anaconda.org/conda-forge/osx-64/htcondor-classads-24.12.4-hf470585_0.conda + sha256: cde6a889d223ae885878afc92c0500f2f99acf4764c50ed1d3c5a414db440970 + md5: 8f4d757577e61256e73388de61efda35 + depends: + - __osx >=10.13 + - libcxx >=19 + - pcre2 >=10.46,<10.47.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 2501573 + timestamp: 1759416537017 +- conda: https://conda.anaconda.org/conda-forge/osx-64/htcondor-classads-25.10.1-h4086b99_0.conda + sha256: 5c85547b7711204d3496aded6f00aed09a2962f708986a7afb700e4721456e2c + md5: bf9a09fe64a60f82a3572df78480bbe0 + depends: + - __osx >=11.0 + - libcxx >=19 + - pcre2 >=10.47,<10.48.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 2531070 + timestamp: 1778793321176 +- conda: https://conda.anaconda.org/conda-forge/osx-64/htcondor-cli-24.12.4-py312hb401068_0.conda + sha256: aa45b715147cff677d97a1320a3a4499f39ff67e003427add1296f21feb8bf70 + md5: 568ce7709491a3a154ed5226792e9d05 + depends: + - python >=3.12,<3.13.0a0 + - python-htcondor 24.12.4 py312h564a4e3_0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/htcondor-cli?source=hash-mapping + size: 154841 + timestamp: 1759418800395 +- conda: https://conda.anaconda.org/conda-forge/osx-64/htcondor-cli-25.10.1-py312hb401068_0.conda + sha256: 4a3f250dce2b0b57e53ede0c427155455bd8efb648ea538b96dc479a90cc1da6 + md5: f7c878e30dfd92e3fa8f78b2fc32b6e8 + depends: + - python >=3.12,<3.13.0a0 + - python-htcondor 25.10.1 py312haf40f37_0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/htcondor-cli?source=hash-mapping + size: 174823 + timestamp: 1778794313520 +- conda: https://conda.anaconda.org/conda-forge/osx-64/htcondor-utils-24.12.4-h1cc2291_0.conda + sha256: 297e5046b76c376e8216757f7e08b22eaea0c2b290ed4245e347356dd8bed129 + md5: 36e6c514601dd35fbb7f347e392589a1 + depends: + - __osx >=10.13 + - htcondor-classads 24.12.4 hf470585_0 + - libcondor_utils 24.12.4 h2da22b3_0 + - libcurl >=8.14.1,<9.0a0 + - libcxx >=19 + - openssl >=3.5.4,<4.0a0 + - scitokens-cpp >=1.1.3,<2.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 1412560 + timestamp: 1759416703980 +- conda: https://conda.anaconda.org/conda-forge/osx-64/htcondor-utils-25.10.1-h712aeec_0.conda + sha256: c8eef406e2a721b1154172373f6d205efb06cd591616c15c38d4d4d52cc4c336 + md5: fbc041f268d12cf49e382f52e3efa0e3 + depends: + - __osx >=11.0 + - htcondor-classads 25.10.1 h4086b99_0 + - libcondor_utils 25.10.1 hcd6a731_0 + - libcurl >=8.20.0,<9.0a0 + - libcxx >=19 + - openssl >=3.5.6,<4.0a0 + - scitokens-cpp >=1.4.0,<2.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 1474378 + timestamp: 1778793478879 +- conda: https://conda.anaconda.org/conda-forge/osx-64/htgettoken-2.6-py312hb401068_0.conda + sha256: 2abe186b0c33b212d4256383336d4ccd65106f6461b70d8fde0d1fb8bc1812a0 + md5: d9b96489e1948a1d17a2d254874d4293 + depends: + - jq + - paramiko + - python >=3.12,<3.13.0a0 + - python-gssapi + - python_abi 3.12.* *_cp312 + - urllib3 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/htgettoken?source=hash-mapping + size: 52806 + timestamp: 1768653631587 +- conda: https://conda.anaconda.org/conda-forge/osx-64/icu-75.1-h120a0e1_0.conda + sha256: 2e64307532f482a0929412976c8450c719d558ba20c0962832132fd0d07ba7a7 + md5: d68d48a3060eb5abdc1cdc8e2a3a5966 + depends: + - __osx >=10.13 + license: MIT + license_family: MIT + purls: [] + size: 11761697 + timestamp: 1720853679409 +- conda: https://conda.anaconda.org/conda-forge/osx-64/igwn-ligolw-2.1.1-py312h0b9663a_1.conda + sha256: 65a72e7dd6333fc207e8155e5994fc6a073bce23ff3d18055431d4dc4b092098 + md5: 1d3db82d02712e20c3b18fce291b0573 + depends: + - igwn-segments + - numpy + - python + - python-dateutil + - pyyaml + - tqdm + - __osx >=10.13 + - python_abi 3.12.* *_cp312 + license: GPL-3.0-or-later + license_family: GPL + purls: + - pkg:pypi/igwn-ligolw?source=hash-mapping + size: 2420289 + timestamp: 1771054390399 +- conda: https://conda.anaconda.org/conda-forge/osx-64/igwn-segments-2.1.1-py312h0b9663a_2.conda + sha256: b9c3fc7fad57e4fab0dafd1adc04ddd34e8c731dda42c341d8c6511fac4f34d5 + md5: 9954af9fb6a95b0a462a91170d88424d + depends: + - python + - __osx >=10.13 + - python_abi 3.12.* *_cp312 + license: GPL-3.0-or-later + license_family: GPL + purls: + - pkg:pypi/igwn-segments?source=hash-mapping + size: 93915 + timestamp: 1770798029599 +- conda: https://conda.anaconda.org/conda-forge/osx-64/imagecodecs-2026.5.10-py312hc8b613d_0.conda + sha256: 42b0d40fe1473c23ba814bab04255ed50a6fefa4aad9b4e43b692b4501ea18da + md5: ed38c7c051c9b5beee001d821df74b5b + depends: + - __osx >=11.0 + - blosc >=1.21.6,<2.0a0 + - brunsli >=0.1,<1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - c-blosc2 >=3.0.2,<3.1.0a0 + - charls >=2.4.3,<2.5.0a0 + - giflib >=5.2.2,<5.3.0a0 + - jxrlib >=1.1,<1.2.0a0 + - lcms2 >=2.19.1,<3.0a0 + - lerc >=4.1.0,<5.0a0 + - libaec >=1.1.5,<2.0a0 + - libavif16 >=1.4.1,<2.0a0 + - libbrotlicommon >=1.2.0,<1.3.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libcxx >=19 + - libdeflate >=1.25,<1.26.0a0 + - libjpeg-turbo >=3.1.4.1,<4.0a0 + - libjxl >=0.11,<1.0a0 + - liblzma >=5.8.3,<6.0a0 + - libpng >=1.6.58,<1.7.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - libzopfli >=1.0.3,<1.1.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - numpy >=1.23,<3 + - openjpeg >=2.5.4,<3.0a0 + - openjph >=0.27.2,<0.28.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - snappy >=1.2.2,<1.3.0a0 + - zfp >=1.0.1,<2.0a0 + - zlib-ng >=2.3.3,<2.4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/imagecodecs?source=hash-mapping + size: 1834124 + timestamp: 1778501971550 +- conda: https://conda.anaconda.org/conda-forge/osx-64/jq-1.8.1-h2287256_0.conda + sha256: 971ec2b98b491bc9419bb8d97006dc521e2e06d7466f2da37612796fd38066ff + md5: f76d7d452699d8208be98516ab18df96 + depends: + - oniguruma 6.9.* + - __osx >=10.13 + - oniguruma >=6.9.10,<6.10.0a0 + license: MIT + license_family: MIT + purls: [] + size: 331126 + timestamp: 1751447338102 +- conda: https://conda.anaconda.org/conda-forge/osx-64/jxrlib-1.1-h10d778d_3.conda + sha256: a548a4be14a4c76d6d992a5c1feffcbb08062f5c57abc6e4278d40c2c9a7185b + md5: cfaf81d843a80812fe16a68bdae60562 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 220376 + timestamp: 1703334073774 +- conda: https://conda.anaconda.org/conda-forge/osx-64/kiwisolver-1.5.0-py312hb1dc2e7_0.conda + sha256: 6ab69d441b3400cdf773f67e20f9fae7c37f076d32c31d06843f12d2099e70ce + md5: 4a38b6e74b5a7ea22f1840226e5103a8 + depends: + - python + - libcxx >=19 + - __osx >=11.0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/kiwisolver?source=hash-mapping + size: 69432 + timestamp: 1773067281295 +- conda: https://conda.anaconda.org/conda-forge/osx-64/krb5-1.21.3-h37d8d59_0.conda + sha256: 83b52685a4ce542772f0892a0f05764ac69d57187975579a0835ff255ae3ef9c + md5: d4765c524b1d91567886bde656fb514b + depends: + - __osx >=10.13 + - libcxx >=16 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 1185323 + timestamp: 1719463492984 +- conda: https://conda.anaconda.org/conda-forge/osx-64/krb5-1.22.2-h207b36a_0.conda + sha256: df009385e8262c234c0dae9016540b86dad3d299f0d9366d08e327e8e7731634 + md5: e66e2c52d2fdddcf314ad750fb4ebb4a + depends: + - __osx >=10.13 + - libcxx >=19 + - libedit >=3.1.20250104,<3.2.0a0 + - libedit >=3.1.20250104,<4.0a0 + - openssl >=3.5.5,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 1193620 + timestamp: 1769770267475 +- conda: https://conda.anaconda.org/conda-forge/osx-64/lal-7.6.1-fftw_py312h6f020d4_100.conda + sha256: c581cb8c8d0e9000e06668b1b3bdf2b4d94c2f8a442ccb4cd94e6098a815c891 + md5: d5780addb79aa076c6ca0398d93ef63f + depends: + - __osx >=10.13 + - fftw >=3.3.10,<4.0a0 + - liblal 7.6.1 fftw_ha907fd1_100 + - ligo-segments + - numpy + - python >=3.12,<3.13.0a0 + - python-lal 7.6.1 fftw_py312ha78c7a8_100 + - python-ligo-lw + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 43445 + timestamp: 1734695921372 +- conda: https://conda.anaconda.org/conda-forge/osx-64/lalapps-10.0.2-py312h72dd72b_2.conda + sha256: 88c1fed7afdd1bb95949ee330e99bbea6db6cc9659fa2fa5f63ee506565d9c6a + md5: 48273c706d276e284697d05d06d4c981 + depends: + - __osx >=10.13 + - cfitsio >=4.6.2,<4.6.3.0a0 + - gsl >=2.7,<2.8.0a0 + - h5py + - lal >=7.6.0 + - lalburst >=2.0.0 + - lalframe >=3.0.0 + - lalinference >=4.1.0 + - lalinspiral >=5.0.0 + - lalmetaio >=4.0.0 + - lalpulsar >=7.1.0 + - lalsimulation >=6.1.0 + - libframel >=8.39.2 + - libframel >=8.41.3,<9.0a0 + - liblal >=7.6.1,<8.0a0 + - liblalburst >=2.0.6,<3.0a0 + - liblalframe >=3.0.6,<4.0a0 + - liblalinference >=4.1.8,<5.0a0 + - liblalinspiral >=5.0.2,<6.0a0 + - liblalmetaio >=4.0.5,<5.0a0 + - liblalpulsar >=7.1.0,<8.0a0 + - liblalsimulation >=6.1.0,<7.0a0 + - libmetaio >=8.4.0 + - libmetaio >=8.5.1,<9.0a0 + - ligo-segments + - numpy + - pillow + - python >=3.12,<3.13.0a0 + - python-ligo-lw >=1.7.0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 1328048 + timestamp: 1744229130008 +- conda: https://conda.anaconda.org/conda-forge/osx-64/lalburst-2.0.6-py312h01d7ebd_0.conda + sha256: 6aeccae4161ea2f90f2677b3e19821ae9606ce4dc6b71e3789fe083e85a072a2 + md5: b233c2c02d4da1fc276403c88747d67a + depends: + - __osx >=10.13 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalburst 2.0.6 h8fa4168_0 + - pillow + - python >=3.12,<3.13.0a0 + - python-lalburst 2.0.6 py312h44bc254_0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 50930 + timestamp: 1734709691522 +- conda: https://conda.anaconda.org/conda-forge/osx-64/lalframe-3.0.6-py312h01d7ebd_0.conda + sha256: 1354322fd2b796983cad162b5e8c7e6f63c03f31d2c0c79bf8a6318edec3ab12 + md5: 48396b4e62de09324eefa6cdf06a284a + depends: + - __osx >=10.13 + - liblal >=7.6.0,<8.0a0 + - liblalframe 3.0.6 h6e76fb1_0 + - python >=3.12,<3.13.0a0 + - python-lalframe 3.0.6 py312h025c719_0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 307600 + timestamp: 1734696999191 +- conda: https://conda.anaconda.org/conda-forge/osx-64/lalinference-4.1.8-nompi_py312h34ae946_100.conda + sha256: 042e23acfe6306eae46faea15a2357229e4cb0386cda3c1a54e7e23e5ec643b5 + md5: 518b4cfdc80acc1632cb5130f141609a + depends: + - __osx >=10.13 + - astropy-base >=1.1.1 + - h5py + - icu >=75.1,<76.0a0 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalinference 4.1.8 h506f03e_0 + - ligo-gracedb + - lscsoft-glue >=1.54.1 + - matplotlib-base >=1.2.0 + - python >=3.12,<3.13.0a0 + - python-lal >=7.6.0 + - python-lalinference 4.1.8 py312h025c719_0 + - python-lalsimulation >=6.1.0 + - python-ligo-lw >=1.7.0 + - python_abi 3.12.* *_cp312 + - scipy >=0.9.0 + - six + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 99449 + timestamp: 1734715027565 +- conda: https://conda.anaconda.org/conda-forge/osx-64/lalinference-data-4.1.8-h694c41f_0.conda + sha256: 1a5d133d15942e70b58e3f67762c6ff6e1e53507c2c324ad4e0c43903b00f3f5 + md5: f1c8c3c719bcce0eb4ead143e6f63a12 + constrains: + - liblalinference >=3.0.3 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 32031 + timestamp: 1734712978366 +- conda: https://conda.anaconda.org/conda-forge/osx-64/lalinspiral-5.0.2-py312h01d7ebd_0.conda + sha256: a1595df80ef9bc1eddcdb0783ed5b89797d900c095687a0859ce065e52b47718 + md5: 79fdb5dfc49b54c94a410d53ab367226 + depends: + - __osx >=10.13 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalinspiral 5.0.2 h8fa4168_0 + - ligo-segments + - python >=3.12,<3.13.0a0 + - python-lalinspiral 5.0.2 py312h025c719_0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 22667 + timestamp: 1734709741031 +- conda: https://conda.anaconda.org/conda-forge/osx-64/lalmetaio-4.0.5-py312h01d7ebd_2.conda + sha256: 6812c1aacbad482d6495d60002fc2560b4e7f272b000255e5fe5866c0434a41f + md5: bcb9d5cdac51a9ea8495bae7e8844e4c + depends: + - __osx >=10.13 + - liblal >=7.6.0 + - liblal >=7.6.0,<8.0a0 + - liblalmetaio 4.0.5 h6e16a3a_2 + - python >=3.12,<3.13.0a0 + - python-lalmetaio 4.0.5 py312h025c719_2 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 18084 + timestamp: 1734275589330 +- conda: https://conda.anaconda.org/conda-forge/osx-64/lalpulsar-7.1.0-py312h5a434c1_1.conda + sha256: 07f71f7911d71ce1e3eef03c6f72cf322a1c4fe8148887a2672a10ded0344248 + md5: 6dd29b71cc1cb87389f60bedc7dd7062 + depends: + - __osx >=10.13 + - astropy-base + - cfitsio >=4.6.2,<4.6.3.0a0 + - fftw >=3.3.10,<4.0a0 + - gsl >=2.7,<2.8.0a0 + - jplephem + - lalinference >=4.1.0 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalframe >=3.0.0 + - liblalframe >=3.0.6,<4.0a0 + - liblalinference >=4.1.0 + - liblalinference >=4.1.8,<5.0a0 + - liblalpulsar 7.1.0 h0f29fec_1 + - liblalsimulation >=6.1.0 + - liblalsimulation >=6.1.0,<7.0a0 + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python-lal >=7.6.0 + - python-lalframe >=3.0.0 + - python-lalinference >=4.1.0 + - python-lalpulsar 7.1.0 py312h025c719_1 + - python-lalsimulation >=6.1.0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 660696 + timestamp: 1744217222639 +- conda: https://conda.anaconda.org/conda-forge/osx-64/lalpulsar-data-7.1.0-h694c41f_1.conda + sha256: cfb2609026627ee7a2d313a0370ff9df0e1dab2466598a675c3950fe3cbc01f6 + md5: c2fc122252d1a123f3e0bf0c4389118c + constrains: + - liblalpulsar >=4.0.0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 90029332 + timestamp: 1744215574224 +- conda: https://conda.anaconda.org/conda-forge/osx-64/lalsimulation-6.1.0-py312haa07450_0.conda + sha256: 1f0b4401850b20f2cf0d13f1171448fb2ce95e31c6bbae3aeafc808579e1b66c + md5: 60f80bc7dc35ef3254fd0152bb5e53fa + depends: + - __osx >=10.13 + - gsl >=2.7,<2.8.0a0 + - liblal >=7.6.0 + - liblal >=7.6.0,<8.0a0 + - liblalsimulation 6.1.0 py312h1b08b07_0 + - mpmath >=1.0.0 + - python >=3.12,<3.13.0a0 + - python-lalsimulation 6.1.0 py312h1b08b07_0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 74079 + timestamp: 1734696422300 +- conda: https://conda.anaconda.org/conda-forge/osx-64/lalsimulation-data-6.1.0-h694c41f_0.conda + sha256: 43a228de459c64486d966ec6713b0c5cdc38796319205c3b9971e50b60cd2938 + md5: 323419c68a641fe57639c4083c5aec9c + constrains: + - liblalsimulation >=3.1.2 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 3634912 + timestamp: 1734695995163 +- conda: https://conda.anaconda.org/conda-forge/osx-64/lcms2-2.19.1-h5ea7634_0.conda + sha256: af14a2021a151b3bd98e5b40db0762b35ff54e57fa8c1968cca728cef8d13a8a + md5: 3ae3b6db0dcada986f1e3b608e1cb0fc + depends: + - __osx >=11.0 + - libjpeg-turbo >=3.1.4.1,<4.0a0 + - libtiff >=4.7.1,<4.8.0a0 + license: MIT + license_family: MIT + purls: [] + size: 229186 + timestamp: 1778079697832 +- conda: https://conda.anaconda.org/conda-forge/osx-64/ldas-tools-al-2.7.0-hb7f9d93_3.conda + sha256: c7482930b65fc8f9028d4662c907910278c2834e01e4e0eb1fb1066d9c180521 + md5: 6b432374926255bb481fe50e9e4b9bc0 + depends: + - __osx >=10.13 + - libboost >=1.88.0,<1.89.0a0 + - libcxx >=19 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 279328 + timestamp: 1764932851268 +- conda: https://conda.anaconda.org/conda-forge/osx-64/ldas-tools-framecpp-2.9.3-hf716561_4.conda + sha256: 83a8e2eb9c637c5ee56d2fa1ad2b3df4392fd124c501ec7a41f687510b0d6f8d + md5: 56098c7f0b7d234d0307ab71c02bd6eb + depends: + - __osx >=10.13 + - ldas-tools-al >=2.6.7 + - ldas-tools-al >=2.7.0,<2.8.0a0 + - libboost >=1.88.0,<1.89.0a0 + - libcxx >=19 + - libzlib >=1.3.1,<2.0a0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 1790416 + timestamp: 1756907351606 +- conda: https://conda.anaconda.org/conda-forge/osx-64/lerc-4.1.0-h35c7297_0.conda + sha256: f918716c71c8bebbc0c40e1050878aa512fea92c1d17c363ca35650bc60f6c35 + md5: d2fe7e177d1c97c985140bd54e2a5e33 + depends: + - __osx >=11.0 + - libcxx >=19 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 215089 + timestamp: 1773114468701 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20260107.1-cxx17_h7ed6875_0.conda + sha256: 2b4ff36082ddfbacc47ac6e11d4dd9f3403cd109ce8d7f0fbee0cdd47cdef013 + md5: 317f40d7bd7bf6d54b56d4a5b5f5085d + depends: + - __osx >=10.13 + - libcxx >=19 + constrains: + - libabseil-static =20260107.1=cxx17* + - abseil-cpp =20260107.1 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 1217836 + timestamp: 1770863510112 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libaec-1.1.5-he7c3a48_0.conda + sha256: b42ac9c684c730cb97cb3931a0a97aaf791da38bace4f6944eca10de609e5946 + md5: 975f98248cde8d54884c6d1eb5184e13 + depends: + - __osx >=10.13 + - libcxx >=19 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 30555 + timestamp: 1769222189944 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-23.0.1-h47227bc_4_cpu.conda + build_number: 4 + sha256: 17d2d7bfbcb787870055d97ef7df68c4ac67ad231619847fbf853f6aceae4fa3 + md5: 171dd70985713732069e1b5744b7ae8d + depends: + - __osx >=11.0 + - aws-crt-cpp >=0.37.3,<0.37.4.0a0 + - aws-sdk-cpp >=1.11.747,<1.11.748.0a0 + - azure-core-cpp >=1.16.2,<1.16.3.0a0 + - azure-identity-cpp >=1.13.3,<1.13.4.0a0 + - azure-storage-blobs-cpp >=12.16.0,<12.16.1.0a0 + - azure-storage-files-datalake-cpp >=12.14.0,<12.14.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libcxx >=21 + - libgoogle-cloud >=2.39.0,<2.40.0a0 + - libgoogle-cloud-storage >=2.39.0,<2.40.0a0 + - libopentelemetry-cpp >=1.21.0,<1.22.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.3.0,<2.3.1.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: [] + size: 4369844 + timestamp: 1773271515389 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-24.0.0-h7252e1b_2_cpu.conda + build_number: 2 + sha256: 735c4a9ad7c11c69a70bac223822f9b38a6de31942fe7b3abeb1b87d49b335cb + md5: 668d559c020eb9ad055265618e7ce615 + depends: + - __osx >=11.0 + - aws-crt-cpp >=0.38.3,<0.38.4.0a0 + - aws-sdk-cpp >=1.11.747,<1.11.748.0a0 + - azure-core-cpp >=1.16.2,<1.16.3.0a0 + - azure-identity-cpp >=1.13.3,<1.13.4.0a0 + - azure-storage-blobs-cpp >=12.16.0,<12.16.1.0a0 + - azure-storage-files-datalake-cpp >=12.14.0,<12.14.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libcxx >=21 + - libgoogle-cloud >=3.5.0,<3.6.0a0 + - libgoogle-cloud-storage >=3.5.0,<3.6.0a0 + - libopentelemetry-cpp >=1.26.0,<1.27.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libzlib >=1.3.2,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.3.0,<2.3.1.0a0 + - snappy >=1.2.2,<1.3.0a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - apache-arrow-proc =*=cpu + - parquet-cpp <0.0a0 + - arrow-cpp <0.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 4397109 + timestamp: 1779478135998 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-acero-23.0.1-hc9ab1f6_4_cpu.conda + build_number: 4 + sha256: d6027ad1543433274a6bc8eade6e019343e99473af889ba19eb77df4becd6311 + md5: 5d1ea7aa1625e1f18e17feffbd8960d1 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libarrow 23.0.1 h47227bc_4_cpu + - libarrow-compute 23.0.1 h3b2c5b4_4_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.21.0,<1.22.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 562451 + timestamp: 1773272842993 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-acero-24.0.0-h66151e4_2_cpu.conda + build_number: 2 + sha256: c93c7fa0ddea32dfe73b51115da22fe2a9b22ca51a9e76b0d60c9b5b8467b637 + md5: f3ac93940da8fa0ec79d38b8a353aa11 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libarrow 24.0.0 h7252e1b_2_cpu + - libarrow-compute 24.0.0 h5d4fa73_2_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.26.0,<1.27.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 544508 + timestamp: 1779478662341 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-compute-23.0.1-h3b2c5b4_4_cpu.conda + build_number: 4 + sha256: 90215a74522d92901a5a73541a692794681a2d4868f8a542e5555f8b12f52298 + md5: 101ea394be8485114f828c22898fc073 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libarrow 23.0.1 h47227bc_4_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.21.0,<1.22.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libre2-11 >=2025.11.5 + - libutf8proc >=2.11.3,<2.12.0a0 + - re2 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 2402759 + timestamp: 1773272015660 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-compute-24.0.0-h5d4fa73_2_cpu.conda + build_number: 2 + sha256: 6b3f3cf15a92abd705091b886482938f28d3d2b570c91bbb03c2925ab773c893 + md5: a24dde555e47dd8d475f8d2ffa201595 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libarrow 24.0.0 h7252e1b_2_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.26.0,<1.27.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libre2-11 >=2025.11.5 + - libutf8proc >=2.11.3,<2.12.0a0 + - re2 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 2386077 + timestamp: 1779478316802 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-dataset-23.0.1-hc9ab1f6_4_cpu.conda + build_number: 4 + sha256: 418cbd0ab79ad17315b136b3550f6d06db7ab48197738860972625bd032fdb9e + md5: 7cd0e665893c03c07df98226b1301d0f + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libarrow 23.0.1 h47227bc_4_cpu + - libarrow-acero 23.0.1 hc9ab1f6_4_cpu + - libarrow-compute 23.0.1 h3b2c5b4_4_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.21.0,<1.22.0a0 + - libparquet 23.0.1 hb3ef814_4_cpu + - libprotobuf >=6.33.5,<6.33.6.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 550650 + timestamp: 1773273387721 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-dataset-24.0.0-h66151e4_2_cpu.conda + build_number: 2 + sha256: a1dfd554f33ba6154c432e8fe388ec3afdbc1e314e386d4f22d5494f391a1916 + md5: 1b70e5a6afc69aaf08cad78594405c5c + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libarrow 24.0.0 h7252e1b_2_cpu + - libarrow-acero 24.0.0 h66151e4_2_cpu + - libarrow-compute 24.0.0 h5d4fa73_2_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.26.0,<1.27.0a0 + - libparquet 24.0.0 h527dc83_2_cpu + - libprotobuf >=6.33.5,<6.33.6.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 534867 + timestamp: 1779478899386 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-substrait-23.0.1-h613493e_4_cpu.conda + build_number: 4 + sha256: 2f6ebdc92d72997306783c5e45faeb067da28c429e7e7af51741e0718a9dba8a + md5: 891da82209b59a87e3d09b83f7571afa + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libarrow 23.0.1 h47227bc_4_cpu + - libarrow-acero 23.0.1 hc9ab1f6_4_cpu + - libarrow-dataset 23.0.1 hc9ab1f6_4_cpu + - libcxx >=21 + - libprotobuf >=6.33.5,<6.33.6.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 466301 + timestamp: 1773273571522 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libarrow-substrait-24.0.0-h613493e_2_cpu.conda + build_number: 2 + sha256: f9bcd716ca8377e83d08699832bada98e33dbb1bb2b1009cdc8ac5cb4479bfe1 + md5: 73a69b3ef4df89bff6cf072d0c9adfaa + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libarrow 24.0.0 h7252e1b_2_cpu + - libarrow-acero 24.0.0 h66151e4_2_cpu + - libarrow-dataset 24.0.0 h66151e4_2_cpu + - libcxx >=21 + - libprotobuf >=6.33.5,<6.33.6.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 449689 + timestamp: 1779478977953 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libavif16-1.4.1-h9d3eb37_0.conda + sha256: b05246b6940fab957585bbc45bca440c0ad4e98ba3ead0ddb28b95ac7583337c + md5: ceedf05905ff9d9bc784ed91a5705f01 + depends: + - __osx >=11.0 + - aom >=3.9.1,<3.10.0a0 + - dav1d >=1.2.1,<1.2.2.0a0 + - rav1e >=0.8.1,<0.9.0a0 + - svt-av1 >=4.0.1,<4.0.2.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 132870 + timestamp: 1774043184519 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-7_he492b99_openblas.conda + build_number: 7 + sha256: b2fe36d35c7b60e5f63cb0c865f4b3e40839120c47fd0cd56fd633765b25c148 + md5: 9033f3879f6a191d759d7fde5914555f + depends: + - libopenblas >=0.3.33,<0.3.34.0a0 + - libopenblas >=0.3.33,<1.0a0 + constrains: + - mkl <2027 + - liblapacke 3.11.0 7*_openblas + - libcblas 3.11.0 7*_openblas + - liblapack 3.11.0 7*_openblas + - blas 2.307 openblas + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 18868 + timestamp: 1778491111970 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libboost-1.88.0-hf9ddd82_6.conda + sha256: 5935c37509140738f979f007de739304734ec223064bc31521c6e0ca560405e5 + md5: c4b1fee2a2d31b87c7e95c9202e68b5c + depends: + - __osx >=10.13 + - bzip2 >=1.0.8,<2.0a0 + - icu >=75.1,<76.0a0 + - libcxx >=19 + - liblzma >=5.8.1,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - boost-cpp <0.0a0 + license: BSL-1.0 + purls: [] + size: 2103604 + timestamp: 1763018953490 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libboost-python-1.88.0-py312h83679cb_6.conda + sha256: 6e8843ace47265a7b2e0769cc34d55c98421d7c14619900d14614ed9aa9eaf9f + md5: 68eafd7bf455a5b3628bb715ab95bfdd + depends: + - __osx >=10.13 + - libboost 1.88.0 hf9ddd82_6 + - libcxx >=19 + - numpy >=1.23,<3 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - py-boost <0.0a0 + - boost <0.0a0 + license: BSL-1.0 + purls: [] + size: 108301 + timestamp: 1763019253722 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlicommon-1.2.0-h8616949_1.conda + sha256: 4c19b211b3095f541426d5a9abac63e96a5045e509b3d11d4f9482de53efe43b + md5: f157c098841474579569c85a60ece586 + depends: + - __osx >=10.13 + license: MIT + license_family: MIT + purls: [] + size: 78854 + timestamp: 1764017554982 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlidec-1.2.0-h8616949_1.conda + sha256: 729158be90ae655a4e0427fe4079767734af1f9b69ff58cf94ca6e8d4b3eb4b7 + md5: 63186ac7a8a24b3528b4b14f21c03f54 + depends: + - __osx >=10.13 + - libbrotlicommon 1.2.0 h8616949_1 + license: MIT + license_family: MIT + purls: [] + size: 30835 + timestamp: 1764017584474 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlienc-1.2.0-h8616949_1.conda + sha256: 8ece7b41b6548d6601ac2c2cd605cf2261268fc4443227cc284477ed23fbd401 + md5: 12a58fd3fc285ce20cf20edf21a0ff8f + depends: + - __osx >=10.13 + - libbrotlicommon 1.2.0 h8616949_1 + license: MIT + license_family: MIT + purls: [] + size: 310355 + timestamp: 1764017609985 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-7_h9b27e0a_openblas.conda + build_number: 7 + sha256: 91b5abb146f7de3f95fda287c6a314a8ca3ceb2ae597a4bf5a180b6e0790b180 + md5: ebd8b6277cef635b7ae80400eda886e5 + depends: + - libblas 3.11.0 7_he492b99_openblas + constrains: + - liblapacke 3.11.0 7*_openblas + - liblapack 3.11.0 7*_openblas + - blas 2.307 openblas + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 18868 + timestamp: 1778491135869 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libcondor_utils-24.12.4-h2da22b3_0.conda + sha256: aeedb0cda083e180500dbb9ed6c51f5f530633a46be286f2f0b45c153d2e3ca5 + md5: 3ed50b6e4b935933ddc3b873a0f71e27 + depends: + - __osx >=10.13 + - htcondor-classads 24.12.4 hf470585_0 + - krb5 >=1.21.3,<1.22.0a0 + - libcxx >=19 + - llvm-openmp >=19.1.7 + - openssl >=3.5.4,<4.0a0 + - pcre2 >=10.46,<10.47.0a0 + - scitokens-cpp >=0.5.0 + - scitokens-cpp >=1.1.3,<2.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 1894267 + timestamp: 1759416602722 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libcondor_utils-25.10.1-hcd6a731_0.conda + sha256: 700e5a29cfdee12334fb33373b6f920e8f0ff75384431dd8951b88370d183366 + md5: 8c877ddf6d1c8d294afb90c41472aacf + depends: + - __osx >=11.0 + - htcondor-classads 25.10.1 h4086b99_0 + - krb5 >=1.22.2,<1.23.0a0 + - libcxx >=19 + - llvm-openmp >=19.1.7 + - openssl >=3.5.6,<4.0a0 + - pcre2 >=10.47,<10.48.0a0 + - scitokens-cpp >=0.5.0 + - scitokens-cpp >=1.4.0,<2.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 1939516 + timestamp: 1778793378362 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libcrc32c-1.1.2-he49afe7_0.tar.bz2 + sha256: 3043869ac1ee84554f177695e92f2f3c2c507b260edad38a0bf3981fce1632ff + md5: 23d6d5a69918a438355d7cbc4c3d54c9 + depends: + - libcxx >=11.1.0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 20128 + timestamp: 1633683906221 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.18.0-h9348e2b_0.conda + sha256: 1a0af3b7929af3c5893ebf50161978f54ae0256abb9532d4efba2735a0688325 + md5: de1910529f64ba4a9ac9005e0be78601 + depends: + - __osx >=10.13 + - krb5 >=1.21.3,<1.22.0a0 + - libnghttp2 >=1.67.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.4,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: curl + license_family: MIT + purls: [] + size: 419089 + timestamp: 1767822218800 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.20.0-h8f0b9e4_0.conda + sha256: 5d3d8a82ca43347e96f1d79048921f3a7c25e32514bc7feb53ed2a040dcca54d + md5: 4a0085ccf90dc514f0fc0909a874045e + depends: + - __osx >=11.0 + - krb5 >=1.22.2,<1.23.0a0 + - libnghttp2 >=1.68.1,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.6,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: curl + license_family: MIT + purls: [] + size: 419676 + timestamp: 1777462238769 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.6-h19cb2f5_0.conda + sha256: 6d60efb63fe4d0299526fcb26e06de1933de55c36fc2ae5a1478f1aa734604bb + md5: fa1bbb55bfda7a8a022d508fb03f1625 + depends: + - __osx >=11.0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 565211 + timestamp: 1779253305906 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libdeflate-1.25-h517ebb2_0.conda + sha256: 025f8b1e85dd8254e0ca65f011919fb1753070eb507f03bca317871a884d24de + md5: 31aa65919a729dc48180893f62c25221 + depends: + - __osx >=10.13 + license: MIT + license_family: MIT + purls: [] + size: 70840 + timestamp: 1761980008502 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libedit-3.1.20250104-pl5321ha958ccf_0.conda + sha256: 6cc49785940a99e6a6b8c6edbb15f44c2dd6c789d9c283e5ee7bdfedd50b4cd6 + md5: 1f4ed31220402fcddc083b4bff406868 + depends: + - ncurses + - __osx >=10.13 + - ncurses >=6.5,<7.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 115563 + timestamp: 1738479554273 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libev-4.33-h10d778d_2.conda + sha256: 0d238488564a7992942aa165ff994eca540f687753b4f0998b29b4e4d030ff43 + md5: 899db79329439820b7e8f8de41bca902 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 106663 + timestamp: 1702146352558 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libevent-2.1.12-ha90c15b_1.conda + sha256: e0bd9af2a29f8dd74309c0ae4f17a7c2b8c4b89f875ff1d6540c941eefbd07fb + md5: e38e467e577bd193a7d5de7c2c540b04 + depends: + - openssl >=3.1.1,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 372661 + timestamp: 1685726378869 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.8.1-hcc62823_0.conda + sha256: 460afe7ba0882e6d2fcc0ad1568dce27025110ec09c2b9ce9e3b49d61e52ce6b + md5: f95dc08366f2a452005062b5bcceac51 + depends: + - __osx >=11.0 + constrains: + - expat 2.8.1.* + license: MIT + license_family: MIT + purls: [] + size: 75654 + timestamp: 1779279058576 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda + sha256: 951958d1792238006fdc6fce7f71f1b559534743b26cc1333497d46e5903a2d6 + md5: 66a0dc7464927d0853b590b6f53ba3ea + depends: + - __osx >=10.13 + license: MIT + license_family: MIT + purls: [] + size: 53583 + timestamp: 1769456300951 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libframel-8.48.5-hbd657fc_0.conda + sha256: 056cb4aea9a3670750a274d542fc7b21beab5b9a157134d15ad0ccb952b84e57 + md5: dcd3464ff760e84dbda4321a45690e9b + depends: + - __osx >=10.13 + license: LGPL-2.1-or-later + license_family: LGPL + purls: [] + size: 214238 + timestamp: 1767613641146 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libfreetype-2.14.3-h694c41f_0.conda + sha256: b5daa4cee3beb98a0317e81a20aa507b9f897a9e21b11fe0b2e32852e372f746 + md5: 63b822fcf984c891f0afab2eedfcfaf4 + depends: + - libfreetype6 >=2.14.3 + license: GPL-2.0-only OR FTL + purls: [] + size: 8088 + timestamp: 1774298785964 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libfreetype6-2.14.3-h58fbd8d_0.conda + sha256: 9d34b5b2be6ebdd3bcd9e21d6598d493afce4d3fcd2d419f3356022cb4d746fd + md5: 27515b8ab8bf4abd8d3d90cf11212411 + depends: + - __osx >=11.0 + - libpng >=1.6.55,<1.7.0a0 + - libzlib >=1.3.2,<2.0a0 + constrains: + - freetype >=2.14.3 + license: GPL-2.0-only OR FTL + purls: [] + size: 364828 + timestamp: 1774298783922 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_19.conda + sha256: 17a5dcd818f89173db51d7d1acd77615cb77db7b4c2b5f571d4dafe559430ab5 + md5: 4bf33d5ca73f4b89d3495285a42414a4 + depends: + - _openmp_mutex + constrains: + - libgomp 15.2.0 19 + - libgcc-ng ==15.2.0=*_19 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 424164 + timestamp: 1778271183296 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_19.conda + sha256: 519045363b87b870be779d38f0bfd325d4b787acdaa0a2136a92c1081eff5112 + md5: d362f41203d0a1d2d4940446f95374c9 + depends: + - libgfortran5 15.2.0 hd16e46c_19 + constrains: + - libgfortran-ng ==15.2.0=*_19 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 139925 + timestamp: 1778271458366 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_19.conda + sha256: c7f5f6e80357d6d5bc69588c16144205b0c79cf32cd090ccb5afef9d557632af + md5: 1cddb3f7e54f5871297afc0fafa61c2c + depends: + - libgcc >=15.2.0 + constrains: + - libgfortran 15.2.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 1063687 + timestamp: 1778271196574 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libgoogle-cloud-2.39.0-h11ac9da_1.conda + sha256: 1a10ca58677c4b5fb519c811bae095793c5d7324a0dd5bd093342c13a3f4aede + md5: 310fe75e741f8c6b3d1eadbd172de276 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.0,<20260108.0a0 + - libcurl >=8.18.0,<9.0a0 + - libcxx >=19 + - libgrpc >=1.78.0,<1.79.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - openssl >=3.5.5,<4.0a0 + constrains: + - libgoogle-cloud 2.39.0 *_1 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 905886 + timestamp: 1770461298001 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libgoogle-cloud-3.5.0-h10ed7cb_0.conda + sha256: dc19780b67454772a25369fa9e56d70313b29a08030df7a5b39970a533d53a52 + md5: 79317f4baa4dc5ab8c81d08948c6b14a + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libcurl >=8.20.0,<9.0a0 + - libcxx >=19 + - libgrpc >=1.78.1,<1.79.0a0 + - libopentelemetry-cpp >=1.26.0,<1.27.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - openssl >=3.5.6,<4.0a0 + constrains: + - libgoogle-cloud 3.5.0 *_0 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 1866947 + timestamp: 1779227772698 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libgoogle-cloud-storage-2.39.0-hea209c6_1.conda + sha256: 53774cf2a62ae2d8d4713c63032523de4b19c05374fafed2d7f1e5b0ec292756 + md5: f0cd372ebbff46dff0bb67e5630a772c + depends: + - __osx >=11.0 + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libcxx >=19 + - libgoogle-cloud 2.39.0 h11ac9da_1 + - libzlib >=1.3.1,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + purls: [] + size: 542117 + timestamp: 1770461812445 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libgoogle-cloud-storage-3.5.0-hea209c6_0.conda + sha256: 4dc8cb3f96b1c0436d9f4bae58dd28fdfc7c08d259fe88a2266f1917152c82b2 + md5: 0b013f8557cc868bf61053a8bdb75aef + depends: + - __osx >=11.0 + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libcxx >=19 + - libgoogle-cloud 3.5.0 h10ed7cb_0 + - libzlib >=1.3.2,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + purls: [] + size: 542231 + timestamp: 1779228289061 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libgrpc-1.78.1-h147dede_0.conda + sha256: ecf98c41dbde09fb3bf6878d7099613c10e256223ec7ccdb5eb401948eadc558 + md5: 69524227096cee1a8af2f4693cf6afa2 + depends: + - __osx >=11.0 + - c-ares >=1.34.6,<2.0a0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libcxx >=19 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libre2-11 >=2025.11.5 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.5,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.78.1 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 5153859 + timestamp: 1774015913341 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libhwy-1.4.0-hca42a69_0.conda + sha256: fb82a974f5bc029963665a77a0d669cacecfd067e1f3b32fe427d806ec21d52b + md5: b9e41e8946bb04aca90e181f29c5cf82 + depends: + - __osx >=11.0 + - libcxx >=19 + license: Apache-2.0 OR BSD-3-Clause + purls: [] + size: 1000219 + timestamp: 1776990421693 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libiconv-1.18-h57a12c2_2.conda + sha256: a1c8cecdf9966921e13f0ae921309a1f415dfbd2b791f2117cf7e8f5e61a48b6 + md5: 210a85a1119f97ea7887188d176db135 + depends: + - __osx >=10.13 + license: LGPL-2.1-only + purls: [] + size: 737846 + timestamp: 1754908900138 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libjpeg-turbo-3.1.4.1-ha1e9b39_0.conda + sha256: 6b809d8acb6b97bbb1a858eb4ba7b7163c67257b6c3f199dd9d1e0751f4c5b18 + md5: 57cc1464d457d01ac78f5860b9ca1714 + depends: + - __osx >=11.0 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + purls: [] + size: 587997 + timestamp: 1775963139212 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libjxl-0.11.2-h473410d_1.conda + sha256: 5c59a02fcb345c49ef8bdd5e1889de8aa918bedd95917f9805d20659cec65da1 + md5: 596810d804d0b2f2c54bfdd635025e92 + depends: + - __osx >=11.0 + - libcxx >=19 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libhwy >=1.4.0,<1.5.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 1692611 + timestamp: 1777065242546 +- conda: https://conda.anaconda.org/conda-forge/osx-64/liblal-7.6.1-fftw_ha907fd1_100.conda + sha256: 45f32eb2ec6ad863972249aeb0f8cbd4276318f4cb1d50c3498dc89808e6e379 + md5: 321d5e419a1e16e1453a3cdbbef63854 + depends: + - __osx >=10.13 + - fftw >=3.3.10,<4.0a0 + - gsl >=2.7,<2.8.0a0 + - hdf5 >=1.14.3,<1.14.4.0a0 + - libzlib >=1.3.1,<2.0a0 + constrains: + - lal >=7.1.1 + - python-lal >=7.1.1 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 818243 + timestamp: 1734695134733 +- conda: https://conda.anaconda.org/conda-forge/osx-64/liblalburst-2.0.6-h8fa4168_0.conda + sha256: 0157ac128c0cdfe6df1655f9fe64d397058f6d30798af0bebf8bfcb2e1e3f8ee + md5: 43390f7c12cc9d0074224c2a1feba977 + depends: + - __osx >=10.13 + - gsl >=2.7,<2.8.0a0 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalmetaio >=4.0.0 + - liblalmetaio >=4.0.5,<5.0a0 + - liblalsimulation >=6.1.0 + - liblalsimulation >=6.1.0,<7.0a0 + constrains: + - lalburst >=1.5.7 + - python-lalburst >=1.5.7 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 38141 + timestamp: 1734708907009 +- conda: https://conda.anaconda.org/conda-forge/osx-64/liblalframe-3.0.6-h6e76fb1_0.conda + sha256: 807f449b18b91d08d6244617d5c8a4c059a9d1e223504964d366a42fbc53dddd + md5: dc8da41b1a46a2128aa25c59699e2869 + depends: + - __osx >=10.13 + - ldas-tools-framecpp >=2.9.3,<2.10.0a0 + - libframel >=8.41.3,<9.0a0 + - liblal >=7.6.0,<8.0a0 + constrains: + - python-lalframe >=1.5.3 + - lalframe >=1.5.3 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 100322 + timestamp: 1734696330053 +- conda: https://conda.anaconda.org/conda-forge/osx-64/liblalinference-4.1.8-h506f03e_0.conda + sha256: a7db3818f0af0f2a428524c531d152387584cd51f9a82f974413564c14339efa + md5: e6f0300bd87dc118d08f2f474e543dec + depends: + - __osx >=10.13 + - gsl >=2.7,<2.8.0a0 + - lalinference-data 4.1.8 h694c41f_0 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalburst >=2.0.0 + - liblalburst >=2.0.6,<3.0a0 + - liblalframe >=3.0.0 + - liblalframe >=3.0.6,<4.0a0 + - liblalinspiral >=5.0.0 + - liblalinspiral >=5.0.2,<6.0a0 + - liblalmetaio >=4.0.0 + - liblalmetaio >=4.0.5,<5.0a0 + - liblalsimulation >=6.1.0 + - liblalsimulation >=6.1.0,<7.0a0 + - llvm-openmp >=18.1.8 + - llvm-openmp >=19.1.6 + constrains: + - lalinference >=2.0.6 + - python-lalinference >=2.0.6 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 305350 + timestamp: 1734713104840 +- conda: https://conda.anaconda.org/conda-forge/osx-64/liblalinspiral-5.0.2-h8fa4168_0.conda + sha256: 46a5562fd6cc09bac3f6b423255a2dda5cd5d065645ed09e595459daccd6aae5 + md5: 286223906fc39b9222bda4190f940f7d + depends: + - __osx >=10.13 + - gsl >=2.7,<2.8.0a0 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalburst >=2.0.0 + - liblalburst >=2.0.5,<3.0a0 + - liblalframe >=3.0.0 + - liblalframe >=3.0.6,<4.0a0 + - liblalmetaio >=4.0.0 + - liblalmetaio >=4.0.5,<5.0a0 + - liblalsimulation >=6.1.0 + - liblalsimulation >=6.1.0,<7.0a0 + constrains: + - lalinspiral >=2.0.1 + - python-lalinspiral >=2.0.1 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 411255 + timestamp: 1734709079488 +- conda: https://conda.anaconda.org/conda-forge/osx-64/liblalmetaio-4.0.5-h6e16a3a_2.conda + sha256: 99ed292f32c52ec8cb6efe39633f26f6cc7a58713bfb37c0b3aa6e8f67061e18 + md5: aa8c828c84c28781ae9a689db5a60f4b + depends: + - __osx >=10.13 + - liblal >=7.6.0 + - liblal >=7.6.0,<8.0a0 + - libmetaio >=8.4.0 + - libmetaio >=8.5.1,<9.0a0 + constrains: + - lalmetaio >=2.0.1 + - python-lalmetaio >=2.0.1 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 53697 + timestamp: 1734275150232 +- conda: https://conda.anaconda.org/conda-forge/osx-64/liblalpulsar-7.1.0-h0f29fec_1.conda + sha256: dd951d9d458f3ddba5da48c3ce559ef6b15898a6c589a1bc3320cda92993ce57 + md5: 36125546e4d611d4c35e3730c0903978 + depends: + - __osx >=10.13 + - cfitsio >=4.6.2,<4.6.3.0a0 + - fftw + - gsl >=2.7,<2.8.0a0 + - lalpulsar-data 7.1.0 h694c41f_1 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalframe >=3.0.0 + - liblalframe >=3.0.6,<4.0a0 + - liblalinference >=4.1.0 + - liblalinference >=4.1.8,<5.0a0 + - liblalsimulation >=6.1.0 + - liblalsimulation >=6.1.0,<7.0a0 + - llvm-openmp >=18.1.8 + - llvm-openmp >=20.1.2 + constrains: + - python-lalpulsar >=3.0.0 + - lalpulsar >=3.0.0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 650816 + timestamp: 1744215674845 +- conda: https://conda.anaconda.org/conda-forge/osx-64/liblalsimulation-6.1.0-py312h1b08b07_0.conda + sha256: b6a7d296d71f641438ea9f9f4b573ceab6e13ed5300d84b57351385af29d20f6 + md5: 617617aac9b4697dc180a6c67ed79117 + depends: + - __osx >=10.13 + - gsl >=2.7,<2.8.0a0 + - lalsimulation-data 6.1.0 h694c41f_0 + - liblal >=7.6.0 + - liblal >=7.6.0,<8.0a0 + - llvm-openmp >=18.1.8 + - llvm-openmp >=19.1.6 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - lalsimulation >=2.5.0 + - python-lalsimulation >=2.5.0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 3752219 + timestamp: 1734696240502 +- conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-7_h859234e_openblas.conda + build_number: 7 + sha256: 92fe5e99c1a4dbb53268790ce0b738411f21e0d7ca50dd3ce7a8781f1b03ed95 + md5: 3aa5c4d55000d922e71dee6daddc5031 + depends: + - libblas 3.11.0 7_he492b99_openblas + constrains: + - liblapacke 3.11.0 7*_openblas + - libcblas 3.11.0 7*_openblas + - blas 2.307 openblas + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 18862 + timestamp: 1778491179167 +- conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.3-hbb4bfdb_0.conda + sha256: d9e2006051529aec5578c6efeb13bb6a7200a014b2d5a77a579e83a8049d5f3c + md5: becdfbfe7049fa248e52aa37a9df09e2 + depends: + - __osx >=11.0 + constrains: + - xz 5.8.3.* + license: 0BSD + purls: [] + size: 105724 + timestamp: 1775826029494 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libmetaio-8.5.1-h97fe558_1003.conda + sha256: ed28b18a8ca5548711e6387439c01efde06c0f6e771b3243e51481dfd2293a98 + md5: 4a432bbf8764073858009a05a033b6d0 + depends: + - __osx >=10.13 + - libzlib >=1.3.1,<2.0a0 + constrains: + - metaio >=8.5.1 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 44865 + timestamp: 1721742286593 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.68.1-h70048d4_0.conda + sha256: 899551e16aac9dfb85bfc2fd98b655f4d1b7fea45720ec04ccb93d95b4d24798 + md5: dba4c95e2fe24adcae4b77ebf33559ae + depends: + - __osx >=11.0 + - c-ares >=1.34.6,<2.0a0 + - libcxx >=19 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.5,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 606749 + timestamp: 1773854765508 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.33-openmp_h9e49c7b_0.conda + sha256: 2c2ffe7c3ab7becd47ad308946873d2bdc219625af32a53d10efbaa54b595d31 + md5: 30666a6f0afe1471e999eca7ae5c8179 + depends: + - __osx >=11.0 + - libgfortran + - libgfortran5 >=14.3.0 + - llvm-openmp >=19.1.7 + constrains: + - openblas >=0.3.33,<0.3.34.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 6287889 + timestamp: 1776996499823 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libopentelemetry-cpp-1.21.0-h7a0a166_2.conda + sha256: b51d5bbaee67c933cf753e4a9fef6636daef481bf564a16800b57c5323f17b29 + md5: f08ab4450c9d2dee6cb629b03d512b33 + depends: + - libabseil * cxx17* + - libabseil >=20260107.0,<20260108.0a0 + - libcurl >=8.18.0,<9.0a0 + - libgrpc >=1.78.0,<1.79.0a0 + - libopentelemetry-cpp-headers 1.21.0 h694c41f_2 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libzlib >=1.3.1,<2.0a0 + - nlohmann_json + - prometheus-cpp >=1.3.0,<1.4.0a0 + constrains: + - cpp-opentelemetry-sdk =1.21.0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 582608 + timestamp: 1770453113535 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libopentelemetry-cpp-1.26.0-h7a0a166_0.conda + sha256: 6da1b908f427d66ca4a062df2026059229bdbdf5264c4095eec1e64f9351c837 + md5: 93aab3ab901b5b57d8d5d72308ead951 + depends: + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libcurl >=8.19.0,<9.0a0 + - libgrpc >=1.78.0,<1.79.0a0 + - libopentelemetry-cpp-headers 1.26.0 h694c41f_0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libzlib >=1.3.1,<2.0a0 + - nlohmann_json + - prometheus-cpp >=1.3.0,<1.4.0a0 + constrains: + - cpp-opentelemetry-sdk =1.26.0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 602246 + timestamp: 1774001890965 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libopentelemetry-cpp-headers-1.21.0-h694c41f_2.conda + sha256: 2d6da82ed55ee074b0edb20ce8709fe1bca9a1b01f788d4c919ef66d5e2628d9 + md5: 63e0fa4a4c03578de1f7df0b35453bc2 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 364240 + timestamp: 1770452995387 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libopentelemetry-cpp-headers-1.26.0-h694c41f_0.conda + sha256: 039ced2fa6d5fc5d23d06e2764709f0db9af5fbaef486309d47bec0895eddfa6 + md5: 6ed6a92518104721c0e37c032dd9769e + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 395724 + timestamp: 1774001742305 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libparquet-23.0.1-hb3ef814_4_cpu.conda + build_number: 4 + sha256: da870d708c97432b2ddf215fdab87981dee88818cdabb62678d7e6810f3bdbf5 + md5: bd0016547e5e9863b290addc3d6e4a97 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libarrow 23.0.1 h47227bc_4_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.21.0,<1.22.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libthrift >=0.22.0,<0.22.1.0a0 + - openssl >=3.5.5,<4.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 1093828 + timestamp: 1773272696367 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libparquet-24.0.0-h527dc83_2_cpu.conda + build_number: 2 + sha256: 89cc8aaee0e33f353a636481fce8b93eeca30f308dd6ec8d0a83c93a544f0b4e + md5: c868e7d94b1efa4295eb3d153ed7c5f3 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libarrow 24.0.0 h7252e1b_2_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.26.0,<1.27.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libthrift >=0.22.0,<0.22.1.0a0 + - openssl >=3.5.6,<4.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 1122983 + timestamp: 1779478576878 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libpng-1.6.58-he930e7c_0.conda + sha256: a669b22978e546484d18d99a210801b1823360a266d7035c713d8d1facd035f7 + md5: 9744d43d5200f284260637304a069ddd + depends: + - __osx >=11.0 + - libzlib >=1.3.2,<2.0a0 + license: zlib-acknowledgement + purls: [] + size: 299206 + timestamp: 1776315286816 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libprotobuf-6.33.5-h29d92e8_0.conda + sha256: adb74f4f1b1e13b02683ede915ce3a9fbf414325af8e035546c0498ffef870f6 + md5: d6d60b0a64a711d70ec2fd0105c299f9 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.0,<20260108.0a0 + - libcxx >=19 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 2774545 + timestamp: 1769749167835 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libre2-11-2025.11.05-h6e8c311_1.conda + sha256: 092f1ed90ba105402b0868eda0a1a11fd1aedd93ea6bb7a57f6e2fc2218806d5 + md5: 154f9f623c04dac40752d279bfdecebf + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.0,<20260108.0a0 + - libcxx >=19 + constrains: + - re2 2025.11.05.* + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 179250 + timestamp: 1768190310379 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libsodium-1.0.22-ha3d0635_1.conda + sha256: 07f0f37463564d93530fc23e2a77cc1aad58ba8724b1842f8713dbf6cde17cb0 + md5: bf0ce6af8f7628e34cdb399f6aa82e53 + depends: + - __osx >=11.0 + license: ISC + purls: [] + size: 281370 + timestamp: 1779164249823 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.53.1-h77d7759_0.conda + sha256: be4257efcf512aa7b58b0afe2c8dce945a014ae3adc7531528d53523d8e35cba + md5: 33cd0cd68a88361e1c328011539cd641 + depends: + - __osx >=11.0 + - libzlib >=1.3.2,<2.0a0 + license: blessing + purls: [] + size: 1002522 + timestamp: 1777986843821 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libssh2-1.11.1-hed3591d_0.conda + sha256: 00654ba9e5f73aa1f75c1f69db34a19029e970a4aeb0fa8615934d8e9c369c3c + md5: a6cb15db1c2dc4d3a5f6cf3772e09e81 + depends: + - __osx >=10.13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 284216 + timestamp: 1745608575796 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libthrift-0.22.0-hebea4ca_2.conda + sha256: 89a20cb35e0f32d59a7080c934a56120591cb962d4fab1cba3a795a094bc8256 + md5: 36d5479e1b5967c2eb9824b953317e41 + depends: + - __osx >=11.0 + - libcxx >=19 + - 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: [] + size: 332270 + timestamp: 1777019812419 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libtiff-4.7.1-ha0a348c_1.conda + sha256: e53424c34147301beae2cd9223ebf593720d94c038b3f03cacd0535e12c9668e + md5: 9d4344f94de4ab1330cdc41c40152ea6 + depends: + - __osx >=10.13 + - lerc >=4.0.0,<5.0a0 + - libcxx >=19 + - libdeflate >=1.25,<1.26.0a0 + - libjpeg-turbo >=3.1.0,<4.0a0 + - liblzma >=5.8.1,<6.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: HPND + purls: [] + size: 404591 + timestamp: 1762022511178 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libutf8proc-2.11.3-hc282952_0.conda + sha256: 626db214208e8da6aa9a904518a0442e5bff7b4602cc295dd5ce1f4a98844c1d + md5: 2c49b6f6ec9a510bbb75ecbd2a572697 + depends: + - __osx >=10.13 + license: MIT + license_family: MIT + purls: [] + size: 84535 + timestamp: 1768735249136 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libwebp-base-1.6.0-hb807250_0.conda + sha256: 00dbfe574b5d9b9b2b519acb07545380a6bc98d1f76a02695be4995d4ec91391 + md5: 7bb6608cf1f83578587297a158a6630b + depends: + - __osx >=10.13 + constrains: + - libwebp 1.6.0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 365086 + timestamp: 1752159528504 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libxcb-1.17.0-hf1f96e2_0.conda + sha256: 8896cd5deff6f57d102734f3e672bc17120613647288f9122bec69098e839af7 + md5: bbeca862892e2898bdb45792a61c4afc + depends: + - __osx >=10.13 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + purls: [] + size: 323770 + timestamp: 1727278927545 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libxml2-16-2.15.1-ha1d9b0f_0.conda + sha256: e23c5ac1da7b9b65bd18bf32b68717cd9da0387941178cb4d8cc5513eb69a0a9 + md5: 453807a4b94005e7148f89f9327eb1b7 + depends: + - __osx >=10.13 + - icu >=75.1,<76.0a0 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.1,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + constrains: + - libxml2 2.15.1 + license: MIT + license_family: MIT + purls: [] + size: 494318 + timestamp: 1761015899881 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libxml2-2.15.1-h7b7ecba_0.conda + sha256: ddf87bf05955d7870a41ca6f0e9fbd7b896b5a26ec1a98cd990883ac0b4f99bb + md5: e7ed73b34f9d43d80b7e80eba9bce9f3 + depends: + - __osx >=10.13 + - icu >=75.1,<76.0a0 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.1,<6.0a0 + - libxml2-16 2.15.1 ha1d9b0f_0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 39985 + timestamp: 1761015935429 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda + sha256: 4c6da089952b2d70150c74234679d6f7ac04f4a98f9432dec724968f912691e7 + md5: 30439ff30578e504ee5e0b390afc8c65 + depends: + - __osx >=11.0 + constrains: + - zlib 1.3.2 *_2 + license: Zlib + license_family: Other + purls: [] + size: 59000 + timestamp: 1774073052242 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libzopfli-1.0.3-h046ec9c_0.tar.bz2 + sha256: 3f35f8adf997467699a01819aeabba153ef554e796618c446a9626c2173aee90 + md5: 55f3f5c9bccca18d33cb3a4bcfe002d7 + depends: + - libcxx >=11.0.0 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 162262 + timestamp: 1607309210977 +- conda: https://conda.anaconda.org/conda-forge/osx-64/ligo-segments-1.4.0-py312h01d7ebd_6.conda + sha256: 6649042966cbc2269309bf9a8307949c92609321649a0604f6245e9f6848df51 + md5: e9dc7ca3adc58a6db57092b5ca3ce414 + depends: + - __osx >=10.13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - six + license: GPL-3.0-or-later + license_family: GPL + purls: + - pkg:pypi/ligo-segments?source=hash-mapping + size: 75079 + timestamp: 1733852009867 +- conda: https://conda.anaconda.org/conda-forge/osx-64/ligo.skymap-2.3.0-py312h2e055c9_0.conda + sha256: b31ac49594648f997bc8becd7226de8a2c93aa452040263323f0eb5cad3cc6e7 + md5: 4e0f9733a5e11f9175f100c82e3fadfa + depends: + - __osx >=10.13 + - astroplan >=0.7 + - astropy-base >=6.0 + - astropy-healpix >=0.3 + - chealpix >=3.31.0,<3.32.0a0 + - gsl >=2.7,<2.8.0a0 + - h5py + - healpy + - libcblas >=3.9.0,<4.0a0 + - ligo-gracedb >=2.0.1 + - ligo-segments >=1.2.0 + - llvm-openmp >=18.1.8 + - matplotlib-base >=3.9.1 + - networkx + - numpy >=1.19,<3 + - numpy >=1.23.0 + - pillow >=2.5.0 + - ptemcee + - python >=3.12,<3.13.0a0 + - python-lal >=7.6.0 + - python-lalinspiral >=5.0.1 + - python-lalmetaio >=4.0.5 + - python-lalsimulation >=6.0.0 + - python-ligo-lw >=1.8.0 + - python_abi 3.12.* *_cp312 + - pytz + - reproject >=0.3.2 + - scipy >=0.14,!=1.10.0 + - shapely >=2.0.0 + - tqdm >=4.27.0 + license: GPL-3.0-or-later + license_family: GPL + purls: + - pkg:pypi/ligo-skymap?source=hash-mapping + size: 1918619 + timestamp: 1745758339434 +- conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.6-h0d3cbff_0.conda + sha256: afbea63c0ffed8f150ba41a3e85bd849560f15f879d0f1b5e5fb6b90eca8ea78 + md5: b67316dec3b5c028b6b1bb6fd713c14e + depends: + - __osx >=11.0 + constrains: + - openmp 22.1.6|22.1.6.* + - intel-openmp <0.0a0 + license: Apache-2.0 WITH LLVM-exception + license_family: APACHE + purls: [] + size: 311051 + timestamp: 1779341346370 +- conda: https://conda.anaconda.org/conda-forge/osx-64/llvmlite-0.47.0-py312ha5a82fe_1.conda + sha256: 239afb28ccb79afc2ee8c620dcb121100ba87213142c49fcd6a3fbec80f230a0 + md5: 87e10812cded239b5d794dd99b1ab543 + depends: + - __osx >=11.0 + - libcxx >=19 + - libzlib >=1.3.2,<2.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - zstd >=1.5.7,<1.6.0a0 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/llvmlite?source=hash-mapping + size: 26002496 + timestamp: 1776077462405 +- conda: https://conda.anaconda.org/conda-forge/osx-64/lz4-4.4.5-py312ha706d14_1.conda + sha256: 76327601d2b65bb5e3b93cee12cfd301d2cf0b246d150ff52ff7b4b01c6f9147 + md5: 157f8c5e9e63b3a4ceab8e73386f1629 + depends: + - python + - lz4-c + - __osx >=10.13 + - lz4-c >=1.10.0,<1.11.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/lz4?source=hash-mapping + size: 41972 + timestamp: 1765026424344 +- conda: https://conda.anaconda.org/conda-forge/osx-64/lz4-c-1.10.0-h240833e_1.conda + sha256: 8da3c9d4b596e481750440c0250a7e18521e7f69a47e1c8415d568c847c08a1c + md5: d6b9bd7e356abd7e3a633d59b753495a + depends: + - __osx >=10.13 + - libcxx >=18 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 159500 + timestamp: 1733741074747 +- conda: https://conda.anaconda.org/conda-forge/osx-64/markupsafe-3.0.3-py312heb39f77_1.conda + sha256: 0eb418d4776a1a54c1869b11a5c4ae096ef9a46c8d7e481e32fa814561c5cfed + md5: d596f9d03043acd4ec711c844060da59 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/markupsafe?source=hash-mapping + size: 25095 + timestamp: 1772445399364 +- conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-3.10.9-py312hb401068_0.conda + sha256: e236285396458870bd69842978a8c7aa412464e73e608105c59ca537d470d40a + md5: 4277a65bf3b72f018c778d719338e13a + depends: + - matplotlib-base >=3.10.9,<3.10.10.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - tornado >=5 + license: PSF-2.0 + license_family: PSF + purls: [] + size: 17692 + timestamp: 1777000814535 +- conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-base-3.10.9-py312h7609456_0.conda + sha256: 7cec29b831c25744736c92d3fa9afe0e2c090087ce2387e7f1c7ecf08414b23e + md5: c7133e403f67d8e34af93e0be4286478 + depends: + - __osx >=11.0 + - contourpy >=1.0.1 + - cycler >=0.10 + - fonttools >=4.22.0 + - freetype + - kiwisolver >=1.3.1 + - libcxx >=19 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - numpy >=1.23 + - numpy >=1.23,<3 + - packaging >=20.0 + - pillow >=8 + - pyparsing >=2.3.1 + - python >=3.12,<3.13.0a0 + - python-dateutil >=2.7 + - python_abi 3.12.* *_cp312 + - qhull >=2020.2,<2020.3.0a0 + license: PSF-2.0 + license_family: PSF + purls: + - pkg:pypi/matplotlib?source=hash-mapping + size: 8284805 + timestamp: 1777000784713 +- conda: https://conda.anaconda.org/conda-forge/osx-64/msgpack-python-1.1.2-py312hd099df3_1.conda + sha256: 77314afa123abe6c25a0b8a161763d7f624f432bff382b976e5f243c72082944 + md5: 00597ae4dd073faaa9e6d2ca478f21c6 + depends: + - __osx >=10.13 + - libcxx >=19 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/msgpack?source=hash-mapping + size: 90666 + timestamp: 1762504423797 +- conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.6-hcc0dc9a_0.conda + sha256: f5f7e006ff4271305ab4cc08eedd855c67a571793c3d18aff73f645f088a8cae + md5: 31b8740cf1b2588d4e61c81191004061 + depends: + - __osx >=11.0 + license: X11 AND BSD-3-Clause + purls: [] + size: 831711 + timestamp: 1777423052277 +- conda: https://conda.anaconda.org/conda-forge/osx-64/nlohmann_json-3.12.0-h06076ce_1.conda + sha256: 8e1b8ac88e07da2910c72466a94d1fc77aa13c722f8ddbc7ae3beb7c19b41fc7 + md5: 97d7a1cda5546cb0bbdefa3777cb9897 + constrains: + - nlohmann_json-abi ==3.12.0 + license: MIT + license_family: MIT + purls: [] + size: 137081 + timestamp: 1768670842725 +- conda: https://conda.anaconda.org/conda-forge/osx-64/numba-0.65.1-py312h704f9c4_1.conda + sha256: 9453a0323f226052e3a4081e408ba6af3e0de932f7cd0372d1aaf7dcbfd3234f + md5: 49255c01905ec260d05004f75b07039a + depends: + - __osx >=11.0 + - libcxx >=19 + - llvm-openmp >=19.1.7 + - llvmlite >=0.47.0,<0.48.0a0 + - numpy >=1.22.3,<2.5 + - numpy >=1.23,<3 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - libopenblas !=0.3.6 + - cuda-python >=11.6 + - tbb >=2021.6.0 + - cuda-version >=11.2 + - cudatoolkit >=11.2 + - scipy >=1.0 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/numba?source=hash-mapping + size: 5690378 + timestamp: 1778390848946 +- conda: https://conda.anaconda.org/conda-forge/osx-64/numcodecs-0.16.5-py312h86abcb1_0.conda + sha256: dacadc1a0fe597ff94eea6b659ed0597e88f6a15c489a5562f0055bd7ef41c4e + md5: cb2f65f89f8194ff35e16cfe87dd1d62 + depends: + - __osx >=10.13 + - deprecated + - libcxx >=19 + - 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 + size: 753563 + timestamp: 1764782733189 +- conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-1.26.4-py312he3a82b2_0.conda + sha256: 6152b73fba3e227afa4952df8753128fc9669bbaf142ee8f9972bf9df3bf8856 + md5: 96c61a21c4276613748dba069554846b + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libcxx >=16 + - liblapack >=3.9.0,<4.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/numpy?source=hash-mapping + size: 6990646 + timestamp: 1707226178262 +- conda: https://conda.anaconda.org/conda-forge/osx-64/oniguruma-6.9.10-h6e16a3a_0.conda + sha256: c8ecd1cb39e75677235daddc6ead10055a0ef66b2293118ed77adc621b2ffbcc + md5: 1de37bb098b5b39ad79027d1767b02dd + depends: + - __osx >=10.13 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 224022 + timestamp: 1735727100676 +- conda: https://conda.anaconda.org/conda-forge/osx-64/openjpeg-2.5.4-h52bb76a_0.conda + sha256: 9a37ecf9c086f3a50d0132e6087dcbe7ea978d80e2da267fa3199c486529b311 + md5: 46e628da6e796c948fa8ec9d6d10bda3 + depends: + - __osx >=11.0 + - libcxx >=19 + - libpng >=1.6.55,<1.7.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 335227 + timestamp: 1772625294157 +- conda: https://conda.anaconda.org/conda-forge/osx-64/openjph-0.27.3-h2c0e27e_0.conda + sha256: f1bf8f40499668146e1d3ce38312fa79903d4f3f05ec0a48acaf2ff143301233 + md5: ece1fbf9be3cf229b346468376fd2a14 + depends: + - libcxx >=19 + - __osx >=11.0 + - libtiff >=4.7.1,<4.8.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 267852 + timestamp: 1778775444580 +- conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.2-hc881268_0.conda + sha256: 334fd49ea31b99114f5afb1ec44555dc8c90640648302a4f8f838ee345d1ec50 + md5: 5cf0ece4375c73d7a5765e83565a69c7 + depends: + - __osx >=11.0 + - ca-certificates + license: Apache-2.0 + license_family: Apache + purls: [] + size: 2776564 + timestamp: 1775589970694 +- conda: https://conda.anaconda.org/conda-forge/osx-64/orc-2.3.0-hb9b210e_0.conda + sha256: c4872822be78b2503bba06b906604c87000e3a63c7b7b8cb459463d46c55814b + md5: 292d30447800bc51a0d3e0e9738f5730 + depends: + - tzdata + - libcxx >=19 + - __osx >=11.0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + - snappy >=1.2.2,<1.3.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - libabseil >=20260107.1,<20260108.0a0 + - libabseil * cxx17* + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 594601 + timestamp: 1773230256637 +- conda: https://conda.anaconda.org/conda-forge/osx-64/pandas-3.0.3-py312h8e27051_0.conda + sha256: d72b541b510e3a1db86db3ce8d4c30bddc945c3c89eb2c9d16fde0cc9f82e497 + md5: 8cfffbf760a7d7abc16c79141ead177a + depends: + - python + - numpy >=1.26.0 + - python-dateutil >=2.8.2 + - libcxx >=19 + - __osx >=11.0 + - numpy >=1.23,<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 + size: 14170082 + timestamp: 1778602746933 +- conda: https://conda.anaconda.org/conda-forge/osx-64/pcre2-10.46-ha3e7e28_0.conda + sha256: cb262b7f369431d1086445ddd1f21d40003bb03229dfc1d687e3a808de2663a6 + md5: 3b504da3a4f6d8b2b1f969686a0bf0c0 + depends: + - __osx >=10.13 + - bzip2 >=1.0.8,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 1097626 + timestamp: 1756743061564 +- conda: https://conda.anaconda.org/conda-forge/osx-64/pcre2-10.47-h13923f0_0.conda + sha256: 8d64a9d36073346542e5ea042ef8207a45a0069a2e65ce3323ee3146db78134c + md5: 08f970fb2b75f5be27678e077ebedd46 + depends: + - __osx >=10.13 + - bzip2 >=1.0.8,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 1106584 + timestamp: 1763655837207 +- conda: https://conda.anaconda.org/conda-forge/osx-64/pillow-12.2.0-py312he84af14_0.conda + sha256: 720a4bb7aa1cb6a1d4fa026350f69785f5f11fe4899730d575e32292399f470a + md5: 2062ffb1b958b050e65ddd8556bcb4d8 + depends: + - python + - __osx >=11.0 + - openjpeg >=2.5.4,<3.0a0 + - libjpeg-turbo >=3.1.2,<4.0a0 + - libxcb >=1.17.0,<2.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - zlib-ng >=2.3.3,<2.4.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - tk >=8.6.13,<8.7.0a0 + - lcms2 >=2.18,<3.0a0 + - python_abi 3.12.* *_cp312 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + license: HPND + purls: + - pkg:pypi/pillow?source=hash-mapping + size: 973346 + timestamp: 1775060319565 +- conda: https://conda.anaconda.org/conda-forge/osx-64/prometheus-cpp-1.3.0-h7802330_0.conda + sha256: af754a477ee2681cb7d5d77c621bd590d25fe1caf16741841fc2d176815fc7de + md5: f36107fa2557e63421a46676371c4226 + depends: + - __osx >=10.13 + - libcurl >=8.10.1,<9.0a0 + - libcxx >=18 + - libzlib >=1.3.1,<2.0a0 + - zlib + license: MIT + license_family: MIT + purls: [] + size: 179103 + timestamp: 1730769223221 +- conda: https://conda.anaconda.org/conda-forge/osx-64/psutil-7.2.2-py312hf7082af_0.conda + sha256: 517c17b24349476535db4da7d1cd31538dadf2c77f9f7f7d8be6b7dc5dfbb636 + md5: 1fd947fae149960538fc941b8f122bc1 + depends: + - python + - __osx >=10.13 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/psutil?source=hash-mapping + size: 236338 + timestamp: 1769678402626 +- conda: https://conda.anaconda.org/conda-forge/osx-64/pthread-stubs-0.4-h00291cd_1002.conda + sha256: 05944ca3445f31614f8c674c560bca02ff05cb51637a96f665cb2bbe496099e5 + md5: 8bcf980d2c6b17094961198284b8e862 + depends: + - __osx >=10.13 + license: MIT + license_family: MIT + purls: [] + size: 8364 + timestamp: 1726802331537 +- conda: https://conda.anaconda.org/conda-forge/osx-64/pyarrow-23.0.1-py312hb401068_0.conda + sha256: 0d684a15fcba8ffb234956c97bd7eb227b763ee26cbbdadb3d26495ba7cb307d + md5: 9d2d172fb73dc93dc6e1927fa8a49c4c + depends: + - libarrow-acero 23.0.1.* + - libarrow-dataset 23.0.1.* + - libarrow-substrait 23.0.1.* + - libparquet 23.0.1.* + - pyarrow-core 23.0.1 *_0_* + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 28593 + timestamp: 1771308132070 +- conda: https://conda.anaconda.org/conda-forge/osx-64/pyarrow-24.0.0-py312hb401068_0.conda + sha256: 9f34bf129e8a618b6a8fecdfafa509823695b945d2618205758fab529ee9b88f + md5: 3301b7a88750f114aa09fc2a28db2435 + depends: + - libarrow-acero 24.0.0.* + - libarrow-dataset 24.0.0.* + - libarrow-substrait 24.0.0.* + - libparquet 24.0.0.* + - pyarrow-core 24.0.0 *_0_* + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 26749 + timestamp: 1776929273540 +- conda: https://conda.anaconda.org/conda-forge/osx-64/pyarrow-core-23.0.1-py312h3987635_0_cpu.conda + sha256: c01d0acc0c6a68726efdcedf651e8cf0597a5bb1598fb418fa6dea32cc2dc28e + md5: 7b6b5b493e59ace41b368e3a7bc87e09 + depends: + - __osx >=11.0 + - libarrow 23.0.1.* *cpu + - libarrow-compute 23.0.1.* *cpu + - libcxx >=21 + - libzlib >=1.3.1,<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 + size: 4395017 + timestamp: 1771308059514 +- conda: https://conda.anaconda.org/conda-forge/osx-64/pyarrow-core-24.0.0-py312h3987635_0_cpu.conda + sha256: dddbcf6e50454794ebd8ba77e892a099dbee4ad9727562e140f765ce0c08552d + md5: 40fe539ada22b325955f3590aefe39a9 + depends: + - __osx >=11.0 + - libarrow 24.0.0.* *cpu + - libarrow-compute 24.0.0.* *cpu + - libcxx >=21 + - 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 + size: 4055527 + timestamp: 1776929225059 +- conda: https://conda.anaconda.org/conda-forge/osx-64/pyerfa-2.0.1.5-py310hcbffc5d_2.conda + noarch: python + sha256: 06beb9ed2f6df706b5bd050e42819e49606d6256fe66dc7255c577a0140a2379 + md5: cd854c208de8cd3e2a6a878500021633 + depends: + - __osx >=10.13 + - numpy >=1.21,<3 + - python + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pyerfa?source=hash-mapping + size: 270277 + timestamp: 1756821799013 +- conda: https://conda.anaconda.org/conda-forge/osx-64/pynacl-1.6.2-py312h3bc9c61_2.conda + sha256: e9d67074d08df76c8835dcca4619b21549b67f95616edec086f7f73c66a6a576 + md5: 2e55aec7dce7390cbe537e79082c2ffa + depends: + - __osx >=11.0 + - cffi >=1.4.1 + - libsodium >=1.0.22,<1.0.23.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - six + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/pynacl?source=hash-mapping + size: 1194147 + timestamp: 1778985130828 +- conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.12.13-ha9537fe_0_cpython.conda + sha256: fb592ceb1bc247d19247d5535083da4a79721553e29e1290f5d81c07d4f086b5 + md5: ec05996c0d914a4e98ee3c7d789083f8 + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.7.4,<3.0a0 + - libffi >=3.5.2,<3.6.0a0 + - liblzma >=5.8.2,<6.0a0 + - libsqlite >=3.51.2,<4.0a0 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.5.5,<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: [] + size: 13672169 + timestamp: 1772730464626 +- conda: https://conda.anaconda.org/conda-forge/osx-64/python-gssapi-1.11.1-py312h479078b_1.conda + sha256: b18065ebd7f0d126a434fae0b23adbf7b5bb2fbf0c791dee6ed7e88a2397e57a + md5: df4df185716e9fc68c12118b8f457676 + depends: + - __osx >=10.13 + - decorator + - krb5 >=1.22.2,<1.23.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: ISC + purls: + - pkg:pypi/gssapi?source=hash-mapping + size: 483085 + timestamp: 1770935003939 +- conda: https://conda.anaconda.org/conda-forge/osx-64/python-gssapi-1.11.1-py312h972ca57_0.conda + sha256: c36f7b2f763e383b9d5ccce60df8d3a94c1756f38fc4c0568ce16b942e97b8a5 + md5: 69a9c1256164ac1b857499662aa84de0 + depends: + - __osx >=10.13 + - decorator + - krb5 >=1.21.3,<1.22.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: ISC + purls: + - pkg:pypi/gssapi?source=hash-mapping + size: 482564 + timestamp: 1769842851678 +- conda: https://conda.anaconda.org/conda-forge/osx-64/python-htcondor-24.12.4-py312h564a4e3_0.conda + sha256: 2644017fe4104fd006a06c26cff0c0585e6f003526e8860ca38531b123e01c8f + md5: 60acec36edf5ae3bd69c3b5e0326c9db + depends: + - __osx >=10.13 + - htcondor-classads 24.12.4 hf470585_0 + - libboost-python >=1.88.0,<1.89.0a0 + - libcondor_utils 24.12.4 h2da22b3_0 + - libcxx >=19 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/htcondor?source=hash-mapping + size: 933415 + timestamp: 1759417119633 +- conda: https://conda.anaconda.org/conda-forge/osx-64/python-htcondor-25.10.1-py312haf40f37_0.conda + sha256: 9c7644309e92850e044379f4630c1123188fe32775092c62b9b51555b89f5f40 + md5: bf9185ff97db72ebd3262b15dec34905 + depends: + - __osx >=11.0 + - htcondor-classads 25.10.1 h4086b99_0 + - libcondor_utils 25.10.1 hcd6a731_0 + - libcxx >=19 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/htcondor?source=hash-mapping + size: 503241 + timestamp: 1778793697930 +- conda: https://conda.anaconda.org/conda-forge/osx-64/python-lal-7.6.1-fftw_py312ha78c7a8_100.conda + sha256: 5cd28a5b37cee7e56aa0665cad304302d86284ce760583ed3dd568818853549d + md5: 9175f7fb7fe0826a8b30ecfbfb8b5f78 + depends: + - __osx >=10.13 + - liblal 7.6.1 fftw_ha907fd1_100 + - ligo-segments + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python-dateutil + - python_abi 3.12.* *_cp312 + - scipy + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 850028 + timestamp: 1734695620404 +- conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalburst-2.0.6-py312h44bc254_0.conda + sha256: 45a02b6ae62110399f2393ca34693696d59ab4f7713460b4a2e08110ba33a1cd + md5: 0e2b3d44d16c5968973c06df9f4e4d58 + depends: + - __osx >=10.13 + - gsl >=2.7,<2.8.0a0 + - liblalburst 2.0.6 h8fa4168_0 + - ligo-segments + - lscsoft-glue + - matplotlib-base + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python-lal >=7.6.0 + - python-lalmetaio >=4.0.0 + - python-lalsimulation >=6.1.0 + - python-ligo-lw >=1.7.0 + - python_abi 3.12.* *_cp312 + - scipy + - tqdm + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 312039 + timestamp: 1734709312316 +- conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalframe-3.0.6-py312h025c719_0.conda + sha256: db3ab5adf5ae1ac0b21c26d7fd5cf1a48dd929762331cde257e16d592cbcd016 + md5: f6d0bda9d211848968a758f86dab3f09 + depends: + - __osx >=10.13 + - liblalframe 3.0.6 h6e76fb1_0 + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python-lal >=7.6.0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 708928 + timestamp: 1734696769662 +- conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalinference-4.1.8-py312h025c719_0.conda + sha256: 32891899012c61571a3d71ed7b31c33a875dd7e56061188b4d9fc19e34b23e75 + md5: a88c9ff6b9e94ca45f044dbe1a772740 + depends: + - __osx >=10.13 + - astropy-base >=1.1.1 + - h5py + - healpy >=1.17.3 + - liblalinference 4.1.8 h506f03e_0 + - ligo-segments + - lscsoft-glue >=1.54.1 + - matplotlib-base >=1.2.0 + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python-lal >=7.6.0 + - python-lalburst >=2.0.0 + - python-lalinspiral >=5.0.0 + - python-lalmetaio >=4.0.0 + - python-lalsimulation >=6.1.0 + - python-ligo-lw >=1.7.0 + - python_abi 3.12.* *_cp312 + - scipy >=0.9.0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 804026 + timestamp: 1734713277488 +- conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalinspiral-5.0.2-py312h025c719_0.conda + sha256: 8d80d16688d611e958f02ed54b5f465315a3dc38b9d3b42aaa1730b72e2fc5a1 + md5: be660f9e8446540f558cc273f173d317 + depends: + - __osx >=10.13 + - liblalinspiral 5.0.2 h8fa4168_0 + - lscsoft-glue + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python-lal >=7.6.0 + - python-lalburst >=2.0.0 + - python-lalframe >=3.0.0 + - python-lalmetaio >=4.0.0 + - python-lalsimulation >=6.1.0 + - python-ligo-lw + - python_abi 3.12.* *_cp312 + - tqdm + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 61664188 + timestamp: 1734709229174 +- conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalmetaio-4.0.5-py312h025c719_2.conda + sha256: 99edce6c41b648482f3f64c5c677bfa998f27edb120bee3546f7ec007c64e7e9 + md5: b662ef18c786a9436f0021e0d398b175 + depends: + - __osx >=10.13 + - liblalmetaio 4.0.5 h6e16a3a_2 + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python-lal >=7.6.0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 142363 + timestamp: 1734275405378 +- conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalpulsar-7.1.0-py312h025c719_1.conda + sha256: 0f5bbddc047f4116342f3f38abaacadee7b9fbc09cfb8c8780f2edbc334e7a14 + md5: 90f52e49ee6ccd4f8d57091489cf1805 + depends: + - __osx >=10.13 + - astropy-base + - liblalpulsar 7.1.0 h0f29fec_1 + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python-lal >=7.6.0 + - python-lalframe >=3.0.0 + - python-lalinference >=4.1.0 + - python-lalsimulation >=6.1.0 + - python_abi 3.12.* *_cp312 + - six + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 96736713 + timestamp: 1744215867084 +- conda: https://conda.anaconda.org/conda-forge/osx-64/python-lalsimulation-6.1.0-py312h1b08b07_0.conda + sha256: e31700f6d3f3fe8c245cce8da95b1406cb16a3cb57f6e0deeabfe8ca99c16098 + md5: 733897fab6313736f579eb700ce36026 + depends: + - __osx >=10.13 + - astropy-base + - gsl >=2.7,<2.8.0a0 + - gwpy + - liblal >=7.6.0 + - liblal >=7.6.0,<8.0a0 + - liblalsimulation 6.1.0 py312h1b08b07_0 + - llvm-openmp >=18.1.8 + - llvm-openmp >=19.1.6 + - mpmath >=1.0.0 + - numpy + - python >=3.12,<3.13.0a0 + - python-lal >=7.6.0 + - python_abi 3.12.* *_cp312 + - scipy + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 4276447 + timestamp: 1734696345259 +- conda: https://conda.anaconda.org/conda-forge/osx-64/python-ligo-lw-1.8.3-py312h01d7ebd_4.conda + sha256: 0f1112f9dd41298f7d4693bcf36966a90ea170e42ae9e56a7b83141ebb189b8d + md5: 3f048565d1b9c3b255f775bc5a84aba2 + depends: + - __osx >=10.13 + - ligo-segments + - lscsoft-glue >=2.0.0 + - numpy + - python >=3.12,<3.13.0a0 + - python-dateutil + - python-lal >=6.19.0 + - python_abi 3.12.* *_cp312 + - pyyaml + - six + - tqdm + license: GPL-3.0-or-later + license_family: GPL + purls: + - pkg:pypi/python-ligo-lw?source=hash-mapping + size: 191079 + timestamp: 1733996334301 +- conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py312h51361c1_1.conda + sha256: d85e3be523b7173a194a66ae05a585ac1e14ccfbe81a9201b8047d6e45f2f7d9 + md5: 9029301bf8a667cf57d6e88f03a6726b + depends: + - __osx >=10.13 + - 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 + size: 190417 + timestamp: 1770223755226 +- conda: https://conda.anaconda.org/conda-forge/osx-64/qhull-2020.2-h3c5361c_5.conda + sha256: 79d804fa6af9c750e8b09482559814ae18cd8df549ecb80a4873537a5a31e06e + md5: dd1ea9ff27c93db7c01a7b7656bd4ad4 + depends: + - __osx >=10.13 + - libcxx >=16 + license: LicenseRef-Qhull + purls: [] + size: 528122 + timestamp: 1720814002588 +- conda: https://conda.anaconda.org/conda-forge/osx-64/rav1e-0.8.1-h618d828_0.conda + sha256: 4286abee88102b8889d2d20e79a51424fcee2aa24aba3d0ea3c5c7ee251d48ca + md5: 19c2d0ae0255c175faec576d43fe9763 + depends: + - __osx >=11.0 + constrains: + - __osx >=10.13 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 1129893 + timestamp: 1772541463868 +- conda: https://conda.anaconda.org/conda-forge/osx-64/re2-2025.11.05-h77e0585_1.conda + sha256: 1aeb9a9554cc719d454ad6158afbb0c249973fa4ee1d782d7e40cbec1de9b061 + md5: b2cc31f114e4487d24e5617e62a24017 + depends: + - libre2-11 2025.11.05 h6e8c311_1 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 27447 + timestamp: 1768190352348 +- conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda + sha256: 4614af680aa0920e82b953fece85a03007e0719c3399f13d7de64176874b80d5 + md5: eefd65452dfe7cce476a519bece46704 + depends: + - __osx >=10.13 + - ncurses >=6.5,<7.0a0 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 317819 + timestamp: 1765813692798 +- conda: https://conda.anaconda.org/conda-forge/osx-64/regex-2026.5.9-py312h933eb07_0.conda + sha256: 0ca7dc702def6b38ba17d9092c746089b04262a5481523fa636a62caf6de357a + md5: 41e37a699f3ca95813394005d82784e8 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 AND CNRI-Python + license_family: PSF + purls: + - pkg:pypi/regex?source=hash-mapping + size: 377764 + timestamp: 1778374454469 +- conda: https://conda.anaconda.org/conda-forge/osx-64/reproject-0.19.0-py312h391ab28_0.conda + sha256: a9277d09d48763dfed1e50b0be842456309a5f471d48ac78ebd711c5a6fcec81 + md5: 2376a866079c62b37ca09d17638d4bbb + depends: + - __osx >=10.13 + - astropy-base >=5.0 + - astropy-healpix >=1.0 + - dask >=2024.4.1 + - dask-image >=2025.11.0 + - fsspec >=2021.9 + - numpy >=1.23 + - numpy >=1.23,<3 + - pillow >=10.0 + - pyavm >=0.9.6 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - scipy >=1.9 + - shapely + - zarr >=2.17.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/reproject?source=hash-mapping + size: 967338 + timestamp: 1763206623898 +- conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.8.0-np2py312h47bbdc5_1.conda + sha256: 1a03f549462e9c700c93664c663c08a651f6c93c0979384417ac132549c44b98 + md5: 9c037f2050f55c721704013b87c9724e + depends: + - python + - numpy >=1.24.1 + - scipy >=1.10.0 + - joblib >=1.3.0 + - threadpoolctl >=3.2.0 + - __osx >=10.13 + - llvm-openmp >=19.1.7 + - libcxx >=19 + - python_abi 3.12.* *_cp312 + - numpy >=1.23,<3 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scikit-learn?source=hash-mapping + size: 9288972 + timestamp: 1766550860454 +- conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.17.1-py312h6309490_0.conda + sha256: 81842a4b39f700e122c8ba729034c7fc7b3a6bfd8d3ca41fe64e2bc8b0d1a4f4 + md5: 07c955303eea8d00535164eb5f63ee97 + depends: + - __osx >=11.0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libcxx >=19 + - libgfortran + - libgfortran5 >=14.3.0 + - liblapack >=3.9.0,<4.0a0 + - numpy <2.7 + - numpy >=1.23,<3 + - numpy >=1.25.2 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scipy?source=hash-mapping + size: 15312767 + timestamp: 1771881124085 +- conda: https://conda.anaconda.org/conda-forge/osx-64/scitokens-cpp-1.4.0-h1c2ca81_0.conda + sha256: 8cc1d5c4a21e28f06e8d452a3db1f729836cdde7cc84209fac24063bf234f2a1 + md5: 431ea6026f7d94436135184130347ee6 + depends: + - __osx >=11.0 + - libcurl >=8.18.0,<9.0a0 + - libcxx >=19 + - libsqlite >=3.51.2,<4.0a0 + - openssl >=3.5.5,<4.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 188453 + timestamp: 1771576368104 +- conda: https://conda.anaconda.org/conda-forge/osx-64/shapely-2.1.2-py312hd8edc82_2.conda + sha256: 0ad376aee3a2fe149443af9345aadeb8ad82a95953bee74b59ca17997da03012 + md5: eae9cbc6418de8f26e08f4fb255759e9 + depends: + - __osx >=10.13 + - geos >=3.14.1,<3.14.2.0a0 + - numpy >=1.23,<3 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/shapely?source=hash-mapping + size: 603294 + timestamp: 1762523892524 +- conda: https://conda.anaconda.org/conda-forge/osx-64/snappy-1.2.2-h01f5ddf_1.conda + sha256: 1525e6d8e2edf32dabfe2a8e2fc8bf2df81c5ef9f0b5374a3d4ccfa672bfd949 + md5: 2e993292ec18af5cd480932d448598cf + depends: + - libcxx >=19 + - __osx >=10.13 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 40023 + timestamp: 1762948053450 +- conda: https://conda.anaconda.org/conda-forge/osx-64/svt-av1-4.0.1-h991f03e_0.conda + sha256: 5f8c150f558437364bfe6dade1a81cf1b3fe8ba291d8d8db01f889c32a310f08 + md5: 111339b9431e9de1e37ffa8e53040609 + depends: + - __osx >=10.13 + - libcxx >=19 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 2364161 + timestamp: 1769664663364 +- conda: https://conda.anaconda.org/conda-forge/osx-64/swig-4.3.1-hf470585_4.conda + sha256: 59d08fe888e623eabd72b320753dc507280e17370b8642ef97de592300d5d399 + md5: 53469220ecb176496c25c151e3ed9044 + depends: + - __osx >=10.13 + - libcxx >=19 + - pcre2 >=10.46,<10.47.0a0 + constrains: + - swig-abi ==4 + license: GPL-3.0-or-later + license_family: GPL + purls: [] + size: 1164059 + timestamp: 1761740878132 +- conda: https://conda.anaconda.org/conda-forge/osx-64/swig-4.4.1-hdac4ec2_0.conda + sha256: 5f89ae3ec8f2ac47f355838e5071708440807c78afbe68bf5d5719e0d9483197 + md5: 4f5f602a207a0b56d48ada419014b26e + depends: + - libcxx >=19 + - __osx >=11.0 + - pcre2 >=10.47,<10.48.0a0 + constrains: + - swig-abi ==5 + license: GPL-3.0-or-later + license_family: GPL + purls: [] + size: 1234602 + timestamp: 1773252006725 +- conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda + sha256: 7f0d9c320288532873e2d8486c331ec6d87919c9028208d3f6ac91dc8f99a67b + md5: 6e6efb7463f8cef69dbcb4c2205bf60e + depends: + - __osx >=10.13 + - libzlib >=1.3.1,<2.0a0 + license: TCL + license_family: BSD + purls: [] + size: 3282953 + timestamp: 1769460532442 +- conda: https://conda.anaconda.org/conda-forge/osx-64/tornado-6.5.5-py312h933eb07_0.conda + sha256: da46b42ce6413f6fd03195507df57c8149cc04e043cdbf4d06f7cae236a5445c + md5: af855ebae119a5ff8902ef7a8e200303 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/tornado?source=hash-mapping + size: 857717 + timestamp: 1774358322837 +- conda: https://conda.anaconda.org/conda-forge/osx-64/unicodedata2-17.0.1-py312h1a1c95f_0.conda + sha256: 29bdc3648a4b8011c572003234ebfaa13666aa71760c9f89f798758ccebd7448 + md5: 563dc18b746f873408b76e068e2b4baa + depends: + - __osx >=10.13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/unicodedata2?source=hash-mapping + size: 406056 + timestamp: 1770909495553 +- conda: https://conda.anaconda.org/conda-forge/osx-64/wrapt-2.2.1-py312h933eb07_0.conda + sha256: d836266c79039b60e2104d87d277a74ddc95b195c84ed08dc31185faf3dead6c + md5: 9832454120b29950e9304a608d71c01a + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/wrapt?source=hash-mapping + size: 111048 + timestamp: 1779477872468 +- conda: https://conda.anaconda.org/conda-forge/osx-64/xorg-libxau-1.0.12-h8616949_1.conda + sha256: 928f28bd278c7da674b57d71b2e7f4ac4e7c7ce56b0bf0f60d6a074366a2e76d + md5: 47f1b8b4a76ebd0cd22bd7153e54a4dc + depends: + - __osx >=10.13 + license: MIT + license_family: MIT + purls: [] + size: 13810 + timestamp: 1762977180568 +- conda: https://conda.anaconda.org/conda-forge/osx-64/xorg-libxdmcp-1.1.5-h8616949_1.conda + sha256: b7b291cc5fd4e1223058542fca46f462221027779920dd433d68b98e858a4afc + md5: 435446d9d7db8e094d2c989766cfb146 + depends: + - __osx >=10.13 + license: MIT + license_family: MIT + purls: [] + size: 19067 + timestamp: 1762977101974 +- conda: https://conda.anaconda.org/conda-forge/osx-64/yaml-0.2.5-h4132b18_3.conda + sha256: a335161bfa57b64e6794c3c354e7d49449b28b8d8a7c4ed02bf04c3f009953f9 + md5: a645bb90997d3fc2aea0adf6517059bd + depends: + - __osx >=10.13 + license: MIT + license_family: MIT + purls: [] + size: 79419 + timestamp: 1753484072608 +- conda: https://conda.anaconda.org/conda-forge/osx-64/zfp-1.0.1-h1b13a81_4.conda + sha256: fa9f5df5c864abe1360633029789c9d54881d75752e064d0b76ea0ba578cbff6 + md5: ab3f885d2b4f24cdcbc91926f3ad9301 + depends: + - __osx >=10.13 + - libcxx >=19 + - llvm-openmp >=19.1.7 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 211942 + timestamp: 1766458562226 +- conda: https://conda.anaconda.org/conda-forge/osx-64/zlib-1.3.2-hbb4bfdb_2.conda + sha256: 5dd728cebca2e96fa48d41661f1a35ed0ee3cb722669eee4e2d854c6745655eb + md5: 6276aa61ffc361cbf130d78cfb88a237 + depends: + - __osx >=11.0 + - libzlib 1.3.2 hbb4bfdb_2 + license: Zlib + license_family: Other + purls: [] + size: 92411 + timestamp: 1774073075482 +- conda: https://conda.anaconda.org/conda-forge/osx-64/zlib-ng-2.3.3-h8bce59a_1.conda + sha256: 4a1beb656761c7d8c9a53474bfd3932c30d82af5d93a32b8ef626c01c059d981 + md5: b3ecb6480fd46194e3f7dd0ff4445dff + depends: + - __osx >=10.13 + - libcxx >=19 + license: Zlib + license_family: Other + purls: [] + size: 120464 + timestamp: 1770168263684 +- conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda + sha256: 47101a4055a70a4876ffc87b750ab2287b67eca793f21c8224be5e1ee6394d3f + md5: 727109b184d680772e3122f40136d5ca + depends: + - __osx >=10.13 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 528148 + timestamp: 1764777156963 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda + build_number: 7 + sha256: 7acaa2e0782cad032bdaf756b536874346ac1375745fb250e9bdd6a48a7ab3cd + md5: a44032f282e7d2acdeb1c240308052dd + depends: + - llvm-openmp >=9.0.1 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 8325 + timestamp: 1764092507920 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aom-3.9.1-h7bae524_0.conda + sha256: ec238f18ce8140485645252351a0eca9ef4f7a1c568a420f240a585229bc12ef + md5: 7adba36492a1bb22d98ffffe4f6fc6de + depends: + - __osx >=11.0 + - libcxx >=16 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 2235747 + timestamp: 1718551382432 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/astropy-base-7.2.0-py312ha11c99a_0.conda + sha256: 1d235639c72e2704e4a71548ae229f6fddd5a7599363027ee887692cfa2ad6af + md5: 805e724e1d067bf930ea9a342640a402 + depends: + - __osx >=11.0 + - astropy-iers-data >=0.2025.10.27.0.39.10 + - numpy >=1.23,<3 + - numpy >=1.24 + - packaging >=22.0.0 + - pyerfa >=2.0.1.1 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - pyyaml >=6.0.0 + constrains: + - astropy >=7.0.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/astropy?source=hash-mapping + size: 9377467 + timestamp: 1764121407665 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/astropy-healpix-1.1.3-py312hf57c059_0.conda + sha256: 90d6bfd802bb93ff7326fb9b06109f7e1e7f765575ba33c174a1f00d7f036e65 + md5: 85973aa99b28c4b0067078caf5bb4cd7 + depends: + - __osx >=11.0 + - astropy-base >=3 + - numpy >=1.23,<3 + - 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/astropy-healpix?source=hash-mapping + size: 109368 + timestamp: 1768880553169 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.10.1-ha7d4cc1_3.conda + sha256: ce023981f49a96074bc84d6249c7097b71c27e8d3bd750fc07c520579159521c + md5: 4fbd86a4d1efeb954b0d559d6717bd2b + depends: + - __osx >=11.0 + - aws-c-http >=0.10.13,<0.10.14.0a0 + - aws-c-io >=0.26.3,<0.26.4.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 116717 + timestamp: 1777489477698 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.9.6-ha02d361_1.conda + sha256: 69b1b619958a9120b92ba9f418c51309fbd14f67628ea9617e7e0a4936d5d035 + md5: 798becc566a5335533252906c42ef71b + depends: + - __osx >=11.0 + - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 + - aws-c-io >=0.26.1,<0.26.2.0a0 + - aws-c-http >=0.10.10,<0.10.11.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 115282 + timestamp: 1771494170485 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.13-h6ee9776_1.conda + sha256: 13c42cb54619df0a1c3e5e5b0f7c8e575460b689084024fd23abeb443aac391b + md5: 8baab664c541d6f059e83423d9fc5e30 + depends: + - __osx >=11.0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 45233 + timestamp: 1764593742187 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.12.6-hc919400_0.conda + sha256: cd3817c82470826167b1d8008485676862640cff65750c34062e6c20aeac419b + md5: b759f02a7fa946ea9fd9fb035422c848 + depends: + - __osx >=11.0 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 224116 + timestamp: 1763585987935 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.2-h3e7f9b5_0.conda + sha256: ce405171612acef0924a1ff9729d556db7936ad380a81a36325b7df5405a6214 + md5: 6edccad10fc1c76a7a34b9c14efbeaa3 + depends: + - __osx >=11.0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 21470 + timestamp: 1767790900862 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.5.9-hd533cd8_2.conda + sha256: c06a47704bba4f9f979e2ee2d0b35200458f1ac6d4009fcd2c6d616ed8a18160 + md5: 523157d65a64b29f4bf2be084756df69 + depends: + - libcxx >=19 + - __osx >=11.0 + - aws-checksums >=0.2.10,<0.2.11.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-io >=0.26.1,<0.26.2.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 53198 + timestamp: 1771380419309 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.7.0-h351c84d_0.conda + sha256: 454c02593d88246ac0fd4fc5e306147dd4f6c4866931c436ddeccdb37a70250f + md5: cb6d3b9905ffa47de2628e1ba9666c33 + depends: + - __osx >=11.0 + - libcxx >=19 + - aws-c-io >=0.26.3,<0.26.4.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-checksums >=0.2.10,<0.2.11.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 53822 + timestamp: 1774480046539 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.10.10-ha1850f6_0.conda + sha256: a73aa557b246944f13af9fb3ad9f3bad6260252aa0b92df066eb5113c0be8fec + md5: 2b65d6ea75034df28aa2f2117920c51f + depends: + - __osx >=11.0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-io >=0.26.1,<0.26.2.0a0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + - aws-c-compression >=0.3.2,<0.3.3.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 172345 + timestamp: 1771421384051 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.10.13-h95cdebe_0.conda + sha256: 7a008a5a2f76256e841a8565b373a7dcfd2a439e5d9dbec320322468637f69e5 + md5: fc4478bc51e76c5d26ea2c4f1e3ba366 + depends: + - __osx >=11.0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + - aws-c-compression >=0.3.2,<0.3.3.0a0 + - aws-c-io >=0.26.3,<0.26.4.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 173575 + timestamp: 1774488444724 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.26.1-h4137820_2.conda + sha256: 131064d83b9e8b0214c0c240df053e55fef0a7c0590acf6fb569354ae0d22cb8 + md5: c67922134dc54a497da7a12bca07d001 + depends: + - __osx >=11.0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 177168 + timestamp: 1773328939595 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.26.3-h4137820_2.conda + sha256: 953207d6854b41cb12c4ecfa49f15f5c21086df47c0535de8a5f3cc4eb3e70de + md5: e18c6ab3c89c04be91b14f02386bc916 + depends: + - __osx >=11.0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 176967 + timestamp: 1779133165183 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.14.0-h5721393_1.conda + sha256: e6149bb7b836ddd3ccf87ff84d57925ee27e773b531932e75095b90cb30f87e0 + md5: f06bafa0131571f5a09d25ad2478873f + depends: + - __osx >=11.0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-http >=0.10.10,<0.10.11.0a0 + - aws-c-io >=0.26.1,<0.26.2.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 155370 + timestamp: 1771458064307 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.15.2-h8860bc9_2.conda + sha256: 19a97f5d06ef994d7f48e77de3f998cc0a72d750d9363f2ba3894234c7bc799e + md5: 826c667323e95b2af0223641c69f327c + depends: + - __osx >=11.0 + - aws-c-io >=0.26.3,<0.26.4.0a0 + - aws-c-http >=0.10.13,<0.10.14.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 156329 + timestamp: 1777488187414 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.11.5-h7d214dc_3.conda + sha256: 691d5081569ec9cebf6a9d33b5ea7d0d7e642469b0f11b6736a4c277f5d879a9 + md5: 79e417d4617e8e1c0738184979cd0753 + depends: + - __osx >=11.0 + - aws-checksums >=0.2.10,<0.2.11.0a0 + - aws-c-http >=0.10.10,<0.10.11.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-io >=0.26.1,<0.26.2.0a0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + - aws-c-auth >=0.9.6,<0.9.7.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 129600 + timestamp: 1771586353474 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.12.2-h07b101a_1.conda + sha256: 236b4acfc8b0757e427087b53ebaf090190d106a7e73b6b1e8f80388988e89ac + md5: 7a520ebd6ae9efe641cb207b650d004c + depends: + - __osx >=11.0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-http >=0.10.13,<0.10.14.0a0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + - aws-c-auth >=0.10.1,<0.10.2.0a0 + - aws-c-io >=0.26.3,<0.26.4.0a0 + - aws-checksums >=0.2.10,<0.2.11.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 131374 + timestamp: 1777824889044 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.4-h16f91aa_4.conda + sha256: 8a4ee03ea6e14d5a498657e5fe96875a133b4263b910c5b60176db1a1a0aaa27 + md5: 658a8236f3f1ebecaaa937b5ccd5d730 + depends: + - __osx >=11.0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 53430 + timestamp: 1764755714246 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.10-h3e7f9b5_0.conda + sha256: 06661bc848b27aa38a85d8018ace8d4f4a3069e22fa0963e2431dc6c0dc30450 + md5: 07f6c5a5238f5deeed6e985826b30de8 + depends: + - __osx >=11.0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 91917 + timestamp: 1771063496505 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.37.3-hcfbc53e_0.conda + sha256: c3ec75e1ec5c33ed1d25fb039baad7e7834417279b030f29bd3987190de333cb + md5: 85f41c2eea3b03e0b0b8aedaaee3e2b6 + depends: + - __osx >=11.0 + - libcxx >=19 + - aws-c-s3 >=0.11.5,<0.11.6.0a0 + - aws-c-auth >=0.9.6,<0.9.7.0a0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + - aws-c-mqtt >=0.14.0,<0.14.1.0a0 + - aws-c-io >=0.26.1,<0.26.2.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 + - aws-c-event-stream >=0.5.9,<0.5.10.0a0 + - aws-c-http >=0.10.10,<0.10.11.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 269227 + timestamp: 1771983403739 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.38.3-hba17502_1.conda + sha256: 917ca9bcd9271a55be1c39dc1b07ce2e6c268c1fd507750d86138d98f708724a + md5: 40aa7f64708aef33749bcedd53d04d2e + depends: + - libcxx >=19 + - __osx >=11.0 + - aws-c-auth >=0.10.1,<0.10.2.0a0 + - aws-c-event-stream >=0.7.0,<0.7.1.0a0 + - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-cal >=0.9.13,<0.9.14.0a0 + - aws-c-http >=0.10.13,<0.10.14.0a0 + - aws-c-io >=0.26.3,<0.26.4.0a0 + - aws-c-s3 >=0.12.2,<0.12.3.0a0 + - aws-c-mqtt >=0.15.2,<0.15.3.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 271073 + timestamp: 1778019218424 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.747-h30a6df1_4.conda + sha256: 29c21176bc47051ed20db066dc4917b1c9d8e209f936a1737998755061ee133f + md5: 45bb0d0e776ed7cd12597710058c2d50 + depends: + - __osx >=11.0 + - libcxx >=19 + - aws-crt-cpp >=0.38.3,<0.38.4.0a0 + - libcurl >=8.20.0,<9.0a0 + - libzlib >=1.3.2,<2.0a0 + - aws-c-event-stream >=0.7.0,<0.7.1.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 3261086 + timestamp: 1778156290937 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.747-h35a1687_1.conda + sha256: bee6af5afafd9372028fd6820d6e09798a3248c9991478d96a68fe67e9621112 + md5: e60910468151f2bc63a69d8ee9dab529 + depends: + - __osx >=11.0 + - libcxx >=19 + - libcurl >=8.18.0,<9.0a0 + - libzlib >=1.3.1,<2.0a0 + - aws-c-common >=0.12.6,<0.12.7.0a0 + - aws-c-event-stream >=0.5.9,<0.5.10.0a0 + - aws-crt-cpp >=0.37.3,<0.37.4.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 3260740 + timestamp: 1772084565005 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.16.2-he5ae378_0.conda + sha256: d9a04af33d9200fcd9f6c954e2a882c5ac78af4b82025623e59cb7f7e590b451 + md5: 7efe92d28599c224a24de11bb14d395e + depends: + - __osx >=11.0 + - libcurl >=8.18.0,<9.0a0 + - libcxx >=19 + - openssl >=3.5.4,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 290928 + timestamp: 1768837810218 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.13.3-h810541e_1.conda + sha256: 428fa73808a688a252639080b6751953ad7ecd8a4cbd8f23147b954d6902b31b + md5: ca46cc84466b5e05f15a4c4f263b6e80 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.16.2,<1.16.3.0a0 + - libcxx >=19 + - openssl >=3.5.5,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 167424 + timestamp: 1770345338067 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.16.0-h5446563_2.conda + sha256: 2ab2bc487d2cb985d2d45adbac7a6fe9a554bd78808268622566acb5e28fe5a2 + md5: 1ac96ad3d642a951b4576ea09ae502a3 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.16.2,<1.16.3.0a0 + - azure-storage-common-cpp >=12.13.0,<12.13.1.0a0 + - libcxx >=19 + license: MIT + license_family: MIT + purls: [] + size: 426524 + timestamp: 1778727625073 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.13.0-he467506_0.conda + sha256: bc73ce983d90baa732e6f64e4d8b4ddbb8e671c5d6e7b9475d33dbd118ddd5b6 + md5: 4cfc08976cf62fef7736a763652987cb + depends: + - __osx >=11.0 + - azure-core-cpp >=1.16.2,<1.16.3.0a0 + - libcxx >=19 + - libxml2 + - libxml2-16 >=2.14.6 + - openssl >=3.5.6,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 128808 + timestamp: 1778662321258 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.14.0-hdc9d693_2.conda + sha256: 77dde85d2c3c4c2f2a0a0cf6ac7e2b2458d60fe9a633e8fe934f0c9bfcbae168 + md5: 4dbee4ea590bf017fb7b2fba71b16b24 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.16.2,<1.16.3.0a0 + - azure-storage-blobs-cpp >=12.16.0,<12.16.1.0a0 + - azure-storage-common-cpp >=12.13.0,<12.13.1.0a0 + - libcxx >=19 + license: MIT + license_family: MIT + purls: [] + size: 198818 + timestamp: 1778764243281 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.5.0-py312h87c4bb7_0.conda + sha256: a492dcf07b1c58797b3192f11aef7e3beb18ec91646d6a5acfe5c6e61e66118d + md5: 6ec306e02579965dc9c01092a5f4ce4c + depends: + - python + - __osx >=11.0 + - zstd >=1.5.7,<1.6.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause AND MIT AND EPL-2.0 + purls: + - pkg:pypi/backports-zstd?source=compressed-mapping + size: 240840 + timestamp: 1778594074672 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/bcrypt-5.0.0-py312h6ef9ec0_1.conda + sha256: f93cb3d56e002e0c2a8e37a4a8c555aaacf2e1eeee751bd838d9f2f58b2446c4 + md5: 93bc90afdb07688a2ff63fbd11950033 + depends: + - python + - __osx >=11.0 + - python 3.12.* *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - __osx >=11.0 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/bcrypt?source=hash-mapping + size: 267217 + timestamp: 1762497792005 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/blosc-1.21.6-h7dd00d9_1.conda + sha256: c3fe902114b9a3ac837e1a32408cc2142c147ec054c1038d37aec6814343f48a + md5: 925acfb50a750aa178f7a0aced77f351 + depends: + - __osx >=11.0 + - libcxx >=18 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.1,<1.3.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 33602 + timestamp: 1733513285902 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-h7d5ae5b_1.conda + sha256: 422ac5c91f8ef07017c594d9135b7ae068157393d2a119b1908c7e350938579d + md5: 48ece20aa479be6ac9a284772827d00c + depends: + - __osx >=11.0 + - brotli-bin 1.2.0 hc919400_1 + - libbrotlidec 1.2.0 hc919400_1 + - libbrotlienc 1.2.0 hc919400_1 + license: MIT + license_family: MIT + purls: [] + size: 20237 + timestamp: 1764018058424 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-hc919400_1.conda + sha256: e2d142052a83ff2e8eab3fe68b9079cad80d109696dc063a3f92275802341640 + md5: 377d015c103ad7f3371be1777f8b584c + depends: + - __osx >=11.0 + - libbrotlidec 1.2.0 hc919400_1 + - libbrotlienc 1.2.0 hc919400_1 + license: MIT + license_family: MIT + purls: [] + size: 18628 + timestamp: 1764018033635 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py312h0dfefe5_1.conda + sha256: 6178775a86579d5e8eec6a7ab316c24f1355f6c6ccbe84bb341f342f1eda2440 + md5: 311fcf3f6a8c4eb70f912798035edd35 + depends: + - __osx >=11.0 + - libcxx >=19 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - libbrotlicommon 1.2.0 hc919400_1 + license: MIT + license_family: MIT + purls: + - pkg:pypi/brotli?source=hash-mapping + size: 359503 + timestamp: 1764018572368 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brunsli-0.1-he0dfb12_2.conda + sha256: f32d7c6285601ac3f6baf0715b225fd017702cf24260c6f7f14f7b6e721d92bc + md5: 4cfe5258439d88ce2ef0b159007ee067 + depends: + - __osx >=11.0 + - libbrotlicommon >=1.2.0,<1.3.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libcxx >=19 + license: MIT + license_family: MIT + purls: [] + size: 141089 + timestamp: 1761759272675 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda + sha256: 540fe54be35fac0c17feefbdc3e29725cce05d7367ffedfaaa1bdda234b019df + md5: 620b85a3f45526a8bc4d23fd78fc22f0 + depends: + - __osx >=11.0 + license: bzip2-1.0.6 + license_family: BSD + purls: [] + size: 124834 + timestamp: 1771350416561 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda + sha256: 2995f2aed4e53725e5efbc28199b46bf311c3cab2648fc4f10c2227d6d5fa196 + md5: bcb3cba70cf1eec964a03b4ba7775f01 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 180327 + timestamp: 1765215064054 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-blosc2-3.0.3-hf9886e1_0.conda + sha256: 186fc951441f1af61a903fcedcf4610ac0357e61313626f9cebf1eb1a0c22ab5 + md5: 9e6f81eff6c17a3edad8102faeb286a8 + depends: + - __osx >=11.0 + - libcxx >=19 + - lz4-c >=1.10.0,<1.11.0a0 + - zlib-ng >=2.3.3,<2.4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 277145 + timestamp: 1778844295797 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py312h1b4d9a2_1.conda + sha256: 597e986ac1a1bd1c9b29d6850e1cdea4a075ce8292af55568952ec670e7dd358 + md5: 503ac138ad3cfc09459738c0f5750705 + depends: + - __osx >=11.0 + - libffi >=3.5.2,<3.6.0a0 + - pycparser + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + purls: + - pkg:pypi/cffi?source=hash-mapping + size: 288080 + timestamp: 1761203317419 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cfitsio-4.6.2-h7200ff5_1.conda + sha256: 726c574996c1f61f6adc9d452ad1dae225c2a60aeacace7822b909918544960c + md5: 328455d5425d05bb8ee6171d45dad583 + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libcurl >=8.14.1,<9.0a0 + - libzlib >=1.3.1,<2.0a0 + license: LicenseRef-fitsio + purls: [] + size: 603304 + timestamp: 1753287023695 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/charls-2.4.3-hf6b4638_0.conda + sha256: 1009bd6c2bb26e41dada4015793a1edf44d1320c7ca14fc646f89b0b51236e20 + md5: 91f1daf22f72792e11382938bb0dd9ac + depends: + - __osx >=11.0 + - libcxx >=19 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 118790 + timestamp: 1772712898684 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/chealpix-3.31.0-he71fa57_8.conda + sha256: 51728bc948b9ff27cb5db7784e6058a70ee592680422e8f3cf3871ac612be4d3 + md5: d22ed62b9d072d554952364986ac9002 + depends: + - __osx >=11.0 + - cfitsio >=4.6.2,<4.6.3.0a0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 36439 + timestamp: 1756897559387 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py312h3093aea_4.conda + sha256: fa1b3967c644c1ffaf8beba3d7aee2301a8db32c0e9a56649a0e496cf3abd27c + md5: f9cce0bc86b46533489a994a47d3c7d2 + depends: + - numpy >=1.25 + - python + - python 3.12.* *_cpython + - __osx >=11.0 + - libcxx >=19 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/contourpy?source=hash-mapping + size: 286084 + timestamp: 1769156157865 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.14.0-py312h04c11ed_0.conda + sha256: f96b3c7ebd947defc940cb53c6cb508b8e53db7e21e30426e154a0b69f528192 + md5: 7568d1be300c1b10faac484fdf425241 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - tomli + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/coverage?source=hash-mapping + size: 389749 + timestamp: 1778445251509 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cryptography-48.0.0-py312h1238841_0.conda + sha256: f2560a639d9f67c53f495d75289a63b840744be04669dc949afb0e694f971fdb + md5: 7a4c2757d53e6a36c45f0eeaf50dac34 + depends: + - __osx >=11.0 + - cffi >=2.0 + - openssl >=3.5.6,<4.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - __osx >=11.0 + license: Apache-2.0 AND BSD-3-Clause AND PSF-2.0 AND MIT + license_family: BSD + purls: + - pkg:pypi/cryptography?source=hash-mapping + size: 1853060 + timestamp: 1777966201485 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cytoolz-1.1.0-py312h2bbb03f_2.conda + sha256: be8d2bf477d0bf8d19a7916c2ceccef33cbfecf918508c18b89098aa7b20017e + md5: 49389c14c49a416f458ce91491f62c67 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - toolz >=0.10.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/cytoolz?source=hash-mapping + size: 591797 + timestamp: 1771856474133 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/dav1d-1.2.1-hb547adb_0.conda + sha256: 93e077b880a85baec8227e8c72199220c7f87849ad32d02c14fb3807368260b8 + md5: 5a74cdee497e6b65173e10d94582fae6 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 316394 + timestamp: 1685695959391 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fftw-3.3.11-nompi_haf1500d_100.conda + sha256: fc6c507d7c68db156d6c8c5f6a79ca6b34c2a4c0c6222d8d4ecd0e4b97d3fd5e + md5: 58628fdc0c614982f1dafd2116916288 + depends: + - __osx >=11.0 + - libcxx >=19 + - libgfortran + - libgfortran5 >=14.3.0 + - llvm-openmp >=19.1.7 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 746873 + timestamp: 1776782373231 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.63.0-py312h04c11ed_0.conda + sha256: b78cc8df0d93ac0f0d59cb91cf54cc91effa6c124a78c8d2e8afc8011d9fb320 + md5: 77e64a600d8426c3863942f67491f5fb + depends: + - __osx >=11.0 + - brotli + - munkres + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - unicodedata2 >=15.1.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/fonttools?source=hash-mapping + size: 2904377 + timestamp: 1778771054844 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_0.conda + sha256: 5952bd9db12207a18a112e8924aa2ce8c2f9d57b62584d58a97d2f6afe1ea324 + md5: 6dcc75ba2e04c555e881b72793d3282f + depends: + - libfreetype 2.14.3 hce30654_0 + - libfreetype6 2.14.3 hdfa99f5_0 + license: GPL-2.0-only OR FTL + purls: [] + size: 173313 + timestamp: 1774298702053 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.14.1-h5afe852_0.conda + sha256: 1ac5f5a3a35f2e4778025043c87993208d336e30539406e380e0952bb7ffd188 + md5: 4238412c29eff0bb2bb5c60a720c035a + depends: + - __osx >=11.0 + - libcxx >=19 + license: LGPL-2.1-only + purls: [] + size: 1530844 + timestamp: 1761594597236 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda + sha256: fd56ed8a1dab72ab90d8a8929b6f916a6d9220ca297ff077f8f04c5ed3408e20 + md5: 57a511a5905caa37540eb914dfcbf1fb + depends: + - __osx >=11.0 + - libcxx >=17 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 82090 + timestamp: 1726600145480 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-h93a5062_0.conda + sha256: 843b3f364ff844137e37d5c0a181f11f6d51adcedd216f019d074e5aa5d7e09c + md5: 95fa1486c77505330c20f7202492b913 + license: MIT + license_family: MIT + purls: [] + size: 71613 + timestamp: 1712692611426 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda + sha256: 9fc77de416953aa959039db72bc41bfa4600ae3ff84acad04a7d0c1ab9552602 + md5: fef68d0a95aa5b84b5c1a4f6f3bf40e1 + depends: + - __osx >=11.0 + - gflags >=2.2.2,<2.3.0a0 + - libcxx >=16 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 112215 + timestamp: 1718284365403 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/google-crc32c-1.8.0-py312h090f823_1.conda + sha256: cf6c1345d2c4fb4cee1128256d3a1d3fe5150c8788bc6cad12a04cbfc44bb247 + md5: 0c8ad601cdbec3be85d1c62080b388d7 + depends: + - __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 + size: 25105 + timestamp: 1768549598713 +- 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 + purls: [] + size: 2734398 + timestamp: 1626369562748 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.13.0-nompi_py312hd7c5113_100.conda + sha256: 1cfd7bd567c2d8cd4c532f88c342f9346867ef52dab9d65ec1089c90eb94b6e4 + md5: 227b0c53bc3d4131996bf7de479fe185 + depends: + - __osx >=11.0 + - cached-property + - hdf5 >=1.14.3,<1.14.4.0a0 + - numpy >=1.19,<3 + - 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/h5py?source=hash-mapping + size: 1237897 + timestamp: 1739953225445 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-1.14.3-nompi_ha698983_109.conda + sha256: daba95bd449b77c8d092458f8561d79ef96f790b505c69c17f5425c16ee16eca + md5: be8bf1f5aabe7b5486ccfe5a3cc8bbfe + depends: + - __osx >=11.0 + - libaec >=1.1.3,<2.0a0 + - libcurl >=8.11.1,<9.0a0 + - libcxx >=18 + - libgfortran >=5 + - libgfortran5 >=13.2.0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.4.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 3483256 + timestamp: 1737516321575 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/healpy-1.18.1-py312hb9c4046_2.conda + sha256: ba0d6a163e563318bcd4a4af3e43ca25c8a0dc4a547b9e95abaec366a57cd5a2 + md5: 2fd5a29c42ecd67947508a983320d732 + depends: + - __osx >=11.0 + - astropy-base + - cfitsio >=4.6.2,<4.6.3.0a0 + - libcxx >=19 + - libgfortran + - libgfortran5 >=14.3.0 + - libgfortran5 >=15.1.0 + - libzlib >=1.3.1,<2.0a0 + - matplotlib-base + - numpy >=1.23,<3 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - scipy + license: GPL-2.0-only + license_family: GPL + purls: + - pkg:pypi/healpy?source=hash-mapping + size: 2673601 + timestamp: 1757172667824 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/htcondor-24.12.4-py312h1f38498_0.conda + sha256: 63fed07cf9d3acb89e50ccc2d8431e24bf4288077ace111e6e4ccee0c3e96b83 + md5: fae86127c716727738212baa03336609 + depends: + - htcondor-classads 24.12.4 h0c2a548_0 + - htcondor-cli 24.12.4 py312h81bd7bf_0 + - htcondor-utils 24.12.4 h9e88eaa_0 + - libcondor_utils 24.12.4 h4785e8b_0 + - python >=3.12,<3.13.0a0 + - python-htcondor 24.12.4 py312hd1c112e_0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 23644 + timestamp: 1759419459906 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/htcondor-25.10.1-py312h1f38498_0.conda + sha256: 6db47eb6ecd5cf8a96dfbf3fefd48cbb5101f07841284d40725bfd7ddf84eea3 + md5: 827f308526f68f37574077945474e411 + depends: + - htcondor-classads 25.10.1 h7a491f7_0 + - htcondor-cli 25.10.1 py312h81bd7bf_0 + - htcondor-utils 25.10.1 h91f37e5_0 + - libcondor_utils 25.10.1 h3669c8c_0 + - python >=3.12,<3.13.0a0 + - python-htcondor 25.10.1 py312hacc1691_0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 22455 + timestamp: 1778794634309 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/htcondor-classads-24.12.4-h0c2a548_0.conda + sha256: 1b659bfdc9e9ad01d23939e82631da8e5cbe15ea6ef664545b36a3b4395a6098 + md5: 58c51c6f0c9941cd15ba8f7dd59738ff + depends: + - __osx >=11.0 + - libcxx >=19 + - pcre2 >=10.46,<10.47.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 2571572 + timestamp: 1759416840783 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/htcondor-classads-25.10.1-h7a491f7_0.conda + sha256: f9dded4f282d61e9b796c8e039deac96f919f146a0bf8ea0f20cb56a10e1a4ee + md5: ef73fd05e0f7f6ce50a68e94dd41dcc8 + depends: + - __osx >=11.0 + - libcxx >=19 + - pcre2 >=10.47,<10.48.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 2598071 + timestamp: 1778793318636 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/htcondor-cli-24.12.4-py312h81bd7bf_0.conda + sha256: fbdfe5acaf661052479db8dceed9e8e752d307a191cf097ed2d4b00a9d710a47 + md5: 9bf38bd6a00570d4a2e877076d784e41 + depends: + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-htcondor 24.12.4 py312hd1c112e_0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/htcondor-cli?source=hash-mapping + size: 155395 + timestamp: 1759419263227 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/htcondor-cli-25.10.1-py312h81bd7bf_0.conda + sha256: f4346d299fd60bfe19a5903084f19ad3892e77dbc05e7b65a7b98daf036a839b + md5: 53f1247a5309ac33e1a1357fa709498e + depends: + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-htcondor 25.10.1 py312hacc1691_0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/htcondor-cli?source=hash-mapping + size: 175610 + timestamp: 1778794521864 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/htcondor-utils-24.12.4-h9e88eaa_0.conda + sha256: 041970a055fd3d3fa562ad976600cab0a6b1ec4236a05ea3c0d8aaefb0612880 + md5: ce4b559c3f286459ab51508249c7d44f + depends: + - __osx >=11.0 + - htcondor-classads 24.12.4 h0c2a548_0 + - libcondor_utils 24.12.4 h4785e8b_0 + - libcurl >=8.14.1,<9.0a0 + - libcxx >=19 + - openssl >=3.5.4,<4.0a0 + - scitokens-cpp >=1.1.3,<2.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 1419497 + timestamp: 1759416982079 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/htcondor-utils-25.10.1-h91f37e5_0.conda + sha256: 466bbe8f98cb9c83baaed1df09964f8c52f7890afdf29190d3270b79d7aa87f9 + md5: cd5a3d4997f1d8dd243f77bc5c7cacd0 + depends: + - __osx >=11.0 + - htcondor-classads 25.10.1 h7a491f7_0 + - libcondor_utils 25.10.1 h3669c8c_0 + - libcurl >=8.20.0,<9.0a0 + - libcxx >=19 + - openssl >=3.5.6,<4.0a0 + - scitokens-cpp >=1.4.0,<2.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 1470703 + timestamp: 1778793422810 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/htgettoken-2.6-py312h81bd7bf_0.conda + sha256: dfae2e5a163598ae6a6b6327ac484b7d153d6f8f3a7612468083fbd1f5cedc51 + md5: 6b9da215275c8c9575decbdf45ebefce + depends: + - jq + - paramiko + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-gssapi + - python_abi 3.12.* *_cp312 + - urllib3 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/htgettoken?source=hash-mapping + size: 53052 + timestamp: 1768653800550 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda + sha256: 9ba12c93406f3df5ab0a43db8a4b4ef67a5871dfd401010fbe29b218b2cbe620 + md5: 5eb22c1d7b3fc4abb50d92d621583137 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 11857802 + timestamp: 1720853997952 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/igwn-ligolw-2.1.1-py312h8d938ae_1.conda + sha256: 401c6eaf3696a6123a50d9e1f9b1fc610f8593194ff4797271f0fcf3432251e9 + md5: 001055872cdbc6a9f946f875e18e4903 + depends: + - igwn-segments + - numpy + - python + - python-dateutil + - pyyaml + - tqdm + - python 3.12.* *_cpython + - __osx >=11.0 + - python_abi 3.12.* *_cp312 + license: GPL-3.0-or-later + license_family: GPL + purls: + - pkg:pypi/igwn-ligolw?source=hash-mapping + size: 2423284 + timestamp: 1771054437583 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/igwn-segments-2.1.1-py312h8d938ae_2.conda + sha256: 1f2263245db006e8f44e0946af0ba04ec1fb62964f98e6d48e754d0fd82062df + md5: dc719a38bf0dd2214e823f0d282cca94 + depends: + - python + - python 3.12.* *_cpython + - __osx >=11.0 + - python_abi 3.12.* *_cp312 + license: GPL-3.0-or-later + license_family: GPL + purls: + - pkg:pypi/igwn-segments?source=hash-mapping + size: 96759 + timestamp: 1770797922959 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/imagecodecs-2026.5.10-py312ha5a633e_0.conda + sha256: ff598bd8f3cdb3b0112b6b025a3f0293581c051d25905385842292bb36c48613 + md5: bb35a78bff932bc41fe85075560f6b17 + depends: + - __osx >=11.0 + - blosc >=1.21.6,<2.0a0 + - brunsli >=0.1,<1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - c-blosc2 >=3.0.2,<3.1.0a0 + - charls >=2.4.3,<2.5.0a0 + - giflib >=5.2.2,<5.3.0a0 + - jxrlib >=1.1,<1.2.0a0 + - lcms2 >=2.19.1,<3.0a0 + - lerc >=4.1.0,<5.0a0 + - libaec >=1.1.5,<2.0a0 + - libavif16 >=1.4.1,<2.0a0 + - libbrotlicommon >=1.2.0,<1.3.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libcxx >=19 + - libdeflate >=1.25,<1.26.0a0 + - libjpeg-turbo >=3.1.4.1,<4.0a0 + - libjxl >=0.11,<1.0a0 + - liblzma >=5.8.3,<6.0a0 + - libpng >=1.6.58,<1.7.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - libzopfli >=1.0.3,<1.1.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - numpy >=1.23,<3 + - openjpeg >=2.5.4,<3.0a0 + - openjph >=0.27.2,<0.28.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - snappy >=1.2.2,<1.3.0a0 + - zfp >=1.0.1,<2.0a0 + - zlib-ng >=2.3.3,<2.4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/imagecodecs?source=hash-mapping + size: 1831777 + timestamp: 1778501666198 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/jq-1.8.1-hbc156a2_0.conda + sha256: 4c66141af08a9b1019345e800c5b1bcf005614167e35edee9977034371acce4a + md5: ab406f399e1becf7b51b74510e332f3d + depends: + - oniguruma 6.9.* + - __osx >=11.0 + - oniguruma >=6.9.10,<6.10.0a0 + license: MIT + license_family: MIT + purls: [] + size: 338907 + timestamp: 1751447320937 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/jxrlib-1.1-h93a5062_3.conda + sha256: c9e0d3cf9255d4585fa9b3d07ace3bd934fdc6a67ef4532e5507282eff2364ab + md5: 879997fd868f8e9e4c2a12aec8583799 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 197843 + timestamp: 1703334079437 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.0-py312h3093aea_0.conda + sha256: 8de440f0e33ab6895e81f2c47c51e59d177349a832087a0367e8e259c97f4833 + md5: 58261af35f0d33fd28e2257b208a1be0 + depends: + - python + - __osx >=11.0 + - libcxx >=19 + - python 3.12.* *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/kiwisolver?source=hash-mapping + size: 68490 + timestamp: 1773067215781 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda + sha256: 4442f957c3c77d69d9da3521268cad5d54c9033f1a73f99cde0a3658937b159b + md5: c6dc8a0fdec13a0565936655c33069a1 + depends: + - __osx >=11.0 + - libcxx >=16 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 1155530 + timestamp: 1719463474401 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h385eeb1_0.conda + sha256: c0a0bf028fe7f3defcdcaa464e536cf1b202d07451e18ad83fdd169d15bef6ed + md5: e446e1822f4da8e5080a9de93474184d + depends: + - __osx >=11.0 + - libcxx >=19 + - libedit >=3.1.20250104,<3.2.0a0 + - libedit >=3.1.20250104,<4.0a0 + - openssl >=3.5.5,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 1160828 + timestamp: 1769770119811 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lal-7.6.1-fftw_py312h3b12eeb_100.conda + sha256: 14884ed07d8ab02d54147e43d046a49da9e1b8b082eef5bebead22c4e1b22088 + md5: 0510b0a2d869f03e59d1aec2da647564 + depends: + - __osx >=11.0 + - fftw >=3.3.10,<4.0a0 + - liblal 7.6.1 fftw_h8fcc6d2_100 + - ligo-segments + - numpy + - python >=3.12,<3.13.0a0 + - python-lal 7.6.1 fftw_py312h0f8b06b_100 + - python-ligo-lw + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 42812 + timestamp: 1734696174074 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalapps-10.0.2-py312he91bef5_2.conda + sha256: 3058537f364b7681266063ba95d327363216866a310b91b0e57558b59308989a + md5: 9a48d8d33b912e28bc5bb1b92cfd909c + depends: + - __osx >=11.0 + - cfitsio >=4.6.2,<4.6.3.0a0 + - gsl >=2.7,<2.8.0a0 + - h5py + - lal >=7.6.0 + - lalburst >=2.0.0 + - lalframe >=3.0.0 + - lalinference >=4.1.0 + - lalinspiral >=5.0.0 + - lalmetaio >=4.0.0 + - lalpulsar >=7.1.0 + - lalsimulation >=6.1.0 + - libframel >=8.39.2 + - libframel >=8.41.3,<9.0a0 + - liblal >=7.6.1,<8.0a0 + - liblalburst >=2.0.6,<3.0a0 + - liblalframe >=3.0.6,<4.0a0 + - liblalinference >=4.1.8,<5.0a0 + - liblalinspiral >=5.0.2,<6.0a0 + - liblalmetaio >=4.0.5,<5.0a0 + - liblalpulsar >=7.1.0,<8.0a0 + - liblalsimulation >=6.1.0,<7.0a0 + - libmetaio >=8.4.0 + - libmetaio >=8.5.1,<9.0a0 + - ligo-segments + - numpy + - pillow + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-ligo-lw >=1.7.0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 1322073 + timestamp: 1744228891647 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalburst-2.0.6-py312h028adb6_0.conda + sha256: 8e28495392bfc5ed7548b38c095971bfd84f26cd7cb8b0bea82996230abad376 + md5: 56a0d78b7dc88c2936fbd731d2d4cc28 + depends: + - __osx >=11.0 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalburst 2.0.6 h2786bc8_0 + - pillow + - python >=3.12,<3.13.0a0 + - python-lalburst 2.0.6 py312heec191f_0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 49537 + timestamp: 1734709596122 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalframe-3.0.6-py312h028adb6_0.conda + sha256: f7dd96310aa41565b6e4fce4c2b1191cfe955781f9722f7036100ebd37a13f44 + md5: 4d86373b3509a0990fc3494b05d86c7e + depends: + - __osx >=11.0 + - liblal >=7.6.0,<8.0a0 + - liblalframe 3.0.6 h63b17c2_0 + - python >=3.12,<3.13.0a0 + - python-lalframe 3.0.6 py312he0011b7_0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 308737 + timestamp: 1734697021711 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalinference-4.1.8-nompi_py312h7a6eb69_100.conda + sha256: 03408cb82906fe75c6806afb234398c7a0302719081db232d38d3f7997eb7918 + md5: 083daf6498eb015a6a6359f715e2900e + depends: + - __osx >=11.0 + - astropy-base >=1.1.1 + - h5py + - icu >=75.1,<76.0a0 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalinference 4.1.8 h4d5f05e_0 + - ligo-gracedb + - lscsoft-glue >=1.54.1 + - matplotlib-base >=1.2.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-lal >=7.6.0 + - python-lalinference 4.1.8 py312he0011b7_0 + - python-lalsimulation >=6.1.0 + - python-ligo-lw >=1.7.0 + - python_abi 3.12.* *_cp312 + - scipy >=0.9.0 + - six + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 101447 + timestamp: 1734715099879 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalinference-data-4.1.8-hce30654_0.conda + sha256: 7ef6596c565ec69447010f699e8b9ef55a0a35f2ed170a0c96015cf4677b9303 + md5: 2b82445d316e9f22313d2ffc0f183e17 + constrains: + - liblalinference >=3.0.3 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 32108 + timestamp: 1734713004583 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalinspiral-5.0.2-py312h028adb6_0.conda + sha256: fe398f3ee67a51aebbb1c882cdd75764a9c2b5361d3df688a21f23b9425d6629 + md5: a525c908ada3defa68e5a48149ae01cb + depends: + - __osx >=11.0 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalinspiral 5.0.2 h2786bc8_0 + - ligo-segments + - python >=3.12,<3.13.0a0 + - python-lalinspiral 5.0.2 py312he0011b7_0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 22614 + timestamp: 1734710613786 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalmetaio-4.0.5-py312h028adb6_2.conda + sha256: f6aa0e2b9c005979015df9010e677d17d54f053454523880dd7f98ddabe70930 + md5: a3163013d4affadfa028e578f674fe1c + depends: + - __osx >=11.0 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalmetaio 4.0.5 h5505292_2 + - python >=3.12,<3.13.0a0 + - python-lalmetaio 4.0.5 py312he0011b7_2 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 18308 + timestamp: 1736418219629 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalpulsar-7.1.0-py312hf4cc3a4_1.conda + sha256: 7f82d724a3b621a98c3095ccbbb58ac675904e0939993c322f5e209abbaba580 + md5: 8fadeded4c0a24b9c9ec9b5e9b196d4c + depends: + - __osx >=11.0 + - astropy-base + - cfitsio >=4.6.2,<4.6.3.0a0 + - fftw >=3.3.10,<4.0a0 + - gsl >=2.7,<2.8.0a0 + - jplephem + - lalinference >=4.1.0 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalframe >=3.0.0 + - liblalframe >=3.0.6,<4.0a0 + - liblalinference >=4.1.0 + - liblalinference >=4.1.8,<5.0a0 + - liblalpulsar 7.1.0 h8480f09_1 + - liblalsimulation >=6.1.0 + - liblalsimulation >=6.1.0,<7.0a0 + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python-lal >=7.6.0 + - python-lalframe >=3.0.0 + - python-lalinference >=4.1.0 + - python-lalpulsar 7.1.0 py312he0011b7_1 + - python-lalsimulation >=6.1.0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 653367 + timestamp: 1744215378338 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalpulsar-data-7.1.0-hce30654_1.conda + sha256: 80acda84aaaea5045486111c4bec60cc9e22bd3c7fa5eba01e1c98052e6406aa + md5: 34b154e4bc255a942c1f3fe53e9efa8e + constrains: + - liblalpulsar >=4.0.0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 90030025 + timestamp: 1744214436352 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalsimulation-6.1.0-py312hd4754c9_0.conda + sha256: 70d542489f864b087044934aa45f3971851a268dfc8564bb1cdec2db2544c8d8 + md5: d0b8abfd975ece415a345bc330bf8d6f + depends: + - __osx >=11.0 + - gsl >=2.7,<2.8.0a0 + - liblal >=7.6.0 + - liblal >=7.6.0,<8.0a0 + - liblalsimulation 6.1.0 py312h0975e73_0 + - mpmath >=1.0.0 + - python >=3.12,<3.13.0a0 + - python-lalsimulation 6.1.0 py312he758b9a_0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 71296 + timestamp: 1734696545599 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lalsimulation-data-6.1.0-hce30654_0.conda + sha256: b4026b84c03559d4d8343bf8978833bcb431b02e563f06e46e78e1aaa3cebd2d + md5: 8f7cbff88ba74ed2437cf57902a84eb4 + constrains: + - liblalsimulation >=3.1.2 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 3673142 + timestamp: 1734695809215 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.19.1-hdfa7624_0.conda + sha256: d589ff5294e42576563b22bdea0860cb80b0cbe0f3852836eddaadedf6eec4ef + md5: e5ba982008c0ac1a1c0154617371bab5 + depends: + - __osx >=11.0 + - libjpeg-turbo >=3.1.4.1,<4.0a0 + - libtiff >=4.7.1,<4.8.0a0 + license: MIT + license_family: MIT + purls: [] + size: 212998 + timestamp: 1778079809873 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ldas-tools-al-2.7.0-he7ec6a4_3.conda + sha256: 7ab52ef245f80b6e4788dbbe3887e71c001ee8bcc213512a91615c93ee372834 + md5: 6cbbac2a6747c712d59ac1ad86e5476d + depends: + - __osx >=11.0 + - libboost >=1.88.0,<1.89.0a0 + - libcxx >=19 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 269293 + timestamp: 1764933123930 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ldas-tools-framecpp-2.9.3-ha7f91e6_4.conda + sha256: 78afd30c0e75e7f9b6d4e2b11846f04964d11aecc0cc08614fcd7a83f0f4cdbb + md5: 394320b801104d9b7a3080906fd19dec + depends: + - __osx >=11.0 + - ldas-tools-al >=2.6.7 + - ldas-tools-al >=2.7.0,<2.8.0a0 + - libboost >=1.88.0,<1.89.0a0 + - libcxx >=19 + - libzlib >=1.3.1,<2.0a0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 1541492 + timestamp: 1756906717246 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.1.0-h1eee2c3_0.conda + sha256: 66e5ffd301a44da696f3efc2f25d6d94f42a9adc0db06c44ad753ab844148c51 + md5: 095e5749868adab9cae42d4b460e5443 + depends: + - __osx >=11.0 + - libcxx >=19 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 164222 + timestamp: 1773114244984 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260107.1-cxx17_h2062a1b_0.conda + sha256: 756611fbb8d2957a5b4635d9772bd8432cb6ddac05580a6284cca6fdc9b07fca + md5: bb65152e0d7c7178c0f1ee25692c9fd1 + depends: + - __osx >=11.0 + - libcxx >=19 + constrains: + - abseil-cpp =20260107.1 + - libabseil-static =20260107.1=cxx17* + license: Apache-2.0 + license_family: Apache + purls: [] + size: 1229639 + timestamp: 1770863511331 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.5-h8664d51_0.conda + sha256: af9cd8db11eb719e38a3340c88bb4882cf19b5b4237d93845224489fc2a13b46 + md5: 13e6d9ae0efbc9d2e9a01a91f4372b41 + depends: + - __osx >=11.0 + - libcxx >=19 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 30390 + timestamp: 1769222133373 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-23.0.1-h5390cfe_4_cpu.conda + build_number: 4 + sha256: bee77acf20b51d0f5ff81076ce2e0c922085696175dd2dc2b15ce22aeae15fa0 + md5: bbbd069f104614c8ea37bc181fb628f8 + depends: + - __osx >=11.0 + - aws-crt-cpp >=0.37.3,<0.37.4.0a0 + - aws-sdk-cpp >=1.11.747,<1.11.748.0a0 + - azure-core-cpp >=1.16.2,<1.16.3.0a0 + - azure-identity-cpp >=1.13.3,<1.13.4.0a0 + - azure-storage-blobs-cpp >=12.16.0,<12.16.1.0a0 + - azure-storage-files-datalake-cpp >=12.14.0,<12.14.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libcxx >=21 + - libgoogle-cloud >=2.39.0,<2.40.0a0 + - libgoogle-cloud-storage >=2.39.0,<2.40.0a0 + - libopentelemetry-cpp >=1.21.0,<1.22.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.3.0,<2.3.1.0a0 + - snappy >=1.2.2,<1.3.0a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - arrow-cpp <0.0a0 + - parquet-cpp <0.0a0 + - apache-arrow-proc =*=cpu + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 4244946 + timestamp: 1773268061189 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-24.0.0-h4c70b79_2_cpu.conda + build_number: 2 + sha256: 5055e2568d835e7d2343047a9462aa3ec8f2ca806829a4b7c2a895671e322e6a + md5: 8a2c3ba1a7579f5e537ae90d2ddae2ae + depends: + - __osx >=11.0 + - aws-crt-cpp >=0.38.3,<0.38.4.0a0 + - aws-sdk-cpp >=1.11.747,<1.11.748.0a0 + - azure-core-cpp >=1.16.2,<1.16.3.0a0 + - azure-identity-cpp >=1.13.3,<1.13.4.0a0 + - azure-storage-blobs-cpp >=12.16.0,<12.16.1.0a0 + - azure-storage-files-datalake-cpp >=12.14.0,<12.14.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libcxx >=21 + - libgoogle-cloud >=3.5.0,<3.6.0a0 + - libgoogle-cloud-storage >=3.5.0,<3.6.0a0 + - libopentelemetry-cpp >=1.26.0,<1.27.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libzlib >=1.3.2,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.3.0,<2.3.1.0a0 + - snappy >=1.2.2,<1.3.0a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - arrow-cpp <0.0a0 + - apache-arrow-proc =*=cpu + - parquet-cpp <0.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 4236842 + timestamp: 1779475409157 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-23.0.1-hbf36091_4_cpu.conda + build_number: 4 + sha256: 84e1352258e1258dc499cc8b4cb860f1c4a6708070723f45fb6ec159c2bd78c8 + md5: 660c265fd29448a1a6dd9477a961d94c + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libarrow 23.0.1 h5390cfe_4_cpu + - libarrow-compute 23.0.1 h4dbefc3_4_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.21.0,<1.22.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 540045 + timestamp: 1773268413763 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-24.0.0-hee8fe31_2_cpu.conda + build_number: 2 + sha256: c63c219630454d6a9a4c54ae6d5a770c94a6702343fddd55b154731426d46456 + md5: 4e27bf26634f174f03e1d16091d19f63 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libarrow 24.0.0 h4c70b79_2_cpu + - libarrow-compute 24.0.0 h3b6a98a_2_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.26.0,<1.27.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 520430 + timestamp: 1779475832626 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-compute-23.0.1-h4dbefc3_4_cpu.conda + build_number: 4 + sha256: a90c7caa41c6879f4e09841b8af528b72319ff194eb0e2ccd43b4153e320028b + md5: 7429882c9878a30891426c5ddff1a313 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libarrow 23.0.1 h5390cfe_4_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.21.0,<1.22.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libre2-11 >=2025.11.5 + - libutf8proc >=2.11.3,<2.12.0a0 + - re2 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 2256372 + timestamp: 1773268168954 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-compute-24.0.0-h3b6a98a_2_cpu.conda + build_number: 2 + sha256: e1e8d6dc8f260d47c01c920609d0b3900cf6f1f901faf3fab1108e1903fb4abd + md5: 9f50362294e8a49c13fe403caf8a2931 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libarrow 24.0.0 h4c70b79_2_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.26.0,<1.27.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libre2-11 >=2025.11.5 + - libutf8proc >=2.11.3,<2.12.0a0 + - re2 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 2243118 + timestamp: 1779475555728 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-23.0.1-hbf36091_4_cpu.conda + build_number: 4 + sha256: 2b9fda07d3f4eab30131f88d3a1fb6dd1a576515ccb131fad81c61b8f4765894 + md5: ab67f4e519042b1638e638297ac37d4e + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libarrow 23.0.1 h5390cfe_4_cpu + - libarrow-acero 23.0.1 hbf36091_4_cpu + - libarrow-compute 23.0.1 h4dbefc3_4_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.21.0,<1.22.0a0 + - libparquet 23.0.1 h7a13205_4_cpu + - libprotobuf >=6.33.5,<6.33.6.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 536312 + timestamp: 1773268563348 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-24.0.0-hee8fe31_2_cpu.conda + build_number: 2 + sha256: 4fa7d1ba6ad82864a1ffaaa933d4e8fbb9b8efe07f496ee95b7f74fe2e4b09fa + md5: 044c3c020777f44d95a2326d4615e75f + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libarrow 24.0.0 h4c70b79_2_cpu + - libarrow-acero 24.0.0 hee8fe31_2_cpu + - libarrow-compute 24.0.0 h3b6a98a_2_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.26.0,<1.27.0a0 + - libparquet 24.0.0 h16c0493_2_cpu + - libprotobuf >=6.33.5,<6.33.6.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 519730 + timestamp: 1779476031364 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-23.0.1-h05be00f_4_cpu.conda + build_number: 4 + sha256: 2224ddad25b073b72c0b92c94fa18de2857369ed8590c2ec187844bfec1fc5bc + md5: b535b4df80b502a3da04aeccc797a747 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libarrow 23.0.1 h5390cfe_4_cpu + - libarrow-acero 23.0.1 hbf36091_4_cpu + - libarrow-dataset 23.0.1 hbf36091_4_cpu + - libcxx >=21 + - libprotobuf >=6.33.5,<6.33.6.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 471801 + timestamp: 1773268633092 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-24.0.0-h05be00f_2_cpu.conda + build_number: 2 + sha256: a860ea929d23b02085ff4894498b01a20f9e9b06cb2afc4cc7d2554dd14fbe6e + md5: b600071368fbdf4d6fddbe0beee56aef + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libarrow 24.0.0 h4c70b79_2_cpu + - libarrow-acero 24.0.0 hee8fe31_2_cpu + - libarrow-dataset 24.0.0 hee8fe31_2_cpu + - libcxx >=21 + - libprotobuf >=6.33.5,<6.33.6.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 454958 + timestamp: 1779476106037 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libavif16-1.4.1-hfce71f6_0.conda + sha256: 09e31e51026a3b74d947ba4b30a68dd99013aeef2860bcb03565bf43cad18da6 + md5: 2df04ee54a2ce2d34cf375eb02a63725 + depends: + - __osx >=11.0 + - aom >=3.9.1,<3.10.0a0 + - dav1d >=1.2.1,<1.2.2.0a0 + - rav1e >=0.8.1,<0.9.0a0 + - svt-av1 >=4.0.1,<4.0.2.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 118959 + timestamp: 1774043016600 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-7_h51639a9_openblas.conda + build_number: 7 + sha256: 662935bfb93d2d097e26e273a3a2f504a9f833f64aa6f9e295b577655478c39b + md5: ab6670d099d19fe70cb9efb88a1b5f78 + depends: + - libopenblas >=0.3.33,<0.3.34.0a0 + - libopenblas >=0.3.33,<1.0a0 + constrains: + - libcblas 3.11.0 7*_openblas + - blas 2.307 openblas + - mkl <2027 + - liblapack 3.11.0 7*_openblas + - liblapacke 3.11.0 7*_openblas + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 18783 + timestamp: 1778489983152 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libboost-1.88.0-h18cd856_6.conda + sha256: 717fbfa249f14fb1c6ce564cd0f242460cbc1703b584ad4063366ee349b22325 + md5: 605e24dba1de926ae54fc01ab557dfa8 + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - icu >=75.1,<76.0a0 + - libcxx >=19 + - liblzma >=5.8.1,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - boost-cpp <0.0a0 + license: BSL-1.0 + purls: [] + size: 1954928 + timestamp: 1763019429173 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libboost-python-1.88.0-py312h4c080bd_6.conda + sha256: 9d87069c3d7e620adeea40e37571615b55aa89f9764391a2dd8b1feb8db196ff + md5: 26ae282b8a00ce47f039d58c4a6df53d + depends: + - __osx >=11.0 + - libboost 1.88.0 h18cd856_6 + - libcxx >=19 + - numpy >=1.23,<3 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - py-boost <0.0a0 + - boost <0.0a0 + license: BSL-1.0 + purls: [] + size: 107106 + timestamp: 1763020313841 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-hc919400_1.conda + sha256: a7cb9e660531cf6fbd4148cff608c85738d0b76f0975c5fc3e7d5e92840b7229 + md5: 006e7ddd8a110771134fcc4e1e3a6ffa + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 79443 + timestamp: 1764017945924 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-hc919400_1.conda + sha256: 2eae444039826db0454b19b52a3390f63bfe24f6b3e63089778dd5a5bf48b6bf + md5: 079e88933963f3f149054eec2c487bc2 + depends: + - __osx >=11.0 + - libbrotlicommon 1.2.0 hc919400_1 + license: MIT + license_family: MIT + purls: [] + size: 29452 + timestamp: 1764017979099 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-hc919400_1.conda + sha256: 01436c32bb41f9cb4bcf07dda647ce4e5deb8307abfc3abdc8da5317db8189d1 + md5: b2b7c8288ca1a2d71ff97a8e6a1e8883 + depends: + - __osx >=11.0 + - libbrotlicommon 1.2.0 hc919400_1 + license: MIT + license_family: MIT + purls: [] + size: 290754 + timestamp: 1764018009077 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-7_hb0561ab_openblas.conda + build_number: 7 + sha256: 3ac3d27022b3ca8b1980c087e0ede250434f6ed90a4fdc78a8a5ed382bc75505 + md5: 189b373453ec3904095dcb16f502bace + depends: + - libblas 3.11.0 7_h51639a9_openblas + constrains: + - blas 2.307 openblas + - liblapack 3.11.0 7*_openblas + - liblapacke 3.11.0 7*_openblas + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 18810 + timestamp: 1778489991330 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcondor_utils-24.12.4-h4785e8b_0.conda + sha256: 25ec338070e98dcaac8914918c816696589ea361c6d1a0506e727d41157c5f1e + md5: c946d31727e6fe3d3330b7b37743cca4 + depends: + - __osx >=11.0 + - htcondor-classads 24.12.4 h0c2a548_0 + - krb5 >=1.21.3,<1.22.0a0 + - libcxx >=19 + - llvm-openmp >=19.1.7 + - openssl >=3.5.4,<4.0a0 + - pcre2 >=10.46,<10.47.0a0 + - scitokens-cpp >=0.5.0 + - scitokens-cpp >=1.1.3,<2.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 1824828 + timestamp: 1759416895146 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcondor_utils-25.10.1-h3669c8c_0.conda + sha256: 2aa01b6a844aed635c5d5b88c0b7c39ea1c95cf959d283d9dcc3fb84fb00ed6e + md5: 5d0785219d97eeb8ca8a12646f834d33 + depends: + - __osx >=11.0 + - htcondor-classads 25.10.1 h7a491f7_0 + - krb5 >=1.22.2,<1.23.0a0 + - libcxx >=19 + - llvm-openmp >=19.1.7 + - openssl >=3.5.6,<4.0a0 + - pcre2 >=10.47,<10.48.0a0 + - scitokens-cpp >=0.5.0 + - scitokens-cpp >=1.4.0,<2.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 1878082 + timestamp: 1778793357885 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 + sha256: 58477b67cc719060b5b069ba57161e20ba69b8695d154a719cb4b60caf577929 + md5: 32bd82a6a625ea6ce090a81c3d34edeb + depends: + - libcxx >=11.1.0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 18765 + timestamp: 1633683992603 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.18.0-he38603e_0.conda + sha256: 11c78b3e89bc332933386f0a11ac60d9200afb7a811b9e3bec98aef8d4a6389b + md5: 36190179a799f3aee3c2d20a8a2b970d + depends: + - __osx >=11.0 + - krb5 >=1.21.3,<1.22.0a0 + - libnghttp2 >=1.67.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.4,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: curl + license_family: MIT + purls: [] + size: 402681 + timestamp: 1767822693908 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.20.0-hd5a2499_0.conda + sha256: 38c0bc634b61e542776e97cfd15d5d41edd304d4e47c333004d2d622439b2381 + md5: 2f57b7d0c6adda88957586b7afd78438 + depends: + - __osx >=11.0 + - krb5 >=1.22.2,<1.23.0a0 + - libnghttp2 >=1.68.1,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.6,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: curl + license_family: MIT + purls: [] + size: 400568 + timestamp: 1777462251987 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.6-h55c6f16_0.conda + sha256: 3e2f8ad32ddab88c5114b9aa2160f8c129f515df0e551d0d86ef5744446afdbd + md5: 589cc6f6222fdc0eaf8e90bc38fcce7b + depends: + - __osx >=11.0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 570038 + timestamp: 1779253025527 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-hc11a715_0.conda + sha256: 5e0b6961be3304a5f027a8c00bd0967fc46ae162cffb7553ff45c70f51b8314c + md5: a6130c709305cd9828b4e1bd9ba0000c + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 55420 + timestamp: 1761980066242 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda + sha256: 66aa216a403de0bb0c1340a88d1a06adaff66bae2cfd196731aa24db9859d631 + md5: 44083d2d2c2025afca315c7a172eab2b + depends: + - ncurses + - __osx >=11.0 + - ncurses >=6.5,<7.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 107691 + timestamp: 1738479560845 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda + sha256: 95cecb3902fbe0399c3a7e67a5bed1db813e5ab0e22f4023a5e0f722f2cc214f + md5: 36d33e440c31857372a72137f78bacf5 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 107458 + timestamp: 1702146414478 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda + sha256: 8c136d7586259bb5c0d2b913aaadc5b9737787ae4f40e3ad1beaf96c80b919b7 + md5: 1a109764bff3bdc7bdd84088347d71dc + depends: + - openssl >=3.1.1,<4.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 368167 + timestamp: 1685726248899 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_0.conda + sha256: 3133fb6bfa871288b92c8b8752696686a841bf4ffe035aa3038033c9e15b738e + md5: ef22e9ab1dc7c2f334252f565f90b3b8 + depends: + - __osx >=11.0 + constrains: + - expat 2.8.1.* + license: MIT + license_family: MIT + purls: [] + size: 69110 + timestamp: 1779278728511 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda + sha256: 6686a26466a527585e6a75cc2a242bf4a3d97d6d6c86424a441677917f28bec7 + md5: 43c04d9cb46ef176bb2a4c77e324d599 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 40979 + timestamp: 1769456747661 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libframel-8.48.5-h2f1a0bb_0.conda + sha256: c4b320b091db4041c59118a18a8f653b862590fdcb5555a6ca7e006ac67afb4d + md5: cd113dfd259676215493a7a0dd227fed + depends: + - __osx >=11.0 + license: LGPL-2.1-or-later + license_family: LGPL + purls: [] + size: 202282 + timestamp: 1767613720062 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_0.conda + sha256: a047a2f238362a37d484f9620e8cba29f513a933cd9eb68571ad4b270d6f8f3e + md5: f73b109d49568d5d1dda43bb147ae37f + depends: + - libfreetype6 >=2.14.3 + license: GPL-2.0-only OR FTL + purls: [] + size: 8091 + timestamp: 1774298691258 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-hdfa99f5_0.conda + sha256: ff764608e1f2839e95e2cf9b243681475f8778c36af7a42b3f78f476fdbb1dd3 + md5: e98ba7b5f09a5f450eca083d5a1c4649 + depends: + - __osx >=11.0 + - libpng >=1.6.55,<1.7.0a0 + - libzlib >=1.3.2,<2.0a0 + constrains: + - freetype >=2.14.3 + license: GPL-2.0-only OR FTL + purls: [] + size: 338085 + timestamp: 1774298689297 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda + sha256: 06644fa4d34d57c9e48f4d84b1256f9e5f654fdb37f43acc8a58a396952d42b7 + md5: 644058123986582db33aebd4ae2ca184 + depends: + - _openmp_mutex + constrains: + - libgcc-ng ==15.2.0=*_19 + - libgomp 15.2.0 19 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 404080 + timestamp: 1778273064154 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda + sha256: d4837b3b9b30af3132d260225e91ab9dde83be04c59513f500cc81050fb37486 + md5: 1ea03f87cdb1078fbc0e2b2deb63752c + depends: + - libgfortran5 15.2.0 hdae7583_19 + constrains: + - libgfortran-ng ==15.2.0=*_19 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 139675 + timestamp: 1778273280875 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda + sha256: d0a68b7a121d115b80c169e24d1265dcc25a3fe58d107df1bbc430797e226d88 + md5: ba36d8c606a6a53fe0b8c12d47267b3d + depends: + - libgcc >=15.2.0 + constrains: + - libgfortran 15.2.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 599691 + timestamp: 1778273075448 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.39.0-h2f60c08_1.conda + sha256: ccb95b546725d408b5229b7e269139a417594ff33bf30642d4a5b98642c22988 + md5: bc5d2c9015fe3b52b669287130a328af + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.0,<20260108.0a0 + - libcurl >=8.18.0,<9.0a0 + - libcxx >=19 + - libgrpc >=1.78.0,<1.79.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - openssl >=3.5.5,<4.0a0 + constrains: + - libgoogle-cloud 2.39.0 *_1 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 881725 + timestamp: 1770461059435 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-3.5.0-he41eb1d_0.conda + sha256: ff87ab8f88d6e0479ff42889e838f3265e2130b8af5d87e5c24441ede9c584fc + md5: ed72289ab19a3103deee5f28aa351d99 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libcurl >=8.20.0,<9.0a0 + - libcxx >=19 + - libgrpc >=1.78.1,<1.79.0a0 + - libopentelemetry-cpp >=1.26.0,<1.27.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - openssl >=3.5.6,<4.0a0 + constrains: + - libgoogle-cloud 3.5.0 *_0 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 1819466 + timestamp: 1779268312201 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.39.0-ha114238_1.conda + sha256: 82a760b31a498a24c0e58d91d0c3fee9c204bddd626b29072cd24c89ec5423b8 + md5: 8f1142ab8e0284a7a612d777a405a0f6 + depends: + - __osx >=11.0 + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libcxx >=19 + - libgoogle-cloud 2.39.0 h2f60c08_1 + - libzlib >=1.3.1,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + purls: [] + size: 524772 + timestamp: 1770461461389 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-3.5.0-ha114238_0.conda + sha256: 0d1630817981e05dc4c7962cb7acd50e1d5a843f367f7c15556e67e3dd87fdce + md5: b199a64a2c8b0db5ee53b344cc35261f + depends: + - __osx >=11.0 + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libcxx >=19 + - libgoogle-cloud 3.5.0 he41eb1d_0 + - libzlib >=1.3.2,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + purls: [] + size: 529065 + timestamp: 1779268730296 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.78.1-h3e3f78d_0.conda + sha256: a6e01573795484c2200e499ddffb825d24184888be6a596d4beaceebe6f8f525 + md5: 17b9e07ba9b46754a6953999a948dcf7 + depends: + - __osx >=11.0 + - c-ares >=1.34.6,<2.0a0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libcxx >=19 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libre2-11 >=2025.11.5 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.5,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.78.1 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 4820402 + timestamp: 1774012715207 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libhwy-1.4.0-ha332bbd_0.conda + sha256: 4fcad3cbec60da940312e883b7866816517acc5f9baecfe9a778de57327a1b1b + md5: 7394850583ca88325244b68b532c7a39 + depends: + - __osx >=11.0 + - libcxx >=19 + license: Apache-2.0 OR BSD-3-Clause + purls: [] + size: 609931 + timestamp: 1776990524407 +- 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: [] + size: 750379 + timestamp: 1754909073836 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.4.1-h84a0fba_0.conda + sha256: 17e035ae6a520ff6a6bb5dd93a4a7c3895891f4f9743bcb8c6ef607445a31cd0 + md5: b8a7544c83a67258b0e8592ec6a5d322 + depends: + - __osx >=11.0 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + purls: [] + size: 555681 + timestamp: 1775962975624 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjxl-0.11.2-h934fa54_1.conda + sha256: 948cf1370abb58e06a7c9554838c68672ef1d78e01c3fc4e62ccfc3072579645 + md5: 05bead8980f5ae6a070117dacec38b5b + depends: + - libcxx >=19 + - __osx >=11.0 + - libhwy >=1.4.0,<1.5.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 1032419 + timestamp: 1777065264956 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblal-7.6.1-fftw_h8fcc6d2_100.conda + sha256: dae82010eb705bf779c2b6117f615aae5e65d19fef30a124c274d127574af0cf + md5: 81844edb7e7b657e7ee5f7c8a7bc323a + depends: + - __osx >=11.0 + - fftw >=3.3.10,<4.0a0 + - gsl >=2.7,<2.8.0a0 + - hdf5 >=1.14.3,<1.14.4.0a0 + - libzlib >=1.3.1,<2.0a0 + constrains: + - python-lal >=7.1.1 + - lal >=7.1.1 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 752709 + timestamp: 1734695092130 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalburst-2.0.6-h2786bc8_0.conda + sha256: dcd5a9dce7519aca4ae6a4d7f5a7f62f69fe61e635efac3e2069a4ebaee77812 + md5: 42d03ef4cf7c1b941c7985274e30d94e + depends: + - __osx >=11.0 + - gsl >=2.7,<2.8.0a0 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalmetaio >=4.0.0 + - liblalmetaio >=4.0.5,<5.0a0 + - liblalsimulation >=6.1.0 + - liblalsimulation >=6.1.0,<7.0a0 + constrains: + - python-lalburst >=1.5.7 + - lalburst >=1.5.7 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 37876 + timestamp: 1734708926283 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalframe-3.0.6-h63b17c2_0.conda + sha256: 5bd7171b58750abe98b37b0ce87d1ab0e860e4f0ac42bb571e127eadd0471653 + md5: 9416eeb29c217946b6a242ddd75cbbac + depends: + - __osx >=11.0 + - ldas-tools-framecpp >=2.9.3,<2.10.0a0 + - libframel >=8.41.3,<9.0a0 + - liblal >=7.6.0,<8.0a0 + constrains: + - lalframe >=1.5.3 + - python-lalframe >=1.5.3 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 101511 + timestamp: 1734696361703 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalinference-4.1.8-h4d5f05e_0.conda + sha256: 68a96d4890299e1ea0f727d7c1d4b4d741a1a25548ccc3cd6a7dbe6e7a05e807 + md5: b4b890398dbbedc0c0038677769995a4 + depends: + - __osx >=11.0 + - gsl >=2.7,<2.8.0a0 + - lalinference-data 4.1.8 hce30654_0 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalburst >=2.0.0 + - liblalburst >=2.0.6,<3.0a0 + - liblalframe >=3.0.0 + - liblalframe >=3.0.6,<4.0a0 + - liblalinspiral >=5.0.0 + - liblalinspiral >=5.0.2,<6.0a0 + - liblalmetaio >=4.0.0 + - liblalmetaio >=4.0.5,<5.0a0 + - liblalsimulation >=6.1.0 + - liblalsimulation >=6.1.0,<7.0a0 + - llvm-openmp >=18.1.8 + - llvm-openmp >=19.1.6 + constrains: + - lalinference >=2.0.6 + - python-lalinference >=2.0.6 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 285907 + timestamp: 1734713095002 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalinspiral-5.0.2-h2786bc8_0.conda + sha256: 216db3cff92c91f3ba780b6a99e25ba942827cb59db3c3f8561b9e8daaa7fb4f + md5: ed53f908dc7538ee4771ae47047a4de6 + depends: + - __osx >=11.0 + - gsl >=2.7,<2.8.0a0 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalburst >=2.0.0 + - liblalburst >=2.0.5,<3.0a0 + - liblalframe >=3.0.0 + - liblalframe >=3.0.6,<4.0a0 + - liblalmetaio >=4.0.0 + - liblalmetaio >=4.0.5,<5.0a0 + - liblalsimulation >=6.1.0 + - liblalsimulation >=6.1.0,<7.0a0 + constrains: + - lalinspiral >=2.0.1 + - python-lalinspiral >=2.0.1 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 378031 + timestamp: 1734709396322 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalmetaio-4.0.5-h5505292_2.conda + sha256: d9eee370b5f4fbc466129b6e5435981490120ed086bccb44de107a1c16812a7c + md5: 7507d1ecdcb60174715ab734c0e9ec1b + depends: + - __osx >=11.0 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - libmetaio >=8.4.0 + - libmetaio >=8.5.1,<9.0a0 + constrains: + - python-lalmetaio >=2.0.1 + - lalmetaio >=2.0.1 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 55805 + timestamp: 1736417471577 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalpulsar-7.1.0-h8480f09_1.conda + sha256: 5f45c9b8c064644cdf1077a2b16ac0956089e4543e9d1eecb1c953a106e25d89 + md5: 17ae6c3fd86d8da9eec8fdf93343d0d0 + depends: + - __osx >=11.0 + - cfitsio >=4.6.2,<4.6.3.0a0 + - fftw + - gsl >=2.7,<2.8.0a0 + - lalpulsar-data 7.1.0 hce30654_1 + - liblal >=7.6.0 + - liblal >=7.6.1,<8.0a0 + - liblalframe >=3.0.0 + - liblalframe >=3.0.6,<4.0a0 + - liblalinference >=4.1.0 + - liblalinference >=4.1.8,<5.0a0 + - liblalsimulation >=6.1.0 + - liblalsimulation >=6.1.0,<7.0a0 + - llvm-openmp >=18.1.8 + - llvm-openmp >=20.1.2 + constrains: + - python-lalpulsar >=3.0.0 + - lalpulsar >=3.0.0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 624107 + timestamp: 1744214500157 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblalsimulation-6.1.0-py312h0975e73_0.conda + sha256: bbc2ad36e048db5f4bf27d057ed1526b058888f192069c9e07f3dc256e1b6d15 + md5: 07b65909c6c67ad009bbbbd4eb87a05c + depends: + - __osx >=11.0 + - gsl >=2.7,<2.8.0a0 + - lalsimulation-data 6.1.0 hce30654_0 + - liblal >=7.6.0 + - liblal >=7.6.0,<8.0a0 + - llvm-openmp >=18.1.8 + - llvm-openmp >=19.1.6 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - python-lalsimulation >=2.5.0 + - lalsimulation >=2.5.0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 3545985 + timestamp: 1734696382770 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-7_hd9741b5_openblas.conda + build_number: 7 + sha256: ff3018918ca8b22173dcb231842e819767fd05a08df61483eb5f3e9f2895d114 + md5: d1289ad41d5a78e2269eea3a2d7f0c7d + depends: + - libblas 3.11.0 7_h51639a9_openblas + constrains: + - libcblas 3.11.0 7*_openblas + - blas 2.307 openblas + - liblapacke 3.11.0 7*_openblas + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 18780 + timestamp: 1778490000843 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda + sha256: 34878d87275c298f1a732c6806349125cebbf340d24c6c23727268184bba051e + md5: b1fd823b5ae54fbec272cea0811bd8a9 + depends: + - __osx >=11.0 + constrains: + - xz 5.8.3.* + license: 0BSD + purls: [] + size: 92472 + timestamp: 1775825802659 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmetaio-8.5.1-h57f5043_1003.conda + sha256: 11ec8b7c14fcc991eae90908ee757abcbd9cb9d05e3f5ae9f9892f5651864005 + md5: 535416098c190840a512fae1677bc4a8 + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + constrains: + - metaio >=8.5.1 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 44891 + timestamp: 1721742371302 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.conda + sha256: 2bc7bc3978066f2c274ebcbf711850cc9ab92e023e433b9631958a098d11e10a + md5: 6ea18834adbc3b33df9bd9fb45eaf95b + depends: + - __osx >=11.0 + - c-ares >=1.34.6,<2.0a0 + - libcxx >=19 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.5,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 576526 + timestamp: 1773854624224 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.33-openmp_he657e61_0.conda + sha256: 9dd455b2d172aeedfa2058d324b5b5822b0bc1b7c1f32cd183d7078540d2f6eb + md5: 909e41855c29f0d52ae630198cd57135 + depends: + - __osx >=11.0 + - libgfortran + - libgfortran5 >=14.3.0 + - llvm-openmp >=19.1.7 + constrains: + - openblas >=0.3.33,<0.3.34.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 4304965 + timestamp: 1776995497368 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-1.21.0-h08d5cc3_2.conda + sha256: e09ebfabe397f03a408697cd7464b4c8277b93fe776a51fc33c4be17825abd1a + md5: dcbf0ebf1dbbffe6ced8bf48562f5c6f + depends: + - libabseil * cxx17* + - libabseil >=20260107.0,<20260108.0a0 + - libcurl >=8.18.0,<9.0a0 + - libgrpc >=1.78.0,<1.79.0a0 + - libopentelemetry-cpp-headers 1.21.0 hce30654_2 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libzlib >=1.3.1,<2.0a0 + - nlohmann_json + - prometheus-cpp >=1.3.0,<1.4.0a0 + constrains: + - cpp-opentelemetry-sdk =1.21.0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 560169 + timestamp: 1770452742811 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-1.26.0-h08d5cc3_0.conda + sha256: 47ce35cc7b903d546cc8ac0a09abfab7aea955147dc18bb2c9eaa5dc7c378a37 + md5: 8cb49289db7cfec1dea3bf7e0e4f0c8d + depends: + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libcurl >=8.19.0,<9.0a0 + - libgrpc >=1.78.0,<1.79.0a0 + - libopentelemetry-cpp-headers 1.26.0 hce30654_0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libzlib >=1.3.1,<2.0a0 + - nlohmann_json + - prometheus-cpp >=1.3.0,<1.4.0a0 + constrains: + - cpp-opentelemetry-sdk =1.26.0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 579527 + timestamp: 1774001294901 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-headers-1.21.0-hce30654_2.conda + sha256: 793fe6c7189290934578ef4bda0f34b529717a00c1676a66a7cfb3425b04abed + md5: d1adb8f085e35aa6335c2a4e6f025fb6 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 364108 + timestamp: 1770452651582 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-headers-1.26.0-hce30654_0.conda + sha256: 17f18bab128650598d2f09ae653ab406b9f049e0692b4519a2cf09a6f1603ee9 + md5: efdb13315f1041c7750214a20c1ab162 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 396412 + timestamp: 1774001222028 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-23.0.1-h7a13205_4_cpu.conda + build_number: 4 + sha256: 1c837890f0a71e9506d32ecd3f64e5a9221913573899af9d89135b224e790f83 + md5: 4a0b3e6db6084493c914b3f5596ccb56 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libarrow 23.0.1 h5390cfe_4_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.21.0,<1.22.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libthrift >=0.22.0,<0.22.1.0a0 + - openssl >=3.5.5,<4.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 1072294 + timestamp: 1773268358554 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-24.0.0-h16c0493_2_cpu.conda + build_number: 2 + sha256: 591c2605168287d5fab42c6e7481d2d950b282ca76fc7c3c9e343e95e8c2e780 + md5: 93d0cfbb69902a92aa4426c33a775d00 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.1,<20260108.0a0 + - libarrow 24.0.0 h4c70b79_2_cpu + - libcxx >=21 + - libopentelemetry-cpp >=1.26.0,<1.27.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libthrift >=0.22.0,<0.22.1.0a0 + - openssl >=3.5.6,<4.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 1098691 + timestamp: 1779475768999 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-h132b30e_0.conda + sha256: 66eae34546df1f098a67064970c92aa14ae7a7505091889e00468294d2882c36 + md5: 2259ae0949dbe20c0665850365109b27 + depends: + - __osx >=11.0 + - libzlib >=1.3.2,<2.0a0 + license: zlib-acknowledgement + purls: [] + size: 289546 + timestamp: 1776315246750 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-6.33.5-h4a5acfd_0.conda + sha256: 626852cd50690526c9eac216a9f467edd4cbb01060d0efe41b7def10b54bdb08 + md5: b839e3295b66434f20969c8b940f056a + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.0,<20260108.0a0 + - libcxx >=19 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 2713660 + timestamp: 1769748299578 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h4c27e2a_1.conda + sha256: 1e2d23bbc1ffca54e4912365b7b59992b7ae5cbeb892779a6dcd9eca9f71c428 + md5: 40d8ad21be4ccfff83a314076c3563f4 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20260107.0,<20260108.0a0 + - libcxx >=19 + constrains: + - re2 2025.11.05.* + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 165851 + timestamp: 1768190225157 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.22-h1a92334_1.conda + sha256: 202be45db5726757a8ea1f374f85aacc18c504f5ff15b2558496dff4c8779c48 + md5: 9ed5ab909c449bdcae72322e44875a18 + depends: + - __osx >=11.0 + license: ISC + purls: [] + size: 247352 + timestamp: 1779164136206 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.1-h1b79a29_0.conda + sha256: 49daec7c83e70d4efc17b813547824bc2bcf2f7256d84061d24fbfe537da9f74 + md5: 6681822ea9d362953206352371b6a904 + depends: + - __osx >=11.0 + - libzlib >=1.3.2,<2.0a0 + license: blessing + purls: [] + size: 920047 + timestamp: 1777987051643 +- 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: [] + 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 + - 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: [] + size: 323017 + timestamp: 1777019893083 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.1-h4030677_1.conda + sha256: e9248077b3fa63db94caca42c8dbc6949c6f32f94d1cafad127f9005d9b1507f + md5: e2a72ab2fa54ecb6abab2b26cde93500 + depends: + - __osx >=11.0 + - lerc >=4.0.0,<5.0a0 + - libcxx >=19 + - libdeflate >=1.25,<1.26.0a0 + - libjpeg-turbo >=3.1.0,<4.0a0 + - liblzma >=5.8.1,<6.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: HPND + purls: [] + size: 373892 + timestamp: 1762022345545 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.11.3-h2431656_0.conda + sha256: ae1a82e62cd4e3c18e005ae7ff4358ed72b2bfbfe990d5a6a5587f81e9a100dc + md5: 2255add2f6ae77d0a96624a5cbde6d45 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 87916 + timestamp: 1768735311947 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h07db88b_0.conda + sha256: a4de3f371bb7ada325e1f27a4ef7bcc81b2b6a330e46fac9c2f78ac0755ea3dd + md5: e5e7d467f80da752be17796b87fe6385 + depends: + - __osx >=11.0 + constrains: + - libwebp 1.6.0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 294974 + timestamp: 1752159906788 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda + sha256: bd3816218924b1e43b275863e21a3e13a5db4a6da74cca8e60bc3c213eb62f71 + md5: af523aae2eca6dfa1c8eec693f5b9a79 + depends: + - __osx >=11.0 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + purls: [] + size: 323658 + timestamp: 1727278733917 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.1-h0ff4647_0.conda + sha256: ebe2dd9da94280ad43da936efa7127d329b559f510670772debc87602b49b06d + md5: 438c97d1e9648dd7342f86049dd44638 + depends: + - __osx >=11.0 + - icu >=75.1,<76.0a0 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.1,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + constrains: + - libxml2 2.15.1 + license: MIT + license_family: MIT + purls: [] + size: 464952 + timestamp: 1761016087733 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.1-h9329255_0.conda + sha256: c409e384ddf5976a42959265100d6b2c652017d250171eb10bae47ef8166193f + md5: fb5ce61da27ee937751162f86beba6d1 + depends: + - __osx >=11.0 + - icu >=75.1,<76.0a0 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.1,<6.0a0 + - libxml2-16 2.15.1 h0ff4647_0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 40607 + timestamp: 1761016108361 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda + sha256: 361415a698514b19a852f5d1123c5da746d4642139904156ddfca7c922d23a05 + md5: bc5a5721b6439f2f62a84f2548136082 + depends: + - __osx >=11.0 + constrains: + - zlib 1.3.2 *_2 + license: Zlib + license_family: Other + purls: [] + size: 47759 + timestamp: 1774072956767 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzopfli-1.0.3-h9f76cd9_0.tar.bz2 + sha256: e3003b8efe551902dc60b21c81d7164b291b26b7862704421368d26ba5c10fa0 + md5: a0758d74f57741aa0d9ede13fd592e56 + depends: + - libcxx >=11.0.0 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 147901 + timestamp: 1607309166373 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ligo-segments-1.4.0-py312hea69d52_6.conda + sha256: 1b3679d240cb2f427f32133dc7756c2ff06dcbaa44b366c43e7188b91565208e + md5: 2b9f4e5f404d5acd85efdf5b127151e7 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - six + license: GPL-3.0-or-later + license_family: GPL + purls: + - pkg:pypi/ligo-segments?source=hash-mapping + size: 74969 + timestamp: 1733851948998 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ligo.skymap-2.3.0-py312hd0a6ca1_0.conda + sha256: a213a2d1f7617224e2e6d5c5d7933ae39142efe8158ca178d0cb04d3a956c80a + md5: 916df1503a16a30d99022a5e041fb256 + depends: + - __osx >=11.0 + - astroplan >=0.7 + - astropy-base >=6.0 + - astropy-healpix >=0.3 + - chealpix >=3.31.0,<3.32.0a0 + - gsl >=2.7,<2.8.0a0 + - h5py + - healpy + - libcblas >=3.9.0,<4.0a0 + - ligo-gracedb >=2.0.1 + - ligo-segments >=1.2.0 + - llvm-openmp >=18.1.8 + - matplotlib-base >=3.9.1 + - networkx + - numpy >=1.19,<3 + - numpy >=1.23.0 + - pillow >=2.5.0 + - ptemcee + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-lal >=7.6.0 + - python-lalinspiral >=5.0.1 + - python-lalmetaio >=4.0.5 + - python-lalsimulation >=6.0.0 + - python-ligo-lw >=1.8.0 + - python_abi 3.12.* *_cp312 + - pytz + - reproject >=0.3.2 + - scipy >=0.14,!=1.10.0 + - shapely >=2.0.0 + - tqdm >=4.27.0 + license: GPL-3.0-or-later + license_family: GPL + purls: + - pkg:pypi/ligo-skymap?source=hash-mapping + size: 1912189 + timestamp: 1745758375183 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.6-hc7d1edf_0.conda + sha256: 12d3652549a9abd30f3cc14797715327b86e91001d11865106eb3e02c40be9da + md5: b8cf70b77b2ed10d5f82e367c327b76b + depends: + - __osx >=11.0 + constrains: + - openmp 22.1.6|22.1.6.* + - intel-openmp <0.0a0 + license: Apache-2.0 WITH LLVM-exception + license_family: APACHE + purls: [] + size: 284850 + timestamp: 1779340584016 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvmlite-0.47.0-py312h7ca588d_1.conda + sha256: dc8477d74574e5b4bd4b45882f021a95f8a311b66633869e9b4d750e00a81add + md5: 24c6c9295e1048c90b8b1c3e9b6340f0 + depends: + - __osx >=11.0 + - libcxx >=19 + - libzlib >=1.3.2,<2.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - zstd >=1.5.7,<1.6.0a0 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/llvmlite?source=hash-mapping + size: 24314492 + timestamp: 1776077372867 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-4.4.5-py312h2b25a0d_1.conda + sha256: fb2c6c6d0078cc7097f71ca4117adfb013163dd7845d3a7b90c80cf8c324b2e3 + md5: 43132aaf61e6d8a59624b2da26aec518 + depends: + - python + - lz4-c + - __osx >=11.0 + - python 3.12.* *_cpython + - lz4-c >=1.10.0,<1.11.0a0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/lz4?source=hash-mapping + size: 125772 + timestamp: 1765026411222 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda + sha256: 94d3e2a485dab8bdfdd4837880bde3dd0d701e2b97d6134b8806b7c8e69c8652 + md5: 01511afc6cc1909c5303cf31be17b44f + depends: + - __osx >=11.0 + - libcxx >=18 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 148824 + timestamp: 1733741047892 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py312h04c11ed_1.conda + sha256: 330394fb9140995b29ae215a19fad46fcc6691bdd1b7654513d55a19aaa091c1 + md5: 11d95ab83ef0a82cc2de12c1e0b47fe4 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/markupsafe?source=hash-mapping + size: 25564 + timestamp: 1772445846939 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.9-py312h1f38498_0.conda + sha256: 79d518d9556ce81ff4e55e3e947a5a8cb6b1c4a101536e86ab90c34d2c80efcf + md5: 94174d301ce2f5962197fb9b880ea8ff + depends: + - matplotlib-base >=3.10.9,<3.10.10.0a0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - tornado >=5 + license: PSF-2.0 + license_family: PSF + purls: [] + size: 17770 + timestamp: 1777001080046 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.9-py312hf3defc7_0.conda + sha256: c2dc997012fb8a901163cdad333ba5ba27733aa26d67a28a6501f4b4d69fcbce + md5: e5d83f3ae6ada32d4e64e366a41f9ff6 + depends: + - __osx >=11.0 + - contourpy >=1.0.1 + - cycler >=0.10 + - fonttools >=4.22.0 + - freetype + - kiwisolver >=1.3.1 + - libcxx >=19 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - numpy >=1.23 + - numpy >=1.23,<3 + - packaging >=20.0 + - pillow >=8 + - pyparsing >=2.3.1 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-dateutil >=2.7 + - python_abi 3.12.* *_cp312 + - qhull >=2020.2,<2020.3.0a0 + license: PSF-2.0 + license_family: PSF + purls: + - pkg:pypi/matplotlib?source=hash-mapping + size: 8191891 + timestamp: 1777001043842 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgpack-python-1.1.2-py312h84eede6_1.conda + sha256: 1540339678e13365001453fdcb698887075a2b326d5fab05cfd0f4fdefae4eab + md5: e3973f0ac5ac854bf86f0d5674a1a289 + depends: + - __osx >=11.0 + - libcxx >=19 + - 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/msgpack?source=hash-mapping + size: 91268 + timestamp: 1762504467174 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda + sha256: 4ea6c620b87bd1d42bb2ccc2c87cd2483fa2d7f9e905b14c223f11ff3f4c455d + md5: 343d10ed5b44030a2f67193905aea159 + depends: + - __osx >=11.0 + license: X11 AND BSD-3-Clause + purls: [] + size: 805509 + timestamp: 1777423252320 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h784d473_1.conda + sha256: 1945fd5b64b74ef3d57926156fb0bfe88ee637c49f3273067f7231b224f1d26d + md5: 755cfa6c08ed7b7acbee20ccbf15a47c + constrains: + - nlohmann_json-abi ==3.12.0 + license: MIT + license_family: MIT + purls: [] + size: 137595 + timestamp: 1768670878127 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/numba-0.65.1-py312h2d3d6e9_1.conda + sha256: 304cc4894ed021d6101801219449afcca1179c7f1c700cc7a87b543a9bbbc7e7 + md5: 489bd27f778c3194664c82447b73bc96 + depends: + - __osx >=11.0 + - libcxx >=19 + - llvm-openmp >=19.1.7 + - llvmlite >=0.47.0,<0.48.0a0 + - numpy >=1.22.3,<2.5 + - numpy >=1.23,<3 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - libopenblas >=0.3.18,!=0.3.20 + - scipy >=1.0 + - tbb >=2021.6.0 + - cuda-python >=11.6 + - cuda-version >=11.2 + - cudatoolkit >=11.2 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/numba?source=hash-mapping + size: 5721304 + timestamp: 1778390934553 +- 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 + - 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 + size: 660694 + timestamp: 1764782989453 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py312h8442bc7_0.conda + sha256: c8841d6d6f61fd70ca80682efbab6bdb8606dc77c68d8acabfbd7c222054f518 + md5: d83fc83d589e2625a3451c9a7e21047c + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libcxx >=16 + - liblapack >=3.9.0,<4.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/numpy?source=hash-mapping + size: 6073136 + timestamp: 1707226249608 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/oniguruma-6.9.10-h5505292_0.conda + sha256: cedcd880e316240cbb35a1275990bfed1da36dba4a4f714edf95237f03d48665 + md5: 045afd0b8e35a71bfbe95345146592c4 + depends: + - __osx >=11.0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 223354 + timestamp: 1735727101839 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda + sha256: 60aca8b9f94d06b852b296c276b3cf0efba5a6eb9f25feb8708570d3a74f00e4 + md5: 4b5d3a91320976eec71678fad1e3569b + depends: + - __osx >=11.0 + - libcxx >=19 + - libpng >=1.6.55,<1.7.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 319697 + timestamp: 1772625397692 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjph-0.27.3-h2a4d681_0.conda + sha256: 05189fd9379e48e097016a18c1403a64cb0a08471cb5e64fdb3bbd0d6ce34e3b + md5: 97e85be52ac311fbedd3650699c7193b + depends: + - libcxx >=19 + - __osx >=11.0 + - libtiff >=4.7.1,<4.8.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 184336 + timestamp: 1778775342709 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.2-hd24854e_0.conda + sha256: c91bf510c130a1ea1b6ff023e28bac0ccaef869446acd805e2016f69ebdc49ea + md5: 25dcccd4f80f1638428613e0d7c9b4e1 + depends: + - __osx >=11.0 + - ca-certificates + license: Apache-2.0 + license_family: Apache + purls: [] + size: 3106008 + timestamp: 1775587972483 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.3.0-hd11884d_0.conda + sha256: 8594f064828cca9b8d625e2ebe79436fd4ffc030c950573380c54a8f4329f955 + md5: 77bfe521901c1a247cc66c1276826a85 + depends: + - tzdata + - libcxx >=19 + - __osx >=11.0 + - zstd >=1.5.7,<1.6.0a0 + - libzlib >=1.3.1,<2.0a0 + - snappy >=1.2.2,<1.3.0a0 + - libprotobuf >=6.33.5,<6.33.6.0a0 + - libabseil >=20260107.1,<20260108.0a0 + - libabseil * cxx17* + - lz4-c >=1.10.0,<1.11.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 548180 + timestamp: 1773230270828 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.3-py312h6510ced_0.conda + sha256: 7202013525593f57a452dac7e5fee9f26478822be3ba5c893643517b8627406d + md5: 4581a32b837950217327fcab93214313 + depends: + - python + - numpy >=1.26.0 + - python-dateutil >=2.8.2 + - __osx >=11.0 + - python 3.12.* *_cpython + - libcxx >=19 + - python_abi 3.12.* *_cp312 + - numpy >=1.23,<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=hash-mapping + size: 13926263 + timestamp: 1778602825408 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.46-h7125dd6_0.conda + sha256: 5bf2eeaa57aab6e8e95bea6bd6bb2a739f52eb10572d8ed259d25864d3528240 + md5: 0e6e82c3cc3835f4692022e9b9cd5df8 + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 835080 + timestamp: 1756743041908 +- 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: [] + size: 850231 + timestamp: 1763655726735 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.2.0-py312h4e908a4_0.conda + sha256: f7ee5d45bf16184d2b53f0d35c98c06e4e82e21688ce93e52b55c02ec7153bf3 + md5: 0634560e556adb3e7924668e49ad53fc + depends: + - python + - __osx >=11.0 + - python 3.12.* *_cpython + - libxcb >=1.17.0,<2.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - libjpeg-turbo >=3.1.2,<4.0a0 + - zlib-ng >=2.3.3,<2.4.0a0 + - tk >=8.6.13,<8.7.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - lcms2 >=2.18,<3.0a0 + - python_abi 3.12.* *_cp312 + - openjpeg >=2.5.4,<3.0a0 + - libwebp-base >=1.6.0,<2.0a0 + license: HPND + purls: + - pkg:pypi/pillow?source=hash-mapping + size: 965082 + timestamp: 1775060469004 +- 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 + - libcurl >=8.10.1,<9.0a0 + - libcxx >=18 + - libzlib >=1.3.1,<2.0a0 + - zlib + license: MIT + license_family: MIT + purls: [] + size: 173220 + timestamp: 1730769371051 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py312hb3ab3e3_0.conda + sha256: 6d0e21c76436374635c074208cfeee62a94d3c37d0527ad67fd8a7615e546a05 + md5: fd856899666759403b3c16dcba2f56ff + depends: + - python + - __osx >=11.0 + - python 3.12.* *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/psutil?source=hash-mapping + size: 239031 + timestamp: 1769678393511 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda + sha256: 8ed65e17fbb0ca944bfb8093b60086e3f9dd678c3448b5de212017394c247ee3 + md5: 415816daf82e0b23a736a069a75e9da7 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 8381 + timestamp: 1726802424786 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-23.0.1-py312h1f38498_0.conda + sha256: 3cc847d7fc9d16efb145dd2593c3bcc78a4423a77be1748214e7b1c02a85bcb7 + md5: 9a2007e9af67ae4fc94ec64d29672382 + depends: + - libarrow-acero 23.0.1.* + - libarrow-dataset 23.0.1.* + - libarrow-substrait 23.0.1.* + - libparquet 23.0.1.* + - pyarrow-core 23.0.1 *_0_* + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 28670 + timestamp: 1771307852216 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-24.0.0-py312h1f38498_0.conda + sha256: c7cc2c75525c6522f9b948cd9ead2d5ceec55ba8b78cfd6f222fbc581219d0ff + md5: 2b3892c12915851e12955ee753eaedbb + depends: + - libarrow-acero 24.0.0.* + - libarrow-dataset 24.0.0.* + - libarrow-substrait 24.0.0.* + - libparquet 24.0.0.* + - pyarrow-core 24.0.0 *_0_* + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 26799 + timestamp: 1776928498495 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-23.0.1-py312h21b41d0_0_cpu.conda + sha256: 285b146dbb09da5cd71cb8a460f833572f5e527ba44c5541b4739254aaf447d1 + md5: 2c0f60cf75d24b80367308e5454b472a + depends: + - __osx >=11.0 + - libarrow 23.0.1.* *cpu + - libarrow-compute 23.0.1.* *cpu + - libcxx >=21 + - libzlib >=1.3.1,<2.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - 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 + size: 3915948 + timestamp: 1771307781330 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-24.0.0-py312h21b41d0_0_cpu.conda + sha256: 38049b8b098fa02446e97bedebdde2ff4cae4b579581a7125da3e751bcf5a54d + md5: 7020684cfd5c066e86cee8affad06e83 + depends: + - __osx >=11.0 + - libarrow 24.0.0.* *cpu + - libarrow-compute 24.0.0.* *cpu + - libcxx >=21 + - libzlib >=1.3.2,<2.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - 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 + size: 4322018 + timestamp: 1776928464897 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyerfa-2.0.1.5-py310hbb12772_2.conda + noarch: python + sha256: ec2a947d95ffb46ca3a818272c8594f195b1e74369164c46f4512b2d66f7f4c4 + md5: 51a8f8137ff9e55513e5e722c86fb9f8 + depends: + - __osx >=11.0 + - numpy >=1.21,<3 + - python + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pyerfa?source=hash-mapping + size: 271352 + timestamp: 1756821964759 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pynacl-1.6.2-py312h8fd69cb_2.conda + sha256: fc7973feaa661bcefb3e97cc67438e5345100f7c9e894bbcc42b9f78ac319ba0 + md5: 52bace8397027430e2be07d2a3876078 + depends: + - __osx >=11.0 + - cffi >=1.4.1 + - libsodium >=1.0.22,<1.0.23.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - six + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/pynacl?source=hash-mapping + size: 1155969 + timestamp: 1778985617424 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.13-h8561d8f_0_cpython.conda + sha256: e658e647a4a15981573d6018928dec2c448b10c77c557c29872043ff23c0eb6a + md5: 8e7608172fa4d1b90de9a745c2fd2b81 + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.7.4,<3.0a0 + - libffi >=3.5.2,<3.6.0a0 + - liblzma >=5.8.2,<6.0a0 + - libsqlite >=3.51.2,<4.0a0 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.5.5,<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: [] + size: 12127424 + timestamp: 1772730755512 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-gssapi-1.11.1-py312h72ca3cf_0.conda + sha256: ec889c7463bbd63b748f9b42970ac96efdd8e6dd2f7cf38cb9996bf93a192cbf + md5: ef1087dc64dd9484ee77d81705375eb9 + depends: + - __osx >=11.0 + - decorator + - krb5 >=1.21.3,<1.22.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: ISC + purls: + - pkg:pypi/gssapi?source=hash-mapping + size: 484822 + timestamp: 1769843035292 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-gssapi-1.11.1-py312h858ef95_1.conda + sha256: 7a28921306a1e39083483ca6e8d836ea99ede38abfa6a9b8e587c5ec5b51c9e8 + md5: ebee80a25b43b351b0848a4d7d4c03dd + depends: + - __osx >=11.0 + - decorator + - krb5 >=1.22.2,<1.23.0a0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: ISC + purls: + - pkg:pypi/gssapi?source=hash-mapping + size: 484305 + timestamp: 1770935400303 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-htcondor-24.12.4-py312hd1c112e_0.conda + sha256: 6ef36799dca4aa265027159dd7c9fbb881fd2da56275478a1ccbde163f2b1660 + md5: b582afdb6a35f4c1657016182a81c673 + depends: + - __osx >=11.0 + - htcondor-classads 24.12.4 h0c2a548_0 + - libboost-python >=1.88.0,<1.89.0a0 + - libcondor_utils 24.12.4 h4785e8b_0 + - libcxx >=19 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/htcondor?source=hash-mapping + size: 932493 + timestamp: 1759418635468 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-htcondor-25.10.1-py312hacc1691_0.conda + sha256: cab7c838b00ea51cc02d8ba941cd711e5f540b9f67a12f923886170d2eb3dfd5 + md5: 408cc0cc2900d098c250945ab82737a2 + depends: + - __osx >=11.0 + - htcondor-classads 25.10.1 h7a491f7_0 + - libcondor_utils 25.10.1 h3669c8c_0 + - libcxx >=19 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/htcondor?source=hash-mapping + size: 505934 + timestamp: 1778794246872 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lal-7.6.1-fftw_py312h0f8b06b_100.conda + sha256: 53fb1b683ae7e4615e5f11c6a3a7b402793466fa5bf6aa918f167dcc3b69794f + md5: f5972581f437cbe05421aa6bfc0511c3 + depends: + - __osx >=11.0 + - liblal 7.6.1 fftw_h8fcc6d2_100 + - ligo-segments + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-dateutil + - python_abi 3.12.* *_cp312 + - scipy + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 798310 + timestamp: 1734695835019 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalburst-2.0.6-py312heec191f_0.conda + sha256: 76b35ec460ead89af9fde5dae9095cfbca556a2ad40c7bb0ed8400dc71661d1c + md5: f721f4a8a6c607275e6d76fb3a94a87c + depends: + - __osx >=11.0 + - gsl >=2.7,<2.8.0a0 + - liblalburst 2.0.6 h2786bc8_0 + - ligo-segments + - lscsoft-glue + - matplotlib-base + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-lal >=7.6.0 + - python-lalmetaio >=4.0.0 + - python-lalsimulation >=6.1.0 + - python-ligo-lw >=1.7.0 + - python_abi 3.12.* *_cp312 + - scipy + - tqdm + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 314046 + timestamp: 1734709412793 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalframe-3.0.6-py312he0011b7_0.conda + sha256: 5d74ffc908ca4f45f7222c3566a4a59b2d2349b16e34aa6e01dc490117b9e8cc + md5: 66e9b6c1baa768805f8e976f769d08d4 + depends: + - __osx >=11.0 + - liblalframe 3.0.6 h63b17c2_0 + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-lal >=7.6.0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 739027 + timestamp: 1734696549991 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalinference-4.1.8-py312he0011b7_0.conda + sha256: 558f07197c5ffbe30fd4e0aad3db112b914174fe943cb6b27bb20b331e5e666a + md5: 237b8a8b666995ede9bad35cc54de638 + depends: + - __osx >=11.0 + - astropy-base >=1.1.1 + - h5py + - healpy >=1.17.3 + - liblalinference 4.1.8 h4d5f05e_0 + - ligo-segments + - lscsoft-glue >=1.54.1 + - matplotlib-base >=1.2.0 + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-lal >=7.6.0 + - python-lalburst >=2.0.0 + - python-lalinspiral >=5.0.0 + - python-lalmetaio >=4.0.0 + - python-lalsimulation >=6.1.0 + - python-ligo-lw >=1.7.0 + - python_abi 3.12.* *_cp312 + - scipy >=0.9.0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 807142 + timestamp: 1734713244369 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalinspiral-5.0.2-py312he0011b7_0.conda + sha256: ef22e941b69ce0f79657ad22b7f2a1b46ec0e804fdaf0b283efce38e02eca824 + md5: 5117ae7ef0919138027fca7284eef633 + depends: + - __osx >=11.0 + - liblalinspiral 5.0.2 h2786bc8_0 + - lscsoft-glue + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-lal >=7.6.0 + - python-lalburst >=2.0.0 + - python-lalframe >=3.0.0 + - python-lalmetaio >=4.0.0 + - python-lalsimulation >=6.1.0 + - python-ligo-lw + - python_abi 3.12.* *_cp312 + - tqdm + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 61662448 + timestamp: 1734710223688 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalmetaio-4.0.5-py312he0011b7_2.conda + sha256: 74de958d36c048c1a38f478de9d780a24c5585e8debb42c809db61714eb9267b + md5: 269b7b2c7d77b86d12993631f10096b0 + depends: + - __osx >=11.0 + - liblalmetaio 4.0.5 h5505292_2 + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-lal >=7.6.0 + - python_abi 3.12.* *_cp312 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 144585 + timestamp: 1736417741326 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalpulsar-7.1.0-py312he0011b7_1.conda + sha256: 3891a0ac3b03b4d2447a791e8b5b913b3ed26954c49606cfcb14582eb5ffe4a0 + md5: 6ca4ed5c100a39e45c064b96d00a0929 + depends: + - __osx >=11.0 + - astropy-base + - liblalpulsar 7.1.0 h8480f09_1 + - numpy >=1.19,<3 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-lal >=7.6.0 + - python-lalframe >=3.0.0 + - python-lalinference >=4.1.0 + - python-lalsimulation >=6.1.0 + - python_abi 3.12.* *_cp312 + - six + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 96730459 + timestamp: 1744214795094 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-lalsimulation-6.1.0-py312he758b9a_0.conda + sha256: 91f99a157763eba6bd71ce65aad9e874483ca336cfd3b99387f27479534b0df8 + md5: 03da7e906a89a103ec5c9cd55c4c41bb + depends: + - __osx >=11.0 + - astropy-base + - gsl >=2.7,<2.8.0a0 + - gwpy + - liblal >=7.6.0 + - liblal >=7.6.0,<8.0a0 + - liblalsimulation 6.1.0 py312h0975e73_0 + - llvm-openmp >=18.1.8 + - llvm-openmp >=19.1.6 + - mpmath >=1.0.0 + - numpy + - python >=3.12,<3.13.0a0 + - python-lal >=7.6.0 + - python_abi 3.12.* *_cp312 + - scipy + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 3984127 + timestamp: 1734696469407 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-ligo-lw-1.8.3-py312hea69d52_4.conda + sha256: 89c3fd3461044d9aee2d4dc537dafd8bbbe64fb0d558519c38afb2b7ba1dc5c2 + md5: 65940c93b9e017cc8a135c7fb11357d1 + depends: + - __osx >=11.0 + - ligo-segments + - lscsoft-glue >=2.0.0 + - numpy + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python-dateutil + - python-lal >=6.19.0 + - python_abi 3.12.* *_cp312 + - pyyaml + - six + - tqdm + license: GPL-3.0-or-later + license_family: GPL + purls: + - pkg:pypi/python-ligo-lw?source=hash-mapping + size: 191541 + timestamp: 1733996421860 +- 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 + size: 187278 + timestamp: 1770223990452 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/qhull-2020.2-h420ef59_5.conda + sha256: 873ac689484262a51fd79bc6103c1a1bedbf524924d7f0088fb80703042805e4 + md5: 6483b1f59526e05d7d894e466b5b6924 + depends: + - __osx >=11.0 + - libcxx >=16 + license: LicenseRef-Qhull + purls: [] + size: 516376 + timestamp: 1720814307311 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/rav1e-0.8.1-h8246384_0.conda + sha256: 925e35b71fe513e0380ecd2fe137e3f4f248bf7ce4bad96946c7c704b7a50d26 + md5: 4706a8a71474c692482c3f86c2175454 + depends: + - __osx >=11.0 + constrains: + - __osx >=11.0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 886953 + timestamp: 1772541394570 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-ha480c28_1.conda + sha256: 5bab972e8f2bff1b5b3574ffec8ecb89f7937578bd107584ed3fde507ff132f9 + md5: a1ff22f664b0affa3de712749ccfbf04 + depends: + - libre2-11 2025.11.05 h4c27e2a_1 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 27445 + timestamp: 1768190259003 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda + sha256: a77010528efb4b548ac2a4484eaf7e1c3907f2aec86123ed9c5212ae44502477 + md5: f8381319127120ce51e081dce4865cf4 + depends: + - __osx >=11.0 + - ncurses >=6.5,<7.0a0 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 313930 + timestamp: 1765813902568 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/regex-2026.5.9-py312h2bbb03f_0.conda + sha256: 70c8590c2fa6474dd1f440d4fe674faed5ff17f9079d69abef7aaceefa229bbe + md5: 5f8dbdc69edd17a2fd6cf0833e532357 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: Apache-2.0 AND CNRI-Python + license_family: PSF + purls: + - pkg:pypi/regex?source=compressed-mapping + size: 373798 + timestamp: 1778374452771 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/reproject-0.19.0-py312ha11c99a_0.conda + sha256: a9154fe429d440b1d2621a5059cefd60532076949fcfacedf4ea67a61dcb7467 + md5: ea6c3bacc93d7b704517f2ecc463b4e6 + depends: + - __osx >=11.0 + - astropy-base >=5.0 + - astropy-healpix >=1.0 + - dask >=2024.4.1 + - dask-image >=2025.11.0 + - fsspec >=2021.9 + - numpy >=1.23 + - numpy >=1.23,<3 + - pillow >=10.0 + - pyavm >=0.9.6 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + - scipy >=1.9 + - shapely + - zarr >=2.17.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/reproject?source=hash-mapping + size: 963288 + timestamp: 1763206877177 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.8.0-np2py312he5ca3e3_1.conda + sha256: 5f640a06e001666f9d4dca7cca992f1753e722e9f6e50899d7d250c02ddf7398 + md5: ed7887c51edfa304c69a424279cec675 + depends: + - python + - numpy >=1.24.1 + - scipy >=1.10.0 + - joblib >=1.3.0 + - threadpoolctl >=3.2.0 + - libcxx >=19 + - python 3.12.* *_cpython + - __osx >=11.0 + - llvm-openmp >=19.1.7 + - numpy >=1.23,<3 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scikit-learn?source=hash-mapping + size: 9124177 + timestamp: 1766550900752 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py312h0f234b1_0.conda + sha256: 7082a8c87ae32b6090681a1376e3335cf23c95608c68a3f96f3581c847f8b840 + md5: fd035cd01bb171090a990ae4f4143090 + depends: + - __osx >=11.0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libcxx >=19 + - libgfortran + - libgfortran5 >=14.3.0 + - liblapack >=3.9.0,<4.0a0 + - numpy <2.7 + - numpy >=1.23,<3 + - numpy >=1.25.2 + - 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/scipy?source=hash-mapping + size: 13966986 + timestamp: 1771881089893 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/scitokens-cpp-1.4.0-h608d757_0.conda + sha256: 87fd75f8f087763bf83d081aac23d4a481ba47cb14c8d6ea4a922643f47cb666 + md5: 2c4e402af68d87892ebede3ba1f30ab9 + depends: + - __osx >=11.0 + - libcurl >=8.18.0,<9.0a0 + - libcxx >=19 + - libsqlite >=3.51.2,<4.0a0 + - openssl >=3.5.5,<4.0a0 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 181487 + timestamp: 1771576981524 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/shapely-2.1.2-py312h35cd81b_2.conda + sha256: 81d4780a8a7d2f6b696fc8cd43f791ef058a420e35366fd4cd68bef9f139f3d5 + md5: 624173184d65db80f267b6191c1ad26d + depends: + - __osx >=11.0 + - geos >=3.14.1,<3.14.2.0a0 + - numpy >=1.23,<3 + - 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/shapely?source=hash-mapping + size: 596152 + timestamp: 1762524099944 +- 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: [] + size: 38883 + timestamp: 1762948066818 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/svt-av1-4.0.1-h0cb729a_0.conda + sha256: bdef3c1c4d2a396ad4f7dc64c5e9a02d4c5a21ff93ed07a33e49574de5d2d18d + md5: 8badc3bf16b62272aa2458f138223821 + depends: + - __osx >=11.0 + - libcxx >=19 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 1456245 + timestamp: 1769664727051 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/swig-4.3.1-h0c2a548_4.conda + sha256: 0c107166881dc4b2d2958a8a721fd211def42aaf7cd8ea7dc01026c7641d6118 + md5: 5701bdedd264c6c6bb1b3c0561ce7916 + depends: + - __osx >=11.0 + - libcxx >=19 + - pcre2 >=10.46,<10.47.0a0 + constrains: + - swig-abi ==4 + license: GPL-3.0-or-later + license_family: GPL + purls: [] + size: 1125420 + timestamp: 1761740898649 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/swig-4.4.1-h4366dc5_0.conda + sha256: 6491edeb3df94578b016015f78dd80bddc0f85e2624ed9cea79ad9f9f4b240ca + md5: f9a4ce9ad596a0feac7cc7aba8517389 + depends: + - libcxx >=19 + - __osx >=11.0 + - pcre2 >=10.47,<10.48.0a0 + constrains: + - swig-abi ==5 + license: GPL-3.0-or-later + license_family: GPL + purls: [] + size: 1189583 + timestamp: 1773252068990 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda + sha256: 799cab4b6cde62f91f750149995d149bc9db525ec12595e8a1d91b9317f038b3 + md5: a9d86bc62f39b94c4661716624eb21b0 + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: TCL + license_family: BSD + purls: [] + size: 3127137 + timestamp: 1769460817696 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.5-py312h2bbb03f_0.conda + sha256: 29edd36311b4a810a9e6208437bdbedb28c9ac15221caf812cb5c5cf48375dca + md5: 02cce5319b0f1317d9642dcb2e475379 + depends: + - __osx >=11.0 + - 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/tornado?source=hash-mapping + size: 859155 + timestamp: 1774358568476 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py312h2bbb03f_0.conda + sha256: e935d0c11581e31e89ce4899a28b16f924d1a3c1af89f18f8a2c5f5728b3107f + md5: 45b836f333fd3e282c16fff7dc82994e + depends: + - __osx >=11.0 + - 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/unicodedata2?source=hash-mapping + size: 415828 + timestamp: 1770909782683 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-2.2.1-py312h2bbb03f_0.conda + sha256: baefb9a73519863a70f6e7a6f6acb982ee5485ae986a16960ff1e8cb99f1a023 + md5: a82ae4e18607e068c9259136d07e4f87 + depends: + - __osx >=11.0 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/wrapt?source=hash-mapping + size: 111458 + timestamp: 1779477998634 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-hc919400_1.conda + sha256: adae11db0f66f86156569415ed79cda75b2dbf4bea48d1577831db701438164f + md5: 78b548eed8227a689f93775d5d23ae09 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 14105 + timestamp: 1762976976084 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hc919400_1.conda + sha256: f7fa0de519d8da589995a1fe78ef74556bb8bc4172079ae3a8d20c3c81354906 + md5: 9d1299ace1924aa8f4e0bc8e71dd0cf7 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 19156 + timestamp: 1762977035194 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h925e9cb_3.conda + sha256: b03433b13d89f5567e828ea9f1a7d5c5d697bf374c28a4168d71e9464f5dafac + md5: 78a0fe9e9c50d2c381e8ee47e3ea437d + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 83386 + timestamp: 1753484079473 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zfp-1.0.1-ha86207d_5.conda + sha256: 5b8bc86ca206f456ca9fe9e1a629f68b949ac47070211bccf4b44d29141c85d7 + md5: 581bd74656ccd460cf2bbe152292a1eb + depends: + - __osx >=11.0 + - libcxx >=19 + - llvm-openmp >=19.1.7 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 204043 + timestamp: 1766549790975 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_2.conda + sha256: 8dd2ac25f0ba714263aac5832d46985648f4bfb9b305b5021d702079badc08d2 + md5: f1c0bce276210bed45a04949cfe8dc20 + depends: + - __osx >=11.0 + - libzlib 1.3.2 h8088a28_2 + license: Zlib + license_family: Other + purls: [] + size: 81123 + timestamp: 1774072974535 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda + sha256: a339606a6b224bb230ff3d711e801934f3b3844271df9720165e0353716580d4 + md5: d99c2a23a31b0172e90f456f580b695e + depends: + - __osx >=11.0 + - libcxx >=19 + license: Zlib + license_family: Other + purls: [] + size: 94375 + timestamp: 1770168363685 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda + sha256: 9485ba49e8f47d2b597dd399e88f4802e100851b27c21d7525625b0b4025a5d9 + md5: ab136e4c34e97f34fb621d2592a393d8 + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 433413 + timestamp: 1764777166076 +- pypi: https://files.pythonhosted.org/packages/00/e2/1cb7cfb88fd3866062977a484bc0dda4e165361e949cc8e270302d05b007/spinsfast-2022.4.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: spinsfast + version: 2022.4.10 + sha256: edfe300d3b83b12033d5d18d9619c5eaae43cc4d830ce7164b71db233982a615 +- pypi: https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl + name: itsdangerous + version: 2.2.0 + sha256: c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/05/30/affbabf3c27fb501ec7b5808230c619d4d1a4525c07301074eb4bda92fa9/statsmodels-0.14.6-cp312-cp312-macosx_11_0_arm64.whl + name: statsmodels + version: 0.14.6 + sha256: 26d4f0ed3b31f3c86f83a92f5c1f5cbe63fc992cd8915daf28ca49be14463a1c + requires_dist: + - numpy>=1.22.3,<3 + - scipy>=1.8,!=1.9.2 + - pandas>=1.4,!=2.1.0 + - patsy>=0.5.6 + - packaging>=21.3 + - cython>=3.0.10 ; extra == 'build' + - cython>=3.0.10 ; extra == 'develop' + - cython>=3.0.10,<4 ; extra == 'develop' + - setuptools-scm[toml]~=8.0 ; extra == 'develop' + - matplotlib>=3 ; extra == 'develop' + - colorama ; extra == 'develop' + - joblib ; extra == 'develop' + - jinja2 ; extra == 'develop' + - pytest>=7.3.0,<8 ; extra == 'develop' + - pytest-randomly ; extra == 'develop' + - pytest-xdist ; extra == 'develop' + - pytest-cov ; extra == 'develop' + - pywinpty ; os_name == 'nt' and extra == 'develop' + - flake8 ; extra == 'develop' + - isort ; extra == 'develop' + - sphinx ; extra == 'docs' + - nbconvert ; extra == 'docs' + - jupyter-client ; extra == 'docs' + - ipykernel ; extra == 'docs' + - matplotlib ; extra == 'docs' + - nbformat ; extra == 'docs' + - numpydoc ; extra == 'docs' + - pandas-datareader ; extra == 'docs' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/07/40/4058220b3d60890b62e0a2e8212e2546695827cf85e6186405ecf8ef33f1/numpy_quaternion-2024.0.13-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + name: numpy-quaternion + version: 2024.0.13 + sha256: cc2e037ad4269a8f7c9ccf37711cae16451745822e0e77db5a4da7c3633c51d1 + requires_dist: + - numpy>=1.25,<3 + - scipy>=1.5,<2 + - mkdocs-material ; extra == 'docs' + - mkdocstrings-python ; extra == 'docs' + - pymdown-extensions ; extra == 'docs' + requires_python: '>=3.10,<3.15' +- pypi: https://files.pythonhosted.org/packages/09/c9/a9271d39c86d28d8bfacec831e416e42eb6d154f03605429631c4c08138b/pygsl_lite-0.1.8.tar.gz + name: pygsl-lite + version: 0.1.8 + sha256: 3213755c71da61e9629551e088530130d28e27048cd5f3ba48deb8165e6df6de + requires_dist: + - numpy>=1.23.0 + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl + name: blinker + version: 1.9.0 + sha256: ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl + name: pydantic-core + version: 2.46.4 + sha256: 962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f + requires_dist: + - typing-extensions>=4.14.1 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/19/cb/d31459da1f482479512c642c8463347b828004ecfa5bcd0a26ab57317add/spinsfast-2022.4.10-cp312-cp312-macosx_13_0_x86_64.whl + name: spinsfast + version: 2022.4.10 + sha256: ba472e367da00ed650775fcc439dd0fd7e6230dcb9a299607fe05a730820ac12 +- pypi: https://files.pythonhosted.org/packages/20/7a/1c6e3562dfd8950adbb11ffbc65d21e7c89d01a6e4f137fa981056de25c5/gitpython-3.1.50-py3-none-any.whl + name: gitpython + version: 3.1.50 + sha256: d352abe2908d07355014abdd21ddf798c2a961469239afec4962e9da884858f9 + requires_dist: + - gitdb>=4.0.1,<5 + - typing-extensions>=3.10.0.2 ; python_full_version < '3.10' + - coverage[toml] ; extra == 'test' + - ddt>=1.1.1,!=1.4.3 ; extra == 'test' + - mock ; python_full_version < '3.8' and extra == 'test' + - mypy==1.18.2 ; python_full_version >= '3.9' and extra == 'test' + - pre-commit ; extra == 'test' + - pytest>=7.3.1 ; extra == 'test' + - pytest-cov ; extra == 'test' + - pytest-instafail ; extra == 'test' + - pytest-mock ; extra == 'test' + - pytest-sugar ; extra == 'test' + - typing-extensions ; python_full_version < '3.11' and extra == 'test' + - sphinx>=7.4.7,<8 ; extra == 'doc' + - sphinx-rtd-theme ; extra == 'doc' + - sphinx-autodoc-typehints ; extra == 'doc' + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/25/ce/308e5e5da57515dd7cab3ec37ea2d5b8ff50bef1fcc8e6d31456f9fae08e/statsmodels-0.14.6-cp312-cp312-macosx_10_13_x86_64.whl + name: statsmodels + version: 0.14.6 + sha256: fe76140ae7adc5ff0e60a3f0d56f4fffef484efa803c3efebf2fcd734d72ecb5 + requires_dist: + - numpy>=1.22.3,<3 + - scipy>=1.8,!=1.9.2 + - pandas>=1.4,!=2.1.0 + - patsy>=0.5.6 + - packaging>=21.3 + - cython>=3.0.10 ; extra == 'build' + - cython>=3.0.10 ; extra == 'develop' + - cython>=3.0.10,<4 ; extra == 'develop' + - setuptools-scm[toml]~=8.0 ; extra == 'develop' + - matplotlib>=3 ; extra == 'develop' + - colorama ; extra == 'develop' + - joblib ; extra == 'develop' + - jinja2 ; extra == 'develop' + - pytest>=7.3.0,<8 ; extra == 'develop' + - pytest-randomly ; extra == 'develop' + - pytest-xdist ; extra == 'develop' + - pytest-cov ; extra == 'develop' + - pywinpty ; os_name == 'nt' and extra == 'develop' + - flake8 ; extra == 'develop' + - isort ; extra == 'develop' + - sphinx ; extra == 'docs' + - nbconvert ; extra == 'docs' + - jupyter-client ; extra == 'docs' + - ipykernel ; extra == 'docs' + - matplotlib ; extra == 'docs' + - nbformat ; extra == 'docs' + - numpydoc ; extra == 'docs' + - pandas-datareader ; extra == 'docs' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/26/96/92119e6b279a88547a51eb99726ecae1ada839bf4fbcc2856503e2558e80/blosc2-4.3.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + name: blosc2 + version: 4.3.3 + sha256: ef89bc8fadf6e586fa38dfa998f8b6091aae653c41ef85232814e59ed6e9db85 + requires_dist: + - numpy>=1.26 + - ndindex + - msgpack + - numexpr>=2.14.1 ; platform_machine != 'wasm32' + - pydantic + - requests + - threadpoolctl ; platform_machine != 'wasm32' + - pyarrow ; extra == 'parquet' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/27/ae/defd665dbbeb2fffa077491365ed160acaec49274ce8d4b979f55db71f18/ndindex-1.10.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl + name: ndindex + version: 1.10.1 + sha256: 03cf1e6cdac876bd8fc92d3b65bb223496b1581d10eab3ba113f7c195121a959 + requires_dist: + - numpy ; extra == 'arrays' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/36/e1/84f6ede106afadc0e2c6a43d35b96ed6909149023f9a6a929175ad13a503/blosc2-4.3.3-cp312-cp312-macosx_10_13_x86_64.whl + name: blosc2 + version: 4.3.3 + sha256: b5ca83094ce1e2885f4a44eae6e2689959af67aee22b3f69af64447206dde3d9 + requires_dist: + - numpy>=1.26 + - ndindex + - msgpack + - numexpr>=2.14.1 ; platform_machine != 'wasm32' + - pydantic + - requests + - threadpoolctl ; platform_machine != 'wasm32' + - pyarrow ; extra == 'parquet' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl + name: requests-toolbelt + version: 1.0.0 + sha256: cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06 + requires_dist: + - requests>=2.0.1,<3.0.0 + requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*' +- pypi: https://files.pythonhosted.org/packages/42/c2/b602d2a5bbaaf9db1cff035ee6ddea76938c0b25ac1e6ac8ca288a073b47/otter_report-0.4.0-py2.py3-none-any.whl + name: otter-report + version: 0.4.0 + sha256: 8728862532c3cc5a385bf72200922e78d17dc58bcaf5777858311523ac3153c2 + requires_dist: + - tabulate + - jinja2>=3.1.4 + - markdown + - pyyaml>=6.0 + - matplotlib + - bumpversion>=0.6.0 ; extra == 'dev' + - wheel>=0.42 ; extra == 'dev' + - watchdog>=2.0 ; extra == 'dev' + - flake8>=6.0.0 ; extra == 'dev' + - tox>=3.28,<5 ; extra == 'dev' + - coverage>=6.5,<8 ; extra == 'dev' + - cryptography>=43.0.1 ; extra == 'dev' + - pygments>=2.15.0 ; extra == 'dev' + - sphinx>=4.0 ; extra == 'docs' + - numpydoc ; extra == 'docs' + - sphinx-multiversion ; extra == 'docs' + - kentigern ; extra == 'docs' + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/45/93/b6760dd1904c2a498e5f43d1bb436f59383c3ddea3815f1461dfaa259373/numexpr-2.14.1-cp312-cp312-macosx_11_0_arm64.whl + name: numexpr + version: 2.14.1 + sha256: 47041f2f7b9e69498fb311af672ba914a60e6e6d804011caacb17d66f639e659 + requires_dist: + - numpy>=1.23.0 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/46/75/505cd72dfde3a1d7e719f840a2aa656a8c6b4c7b1b8c604a2d587cb1fc52/precession-2.1.1-py3-none-any.whl + name: precession + version: 2.1.1 + sha256: c58cbe813cb14acba7a27217b8aaf0f9555fee52ed68949dd578dee73d60d1b4 + requires_dist: + - numpy + - scipy>=1.8.0 +- pypi: https://files.pythonhosted.org/packages/59/91/aa6bde563e0085a02a435aa99b49ef75b0a4b062635e606dab23ce18d720/inflection-0.5.1-py2.py3-none-any.whl + name: inflection + version: 0.5.1 + sha256: f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2 + requires_python: '>=3.5' +- pypi: https://files.pythonhosted.org/packages/59/f3/77f85bfac22ee84b231ee8b147cdd948f679edf7d5a622acf9169d2a9b63/juliacall-0.9.34-py3-none-any.whl + name: juliacall + version: 0.9.34 + sha256: ac75b718a25357343d9be86981e5e5b7551621f3d2d5f58a815b852793857f41 + requires_dist: + - juliapkg>=0.1.21,<0.2 + requires_python: '>=3.10,<4' +- pypi: https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: pydantic-core + version: 2.46.4 + sha256: 926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce + requires_dist: + - typing-extensions>=4.14.1 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/65/90/774ddd08b2a1b41faa56da111f0fbfeb4f17ee537214c938ef41d61af949/ndindex-1.10.1-cp312-cp312-macosx_10_13_x86_64.whl + name: ndindex + version: 1.10.1 + sha256: 87f83e8c35a7f49a68cd3a3054c406e6c22f8c1315f3905f7a778c657669187e + requires_dist: + - numpy ; extra == 'arrays' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/66/8f/2515bd2f110f6316f39568e25943577effffa5283b6376969e8277ba330d/sxs-2025.0.12-py3-none-any.whl + name: sxs + version: 2025.0.12 + sha256: 96bf016c94505cbd28d29ac4d6b3ff95c7ff7f5f142a2ca38a6eb8e926f24bbf + requires_dist: + - h5py>=3 + - inflection>=0.5.1 + - juliacall>=0.9.20 + - numba>=0.55 ; implementation_name == 'cpython' + - numpy>=1.25 + - packaging + - pandas>=1.1.2 + - pytz>=2020.1 + - quaternionic>=1.0.15 + - requests>=2.24.0 + - scipy>=1.13 + - spherical>=1.0.15 + - sxscatalog>=3.0.14 + - tqdm>=4.63.1 + - urllib3>=1.25.10 + - mkdocs ; extra == 'docs' + - mkdocs-jupyter ; extra == 'docs' + - mkdocs-material ; extra == 'docs' + - mkdocstrings-python ; extra == 'docs' + - ipykernel>=6.29 ; extra == 'ecosystem' + - ipywidgets>=8.0 ; extra == 'ecosystem' + - jupyterlab>=4.2 ; extra == 'ecosystem' + - line-profiler>=3.0.2 ; extra == 'ecosystem' + - matplotlib>=2.1.1 ; extra == 'ecosystem' + - memory-profiler>=0.57.0 ; extra == 'ecosystem' + - numpy-quaternion>=2023.0.4 ; extra == 'ecosystem' + - numpy>=1.20,<2.0 ; extra == 'ecosystem' + - qgridnext>=2.0 ; extra == 'ecosystem' + - rise>=5.6.1 ; extra == 'ecosystem' + - scri>=2020.8.18 ; sys_platform != 'win32' and extra == 'ecosystem' + - seaborn>=0.13 ; extra == 'ecosystem' + - spinsfast>=2022 ; sys_platform != 'win32' and extra == 'ecosystem' + - sympy>=1.6.2 ; extra == 'ecosystem' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/66/92/8e7104d2cf40ba2f88528c01e8a063aba7b223a21c0310fcf043b441f5da/sxscatalog-3.0.28-py3-none-any.whl + name: sxscatalog + version: 3.0.28 + sha256: 622c8c52f93d744e3464c72ac087bed85df7e44908c19b0416db7afd970e9fa2 + requires_dist: + - numpy + - packaging + - pandas + - requests + - tqdm + - bs4 ; extra == 'scrape-rit' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/6d/a3/e2d6b17b02b5c52ce6c68fce7f2f190e796c2c1c5419e675f6a947fdb78c/qnm-0.4.4-py3-none-any.whl + name: qnm + version: 0.4.4 + sha256: 63711c37ff4847c8cb59e6266da5baf81f88cc6ab728f40fc3fe39a3025f6252 + requires_dist: + - numpy + - scipy + - numba + - tqdm +- pypi: https://files.pythonhosted.org/packages/6f/33/81570e825bdb2c0d7fa705087b704007bb7898c7dbb7a64f868da59252b3/pyseobnr-0.3.6.tar.gz + name: pyseobnr + version: 0.3.6 + sha256: 46508432709e637e6661252b55675bfb002a4425b770bb94b0bb4194ccdc5b97 + requires_dist: + - numpy>=1.23.0 + - scipy>=1.8.0 + - h5py + - numexpr + - numba + - qnm + - scri + - pygsl-lite + - lalsuite + - importlib-resources ; python_full_version < '3.10' + - bilby ; extra == 'checks' + - gwsurrogate ; extra == 'checks' + - pathos ; extra == 'checks' + - scikit-optimize ; extra == 'checks' + - matplotlib ; extra == 'checks' + - nbsphinx ; extra == 'docs' + - numpydoc ; extra == 'docs' + - sphinx ; extra == 'docs' + - sphinx-rtd-theme ; extra == 'docs' + - sphinx-tabs ; extra == 'docs' + - cython ; extra == 'docs' + - sphinx-gallery ; extra == 'docs' + - jupytext ; extra == 'docs' + - jupyterlab ; extra == 'docs' + - pandas ; extra == 'docs' + - pyarrow ; extra == 'docs' + - seaborn ; extra == 'docs' + - pycbc ; extra == 'docs' + - astropy ; extra == 'docs' + - gwpy ; extra == 'docs' + - matplotlib ; extra == 'docs' + - pytest ; extra == 'tests' + - pytest-sugar ; extra == 'tests' + - pandas ; extra == 'tests' + - pyarrow ; extra == 'tests' + - pycbc ; extra == 'tests' + - gwpy ; extra == 'tests' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/78/17/853354204e1ca022d6b7d011ca7f3206c4f8faa3cc743e92609b49c1d83f/tinydb-4.8.2-py3-none-any.whl + name: tinydb + version: 4.8.2 + sha256: f97030ee5cbc91eeadd1d7af07ab0e48ceb04aa63d4a983adbaca4cba16e86c3 + requires_python: '>=3.8,<4.0' +- pypi: https://files.pythonhosted.org/packages/78/74/6568c8d3aabf9982ab89fe3e378afbd7aad4894bde4570991a3246169ef4/tables-3.11.1-cp311-abi3-macosx_11_0_arm64.whl + name: tables + version: 3.11.1 + sha256: f0367d2e3df0f10ea63ccf4279f3fe58e32ec481767320301a483e2b3cd83efc + requires_dist: + - numpy>=1.20.0 + - numexpr>=2.6.2 + - packaging + - py-cpuinfo + - blosc2>=2.3.0 + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl + name: annotated-types + version: 0.7.0 + sha256: 1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 + requires_dist: + - typing-extensions>=4.0.0 ; python_full_version < '3.9' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/7d/4a/75aadc3b17430d4d9f3ad7bf0332a22b7ef78312e2b5b70774d2a301b9ec/spherical-1.0.18-py3-none-any.whl + name: spherical + version: 1.0.18 + sha256: 4ed6ccc3964c5338b197f70e2c68e36e73fea9da7b3f53c9f24d6922f687242b + requires_dist: + - numba>=0.55 ; implementation_name == 'cpython' + - numpy>=1.20 + - quaternionic>=1.0 + - scipy>=1.5 + - spinsfast>=104.2020.8 ; sys_platform != 'win32' + - mkdocs ; extra == 'docs' + - mktheapidocs ; extra == 'docs' + - pymdown-extensions ; extra == 'docs' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl + name: flask + version: 3.1.3 + sha256: f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c + requires_dist: + - blinker>=1.9.0 + - click>=8.1.3 + - importlib-metadata>=3.6.0 ; python_full_version < '3.10' + - itsdangerous>=2.2.0 + - jinja2>=3.1.2 + - markupsafe>=2.1.1 + - werkzeug>=3.1.0 + - asgiref>=3.2 ; extra == 'async' + - python-dotenv ; extra == 'dotenv' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl + name: filelock + version: 3.29.0 + sha256: 96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/81/68/dddd76117df2ef14c943c6bbb6618be5c9401280046f4ddfc9fb4596a1b8/statsmodels-0.14.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + name: statsmodels + version: 0.14.6 + sha256: 19b58cf7474aa9e7e3b0771a66537148b2df9b5884fbf156096c0e6c1ff0469d + requires_dist: + - numpy>=1.22.3,<3 + - scipy>=1.8,!=1.9.2 + - pandas>=1.4,!=2.1.0 + - patsy>=0.5.6 + - packaging>=21.3 + - cython>=3.0.10 ; extra == 'build' + - cython>=3.0.10 ; extra == 'develop' + - cython>=3.0.10,<4 ; extra == 'develop' + - setuptools-scm[toml]~=8.0 ; extra == 'develop' + - matplotlib>=3 ; extra == 'develop' + - colorama ; extra == 'develop' + - joblib ; extra == 'develop' + - jinja2 ; extra == 'develop' + - pytest>=7.3.0,<8 ; extra == 'develop' + - pytest-randomly ; extra == 'develop' + - pytest-xdist ; extra == 'develop' + - pytest-cov ; extra == 'develop' + - pywinpty ; os_name == 'nt' and extra == 'develop' + - flake8 ; extra == 'develop' + - isort ; extra == 'develop' + - sphinx ; extra == 'docs' + - nbconvert ; extra == 'docs' + - jupyter-client ; extra == 'docs' + - ipykernel ; extra == 'docs' + - matplotlib ; extra == 'docs' + - nbformat ; extra == 'docs' + - numpydoc ; extra == 'docs' + - pandas-datareader ; extra == 'docs' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl + name: seaborn + version: 0.13.2 + sha256: 636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987 + requires_dist: + - numpy>=1.20,!=1.24.0 + - pandas>=1.2 + - matplotlib>=3.4,!=3.6.1 + - pytest ; extra == 'dev' + - pytest-cov ; extra == 'dev' + - pytest-xdist ; extra == 'dev' + - flake8 ; extra == 'dev' + - mypy ; extra == 'dev' + - pandas-stubs ; extra == 'dev' + - pre-commit ; extra == 'dev' + - flit ; extra == 'dev' + - numpydoc ; extra == 'docs' + - nbconvert ; extra == 'docs' + - ipykernel ; extra == 'docs' + - sphinx<6.0.0 ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - sphinx-issues ; extra == 'docs' + - sphinx-design ; extra == 'docs' + - pyyaml ; extra == 'docs' + - pydata-sphinx-theme==0.10.0rc2 ; extra == 'docs' + - scipy>=1.7 ; extra == 'stats' + - statsmodels>=0.12 ; extra == 'stats' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/86/9d/1f4495bf047e61e904a69242941aca04ad3c7453639d9019c5d8facb3862/juliapkg-0.1.23-py3-none-any.whl + name: juliapkg + version: 0.1.23 + sha256: 195eb0986d83c7e4df7781290d1158e5dd8809dada634ff2e8196ea16608f1a0 + requires_dist: + - filelock>=3.16,<4.0 + - semver>=3.0,<4.0 + - tomli>=2.0,<3.0 + - tomlkit>=0.13.3,<0.15 + - click>=8.0,<9.0 ; extra == 'cli' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/88/f1/2033813456213ecfe8ccab18d78ef7b00af70d049c916cb167d592bdd6da/scri-2022.9.0-py3-none-any.whl + name: scri + version: 2022.9.0 + sha256: 8ce8dfa40cdf20f8b8b009e9cc5ab0da638ccf679dcd9e1d01d17fc374db4588 + requires_dist: + - h5py>=3 + - numba>=0.55 + - numpy-quaternion>=2022.4 + - numpy>=1.20,<2 + - scipy>=1.12 + - spherical-functions>=2022.4 + - spinsfast>=2022.4 + - sxs>=2022.4.0 + - tqdm>=4.63.1 + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/90/ad/cba91b3bcf04073e4d1655a5c1710ef3f457f56f7d1b79dcc3d72f4dd912/plotly-6.7.0-py3-none-any.whl + name: plotly + version: 6.7.0 + sha256: ac8aca1c25c663a59b5b9140a549264a5badde2e057d79b8c772ae2920e32ff0 + requires_dist: + - narwhals>=1.15.1 + - packaging + - anywidget ; extra == 'dev' + - build ; extra == 'dev' + - colorcet ; extra == 'dev' + - fiona<=1.9.6 ; python_full_version < '3.9' and extra == 'dev' + - geopandas ; extra == 'dev' + - inflect ; extra == 'dev' + - jupyterlab ; extra == 'dev' + - kaleido>=1.1.0 ; extra == 'dev' + - numpy>=1.22 ; extra == 'dev' + - orjson ; extra == 'dev' + - pandas ; extra == 'dev' + - pdfrw ; extra == 'dev' + - pillow ; extra == 'dev' + - plotly-geo ; extra == 'dev' + - polars[timezone] ; extra == 'dev' + - pyarrow ; extra == 'dev' + - pyshp ; extra == 'dev' + - pytest ; extra == 'dev' + - pytz ; extra == 'dev' + - requests ; extra == 'dev' + - ruff==0.11.12 ; extra == 'dev' + - scikit-image ; extra == 'dev' + - scipy ; extra == 'dev' + - shapely ; extra == 'dev' + - statsmodels ; extra == 'dev' + - vaex ; python_full_version < '3.10' and extra == 'dev' + - xarray ; extra == 'dev' + - build ; extra == 'dev-build' + - jupyterlab ; extra == 'dev-build' + - pytest ; extra == 'dev-build' + - requests ; extra == 'dev-build' + - ruff==0.11.12 ; extra == 'dev-build' + - pytest ; extra == 'dev-core' + - requests ; extra == 'dev-core' + - ruff==0.11.12 ; extra == 'dev-core' + - anywidget ; extra == 'dev-optional' + - build ; extra == 'dev-optional' + - colorcet ; extra == 'dev-optional' + - fiona<=1.9.6 ; python_full_version < '3.9' and extra == 'dev-optional' + - geopandas ; extra == 'dev-optional' + - inflect ; extra == 'dev-optional' + - jupyterlab ; extra == 'dev-optional' + - kaleido>=1.1.0 ; extra == 'dev-optional' + - numpy>=1.22 ; extra == 'dev-optional' + - orjson ; extra == 'dev-optional' + - pandas ; extra == 'dev-optional' + - pdfrw ; extra == 'dev-optional' + - pillow ; extra == 'dev-optional' + - plotly-geo ; extra == 'dev-optional' + - polars[timezone] ; extra == 'dev-optional' + - pyarrow ; extra == 'dev-optional' + - pyshp ; extra == 'dev-optional' + - pytest ; extra == 'dev-optional' + - pytz ; extra == 'dev-optional' + - requests ; extra == 'dev-optional' + - ruff==0.11.12 ; extra == 'dev-optional' + - scikit-image ; extra == 'dev-optional' + - scipy ; extra == 'dev-optional' + - shapely ; extra == 'dev-optional' + - statsmodels ; extra == 'dev-optional' + - vaex ; python_full_version < '3.10' and extra == 'dev-optional' + - xarray ; extra == 'dev-optional' + - numpy>=1.22 ; extra == 'express' + - kaleido>=1.1.0 ; extra == 'kaleido' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl + name: werkzeug + version: 3.1.8 + sha256: 63a77fb8892bf28ebc3178683445222aa500e48ebad5ec77b0ad80f8726b1f50 + requires_dist: + - markupsafe>=2.1.1 + - watchdog>=2.3 ; extra == 'watchdog' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/97/8d/17e56f2208b885aa8462277ae75a9bc82f7877bb52d88690636246853032/requests_pelican-0.2.0-py3-none-any.whl + name: requests-pelican + version: 0.2.0 + sha256: e4f4aab8c9a6c1f6a72271ce5e78836161159e52f65b791746a83931c835f26c + requires_dist: + - requests>=2.32.3 + - furo ; extra == 'docs' + - sphinx ; extra == 'docs' + - sphinx-automodapi ; extra == 'docs' + - requests-scitokens>=0.1.0 ; extra == 'scitokens' + - pytest>=3.9.1 ; extra == 'test' + - pytest-cov ; extra == 'test' + - pytest-remotedata ; extra == 'test' + - requests-mock ; extra == 'test' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/99/29/c2dc674ea70fa9a4819417289a9c0d3e4780835beeed573eb66964cfb763/tables-3.11.1-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + name: tables + version: 3.11.1 + sha256: 1e78fe190fdeb4afe430b79651bae2a4f341904eb85aa8dbafe5f1caee1c7f67 + requires_dist: + - numpy>=1.20.0 + - numexpr>=2.6.2 + - packaging + - py-cpuinfo + - blosc2>=2.3.0 + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl + name: tabulate + version: 0.10.0 + sha256: f0b0622e567335c8fabaaa659f1b33bcb6ddfe2e496071b743aa113f8774f2d3 + requires_dist: + - wcwidth ; extra == 'widechars' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/9d/20/c473fc04a371f5e2f8c5749e04505c13e7a8ede27c09e9f099b2ad6f43d6/numexpr-2.14.1-cp312-cp312-macosx_10_13_x86_64.whl + name: numexpr + version: 2.14.1 + sha256: 91ebae0ab18c799b0e6b8c5a8d11e1fa3848eb4011271d99848b297468a39430 + requires_dist: + - numpy>=1.23.0 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + name: gitdb + version: 4.0.12 + sha256: 67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf + requires_dist: + - smmap>=3.0.1,<6 + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/a6/24/4d91e05817e92e3a61c8a21e08fd0f390f5301f1c448b137c57c4bc6e543/semver-3.0.4-py3-none-any.whl + name: semver + version: 3.0.4 + sha256: 9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746 + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/a8/30/12c87de87d6a3b523996737b97ba2e75972afac3c804249e5e61036e03d7/spinsfast-2022.4.10.tar.gz + name: spinsfast + version: 2022.4.10 + sha256: a404c91d34e6df54741c925416d6a5be89a9801446af3a0e55280756e491baad +- pypi: https://files.pythonhosted.org/packages/ad/28/9f3700cb784c0f0475de7074ed489702d5c6fb63a3df56b4cb4333b055fc/liquidpy-0.9.0-py3-none-any.whl + name: liquidpy + version: 0.9.0 + sha256: df590d9cf89599cf3ee1b017f77e98e1586022685f7d55c9fb2820095e134aa8 + requires_dist: + - jinja2>=3,<4 + - markdown>=3.5 ; extra == 'extra' + - python-dateutil>=2.8 ; extra == 'extra' + - python-frontmatter>=1.0 ; extra == 'extra' + - python-slugify>=8 ; extra == 'extra' + - regex>=2024.11 ; extra == 'extra' + - toml>=0.10 ; extra == 'extra' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/b3/8a/eafc962e29c7fe800c702b4ae09cc358f1cbe40089e7a8cdac4f5b4c8c7a/requests_scitokens-0.2.1-py3-none-any.whl + name: requests-scitokens + version: 0.2.1 + sha256: a7f2b826500ea2e2f311bbe10fb99c6a981ed40975459d552e681d36ca19acfb + requires_dist: + - requests>=2.32.0 + - scitokens>=1.8 + - pytest>=3.1.0 ; extra == 'test' + - pytest-cov>=2.4.0 ; extra == 'test' + - requests-mock ; extra == 'test' + - furo ; extra == 'docs' + - sphinx ; extra == 'docs' + - sphinx-automodapi ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - sphinx-design ; extra == 'docs' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl + name: tomlkit + version: 0.14.0 + sha256: 592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/ba/b2/138065f2c50fd66b89623a5ee45d43d2341cfede4d7e0f7292075953ff12/numpy_quaternion-2024.0.13-cp312-cp312-macosx_10_13_x86_64.whl + name: numpy-quaternion + version: 2024.0.13 + sha256: 95e35c3c1033984cdc0177c723f084c0e617d9065eb071287aae35b9d7f8d0f5 + requires_dist: + - numpy>=1.25,<3 + - scipy>=1.5,<2 + - mkdocs-material ; extra == 'docs' + - mkdocstrings-python ; extra == 'docs' + - pymdown-extensions ; extra == 'docs' + requires_python: '>=3.10,<3.15' +- pypi: https://files.pythonhosted.org/packages/bc/b2/d213f02254956b40f70dfaf2ba21ed0d1b7ed54b7cf361865d9d95fae867/asimov-0.6.1-py3-none-any.whl + name: asimov + version: 0.6.1 + sha256: 0bd278ed86e6f5cec0b5cfcdb4f058eaac5499856e102276af3d54518dba2e7c + requires_dist: + - click + - gitpython + - htcondor + - ligo-gracedb + - liquidpy + - networkx + - numpy + - otter-report>=0.3.3 + - python-gitlab + - pytz + - pyyaml + - requests + - gwpy + - lscsoft-glue>=2.0.0 + - igwn-auth-utils>=0.2.1 + - flask + - tinydb + - pillow>=10.2.0 +- pypi: https://files.pythonhosted.org/packages/bd/b1/6bc28d9441c48744b6ecfb46af928fca427a3213282efa0faa5bea7eefd8/pesummary-1.0.0-py3-none-any.whl + name: pesummary + version: 1.0.0 + sha256: d65d8dd6937ae5eff7ecb93b5c0a5568ba9f6f06cf74150aeffc37c0cc8797ba + requires_dist: + - astropy>=3.2.3 + - corner + - deepdish + - gwpy>=2.1.2 + - h5py + - lalsuite>=7.0.0 + - ligo-gracedb + - matplotlib + - numpy>=1.15.4 + - pandas + - pygments + - plotly + - pillow + - seaborn>=0.11.0 + - statsmodels + - scipy>=1.8.0 + - tables + - tqdm>=4.44.0 + - ipykernel ; extra == 'docs' + - jinja2==3.0.3 ; extra == 'docs' + - nbsphinx ; extra == 'docs' + - requests ; extra == 'docs' + - sphinx>=1.2.2 ; extra == 'docs' + - sphinx-argparse ; extra == 'docs' + - sphinx-rtd-theme ; extra == 'docs' + - sphinx-panels ; extra == 'docs' + - sphinxcontrib-programoutput ; extra == 'docs' + - bilby>=1.1.1 ; extra == 'extras' + - coloredlogs ; extra == 'extras' + - gitpython ; extra == 'extras' + - gwosc ; extra == 'extras' + - jupyter-client ; extra == 'extras' + - ligo-em-bright>=0.1.2 ; extra == 'extras' + - ligo-skymap ; extra == 'extras' + - nbformat ; extra == 'extras' + - pepredicates>=0.0.3 ; extra == 'extras' + - flake8>=3.7.0 ; extra == 'lint' + - flake8-bandit ; extra == 'lint' + - astropy>=3.2.3,!=4.3.0 ; extra == 'test' + - beautifulsoup4 ; extra == 'test' + - bilby-pipe ; extra == 'test' + - coverage ; extra == 'test' + - coverage-badge ; extra == 'test' + - cython>=0.28.5 ; extra == 'test' + - markupsafe==2.0.1 ; extra == 'test' + - pycbc>=2.0.3 ; extra == 'test' + - pytest>=3.0.0 ; extra == 'test' + - pytest-xdist ; extra == 'test' + - pytest-rerunfailures ; extra == 'test' + - pytest-cov ; extra == 'test' + - requests ; extra == 'test' + - testfixtures ; extra == 'test' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl + name: smmap + version: 5.0.3 + sha256: c106e05d5a61449cf6ba9a1e650227ecfb141590d2a98412103ff35d89fc7b2f + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl + name: pydantic-core + version: 2.46.4 + sha256: 3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2 + requires_dist: + - typing-extensions>=4.14.1 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/d5/9b/7a0e11d7858feac24372ae770575802833436a050f93dfd012d8025e4ae9/deepdish-0.3.7-py2.py3-none-any.whl + name: deepdish + version: 0.3.7 + sha256: 272d6b075239efe66dd3e6d4e89bb6b0ffe68b0d9ce4f823e4e1c4549c4a294a + requires_dist: + - numpy + - scipy + - tables + - skimage ; extra == 'image' +- pypi: https://files.pythonhosted.org/packages/d6/c6/7a6d8f56d3efe2ddb2bc659c2b1f9ba01b8a27174733526b57e181ba23b0/spherical_functions-2023.0.2-py3-none-any.whl + name: spherical-functions + version: 2023.0.2 + sha256: cb3a7fbbf3728893a5c230226db8c4ce86ce4208a2453591043a7b3cccf55e64 + requires_dist: + - numba>=0.55 + - numpy-quaternion>=2022 + - numpy>=1.20 + - scipy>=1.0 + - spinsfast>=2022 + - black>=22.1 ; extra == 'dev' + - line-profiler>=3.5.0 ; extra == 'dev' + - pytest-cov>=2.10.1 ; extra == 'dev' + - pytest>=7.0 ; extra == 'dev' + - sympy>=1.10 ; extra == 'dev' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/d9/43/560e9ba23c02c904b5934496486d061bcb14cd3ebba2e3cf0e2dccb6c22b/numexpr-2.14.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + name: numexpr + version: 2.14.1 + sha256: eee6d4fbbbc368e6cdd0772734d6249128d957b3b8ad47a100789009f4de7083 + requires_dist: + - numpy>=1.23.0 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/da/cc/ca6129956980fc6148d6b1983bc5dfc302f73bc734b376069eb560d170b6/blosc2-4.3.3-cp312-cp312-macosx_11_0_arm64.whl + name: blosc2 + version: 4.3.3 + sha256: 2153d08ba45f4a7bb632fa1eac9a7204b438336d7c7188edf3162f4a4087d719 + requires_dist: + - numpy>=1.26 + - ndindex + - msgpack + - numexpr>=2.14.1 ; platform_machine != 'wasm32' + - pydantic + - requests + - threadpoolctl ; platform_machine != 'wasm32' + - pyarrow ; extra == 'parquet' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl + name: typing-inspection + version: 0.4.2 + sha256: 4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7 + requires_dist: + - typing-extensions>=4.12.0 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/dc/9c/6a64269df4f032d883c5da72052850ef94fb79743b70606719c1ca579c79/numpy_quaternion-2024.0.13-cp312-cp312-macosx_11_0_arm64.whl + name: numpy-quaternion + version: 2024.0.13 + sha256: e64b15888c2d363fb1bd289f8d6f9785658edefe15acfc317395faca223a0ebb + requires_dist: + - numpy>=1.25,<3 + - scipy>=1.5,<2 + - mkdocs-material ; extra == 'docs' + - mkdocstrings-python ; extra == 'docs' + - pymdown-extensions ; extra == 'docs' + requires_python: '>=3.10,<3.15' +- pypi: https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl + name: markdown + version: 3.10.2 + sha256: e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36 + requires_dist: + - coverage ; extra == 'testing' + - pyyaml ; extra == 'testing' + - mkdocs>=1.6 ; extra == 'docs' + - mkdocs-nature>=0.6 ; extra == 'docs' + - mdx-gh-links>=0.2 ; extra == 'docs' + - mkdocstrings[python]>=0.28.3 ; extra == 'docs' + - mkdocs-gen-files ; extra == 'docs' + - mkdocs-section-index ; extra == 'docs' + - mkdocs-literate-nav ; extra == 'docs' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/df/b1/3e3144c3ada2ae1a60a378396629064ef14e425e356c8e7c1db3a50ef351/python_gitlab-8.3.0-py3-none-any.whl + name: python-gitlab + version: 8.3.0 + sha256: 7aace3585d57ee97ebcb74bc776f30af3e861e285290082a91646348aca3f22a + requires_dist: + - requests>=2.32.0 + - requests-toolbelt>=1.0.0 + - argcomplete>=1.10.0,<3 ; extra == 'autocompletion' + - pyyaml>=6.0.1 ; extra == 'yaml' + - gql[httpx]>=3.5.0,<5 ; extra == 'graphql' + requires_python: '>=3.10.0' +- pypi: https://files.pythonhosted.org/packages/e0/a9/023730ba63db1e494a271cb018dcd361bd2c917ba7004c3e49d5daf795a2/py_cpuinfo-9.0.0-py3-none-any.whl + name: py-cpuinfo + version: 9.0.0 + sha256: 859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5 +- pypi: https://files.pythonhosted.org/packages/e3/47/a8d68aed3f6cb55bae90f504cd5ea3698a923c5fb6e9690726359e28eb1c/asimov_gwdata-0.7.1-py3-none-any.whl + name: asimov-gwdata + version: 0.7.1 + sha256: 1e9ffd306550adec215d7b05530e3616efceda7fe422ca16f6b7afcea867bffc + requires_dist: + - gwosc + - asimov>=0.4.0 + - pesummary + - numpy + - requests + - click + - igwn-auth-utils + - requests-pelican + - requests-scitokens>=0.1.0 + - gwpy + - matplotlib + - otter-report + - sphinx ; extra == 'docs' + - kentigern ; extra == 'docs' + - rift ; extra == 'rift' + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/ed/ee/a423e857f5b45da3adc8ddbcfbfd4a0e9a047edce3915d3e3d6e189b6bd9/ndindex-1.10.1-cp312-cp312-macosx_11_0_arm64.whl + name: ndindex + version: 1.10.1 + sha256: cf9e05986b2eb8c5993bce0f911d6cedd15bda30b5e35dd354b1ad1f4cc3599d + requires_dist: + - numpy ; extra == 'arrays' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/f1/70/ba4b949bdc0490ab78d545459acd7702b211dfccf7eb89bbc1060f52818d/patsy-1.0.2-py2.py3-none-any.whl + name: patsy + version: 1.0.2 + sha256: 37bfddbc58fcf0362febb5f54f10743f8b21dd2aa73dec7e7ef59d1b02ae668a + requires_dist: + - numpy>=1.4 + - pytest ; extra == 'test' + - pytest-cov ; extra == 'test' + - scipy ; extra == 'test' + requires_python: '>=3.6' +- pypi: https://files.pythonhosted.org/packages/fa/bb/4a9cde6628563388db26fa86c64adb0f2475a757e72af0ec185fd520b72f/tables-3.11.1-cp311-abi3-macosx_10_9_x86_64.whl + name: tables + version: 3.11.1 + sha256: eb30684c42a77bbecdef2b9c763c4372b0ddc9cc5bd8b2a2055f2042eee67217 + requires_dist: + - numpy>=1.20.0 + - numexpr>=2.6.2 + - packaging + - py-cpuinfo + - blosc2>=2.3.0 + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl + name: pydantic + version: 2.13.4 + sha256: 45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba + requires_dist: + - annotated-types>=0.6.0 + - pydantic-core==2.46.4 + - typing-extensions>=4.14.1 + - typing-inspection>=0.4.2 + - email-validator>=2.0.0 ; extra == 'email' + - tzdata ; python_full_version >= '3.9' and sys_platform == 'win32' and extra == 'timezone' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/fd/86/e74734fcc564c7c9dfb47758ea9699c5294c68979fe0d0017454f3ca3f54/quaternionic-1.0.17-py3-none-any.whl + name: quaternionic + version: 1.0.17 + sha256: 5cb6d7948dc2a782575b0582ced8ea07ccb41b615c15cf14ea4d058434524252 + requires_dist: + - numba>=0.55 ; implementation_name == 'cpython' + - numpy>=1.20 + - scipy>=1.5 + - mkdocs ; extra == 'docs' + - mktheapidocs ; extra == 'docs' + - pymdown-extensions ; extra == 'docs' + requires_python: '>=3.8' diff --git a/pixi.toml b/pixi.toml new file mode 100644 index 000000000..ed59bdf85 --- /dev/null +++ b/pixi.toml @@ -0,0 +1,90 @@ +# Root pixi project for RIFT development and SWIG compatibility testing. +# +# Default local environment: +# pixi install +# pixi run install-rift +# pixi run import-check +# +# SWIG comparison environments: +# pixi run -e swig-pre44 swig-version +# pixi run -e swig-post44 swig-version +# pixi run -e swig-pre44 import-check +# pixi run -e swig-post44 import-check +# +# RIFT is sensitive to generated SWIG bindings. Keep the default development +# environment on SWIG <4.4.0, but preserve a >=4.4.0 lane for deployment checks. + +[workspace] +name = "rift-dev" +version = "0.0.18.0rc0" +description = "Development and CI environments for RIFT." +authors = ["RIFT developers"] +channels = ["conda-forge"] +platforms = ["linux-64", "osx-64", "osx-arm64"] + +[dependencies] +python = ">=3.11,<3.13" +pip = "*" +setuptools = "*" +wheel = "*" +pytest = "*" +coverage = "*" +natsort = "*" +numpy = "<2.0" +h5py = "*" +numba = ">=0.56" +pandas = "*" +corner = "*" +scikit-learn = "*" +matplotlib = "*" +lalsuite = "==7.25" +lalmetaio = "<=4.0.5" +gwdatafind = ">=1.2" +igwn-segments = "*" +igwn-ligolw = "*" +six = "*" +"ligo.skymap" = "*" +gwpy = "*" +scipy = ">=1.9.3" +htcondor = "*" +pybind11 = ">=2.12" +gsl = "*" +hydra-core = "*" +omegaconf = "*" +pyyaml = "*" + +[pypi-dependencies] +# requirements.txt currently spells this as precession[chip_avg], but the +# published PyPI metadata exposes no chip_avg extra; the base package is what +# pip/pixi can actually resolve. +precession = "*" +pyseobnr = "*" +asimov = ">=0.5.6" +asimov-gwdata = ">=0.4.0" + +[feature.swig-pre44.dependencies] +swig = "<4.4.0" + +[feature.swig-post44.dependencies] +swig = ">=4.4.0" + +[environments] +default = ["swig-pre44"] +swig-pre44 = ["swig-pre44"] +swig-post44 = ["swig-post44"] + +[activation.env] +GW_SURROGATE = "" +RIFT_ROOT = "$PIXI_PROJECT_ROOT" +RIFT_PY = "$PIXI_PROJECT_ROOT/MonteCarloMarginalizeCode/Code" +RIFT_BIN = "$PIXI_PROJECT_ROOT/MonteCarloMarginalizeCode/Code/bin" +PYTHONPATH = "$PIXI_PROJECT_ROOT/MonteCarloMarginalizeCode/Code:$PYTHONPATH" +PATH = "$PIXI_PROJECT_ROOT/MonteCarloMarginalizeCode/Code/bin:$PATH" + +[tasks] +install-rift = "python -m pip install --no-deps --editable ." +swig-version = "swig -version" +import-check = "python .travis/test-all-mod.py" +help-check = "bash .travis/test-all-bin.sh" +sim-manager-check = "python MonteCarloMarginalizeCode/Code/test/test_simulation_manager.py -v" +test-likelihood = "python MonteCarloMarginalizeCode/Code/test/test_likelihood.py"