diff --git a/AGENTS.md b/AGENTS.md index e6446f27..e59d75bd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -33,6 +33,8 @@ SkalaXC's bundled model selectors are `LDA`, `PBE`, and `TPSS`, or an explicit ` ```bash pixi install --locked -e default ``` + On Linux x86_64 with a CUDA 12-compatible driver, use `pixi install --locked -e dev` + for the GPU-enabled superset with profiling, IPython, notebook, and plotting tools. 3. **Pre-commit hooks** (required before committing): ```bash pixi run -e default pre-commit install diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e273ca59..7599952f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,6 +23,16 @@ pixi install --locked -e default pixi run -e default pre-commit install ``` +On Linux x86_64 with a CUDA 12-compatible driver, the `dev` environment is the +GPU-enabled development superset. It adds GPU4PySCF, profiling, IPython, a +notebook kernel, and plotting tools while retaining the default test, lint, +model, and benchmark tooling: + +```bash +pixi install --locked -e dev +pixi run -e dev ipython +``` + Run the standard checks in their Pixi environments: ```bash diff --git a/README.md b/README.md index 1bc81e13..47a6dc7e 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,8 @@ pip install skala-cuda12x The `skala-cuda13x` package is available for CUDA 13. -For a reproducible source environment, choose one of the locked GPU environments: +For a reproducible source environment, choose one of the locked GPU compatibility +environments: | Environment | CUDA | PyTorch | |---|---:|---:| diff --git a/benchmarks/.gitignore b/benchmarks/.gitignore new file mode 100644 index 00000000..fbca2253 --- /dev/null +++ b/benchmarks/.gitignore @@ -0,0 +1 @@ +results/ diff --git a/benchmarks/pyscf_ao_screening_performance.ipynb b/benchmarks/pyscf_ao_screening_performance.ipynb new file mode 100644 index 00000000..177feba7 --- /dev/null +++ b/benchmarks/pyscf_ao_screening_performance.ipynb @@ -0,0 +1,897 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "236c0ac1", + "metadata": {}, + "source": [ + "# Skala PySCF / GPU4PySCF Benchmark Results\n", + "\n", + "This notebook loads and compares JSON produced by `benchmarks/run_pyscf_ao_screening_benchmark.py`. It does not construct molecules, load Skala, or execute benchmark workloads.\n", + "\n", + "Generate result files from a shell before opening the analysis cells:\n", + "\n", + "```bash\n", + "python benchmarks/run_pyscf_ao_screening_benchmark.py --label mr\n", + "python benchmarks/run_pyscf_ao_screening_benchmark.py \\\n", + " --label main \\\n", + " --source-root /path/to/main-worktree\n", + "```\n", + "\n", + "Add `--smoke` to run only C4H10, or `--preflight-only` to validate the selected checkout and environment without collecting measurements." + ] + }, + { + "cell_type": "markdown", + "id": "e758ba91", + "metadata": {}, + "source": [ + "## Select Result Files\n", + "\n", + "By default, every compatible molecule-benchmark result in `benchmarks/results` is loaded. Results with other schemas, such as rotation comparisons, are reported and ignored. Replace `SELECTED_RESULT_FILES` with an explicit list when comparing only particular labels or commits." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6909409", + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import annotations\n", + "\n", + "import json\n", + "from pathlib import Path\n", + "from typing import Any\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "MODES = (\"cpu\", \"cpu_dense\", \"gpu\")\n", + "MEASUREMENTS = (\"runtime\", \"memory\")\n", + "TERMINAL_STATUSES = {\n", + " \"ok\",\n", + " \"timeout\",\n", + " \"oom\",\n", + " \"error\",\n", + " \"unsupported\",\n", + " \"skipped_after_resource_failure\",\n", + "}\n", + "SCIENTIFIC_CONFIG_KEYS = (\n", + " \"functional\",\n", + " \"basis\",\n", + " \"grid_level\",\n", + " \"grid_alignment\",\n", + " \"skalaxc_grid_size\",\n", + " \"max_memory_mb\",\n", + " \"cpu_threads\",\n", + " \"full_carbon_counts\",\n", + " \"expected_ao_counts\",\n", + ")\n", + "\n", + "\n", + "def find_repository_root(start: Path) -> Path:\n", + " for candidate in (start.resolve(), *start.resolve().parents):\n", + " if (candidate / \"pyproject.toml\").is_file() and (\n", + " candidate / \"benchmarks\"\n", + " ).is_dir():\n", + " return candidate\n", + " raise FileNotFoundError(f\"Could not find the Skala repository above {start}\")\n", + "\n", + "\n", + "def is_molecule_benchmark_result(path: Path) -> bool:\n", + " document = json.loads(path.read_text(encoding=\"utf-8\"))\n", + " if document.get(\"schema_version\") == 2:\n", + " return isinstance(\n", + " document.get(\"implementations\", {}).get(\"skala\", {}).get(\"molecules\"),\n", + " dict,\n", + " )\n", + " return isinstance(document.get(\"molecules\"), dict)\n", + "\n", + "\n", + "REPOSITORY_ROOT = find_repository_root(Path.cwd())\n", + "RESULTS_DIR = REPOSITORY_ROOT / \"benchmarks\" / \"results\"\n", + "CANDIDATE_RESULT_FILES = sorted(RESULTS_DIR.glob(\"skala-pyscf-ao-screening-*.json\"))\n", + "SELECTED_RESULT_FILES = [\n", + " path for path in CANDIDATE_RESULT_FILES if is_molecule_benchmark_result(path)\n", + "]\n", + "IGNORED_RESULT_FILES = [\n", + " path for path in CANDIDATE_RESULT_FILES if path not in SELECTED_RESULT_FILES\n", + "]\n", + "\n", + "print(f\"Selected {len(SELECTED_RESULT_FILES)} result file(s) from {RESULTS_DIR}\")\n", + "for result_file in SELECTED_RESULT_FILES:\n", + " print(f\" {result_file.name}\")\n", + "if IGNORED_RESULT_FILES:\n", + " print(\"Ignored incompatible result file(s):\")\n", + " for result_file in IGNORED_RESULT_FILES:\n", + " print(f\" {result_file.name}\")" + ] + }, + { + "cell_type": "markdown", + "id": "8284d45b", + "metadata": {}, + "source": [ + "## Load and Validate\n", + "\n", + "The checks below surface incompatible schemas, scientific settings, hardware, routing implementations, dirty checkouts, unexpected statuses, AO counts, and production-versus-CPU-dense fingerprint differences." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f4de7a3a", + "metadata": {}, + "outputs": [], + "source": [ + "def implementation_view(\n", + " document: dict[str, Any], implementation: str\n", + ") -> dict[str, Any] | None:\n", + " schema_version = document.get(\"schema_version\")\n", + " if schema_version == 1:\n", + " if implementation != \"skala\" or not isinstance(document.get(\"molecules\"), dict):\n", + " return None\n", + " view = dict(document)\n", + " view[\"implementation\"] = \"skala\"\n", + " view[\"modes\"] = list(MODES)\n", + " return view\n", + " if schema_version != 2:\n", + " return None\n", + " implementation_record = document.get(\"implementations\", {}).get(implementation)\n", + " if not isinstance(implementation_record, dict):\n", + " return None\n", + " view = dict(document)\n", + " view[\"implementation\"] = implementation\n", + " view[\"molecules\"] = implementation_record.get(\"molecules\", {})\n", + " view[\"modes\"] = implementation_record.get(\"modes\", [])\n", + " if implementation != \"skala\":\n", + " view[\"run_label\"] = f\"{document['run_label']}:{implementation}\"\n", + " return view\n", + "\n", + "\n", + "def validate_result_document(document: dict[str, Any]) -> list[str]:\n", + " errors: list[str] = []\n", + " if document.get(\"schema_version\") not in {1, 2}:\n", + " errors.append(f\"Unsupported schema version: {document.get('schema_version')}\")\n", + " for formula, molecule in document.get(\"molecules\", {}).items():\n", + " observed = molecule.get(\"observed\")\n", + " if observed is not None:\n", + " if observed.get(\"actual_aos\") != molecule.get(\"expected_aos\"):\n", + " errors.append(\n", + " f\"{formula}: expected {molecule.get('expected_aos')} AOs, \"\n", + " f\"observed {observed.get('actual_aos')}\"\n", + " )\n", + " carbon_count = int(molecule[\"carbon_count\"])\n", + " expected_electrons = 8 * carbon_count + 2\n", + " if observed.get(\"electron_count\") != expected_electrons:\n", + " errors.append(\n", + " f\"{formula}: expected {expected_electrons} electrons, \"\n", + " f\"observed {observed.get('electron_count')}\"\n", + " )\n", + " for mode, mode_record in molecule.get(\"modes\", {}).items():\n", + " for measurement in MEASUREMENTS:\n", + " result = mode_record.get(measurement)\n", + " if result is not None and result.get(\"status\") not in TERMINAL_STATUSES:\n", + " errors.append(\n", + " f\"{formula} {mode} {measurement}: unknown status {result.get('status')}\"\n", + " )\n", + " return errors\n", + "\n", + "\n", + "def preferred_fingerprint(mode_record: dict[str, Any]) -> dict[str, float] | None:\n", + " for measurement in MEASUREMENTS:\n", + " result = mode_record.get(measurement, {})\n", + " if result.get(\"status\") == \"ok\" and \"fingerprint\" in result:\n", + " return result[\"fingerprint\"]\n", + " return None\n", + "\n", + "\n", + "def fingerprint_warnings(document: dict[str, Any]) -> list[str]:\n", + " messages: list[str] = []\n", + " if \"cpu_dense\" not in document.get(\"modes\", []):\n", + " return messages\n", + " for formula, molecule in document[\"molecules\"].items():\n", + " reference = preferred_fingerprint(molecule[\"modes\"][\"cpu_dense\"])\n", + " if reference is None:\n", + " continue\n", + " for production_mode, rtol, atol in (\n", + " (\"cpu\", 1e-8, 5e-8),\n", + " (\"gpu\", 1e-7, 2e-7),\n", + " ):\n", + " production = preferred_fingerprint(molecule[\"modes\"][production_mode])\n", + " if production is None:\n", + " continue\n", + " for key in production.keys() & reference.keys():\n", + " if not np.isclose(\n", + " production[key], reference[key], rtol=rtol, atol=atol\n", + " ):\n", + " messages.append(\n", + " f\"{formula} {production_mode}/cpu_dense: {key} differs \"\n", + " f\"({production[key]:.12g} vs {reference[key]:.12g})\"\n", + " )\n", + " return messages\n", + "\n", + "\n", + "def comparison_warnings(documents: list[dict[str, Any]]) -> list[str]:\n", + " messages: list[str] = []\n", + " if not documents:\n", + " return [\"No result documents were selected\"]\n", + " reference = documents[0]\n", + " reference_config = reference[\"configuration\"]\n", + " reference_environment = reference[\"environment\"]\n", + " for document in documents:\n", + " label = document[\"run_label\"]\n", + " if document[\"source\"].get(\"dirty\"):\n", + " messages.append(f\"{label}: source checkout is dirty\")\n", + " messages.extend(\n", + " f\"{label}: {error}\" for error in validate_result_document(document)\n", + " )\n", + " messages.extend(\n", + " f\"{label}: {warning}\" for warning in fingerprint_warnings(document)\n", + " )\n", + " for document in documents[1:]:\n", + " label = document[\"run_label\"]\n", + " for key in SCIENTIFIC_CONFIG_KEYS:\n", + " if document[\"configuration\"].get(key) != reference_config.get(key):\n", + " messages.append(f\"{label}: configuration differs for {key}\")\n", + " for key_path in ((\"hostname\",), (\"platform\",), (\"cuda\", \"device_name\")):\n", + " left: Any = reference_environment\n", + " right: Any = document[\"environment\"]\n", + " for key in key_path:\n", + " left = left.get(key) if isinstance(left, dict) else None\n", + " right = right.get(key) if isinstance(right, dict) else None\n", + " if left != right:\n", + " messages.append(\n", + " f\"{label}: environment differs for {'.'.join(key_path)}\"\n", + " )\n", + "\n", + " route_implementations: dict[str, set[str]] = {mode: set() for mode in MODES}\n", + " for document in documents:\n", + " for molecule in document[\"molecules\"].values():\n", + " for mode in MODES:\n", + " implementation = (\n", + " molecule[\"modes\"][mode].get(\"route\", {}).get(\"implementation\")\n", + " )\n", + " if implementation:\n", + " route_implementations[mode].add(implementation)\n", + " for mode, implementations in route_implementations.items():\n", + " if len(implementations) > 1:\n", + " messages.append(\n", + " f\"{mode}: routing implementations differ: {sorted(implementations)}\"\n", + " )\n", + " return messages\n", + "\n", + "\n", + "def load_result_documents(\n", + " paths: list[Path], implementation: str\n", + ") -> list[dict[str, Any]]:\n", + " raw_documents = [json.loads(path.read_text(encoding=\"utf-8\")) for path in paths]\n", + " documents = [\n", + " view\n", + " for document in raw_documents\n", + " if (view := implementation_view(document, implementation)) is not None\n", + " ]\n", + " messages = comparison_warnings(documents)\n", + " if messages:\n", + " print(f\"{implementation} comparison warnings:\")\n", + " for message in messages:\n", + " print(f\" WARNING: {message}\")\n", + " return documents\n", + "\n", + "\n", + "def print_status_table(documents: list[dict[str, Any]]) -> None:\n", + " header = (\n", + " f\"{'label':18s} {'formula':9s} {'AOs':>5s} {'mode':10s} \"\n", + " f\"{'runtime':12s} {'memory':12s}\"\n", + " )\n", + " print(header)\n", + " print(\"-\" * len(header))\n", + " for document in documents:\n", + " for molecule in document[\"molecules\"].values():\n", + " observed = molecule.get(\"observed\") or {}\n", + " aos = observed.get(\"actual_aos\", molecule[\"expected_aos\"])\n", + " for mode in document.get(\"modes\", MODES):\n", + " mode_record = molecule[\"modes\"][mode]\n", + " runtime_status = mode_record.get(\"runtime\", {}).get(\"status\", \"pending\")\n", + " memory_status = mode_record.get(\"memory\", {}).get(\"status\", \"pending\")\n", + " print(\n", + " f\"{document['run_label'][:18]:18s} {molecule['formula']:9s} {aos:5d} \"\n", + " f\"{mode:10s} {runtime_status:12s} {memory_status:12s}\"\n", + " )\n", + "\n", + "\n", + "def print_skalaxc_grid_table(documents: list[dict[str, Any]]) -> None:\n", + " if not documents:\n", + " return\n", + " print(\"\\nSkalaXC/PySCF grid comparison:\")\n", + " print(\n", + " f\"{'label':18s} {'formula':9s} {'grid':12s} {'PySCF':>10s} {'SkalaXC':>10s} {'ratio':>8s}\"\n", + " )\n", + " for document in documents:\n", + " for molecule in document[\"molecules\"].values():\n", + " observed = molecule.get(\"observed\") or {}\n", + " if not observed:\n", + " continue\n", + " print(\n", + " f\"{document['run_label'][:18]:18s} {molecule['formula']:9s} \"\n", + " f\"{str(observed.get('grid_size', '')):12s} \"\n", + " f\"{int(observed.get('pyscf_grid_points', 0)):10d} \"\n", + " f\"{int(observed.get('grid_points', 0)):10d} \"\n", + " f\"{float(observed.get('grid_point_ratio', np.nan)):8.3f}\"\n", + " )\n", + "\n", + "\n", + "SELECTED_DOCUMENTS = (\n", + " load_result_documents(SELECTED_RESULT_FILES, \"skala\")\n", + " if SELECTED_RESULT_FILES\n", + " else []\n", + ")\n", + "SKALAXC_DOCUMENTS = (\n", + " load_result_documents(SELECTED_RESULT_FILES, \"skalaxc\")\n", + " if SELECTED_RESULT_FILES\n", + " else []\n", + ")\n", + "if SELECTED_DOCUMENTS or SKALAXC_DOCUMENTS:\n", + " print_status_table([*SELECTED_DOCUMENTS, *SKALAXC_DOCUMENTS])\n", + " print_skalaxc_grid_table(SKALAXC_DOCUMENTS)\n", + "else:\n", + " print(\"No benchmark result files were found. Run the benchmark script first.\")" + ] + }, + { + "cell_type": "markdown", + "id": "57fda27a", + "metadata": {}, + "source": [ + "## Visualize\n", + "\n", + "Each metric is rendered in its own notebook output with a single y-axis. Runtime curves show the median of the recorded samples with error bars spanning the observed minimum and maximum; one-sample legacy results therefore have zero-width bounds. Successful observations are plotted against actual spherical AO counts. Failed or timed-out points stay absent from curves and remain visible in the status table." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "caf55d9d", + "metadata": {}, + "outputs": [], + "source": [ + "from matplotlib.axes import Axes\n", + "\n", + "MODE_COLORS = {\n", + " \"cpu\": \"#006D77\",\n", + " \"cpu_dense\": \"#83C5BE\",\n", + " \"gpu\": \"#C44536\",\n", + "}\n", + "REVISION_LINESTYLES = (\"-\", \"--\", \":\", \"-.\")\n", + "RESULT_MARKERS = (\"o\", \"s\", \"^\", \"D\", \"v\", \"P\", \"X\")\n", + "MARKER_SIZE = 5\n", + "LEGEND_MARKER_SCALE = 1.8\n", + "LEGEND_HANDLE_LENGTH = 3.0\n", + "\n", + "\n", + "def measurement_samples(mode_record: dict[str, Any], measurement: str) -> list[float]:\n", + " result = mode_record.get(measurement, {})\n", + " if result.get(\"status\") != \"ok\":\n", + " return []\n", + " if measurement == \"runtime\":\n", + " return [float(value) for value in result.get(\"runtime_samples_seconds\", [])]\n", + " peak_bytes = result.get(\"incremental_peak_bytes\")\n", + " return [float(peak_bytes) / 1024**3] if peak_bytes is not None else []\n", + "\n", + "\n", + "def sample_summary(samples: list[float]) -> tuple[float, float, float] | None:\n", + " if not samples:\n", + " return None\n", + " values = np.asarray(samples, dtype=float)\n", + " center = float(np.median(values))\n", + " return center, center - float(values.min()), float(values.max()) - center\n", + "\n", + "\n", + "def measurement_value(mode_record: dict[str, Any], measurement: str) -> float | None:\n", + " summary = sample_summary(measurement_samples(mode_record, measurement))\n", + " return summary[0] if summary is not None else None\n", + "\n", + "\n", + "def measurement_series(\n", + " document: dict[str, Any], mode: str, measurement: str\n", + ") -> tuple[list[int], list[float], list[float], list[float]]:\n", + " points: list[tuple[int, float, float, float]] = []\n", + " for molecule in document[\"molecules\"].values():\n", + " observed = molecule.get(\"observed\") or {}\n", + " aos = int(observed.get(\"actual_aos\", molecule[\"expected_aos\"]))\n", + " summary = sample_summary(\n", + " measurement_samples(molecule[\"modes\"][mode], measurement)\n", + " )\n", + " if summary is not None and summary[0] > 0.0:\n", + " points.append((aos, *summary))\n", + " points.sort()\n", + " return (\n", + " [point[0] for point in points],\n", + " [point[1] for point in points],\n", + " [point[2] for point in points],\n", + " [point[3] for point in points],\n", + " )\n", + "\n", + "\n", + "def cpu_reference_ratio_series(\n", + " document: dict[str, Any], measurement: str\n", + ") -> tuple[list[int], list[float], list[float], list[float]]:\n", + " points: list[tuple[int, float, float, float]] = []\n", + " for molecule in document[\"molecules\"].values():\n", + " observed = molecule.get(\"observed\") or {}\n", + " aos = int(observed.get(\"actual_aos\", molecule[\"expected_aos\"]))\n", + " production_samples = measurement_samples(molecule[\"modes\"][\"cpu\"], measurement)\n", + " dense_samples = measurement_samples(molecule[\"modes\"][\"cpu_dense\"], measurement)\n", + " production_summary = sample_summary(production_samples)\n", + " dense_summary = sample_summary(dense_samples)\n", + " if (\n", + " production_summary is None\n", + " or dense_summary is None\n", + " or min(production_samples) <= 0.0\n", + " ):\n", + " continue\n", + " center = dense_summary[0] / production_summary[0]\n", + " lower_bound = min(dense_samples) / max(production_samples)\n", + " upper_bound = max(dense_samples) / min(production_samples)\n", + " points.append((aos, center, center - lower_bound, upper_bound - center))\n", + " points.sort()\n", + " return (\n", + " [point[0] for point in points],\n", + " [point[1] for point in points],\n", + " [point[2] for point in points],\n", + " [point[3] for point in points],\n", + " )\n", + "\n", + "\n", + "def endpoint_label(label: str, x_values: list[int]) -> str:\n", + " return (\n", + " f\"{label} (last: {x_values[-1]} AOs)\"\n", + " if x_values\n", + " else f\"{label} (no successful points)\"\n", + " )\n", + "\n", + "\n", + "def style_benchmark_axis(\n", + " axis: Axes, *, title: str, ylabel: str, logarithmic: bool = False\n", + ") -> None:\n", + " axis.set(title=title, xlabel=\"Spherical AO count\", ylabel=ylabel)\n", + " if logarithmic:\n", + " axis.set_yscale(\"log\")\n", + " axis.grid(True, which=\"both\", color=\"#D9D9D9\", linewidth=0.6)\n", + " axis.legend(\n", + " fontsize=8,\n", + " loc=\"upper left\",\n", + " bbox_to_anchor=(1.02, 1.0),\n", + " borderaxespad=0.0,\n", + " markerscale=LEGEND_MARKER_SCALE,\n", + " handlelength=LEGEND_HANDLE_LENGTH,\n", + " )\n", + "\n", + "\n", + "def plot_measurement(documents: list[dict[str, Any]], measurement: str) -> None:\n", + " if not documents:\n", + " print(f\"No result files selected; the {measurement} plot was not created.\")\n", + " return\n", + " _, axis = plt.subplots(figsize=(11, 6), constrained_layout=True)\n", + " for document_index, document in enumerate(documents):\n", + " label = document[\"run_label\"]\n", + " line_style = REVISION_LINESTYLES[document_index % len(REVISION_LINESTYLES)]\n", + " marker = RESULT_MARKERS[document_index % len(RESULT_MARKERS)]\n", + " for mode in MODES:\n", + " x_values, y_values, lower_errors, upper_errors = measurement_series(\n", + " document, mode, measurement\n", + " )\n", + " curve_label = f\"{label} {mode}\"\n", + " plot_arguments = {\n", + " \"color\": MODE_COLORS[mode],\n", + " \"linestyle\": line_style,\n", + " \"marker\": marker,\n", + " \"markersize\": MARKER_SIZE,\n", + " \"label\": endpoint_label(curve_label, x_values),\n", + " }\n", + " if measurement == \"runtime\":\n", + " axis.errorbar(\n", + " x_values,\n", + " y_values,\n", + " yerr=np.asarray([lower_errors, upper_errors]),\n", + " capsize=3,\n", + " **plot_arguments,\n", + " )\n", + " else:\n", + " axis.plot(x_values, y_values, **plot_arguments)\n", + " if measurement == \"runtime\":\n", + " title = \"One XC/Vxc evaluation (median and min-max)\"\n", + " ylabel = \"Runtime (s)\"\n", + " elif measurement == \"memory\":\n", + " title = \"Incremental allocation peak\"\n", + " ylabel = \"Memory (GiB)\"\n", + " else:\n", + " raise ValueError(f\"Unknown measurement: {measurement}\")\n", + " style_benchmark_axis(axis, title=title, ylabel=ylabel, logarithmic=True)\n", + " plt.show()\n", + "\n", + "\n", + "def plot_cpu_reference_ratio(documents: list[dict[str, Any]], measurement: str) -> None:\n", + " if not documents:\n", + " print(\n", + " f\"No result files selected; the {measurement} ratio plot was not created.\"\n", + " )\n", + " return\n", + " _, axis = plt.subplots(figsize=(11, 6), constrained_layout=True)\n", + " for document_index, document in enumerate(documents):\n", + " x_values, y_values, lower_errors, upper_errors = cpu_reference_ratio_series(\n", + " document, measurement\n", + " )\n", + " line_style = REVISION_LINESTYLES[document_index % len(REVISION_LINESTYLES)]\n", + " marker = RESULT_MARKERS[document_index % len(RESULT_MARKERS)]\n", + " plot_arguments = {\n", + " \"color\": MODE_COLORS[\"cpu\"],\n", + " \"linestyle\": line_style,\n", + " \"marker\": marker,\n", + " \"markersize\": MARKER_SIZE,\n", + " \"label\": f\"{document['run_label']} cpu\",\n", + " }\n", + " if measurement == \"runtime\":\n", + " axis.errorbar(\n", + " x_values,\n", + " y_values,\n", + " yerr=np.asarray([lower_errors, upper_errors]),\n", + " capsize=3,\n", + " **plot_arguments,\n", + " )\n", + " else:\n", + " axis.plot(x_values, y_values, **plot_arguments)\n", + " if measurement == \"runtime\":\n", + " title = \"CPU production speedup (median and min-max)\"\n", + " ylabel = \"CPU dense runtime / production runtime\"\n", + " elif measurement == \"memory\":\n", + " title = \"CPU production memory reduction\"\n", + " ylabel = \"CPU dense peak / production peak\"\n", + " else:\n", + " raise ValueError(f\"Unknown measurement: {measurement}\")\n", + " style_benchmark_axis(axis, title=title, ylabel=ylabel)\n", + " axis.axhline(1.0, color=\"#777777\", linewidth=0.8, linestyle=\":\")\n", + " plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "165b8f50", + "metadata": {}, + "outputs": [], + "source": [ + "plot_measurement(SELECTED_DOCUMENTS, \"runtime\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b923074a", + "metadata": {}, + "outputs": [], + "source": [ + "plot_measurement(SELECTED_DOCUMENTS, \"memory\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ce3a086b", + "metadata": {}, + "outputs": [], + "source": [ + "plot_cpu_reference_ratio(SELECTED_DOCUMENTS, \"runtime\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fded2dd7", + "metadata": {}, + "outputs": [], + "source": [ + "plot_cpu_reference_ratio(SELECTED_DOCUMENTS, \"memory\")" + ] + }, + { + "cell_type": "markdown", + "id": "58750d35", + "metadata": {}, + "source": [ + "## Numerical Differences\n", + "\n", + "Production CPU and GPU fingerprints are compared molecule-by-molecule with the CPU-dense reference. The summary reports maximum absolute and relative errors and counts values outside the existing acceptance tolerances. Each per-fingerprint plot shows the signed difference `production - cpu_dense`; the dotted zero line is the CPU-dense reference.\n", + "\n", + "The final six-panel figure compares CPU-dense references across result files. The first selected result is the baseline, and each curve shows `comparison cpu_dense - baseline cpu_dense`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f4de2998", + "metadata": {}, + "outputs": [], + "source": [ + "FINGERPRINT_LABELS = {\n", + " \"electron_integral\": \"Electron integral\",\n", + " \"xc_energy\": \"XC energy\",\n", + " \"vxc_sum\": \"Vxc sum\",\n", + " \"vxc_trace\": \"Vxc trace\",\n", + " \"vxc_frobenius_norm\": \"Vxc Frobenius norm\",\n", + " \"vxc_max_abs\": \"Vxc max abs\",\n", + "}\n", + "ERROR_TOLERANCES = {\n", + " \"cpu\": (1e-8, 5e-8),\n", + " \"gpu\": (1e-7, 2e-7),\n", + "}\n", + "\n", + "\n", + "def fingerprint_error_records(\n", + " document: dict[str, Any], production_mode: str, fingerprint_key: str\n", + ") -> list[dict[str, Any]]:\n", + " rtol, atol = ERROR_TOLERANCES[production_mode]\n", + " records: list[dict[str, Any]] = []\n", + " for formula, molecule in document[\"molecules\"].items():\n", + " reference = preferred_fingerprint(molecule[\"modes\"][\"cpu_dense\"])\n", + " production = preferred_fingerprint(molecule[\"modes\"][production_mode])\n", + " if (\n", + " reference is None\n", + " or production is None\n", + " or fingerprint_key not in reference\n", + " or fingerprint_key not in production\n", + " ):\n", + " continue\n", + " reference_value = float(reference[fingerprint_key])\n", + " production_value = float(production[fingerprint_key])\n", + " difference = production_value - reference_value\n", + " absolute_error = abs(difference)\n", + " relative_error = absolute_error / max(\n", + " abs(reference_value), np.finfo(float).tiny\n", + " )\n", + " tolerance_scale = atol + rtol * abs(reference_value)\n", + " observed = molecule.get(\"observed\") or {}\n", + " records.append(\n", + " {\n", + " \"formula\": formula,\n", + " \"aos\": int(observed.get(\"actual_aos\", molecule[\"expected_aos\"])),\n", + " \"difference\": difference,\n", + " \"absolute_error\": absolute_error,\n", + " \"relative_error\": relative_error,\n", + " \"tolerance_ratio\": absolute_error / tolerance_scale,\n", + " }\n", + " )\n", + " records.sort(key=lambda record: record[\"aos\"])\n", + " return records\n", + "\n", + "\n", + "def dense_reference_difference_records(\n", + " reference_document: dict[str, Any],\n", + " comparison_document: dict[str, Any],\n", + " fingerprint_key: str,\n", + ") -> list[dict[str, Any]]:\n", + " records: list[dict[str, Any]] = []\n", + " for formula, reference_molecule in reference_document[\"molecules\"].items():\n", + " comparison_molecule = comparison_document[\"molecules\"].get(formula)\n", + " if comparison_molecule is None:\n", + " continue\n", + " reference = preferred_fingerprint(reference_molecule[\"modes\"][\"cpu_dense\"])\n", + " comparison = preferred_fingerprint(comparison_molecule[\"modes\"][\"cpu_dense\"])\n", + " if (\n", + " reference is None\n", + " or comparison is None\n", + " or fingerprint_key not in reference\n", + " or fingerprint_key not in comparison\n", + " ):\n", + " continue\n", + " observed = comparison_molecule.get(\"observed\") or {}\n", + " records.append(\n", + " {\n", + " \"formula\": formula,\n", + " \"aos\": int(\n", + " observed.get(\"actual_aos\", comparison_molecule[\"expected_aos\"])\n", + " ),\n", + " \"difference\": float(comparison[fingerprint_key])\n", + " - float(reference[fingerprint_key]),\n", + " }\n", + " )\n", + " records.sort(key=lambda record: record[\"aos\"])\n", + " return records\n", + "\n", + "\n", + "def print_fingerprint_error_summary(documents: list[dict[str, Any]]) -> None:\n", + " header = (\n", + " f\"{'label':10s} {'mode':4s} {'fingerprint':22s} \"\n", + " f\"{'max abs':>11s} {'max rel':>11s} {'outside':>8s} {'at':>8s}\"\n", + " )\n", + " print(header)\n", + " print(\"-\" * len(header))\n", + " for document in documents:\n", + " for production_mode in ERROR_TOLERANCES:\n", + " for fingerprint_key, fingerprint_label in FINGERPRINT_LABELS.items():\n", + " records = fingerprint_error_records(\n", + " document, production_mode, fingerprint_key\n", + " )\n", + " if not records:\n", + " continue\n", + " max_absolute_error = max(record[\"absolute_error\"] for record in records)\n", + " worst_relative = max(\n", + " records, key=lambda record: record[\"relative_error\"]\n", + " )\n", + " outside_tolerance = sum(\n", + " record[\"tolerance_ratio\"] > 1.0 for record in records\n", + " )\n", + " print(\n", + " f\"{document['run_label'][:10]:10s} {production_mode:4s} \"\n", + " f\"{fingerprint_label:22s} {max_absolute_error:11.3e} \"\n", + " f\"{worst_relative['relative_error']:11.3e} \"\n", + " f\"{outside_tolerance:3d}/{len(records):<4d} \"\n", + " f\"{worst_relative['formula']:>8s}\"\n", + " )\n", + "\n", + "\n", + "def plot_fingerprint_differences(\n", + " documents: list[dict[str, Any]], fingerprint_key: str\n", + ") -> None:\n", + " if fingerprint_key not in FINGERPRINT_LABELS:\n", + " raise ValueError(f\"Unknown fingerprint: {fingerprint_key}\")\n", + " if not documents:\n", + " print(f\"No result files selected; the {fingerprint_key} plot was not created.\")\n", + " return\n", + " _, axis = plt.subplots(figsize=(11, 6), constrained_layout=True)\n", + " for document_index, document in enumerate(documents):\n", + " line_style = REVISION_LINESTYLES[document_index % len(REVISION_LINESTYLES)]\n", + " marker = RESULT_MARKERS[document_index % len(RESULT_MARKERS)]\n", + " for production_mode in ERROR_TOLERANCES:\n", + " records = fingerprint_error_records(\n", + " document, production_mode, fingerprint_key\n", + " )\n", + " x_values = [record[\"aos\"] for record in records]\n", + " curve_label = f\"{document['run_label']} {production_mode}\"\n", + " axis.plot(\n", + " x_values,\n", + " [record[\"difference\"] for record in records],\n", + " color=MODE_COLORS[production_mode],\n", + " linestyle=line_style,\n", + " marker=marker,\n", + " markersize=MARKER_SIZE,\n", + " label=endpoint_label(curve_label, x_values),\n", + " )\n", + " fingerprint_label = FINGERPRINT_LABELS[fingerprint_key]\n", + " axis.axhline(\n", + " 0.0,\n", + " color=MODE_COLORS[\"cpu_dense\"],\n", + " linewidth=1.0,\n", + " linestyle=\":\",\n", + " label=\"CPU dense reference\",\n", + " )\n", + " style_benchmark_axis(\n", + " axis,\n", + " title=f\"{fingerprint_label} difference from CPU dense\",\n", + " ylabel=f\"{fingerprint_label} - CPU dense reference\",\n", + " )\n", + " axis.ticklabel_format(axis=\"y\", style=\"sci\", scilimits=(0, 0))\n", + " plt.show()\n", + "\n", + "\n", + "def plot_dense_reference_differences(documents: list[dict[str, Any]]) -> None:\n", + " if len(documents) < 2:\n", + " print(\"At least two result files are required to compare CPU-dense references.\")\n", + " return\n", + " reference_document = documents[0]\n", + " figure, axes = plt.subplots(2, 3, figsize=(16, 9), constrained_layout=True)\n", + " for axis, (fingerprint_key, fingerprint_label) in zip(\n", + " axes.flat, FINGERPRINT_LABELS.items(), strict=True\n", + " ):\n", + " plotted_differences: list[float] = []\n", + " for document_index, document in enumerate(documents[1:], start=1):\n", + " records = dense_reference_difference_records(\n", + " reference_document, document, fingerprint_key\n", + " )\n", + " x_values = [record[\"aos\"] for record in records]\n", + " differences = [record[\"difference\"] for record in records]\n", + " plotted_differences.extend(differences)\n", + " curve_label = f\"{document['run_label']} - {reference_document['run_label']}\"\n", + " axis.plot(\n", + " x_values,\n", + " differences,\n", + " color=MODE_COLORS[\"cpu_dense\"],\n", + " linestyle=REVISION_LINESTYLES[\n", + " document_index % len(REVISION_LINESTYLES)\n", + " ],\n", + " marker=RESULT_MARKERS[document_index % len(RESULT_MARKERS)],\n", + " markersize=MARKER_SIZE,\n", + " label=endpoint_label(curve_label, x_values),\n", + " )\n", + " axis.axhline(0.0, color=\"#777777\", linewidth=0.8, linestyle=\":\")\n", + " axis.set(\n", + " title=fingerprint_label,\n", + " xlabel=\"Spherical AO count\",\n", + " ylabel=\"CPU-dense difference\",\n", + " )\n", + " if plotted_differences and all(value == 0.0 for value in plotted_differences):\n", + " axis.set_ylim(-0.5, 0.5)\n", + " axis.set_yticks([0.0])\n", + " axis.text(\n", + " 0.5,\n", + " 0.54,\n", + " \"All matched differences are exactly zero\",\n", + " color=\"#555555\",\n", + " fontsize=8,\n", + " ha=\"center\",\n", + " transform=axis.transAxes,\n", + " )\n", + " else:\n", + " axis.ticklabel_format(axis=\"y\", style=\"sci\", scilimits=(0, 0))\n", + " axis.grid(True, which=\"both\", color=\"#D9D9D9\", linewidth=0.6)\n", + " handles, labels = axes.flat[0].get_legend_handles_labels()\n", + " figure.legend(\n", + " handles,\n", + " labels,\n", + " fontsize=8,\n", + " loc=\"center left\",\n", + " bbox_to_anchor=(1.01, 0.5),\n", + " borderaxespad=0.0,\n", + " markerscale=LEGEND_MARKER_SCALE,\n", + " handlelength=LEGEND_HANDLE_LENGTH,\n", + " )\n", + " figure.suptitle(\n", + " f\"CPU-dense reference differences from {reference_document['run_label']}\"\n", + " )\n", + " plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "db731370", + "metadata": {}, + "outputs": [], + "source": [ + "print_fingerprint_error_summary(SELECTED_DOCUMENTS)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "148309a1", + "metadata": {}, + "outputs": [], + "source": [ + "for fingerprint_key in FINGERPRINT_LABELS:\n", + " plot_fingerprint_differences(SELECTED_DOCUMENTS, fingerprint_key)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16648b0a", + "metadata": {}, + "outputs": [], + "source": [ + "plot_dense_reference_differences(SELECTED_DOCUMENTS)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/benchmarks/pyscf_ao_screening_rotation_comparison.ipynb b/benchmarks/pyscf_ao_screening_rotation_comparison.ipynb new file mode 100644 index 00000000..d4e2cdcd --- /dev/null +++ b/benchmarks/pyscf_ao_screening_rotation_comparison.ipynb @@ -0,0 +1,1462 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "ac79c661", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "c081221d", + "metadata": {}, + "source": [ + "# Skala AO Screening Rotation Comparison\n", + "\n", + "This notebook loads JSON written by `benchmarks/run_pyscf_ao_screening_rotation_benchmark.py`. It does not construct molecules, load Skala, or execute benchmark workloads.\n", + "\n", + "Generate a full result file before running the analysis:\n", + "\n", + "```bash\n", + "/home/jenswehner/micromamba/envs/skala_gpu_python/bin/python \\\n", + " benchmarks/run_pyscf_ao_screening_rotation_benchmark.py \\\n", + " --label screening\n", + "```\n", + "\n", + "The default run records runtime and incremental peak memory for 72 orientations in each of `gpu`, `cpu_dense`, and `cpu_screened`. Add `--smoke` for one orientation per mode or `--preflight-only` to validate geometry and dependencies without measurements.\n", + "\n", + "## 1. Import Analysis Libraries and Configure Paths\n", + "\n", + "Select one or more rotation result files. Runtime is reported in seconds and incremental peak memory in GiB; CPU dense is the numerical and ratio reference." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3bb528e2", + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import annotations\n", + "\n", + "import json\n", + "import statistics\n", + "from collections import Counter\n", + "from datetime import UTC, datetime\n", + "from pathlib import Path\n", + "from typing import Any\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import seaborn as sns\n", + "\n", + "MODES = (\"gpu\", \"cpu_dense\", \"cpu_screened\")\n", + "SKALAXC_MODES = (\"cpu\", \"gpu\")\n", + "MEASUREMENTS = (\"runtime\", \"memory\")\n", + "REFERENCE_MODE = \"cpu_dense\"\n", + "EXPECTED_AOS = 879\n", + "EXPECTED_ROUTES = {\n", + " \"gpu\": \"global_ao_screening\",\n", + " \"cpu_dense\": \"dense\",\n", + " \"cpu_screened\": \"global_ao_screening\",\n", + "}\n", + "TERMINAL_STATUSES = {\n", + " \"ok\",\n", + " \"timeout\",\n", + " \"oom\",\n", + " \"error\",\n", + " \"unsupported\",\n", + " \"skipped_after_resource_failure\",\n", + "}\n", + "FINGERPRINT_LABELS = {\n", + " \"electron_integral\": \"Electron integral\",\n", + " \"xc_energy\": \"XC energy\",\n", + " \"vxc_sum\": \"Vxc sum\",\n", + " \"vxc_trace\": \"Vxc trace\",\n", + " \"vxc_frobenius_norm\": \"Vxc Frobenius norm\",\n", + " \"vxc_max_abs\": \"Vxc max abs\",\n", + "}\n", + "ERROR_TOLERANCES = {\n", + " \"cpu_screened\": (5e-8, 1e-8),\n", + " \"gpu\": (2e-7, 1e-7),\n", + "}\n", + "MODE_COLORS = {\n", + " \"gpu\": \"#C44536\",\n", + " \"cpu_dense\": \"#83C5BE\",\n", + " \"cpu_screened\": \"#006D77\",\n", + "}\n", + "\n", + "\n", + "def find_repository_root(start: Path) -> Path:\n", + " for candidate in (start.resolve(), *start.resolve().parents):\n", + " if (candidate / \"pyproject.toml\").is_file() and (\n", + " candidate / \"benchmarks\"\n", + " ).is_dir():\n", + " return candidate\n", + " raise FileNotFoundError(f\"Could not find the Skala repository above {start}\")\n", + "\n", + "\n", + "REPOSITORY_ROOT = find_repository_root(Path.cwd())\n", + "RESULTS_DIR = REPOSITORY_ROOT / \"benchmarks\" / \"results\"\n", + "ARTIFACT_DIR = RESULTS_DIR / \"rotation_comparison\"\n", + "FIGURE_DIR = ARTIFACT_DIR / \"figures\"\n", + "TABLE_DIR = ARTIFACT_DIR / \"tables\"\n", + "COMPARISON_JSON = ARTIFACT_DIR / \"comparison.json\"\n", + "\n", + "# Replace this list with explicit paths to compare a subset of result files.\n", + "SELECTED_RESULT_FILES = sorted(\n", + " RESULTS_DIR.glob(\"skala-pyscf-ao-screening-rotations-*.json\")\n", + ")\n", + "\n", + "sns.set_theme(style=\"whitegrid\", context=\"notebook\")\n", + "print(f\"Selected {len(SELECTED_RESULT_FILES)} rotation result file(s)\")\n", + "for result_file in SELECTED_RESULT_FILES:\n", + " print(f\" {result_file.name}\")" + ] + }, + { + "cell_type": "markdown", + "id": "556eb2a0", + "metadata": {}, + "source": [ + "## 2. Load and Validate Benchmark JSON Files\n", + "\n", + "Each file is checked for the rotation schema, provenance, configuration, environment, timestamps, and runner hashes. Malformed and partially completed files remain visible in the validation table.\n", + "\n", + "## 3. Normalize Molecule and Execution-Mode Results\n", + "\n", + "The molecule is fixed at C7H16, so normalization produces one row per result file, orientation, and execution mode. Rows include geometry, AO and grid sizes, routes, statuses, measurements, allocator baselines, and both runtime and memory fingerprints.\n", + "\n", + "## 4. Validate Benchmark Completeness and Status\n", + "\n", + "A full run requires 72 orientations, three modes, and successful runtime and memory records. Smoke and partial runs are accepted but explicitly reported.\n", + "\n", + "## 5. Verify AO Counts, Routes, and Screening Thresholds\n", + "\n", + "Observed AO counts must remain 879. Dense CPU must report `dense`; screened CPU and GPU must report `global_ao_screening`, which is expected because 879 exceeds PySCF's switch size of 800." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "50a0ade9", + "metadata": {}, + "outputs": [], + "source": [ + "NORMALIZED_COLUMNS = [\n", + " \"run_label\",\n", + " \"commit\",\n", + " \"branch\",\n", + " \"dirty\",\n", + " \"orientation_key\",\n", + " \"orientation_index\",\n", + " \"azimuth_degrees\",\n", + " \"polar_degrees\",\n", + " \"formula\",\n", + " \"carbon_count\",\n", + " \"expected_aos\",\n", + " \"actual_aos\",\n", + " \"electron_count\",\n", + " \"grid_points\",\n", + " \"mode\",\n", + " \"selected_route\",\n", + " \"route_request\",\n", + " \"switch_size\",\n", + " \"implementation_sha256\",\n", + " \"runtime_status\",\n", + " \"memory_status\",\n", + " \"runtime_samples_seconds\",\n", + " \"runtime_seconds\",\n", + " \"incremental_peak_bytes\",\n", + " \"incremental_peak_gib\",\n", + " \"allocator_baseline_bytes\",\n", + " \"allocator_baseline_gib\",\n", + "] + [\n", + " f\"{measurement}_{fingerprint}\"\n", + " for measurement in MEASUREMENTS\n", + " for fingerprint in FINGERPRINT_LABELS\n", + "]\n", + "\n", + "\n", + "def rotation_implementation_view(\n", + " document: dict[str, Any], implementation: str\n", + ") -> dict[str, Any] | None:\n", + " schema_version = document.get(\"schema_version\")\n", + " if schema_version == 1:\n", + " if implementation != \"skala\" or not isinstance(\n", + " document.get(\"orientations\"), dict\n", + " ):\n", + " return None\n", + " view = dict(document)\n", + " view[\"implementation\"] = \"skala\"\n", + " view[\"modes\"] = list(MODES)\n", + " return view\n", + " if schema_version != 2:\n", + " return None\n", + " implementation_record = document.get(\"implementations\", {}).get(implementation)\n", + " if not isinstance(implementation_record, dict):\n", + " return None\n", + " view = dict(document)\n", + " view[\"implementation\"] = implementation\n", + " view[\"orientations\"] = implementation_record.get(\"orientations\", {})\n", + " view[\"modes\"] = implementation_record.get(\"modes\", [])\n", + " if implementation != \"skala\":\n", + " view[\"run_label\"] = f\"{document['run_label']}:{implementation}\"\n", + " return view\n", + "\n", + "\n", + "def validate_document(path: Path, document: dict[str, Any]) -> list[str]:\n", + " failures: list[str] = []\n", + " required_fields = {\n", + " \"configuration\",\n", + " \"created_at\",\n", + " \"environment\",\n", + " \"orientations\",\n", + " \"runner_hashes\",\n", + " \"schema_version\",\n", + " \"source\",\n", + " \"updated_at\",\n", + " }\n", + " missing_fields = sorted(required_fields - document.keys())\n", + " if missing_fields:\n", + " failures.append(f\"missing top-level fields: {missing_fields}\")\n", + " if document.get(\"schema_version\") not in {1, 2}:\n", + " failures.append(f\"unsupported schema version {document.get('schema_version')}\")\n", + " if document.get(\"benchmark\") != \"pyscf_ao_screening_rotations\":\n", + " failures.append(f\"unexpected benchmark marker {document.get('benchmark')!r}\")\n", + "\n", + " configuration = document.get(\"configuration\", {})\n", + " configured_modes = tuple(document.get(\"modes\", ()))\n", + " configured_measurements = tuple(configuration.get(\"measurements\", ()))\n", + " if configured_modes and configured_modes != MODES:\n", + " failures.append(f\"configured modes are {configured_modes}, expected {MODES}\")\n", + " if configured_measurements and configured_measurements != MEASUREMENTS:\n", + " failures.append(\n", + " f\"configured measurements are {configured_measurements}, expected {MEASUREMENTS}\"\n", + " )\n", + "\n", + " orientations = document.get(\"orientations\", {})\n", + " expected_count = int(configuration.get(\"orientation_count\", len(orientations)))\n", + " if len(orientations) != expected_count:\n", + " failures.append(\n", + " f\"contains {len(orientations)} orientations, configuration requests {expected_count}\"\n", + " )\n", + " if not configuration.get(\"smoke_run\", False) and len(orientations) != 72:\n", + " failures.append(\n", + " f\"full run contains {len(orientations)} orientations, expected 72\"\n", + " )\n", + " coordinate_hashes = [\n", + " orientation.get(\"coordinate_sha256\") for orientation in orientations.values()\n", + " ]\n", + " if len(set(coordinate_hashes)) != len(coordinate_hashes):\n", + " failures.append(\"orientation coordinate hashes are not unique\")\n", + "\n", + " for orientation_key, orientation in orientations.items():\n", + " observed = orientation.get(\"observed\") or {}\n", + " actual_aos = observed.get(\"actual_aos\")\n", + " if actual_aos is not None and int(actual_aos) != EXPECTED_AOS:\n", + " failures.append(\n", + " f\"{orientation_key}: observed {actual_aos} AOs, expected {EXPECTED_AOS}\"\n", + " )\n", + " modes = orientation.get(\"modes\", {})\n", + " missing_modes = sorted(set(MODES) - modes.keys())\n", + " if missing_modes:\n", + " failures.append(f\"{orientation_key}: missing modes {missing_modes}\")\n", + " for mode in MODES:\n", + " mode_record = modes.get(mode, {})\n", + " route = mode_record.get(\"route\", {})\n", + " selected_route = route.get(\"selected_route\")\n", + " if selected_route is not None and selected_route != EXPECTED_ROUTES[mode]:\n", + " failures.append(\n", + " f\"{orientation_key} {mode}: selected {selected_route}, \"\n", + " f\"expected {EXPECTED_ROUTES[mode]}\"\n", + " )\n", + " switch_size = route.get(\"pyscf_switch_size\")\n", + " if switch_size is not None and EXPECTED_AOS <= int(switch_size):\n", + " failures.append(\n", + " f\"{orientation_key} {mode}: {EXPECTED_AOS} AOs do not exceed \"\n", + " f\"reported switch size {switch_size}\"\n", + " )\n", + " for measurement in MEASUREMENTS:\n", + " result = mode_record.get(measurement)\n", + " if result is None:\n", + " failures.append(f\"{orientation_key} {mode}: missing {measurement}\")\n", + " continue\n", + " status = result.get(\"status\")\n", + " if status not in TERMINAL_STATUSES:\n", + " failures.append(\n", + " f\"{orientation_key} {mode} {measurement}: unknown status {status!r}\"\n", + " )\n", + " elif status != \"ok\":\n", + " failures.append(\n", + " f\"{orientation_key} {mode} {measurement}: status {status}\"\n", + " )\n", + " if (\n", + " measurement == \"runtime\"\n", + " and status == \"ok\"\n", + " and not result.get(\"runtime_samples_seconds\")\n", + " ):\n", + " failures.append(\n", + " f\"{orientation_key} {mode}: successful runtime has no samples\"\n", + " )\n", + " return failures\n", + "\n", + "\n", + "def normalize_document(path: Path, document: dict[str, Any]) -> list[dict[str, Any]]:\n", + " base_molecule = document.get(\"geometry\", {}).get(\"base_molecule\", {})\n", + " source = document.get(\"source\", {})\n", + " run_label = str(document.get(\"run_label\") or path.stem)\n", + " rows: list[dict[str, Any]] = []\n", + " for orientation_key, orientation in document.get(\"orientations\", {}).items():\n", + " observed = orientation.get(\"observed\") or {}\n", + " for mode in MODES:\n", + " mode_record = orientation.get(\"modes\", {}).get(mode, {})\n", + " runtime = mode_record.get(\"runtime\", {})\n", + " memory = mode_record.get(\"memory\", {})\n", + " route = mode_record.get(\"route\", {})\n", + " runtime_samples = [\n", + " float(value) for value in runtime.get(\"runtime_samples_seconds\", [])\n", + " ]\n", + " row: dict[str, Any] = {\n", + " \"run_label\": run_label,\n", + " \"commit\": source.get(\"commit\"),\n", + " \"branch\": source.get(\"branch\"),\n", + " \"dirty\": source.get(\"dirty\"),\n", + " \"orientation_key\": orientation_key,\n", + " \"orientation_index\": orientation.get(\"index\"),\n", + " \"azimuth_degrees\": orientation.get(\"azimuth_degrees\"),\n", + " \"polar_degrees\": orientation.get(\"polar_degrees\"),\n", + " \"formula\": base_molecule.get(\"formula\", observed.get(\"formula\")),\n", + " \"carbon_count\": base_molecule.get(\n", + " \"carbon_count\", observed.get(\"carbon_count\")\n", + " ),\n", + " \"expected_aos\": base_molecule.get(\"expected_aos\", EXPECTED_AOS),\n", + " \"actual_aos\": observed.get(\"actual_aos\"),\n", + " \"electron_count\": observed.get(\"electron_count\"),\n", + " \"grid_points\": observed.get(\"grid_points\"),\n", + " \"mode\": mode,\n", + " \"selected_route\": route.get(\"selected_route\"),\n", + " \"route_request\": route.get(\"request\"),\n", + " \"switch_size\": route.get(\"pyscf_switch_size\"),\n", + " \"implementation_sha256\": route.get(\"implementation_sha256\"),\n", + " \"runtime_status\": runtime.get(\"status\", \"missing\"),\n", + " \"memory_status\": memory.get(\"status\", \"missing\"),\n", + " \"runtime_samples_seconds\": runtime_samples,\n", + " \"runtime_seconds\": (\n", + " statistics.median(runtime_samples) if runtime_samples else np.nan\n", + " ),\n", + " \"incremental_peak_bytes\": memory.get(\"incremental_peak_bytes\"),\n", + " \"incremental_peak_gib\": (\n", + " float(memory[\"incremental_peak_bytes\"]) / 1024**3\n", + " if memory.get(\"incremental_peak_bytes\") is not None\n", + " else np.nan\n", + " ),\n", + " \"allocator_baseline_bytes\": memory.get(\"allocator_baseline_bytes\"),\n", + " \"allocator_baseline_gib\": (\n", + " float(memory[\"allocator_baseline_bytes\"]) / 1024**3\n", + " if memory.get(\"allocator_baseline_bytes\") is not None\n", + " else np.nan\n", + " ),\n", + " }\n", + " for measurement, result in ((\"runtime\", runtime), (\"memory\", memory)):\n", + " fingerprint = result.get(\"fingerprint\", {})\n", + " for fingerprint_key in FINGERPRINT_LABELS:\n", + " row[f\"{measurement}_{fingerprint_key}\"] = fingerprint.get(\n", + " fingerprint_key, np.nan\n", + " )\n", + " rows.append(row)\n", + " return rows\n", + "\n", + "\n", + "def print_skalaxc_rotation_summary(\n", + " documents: list[tuple[Path, dict[str, Any]]],\n", + ") -> None:\n", + " if not documents:\n", + " return\n", + " print(\"SkalaXC rotation grid summary:\")\n", + " print(\n", + " f\"{'run':22s} {'orientations':>12s} {'grid':12s} \"\n", + " f\"{'PySCF':>10s} {'SkalaXC':>10s} {'ratio':>8s}\"\n", + " )\n", + " for _, document in documents:\n", + " orientations = document.get(\"orientations\", {})\n", + " observed_records = [\n", + " orientation.get(\"observed\") or {}\n", + " for orientation in orientations.values()\n", + " if orientation.get(\"observed\")\n", + " ]\n", + " observed = observed_records[0] if observed_records else {}\n", + " print(\n", + " f\"{document['run_label'][:22]:22s} {len(orientations):12d} \"\n", + " f\"{str(observed.get('grid_size', 'pending')):12s} \"\n", + " f\"{int(observed.get('pyscf_grid_points', 0)):10d} \"\n", + " f\"{int(observed.get('grid_points', 0)):10d} \"\n", + " f\"{float(observed.get('grid_point_ratio', np.nan)):8.3f}\"\n", + " )\n", + "\n", + "\n", + "DOCUMENTS: list[tuple[Path, dict[str, Any]]] = []\n", + "SKALAXC_DOCUMENTS: list[tuple[Path, dict[str, Any]]] = []\n", + "validation_rows: list[dict[str, str]] = []\n", + "normalized_rows: list[dict[str, Any]] = []\n", + "for result_file in SELECTED_RESULT_FILES:\n", + " try:\n", + " raw_document = json.loads(result_file.read_text(encoding=\"utf-8\"))\n", + " except (OSError, json.JSONDecodeError) as error:\n", + " validation_rows.append(\n", + " {\"run_label\": result_file.stem, \"failure\": f\"could not load: {error}\"}\n", + " )\n", + " continue\n", + " document = rotation_implementation_view(raw_document, \"skala\")\n", + " if document is None:\n", + " validation_rows.append(\n", + " {\"run_label\": result_file.stem, \"failure\": \"missing Skala results\"}\n", + " )\n", + " continue\n", + " DOCUMENTS.append((result_file, document))\n", + " skalaxc_document = rotation_implementation_view(raw_document, \"skalaxc\")\n", + " if skalaxc_document is not None:\n", + " SKALAXC_DOCUMENTS.append((result_file, skalaxc_document))\n", + " normalized_rows.extend(normalize_document(result_file, document))\n", + " failures = validate_document(result_file, document)\n", + " run_label = str(document.get(\"run_label\") or result_file.stem)\n", + " validation_rows.extend(\n", + " {\"run_label\": run_label, \"failure\": failure} for failure in failures\n", + " )\n", + "\n", + "run_labels = [\n", + " str(document.get(\"run_label\") or path.stem) for path, document in DOCUMENTS\n", + "]\n", + "duplicate_run_labels = sorted(\n", + " label for label, count in Counter(run_labels).items() if count > 1\n", + ")\n", + "if duplicate_run_labels:\n", + " raise ValueError(f\"Run labels must be unique: {duplicate_run_labels}\")\n", + "\n", + "normalized_df = pd.DataFrame(normalized_rows, columns=NORMALIZED_COLUMNS)\n", + "validation_df = pd.DataFrame(validation_rows, columns=[\"run_label\", \"failure\"])\n", + "if DOCUMENTS:\n", + " print(\n", + " f\"Loaded {len(DOCUMENTS)} document(s) and {len(normalized_df)} normalized Skala rows\"\n", + " )\n", + " print_skalaxc_rotation_summary(SKALAXC_DOCUMENTS)\n", + "else:\n", + " print(\"No rotation benchmark JSON files found. Run the benchmark first.\")\n", + "display(\n", + " validation_df\n", + " if not validation_df.empty\n", + " else pd.DataFrame({\"validation\": [\"passed\"]})\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c20ff5e3", + "metadata": {}, + "outputs": [], + "source": [ + "if normalized_df.empty:\n", + " status_summary_df = pd.DataFrame()\n", + " route_summary_df = pd.DataFrame()\n", + "else:\n", + " status_rows: list[dict[str, Any]] = []\n", + " for status_column in (\"runtime_status\", \"memory_status\"):\n", + " measurement = status_column.removesuffix(\"_status\")\n", + " grouped = normalized_df.groupby(\n", + " [\"run_label\", \"mode\", status_column], dropna=False\n", + " ).size()\n", + " for (run_label, mode, status), count in grouped.items():\n", + " status_rows.append(\n", + " {\n", + " \"run_label\": run_label,\n", + " \"mode\": mode,\n", + " \"measurement\": measurement,\n", + " \"status\": status,\n", + " \"count\": int(count),\n", + " }\n", + " )\n", + " status_summary_df = pd.DataFrame(status_rows)\n", + " route_summary_df = (\n", + " normalized_df.groupby([\"run_label\", \"mode\", \"selected_route\"], dropna=False)\n", + " .size()\n", + " .rename(\"orientation_count\")\n", + " .reset_index()\n", + " )\n", + "\n", + "print(\"Measurement status counts\")\n", + "display(status_summary_df)\n", + "print(\"Selected route counts\")\n", + "display(route_summary_df)" + ] + }, + { + "cell_type": "markdown", + "id": "b0a59c29", + "metadata": {}, + "source": [ + "## 6. Compare Numerical Fingerprints\n", + "\n", + "For every orientation, the runtime and memory workers are compared within each mode. The runtime fingerprints for GPU and screened CPU are also compared with CPU dense at the same orientation. The table reports signed, absolute, and relative differences for all six recorded quantities and applies configurable mode-specific tolerances." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3d5affd6", + "metadata": {}, + "outputs": [], + "source": [ + "NUMERICAL_COLUMNS = [\n", + " \"run_label\",\n", + " \"orientation_key\",\n", + " \"orientation_index\",\n", + " \"azimuth_degrees\",\n", + " \"polar_degrees\",\n", + " \"comparison\",\n", + " \"mode\",\n", + " \"fingerprint\",\n", + " \"reference_value\",\n", + " \"comparison_value\",\n", + " \"difference\",\n", + " \"absolute_error\",\n", + " \"relative_error\",\n", + " \"tolerance\",\n", + " \"within_tolerance\",\n", + "]\n", + "\n", + "\n", + "def numerical_difference(\n", + " row: pd.Series,\n", + " *,\n", + " comparison: str,\n", + " mode: str,\n", + " fingerprint: str,\n", + " reference_value: float,\n", + " comparison_value: float,\n", + " rtol: float,\n", + " atol: float,\n", + ") -> dict[str, Any] | None:\n", + " if not np.isfinite(reference_value) or not np.isfinite(comparison_value):\n", + " return None\n", + " difference = float(comparison_value - reference_value)\n", + " absolute_error = abs(difference)\n", + " relative_error = absolute_error / max(\n", + " abs(float(reference_value)), np.finfo(float).tiny\n", + " )\n", + " tolerance = atol + rtol * abs(float(reference_value))\n", + " return {\n", + " \"run_label\": row[\"run_label\"],\n", + " \"orientation_key\": row[\"orientation_key\"],\n", + " \"orientation_index\": row[\"orientation_index\"],\n", + " \"azimuth_degrees\": row[\"azimuth_degrees\"],\n", + " \"polar_degrees\": row[\"polar_degrees\"],\n", + " \"comparison\": comparison,\n", + " \"mode\": mode,\n", + " \"fingerprint\": fingerprint,\n", + " \"reference_value\": float(reference_value),\n", + " \"comparison_value\": float(comparison_value),\n", + " \"difference\": difference,\n", + " \"absolute_error\": absolute_error,\n", + " \"relative_error\": relative_error,\n", + " \"tolerance\": tolerance,\n", + " \"within_tolerance\": absolute_error <= tolerance,\n", + " }\n", + "\n", + "\n", + "numerical_rows: list[dict[str, Any]] = []\n", + "for _, row in normalized_df.iterrows():\n", + " mode = str(row[\"mode\"])\n", + " rtol, atol = ERROR_TOLERANCES.get(mode, (5e-8, 1e-8))\n", + " for fingerprint in FINGERPRINT_LABELS:\n", + " record = numerical_difference(\n", + " row,\n", + " comparison=\"runtime_vs_memory\",\n", + " mode=mode,\n", + " fingerprint=fingerprint,\n", + " reference_value=float(row[f\"runtime_{fingerprint}\"]),\n", + " comparison_value=float(row[f\"memory_{fingerprint}\"]),\n", + " rtol=rtol,\n", + " atol=atol,\n", + " )\n", + " if record is not None:\n", + " numerical_rows.append(record)\n", + "\n", + "if not normalized_df.empty:\n", + " indexed = normalized_df.set_index(\n", + " [\"run_label\", \"orientation_key\", \"mode\"], drop=False\n", + " )\n", + " for (run_label, orientation_key), _ in normalized_df.groupby(\n", + " [\"run_label\", \"orientation_key\"]\n", + " ):\n", + " dense_key = (run_label, orientation_key, REFERENCE_MODE)\n", + " if dense_key not in indexed.index:\n", + " continue\n", + " dense_row = indexed.loc[dense_key]\n", + " for mode in (\"cpu_screened\", \"gpu\"):\n", + " production_key = (run_label, orientation_key, mode)\n", + " if production_key not in indexed.index:\n", + " continue\n", + " production_row = indexed.loc[production_key]\n", + " rtol, atol = ERROR_TOLERANCES[mode]\n", + " for fingerprint in FINGERPRINT_LABELS:\n", + " record = numerical_difference(\n", + " production_row,\n", + " comparison=\"mode_vs_cpu_dense\",\n", + " mode=mode,\n", + " fingerprint=fingerprint,\n", + " reference_value=float(dense_row[f\"runtime_{fingerprint}\"]),\n", + " comparison_value=float(production_row[f\"runtime_{fingerprint}\"]),\n", + " rtol=rtol,\n", + " atol=atol,\n", + " )\n", + " if record is not None:\n", + " numerical_rows.append(record)\n", + "\n", + "numerical_df = pd.DataFrame(numerical_rows, columns=NUMERICAL_COLUMNS)\n", + "if numerical_df.empty:\n", + " numerical_summary_df = pd.DataFrame()\n", + "else:\n", + " numerical_summary_df = (\n", + " numerical_df.groupby([\"run_label\", \"comparison\", \"mode\", \"fingerprint\"])\n", + " .agg(\n", + " compared=(\"absolute_error\", \"size\"),\n", + " max_absolute_error=(\"absolute_error\", \"max\"),\n", + " max_relative_error=(\"relative_error\", \"max\"),\n", + " outside_tolerance=(\"within_tolerance\", lambda values: int((~values).sum())),\n", + " )\n", + " .reset_index()\n", + " )\n", + "display(numerical_summary_df)" + ] + }, + { + "cell_type": "markdown", + "id": "1c54906d", + "metadata": {}, + "source": [ + "## 7. Calculate Runtime Metrics and Speedups\n", + "\n", + "Runtime summaries use the median sample as the representative value. The comparison table includes dense-to-screened, dense-to-GPU, screened-CPU-to-GPU, and cross-run speedups at matched orientations.\n", + "\n", + "## 8. Calculate Memory Metrics and Reductions\n", + "\n", + "Incremental peaks and GPU allocator baselines are converted to GiB. Reduction factors use CPU dense as the numerator so values above one indicate improvement.\n", + "\n", + "## 9. Analyze Scaling with Molecular Size\n", + "\n", + "This benchmark intentionally fixes molecular size at C7H16 and 879 AOs, so molecular-size fitting is not meaningful. Instead, a harmonic least-squares model summarizes orientation sensitivity and records fit coefficients, $R^2$, and residual RMSE for runtime and memory." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "56dee578", + "metadata": {}, + "outputs": [], + "source": [ + "def summarize_metric(frame: pd.DataFrame, metric: str, value_name: str) -> pd.DataFrame:\n", + " rows: list[dict[str, Any]] = []\n", + " for (run_label, mode), group in frame.groupby([\"run_label\", \"mode\"]):\n", + " values = group[metric].dropna().astype(float).to_numpy()\n", + " if values.size == 0:\n", + " continue\n", + " mean_value = float(values.mean())\n", + " rows.append(\n", + " {\n", + " \"run_label\": run_label,\n", + " \"mode\": mode,\n", + " \"observation_count\": int(values.size),\n", + " \"minimum\": float(values.min()),\n", + " \"median\": float(np.median(values)),\n", + " \"mean\": mean_value,\n", + " \"standard_deviation\": (\n", + " float(values.std(ddof=1)) if values.size > 1 else 0.0\n", + " ),\n", + " \"coefficient_of_variation\": (\n", + " float(values.std(ddof=1) / mean_value)\n", + " if values.size > 1 and mean_value != 0.0\n", + " else 0.0\n", + " ),\n", + " \"representative\": float(np.median(values)),\n", + " \"unit\": value_name,\n", + " }\n", + " )\n", + " return pd.DataFrame(rows)\n", + "\n", + "\n", + "runtime_statistics_df = summarize_metric(normalized_df, \"runtime_seconds\", \"seconds\")\n", + "if not runtime_statistics_df.empty:\n", + " runtime_sample_counts = (\n", + " normalized_df.assign(\n", + " runtime_sample_count=normalized_df[\"runtime_samples_seconds\"].map(len)\n", + " )\n", + " .groupby([\"run_label\", \"mode\"])[\"runtime_sample_count\"]\n", + " .sum()\n", + " .reset_index()\n", + " )\n", + " runtime_statistics_df = runtime_statistics_df.merge(\n", + " runtime_sample_counts,\n", + " on=[\"run_label\", \"mode\"],\n", + " how=\"left\",\n", + " )\n", + "memory_statistics_df = summarize_metric(normalized_df, \"incremental_peak_gib\", \"GiB\")\n", + "\n", + "\n", + "def finite_ratio(numerator: Any, denominator: Any) -> float:\n", + " numerator_value = float(numerator)\n", + " denominator_value = float(denominator)\n", + " if (\n", + " np.isfinite(numerator_value)\n", + " and np.isfinite(denominator_value)\n", + " and denominator_value > 0.0\n", + " ):\n", + " return numerator_value / denominator_value\n", + " return np.nan\n", + "\n", + "\n", + "comparison_rows: list[dict[str, Any]] = []\n", + "for (run_label, orientation_key), group in normalized_df.groupby(\n", + " [\"run_label\", \"orientation_key\"]\n", + "):\n", + " by_mode = group.set_index(\"mode\")\n", + " if any(mode not in by_mode.index for mode in MODES):\n", + " continue\n", + " dense = by_mode.loc[\"cpu_dense\"]\n", + " screened = by_mode.loc[\"cpu_screened\"]\n", + " gpu = by_mode.loc[\"gpu\"]\n", + " comparison_rows.append(\n", + " {\n", + " \"run_label\": run_label,\n", + " \"orientation_key\": orientation_key,\n", + " \"orientation_index\": dense[\"orientation_index\"],\n", + " \"azimuth_degrees\": dense[\"azimuth_degrees\"],\n", + " \"polar_degrees\": dense[\"polar_degrees\"],\n", + " \"dense_to_screened_runtime_speedup\": finite_ratio(\n", + " dense[\"runtime_seconds\"], screened[\"runtime_seconds\"]\n", + " ),\n", + " \"dense_to_gpu_runtime_speedup\": finite_ratio(\n", + " dense[\"runtime_seconds\"], gpu[\"runtime_seconds\"]\n", + " ),\n", + " \"screened_cpu_to_gpu_runtime_speedup\": finite_ratio(\n", + " screened[\"runtime_seconds\"], gpu[\"runtime_seconds\"]\n", + " ),\n", + " \"dense_to_screened_memory_reduction\": finite_ratio(\n", + " dense[\"incremental_peak_gib\"], screened[\"incremental_peak_gib\"]\n", + " ),\n", + " \"dense_to_gpu_memory_reduction\": finite_ratio(\n", + " dense[\"incremental_peak_gib\"], gpu[\"incremental_peak_gib\"]\n", + " ),\n", + " \"screened_cpu_to_gpu_memory_ratio\": finite_ratio(\n", + " screened[\"incremental_peak_gib\"], gpu[\"incremental_peak_gib\"]\n", + " ),\n", + " }\n", + " )\n", + "comparison_metrics_df = pd.DataFrame(comparison_rows)\n", + "\n", + "cross_run_rows: list[dict[str, Any]] = []\n", + "if len(DOCUMENTS) > 1 and not normalized_df.empty:\n", + " baseline_path, baseline_document = DOCUMENTS[0]\n", + " baseline_run_label = str(baseline_document.get(\"run_label\") or baseline_path.stem)\n", + " baseline = normalized_df[\n", + " normalized_df[\"run_label\"] == baseline_run_label\n", + " ].set_index([\"orientation_key\", \"mode\"])\n", + " for current_path, document in DOCUMENTS[1:]:\n", + " current_run_label = str(document.get(\"run_label\") or current_path.stem)\n", + " current = normalized_df[\n", + " normalized_df[\"run_label\"] == current_run_label\n", + " ].set_index([\"orientation_key\", \"mode\"])\n", + " for key in baseline.index.intersection(current.index):\n", + " baseline_row = baseline.loc[key]\n", + " current_row = current.loc[key]\n", + " cross_run_rows.append(\n", + " {\n", + " \"baseline_run_label\": baseline_run_label,\n", + " \"comparison_run_label\": current_run_label,\n", + " \"orientation_key\": key[0],\n", + " \"mode\": key[1],\n", + " \"runtime_speedup\": finite_ratio(\n", + " baseline_row[\"runtime_seconds\"],\n", + " current_row[\"runtime_seconds\"],\n", + " ),\n", + " \"memory_reduction\": finite_ratio(\n", + " baseline_row[\"incremental_peak_gib\"],\n", + " current_row[\"incremental_peak_gib\"],\n", + " ),\n", + " }\n", + " )\n", + "cross_run_df = pd.DataFrame(cross_run_rows)\n", + "\n", + "orientation_fit_rows: list[dict[str, Any]] = []\n", + "for (run_label, mode), group in normalized_df.groupby([\"run_label\", \"mode\"]):\n", + " for metric in (\"runtime_seconds\", \"incremental_peak_gib\"):\n", + " fit_data = group.dropna(subset=[\"azimuth_degrees\", \"polar_degrees\", metric])\n", + " if len(fit_data) < 5:\n", + " continue\n", + " azimuth = np.radians(fit_data[\"azimuth_degrees\"].to_numpy(float))\n", + " polar = np.radians(fit_data[\"polar_degrees\"].to_numpy(float))\n", + " design = np.column_stack(\n", + " [\n", + " np.ones(len(fit_data)),\n", + " np.sin(azimuth),\n", + " np.cos(azimuth),\n", + " np.sin(polar),\n", + " np.cos(polar),\n", + " ]\n", + " )\n", + " values = fit_data[metric].to_numpy(float)\n", + " coefficients, _, _, _ = np.linalg.lstsq(design, values, rcond=None)\n", + " residuals = values - design @ coefficients\n", + " total_variation = float(np.square(values - values.mean()).sum())\n", + " residual_variation = float(np.square(residuals).sum())\n", + " orientation_fit_rows.append(\n", + " {\n", + " \"run_label\": run_label,\n", + " \"mode\": mode,\n", + " \"metric\": metric,\n", + " \"intercept\": float(coefficients[0]),\n", + " \"sin_azimuth\": float(coefficients[1]),\n", + " \"cos_azimuth\": float(coefficients[2]),\n", + " \"sin_polar\": float(coefficients[3]),\n", + " \"cos_polar\": float(coefficients[4]),\n", + " \"r_squared\": (\n", + " 1.0 - residual_variation / total_variation\n", + " if total_variation > 0.0\n", + " else 1.0\n", + " ),\n", + " \"residual_rmse\": float(np.sqrt(np.mean(np.square(residuals)))),\n", + " }\n", + " )\n", + "orientation_fit_df = pd.DataFrame(orientation_fit_rows)\n", + "\n", + "print(\"Runtime statistics\")\n", + "display(runtime_statistics_df)\n", + "print(\"Memory statistics\")\n", + "display(memory_statistics_df)\n", + "print(\"Per-orientation speedup and reduction metrics\")\n", + "display(comparison_metrics_df)\n", + "print(\"Cross-run changes\")\n", + "display(cross_run_df)\n", + "print(\"Orientation-sensitivity fits\")\n", + "display(orientation_fit_df)" + ] + }, + { + "cell_type": "markdown", + "id": "bf4b225f", + "metadata": {}, + "source": [ + "## 10. Visualize Runtime Comparisons\n", + "\n", + "Runtime is shown by orientation index, as mode/revision distributions, and as 12-by-6 azimuth/polar heatmaps. Since AO count is fixed, route labels replace a screening-threshold marker.\n", + "\n", + "## 11. Visualize Memory Comparisons\n", + "\n", + "Incremental peak memory uses the same views, with GPU allocator baseline retained in the normalized table and exported metadata.\n", + "\n", + "## 12. Visualize Speedup and Memory Reduction\n", + "\n", + "Dense-to-screened and dense-to-GPU factors are rendered on the same angular grid. Values above one indicate faster execution or lower peak memory than CPU dense.\n", + "\n", + "## 13. Visualize Numerical Differences\n", + "\n", + "Signed fingerprint differences use CPU dense as zero. Change `FINGERPRINT_TO_PLOT` to inspect any of the six recorded fingerprint quantities." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ea347d87", + "metadata": {}, + "outputs": [], + "source": [ + "import re\n", + "\n", + "from matplotlib.figure import Figure\n", + "\n", + "LINE_STYLES = (\"-\", \"--\", \"-.\", \":\")\n", + "MARKERS = (\"o\", \"s\", \"^\", \"D\")\n", + "PLOT_STYLES = tuple(zip(LINE_STYLES, MARKERS, strict=True))\n", + "if len(DOCUMENTS) > len(PLOT_STYLES):\n", + " raise ValueError(f\"At most {len(PLOT_STYLES)} distinct run labels can be plotted\")\n", + "LABEL_PLOT_STYLES = {\n", + " str(document.get(\"run_label\") or path.stem): PLOT_STYLES[index]\n", + " for index, (path, document) in enumerate(DOCUMENTS)\n", + "}\n", + "\n", + "\n", + "def safe_filename(value: str) -> str:\n", + " return re.sub(r\"[^A-Za-z0-9_.-]+\", \"-\", value).strip(\"-.\") or \"result\"\n", + "\n", + "\n", + "def orientation_matrix(frame: pd.DataFrame, value_column: str) -> pd.DataFrame:\n", + " if frame.empty:\n", + " return pd.DataFrame()\n", + " return (\n", + " frame.pivot(\n", + " index=\"polar_degrees\",\n", + " columns=\"azimuth_degrees\",\n", + " values=value_column,\n", + " )\n", + " .sort_index()\n", + " .sort_index(axis=1)\n", + " )\n", + "\n", + "\n", + "def finite_value_range(values: Any) -> tuple[float, float] | None:\n", + " numeric = np.asarray(values, dtype=float)\n", + " finite = numeric[np.isfinite(numeric)]\n", + " if finite.size == 0:\n", + " return None\n", + " minimum = float(finite.min())\n", + " maximum = float(finite.max())\n", + " if minimum == maximum:\n", + " padding = max(abs(minimum) * 1e-9, np.finfo(float).eps)\n", + " return minimum - padding, maximum + padding\n", + " return minimum, maximum\n", + "\n", + "\n", + "def observed_range_errors(samples: Any, center: float) -> tuple[float, float]:\n", + " numeric = np.asarray(samples, dtype=float)\n", + " finite = numeric[np.isfinite(numeric)]\n", + " if finite.size == 0:\n", + " return 0.0, 0.0\n", + " return (\n", + " max(0.0, center - float(finite.min())),\n", + " max(0.0, float(finite.max()) - center),\n", + " )\n", + "\n", + "\n", + "def finish_figure(figure: Figure, output_path: Path | None = None) -> None:\n", + " if output_path is not None:\n", + " output_path.parent.mkdir(parents=True, exist_ok=True)\n", + " figure.savefig(output_path, dpi=180, bbox_inches=\"tight\")\n", + " plt.show()\n", + "\n", + "\n", + "def plot_orientation_lines(\n", + " value_column: str,\n", + " ylabel: str,\n", + " output_path: Path | None = None,\n", + " error_samples_column: str | None = None,\n", + ") -> None:\n", + " data = normalized_df.dropna(subset=[\"orientation_index\", value_column])\n", + " if data.empty:\n", + " print(f\"No successful values available for {value_column}\")\n", + " return\n", + " figure, axis = plt.subplots(figsize=(12, 6), constrained_layout=True)\n", + " for (run_label, mode), group in data.groupby([\"run_label\", \"mode\"]):\n", + " ordered = group.sort_values(\"orientation_index\")\n", + " x_values = ordered[\"orientation_index\"].to_numpy(float)\n", + " centers = ordered[value_column].to_numpy(float)\n", + " line_style, marker = LABEL_PLOT_STYLES[str(run_label)]\n", + " plot_options = {\n", + " \"color\": MODE_COLORS[mode],\n", + " \"linewidth\": 1.2,\n", + " \"alpha\": 0.85,\n", + " \"label\": f\"{run_label} {mode}\",\n", + " \"linestyle\": line_style,\n", + " \"marker\": marker,\n", + " \"markersize\": 3.5,\n", + " \"markevery\": max(1, len(ordered) // 12),\n", + " }\n", + " if error_samples_column is None:\n", + " axis.plot(x_values, centers, **plot_options)\n", + " else:\n", + " errors = np.asarray(\n", + " [\n", + " observed_range_errors(samples, center)\n", + " for samples, center in zip(\n", + " ordered[error_samples_column], centers, strict=True\n", + " )\n", + " ],\n", + " dtype=float,\n", + " ).T\n", + " axis.errorbar(\n", + " x_values,\n", + " centers,\n", + " yerr=errors,\n", + " capsize=2,\n", + " elinewidth=0.7,\n", + " **plot_options,\n", + " )\n", + " title = f\"{ylabel} across molecular orientations\"\n", + " if error_samples_column is not None:\n", + " title += \" (median and observed min-max)\"\n", + " axis.set(\n", + " title=title,\n", + " xlabel=\"Orientation index (azimuth-major, then polar)\",\n", + " ylabel=ylabel,\n", + " )\n", + " axis.grid(True, color=\"#D9D9D9\", linewidth=0.6)\n", + " axis.legend(fontsize=8, ncol=2)\n", + " finish_figure(figure, output_path)\n", + "\n", + "\n", + "def plot_mode_distribution(\n", + " value_column: str, ylabel: str, output_path: Path | None = None\n", + ") -> None:\n", + " data = normalized_df.dropna(subset=[value_column])\n", + " if data.empty:\n", + " print(f\"No successful values available for {value_column}\")\n", + " return\n", + " figure, axis = plt.subplots(figsize=(10, 6), constrained_layout=True)\n", + " sns.boxplot(\n", + " data=data,\n", + " x=\"mode\",\n", + " y=value_column,\n", + " hue=\"run_label\",\n", + " order=MODES,\n", + " showfliers=True,\n", + " ax=axis,\n", + " )\n", + " axis.set(title=f\"{ylabel} distribution by mode\", xlabel=\"Mode\", ylabel=ylabel)\n", + " axis.grid(True, axis=\"y\", color=\"#D9D9D9\", linewidth=0.6)\n", + " finish_figure(figure, output_path)\n", + "\n", + "\n", + "def plot_measurement_heatmaps(\n", + " value_column: str,\n", + " colorbar_label: str,\n", + " output_dir: Path | None = None,\n", + ") -> None:\n", + " if normalized_df[value_column].dropna().empty:\n", + " print(f\"No successful values available for {value_column}\")\n", + " return\n", + " for path, document in DOCUMENTS:\n", + " run_label = str(document.get(\"run_label\") or path.stem)\n", + " run_data = normalized_df[normalized_df[\"run_label\"] == run_label]\n", + " figure, axes = plt.subplots(\n", + " 1, len(MODES), figsize=(18, 4.8), constrained_layout=True\n", + " )\n", + " for axis, mode in zip(axes, MODES, strict=True):\n", + " matrix = orientation_matrix(\n", + " run_data[run_data[\"mode\"] == mode], value_column\n", + " )\n", + " value_range = finite_value_range(matrix)\n", + " if value_range is None:\n", + " axis.text(0.5, 0.5, \"No successful data\", ha=\"center\", va=\"center\")\n", + " axis.set_axis_off()\n", + " continue\n", + " minimum, maximum = value_range\n", + " sns.heatmap(\n", + " matrix,\n", + " mask=matrix.isna(),\n", + " cmap=\"viridis\",\n", + " vmin=minimum,\n", + " vmax=maximum,\n", + " cbar_kws={\"label\": colorbar_label},\n", + " ax=axis,\n", + " )\n", + " route_values = (\n", + " run_data.loc[run_data[\"mode\"] == mode, \"selected_route\"]\n", + " .dropna()\n", + " .unique()\n", + " )\n", + " route_label = \", \".join(str(value) for value in route_values) or \"no route\"\n", + " axis.set(\n", + " title=f\"{mode} ({route_label})\",\n", + " xlabel=\"Azimuth (degrees)\",\n", + " ylabel=\"Polar angle (degrees)\",\n", + " )\n", + " figure.suptitle(f\"{run_label} {colorbar_label} by orientation\")\n", + " output_path = (\n", + " output_dir\n", + " / f\"{safe_filename(run_label)}-{safe_filename(value_column)}-heatmap.png\"\n", + " if output_dir is not None\n", + " else None\n", + " )\n", + " finish_figure(figure, output_path)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f41656f2", + "metadata": {}, + "outputs": [], + "source": [ + "RATIO_LABELS = {\n", + " \"dense_to_screened_runtime_speedup\": \"CPU dense / CPU screened runtime\",\n", + " \"dense_to_gpu_runtime_speedup\": \"CPU dense / GPU runtime\",\n", + " \"dense_to_screened_memory_reduction\": \"CPU dense / CPU screened peak\",\n", + " \"dense_to_gpu_memory_reduction\": \"CPU dense / GPU peak\",\n", + "}\n", + "\n", + "\n", + "def plot_ratio_heatmaps(\n", + " ratio_columns: tuple[str, ...],\n", + " title: str,\n", + " output_dir: Path | None = None,\n", + ") -> None:\n", + " if comparison_metrics_df.empty:\n", + " print(f\"No matched mode data available for {title}\")\n", + " return\n", + " for path, document in DOCUMENTS:\n", + " run_label = str(document.get(\"run_label\") or path.stem)\n", + " run_data = comparison_metrics_df[\n", + " comparison_metrics_df[\"run_label\"] == run_label\n", + " ]\n", + " figure, axes = plt.subplots(\n", + " 1, len(ratio_columns), figsize=(12, 4.8), constrained_layout=True\n", + " )\n", + " axes_array = np.atleast_1d(axes)\n", + " for axis, ratio_column in zip(axes_array, ratio_columns, strict=True):\n", + " matrix = orientation_matrix(run_data, ratio_column)\n", + " value_range = finite_value_range(matrix)\n", + " if value_range is None:\n", + " axis.text(0.5, 0.5, \"No successful data\", ha=\"center\", va=\"center\")\n", + " axis.set_axis_off()\n", + " continue\n", + " minimum, maximum = value_range\n", + " heatmap_options: dict[str, Any] = {\n", + " \"cmap\": \"RdYlGn\",\n", + " \"vmin\": minimum,\n", + " \"vmax\": maximum,\n", + " }\n", + " if minimum < 1.0 < maximum:\n", + " heatmap_options[\"center\"] = 1.0\n", + " sns.heatmap(\n", + " matrix,\n", + " mask=matrix.isna(),\n", + " cbar_kws={\"label\": \"Factor\"},\n", + " ax=axis,\n", + " **heatmap_options,\n", + " )\n", + " axis.set(\n", + " title=RATIO_LABELS[ratio_column],\n", + " xlabel=\"Azimuth (degrees)\",\n", + " ylabel=\"Polar angle (degrees)\",\n", + " )\n", + " figure.suptitle(f\"{run_label} {title}\")\n", + " output_path = (\n", + " output_dir / f\"{safe_filename(run_label)}-{safe_filename(title)}.png\"\n", + " if output_dir is not None\n", + " else None\n", + " )\n", + " finish_figure(figure, output_path)\n", + "\n", + "\n", + "def plot_fingerprint_heatmaps(fingerprint: str, output_dir: Path | None = None) -> None:\n", + " if fingerprint not in FINGERPRINT_LABELS:\n", + " raise ValueError(f\"Unknown fingerprint {fingerprint!r}\")\n", + " data = numerical_df[\n", + " (numerical_df[\"comparison\"] == \"mode_vs_cpu_dense\")\n", + " & (numerical_df[\"fingerprint\"] == fingerprint)\n", + " ]\n", + " if data.empty:\n", + " print(f\"No matched numerical data available for {fingerprint}\")\n", + " return\n", + " for run_label, run_data in data.groupby(\"run_label\"):\n", + " figure, axes = plt.subplots(1, 2, figsize=(12, 4.8), constrained_layout=True)\n", + " for axis, mode in zip(axes, (\"cpu_screened\", \"gpu\"), strict=True):\n", + " matrix = orientation_matrix(\n", + " run_data[run_data[\"mode\"] == mode], \"difference\"\n", + " )\n", + " value_range = finite_value_range(matrix)\n", + " if value_range is None:\n", + " axis.text(0.5, 0.5, \"No successful data\", ha=\"center\", va=\"center\")\n", + " axis.set_axis_off()\n", + " continue\n", + " minimum, maximum = value_range\n", + " heatmap_options = {\n", + " \"cmap\": \"coolwarm\",\n", + " \"vmin\": minimum,\n", + " \"vmax\": maximum,\n", + " }\n", + " if minimum < 0.0 < maximum:\n", + " heatmap_options[\"center\"] = 0.0\n", + " sns.heatmap(\n", + " matrix,\n", + " mask=matrix.isna(),\n", + " cbar_kws={\"label\": \"Mode - CPU dense\"},\n", + " ax=axis,\n", + " **heatmap_options,\n", + " )\n", + " axis.set(\n", + " title=mode,\n", + " xlabel=\"Azimuth (degrees)\",\n", + " ylabel=\"Polar angle (degrees)\",\n", + " )\n", + " figure.suptitle(f\"{run_label} {FINGERPRINT_LABELS[fingerprint]} difference\")\n", + " output_path = (\n", + " output_dir\n", + " / f\"{safe_filename(str(run_label))}-{safe_filename(fingerprint)}-difference.png\"\n", + " if output_dir is not None\n", + " else None\n", + " )\n", + " finish_figure(figure, output_path)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "942e3acc", + "metadata": {}, + "outputs": [], + "source": [ + "FINGERPRINT_TO_PLOT = \"xc_energy\"\n", + "\n", + "plot_orientation_lines(\n", + " \"runtime_seconds\",\n", + " \"Runtime (s)\",\n", + " error_samples_column=\"runtime_samples_seconds\",\n", + ")\n", + "plot_mode_distribution(\"runtime_seconds\", \"Runtime (s)\")\n", + "plot_measurement_heatmaps(\"runtime_seconds\", \"Runtime (s)\")\n", + "\n", + "plot_orientation_lines(\"incremental_peak_gib\", \"Incremental peak memory (GiB)\")\n", + "plot_mode_distribution(\"incremental_peak_gib\", \"Incremental peak memory (GiB)\")\n", + "plot_measurement_heatmaps(\"incremental_peak_gib\", \"Incremental peak memory (GiB)\")\n", + "\n", + "plot_ratio_heatmaps(\n", + " (\"dense_to_screened_runtime_speedup\", \"dense_to_gpu_runtime_speedup\"),\n", + " \"runtime speedup\",\n", + ")\n", + "plot_ratio_heatmaps(\n", + " (\"dense_to_screened_memory_reduction\", \"dense_to_gpu_memory_reduction\"),\n", + " \"memory reduction\",\n", + ")\n", + "plot_fingerprint_heatmaps(FINGERPRINT_TO_PLOT)" + ] + }, + { + "cell_type": "markdown", + "id": "be0bdf30", + "metadata": {}, + "source": [ + "## 14. Record Environment and Source Metadata\n", + "\n", + "This table keeps hardware, package versions, CUDA details, thread settings, scientific configuration, source commit, dirty state, implementation hash, and both runner hashes alongside every comparison.\n", + "\n", + "## 15. Export Comparison Results to JSON\n", + "\n", + "The comparison artifact contains JSON-safe configuration summaries, normalized rows, runtime and memory statistics, speedups, numerical differences, validation failures, angular fits, environment metadata, and source provenance.\n", + "\n", + "## 16. Save Tables and Figures\n", + "\n", + "The final cell writes CSV tables and deterministic PNG files below `benchmarks/results/rotation_comparison`. Re-running the cell refreshes the report artifacts from the currently selected input files." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c530c585", + "metadata": {}, + "outputs": [], + "source": [ + "metadata_rows: list[dict[str, Any]] = []\n", + "validation_failure_counts = Counter(row[\"run_label\"] for row in validation_rows)\n", + "for path, document in DOCUMENTS:\n", + " environment = document.get(\"environment\", {})\n", + " packages = environment.get(\"packages\", {})\n", + " cuda = environment.get(\"cuda\", {})\n", + " configuration = document.get(\"configuration\", {})\n", + " source = document.get(\"source\", {})\n", + " hashes = document.get(\"runner_hashes\", {})\n", + " run_label = str(document.get(\"run_label\") or path.stem)\n", + " document_rows = normalized_df[normalized_df[\"run_label\"] == run_label]\n", + " implementation_hashes = sorted(\n", + " str(value) for value in document_rows[\"implementation_sha256\"].dropna().unique()\n", + " )\n", + " metadata_rows.append(\n", + " {\n", + " \"run_label\": run_label,\n", + " \"created_at\": document.get(\"created_at\"),\n", + " \"updated_at\": document.get(\"updated_at\"),\n", + " \"python\": environment.get(\"python\"),\n", + " \"python_executable\": environment.get(\"python_executable\"),\n", + " \"pyscf\": packages.get(\"pyscf\"),\n", + " \"skala\": packages.get(\"skala\"),\n", + " \"torch\": packages.get(\"torch\"),\n", + " \"cupy\": packages.get(\"cupy\"),\n", + " \"gpu4pyscf\": packages.get(\"gpu4pyscf\"),\n", + " \"memray\": packages.get(\"memray\"),\n", + " \"cuda_available\": cuda.get(\"available\"),\n", + " \"torch_cuda_version\": cuda.get(\"torch_cuda_version\"),\n", + " \"device_name\": cuda.get(\"device_name\"),\n", + " \"cpu_threads\": configuration.get(\"cpu_threads\"),\n", + " \"thread_environment\": environment.get(\"thread_environment\"),\n", + " \"basis\": configuration.get(\"basis\"),\n", + " \"functional\": configuration.get(\"functional\"),\n", + " \"grid_level\": configuration.get(\"grid_level\"),\n", + " \"grid_alignment\": configuration.get(\"grid_alignment\"),\n", + " \"max_memory_mb\": configuration.get(\"max_memory_mb\"),\n", + " \"orientation_count\": configuration.get(\"orientation_count\"),\n", + " \"commit\": source.get(\"commit\"),\n", + " \"branch\": source.get(\"branch\"),\n", + " \"dirty\": source.get(\"dirty\"),\n", + " \"implementation_hashes\": implementation_hashes,\n", + " \"worker_sha256\": hashes.get(\"worker_sha256\"),\n", + " \"rotation_runner_sha256\": hashes.get(\"rotation_runner_sha256\"),\n", + " \"validation_failure_count\": validation_failure_counts[run_label],\n", + " }\n", + " )\n", + "metadata_df = pd.DataFrame(metadata_rows)\n", + "display(metadata_df)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e35df038", + "metadata": {}, + "outputs": [], + "source": [ + "def records(frame: pd.DataFrame) -> list[dict[str, Any]]:\n", + " return frame.to_dict(orient=\"records\") if not frame.empty else []\n", + "\n", + "\n", + "def json_safe(value: Any) -> Any:\n", + " if isinstance(value, dict):\n", + " return {str(key): json_safe(item) for key, item in value.items()}\n", + " if isinstance(value, (list, tuple)):\n", + " return [json_safe(item) for item in value]\n", + " if isinstance(value, np.generic):\n", + " value = value.item()\n", + " if isinstance(value, float) and not np.isfinite(value):\n", + " return None\n", + " if value is pd.NA:\n", + " return None\n", + " return value\n", + "\n", + "\n", + "comparison_document = {\n", + " \"schema_version\": 1,\n", + " \"benchmark\": \"pyscf_ao_screening_rotation_comparison\",\n", + " \"generated_at\": datetime.now(UTC).isoformat(),\n", + " \"selected_run_labels\": [\n", + " str(document.get(\"run_label\") or path.stem) for path, document in DOCUMENTS\n", + " ],\n", + " \"configuration_summaries\": [\n", + " {\n", + " \"run_label\": str(document.get(\"run_label\") or path.stem),\n", + " \"configuration\": document.get(\"configuration\"),\n", + " }\n", + " for path, document in DOCUMENTS\n", + " ],\n", + " \"normalized_measurements\": records(normalized_df),\n", + " \"runtime_statistics\": records(runtime_statistics_df),\n", + " \"memory_statistics\": records(memory_statistics_df),\n", + " \"speedups_and_memory_reductions\": records(comparison_metrics_df),\n", + " \"cross_run_changes\": records(cross_run_df),\n", + " \"numerical_differences\": records(numerical_df),\n", + " \"numerical_summary\": records(numerical_summary_df),\n", + " \"orientation_sensitivity_fits\": records(orientation_fit_df),\n", + " \"validation_failures\": records(validation_df),\n", + " \"environment_metadata\": records(metadata_df),\n", + " \"source_provenance\": [\n", + " {\n", + " \"run_label\": str(document.get(\"run_label\") or path.stem),\n", + " \"source\": document.get(\"source\"),\n", + " \"runner_hashes\": document.get(\"runner_hashes\"),\n", + " }\n", + " for path, document in DOCUMENTS\n", + " ],\n", + "}\n", + "comparison_document = json_safe(comparison_document)\n", + "print(\n", + " f\"Prepared comparison JSON with \"\n", + " f\"{len(comparison_document['normalized_measurements'])} normalized rows\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9b9024b3", + "metadata": {}, + "outputs": [], + "source": [ + "ARTIFACT_DIR.mkdir(parents=True, exist_ok=True)\n", + "FIGURE_DIR.mkdir(parents=True, exist_ok=True)\n", + "TABLE_DIR.mkdir(parents=True, exist_ok=True)\n", + "\n", + "with COMPARISON_JSON.open(\"w\", encoding=\"utf-8\") as stream:\n", + " json.dump(comparison_document, stream, indent=2, sort_keys=True, allow_nan=False)\n", + " stream.write(\"\\n\")\n", + "\n", + "tables = {\n", + " \"normalized-measurements.csv\": normalized_df,\n", + " \"validation-failures.csv\": validation_df,\n", + " \"status-summary.csv\": status_summary_df,\n", + " \"route-summary.csv\": route_summary_df,\n", + " \"runtime-statistics.csv\": runtime_statistics_df,\n", + " \"memory-statistics.csv\": memory_statistics_df,\n", + " \"speedups-and-memory-reductions.csv\": comparison_metrics_df,\n", + " \"cross-run-changes.csv\": cross_run_df,\n", + " \"numerical-differences.csv\": numerical_df,\n", + " \"numerical-summary.csv\": numerical_summary_df,\n", + " \"orientation-sensitivity-fits.csv\": orientation_fit_df,\n", + " \"environment-and-source-metadata.csv\": metadata_df,\n", + "}\n", + "for filename, table in tables.items():\n", + " table.to_csv(TABLE_DIR / filename, index=False)\n", + "\n", + "plot_orientation_lines(\n", + " \"runtime_seconds\",\n", + " \"Runtime (s)\",\n", + " FIGURE_DIR / \"runtime-by-orientation.png\",\n", + ")\n", + "plot_mode_distribution(\n", + " \"runtime_seconds\",\n", + " \"Runtime (s)\",\n", + " FIGURE_DIR / \"runtime-by-mode.png\",\n", + ")\n", + "plot_measurement_heatmaps(\"runtime_seconds\", \"Runtime (s)\", FIGURE_DIR)\n", + "plot_orientation_lines(\n", + " \"incremental_peak_gib\",\n", + " \"Incremental peak memory (GiB)\",\n", + " FIGURE_DIR / \"memory-by-orientation.png\",\n", + ")\n", + "plot_mode_distribution(\n", + " \"incremental_peak_gib\",\n", + " \"Incremental peak memory (GiB)\",\n", + " FIGURE_DIR / \"memory-by-mode.png\",\n", + ")\n", + "plot_measurement_heatmaps(\n", + " \"incremental_peak_gib\", \"Incremental peak memory (GiB)\", FIGURE_DIR\n", + ")\n", + "plot_ratio_heatmaps(\n", + " (\"dense_to_screened_runtime_speedup\", \"dense_to_gpu_runtime_speedup\"),\n", + " \"runtime speedup\",\n", + " FIGURE_DIR,\n", + ")\n", + "plot_ratio_heatmaps(\n", + " (\"dense_to_screened_memory_reduction\", \"dense_to_gpu_memory_reduction\"),\n", + " \"memory reduction\",\n", + " FIGURE_DIR,\n", + ")\n", + "for fingerprint in FINGERPRINT_LABELS:\n", + " plot_fingerprint_heatmaps(fingerprint, FIGURE_DIR)\n", + "\n", + "print(f\"Comparison JSON: {COMPARISON_JSON}\")\n", + "print(f\"Tables: {TABLE_DIR}\")\n", + "print(f\"Figures: {FIGURE_DIR}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/benchmarks/run_pyscf_ao_screening_benchmark.py b/benchmarks/run_pyscf_ao_screening_benchmark.py new file mode 100644 index 00000000..8bbec288 --- /dev/null +++ b/benchmarks/run_pyscf_ao_screening_benchmark.py @@ -0,0 +1,1500 @@ +"""Run isolated Skala PySCF and GPU4PySCF AO-screening benchmarks.""" + +from __future__ import annotations + +import argparse +import hashlib +import importlib +import importlib.metadata +import inspect +import json +import math +import os +import platform +import re +import socket +import subprocess +import sys +import tempfile +import time +import traceback +from contextlib import AbstractContextManager, nullcontext +from dataclasses import asdict, dataclass +from datetime import UTC, datetime +from itertools import pairwise +from pathlib import Path +from typing import Any, cast +from unittest.mock import patch + +FULL_CARBON_COUNTS = (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) +EXPECTED_AO_COUNTS = ( + 294, + 411, + 528, + 645, + 762, + 879, + 996, + 1113, + 1230, + 1347, + 1464, +) +SKALA_MODES = ("cpu", "cpu_dense", "gpu") +SKALAXC_MODES = ("cpu", "gpu") +MODES = SKALA_MODES +IMPLEMENTATION_MODES = { + "skala": SKALA_MODES, + "skalaxc": SKALAXC_MODES, +} +MEASUREMENTS = ("runtime", "memory") +TERMINAL_STATUSES = { + "ok", + "timeout", + "oom", + "error", + "unsupported", + "skipped_after_resource_failure", +} +WORKER_RESULT_PREFIX = "SKALA_BENCHMARK_RESULT=" +THREAD_ENVIRONMENT_VARIABLES = ( + "OMP_NUM_THREADS", + "MKL_NUM_THREADS", + "OPENBLAS_NUM_THREADS", + "NUMEXPR_NUM_THREADS", +) + +Vector = tuple[float, float, float] +Atom = tuple[str, float, float, float] + +CARBON_CARBON_BOND_ANGSTROM = 1.54 +CARBON_HYDROGEN_BOND_ANGSTROM = 1.09 +CARBON_BOND_ANGLE_DEGREES = 112.0 +COORDINATE_PRECISION = 12 +GEOMETRY_PARAMETERS = { + "version": "zigzag-alkane-v1", + "carbon_carbon_bond_angstrom": CARBON_CARBON_BOND_ANGSTROM, + "carbon_hydrogen_bond_angstrom": CARBON_HYDROGEN_BOND_ANGSTROM, + "carbon_bond_angle_degrees": CARBON_BOND_ANGLE_DEGREES, + "hydrogen_dot_product": -1.0 / 3.0, + "coordinate_precision": COORDINATE_PRECISION, +} +EXPECTED_AOS_BY_CARBON: dict[int, int] = dict( + zip(FULL_CARBON_COUNTS, EXPECTED_AO_COUNTS, strict=True) +) + + +def find_repository_root(start: Path) -> Path: + for candidate in (start.resolve(), *start.resolve().parents): + if (candidate / "pyproject.toml").is_file() and ( + candidate / "LICENSE.txt" + ).is_file(): + return candidate + raise FileNotFoundError(f"Could not find the Skala repository above {start}") + + +RUNNER_ROOT = find_repository_root(Path(__file__).resolve()) +DEFAULT_SOURCE_ROOT = RUNNER_ROOT / "skala" + + +@dataclass(frozen=True) +class BenchmarkConfig: + source_root: Path + results_dir: Path + run_label: str + functional: str = "skala-1.1" + skalaxc_grid_size: str = "GM3" + basis: str = "def2-qzvpp" + grid_level: int = 1 + grid_alignment: int = 1 + max_memory_mb: int = 2000 + cpu_threads: int = 4 + runtime_repetitions: int = 3 + runtime_warmup_runs: int = 2 + worker_timeout_seconds: int = 30 * 60 + smoke_run: bool = False + + @property + def carbon_counts(self) -> tuple[int, ...]: + return FULL_CARBON_COUNTS[:1] if self.smoke_run else FULL_CARBON_COUNTS + + @property + def worker_thread_environment(self) -> dict[str, str]: + thread_count = str(self.cpu_threads) + return {name: thread_count for name in THREAD_ENVIRONMENT_VARIABLES} + + def as_json(self) -> dict[str, Any]: + data = asdict(self) + data["source_root"] = str(self.source_root) + data["results_dir"] = str(self.results_dir) + data["carbon_counts"] = list(self.carbon_counts) + data["full_carbon_counts"] = list(FULL_CARBON_COUNTS) + data["expected_ao_counts"] = list(EXPECTED_AO_COUNTS) + data["worker_thread_environment"] = self.worker_thread_environment + return data + + +def vector_add(left: Vector, right: Vector) -> Vector: + return tuple(a + b for a, b in zip(left, right, strict=True)) # type: ignore[return-value] + + +def vector_subtract(left: Vector, right: Vector) -> Vector: + return tuple(a - b for a, b in zip(left, right, strict=True)) # type: ignore[return-value] + + +def vector_scale(scale: float, vector: Vector) -> Vector: + return tuple(scale * value for value in vector) # type: ignore[return-value] + + +def vector_dot(left: Vector, right: Vector) -> float: + return sum(a * b for a, b in zip(left, right, strict=True)) + + +def vector_cross(left: Vector, right: Vector) -> Vector: + return ( + left[1] * right[2] - left[2] * right[1], + left[2] * right[0] - left[0] * right[2], + left[0] * right[1] - left[1] * right[0], + ) + + +def vector_normalize(vector: Vector) -> Vector: + norm = math.sqrt(vector_dot(vector, vector)) + if norm == 0.0: + raise ValueError("Cannot normalize a zero vector") + return vector_scale(1.0 / norm, vector) + + +def carbon_backbone(carbon_count: int) -> tuple[Vector, ...]: + if carbon_count < 2: + raise ValueError("The benchmark requires at least two carbon atoms") + half_turn = math.radians((180.0 - CARBON_BOND_ANGLE_DEGREES) / 2.0) + positions: list[Vector] = [(0.0, 0.0, 0.0)] + for bond_index in range(carbon_count - 1): + angle = half_turn if bond_index % 2 == 0 else -half_turn + direction = (math.cos(angle), math.sin(angle), 0.0) + positions.append( + vector_add( + positions[-1], vector_scale(CARBON_CARBON_BOND_ANGSTROM, direction) + ) + ) + + center = tuple( + sum(position[axis] for position in positions) / carbon_count + for axis in range(3) + ) + return tuple(vector_subtract(position, center) for position in positions) # type: ignore[arg-type] + + +def terminal_hydrogen_directions( + carbon: Vector, neighbor: Vector +) -> tuple[Vector, ...]: + neighbor_direction = vector_normalize(vector_subtract(neighbor, carbon)) + perpendicular = (0.0, 0.0, 1.0) + second_perpendicular = vector_normalize( + vector_cross(neighbor_direction, perpendicular) + ) + radial_scale = math.sqrt(8.0 / 9.0) + directions = [] + for index in range(3): + phase = 2.0 * math.pi * index / 3.0 + radial = vector_add( + vector_scale(math.cos(phase), perpendicular), + vector_scale(math.sin(phase), second_perpendicular), + ) + directions.append( + vector_add( + vector_scale(-1.0 / 3.0, neighbor_direction), + vector_scale(radial_scale, radial), + ) + ) + return tuple(directions) + + +def internal_hydrogen_directions( + carbon: Vector, previous_carbon: Vector, next_carbon: Vector +) -> tuple[Vector, Vector]: + previous_direction = vector_normalize(vector_subtract(previous_carbon, carbon)) + next_direction = vector_normalize(vector_subtract(next_carbon, carbon)) + neighbor_dot = vector_dot(previous_direction, next_direction) + in_plane_scale = (-1.0 / 3.0) / (1.0 + neighbor_dot) + in_plane = vector_scale( + in_plane_scale, vector_add(previous_direction, next_direction) + ) + normal = vector_normalize(vector_cross(previous_direction, next_direction)) + normal_scale = math.sqrt(max(0.0, 1.0 - vector_dot(in_plane, in_plane))) + return ( + vector_add(in_plane, vector_scale(normal_scale, normal)), + vector_subtract(in_plane, vector_scale(normal_scale, normal)), + ) + + +def generate_alkane_atoms(carbon_count: int) -> tuple[Atom, ...]: + carbons = carbon_backbone(carbon_count) + atoms: list[Atom] = [("C", *position) for position in carbons] + for index, carbon in enumerate(carbons): + if index == 0: + directions = terminal_hydrogen_directions(carbon, carbons[1]) + elif index == carbon_count - 1: + directions = terminal_hydrogen_directions(carbon, carbons[-2]) + else: + directions = internal_hydrogen_directions( + carbon, carbons[index - 1], carbons[index + 1] + ) + atoms.extend( + ( + "H", + *vector_add( + carbon, vector_scale(CARBON_HYDROGEN_BOND_ANGSTROM, direction) + ), + ) + for direction in directions + ) + return tuple(atoms) + + +def atoms_to_pyscf(atoms: tuple[Atom, ...]) -> str: + return "\n".join( + f"{element} {x:.{COORDINATE_PRECISION}f} " + f"{y:.{COORDINATE_PRECISION}f} {z:.{COORDINATE_PRECISION}f}" + for element, x, y, z in atoms + ) + + +@dataclass(frozen=True) +class MoleculeSpec: + carbon_count: int + expected_aos: int + formula: str + atoms: tuple[Atom, ...] + + @property + def atom_text(self) -> str: + return atoms_to_pyscf(self.atoms) + + @property + def coordinate_sha256(self) -> str: + return hashlib.sha256(self.atom_text.encode()).hexdigest() + + def as_json(self) -> dict[str, Any]: + return { + "carbon_count": self.carbon_count, + "expected_aos": self.expected_aos, + "formula": self.formula, + "atoms": [ + {"element": element, "xyz_angstrom": [x, y, z]} + for element, x, y, z in self.atoms + ], + "coordinate_sha256": self.coordinate_sha256, + } + + +def make_molecule_spec(carbon_count: int) -> MoleculeSpec: + hydrogen_count = 2 * carbon_count + 2 + return MoleculeSpec( + carbon_count=carbon_count, + expected_aos=EXPECTED_AOS_BY_CARBON[carbon_count], + formula=f"C{carbon_count}H{hydrogen_count}", + atoms=generate_alkane_atoms(carbon_count), + ) + + +FULL_MOLECULE_LADDER = tuple(make_molecule_spec(count) for count in FULL_CARBON_COUNTS) + + +def package_version(distribution: str) -> str | None: + try: + return importlib.metadata.version(distribution) + except importlib.metadata.PackageNotFoundError: + return None + + +def verify_skala_import(source_root: Path) -> str: + import skala + + imported_path = Path(skala.__file__).resolve() + expected_root = (source_root / "src").resolve() + try: + imported_path.relative_to(expected_root) + except ValueError as error: + raise RuntimeError( + f"Imported Skala from {imported_path}, expected a module below {expected_root}" + ) from error + return str(imported_path) + + +def module_path(module: Any) -> Path: + module_file = getattr(module, "__file__", None) + if module_file is None: + raise RuntimeError(f"Module {module.__name__} has no filesystem path") + return Path(module_file).resolve() + + +def collect_environment(payload: dict[str, Any]) -> dict[str, Any]: + import torch + + import pyscf + + source_root = Path(payload["source_root"]).resolve() + imported_skala = verify_skala_import(source_root) + cuda_available = torch.cuda.is_available() + gpu_name = torch.cuda.get_device_name(0) if cuda_available else None + cupy_version = package_version("cupy-cuda12x") or package_version("cupy") + torch_cuda_version = getattr(getattr(torch, "version", None), "cuda", None) + try: + skalaxc = importlib.import_module("skalaxc") + + skalaxc_environment: dict[str, Any] = { + "available": True, + "module": str(module_path(skalaxc)), + "version": skalaxc.__version__, + "native_version": skalaxc.native_version(), + "cuda_enabled": bool(skalaxc.CUDA_ENABLED), + "cuda_toolkit_version": skalaxc.CUDA_TOOLKIT_VERSION, + "mpi_enabled": bool(skalaxc.MPI_ENABLED), + "openmp_enabled": bool(skalaxc.OPENMP_ENABLED), + "hdf5_enabled": getattr(skalaxc, "HDF5_ENABLED", None), + "model_dir": str(Path(skalaxc.MODEL_DIR).resolve()), + } + except (ImportError, OSError) as error: + skalaxc_environment = { + "available": False, + "error": f"{type(error).__name__}: {error}", + } + return { + "python": sys.version, + "python_executable": sys.executable, + "platform": platform.platform(), + "hostname": socket.gethostname(), + "processor": platform.processor(), + "logical_cpu_count": os.cpu_count(), + "source_root": str(source_root), + "imported_skala": imported_skala, + "packages": { + "skala": package_version("skala"), + "pyscf": pyscf.__version__, + "gpu4pyscf": package_version("gpu4pyscf-cuda12x") + or package_version("gpu4pyscf"), + "torch": torch.__version__, + "cupy": cupy_version, + "memray": package_version("memray"), + "skalaxc": package_version("skalaxc"), + }, + "skalaxc": skalaxc_environment, + "cuda": { + "available": cuda_available, + "torch_cuda_version": torch_cuda_version, + "device_name": gpu_name, + "device_count": torch.cuda.device_count() if cuda_available else 0, + }, + "thread_environment": { + name: os.environ.get(name) for name in THREAD_ENVIRONMENT_VARIABLES + }, + } + + +def find_route_controller(numint: Any) -> tuple[Any, Any, Any]: + candidates = (numint, getattr(numint, "integrator", None)) + control_symbols = {"_should_screen_aos", "_functional_supports_atom_chunking"} + for candidate in candidates: + if candidate is None: + continue + route_callable = inspect.unwrap(type(candidate).__call__) + referenced_names = set(route_callable.__code__.co_names) + if referenced_names & control_symbols: + route_module = inspect.getmodule(route_callable) + if route_module is None: + raise RuntimeError( + f"Cannot identify the module defining {route_callable.__qualname__}" + ) + return candidate, route_callable, route_module + raise RuntimeError("Cannot find the Skala route-selection implementation") + + +def force_dense_route(numint: Any) -> AbstractContextManager[Any]: + route_owner, route_callable, route_module = find_route_controller(numint) + referenced_names = set(route_callable.__code__.co_names) + if "_should_screen_aos" in referenced_names and hasattr( + route_module, "_should_screen_aos" + ): + return patch.object(route_module, "_should_screen_aos", return_value=False) + if "_functional_supports_atom_chunking" in referenced_names and hasattr( + type(route_owner), "_functional_supports_atom_chunking" + ): + return patch.object( + type(route_owner), + "_functional_supports_atom_chunking", + return_value=False, + ) + raise RuntimeError( + "Cannot force dense evaluation through " + f"{route_module.__name__}.{route_callable.__qualname__}" + ) + + +def route_metadata(numint: Any, mol: Any, forced_dense: bool) -> dict[str, Any]: + from pyscf.dft import numint as pyscf_numint + + route_owner, route_callable, route_module = find_route_controller(numint) + routing_source = inspect.getsource(route_callable) + routing_sha256 = hashlib.sha256(routing_source.encode()).hexdigest() + referenced_names = set(route_callable.__code__.co_names) + if "_should_screen_aos" in referenced_names and hasattr( + route_module, "_should_screen_aos" + ): + route_decision_callable = route_module._should_screen_aos + route_decision = bool(route_decision_callable(mol)) + supports_screened_evaluation = bool( + route_owner.feature_spec.supports_spatial_decomposition + ) + route_selector = "ao_threshold" + elif "_functional_supports_atom_chunking" in referenced_names and hasattr( + type(route_owner), "_functional_supports_atom_chunking" + ): + route_decision_callable = route_owner._functional_supports_atom_chunking + route_decision = bool(route_decision_callable()) + supports_screened_evaluation = route_decision + route_selector = "functional_capability" + else: + raise RuntimeError("Unrecognized Skala route-selection API") + route_decision_source = inspect.getsource(route_decision_callable) + if "_should_screen_aos" in routing_source and ( + "_global_screened_features" in routing_source + or "_integrate_screened" in routing_source + ): + implementation = "threshold_gated_global_ao_screening" + elif "chunked_features" in routing_source: + implementation = "legacy_atom_chunking" + else: + implementation = "unclassified" + + switch_size = int(pyscf_numint.SWITCH_SIZE) + if forced_dense or not supports_screened_evaluation: + selected_route = "dense" + elif implementation == "threshold_gated_global_ao_screening": + selected_route = "global_ao_screening" if route_decision else "dense" + elif implementation == "legacy_atom_chunking": + selected_route = "atom_chunking" + else: + selected_route = "unknown" + return { + "request": "forced_dense" if forced_dense else "natural", + "implementation": implementation, + "implementation_sha256": routing_sha256, + "implementation_target": ( + f"{route_module.__name__}.{route_callable.__qualname__}" + ), + "route_selector": route_selector, + "route_decision": { + "target": ( + f"{route_decision_callable.__module__}." + f"{route_decision_callable.__qualname__}" + ), + "source": route_decision_source, + "source_sha256": hashlib.sha256(route_decision_source.encode()).hexdigest(), + "result": route_decision, + }, + "functional_supports_screened_evaluation": supports_screened_evaluation, + "pyscf_switch_size": switch_size, + "selected_route": selected_route, + } + + +def _enum_name(value: Any) -> str: + name = getattr(value, "name", None) + return str(name) if name is not None else str(value).rsplit(".", 1)[-1] + + +def _pyscf_to_skalaxc(pyscf_molecule: Any, skalaxc: Any) -> tuple[Any, Any]: + molecule = skalaxc.Molecule() + coordinates = pyscf_molecule.atom_coords(unit="Bohr") + for atomic_number, center in zip( + pyscf_molecule.atom_charges(), coordinates, strict=True + ): + molecule.append( + skalaxc.Atom( + int(atomic_number), + float(center[0]), + float(center[1]), + float(center[2]), + ) + ) + + basis = skalaxc.BasisSet() + for atom_index, (atom_label, _) in enumerate(pyscf_molecule._atom): + center = coordinates[atom_index].tolist() + for pyscf_shell in pyscf_molecule._basis[atom_label]: + angular_momentum = int(pyscf_shell[0]) + primitives = pyscf_shell[1:] + exponents = [float(primitive[0]) for primitive in primitives] + for contraction_index in range(1, len(primitives[0])): + coefficients = [ + float(primitive[contraction_index]) for primitive in primitives + ] + basis.append( + skalaxc.Shell( + angular_momentum, + not pyscf_molecule.cart and angular_momentum != 1, + exponents, + coefficients, + center, + normalize=True, + ) + ) + return molecule, basis + + +def _closed_shell_density_channels(density: Any) -> tuple[Any, Any]: + import numpy as np + + scalar_density = np.asfortranarray(density, dtype=np.float64) + if scalar_density.ndim != 2 or scalar_density.shape[0] != scalar_density.shape[1]: + raise ValueError( + f"Expected a square RKS density matrix, got {scalar_density.shape}" + ) + return scalar_density, np.zeros_like(scalar_density, order="F") + + +def _resolve_skalaxc_model(functional: str, skalaxc: Any) -> str: + if functional.lower() in {"lda", "pbe", "tpss"}: + return functional.upper() + model_path = Path(skalaxc.MODEL_DIR) / f"{functional}.fun" + if not model_path.is_file(): + raise FileNotFoundError( + f"SkalaXC model for {functional!r} was not found at {model_path}" + ) + return str(model_path) + + +def _resolve_skalaxc_grid_size(name: str, skalaxc: Any) -> Any: + normalized = name.upper().replace("-", "_") + try: + return getattr(skalaxc.AtomicGridSize, normalized) + except AttributeError as error: + choices = ("FINE", "ULTRA_FINE", "SUPER_FINE", "GM3", "GM5") + raise ValueError( + f"Unknown SkalaXC grid size {name!r}; choose from {', '.join(choices)}" + ) from error + + +def _skalaxc_diagnostics(integrator: Any) -> dict[str, Any]: + diagnostics = integrator.diagnostics() + return { + "backend": _enum_name(diagnostics.backend), + "rank": int(diagnostics.rank), + "communicator_size": int(diagnostics.communicator_size), + "device_id": int(diagnostics.device_id), + "openmp_threads": int(diagnostics.openmp_threads), + "device_memory_fraction": float(diagnostics.device_memory_fraction), + "exc_vxc_calls": int(diagnostics.exc_vxc_calls), + "model_batches": int(diagnostics.model_batches), + "domains": int(diagnostics.domains), + "tasks": int(diagnostics.tasks), + "points": int(diagnostics.points), + "local_atoms": int(diagnostics.local_atoms), + } + + +def _build_skalaxc_case(payload: dict[str, Any]) -> dict[str, Any]: + import numpy as np + import torch + from skala.pyscf.grids import SkalaGrids + + from pyscf import dft, gto, lib + + source_root = Path(payload["source_root"]).resolve() + skalaxc = importlib.import_module("skalaxc") + imported_skala = verify_skala_import(source_root) + thread_count = int(payload["cpu_threads"]) + lib.num_threads(thread_count) + torch.set_num_threads(thread_count) + + molecule_spec = payload["molecule"] + mol = gto.M( + atom=molecule_spec["atom_text"], + basis=payload["basis"], + charge=0, + spin=0, + unit="Angstrom", + cart=False, + verbose=0, + ) + initial_density = dft.RKS(mol).get_init_guess() + scalar_density, spin_density = _closed_shell_density_channels(initial_density) + + reference_grid = SkalaGrids(mol) + reference_grid.level = int(payload["grid_level"]) + reference_grid.alignment = int(payload["grid_alignment"]) + reference_grid.build(sort_grids=False) + if reference_grid.weights is None: + raise RuntimeError("PySCF reference grid construction did not produce weights") + pyscf_grid_points = int(reference_grid.weights.size) + + backend = payload["backend"] + if backend == "cpu": + execution_space = skalaxc.ExecutionSpace.HOST + + def synchronize() -> None: + return None + + elif backend == "gpu": + if not bool(skalaxc.CUDA_ENABLED): + raise RuntimeError("SkalaXC was built without CUDA support") + execution_space = skalaxc.ExecutionSpace.DEVICE + synchronize = torch.cuda.synchronize + else: + raise ValueError(f"Unknown backend: {backend}") + + molecule, basis = _pyscf_to_skalaxc(mol, skalaxc) + grid_size = _resolve_skalaxc_grid_size(payload["skalaxc_grid_size"], skalaxc) + grid = skalaxc.MolGridFactory.create_default( + molecule, + pruning_scheme=skalaxc.PruningScheme.UNPRUNED, + radial_quad=skalaxc.RadialQuad.MURA_KNOWLES, + grid_size=grid_size, + ) + + if backend == "gpu": + device_settings = skalaxc.DeviceRuntimeSettings() + if skalaxc.MPI_ENABLED: + MPI = importlib.import_module("mpi4py").MPI + + runtime = skalaxc.RuntimeEnvironment(MPI.COMM_SELF, device_settings) + else: + runtime = skalaxc.RuntimeEnvironment(device_settings) + elif skalaxc.MPI_ENABLED: + MPI = importlib.import_module("mpi4py").MPI + + runtime = skalaxc.RuntimeEnvironment(MPI.COMM_SELF) + else: + runtime = skalaxc.RuntimeEnvironment() + + load_balancer = skalaxc.LoadBalancerFactory(execution_space).get_instance( + runtime, molecule, grid, basis + ) + weights = skalaxc.MolecularWeightsFactory(execution_space).get_instance() + weights.modify_weights(load_balancer) + model = _resolve_skalaxc_model(payload["functional"], skalaxc) + integrator = skalaxc.XCIntegratorFactory(execution_space).get_instance( + skalaxc.Functional(model), load_balancer + ) + initial_diagnostics = _skalaxc_diagnostics(integrator) + skalaxc_grid_points = int(initial_diagnostics["points"]) + + def evaluate() -> tuple[Any, Any, Any]: + return cast( + tuple[Any, Any, Any], + integrator.eval_exc_vxc(scalar_density, spin_density), + ) + + def make_fingerprint(result: tuple[Any, Any, Any]) -> dict[str, float]: + xc_energy, scalar_potential, spin_potential = result + scalar_matrix = np.asarray(scalar_potential, dtype=np.float64) + spin_matrix = np.asarray(spin_potential, dtype=np.float64) + return { + "xc_energy": float(xc_energy), + "vxc_sum": float(scalar_matrix.sum()), + "vxc_trace": float(np.trace(scalar_matrix)), + "vxc_frobenius_norm": float(np.linalg.norm(scalar_matrix)), + "vxc_max_abs": float(np.max(np.abs(scalar_matrix))), + "spin_vxc_frobenius_norm": float(np.linalg.norm(spin_matrix)), + } + + point_difference = skalaxc_grid_points - pyscf_grid_points + system = { + "formula": molecule_spec["formula"], + "carbon_count": int(molecule_spec["carbon_count"]), + "electron_count": int(mol.nelectron), + "actual_aos": int(mol.nao_nr()), + "grid_points": skalaxc_grid_points, + "pyscf_grid_points": pyscf_grid_points, + "grid_point_difference": point_difference, + "grid_point_ratio": skalaxc_grid_points / pyscf_grid_points, + "grid_size": _enum_name(grid_size), + "coordinate_sha256": molecule_spec["coordinate_sha256"], + "imported_skala": imported_skala, + "imported_skalaxc": str(module_path(skalaxc)), + "skalaxc_model": model, + } + return { + "backend": backend, + "evaluate": evaluate, + "make_fingerprint": make_fingerprint, + "synchronize": synchronize, + "route": None, + "diagnostics": lambda: _skalaxc_diagnostics(integrator), + "system": system, + } + + +def build_case(payload: dict[str, Any]) -> dict[str, Any]: + implementation = payload.get("implementation", "skala") + if implementation == "skalaxc": + return _build_skalaxc_case(payload) + if implementation != "skala": + raise ValueError(f"Unknown implementation: {implementation}") + + import numpy as np + import torch + + from pyscf import dft, gto, lib + + source_root = Path(payload["source_root"]).resolve() + imported_skala = verify_skala_import(source_root) + thread_count = int(payload["cpu_threads"]) + lib.num_threads(thread_count) + torch.set_num_threads(thread_count) + + molecule = payload["molecule"] + mol = gto.M( + atom=molecule["atom_text"], + basis=payload["basis"], + charge=0, + spin=0, + unit="Angstrom", + cart=False, + verbose=0, + ) + initial_dm = dft.RKS(mol).get_init_guess() + backend = payload["backend"] + if backend == "cpu": + from skala.pyscf import SkalaKS as CpuSkalaKS + + ks = CpuSkalaKS(mol, xc=payload["functional"], with_dftd3=False) + dm = initial_dm + + def synchronize() -> None: + return None + + to_numpy = np.asarray + elif backend == "gpu": + if not torch.cuda.is_available(): + raise RuntimeError("CUDA is not available") + import cupy + from skala.gpu4pyscf import SkalaKS as GpuSkalaKS + + ks = GpuSkalaKS(mol, xc=payload["functional"], with_dftd3=False) + dm = cupy.asarray(initial_dm) + synchronize = torch.cuda.synchronize + to_numpy = cupy.asnumpy + else: + raise ValueError(f"Unknown backend: {backend}") + + ks.grids.level = int(payload["grid_level"]) + ks.grids.alignment = int(payload["grid_alignment"]) + ks.grids.build(sort_grids=False) + grid_weights = ks.grids.weights + if grid_weights is None: + raise RuntimeError("Grid construction did not produce weights") + numint = ks._numint + forced_dense = bool(payload["forced_dense"]) + route = route_metadata(numint, mol, forced_dense) + + def evaluate() -> tuple[Any, Any, Any]: + return numint.nr_rks( + mol, + ks.grids, + None, + dm, + max_memory=int(payload["max_memory_mb"]), + ) + + def make_fingerprint(result: tuple[Any, Any, Any]) -> dict[str, float]: + return fingerprint(result, to_numpy) + + system = { + "formula": molecule["formula"], + "carbon_count": int(molecule["carbon_count"]), + "electron_count": int(mol.nelectron), + "actual_aos": int(mol.nao_nr()), + "grid_points": int(cast(Any, grid_weights).size), + "coordinate_sha256": molecule["coordinate_sha256"], + "imported_skala": imported_skala, + } + return { + "mol": mol, + "grids": ks.grids, + "dm": dm, + "numint": numint, + "backend": backend, + "evaluate": evaluate, + "make_fingerprint": make_fingerprint, + "synchronize": synchronize, + "to_numpy": to_numpy, + "route": route, + "system": system, + } + + +def fingerprint(result: tuple[Any, Any, Any], to_numpy: Any) -> dict[str, float]: + import numpy as np + + electron_integral, xc_energy, vxc = result + matrix = np.asarray(to_numpy(vxc), dtype=np.float64) + return { + "electron_integral": float(electron_integral), + "xc_energy": float(xc_energy), + "vxc_sum": float(matrix.sum()), + "vxc_trace": float(np.trace(matrix)), + "vxc_frobenius_norm": float(np.linalg.norm(matrix)), + "vxc_max_abs": float(np.max(np.abs(matrix))), + } + + +def run_measurement(payload: dict[str, Any]) -> dict[str, Any]: + import torch + + if ( + payload.get("implementation", "skala") == "skalaxc" + and payload["measurement"] == "memory" + and payload["backend"] == "gpu" + ): + return { + "status": "unsupported", + "implementation": "skalaxc", + "measurement": "memory", + "mode": payload["mode"], + "error": ( + "PyTorch allocator counters do not observe native " + "SkalaXC/GauXC CUDA allocations" + ), + } + + case = build_case(payload) + dense_route_override: AbstractContextManager[Any] + if not payload["forced_dense"]: + dense_route_override = nullcontext() + else: + dense_route_override = force_dense_route(case["numint"]) + + result: tuple[Any, Any, Any] | None = None + with dense_route_override: + measurement = payload["measurement"] + if measurement == "runtime": + for _ in range(int(payload["runtime_warmup_runs"])): + case["evaluate"]() + case["synchronize"]() + started = time.perf_counter() + result = case["evaluate"]() + case["synchronize"]() + elapsed_seconds = time.perf_counter() - started + measurement_data = {"runtime_seconds": elapsed_seconds} + elif measurement == "memory" and case["backend"] == "cpu": + import memray + + with tempfile.TemporaryDirectory() as temp_dir: + profile_path = Path(temp_dir) / "allocations.bin" + with memray.Tracker(profile_path): + result = case["evaluate"]() + peak_bytes = int(memray.FileReader(profile_path).metadata.peak_memory) + measurement_data = {"incremental_peak_bytes": peak_bytes} + elif measurement == "memory" and case["backend"] == "gpu": + case["synchronize"]() + torch.cuda.empty_cache() + case["synchronize"]() + baseline_bytes = torch.cuda.memory_allocated() + torch.cuda.reset_peak_memory_stats() + result = case["evaluate"]() + case["synchronize"]() + peak_bytes = max(0, torch.cuda.max_memory_allocated() - baseline_bytes) + measurement_data = { + "incremental_peak_bytes": int(peak_bytes), + "allocator_baseline_bytes": int(baseline_bytes), + } + else: + raise ValueError(f"Unknown measurement: {measurement}") + + assert result is not None + response = { + "status": "ok", + "implementation": payload.get("implementation", "skala"), + "measurement": payload["measurement"], + "mode": payload["mode"], + "route": case["route"], + "system": case["system"], + "fingerprint": case["make_fingerprint"](result), + **measurement_data, + } + if "diagnostics" in case: + response["diagnostics"] = case["diagnostics"]() + return response + + +def classify_exception(error: Exception) -> str: + message = f"{type(error).__name__}: {error}".lower() + if ( + isinstance(error, MemoryError) + or "out of memory" in message + or "bad alloc" in message + ): + return "oom" + return "error" + + +def emit_worker_record(record: dict[str, Any]) -> None: + print(WORKER_RESULT_PREFIX + json.dumps(record, sort_keys=True), flush=True) + + +def worker_main() -> None: + payload = json.load(sys.stdin) + try: + if payload["operation"] == "environment": + emit_worker_record( + {"status": "ok", "environment": collect_environment(payload)} + ) + elif payload["operation"] == "measure": + emit_worker_record(run_measurement(payload)) + else: + raise ValueError(f"Unknown operation: {payload['operation']}") + except Exception as error: # noqa: BLE001 - serialize worker failures for the parent + emit_worker_record( + { + "status": classify_exception(error), + "error_type": type(error).__name__, + "error": str(error), + "traceback": traceback.format_exc()[-12000:], + "implementation": payload.get("implementation", "skala"), + "measurement": payload.get("measurement"), + "mode": payload.get("mode"), + } + ) + + +def utc_now() -> str: + return datetime.now(UTC).isoformat() + + +def git_output(source_root: Path, *arguments: str) -> str: + completed = subprocess.run( + ["git", "-C", str(source_root), *arguments], + check=True, + capture_output=True, + text=True, + ) + return completed.stdout.strip() + + +def source_metadata(source_root: Path) -> dict[str, Any]: + return { + "root": str(source_root), + "commit": git_output(source_root, "rev-parse", "HEAD"), + "branch": git_output(source_root, "branch", "--show-current") or None, + "dirty": bool(git_output(source_root, "status", "--porcelain")), + } + + +def runner_sha256() -> str: + return hashlib.sha256(Path(__file__).read_bytes()).hexdigest() + + +def worker_environment(config: BenchmarkConfig) -> dict[str, str]: + environment = os.environ.copy() + source_python_path = str(config.source_root / "src") + existing_python_path = environment.get("PYTHONPATH") + python_paths = [source_python_path, str(RUNNER_ROOT)] + if existing_python_path: + python_paths.append(existing_python_path) + environment["PYTHONPATH"] = os.pathsep.join(python_paths) + environment.update(config.worker_thread_environment) + return environment + + +def execute_worker(payload: dict[str, Any], config: BenchmarkConfig) -> dict[str, Any]: + try: + completed = subprocess.run( + [sys.executable, str(Path(__file__).resolve()), "--worker"], + input=json.dumps(payload), + cwd=config.source_root, + env=worker_environment(config), + capture_output=True, + text=True, + timeout=config.worker_timeout_seconds, + check=False, + ) + except subprocess.TimeoutExpired as error: + return { + "status": "timeout", + "implementation": payload.get("implementation", "skala"), + "measurement": payload.get("measurement"), + "mode": payload.get("mode"), + "error": f"Worker exceeded {config.worker_timeout_seconds} seconds", + "stdout_tail": (error.stdout or "")[-4000:], + "stderr_tail": (error.stderr or "")[-4000:], + } + + marker_lines = [ + line.removeprefix(WORKER_RESULT_PREFIX) + for line in completed.stdout.splitlines() + if line.startswith(WORKER_RESULT_PREFIX) + ] + if marker_lines: + record = cast(dict[str, Any], json.loads(marker_lines[-1])) + if record["status"] != "ok": + record["stderr_tail"] = completed.stderr[-4000:] + return record + + combined_output = f"{completed.stdout}\n{completed.stderr}".lower() + status = ( + "oom" + if completed.returncode in {-9, 137} or "out of memory" in combined_output + else "error" + ) + return { + "status": status, + "implementation": payload.get("implementation", "skala"), + "measurement": payload.get("measurement"), + "mode": payload.get("mode"), + "error": f"Worker exited with code {completed.returncode} without a result record", + "stdout_tail": completed.stdout[-4000:], + "stderr_tail": completed.stderr[-4000:], + } + + +def execute_measurement( + payload: dict[str, Any], config: BenchmarkConfig +) -> dict[str, Any]: + if payload["measurement"] != "runtime": + return execute_worker(payload, config) + + runtime_samples: list[float] = [] + first_result: dict[str, Any] | None = None + for sample_index in range(config.runtime_repetitions): + result = execute_worker(payload, config) + if result["status"] != "ok": + result["runtime_samples_seconds"] = runtime_samples + result["failed_runtime_sample_index"] = sample_index + return result + if first_result is None: + first_result = result + else: + for key in ("route", "system"): + if result.get(key) != first_result.get(key): + raise ValueError( + f"Runtime worker {key} changed between isolated samples" + ) + runtime_samples.append(float(result["runtime_seconds"])) + + assert first_result is not None + first_result.pop("runtime_seconds") + first_result["runtime_samples_seconds"] = runtime_samples + return first_result + + +def atomic_write_json(path: Path, document: dict[str, Any]) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + temporary_path: Path | None = None + try: + with tempfile.NamedTemporaryFile( + "w", + encoding="utf-8", + dir=path.parent, + prefix=f".{path.name}.", + delete=False, + ) as stream: + json.dump(document, stream, indent=2, sort_keys=True, allow_nan=False) + stream.write("\n") + stream.flush() + os.fsync(stream.fileno()) + temporary_path = Path(stream.name) + temporary_path.replace(path) + finally: + if temporary_path is not None and temporary_path.exists(): + temporary_path.unlink() + + +def worker_payload( + config: BenchmarkConfig, + molecule: MoleculeSpec, + mode: str, + measurement: str, + implementation: str = "skala", +) -> dict[str, Any]: + if implementation not in IMPLEMENTATION_MODES: + raise ValueError(f"Unknown implementation: {implementation}") + if mode not in IMPLEMENTATION_MODES[implementation]: + raise ValueError(f"Mode {mode!r} is not valid for {implementation}") + backend = "gpu" if mode.startswith("gpu") else "cpu" + return { + "operation": "measure", + "implementation": implementation, + "source_root": str(config.source_root), + "functional": config.functional, + "skalaxc_grid_size": config.skalaxc_grid_size, + "basis": config.basis, + "grid_level": config.grid_level, + "grid_alignment": config.grid_alignment, + "max_memory_mb": config.max_memory_mb, + "cpu_threads": config.cpu_threads, + "backend": backend, + "forced_dense": implementation == "skala" and mode.endswith("_dense"), + "mode": mode, + "measurement": measurement, + "runtime_warmup_runs": config.runtime_warmup_runs, + "molecule": { + "carbon_count": molecule.carbon_count, + "formula": molecule.formula, + "atom_text": molecule.atom_text, + "coordinate_sha256": molecule.coordinate_sha256, + }, + } + + +def new_result_document( + config: BenchmarkConfig, + molecules: tuple[MoleculeSpec, ...], + source: dict[str, Any], + environment: dict[str, Any], +) -> dict[str, Any]: + created_at = utc_now() + + def molecule_records(modes: tuple[str, ...]) -> dict[str, Any]: + return { + molecule.formula: { + **molecule.as_json(), + "observed": None, + "modes": {mode: {} for mode in modes}, + } + for molecule in molecules + } + + return { + "schema_version": 2, + "benchmark": "pyscf_ao_screening", + "created_at": created_at, + "updated_at": created_at, + "run_label": config.run_label, + "source": source, + "environment": environment, + "configuration": config.as_json(), + "geometry": GEOMETRY_PARAMETERS, + "worker_sha256": runner_sha256(), + "implementations": { + implementation: { + "modes": list(modes), + "molecules": molecule_records(modes), + } + for implementation, modes in IMPLEMENTATION_MODES.items() + }, + } + + +def validate_resume_document( + document: dict[str, Any], config: BenchmarkConfig, source: dict[str, Any] +) -> None: + if document.get("schema_version") != 2: + raise ValueError("Cannot resume a result file that is not schema version 2") + if document.get("worker_sha256") != runner_sha256(): + raise ValueError( + "Cannot resume results created by a different runner implementation" + ) + if document.get("source", {}).get("commit") != source["commit"]: + raise ValueError("Cannot resume results from a different Git commit") + if document.get("configuration") != config.as_json(): + raise ValueError( + "Cannot resume results created with a different benchmark configuration" + ) + + +def merge_worker_result( + molecule_record: dict[str, Any], mode: str, measurement: str, result: dict[str, Any] +) -> None: + result = dict(result) + system = result.pop("system", None) + route = result.pop("route", None) + if system is not None: + observed = molecule_record.get("observed") + if observed is not None and observed != system: + raise ValueError( + f"Worker system metadata changed for {molecule_record['formula']}" + ) + molecule_record["observed"] = system + mode_record = molecule_record["modes"][mode] + if route is not None: + existing_route = mode_record.get("route") + if existing_route is not None and existing_route != route: + raise ValueError( + f"Worker route metadata changed for {molecule_record['formula']} {mode}" + ) + mode_record["route"] = route + if measurement == "runtime" and "runtime_seconds" in result: + result["runtime_samples_seconds"] = [result.pop("runtime_seconds")] + mode_record[measurement] = result + + +def result_path(config: BenchmarkConfig, source: dict[str, Any]) -> Path: + safe_label = re.sub(r"[^A-Za-z0-9_.-]+", "-", config.run_label).strip("-.") + if not safe_label: + raise ValueError("The run label must contain a filename-safe character") + return ( + config.results_dir + / f"skala-pyscf-ao-screening-{safe_label}-{source['commit'][:12]}-v2.json" + ) + + +def run_worker_preflight(config: BenchmarkConfig) -> dict[str, Any]: + result = execute_worker( + {"operation": "environment", "source_root": str(config.source_root)}, config + ) + if result["status"] != "ok": + raise RuntimeError(f"Benchmark preflight failed: {result}") + environment = cast(dict[str, Any], result["environment"]) + imported_path = Path(environment["imported_skala"]) + imported_path.relative_to(config.source_root / "src") + required_packages = ("skala", "pyscf", "torch", "memray") + missing = [ + name for name in required_packages if not environment["packages"].get(name) + ] + if missing: + raise RuntimeError(f"Worker environment is missing packages: {missing}") + if not environment["skalaxc"]["available"]: + raise RuntimeError( + "SkalaXC is unavailable in the worker environment: " + f"{environment['skalaxc'].get('error', 'unknown import error')}" + ) + return environment + + +def _gpu_available( + implementation: str, environment: dict[str, Any] +) -> tuple[bool, str]: + if not bool(environment["cuda"]["available"]): + return False, "CUDA is not available in the worker environment" + if implementation == "skalaxc" and not bool(environment["skalaxc"]["cuda_enabled"]): + return False, "SkalaXC was built without CUDA support" + return True, "" + + +def atom_distance(left: Atom, right: Atom) -> float: + return math.dist(left[1:], right[1:]) + + +def validate_molecule_ladder(config: BenchmarkConfig) -> None: + from pyscf import gto + + assert len(FULL_MOLECULE_LADDER) == len(FULL_CARBON_COUNTS) + assert len( + {molecule.coordinate_sha256 for molecule in FULL_MOLECULE_LADDER} + ) == len(FULL_CARBON_COUNTS) + for molecule, expected_aos in zip( + FULL_MOLECULE_LADDER, EXPECTED_AO_COUNTS, strict=True + ): + carbon_count = molecule.carbon_count + hydrogen_count = 2 * carbon_count + 2 + assert molecule.formula == f"C{carbon_count}H{hydrogen_count}" + assert len(molecule.atoms) == carbon_count + hydrogen_count + assert molecule.atoms == generate_alkane_atoms(carbon_count) + + carbons = molecule.atoms[:carbon_count] + hydrogens = molecule.atoms[carbon_count:] + for left, right in pairwise(carbons): + assert math.isclose( + atom_distance(left, right), + CARBON_CARBON_BOND_ANGSTROM, + abs_tol=1e-12, + ) + for hydrogen in hydrogens: + nearest_carbon = min(atom_distance(hydrogen, carbon) for carbon in carbons) + assert math.isclose( + nearest_carbon, + CARBON_HYDROGEN_BOND_ANGSTROM, + abs_tol=1e-12, + ) + + mol = gto.M( + atom=molecule.atom_text, + basis=config.basis, + charge=0, + spin=0, + unit="Angstrom", + cart=False, + verbose=0, + ) + assert mol.nao_nr() == expected_aos == molecule.expected_aos + assert mol.nelectron % 2 == 0 + + +def run_benchmark( + config: BenchmarkConfig, + molecules: tuple[MoleculeSpec, ...], + environment: dict[str, Any], +) -> Path: + source = source_metadata(config.source_root) + output_path = result_path(config, source) + if output_path.exists(): + document = json.loads(output_path.read_text(encoding="utf-8")) + validate_resume_document(document, config, source) + else: + document = new_result_document(config, molecules, source, environment) + atomic_write_json(output_path, document) + + for implementation, modes in IMPLEMENTATION_MODES.items(): + implementation_record = document["implementations"][implementation] + for mode in modes: + backend = "gpu" if mode.startswith("gpu") else "cpu" + for measurement in MEASUREMENTS: + blocked_by: dict[str, Any] | None = None + for molecule in molecules: + molecule_record = implementation_record["molecules"][ + molecule.formula + ] + existing = molecule_record["modes"][mode].get(measurement) + if existing and existing.get("status") in TERMINAL_STATUSES: + if existing["status"] in {"oom", "timeout"}: + blocked_by = { + "formula": molecule.formula, + "status": existing["status"], + } + continue + + result: dict[str, Any] + gpu_available, gpu_error = _gpu_available( + implementation, document["environment"] + ) + if backend == "gpu" and not gpu_available: + result = { + "status": "error", + "implementation": implementation, + "mode": mode, + "measurement": measurement, + "error": gpu_error, + } + elif blocked_by is not None: + result = { + "status": "skipped_after_resource_failure", + "implementation": implementation, + "mode": mode, + "measurement": measurement, + "blocked_by": blocked_by, + } + else: + result = execute_measurement( + worker_payload( + config, + molecule, + mode, + measurement, + implementation, + ), + config, + ) + + merge_worker_result(molecule_record, mode, measurement, result) + document["updated_at"] = utc_now() + atomic_write_json(output_path, document) + if result["status"] in {"oom", "timeout"}: + blocked_by = { + "formula": molecule.formula, + "status": result["status"], + } + print( + f"{implementation:7s} {mode:9s} {measurement:7s} " + f"{molecule.formula:8s} {result['status']}" + ) + return output_path + + +def parse_arguments(argv: list[str]) -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Benchmark one Skala XC/Vxc evaluation on CPU and GPU.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument( + "--label", + required=True, + help="Result label, normally the revision name such as 'mr' or 'main'.", + ) + parser.add_argument( + "--source-root", + type=Path, + default=DEFAULT_SOURCE_ROOT, + help="Skala checkout whose src/skala package is benchmarked.", + ) + parser.add_argument( + "--results-dir", + type=Path, + default=RUNNER_ROOT / "benchmarks" / "results", + help="Directory for commit-labelled JSON output.", + ) + parser.add_argument("--functional", default="skala-1.1") + parser.add_argument( + "--skalaxc-grid-size", + type=lambda value: value.upper().replace("-", "_"), + choices=("FINE", "ULTRA_FINE", "SUPER_FINE", "GM3", "GM5"), + default="GM3", + help="SkalaXC atomic grid preset; GM3 is closest to PySCF level 1.", + ) + parser.add_argument("--basis", default="def2-qzvpp") + parser.add_argument("--grid-level", type=int, default=1) + parser.add_argument("--max-memory-mb", type=int, default=2000) + parser.add_argument("--threads", type=int, default=4) + parser.add_argument("--runtime-repetitions", type=int, default=3) + parser.add_argument("--runtime-warmup-runs", type=int, default=2) + parser.add_argument("--timeout-minutes", type=float, default=30.0) + parser.add_argument( + "--smoke", + action="store_true", + help="Run only C2H6 instead of the full 11-molecule ladder.", + ) + parser.add_argument( + "--preflight-only", + action="store_true", + help="Validate geometry, dependencies, source import, and CUDA without measurements.", + ) + return parser.parse_args(argv) + + +def config_from_arguments(arguments: argparse.Namespace) -> BenchmarkConfig: + if arguments.timeout_minutes <= 0: + raise ValueError("--timeout-minutes must be positive") + if arguments.threads <= 0: + raise ValueError("--threads must be positive") + if arguments.runtime_repetitions <= 0: + raise ValueError("--runtime-repetitions must be positive") + if arguments.runtime_warmup_runs < 0: + raise ValueError("--runtime-warmup-runs must be non-negative") + source_root = arguments.source_root.expanduser().resolve() + if not (source_root / "src" / "skala").is_dir(): + raise FileNotFoundError(f"No src/skala package below {source_root}") + return BenchmarkConfig( + source_root=source_root, + results_dir=arguments.results_dir.expanduser().resolve(), + run_label=arguments.label, + functional=arguments.functional, + skalaxc_grid_size=arguments.skalaxc_grid_size, + basis=arguments.basis, + grid_level=arguments.grid_level, + max_memory_mb=arguments.max_memory_mb, + cpu_threads=arguments.threads, + runtime_repetitions=arguments.runtime_repetitions, + runtime_warmup_runs=arguments.runtime_warmup_runs, + worker_timeout_seconds=round(arguments.timeout_minutes * 60), + smoke_run=arguments.smoke, + ) + + +def main(argv: list[str] | None = None) -> int: + arguments = parse_arguments(sys.argv[1:] if argv is None else argv) + config = config_from_arguments(arguments) + validate_molecule_ladder(config) + environment = run_worker_preflight(config) + print("Benchmark configuration:") + print(json.dumps(config.as_json(), indent=2, sort_keys=True)) + print(f"Skala import: {environment['imported_skala']}") + print(f"SkalaXC: {environment['skalaxc']}") + print(f"Python: {environment['python_executable']}") + print(f"CUDA: {environment['cuda']}") + if arguments.preflight_only: + print("Preflight passed.") + return 0 + + molecules = FULL_MOLECULE_LADDER[:1] if config.smoke_run else FULL_MOLECULE_LADDER + output_path = run_benchmark(config, molecules, environment) + print(f"Results written to {output_path}") + return 0 + + +if __name__ == "__main__": + if sys.argv[1:] == ["--worker"]: + worker_main() + else: + raise SystemExit(main()) diff --git a/benchmarks/run_pyscf_ao_screening_rotation_benchmark.py b/benchmarks/run_pyscf_ao_screening_rotation_benchmark.py new file mode 100644 index 00000000..58f33b98 --- /dev/null +++ b/benchmarks/run_pyscf_ao_screening_rotation_benchmark.py @@ -0,0 +1,462 @@ +"""Benchmark AO screening across rotations of one approximately 900-AO molecule. + +The default grid applies ``Rz(azimuth) @ Ry(polar)`` to C7H16 (879 AOs with +def2-qzvpp). Azimuth runs from 0 through 330 degrees and polar angle runs from +0 through 150 degrees, both in 30-degree steps, for 72 orientations per mode. +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import math +import re +import sys +from dataclasses import asdict, dataclass +from pathlib import Path +from typing import Any, cast + +import run_pyscf_ao_screening_benchmark as benchmark # type: ignore[import-not-found] + +SKALA_MODES = ("gpu", "cpu_dense", "cpu_screened") +SKALAXC_MODES = benchmark.SKALAXC_MODES +MODES = SKALA_MODES +IMPLEMENTATION_MODES = { + "skala": SKALA_MODES, + "skalaxc": SKALAXC_MODES, +} +MEASUREMENTS = ("runtime", "memory") +BASE_MOLECULE = benchmark.make_molecule_spec(7) + + +@dataclass(frozen=True) +class Orientation: + azimuth_degrees: int + polar_degrees: int + + @property + def key(self) -> str: + return f"azimuth_{self.azimuth_degrees:03d}_polar_{self.polar_degrees:03d}" + + def as_json(self) -> dict[str, int]: + return { + "azimuth_degrees": self.azimuth_degrees, + "polar_degrees": self.polar_degrees, + } + + +@dataclass(frozen=True) +class RotationBenchmarkConfig(benchmark.BenchmarkConfig): # type: ignore[misc] + azimuth_step_degrees: int = 30 + polar_step_degrees: int = 30 + + @property + def azimuth_angles(self) -> tuple[int, ...]: + return tuple(range(0, 360, self.azimuth_step_degrees)) + + @property + def polar_angles(self) -> tuple[int, ...]: + return tuple(range(0, 180, self.polar_step_degrees)) + + @property + def full_orientations(self) -> tuple[Orientation, ...]: + return tuple( + Orientation(azimuth, polar) + for azimuth in self.azimuth_angles + for polar in self.polar_angles + ) + + @property + def orientations(self) -> tuple[Orientation, ...]: + orientations = self.full_orientations + return orientations[:1] if self.smoke_run else orientations + + def as_json(self) -> dict[str, Any]: + data = asdict(self) + data["source_root"] = str(self.source_root) + data["results_dir"] = str(self.results_dir) + data["azimuth_angles_degrees"] = list(self.azimuth_angles) + data["polar_angles_degrees"] = list(self.polar_angles) + data["orientation_count"] = len(self.orientations) + data["full_orientation_count"] = len(self.full_orientations) + data["implementation_modes"] = { + implementation: list(modes) + for implementation, modes in IMPLEMENTATION_MODES.items() + } + data["measurements"] = list(MEASUREMENTS) + data["worker_thread_environment"] = self.worker_thread_environment + return data + + +def rotate_atoms( + atoms: tuple[benchmark.Atom, ...], orientation: Orientation +) -> tuple[benchmark.Atom, ...]: + azimuth = math.radians(orientation.azimuth_degrees) + polar = math.radians(orientation.polar_degrees) + cos_azimuth = math.cos(azimuth) + sin_azimuth = math.sin(azimuth) + cos_polar = math.cos(polar) + sin_polar = math.sin(polar) + + rotated: list[benchmark.Atom] = [] + for element, x, y, z in atoms: + polar_x = cos_polar * x + sin_polar * z + polar_z = -sin_polar * x + cos_polar * z + rotated.append( + ( + element, + cos_azimuth * polar_x - sin_azimuth * y, + sin_azimuth * polar_x + cos_azimuth * y, + polar_z, + ) + ) + return tuple(rotated) + + +def rotated_molecule(orientation: Orientation) -> benchmark.MoleculeSpec: + return benchmark.MoleculeSpec( + carbon_count=BASE_MOLECULE.carbon_count, + expected_aos=BASE_MOLECULE.expected_aos, + formula=BASE_MOLECULE.formula, + atoms=rotate_atoms(BASE_MOLECULE.atoms, orientation), + ) + + +def runner_hashes() -> dict[str, str]: + return { + "rotation_runner_sha256": hashlib.sha256( + Path(__file__).read_bytes() + ).hexdigest(), + "worker_sha256": benchmark.runner_sha256(), + } + + +def validate_rotation_grid(config: RotationBenchmarkConfig) -> None: + from pyscf import gto + + molecules = tuple(rotated_molecule(item) for item in config.full_orientations) + coordinate_hashes = {molecule.coordinate_sha256 for molecule in molecules} + if len(coordinate_hashes) != len(molecules): + raise ValueError("The rotation grid produced duplicate coordinate sets") + + for molecule in molecules: + for original, rotated in zip(BASE_MOLECULE.atoms, molecule.atoms, strict=True): + if original[0] != rotated[0] or not math.isclose( + math.dist((0.0, 0.0, 0.0), original[1:]), + math.dist((0.0, 0.0, 0.0), rotated[1:]), + abs_tol=1e-12, + ): + raise ValueError("A rotation changed the molecular geometry") + + mol = gto.M( + atom=BASE_MOLECULE.atom_text, + basis=config.basis, + charge=0, + spin=0, + unit="Angstrom", + cart=False, + verbose=0, + ) + actual_aos = int(mol.nao_nr()) + if actual_aos != BASE_MOLECULE.expected_aos: + raise ValueError( + f"Expected {BASE_MOLECULE.expected_aos} AOs for {BASE_MOLECULE.formula} " + f"with {config.basis}, got {actual_aos}" + ) + + +def run_worker_preflight(config: RotationBenchmarkConfig) -> dict[str, Any]: + return cast(dict[str, Any], benchmark.run_worker_preflight(config)) + + +def worker_payload( + config: RotationBenchmarkConfig, + molecule: benchmark.MoleculeSpec, + mode: str, + measurement: str, + implementation: str, +) -> dict[str, Any]: + shared_mode = "cpu" if mode == "cpu_screened" else mode + payload = cast( + dict[str, Any], + benchmark.worker_payload( + config, molecule, shared_mode, measurement, implementation + ), + ) + payload["mode"] = mode + return payload + + +def result_path(config: RotationBenchmarkConfig, source: dict[str, Any]) -> Path: + safe_label = re.sub(r"[^A-Za-z0-9_.-]+", "-", config.run_label).strip("-.") + if not safe_label: + raise ValueError("The run label must contain a filename-safe character") + return Path(config.results_dir) / ( + "skala-pyscf-ao-screening-rotations-" + f"{safe_label}-{source['commit'][:12]}-v2.json" + ) + + +def new_result_document( + config: RotationBenchmarkConfig, + source: dict[str, Any], + environment: dict[str, Any], +) -> dict[str, Any]: + created_at = benchmark.utc_now() + + def orientation_records(modes: tuple[str, ...]) -> dict[str, Any]: + return { + orientation.key: { + "index": index, + **orientation.as_json(), + "coordinate_sha256": rotated_molecule(orientation).coordinate_sha256, + "observed": None, + "modes": {mode: {} for mode in modes}, + } + for index, orientation in enumerate(config.orientations) + } + + return { + "schema_version": 2, + "benchmark": "pyscf_ao_screening_rotations", + "created_at": created_at, + "updated_at": created_at, + "run_label": config.run_label, + "source": source, + "environment": environment, + "configuration": config.as_json(), + "geometry": { + **benchmark.GEOMETRY_PARAMETERS, + "base_molecule": BASE_MOLECULE.as_json(), + "rotation_convention": "active Cartesian rotation Rz(azimuth) @ Ry(polar)", + }, + "runner_hashes": runner_hashes(), + "implementations": { + implementation: { + "modes": list(modes), + "orientations": orientation_records(modes), + } + for implementation, modes in IMPLEMENTATION_MODES.items() + }, + } + + +def validate_resume_document( + document: dict[str, Any], + config: RotationBenchmarkConfig, + source: dict[str, Any], +) -> None: + if document.get("schema_version") != 2: + raise ValueError("Cannot resume a result file that is not schema version 2") + if document.get("runner_hashes") != runner_hashes(): + raise ValueError( + "Cannot resume results created by different runner implementations" + ) + if document.get("source", {}).get("commit") != source["commit"]: + raise ValueError("Cannot resume results from a different Git commit") + if document.get("configuration") != config.as_json(): + raise ValueError( + "Cannot resume results created with a different benchmark configuration" + ) + + +def run_benchmark(config: RotationBenchmarkConfig, environment: dict[str, Any]) -> Path: + source = benchmark.source_metadata(config.source_root) + output_path = result_path(config, source) + if output_path.exists(): + document = json.loads(output_path.read_text(encoding="utf-8")) + validate_resume_document(document, config, source) + else: + document = new_result_document(config, source, environment) + benchmark.atomic_write_json(output_path, document) + + for implementation, modes in IMPLEMENTATION_MODES.items(): + implementation_record = document["implementations"][implementation] + for mode in modes: + backend = "gpu" if mode.startswith("gpu") else "cpu" + for measurement in MEASUREMENTS: + blocked_by: dict[str, Any] | None = None + for orientation in config.orientations: + orientation_record = implementation_record["orientations"][ + orientation.key + ] + existing = orientation_record["modes"][mode].get(measurement) + if ( + existing + and existing.get("status") in benchmark.TERMINAL_STATUSES + ): + if existing["status"] in {"oom", "timeout"}: + blocked_by = { + "orientation": orientation.key, + "status": existing["status"], + } + continue + + result: dict[str, Any] + gpu_available, gpu_error = benchmark._gpu_available( + implementation, document["environment"] + ) + if backend == "gpu" and not gpu_available: + result = { + "status": "error", + "implementation": implementation, + "mode": mode, + "measurement": measurement, + "error": gpu_error, + } + elif blocked_by is not None: + result = { + "status": "skipped_after_resource_failure", + "implementation": implementation, + "mode": mode, + "measurement": measurement, + "blocked_by": blocked_by, + } + else: + molecule = rotated_molecule(orientation) + payload = worker_payload( + config, + molecule, + mode, + measurement, + implementation, + ) + payload["orientation"] = orientation.as_json() + result = benchmark.execute_measurement(payload, config) + + benchmark.merge_worker_result( + orientation_record, mode, measurement, result + ) + document["updated_at"] = benchmark.utc_now() + benchmark.atomic_write_json(output_path, document) + if result["status"] in {"oom", "timeout"}: + blocked_by = { + "orientation": orientation.key, + "status": result["status"], + } + print( + f"{implementation:7s} {mode:12s} {measurement:7s} " + f"{orientation.key} {result['status']}" + ) + return output_path + + +def parse_arguments(argv: list[str]) -> argparse.Namespace: + parser = argparse.ArgumentParser( + description=( + "Benchmark Skala XC/Vxc evaluation for 72 rotations of one 879-AO " + "molecule on GPU, dense CPU, and screened CPU." + ), + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument( + "--label", + required=True, + help="Result label, normally the revision name such as 'mr' or 'main'.", + ) + parser.add_argument( + "--source-root", + type=Path, + default=benchmark.DEFAULT_SOURCE_ROOT, + help="Skala checkout whose src/skala package is benchmarked.", + ) + parser.add_argument( + "--results-dir", + type=Path, + default=benchmark.RUNNER_ROOT / "benchmarks" / "results", + help="Directory for commit-labelled JSON output.", + ) + parser.add_argument("--functional", default="skala-1.1") + parser.add_argument( + "--skalaxc-grid-size", + type=lambda value: value.upper().replace("-", "_"), + choices=("FINE", "ULTRA_FINE", "SUPER_FINE", "GM3", "GM5"), + default="GM3", + help="SkalaXC atomic grid preset; GM3 is closest to PySCF level 1.", + ) + parser.add_argument("--basis", default="def2-qzvpp") + parser.add_argument("--grid-level", type=int, default=1) + parser.add_argument("--max-memory-mb", type=int, default=2000) + parser.add_argument("--threads", type=int, default=4) + parser.add_argument("--runtime-repetitions", type=int, default=3) + parser.add_argument("--runtime-warmup-runs", type=int, default=2) + parser.add_argument("--timeout-minutes", type=float, default=30.0) + parser.add_argument("--azimuth-step-degrees", type=int, default=30) + parser.add_argument("--polar-step-degrees", type=int, default=30) + parser.add_argument( + "--smoke", + action="store_true", + help="Run only the unrotated orientation for each mode.", + ) + parser.add_argument( + "--preflight-only", + action="store_true", + help="Validate rotations, dependencies, source import, and CUDA without measurements.", + ) + return parser.parse_args(argv) + + +def config_from_arguments( + arguments: argparse.Namespace, +) -> RotationBenchmarkConfig: + if arguments.timeout_minutes <= 0: + raise ValueError("--timeout-minutes must be positive") + if arguments.threads <= 0: + raise ValueError("--threads must be positive") + if arguments.runtime_repetitions <= 0: + raise ValueError("--runtime-repetitions must be positive") + if arguments.runtime_warmup_runs < 0: + raise ValueError("--runtime-warmup-runs must be non-negative") + if arguments.azimuth_step_degrees <= 0 or 360 % arguments.azimuth_step_degrees: + raise ValueError("--azimuth-step-degrees must be a positive divisor of 360") + if arguments.polar_step_degrees <= 0 or 180 % arguments.polar_step_degrees: + raise ValueError("--polar-step-degrees must be a positive divisor of 180") + source_root = arguments.source_root.expanduser().resolve() + if not (source_root / "src" / "skala").is_dir(): + raise FileNotFoundError(f"No src/skala package below {source_root}") + return RotationBenchmarkConfig( + source_root=source_root, + results_dir=arguments.results_dir.expanduser().resolve(), + run_label=arguments.label, + functional=arguments.functional, + skalaxc_grid_size=arguments.skalaxc_grid_size, + basis=arguments.basis, + grid_level=arguments.grid_level, + max_memory_mb=arguments.max_memory_mb, + cpu_threads=arguments.threads, + runtime_repetitions=arguments.runtime_repetitions, + runtime_warmup_runs=arguments.runtime_warmup_runs, + worker_timeout_seconds=round(arguments.timeout_minutes * 60), + smoke_run=arguments.smoke, + azimuth_step_degrees=arguments.azimuth_step_degrees, + polar_step_degrees=arguments.polar_step_degrees, + ) + + +def main(argv: list[str] | None = None) -> int: + arguments = parse_arguments(sys.argv[1:] if argv is None else argv) + config = config_from_arguments(arguments) + validate_rotation_grid(config) + environment = run_worker_preflight(config) + print("Benchmark configuration:") + print(json.dumps(config.as_json(), indent=2, sort_keys=True)) + print( + f"Molecule: {BASE_MOLECULE.formula}, " + f"{BASE_MOLECULE.expected_aos} AOs with {config.basis}" + ) + print(f"Skala import: {environment['imported_skala']}") + print(f"SkalaXC: {environment['skalaxc']}") + print(f"Python: {environment['python_executable']}") + print(f"CUDA: {environment['cuda']}") + if arguments.preflight_only: + print("Preflight passed.") + return 0 + + output_path = run_benchmark(config, environment) + print(f"Results written to {output_path}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/benchmarks/vxc_accuracy_grid_grouping.ipynb b/benchmarks/vxc_accuracy_grid_grouping.ipynb new file mode 100644 index 00000000..5acb16a2 --- /dev/null +++ b/benchmarks/vxc_accuracy_grid_grouping.ipynb @@ -0,0 +1,703 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "201eea25", + "metadata": {}, + "source": [ + "# GPU $V_{xc}$ accuracy versus grid grouping\n", + "\n", + "This notebook uses the carbon-chain/def2-QZVPP stress case from `test_gpu_screened_skala_matches_cpu_on_carbon_chain` to isolate how grouping grid points into GPU4PySCF screening blocks affects Skala's integrated $V_{xc}$.\n", + "\n", + "Grid levels 1 and 2 are evaluated independently, each against a dense CPU calculation on the identical grid and density matrix. Every screened candidate executes GPU4PySCF's real CUDA mask construction and AO evaluation with its installed $10^{-10}$ AO threshold and 4096-point block size." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3ce0ae4d", + "metadata": {}, + "outputs": [], + "source": [ + "from dataclasses import dataclass\n", + "from typing import Any\n", + "from unittest.mock import patch\n", + "\n", + "import cupy\n", + "import numpy as np\n", + "import torch\n", + "from skala.functional import load_functional\n", + "from skala.functional.base import ExcFunctionalBase\n", + "from skala.pyscf import features as features_module\n", + "from skala.pyscf.backend import dft_gpu\n", + "from skala.pyscf.features import _spatial_grid_permutations\n", + "from skala.pyscf.numint import SkalaNumInt\n", + "\n", + "from pyscf import dft, gto\n", + "from tests.utils import patch_ao_screening\n", + "\n", + "np.set_printoptions(precision=4, suppress=True)\n", + "\n", + "\n", + "@dataclass(frozen=True)\n", + "class Evaluation:\n", + " electron_count: float\n", + " xc_energy: float\n", + " vxc: np.ndarray\n", + " active_ao_counts: np.ndarray\n", + "\n", + "\n", + "@dataclass(frozen=True)\n", + "class GridExperiment:\n", + " level: int\n", + " coords: np.ndarrays\n", + " dense_reference: Evaluation\n", + " groupings: dict[str, list[np.ndarray]]\n", + " rows: list[dict[str, object]]\n", + "\n", + "\n", + "CARBON_CHAIN = \"\"\"\n", + "C 0.0 0.0 0.0\n", + "C 1.4 0.0 0.0\n", + "C 2.8 0.0 0.0\n", + "C 4.2 0.0 0.0\n", + "C 5.6 0.0 0.0\n", + "C 7.0 0.0 0.0\n", + "\"\"\"\n", + "GRID_LEVELS = (1, 2)\n", + "\n", + "assert torch.cuda.is_available()\n", + "assert dft_gpu is not None\n", + "mol = gto.M(atom=CARBON_CHAIN, basis=\"def2-qzvpp\", verbose=0)\n", + "dm = dft.RKS(mol).get_init_guess()\n", + "\n", + "cpu_functional = load_functional(\"skala-1.1\", device=torch.device(\"cpu\"))\n", + "gpu_functional = load_functional(\"skala-1.1\", device=torch.device(\"cuda:0\"))\n", + "assert isinstance(cpu_functional, ExcFunctionalBase)\n", + "assert isinstance(gpu_functional, ExcFunctionalBase)\n", + "cpu_numint = SkalaNumInt(cpu_functional, device=torch.device(\"cpu\"))\n", + "gpu_numint = SkalaNumInt(gpu_functional, device=torch.device(\"cuda:0\"))\n", + "GPU_BLOCK_SIZE = int(dft_gpu.numint.MIN_BLK_SIZE)\n", + "\n", + "print(f\"Atoms / AOs / shells: {mol.natm} / {mol.nao_nr()} / {mol.nbas}\")\n", + "print(f\"Grid levels: {GRID_LEVELS}\")\n", + "print(f\"GPU4PySCF AO threshold: {dft_gpu.numint.AO_THRESHOLD:.1e}\")\n", + "print(f\"GPU screening block size: {GPU_BLOCK_SIZE}\")" + ] + }, + { + "cell_type": "markdown", + "id": "c5410a08", + "metadata": {}, + "source": [ + "## Screening permutations\n", + "\n", + "Each algorithm partitions the complete grid required by that setting: level 1 has 31,080 points in 8 physical GPU groups, while level 2 has 67,248 points in 17 groups. Every group contains at most 4096 points.\n", + "\n", + "The atom-major case preserves PySCF's original grid order and divides the complete sequence into consecutive blocks. The production spatial case recursively partitions the complete coordinate set, choosing split directions from the spatial extent. The mixed cases exchange points between complete spatial groups while preserving every group size and leaving the final partial group intact. In every case, each grid point appears exactly once.\n", + "\n", + "Even spatially grouped 4096-point blocks can overlap many functions in a diffuse def2-QZVPP basis. `Active AO fraction` reports the grid-point-weighted fraction of AOs retained by the actual masks. `DM matmul proxy` weights the squared fraction, matching the leading $n_{\\mathrm{active}}^2 n_{\\mathrm{grid}}$ scaling of Skala's density-feature matrix multiplications; neither column is a measured runtime." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fa8cb8c1", + "metadata": {}, + "outputs": [], + "source": [ + "def validate_partition(groups: list[np.ndarray], point_count: int) -> None:\n", + " if not groups or any(group.size == 0 for group in groups):\n", + " raise ValueError(\"Groups must be non-empty.\")\n", + " flattened = np.concatenate(groups)\n", + " if flattened.size != point_count or not np.array_equal(\n", + " np.sort(flattened), np.arange(point_count, dtype=np.int64)\n", + " ):\n", + " raise ValueError(\"Groups must partition every grid point exactly once.\")\n", + "\n", + "\n", + "def groups_from_permutation(\n", + " permutation: np.ndarray, block_size: int\n", + ") -> list[np.ndarray]:\n", + " groups = [\n", + " permutation[start : start + block_size].copy()\n", + " for start in range(0, permutation.size, block_size)\n", + " ]\n", + " validate_partition(groups, permutation.size)\n", + " return groups\n", + "\n", + "\n", + "def mix_spatial_groups(\n", + " groups: list[np.ndarray], mixing_fraction: float, point_count: int\n", + ") -> list[np.ndarray]:\n", + " if not 0 <= mixing_fraction <= 1:\n", + " raise ValueError(\"mixing_fraction must be between zero and one.\")\n", + "\n", + " complete = [group for group in groups if group.size == GPU_BLOCK_SIZE]\n", + " remainders = [group.copy() for group in groups if group.size != GPU_BLOCK_SIZE]\n", + " if mixing_fraction == 0 or len(complete) < 2:\n", + " return [group.copy() for group in groups]\n", + "\n", + " source = np.stack(complete)\n", + " mixed = source.copy()\n", + " mixed_columns = round(mixing_fraction * GPU_BLOCK_SIZE)\n", + " columns = np.floor(\n", + " np.arange(mixed_columns) * GPU_BLOCK_SIZE / mixed_columns\n", + " ).astype(np.int64)\n", + " for column_index, column in enumerate(columns):\n", + " shift = 1 + column_index % (source.shape[0] - 1)\n", + " mixed[:, column] = np.roll(source[:, column], shift)\n", + "\n", + " result = [row.copy() for row in mixed] + remainders\n", + " validate_partition(result, point_count)\n", + " return result\n", + "\n", + "\n", + "def build_matching_grids(level: int) -> tuple[Any, Any, np.ndarray]:\n", + " cpu_grids = dft.Grids(mol)\n", + " cpu_grids.level = level\n", + " cpu_grids.alignment = 1\n", + " cpu_grids.build(sort_grids=False)\n", + " assert cpu_grids.coords is not None and cpu_grids.weights is not None\n", + "\n", + " gpu_grids = dft_gpu.Grids(mol)\n", + " gpu_grids.level = level\n", + " gpu_grids.alignment = 1\n", + " gpu_grids.build(sort_grids=False)\n", + "\n", + " coords = np.asarray(cpu_grids.coords)\n", + " np.testing.assert_allclose(\n", + " coords, cupy.asnumpy(gpu_grids.coords), rtol=0.0, atol=0.0\n", + " )\n", + " np.testing.assert_allclose(\n", + " cpu_grids.weights,\n", + " cupy.asnumpy(gpu_grids.weights),\n", + " rtol=1e-12,\n", + " atol=1e-12,\n", + " )\n", + " return cpu_grids, gpu_grids, coords\n", + "\n", + "\n", + "def dense_cpu_reference(cpu_grids: Any) -> Evaluation:\n", + " with patch_ao_screening(False):\n", + " electron_count, xc_energy, vxc = cpu_numint.nr_rks(mol, cpu_grids, None, dm)\n", + " return Evaluation(\n", + " electron_count=float(electron_count),\n", + " xc_energy=float(xc_energy),\n", + " vxc=np.asarray(vxc),\n", + " active_ao_counts=np.asarray([mol.nao_nr()], dtype=np.int64),\n", + " )\n", + "\n", + "\n", + "def fresh_gpu_grids(template: Any) -> Any:\n", + " case_grids = dft_gpu.Grids(mol)\n", + " case_grids.level = template.level\n", + " case_grids.alignment = template.alignment\n", + " case_grids.coords = template.coords\n", + " case_grids.weights = template.weights\n", + " case_grids._non0ao_idx = None\n", + " return case_grids\n", + "\n", + "\n", + "def evaluate_gpu_permutation(\n", + " permutation: np.ndarray, gpu_grid_template: Any, point_count: int\n", + ") -> Evaluation:\n", + " inverse = np.empty_like(permutation)\n", + " inverse[permutation] = np.arange(point_count, dtype=np.int64)\n", + " case_grids = fresh_gpu_grids(gpu_grid_template)\n", + " with (\n", + " patch.object(\n", + " features_module,\n", + " \"_spatial_grid_permutations\",\n", + " return_value=(permutation, inverse),\n", + " ),\n", + " patch_ao_screening(True),\n", + " ):\n", + " electron_count, xc_energy, vxc = gpu_numint.nr_rks(\n", + " mol, case_grids, None, cupy.asarray(dm)\n", + " )\n", + "\n", + " prepared_grids, cached_forward, _ = features_module._prepare_spatially_sorted_grids(\n", + " mol, case_grids, GPU_BLOCK_SIZE, gpu=True\n", + " )\n", + " assert np.array_equal(cached_forward, permutation)\n", + " active_ao_counts = np.asarray(\n", + " [entry[1].size for entry in prepared_grids.get_non0ao_idx()],\n", + " dtype=np.int64,\n", + " )\n", + " return Evaluation(\n", + " electron_count=float(electron_count),\n", + " xc_energy=float(xc_energy),\n", + " vxc=cupy.asnumpy(vxc),\n", + " active_ao_counts=active_ao_counts,\n", + " )\n", + "\n", + "\n", + "def vxc_errors(\n", + " candidate: np.ndarray, dense_reference: Evaluation\n", + ") -> tuple[float, float]:\n", + " difference = candidate - dense_reference.vxc\n", + " return (\n", + " float(np.max(np.abs(difference))),\n", + " float(np.linalg.norm(difference) / np.linalg.norm(dense_reference.vxc)),\n", + " )\n", + "\n", + "\n", + "def normalized_within_group_radius(\n", + " groups: list[np.ndarray], coords: np.ndarray\n", + ") -> float:\n", + " global_center = coords.mean(axis=0)\n", + " global_rms = np.sqrt(np.mean(np.sum(np.square(coords - global_center), axis=1)))\n", + " within_sum = 0.0\n", + " for group in groups:\n", + " group_coords = coords[group]\n", + " center = group_coords.mean(axis=0)\n", + " within_sum += float(np.sum(np.square(group_coords - center)))\n", + " return float(np.sqrt(within_sum / coords.shape[0]) / global_rms)\n", + "\n", + "\n", + "def mean_maximum_bbox_iou(groups: list[np.ndarray], coords: np.ndarray) -> float:\n", + " if len(groups) == 1:\n", + " return 0.0\n", + " minimums = np.asarray([coords[group].min(axis=0) for group in groups])\n", + " maximums = np.asarray([coords[group].max(axis=0) for group in groups])\n", + " volumes = np.prod(np.maximum(maximums - minimums, 0.0), axis=1)\n", + " maximum_ious = []\n", + " for index in range(len(groups)):\n", + " intersection_extent = np.maximum(\n", + " np.minimum(maximums[index], maximums)\n", + " - np.maximum(minimums[index], minimums),\n", + " 0.0,\n", + " )\n", + " intersection = np.prod(intersection_extent, axis=1)\n", + " union = volumes[index] + volumes - intersection\n", + " iou = np.divide(\n", + " intersection,\n", + " union,\n", + " out=np.zeros_like(intersection),\n", + " where=union > 0,\n", + " )\n", + " iou[index] = 0.0\n", + " maximum_ious.append(float(iou.max()))\n", + " return float(np.mean(maximum_ious))\n", + "\n", + "\n", + "def summarize_case(\n", + " level: int,\n", + " name: str,\n", + " groups: list[np.ndarray],\n", + " evaluation: Evaluation,\n", + " coords: np.ndarray,\n", + " dense_reference: Evaluation,\n", + ") -> dict[str, object]:\n", + " point_count = coords.shape[0]\n", + " validate_partition(groups, point_count)\n", + " sizes = np.asarray([group.size for group in groups], dtype=np.int64)\n", + " active_aos = evaluation.active_ao_counts\n", + " assert sizes.size == active_aos.size\n", + " maximum_error, relative_error = vxc_errors(evaluation.vxc, dense_reference)\n", + " return {\n", + " \"level\": level,\n", + " \"grid_points\": point_count,\n", + " \"case\": name,\n", + " \"groups\": len(groups),\n", + " \"occupancy\": f\"{sizes.min()}/{np.median(sizes):.0f}/{sizes.max()}\",\n", + " \"active_aos\": f\"{active_aos.min()}/{np.median(active_aos):.0f}/{active_aos.max()}\",\n", + " \"radius\": normalized_within_group_radius(groups, coords),\n", + " \"bbox_iou\": mean_maximum_bbox_iou(groups, coords),\n", + " \"active_ao_fraction\": float(\n", + " np.sum(sizes * active_aos) / (point_count * mol.nao_nr())\n", + " ),\n", + " \"dm_matmul_proxy\": float(\n", + " np.sum(sizes * np.square(active_aos)) / (point_count * mol.nao_nr() ** 2)\n", + " ),\n", + " \"max_vxc_error\": maximum_error,\n", + " \"relative_vxc_error\": relative_error,\n", + " \"electron_error\": abs(\n", + " evaluation.electron_count - dense_reference.electron_count\n", + " ),\n", + " \"energy_error\": abs(evaluation.xc_energy - dense_reference.xc_energy),\n", + " }\n", + "\n", + "\n", + "def build_groupings(coords: np.ndarray) -> dict[str, list[np.ndarray]]:\n", + " point_count = coords.shape[0]\n", + " atom_major = groups_from_permutation(\n", + " np.arange(point_count, dtype=np.int64), GPU_BLOCK_SIZE\n", + " )\n", + " spatial_forward, _ = _spatial_grid_permutations(coords, GPU_BLOCK_SIZE)\n", + " spatial = groups_from_permutation(spatial_forward, GPU_BLOCK_SIZE)\n", + " return {\n", + " \"GPU4PySCF atom-major blocks\": atom_major,\n", + " \"GPU4PySCF spatial blocks\": spatial,\n", + " \"GPU4PySCF spatial, mix 0.500\": mix_spatial_groups(spatial, 0.5, point_count),\n", + " \"GPU4PySCF spatial, mix 1.000\": mix_spatial_groups(spatial, 1.0, point_count),\n", + " }\n", + "\n", + "\n", + "def run_grid_level(level: int) -> GridExperiment:\n", + " cpu_grids, gpu_grid_template, coords = build_matching_grids(level)\n", + " point_count = coords.shape[0]\n", + " dense_reference = dense_cpu_reference(cpu_grids)\n", + " groupings = build_groupings(coords)\n", + " all_points = [np.arange(point_count, dtype=np.int64)]\n", + " case_data = [(\"Dense CPU reference\", all_points, dense_reference)]\n", + "\n", + " print(f\"Level {level}: {point_count:,} points\")\n", + " for name, groups in groupings.items():\n", + " print(f\" Evaluating {name}...\")\n", + " evaluation = evaluate_gpu_permutation(\n", + " np.concatenate(groups), gpu_grid_template, point_count\n", + " )\n", + " assert np.isfinite(evaluation.electron_count)\n", + " assert np.isfinite(evaluation.xc_energy)\n", + " assert np.isfinite(evaluation.vxc).all()\n", + " assert np.allclose(evaluation.vxc, evaluation.vxc.T, rtol=1e-10, atol=1e-11)\n", + " case_data.append((name, groups, evaluation))\n", + "\n", + " rows = [\n", + " summarize_case(level, name, groups, evaluation, coords, dense_reference)\n", + " for name, groups, evaluation in case_data\n", + " ]\n", + " atom_major_error = rows[1][\"max_vxc_error\"]\n", + " spatial_error = rows[2][\"max_vxc_error\"]\n", + " assert isinstance(atom_major_error, float)\n", + " assert isinstance(spatial_error, float)\n", + " print(\n", + " \" Spatial grouping changes max |dVxc| by \"\n", + " f\"{atom_major_error / spatial_error:.2f}x.\"\n", + " )\n", + " return GridExperiment(level, coords, dense_reference, groupings, rows)\n", + "\n", + "\n", + "def render_results(rows: list[dict[str, object]]) -> str:\n", + " columns = (\n", + " (\"level\", \"Grid level\"),\n", + " (\"grid_points\", \"Grid points\"),\n", + " (\"case\", \"Case\"),\n", + " (\"groups\", \"Groups\"),\n", + " (\"occupancy\", \"Points min/med/max\"),\n", + " (\"active_aos\", \"Active AOs min/med/max\"),\n", + " (\"radius\", \"RMS radius\"),\n", + " (\"bbox_iou\", \"BBox IoU\"),\n", + " (\"active_ao_fraction\", \"Active AO fraction\"),\n", + " (\"dm_matmul_proxy\", \"DM matmul proxy\"),\n", + " (\"max_vxc_error\", \"max |dVxc|\"),\n", + " (\"relative_vxc_error\", \"rel. Frobenius\"),\n", + " (\"electron_error\", \"|dN|\"),\n", + " (\"energy_error\", \"|dExc|\"),\n", + " )\n", + " parts = [\n", + " '',\n", + " \"\",\n", + " ]\n", + " parts.extend(\n", + " f''\n", + " for _, label in columns\n", + " )\n", + " parts.append(\"\")\n", + " for row in rows:\n", + " parts.append(\"\")\n", + " for key, _ in columns:\n", + " value = row[key]\n", + " text = f\"{value:.3e}\" if isinstance(value, float) else str(value)\n", + " parts.append(\n", + " f''\n", + " )\n", + " parts.append(\"\")\n", + " parts.append(\"
{label}
{text}
\")\n", + " return \"\".join(parts)\n", + "\n", + "\n", + "class HTMLTable(str):\n", + " def _repr_html_(self) -> str:\n", + " return str(self)" + ] + }, + { + "cell_type": "markdown", + "id": "9aa97ac5", + "metadata": {}, + "source": [ + "## Results\n", + "\n", + "Each grid level has its own dense CPU reference and independently constructed grouping permutations. All screened rows use actual GPU4PySCF masks. Lower active-AO metrics mean more aggressive screening; lower error means closer agreement with that level's dense reference." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9ab116d3", + "metadata": {}, + "outputs": [], + "source": [ + "experiments = [run_grid_level(level) for level in GRID_LEVELS]\n", + "results = [row for experiment in experiments for row in experiment.rows]\n", + "\n", + "HTMLTable(render_results(results))" + ] + }, + { + "cell_type": "markdown", + "id": "4e6acd97", + "metadata": {}, + "source": [ + "## Fixed grid-group slices\n", + "\n", + "For each grid level, the figure shows three fixed slabs centered at $z=-1$, $0$, and $+1$ bohr relative to the molecular $x$-$y$ plane. Each slab includes points satisfying $|z-z_0|\\leq 0.25$ bohr. The outermost 1% of each grid, ranked by three-dimensional distance to the nearest carbon nucleus, is omitted to keep the molecular region legible.\n", + "\n", + "The selected points are accumulated in shared $x$-$y$ bins. Each occupied bin takes the color of its most frequent screening group; the color is blended toward white according to that group's fraction of points in the bin. Pure color means complete local agreement, while a pale bin contains a stronger mixture of groups. Black crosses mark the carbon nuclei." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e47fa055", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "from matplotlib.colors import BoundaryNorm, ListedColormap\n", + "\n", + "\n", + "def group_labels(groups: list[np.ndarray], point_count: int) -> np.ndarray:\n", + " labels = np.empty(point_count, dtype=np.int64)\n", + " for group_index, group in enumerate(groups):\n", + " labels[group] = group_index\n", + " return labels\n", + "\n", + "\n", + "def dominant_group_image(\n", + " coords: np.ndarray,\n", + " labels: np.ndarray,\n", + " selected: np.ndarray,\n", + " x_edges: np.ndarray,\n", + " y_edges: np.ndarray,\n", + " group_colors: np.ndarray,\n", + ") -> np.ndarray:\n", + " x_bin_count = x_edges.size - 1\n", + " y_bin_count = y_edges.size - 1\n", + " selected_coords = coords[selected]\n", + " x_bins = np.searchsorted(x_edges, selected_coords[:, 0], side=\"right\") - 1\n", + " y_bins = np.searchsorted(y_edges, selected_coords[:, 1], side=\"right\") - 1\n", + " x_bins = np.clip(x_bins, 0, x_bin_count - 1)\n", + " y_bins = np.clip(y_bins, 0, y_bin_count - 1)\n", + " flat_bins = y_bins * x_bin_count + x_bins\n", + " combined = labels[selected] * (x_bin_count * y_bin_count) + flat_bins\n", + " counts = np.bincount(\n", + " combined,\n", + " minlength=group_colors.shape[0] * x_bin_count * y_bin_count,\n", + " ).reshape(group_colors.shape[0], y_bin_count, x_bin_count)\n", + " assert int(counts.sum()) == int(selected.sum())\n", + "\n", + " totals = counts.sum(axis=0)\n", + " dominant_groups = counts.argmax(axis=0)\n", + " dominant_counts = counts.max(axis=0)\n", + " populated = totals > 0\n", + " dominant_fraction = np.divide(\n", + " dominant_counts,\n", + " totals,\n", + " out=np.zeros_like(dominant_counts, dtype=float),\n", + " where=populated,\n", + " )\n", + "\n", + " image = np.ones((*totals.shape, 4), dtype=float)\n", + " dominant_colors = group_colors[dominant_groups]\n", + " image[populated, :3] = 1.0 - dominant_fraction[populated, None] * (\n", + " 1.0 - dominant_colors[populated]\n", + " )\n", + " return image\n", + "\n", + "\n", + "def rgb_to_lab(rgb: np.ndarray) -> np.ndarray:\n", + " linear = np.where(\n", + " rgb <= 0.04045,\n", + " rgb / 12.92,\n", + " ((rgb + 0.055) / 1.055) ** 2.4,\n", + " )\n", + " transform = np.asarray(\n", + " [\n", + " [0.4124564, 0.3575761, 0.1804375],\n", + " [0.2126729, 0.7151522, 0.0721750],\n", + " [0.0193339, 0.1191920, 0.9503041],\n", + " ]\n", + " )\n", + " xyz = linear @ transform.T\n", + " xyz /= np.asarray([0.95047, 1.0, 1.08883])\n", + " delta = 6 / 29\n", + " transformed = np.where(\n", + " xyz > delta**3,\n", + " np.cbrt(xyz),\n", + " xyz / (3 * delta**2) + 4 / 29,\n", + " )\n", + " return np.column_stack(\n", + " (\n", + " 116 * transformed[:, 1] - 16,\n", + " 500 * (transformed[:, 0] - transformed[:, 1]),\n", + " 200 * (transformed[:, 1] - transformed[:, 2]),\n", + " )\n", + " )\n", + "\n", + "\n", + "def distinct_group_colors(count: int) -> np.ndarray:\n", + " levels = np.linspace(0.0, 1.0, 11)\n", + " candidates = np.stack(\n", + " np.meshgrid(levels, levels, levels, indexing=\"ij\"), axis=-1\n", + " ).reshape(-1, 3)\n", + " candidate_lab = rgb_to_lab(candidates)\n", + " chroma = np.linalg.norm(candidate_lab[:, 1:], axis=1)\n", + " keep = (candidate_lab[:, 0] >= 35) & (candidate_lab[:, 0] <= 75) & (chroma >= 35)\n", + " candidates = candidates[keep]\n", + " candidate_lab = candidate_lab[keep]\n", + "\n", + " seed = np.argmin(np.linalg.norm(candidates - np.asarray([0.0, 0.3, 0.8]), axis=1))\n", + " selected = [int(seed)]\n", + " minimum_distance = np.linalg.norm(candidate_lab - candidate_lab[seed], axis=1)\n", + " for _ in range(1, count):\n", + " index = int(np.argmax(minimum_distance))\n", + " selected.append(index)\n", + " distance = np.linalg.norm(candidate_lab - candidate_lab[index], axis=1)\n", + " minimum_distance = np.minimum(minimum_distance, distance)\n", + " return candidates[selected]\n", + "\n", + "\n", + "atom_coords = mol.atom_coords()\n", + "slice_centers = (-1.0, 0.0, 1.0)\n", + "slice_half_width = 0.25\n", + "retained_by_level = {}\n", + "for experiment in experiments:\n", + " nearest_atom_distance = np.linalg.norm(\n", + " experiment.coords[:, None, :] - atom_coords[None, :, :], axis=2\n", + " ).min(axis=1)\n", + " removed_count = round(0.01 * experiment.coords.shape[0])\n", + " retained = np.ones(experiment.coords.shape[0], dtype=bool)\n", + " outside_order = np.argsort(nearest_atom_distance, kind=\"stable\")\n", + " retained[outside_order[-removed_count:]] = False\n", + " assert retained.sum() == experiment.coords.shape[0] - removed_count\n", + " retained_by_level[experiment.level] = retained\n", + "\n", + "trimmed_xy = np.concatenate(\n", + " [\n", + " experiment.coords[retained_by_level[experiment.level], :2]\n", + " for experiment in experiments\n", + " ]\n", + ")\n", + "x_min, y_min = trimmed_xy.min(axis=0)\n", + "x_max, y_max = trimmed_xy.max(axis=0)\n", + "x_bin_count = 120\n", + "bin_width = (x_max - x_min) / x_bin_count\n", + "y_bin_count = max(1, int(np.ceil((y_max - y_min) / bin_width)))\n", + "y_center = 0.5 * (y_min + y_max)\n", + "x_edges = np.linspace(x_min, x_max, x_bin_count + 1)\n", + "y_edges = np.linspace(\n", + " y_center - 0.5 * y_bin_count * bin_width,\n", + " y_center + 0.5 * y_bin_count * bin_width,\n", + " y_bin_count + 1,\n", + ")\n", + "\n", + "for experiment in experiments:\n", + " coords = experiment.coords\n", + " retained = retained_by_level[experiment.level]\n", + " group_count = max(len(groups) for groups in experiment.groupings.values())\n", + " group_colors = distinct_group_colors(group_count)\n", + " palette = ListedColormap(group_colors)\n", + " norm = BoundaryNorm(np.arange(group_count + 1) - 0.5, palette.N)\n", + " labels_by_name = {\n", + " name: group_labels(groups, coords.shape[0])\n", + " for name, groups in experiment.groupings.items()\n", + " }\n", + "\n", + " figure, axes = plt.subplots(\n", + " len(labels_by_name),\n", + " len(slice_centers),\n", + " figsize=(15, 13),\n", + " sharex=True,\n", + " sharey=True,\n", + " constrained_layout=True,\n", + " squeeze=False,\n", + " )\n", + " for row_index, (name, labels) in enumerate(labels_by_name.items()):\n", + " for column_index, height in enumerate(slice_centers):\n", + " axis = axes[row_index, column_index]\n", + " selected = retained & (np.abs(coords[:, 2] - height) <= slice_half_width)\n", + " image = dominant_group_image(\n", + " coords,\n", + " labels,\n", + " selected,\n", + " x_edges,\n", + " y_edges,\n", + " group_colors,\n", + " )\n", + " axis.imshow(\n", + " image,\n", + " origin=\"lower\",\n", + " extent=(x_edges[0], x_edges[-1], y_edges[0], y_edges[-1]),\n", + " interpolation=\"nearest\",\n", + " aspect=\"equal\",\n", + " )\n", + " axis.scatter(\n", + " atom_coords[:, 0],\n", + " atom_coords[:, 1],\n", + " marker=\"x\",\n", + " c=\"black\",\n", + " s=24,\n", + " linewidths=1.0,\n", + " zorder=3,\n", + " )\n", + " axis.text(\n", + " 0.98,\n", + " 0.96,\n", + " f\"{selected.sum():,} points\",\n", + " ha=\"right\",\n", + " va=\"top\",\n", + " transform=axis.transAxes,\n", + " fontsize=8,\n", + " )\n", + " if row_index == 0:\n", + " axis.set_title(\n", + " f\"z = {height:+.1f} +/- {slice_half_width:.2f} bohr\",\n", + " fontsize=10,\n", + " )\n", + " if column_index == 0:\n", + " axis.set_ylabel(f\"{name}\\ny (bohr)\", fontsize=9)\n", + " if row_index == len(labels_by_name) - 1:\n", + " axis.set_xlabel(\"x (bohr)\")\n", + "\n", + " colorbar = figure.colorbar(\n", + " plt.cm.ScalarMappable(norm=norm, cmap=palette),\n", + " ax=axes,\n", + " ticks=np.arange(group_count),\n", + " shrink=0.82,\n", + " pad=0.02,\n", + " )\n", + " colorbar.ax.set_yticklabels(np.arange(1, group_count + 1))\n", + " colorbar.set_label(\"Dominant screening group\")\n", + " figure.suptitle(\n", + " f\"Level {experiment.level}: dominant GPU screening groups \"\n", + " f\"({coords.shape[0]:,} grid points)\"\n", + " )\n", + " plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/pixi.lock b/pixi.lock index 99135867..d53cab0e 100644 --- a/pixi.lock +++ b/pixi.lock @@ -145,7 +145,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.13.0-default_he001693_1000.conda @@ -323,7 +323,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.13.0-default_he001693_1000.conda @@ -501,7 +501,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.13.0-default_he001693_1000.conda @@ -709,7 +709,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-3.8.0-hbc29df5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-3.8.0-hdbdcf42_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.82.1-h792040b_0.conda @@ -782,7 +782,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-25.0.0-py312h7900ff3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-25.0.0-py312h2054cf2_0_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.13.0-cpu_mkl_py312_h999dc20_102.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 @@ -969,7 +969,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-16.2.0-he9431aa_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-16.2.0-he9431aa_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran5-16.2.0-hc864f27_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglib-2.88.3-hb514e8e_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglib-2.88.3-hb514e8e_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-3.8.0-h2efc11c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-storage-3.8.0-h66d5b86_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgrpc-1.82.1-h05b84e8_0.conda @@ -1031,7 +1031,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-25.0.0-py312h8025657_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyarrow-core-25.0.0-py312h2cce741_0_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.12.14-h94ad73b_3_cpython.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-librt-0.15.0-py312h085bef5_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-librt-0.15.0-py312h085bef5_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pytorch-2.13.0-cpu_generic_py312_h556be23_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.3-py312ha4530ae_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/qhull-2020.2-h70be974_5.conda @@ -1287,7 +1287,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.2.0-h3cf6597_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.2.0-h07b0088_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.2.0-hdb7a957_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-h89cd0c0_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-h81decf1_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-3.8.0-ha595bda_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-3.8.0-hc8aed40_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.82.1-hf3e839d_0.conda @@ -1346,7 +1346,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-25.0.0-py312h1f38498_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-25.0.0-py312h7d9569a_0_cpu.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.14-hd05a0c4_3_cpython.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-librt-0.15.0-py312h7ac3c4f_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-librt-0.15.0-py312h7ac3c4f_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pytorch-2.13.0-cpu_generic_py312_ha1580ae_2.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 @@ -1378,6 +1378,386 @@ environments: - pypi: https://files.pythonhosted.org/packages/65/6c/eea1d03e475217aea95b1d52aee09c97575d05bbc592c39c085b71dab89f/pyarrow_stubs-20.0.0.20260819-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b0/42/c84efcc1d4caebafb1ecd8be4643f39c85c47a80fe254d92b8b43b1eadaf/h5py-3.16.0-cp312-cp312-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/e1/26/d4bc2ba3427a623f79a6c10c8f427c7a55b56eb8b3eddc369319d97f741b/scipy_stubs-1.17.1.5-py3-none-any.whl + dev: + channels: + - url: https://conda.anaconda.org/conda-forge/ + indexes: + - https://pypi.org/simple + packages: + linux-64-cuda12: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-8_kmp_llvm.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.16.1-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ast-serialize-0.9.0-py311hf77984d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.10.4-hb7a77c6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.14-h2aa3ae6_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.14.2-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.2-h720e601_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.7.1-h6ffeea8_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.11.0-h38ae05a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.27.3-h6f4d18d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.16.0-h21f4ec5_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.12.8-h46fcd08_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.7-h720e601_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.10-h720e601_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.40.1-h102d43b_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.833-hc7390e0_9.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.16.3-h206d751_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.13.3-h71f81a8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.18.0-h74b55db_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.14.0-hf596fc9_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.16.0-h1f05bef_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-h505cf86_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-h9908984_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.8-hebe6cf0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3c89d7e_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.1.1-py312h703531f_3.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.16.0-py312h8b7ec3f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-crt-tools-12.9.86-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cudart-12.9.79-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cudart-dev-12.9.79-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cudart-static-12.9.79-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cuobjdump-12.9.82-hffce074_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cupti-12.9.79-h676940d_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvcc-tools-12.9.86-he02047a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvdisasm-12.9.88-hffce074_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvrtc-12.9.86-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvrtc-dev-12.9.86-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvtx-12.9.79-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvvm-tools-12.9.86-h4bc722e_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cutensor-2.7.0.5-h2d75abc_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hac629b4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-he8c428d_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.21-py312h8285ef7_0.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/elfutils-0.194-h849f50c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-h76c4fd7_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h4db4eae_1.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_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-h7cc23a3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.3.1-h54a6638_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hb7133d2_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gmp-6.3.0-hfd2156b_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gmpy2-2.3.1-py312h04c1168_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gnutls-3.8.13-h18acefa_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/hf-xet-1.5.2-py310hb823017_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.1-py312h9be0db6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-hbc21106_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h9073bf1_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_102.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.2.0-hdb68285_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260526.0-cxx17_h0dc7533_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.9-gpl_h3152399_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-25.0.0-hcc2f0d9_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-25.0.0-h635bf11_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-compute-25.0.0-h53684a4_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-25.0.0-h635bf11_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-25.0.0-h66fbfdd_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-10_h5875eb1_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h39a168f_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-ha411449_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-h018ffa1_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcap-2.78-h084b8d7_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-10_hfef963f_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp23.1-23.1.0-default_h0acdd01_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-23.1.0-default_h7855034_1.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/libcublas-12.9.2.10-h676940d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcublas-dev-12.9.2.10-h676940d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcudnn-9.10.2.21-hf7e9902_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcudss-0.8.0.10-h58dd1b1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcufft-11.4.1.4-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcufile-1.14.1.1-hbc026e6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurand-10.3.10.19-h676940d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.22.0-ha042cf0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcusolver-11.7.5.82-h676940d_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcusparse-12.5.10.65-hecca717_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-hd45a770_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.129-h7cc23a3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h373387f_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h280c20c_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h81df57d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h5e6c136_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-3.8.0-hbc29df5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-3.8.0-hdbdcf42_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.82.1-h792040b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.13.0-default_he001693_1000.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h0cb94f2_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libidn2-2.3.8-hfac485b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-10_h5e43f62_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm23-23.1.0-h474f4eb_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmagma-2.10.0-ha7672b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmicrohttpd-1.0.10-hc2fc477_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h74cf4be_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnl-3.11.0-hb03c661_1.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/libnvjitlink-12.9.86-hecca717_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-1.27.0-h3133023_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-headers-1.27.0-ha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-25.0.0-h7376487_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h922cc85_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.6-h9d76c99_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-7.35.1-h622638d_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpsl-0.23.1-hd9e3e90_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.12.14-h0c77377_3_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libraqm-0.11.0-h6406941_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h60473fc_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.22-hebe6cf0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.4-h13e7031_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-h6154650_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.2.0-hdf11a46_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-257.13-h084b8d7_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtasn1-4.21.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.22.0-h7d032f7_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-hcc2c06a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtorch-2.13.0-cuda129_mkl_h546cc0b_302.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libudev1-257.13-h084b8d7_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libunistring-0.9.10-h7f98852_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libunwind-1.8.3-h65a8314_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.11.3-hfe17d71_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.3-hcfc3c73_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.52.1-h280c20c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.357.0-h0e34353_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-hb83e432_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.38-h280c20c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.2-h51789e4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.45-h8e12856_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-23.1.0-h7148c6a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-hee9eb32_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-hebe6cf0_1003.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.11.1-py312h6a12a82_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.11.1-py312h4c94fcb_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/memray-1.19.3-py312ha9ecdfb_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mkl-2026.1.0-hd2095e1_245.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mpc-1.4.0-ha2cb11d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mpfr-4.2.2-ha2cb11d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-2.3.1-py312h89dfda2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nccl-2.30.7.1-h4d09622_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nettle-3.10.1-h5ef0d04_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/onednn-3.12-omp_h83de36e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-heb1ab33_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.13-hbde042b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.4-h781a0a9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.20.0-py312ha5105d2_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.3.1-h443056b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/p11-kit-0.26.5-h8d769aa_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.5-py312h8ecdadd_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-h8b3dc9c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py312h38079b3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/prometheus-cpp-1.3.0-ha5d0236_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h7cc23a3_1004.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/py-spy-0.4.2-hbe0e3b8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-25.0.0-py312h7900ff3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-25.0.0-py312h2054cf2_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.2-py312hf143aa3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.13.0-cuda129_mkl_py312_h0509a7e_302.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-gpu-2.13.0-cuda129_mkl_h0d04637_302.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/pyzmq-27.2.0-py312h8a5ba0d_0.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.11.2-pl5321h9df5c37_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rdma-core-64.0-h192683f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h94463f1_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.5-h7e3ee7f_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.18.0-py312h54fa4ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sleef-3.9.0-ha0421bc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tbb-2023.0.0-hab88423_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h1df4ec4_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py312h5cc1888_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/triton-3.7.1-cuda129py312h811769c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.1.0-py312hd9148b4_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.26.0-hc1c935e_2.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.48-h280c20c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-h280c20c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-h0d788c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-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_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-h7cc23a3_4.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/xorg-xorgproto-2025.1-hebe6cf0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-hebe6cf0_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h09e67af_11.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hce19668_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.15.0-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ase-3.29.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.2-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.14-py312hd8ed1ab_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cccl_linux-64-12.9.27-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-crt-dev_linux-64-12.9.86-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-dev_linux-64-12.9.79-h3f2d84a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-static_linux-64-12.9.79-h3f2d84a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart_linux-64-12.9.79-h3f2d84a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-version-12.9-h4f385c5_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.3-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/e3nn-0.6.0-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.32.5-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.7.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-1.30.0-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.19-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.19-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/ipykernel-7.3.0-pyha191276_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.17.1-pyh53cf698_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jedi-0.20.0-pyhcf101f3_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/jupyter_client-8.10.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.9.1-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/linkify-it-py-2.2.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.2.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdit-py-plugins-0.6.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mpmath-1.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio2-1.7.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.10.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opt-einsum-3.4.0-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opt_einsum-3.4.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opt_einsum_fx-0.1.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/parso-0.8.7-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.6-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.6.2-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.53-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/py-cpuinfo-9.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.4-pyh293190f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-abi-11-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-global-3.0.4-pyh648e204_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.21.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-9.1.1-pyhc364b38_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-benchmark-5.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-7.1.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-randomly-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-timeout-2.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-discovery-1.6.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.12.14-hd8ed1ab_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-9_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-81.0.0-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/sniffio-1.3.1-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sympy-1.14.0-pyh2585a3b_106.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/textual-8.2.8-pyhcf101f3_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/tqdm-4.70.0-pyh8f84b5b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.16.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uc-micro-py-2.0.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-21.7.8-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.3-pyhcf101f3_0.conda + - pypi: ./benchmark + - pypi: ./model + - pypi: ./skala + - pypi: https://files.pythonhosted.org/packages/08/52/eefeba09be4ef2a1eb989eb92934561e8e502a6ee3c32654996e4be7e399/types_pyyaml-6.0.12.20260815-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2f/d4/c6a2b043e33f0dd012486dcebe0593585588d400175d22aad42049c88321/optype-0.17.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/40/2f/10a2e9ad81bd5c2479a255d18c4f02c734a3a21bf4b36a236ba615919592/pyscf_dispersion-1.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/4d/57/f53eb30c38aa4eeabddc3bddf283f4c10616de65d95fcd1f43900721f425/geometric-1.1.1.tar.gz + - pypi: https://files.pythonhosted.org/packages/60/c2/959caec5c46f484b5f8bb6def4b0cf7a45ba6acda26f12b75142d98cc5ae/pandas_stubs-3.0.5.260730-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/63/7c/5c2892e6bc0628a2ccf4e938e1e2db22794657ccb374672d66e20d73839e/numpy_typing_compat-20251206.2.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/63/b5/ff337f8db80e67c464846a7d10f7f6bc1039c40a2c8b57ecb90d6b0c11d9/gpu4pyscf_libxc_cuda12x-0.8.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/65/6c/eea1d03e475217aea95b1d52aee09c97575d05bbc592c39c085b71dab89f/pyarrow_stubs-20.0.0.20260819-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7c/5b/65124de2dbaf2e85109f611a41947e39acd6dd938751c04b4c4d7bf6fc82/cupy_cuda12x-14.2.0-cp312-cp312-manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/88/30/8e48717aff32f11fd78f981192d0b567971eb285ea048414b7dac2a211cf/pyscf-2.14.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/99/a7/6ed6f987f79d6442e553841ddd45439aa90a3992d8e009ccd47e46901814/gpu4pyscf_cuda12x-1.8.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/9d/e6/22df83f82f9bc26cb1c42265cf14d34d4908dba2a0f261bd7b28244acb00/cuda_pathfinder-1.8.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/e9/1a19e42cd43cc1365e127db6aae85e1c671da1d9a5d746f4d34a50edb577/h5py-3.16.0-cp312-cp312-manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/e1/26/d4bc2ba3427a623f79a6c10c8f427c7a55b56eb8b3eddc369319d97f741b/scipy_stubs-1.17.1.5-py3-none-any.whl docs: channels: - url: https://conda.anaconda.org/conda-forge/ @@ -1457,7 +1837,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-3.8.0-hbc29df5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-3.8.0-hdbdcf42_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.82.1-h792040b_0.conda @@ -1534,7 +1914,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.52-py312h5253ce2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tbb-2023.0.0-hab88423_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h1df4ec4_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py312h4c3975b_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py312h5cc1888_1.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/xorg-libice-1.1.2-h280c20c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-h0d788c3_1.conda @@ -2114,7 +2494,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-3.8.0-hbc29df5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-3.8.0-hdbdcf42_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.82.1-h792040b_0.conda @@ -2385,7 +2765,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-3.8.0-hbc29df5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-3.8.0-hdbdcf42_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.82.1-h792040b_0.conda @@ -2656,7 +3036,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-3.8.0-hbc29df5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-3.8.0-hdbdcf42_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.82.1-h792040b_0.conda @@ -2909,7 +3289,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-3.8.0-hbc29df5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-3.8.0-hdbdcf42_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.82.1-h792040b_0.conda @@ -2982,7 +3362,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-25.0.0-py312h7900ff3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-25.0.0-py312h2054cf2_0_cpu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.13.0-cpu_mkl_py312_h999dc20_102.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 @@ -3186,6 +3566,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/cutensor-2.7.0.5-h2d75abc_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-1.11.0-hfcd1e18_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/eigen-5.0.1-h19c2612_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/elfutils-0.194-h849f50c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-h76c4fd7_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h4db4eae_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py312h8a5da7c_0.conda @@ -3201,6 +3582,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/git-2.55.0-pl5321h5685339_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gmp-6.3.0-hfd2156b_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gmpy2-2.3.1-py312h04c1168_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gnutls-3.8.13-h18acefa_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx-14.4.0-hc6a0c74_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-14.4.0-hc436dd5_4.conda @@ -3217,6 +3599,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.2.0-hdb68285_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260526.0-cxx17_h0dc7533_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.9-gpl_h3152399_101.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-10_h5875eb1_mkl.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h39a168f_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-ha411449_4.conda @@ -3247,15 +3630,17 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.13.0-default_he001693_1000.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h0cb94f2_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libidn2-2.3.8-hfac485b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-10_h5e43f62_mkl.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmagma-2.10.0-ha7672b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmicrohttpd-1.0.10-hc2fc477_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h74cf4be_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnl-3.11.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda @@ -3273,9 +3658,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.2.0-hdf11a46_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-257.13-h084b8d7_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtasn1-4.21.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-hcc2c06a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtorch-2.13.0-cuda129_mkl_h546cc0b_302.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libudev1-257.13-h084b8d7_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libunistring-0.9.10-h7f98852_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libunwind-1.8.3-h65a8314_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.3-hcfc3c73_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.52.1-h280c20c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda @@ -3285,8 +3673,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-23.1.0-h7148c6a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-hee9eb32_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-hebe6cf0_1003.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-base-3.11.1-py312h4c94fcb_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/memray-1.19.3-py312ha9ecdfb_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mkl-2026.1.0-hd2095e1_245.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mpc-1.4.0-ha2cb11d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mpfr-4.2.2-ha2cb11d_1.conda @@ -3294,6 +3685,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-2.3.1-py312h89dfda2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nccl-2.30.7.1-h4d09622_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nettle-3.10.1-h5ef0d04_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ninja-1.13.2-h171cf75_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda @@ -3302,6 +3694,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/openmpi-5.0.10-hee33aa4_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.4-h781a0a9_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.20.0-py312ha5105d2_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/p11-kit-0.26.5-h8d769aa_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-h8b3dc9c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/perl-5.32.1-7_hd590300_perl5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py312h38079b3_1.conda @@ -3309,8 +3702,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/pkg-config-0.29.2-h1114479_1012.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h7cc23a3_1004.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/py-spy-0.4.2-hbe0e3b8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.13.0-cuda129_mkl_py312_h0509a7e_302.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-gpu-2.13.0-cuda129_mkl_h0d04637_302.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_1.conda @@ -3383,6 +3777,10 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-14.4.0-hd9a9cd0_104.conda - conda: https://conda.anaconda.org/conda-forge/noarch/libnvptxcompiler-dev_linux-64-12.9.86-ha770c72_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-14.4.0-ha5b54cb_104.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/linkify-it-py-2.2.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdit-py-plugins-0.6.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mpi-1.0.1-openmpi.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 @@ -3392,6 +3790,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.8-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/py-cpuinfo-9.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.4-pyh293190f_0.conda @@ -3407,17 +3806,20 @@ environments: - 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-gil-3.12.14-hd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-9_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/scikit-build-core-1.0.3-pyh04d0eab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-81.0.0-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/sniffio-1.3.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sympy-1.14.0-pyh2585a3b_106.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_9.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/textual-8.2.8-pyhcf101f3_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/tqdm-4.70.0-pyh8f84b5b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uc-micro-py-2.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.48.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - pypi: ./skala @@ -3482,6 +3884,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/cutensor-2.7.0.5-h15eaa2f_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-2.0.0-hdab9b82_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/eigen-5.0.1-h19c2612_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/elfutils-0.194-h849f50c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-h76c4fd7_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h4db4eae_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py312h8a5da7c_0.conda @@ -3497,6 +3900,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/git-2.55.0-pl5321h5685339_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gmp-6.3.0-hfd2156b_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gmpy2-2.3.1-py312h04c1168_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gnutls-3.8.13-h18acefa_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx-15.3.0-hc6a0c74_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-15.3.0-h90d9265_4.conda @@ -3513,6 +3917,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.2.0-hdb68285_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260526.0-cxx17_h0dc7533_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.9-gpl_h3152399_101.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-10_h5875eb1_mkl.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h39a168f_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-ha411449_4.conda @@ -3543,15 +3948,17 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.13.0-default_he001693_1000.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h0cb94f2_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libidn2-2.3.8-hfac485b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-10_h5e43f62_mkl.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmagma-2.10.0-hd93470c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmicrohttpd-1.0.10-hc2fc477_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h74cf4be_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnl-3.11.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda @@ -3569,9 +3976,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.2.0-hdf11a46_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-257.13-h084b8d7_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtasn1-4.21.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-hcc2c06a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtorch-2.13.0-cuda130_mkl_h1ca3d63_302.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libudev1-257.13-h084b8d7_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libunistring-0.9.10-h7f98852_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libunwind-1.8.3-h65a8314_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.3-hcfc3c73_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.52.1-h280c20c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda @@ -3581,8 +3991,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-23.1.0-h7148c6a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-hee9eb32_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-hebe6cf0_1003.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-base-3.11.1-py312h4c94fcb_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/memray-1.19.3-py312ha9ecdfb_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mkl-2026.1.0-hd2095e1_245.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mpc-1.4.0-ha2cb11d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mpfr-4.2.2-ha2cb11d_1.conda @@ -3590,6 +4003,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-2.3.1-py312h89dfda2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nccl-2.30.7.1-h1aa9b5a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nettle-3.10.1-h5ef0d04_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ninja-1.13.2-h171cf75_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda @@ -3598,6 +4012,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/openmpi-5.0.10-hee33aa4_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.4-h781a0a9_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.20.0-py312ha5105d2_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/p11-kit-0.26.5-h8d769aa_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-h8b3dc9c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/perl-5.32.1-7_hd590300_perl5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py312h38079b3_1.conda @@ -3605,8 +4020,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/pkg-config-0.29.2-h1114479_1012.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h7cc23a3_1004.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/py-spy-0.4.2-hbe0e3b8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.13.0-cuda130_mkl_py312_hcdc9d04_302.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-gpu-2.13.0-cuda129_mkl_h0d04637_302.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_1.conda @@ -3679,6 +4095,10 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-15.3.0-h2b852eb_104.conda - conda: https://conda.anaconda.org/conda-forge/noarch/libnvptxcompiler-dev_linux-64-13.3.73-ha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-15.3.0-hb2c5482_104.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/linkify-it-py-2.2.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdit-py-plugins-0.6.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mpi-1.0.1-openmpi.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 @@ -3688,6 +4108,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.8-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/py-cpuinfo-9.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.4-pyh293190f_0.conda @@ -3703,17 +4124,20 @@ environments: - 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-gil-3.12.14-hd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-9_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/scikit-build-core-1.0.3-pyh04d0eab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-81.0.0-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/sniffio-1.3.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sympy-1.14.0-pyh2585a3b_106.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_9.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/textual-8.2.8-pyhcf101f3_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/tqdm-4.70.0-pyh8f84b5b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uc-micro-py-2.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.48.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - pypi: ./skala @@ -3784,6 +4208,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/cutensor-2.7.0.5-h15eaa2f_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-2.0.0-hdab9b82_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/eigen-5.0.1-h19c2612_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/elfutils-0.194-h849f50c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-h76c4fd7_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h4db4eae_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py312h8a5da7c_0.conda @@ -3798,6 +4223,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/git-2.55.0-pl5321h5685339_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gmp-6.3.0-hfd2156b_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gmpy2-2.3.1-py312h04c1168_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gnutls-3.8.13-h18acefa_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx-15.2.0-hd47ba16_20.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-15.2.0-he8f701c_20.conda @@ -3814,6 +4240,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.2.0-hdb68285_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260526.0-cxx17_h0dc7533_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.9-gpl_h3152399_101.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-10_h5875eb1_mkl.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h39a168f_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-ha411449_4.conda @@ -3845,16 +4272,18 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.13.0-default_he001693_1000.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h0cb94f2_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libidn2-2.3.8-hfac485b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-10_h5e43f62_mkl.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm20-20.1.8-hf7376ad_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmagma-2.10.0-hd93470c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmicrohttpd-1.0.10-hc2fc477_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h74cf4be_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnl-3.11.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda @@ -3872,9 +4301,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.2.0-hdf11a46_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-257.13-h084b8d7_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtasn1-4.21.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-hcc2c06a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtorch-2.13.0-cuda130_mkl_h1ca3d63_302.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libudev1-257.13-h084b8d7_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libunistring-0.9.10-h7f98852_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libunwind-1.8.3-h65a8314_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.3-hcfc3c73_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.52.1-h280c20c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda @@ -3884,8 +4316,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-23.1.0-h7148c6a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-hee9eb32_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-hebe6cf0_1003.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-base-3.11.1-py312h4c94fcb_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/memray-1.19.3-py312ha9ecdfb_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mkl-2026.1.0-hd2095e1_245.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mpc-1.4.0-ha2cb11d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mpfr-4.2.2-ha2cb11d_1.conda @@ -3893,6 +4328,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-2.3.1-py312h89dfda2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nccl-2.30.7.1-h1aa9b5a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nettle-3.10.1-h5ef0d04_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ninja-1.13.2-h171cf75_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda @@ -3901,6 +4337,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/openmpi-5.0.10-hee33aa4_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.4-h781a0a9_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.20.0-py312ha5105d2_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/p11-kit-0.26.5-h8d769aa_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-h8b3dc9c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/perl-5.32.1-7_hd590300_perl5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py312h38079b3_1.conda @@ -3908,8 +4345,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/pkg-config-0.29.2-h1114479_1012.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h7cc23a3_1004.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/py-spy-0.4.2-hbe0e3b8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.13.0-cuda130_mkl_py312_hcdc9d04_302.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-gpu-2.13.0-cuda129_mkl_h0d04637_302.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_1.conda @@ -3983,6 +4421,10 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-15.2.0-hcc6f6b0_120.conda - conda: https://conda.anaconda.org/conda-forge/noarch/libnvptxcompiler-dev_linux-64-13.3.73-ha770c72_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-15.2.0-hd446a21_120.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/linkify-it-py-2.2.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdit-py-plugins-0.6.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mpi-1.0.1-openmpi.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 @@ -3992,6 +4434,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.8-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/py-cpuinfo-9.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.4-pyh293190f_0.conda @@ -4007,17 +4450,20 @@ environments: - 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-gil-3.12.14-hd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-9_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/scikit-build-core-1.0.3-pyh04d0eab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-81.0.0-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/sniffio-1.3.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sympy-1.14.0-pyh2585a3b_106.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_9.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/textual-8.2.8-pyhcf101f3_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/tqdm-4.70.0-pyh8f84b5b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uc-micro-py-2.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.48.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - pypi: ./skala @@ -4133,7 +4579,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/pkg-config-0.29.2-h1114479_1012.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.13.0-cpu_mkl_py312_h999dc20_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/rdma-core-64.0-h192683f_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda @@ -4293,7 +4739,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/pkg-config-0.29.2-h1114479_1012.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.13.0-cpu_mkl_py312_h999dc20_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/rdma-core-64.0-h192683f_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda @@ -4453,7 +4899,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/pkg-config-0.29.2-h1114479_1012.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.13.0-cpu_mkl_py312_h999dc20_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/rdma-core-64.0-h192683f_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda @@ -4613,7 +5059,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pkg-config-0.29.2-hfbd14cd_1012.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/psutil-7.2.2-py312h898233f_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.12.14-h94ad73b_3_cpython.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-librt-0.15.0-py312h085bef5_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-librt-0.15.0-py312h085bef5_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pytorch-2.13.0-cpu_generic_py312_h556be23_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/rdma-core-64.0-h1f0f388_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.3-ha7194a6_1.conda @@ -4830,7 +5276,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pkg-config-0.29.2-he0b766d_1012.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py312hbd136b4_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.14-hd05a0c4_3_cpython.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-librt-0.15.0-py312h7ac3c4f_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-librt-0.15.0-py312h7ac3c4f_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pytorch-2.13.0-cpu_generic_py312_ha1580ae_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h8b90a29_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rhash-1.4.6-h84a0fba_2.conda @@ -4958,7 +5404,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/pkg-config-0.29.2-h1114479_1012.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.13.0-cpu_mkl_py312_h999dc20_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/rdma-core-64.0-h192683f_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda @@ -5133,7 +5579,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/pkg-config-0.29.2-h1114479_1012.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.13.0-cpu_mkl_py312_h999dc20_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/rdma-core-64.0-h192683f_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda @@ -5308,7 +5754,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/pkg-config-0.29.2-h1114479_1012.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.13.0-cpu_mkl_py312_h999dc20_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/rdma-core-64.0-h192683f_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda @@ -5426,6 +5872,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.16.0-py312h8b7ec3f_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-2.0.0-hdab9b82_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/eigen-5.0.1-h19c2612_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/elfutils-0.194-h849f50c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-h76c4fd7_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h4db4eae_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py312h8a5da7c_0.conda @@ -5436,6 +5883,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/git-2.55.0-pl5321h5685339_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gmp-6.3.0-hfd2156b_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gmpy2-2.3.1-py312h04c1168_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gnutls-3.8.13-h18acefa_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx-15.3.0-hc6a0c74_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-15.3.0-h90d9265_4.conda @@ -5452,6 +5900,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.2.0-hdb68285_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260526.0-cxx17_h0dc7533_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.9-gpl_h3152399_101.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-10_h5875eb1_mkl.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h39a168f_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-ha411449_4.conda @@ -5469,14 +5918,16 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.13.0-default_he001693_1000.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h0cb94f2_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libidn2-2.3.8-hfac485b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-10_h5e43f62_mkl.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmicrohttpd-1.0.10-hc2fc477_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h74cf4be_1.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/libpng-1.6.58-h922cc85_1.conda @@ -5489,8 +5940,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-h6154650_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.2.0-hdf11a46_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtasn1-4.21.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-hcc2c06a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtorch-2.13.0-cpu_mkl_h40bd1c6_102.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libunistring-0.9.10-h7f98852_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libunwind-1.8.3-h65a8314_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.3-hcfc3c73_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.52.1-h280c20c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda @@ -5500,8 +5954,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-23.1.0-h7148c6a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-hee9eb32_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-hebe6cf0_1003.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-base-3.11.1-py312h4c94fcb_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/memray-1.19.3-py312ha9ecdfb_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mkl-2026.1.0-hd2095e1_245.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mpc-1.4.0-ha2cb11d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mpfr-4.2.2-ha2cb11d_1.conda @@ -5509,6 +5966,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/mpi4py-4.1.2-py312h18f78f0_100.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-2.3.1-py312h89dfda2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nettle-3.10.1-h5ef0d04_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ninja-1.13.2-h171cf75_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda @@ -5516,6 +5974,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-heb1ab33_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.4-h781a0a9_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.20.0-py312ha5105d2_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/p11-kit-0.26.5-h8d769aa_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-h8b3dc9c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/perl-5.32.1-7_hd590300_perl5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py312h38079b3_1.conda @@ -5523,8 +5982,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/pkg-config-0.29.2-h1114479_1012.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h7cc23a3_1004.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/py-spy-0.4.2-hbe0e3b8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.13.0-cpu_mkl_py312_h999dc20_102.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 @@ -5581,6 +6041,10 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-4.18.0-he073ed8_9.conda - conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-15.3.0-h2b852eb_104.conda - conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-15.3.0-hb2c5482_104.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/linkify-it-py-2.2.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdit-py-plugins-0.6.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mpmath-1.4.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda @@ -5589,6 +6053,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.8-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/py-cpuinfo-9.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.4-pyh293190f_0.conda @@ -5604,17 +6069,20 @@ environments: - 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-gil-3.12.14-hd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-9_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/scikit-build-core-1.0.3-pyh04d0eab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-81.0.0-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/sniffio-1.3.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sympy-1.14.0-pyh2585a3b_106.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_9.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/textual-8.2.8-pyhcf101f3_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/tqdm-4.70.0-pyh8f84b5b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uc-micro-py-2.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.48.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - pypi: ./skala @@ -5646,6 +6114,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.16.0-py312h8b7ec3f_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-2.0.0-hdab9b82_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/eigen-5.0.1-h19c2612_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/elfutils-0.194-h849f50c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-h76c4fd7_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h4db4eae_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py312h8a5da7c_0.conda @@ -5656,6 +6125,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/git-2.55.0-pl5321h5685339_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gmp-6.3.0-hfd2156b_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gmpy2-2.3.1-py312h04c1168_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gnutls-3.8.13-h18acefa_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx-15.3.0-hc6a0c74_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-15.3.0-h90d9265_4.conda @@ -5672,6 +6142,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.2.0-hdb68285_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260526.0-cxx17_h0dc7533_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.9-gpl_h3152399_101.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-10_h5875eb1_mkl.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h39a168f_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-ha411449_4.conda @@ -5689,14 +6160,16 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.13.0-default_he001693_1000.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h0cb94f2_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libidn2-2.3.8-hfac485b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-10_h5e43f62_mkl.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmicrohttpd-1.0.10-hc2fc477_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h74cf4be_1.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/libpng-1.6.58-h922cc85_1.conda @@ -5709,8 +6182,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-h6154650_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.2.0-hdf11a46_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtasn1-4.21.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-hcc2c06a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtorch-2.13.0-cpu_mkl_h40bd1c6_102.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libunistring-0.9.10-h7f98852_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libunwind-1.8.3-h65a8314_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.3-hcfc3c73_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.52.1-h280c20c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda @@ -5720,8 +6196,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-23.1.0-h7148c6a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-hee9eb32_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-hebe6cf0_1003.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-base-3.11.1-py312h4c94fcb_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/memray-1.19.3-py312ha9ecdfb_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mkl-2026.1.0-hd2095e1_245.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mpc-1.4.0-ha2cb11d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mpfr-4.2.2-ha2cb11d_1.conda @@ -5729,6 +6208,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/mpi4py-4.1.2-py312h18f78f0_100.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-2.3.1-py312h89dfda2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nettle-3.10.1-h5ef0d04_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ninja-1.13.2-h171cf75_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda @@ -5736,6 +6216,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-heb1ab33_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.4-h781a0a9_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.20.0-py312ha5105d2_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/p11-kit-0.26.5-h8d769aa_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-h8b3dc9c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/perl-5.32.1-7_hd590300_perl5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py312h38079b3_1.conda @@ -5743,8 +6224,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/pkg-config-0.29.2-h1114479_1012.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h7cc23a3_1004.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/py-spy-0.4.2-hbe0e3b8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.13.0-cpu_mkl_py312_h999dc20_102.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 @@ -5801,6 +6283,10 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-4.18.0-he073ed8_9.conda - conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-15.3.0-h2b852eb_104.conda - conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-15.3.0-hb2c5482_104.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/linkify-it-py-2.2.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdit-py-plugins-0.6.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mpmath-1.4.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda @@ -5809,6 +6295,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.8-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/py-cpuinfo-9.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.4-pyh293190f_0.conda @@ -5824,17 +6311,20 @@ environments: - 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-gil-3.12.14-hd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-9_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/scikit-build-core-1.0.3-pyh04d0eab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-81.0.0-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/sniffio-1.3.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sympy-1.14.0-pyh2585a3b_106.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_9.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/textual-8.2.8-pyhcf101f3_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/tqdm-4.70.0-pyh8f84b5b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uc-micro-py-2.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.48.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - pypi: ./skala @@ -5866,6 +6356,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.16.0-py312h8b7ec3f_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-2.0.0-hdab9b82_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/eigen-5.0.1-h19c2612_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/elfutils-0.194-h849f50c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fmt-12.1.0-h76c4fd7_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h4db4eae_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py312h8a5da7c_0.conda @@ -5876,6 +6367,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/git-2.55.0-pl5321h5685339_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gmp-6.3.0-hfd2156b_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gmpy2-2.3.1-py312h04c1168_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gnutls-3.8.13-h18acefa_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx-15.3.0-hc6a0c74_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-15.3.0-h90d9265_4.conda @@ -5892,6 +6384,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.2.0-hdb68285_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260526.0-cxx17_h0dc7533_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.9-gpl_h3152399_101.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-10_h5875eb1_mkl.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h39a168f_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-ha411449_4.conda @@ -5909,14 +6402,16 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.13.0-default_he001693_1000.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h0cb94f2_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libidn2-2.3.8-hfac485b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-10_h5e43f62_mkl.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmicrohttpd-1.0.10-hc2fc477_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h74cf4be_1.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/libpng-1.6.58-h922cc85_1.conda @@ -5929,8 +6424,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-h6154650_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.2.0-hdf11a46_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtasn1-4.21.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-hcc2c06a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtorch-2.13.0-cpu_mkl_h40bd1c6_102.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libunistring-0.9.10-h7f98852_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libunwind-1.8.3-h65a8314_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.3-hcfc3c73_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.52.1-h280c20c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda @@ -5940,8 +6438,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-23.1.0-h7148c6a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-hee9eb32_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-hebe6cf0_1003.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-base-3.11.1-py312h4c94fcb_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/memray-1.19.3-py312ha9ecdfb_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mkl-2026.1.0-hd2095e1_245.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mpc-1.4.0-ha2cb11d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mpfr-4.2.2-ha2cb11d_1.conda @@ -5949,6 +6450,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/mpi4py-4.1.2-py312h18f78f0_100.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-2.3.1-py312h89dfda2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nettle-3.10.1-h5ef0d04_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ninja-1.13.2-h171cf75_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda @@ -5956,6 +6458,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-heb1ab33_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.4-h781a0a9_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.20.0-py312ha5105d2_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/p11-kit-0.26.5-h8d769aa_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-h8b3dc9c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/perl-5.32.1-7_hd590300_perl5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.3.0-py312h38079b3_1.conda @@ -5963,8 +6466,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/pkg-config-0.29.2-h1114479_1012.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h7cc23a3_1004.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/py-spy-0.4.2-hbe0e3b8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.13.0-cpu_mkl_py312_h999dc20_102.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 @@ -6021,6 +6525,10 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-4.18.0-he073ed8_9.conda - conda: https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-15.3.0-h2b852eb_104.conda - conda: https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-15.3.0-hb2c5482_104.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/linkify-it-py-2.2.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdit-py-plugins-0.6.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mpmath-1.4.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda @@ -6029,6 +6537,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.2.1-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.8-pyh5ded981_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/py-cpuinfo-9.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.4-pyh293190f_0.conda @@ -6044,17 +6553,20 @@ environments: - 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-gil-3.12.14-hd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-9_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/scikit-build-core-1.0.3-pyh04d0eab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-81.0.0-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/sniffio-1.3.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sympy-1.14.0-pyh2585a3b_106.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_9.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/textual-8.2.8-pyhcf101f3_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/tqdm-4.70.0-pyh8f84b5b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.16.0-h69aa097_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.16.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uc-micro-py-2.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.48.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - pypi: ./skala @@ -6212,7 +6724,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.13.0-default_he001693_1000.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h0cb94f2_3.conda @@ -6412,7 +6924,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-3.8.0-hbc29df5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-3.8.0-hdbdcf42_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.82.1-h792040b_0.conda @@ -6645,7 +7157,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-16.2.0-he9431aa_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-16.2.0-he9431aa_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran5-16.2.0-hc864f27_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglib-2.88.3-hb514e8e_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglib-2.88.3-hb514e8e_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-3.8.0-h2efc11c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-storage-3.8.0-h66d5b86_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgrpc-1.82.1-h05b84e8_0.conda @@ -6922,7 +7434,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.2.0-h3cf6597_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.2.0-h07b0088_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.2.0-hdb7a957_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-h89cd0c0_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-h81decf1_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-3.8.0-ha595bda_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-3.8.0-hc8aed40_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.82.1-hf3e839d_0.conda @@ -7081,7 +7593,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-3.8.0-hbc29df5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-3.8.0-hdbdcf42_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.82.1-h792040b_0.conda @@ -7314,7 +7826,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-16.2.0-he9431aa_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-16.2.0-he9431aa_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran5-16.2.0-hc864f27_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglib-2.88.3-hb514e8e_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglib-2.88.3-hb514e8e_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-3.8.0-h2efc11c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgoogle-cloud-storage-3.8.0-h66d5b86_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgrpc-1.82.1-h05b84e8_0.conda @@ -7591,7 +8103,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-16.2.0-h3cf6597_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-16.2.0-h07b0088_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-16.2.0-hdb7a957_4.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-h89cd0c0_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-h81decf1_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-3.8.0-ha595bda_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-3.8.0-hc8aed40_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.82.1-hf3e839d_0.conda @@ -7714,7 +8226,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-16.2.0-h69a702a_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-16.2.0-h6b99dfc_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.4.0-h23af247_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.13.0-default_he001693_1000.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h0cb94f2_3.conda @@ -7864,6 +8376,20 @@ packages: - _openmp_mutex >=4.5 size: 8050 timestamp: 1788046427177 +- conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.16.1-h7cc23a3_1.conda + sha256: a35bddac04be093769e81814465a537961c6ed0f8d3cc23d6dce6ecdfaf71821 + md5: 7094e0d8d14de0eff6d83f0d2f1f661e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + license: LGPL-2.1-or-later + license_family: LGPL + purls: [] + run_exports: + weak: + - alsa-lib >=1.2.16.1,<1.3.0a0 + size: 594986 + timestamp: 1787763412911 - conda: https://conda.anaconda.org/conda-forge/linux-64/ast-serialize-0.9.0-py311hf77984d_0.conda noarch: python sha256: 5b77c2444c8fef0feb1c37e7050c78f7e235c62ffec9ab58b6c4a82566ef32d4 @@ -9595,6 +10121,42 @@ packages: run_exports: {} size: 6972 timestamp: 1786642183694 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hac629b4_1.conda + sha256: 7684da83306bb69686c0506fb09aa7074e1a55ade50c3a879e4e5df6eebb1009 + md5: af491aae930edc096b58466c51c4126c + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.22.2,<1.23.0a0 + - libgcc >=13 + - libntlm >=1.8,<2.0a0 + - libstdcxx >=13 + - libxcrypt >=4.4.36 + - openssl >=3.5.5,<4.0a0 + license: BSD-3-Clause-Attribution + license_family: BSD + purls: [] + run_exports: + weak: + - cyrus-sasl >=2.1.28,<3.0a0 + size: 210103 + timestamp: 1771943128249 +- conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-he8c428d_2.conda + sha256: 87a0a42d4ccc527597eff3234509dfb03e0d9bf67d5e2b6fc758e2ef389d8abe + md5: a6eb37b51be1a9a654dc3a8aca039b1a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - libstdcxx >=15 + - libexpat >=2.8.1,<3.0a0 + - libzlib >=1.3.2,<2.0a0 + - libglib >=2.88.3,<3.0a0 + license: AFL-2.1 OR GPL-2.0-or-later + purls: [] + run_exports: + weak: + - dbus >=1.16.2,<2.0a0 + size: 449564 + timestamp: 1788197922783 - conda: https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.21-py312h8285ef7_0.conda sha256: b8dbe25820064a099f315bbb8f45f5bac3fddb63e96af3cbf0c93a830733ef34 md5: e6778419a1851f6e15820558abddfa04 @@ -9611,6 +10173,21 @@ packages: run_exports: {} size: 2821960 timestamp: 1780390159181 +- 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: [] + run_exports: + weak: + - double-conversion >=3.4.0,<3.5.0a0 + size: 71809 + timestamp: 1765193127016 - conda: https://conda.anaconda.org/conda-forge/linux-64/doxygen-1.18.0-py310hb664d3d_0.conda sha256: aa8efe84767d1f628f439749df5bd1d369cad6ef1a6cc2922ffc75238e62f07a md5: 13974bf5357b9bb5ecab0a09fd06d144 @@ -10899,6 +11476,7 @@ packages: - libllvm23 >=23.1.0,<23.2.0a0 license: Apache-2.0 WITH LLVM-exception license_family: APACHE + purls: [] run_exports: weak: - libclang-cpp23.1 >=23.1.0,<23.2.0a0 @@ -10919,6 +11497,7 @@ packages: - libllvm23 >=23.1.0,<23.2.0a0 license: Apache-2.0 WITH LLVM-exception license_family: APACHE + purls: [] run_exports: weak: - libclang13 >=23.1.0 @@ -11150,6 +11729,23 @@ packages: run_exports: {} size: 1117538 timestamp: 1782772352403 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda + sha256: 205c4f19550f3647832ec44e35e6d93c8c206782bdd620c1d7cf66237580ff9c + md5: 49c553b47ff679a6a1e9fc80b9c5a2d4 + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.22.2,<1.23.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + license: Apache-2.0 + license_family: Apache + purls: [] + run_exports: + weak: + - libcups >=2.3.3,<2.4.0a0 + size: 4518030 + timestamp: 1770902209173 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurand-10.3.10.19-h676940d_1.conda sha256: 3d40daf956b220cc367a6306ede1e259446fb844051bcfed87c46539cc1aaf03 md5: 2a91559a9345bedf09af8b7903deb6e6 @@ -11271,6 +11867,21 @@ packages: - libdeflate >=1.25,<1.26.0a0 size: 73710 timestamp: 1785908694612 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.129-h7cc23a3_0.conda + sha256: ea46b0ca0fa16af1f8b329b740e6cd8b4577c5378fa8717b28a885f70668633d + md5: 64cc91512b6278c315349dc13c53f680 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - libpciaccess >=0.19,<0.20.0a0 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - libdrm >=2.4.129,<2.5.0a0 + size: 313461 + timestamp: 1786684701973 - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h373387f_1.conda sha256: 6473eb8caf2aae830f37caa93db9b26dddf7ac84b63229e8bf7fc0e5c3ab95b0 md5: 50708d3b951d0f8e2d7f2df5b5edc040 @@ -11287,6 +11898,32 @@ packages: - libedit >=3.1.20250104,<3.2.0a0 size: 135098 timestamp: 1786616658086 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_5.conda + sha256: 13d3fde9c1dcf254968cc70652222a43f25d04e69555ccf855553110e753288e + md5: 3a1b41c4591a0a1734382af3e2b8bc08 + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_5 + license: LicenseRef-libglvnd + purls: [] + run_exports: {} + size: 46694 + timestamp: 1787310030923 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_5.conda + sha256: fd2794bbcff7e692fb476c17886702702893d074071d429217bad7cfb072abfd + md5: 577800fc8c9d8b7d9727246bbfde704e + depends: + - __glibc >=2.17,<3.0.a0 + - libegl 1.7.0 ha4b6fd6_5 + - libgl-devel 1.7.0 ha4b6fd6_5 + - xorg-libx11 + license: LicenseRef-libglvnd + purls: [] + run_exports: + weak: + - libegl >=1.7.0,<2.0a0 + size: 31242 + timestamp: 1787310065866 - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h280c20c_3.conda sha256: e4418f68d01a6307c4431b585c71b584f40189877364e542ee87deb777f5e85b md5: 7bc31538d6e3fb349e897353969237ec @@ -11448,16 +12085,42 @@ packages: run_exports: {} size: 2526008 timestamp: 1787618692926 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_2.conda - sha256: af6c14f387f810c5df491b328ae955329596d3bc73b1e2e091ab5adb46a10759 - md5: 533d342dedadf134c67760ce6fc5b748 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_5.conda + sha256: 7b2e7e31e8a4f5a01c45ecadcd8aa68c8f733b035240bc7ba9881835ef4bf7b4 + md5: 81141db127a106eb5a91df69a29c9918 + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_5 + - libglx 1.7.0 ha4b6fd6_5 + license: LicenseRef-libglvnd + purls: [] + run_exports: {} + size: 131988 + timestamp: 1787310049847 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_5.conda + sha256: 3bebdbeb3ce451287794dddd5cc4d7f4970aac200b2fb797f0d17ac727a71cb7 + md5: f5f7fc038c611a25b22758c0a40d25c9 depends: - __glibc >=2.17,<3.0.a0 + - libgl 1.7.0 ha4b6fd6_5 + - libglx-devel 1.7.0 ha4b6fd6_5 + license: LicenseRef-libglvnd + purls: [] + run_exports: + weak: + - libgl >=1.7.0,<2.0a0 + size: 116212 + timestamp: 1787310061570 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.3-he503a2a_3.conda + sha256: f57bf3954dbba8b27ec68540e84f8b2c2375ffa5702f89ca9741e17329fc3953 + md5: d216efacca3f34f2b13ddd1632f53833 + depends: - libgcc >=15 - - pcre2 >=10.47,<10.48.0a0 - - libzlib >=1.3.2,<2.0a0 + - __glibc >=2.17,<3.0.a0 - libiconv >=1.18,<2.0a0 + - pcre2 >=10.47,<10.48.0a0 - libffi >=3.7.0,<3.8.0a0 + - libzlib >=1.3.2,<2.0a0 constrains: - glib >2.66 license: LGPL-2.1-or-later @@ -11465,8 +12128,45 @@ packages: run_exports: weak: - libglib >=2.88.3,<3.0a0 - size: 4758097 - timestamp: 1787884091247 + size: 4758346 + timestamp: 1788525203514 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_5.conda + sha256: 6eb77601a44b78c4631d886d54ef54f321c2bdc69fdefc4065a1e0491f030c76 + md5: cfcf11edcfd4acf0094771a9c3b8587f + depends: + - __glibc >=2.17,<3.0.a0 + license: LicenseRef-libglvnd + purls: [] + run_exports: {} + size: 133827 + timestamp: 1787310026653 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_5.conda + sha256: d1f0d51aea6bcc5d915969cbed32e72a8ed6a95931b87357ef8d0866573688c4 + md5: 614e10aae180f87b84f0d1ca8ceb7d9e + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_5 + - xorg-libx11 >=1.8.13,<2.0a0 + license: LicenseRef-libglvnd + purls: [] + run_exports: {} + size: 79834 + timestamp: 1787310042550 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_5.conda + sha256: aff3841e3404d78e1887fef988ca4842930c577399d566fa0980808756b1df51 + md5: fb00b4fb800f2d431303a5c1625ef9d0 + depends: + - __glibc >=2.17,<3.0.a0 + - libglx 1.7.0 ha4b6fd6_5 + - xorg-libx11 >=1.8.13,<2.0a0 + - xorg-xorgproto + license: LicenseRef-libglvnd + purls: [] + run_exports: + weak: + - libglx >=1.7.0,<2.0a0 + size: 27698 + timestamp: 1787310053582 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_4.conda sha256: 0fe5cb8e0752241ab55e11656ed1b9726248b522d23b929fe7c95b83eb55b9bb md5: 89d2c1231f47bd818f5d624b9411459d @@ -11701,6 +12401,7 @@ packages: - zstd >=1.5.7,<1.6.0a0 license: Apache-2.0 WITH LLVM-exception license_family: Apache + purls: [] run_exports: weak: - libllvm23 >=23.1.0,<23.2.0a0 @@ -11836,6 +12537,19 @@ packages: - libnsl >=2.0.1,<2.1.0a0 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: [] + run_exports: + weak: + - libntlm >=1.8,<2.0a0 + size: 33418 + timestamp: 1734670021371 - conda: https://conda.anaconda.org/conda-forge/linux-64/libnvjitlink-12.9.86-hecca717_2.conda sha256: 3b1c851f4fc42d347ce1c1606bdd195343a47f121e0fceb7a1f1e5aa1d497da9 md5: 3461b0f2d5cbb7973d361f9e85241d98 @@ -11884,6 +12598,17 @@ packages: run_exports: {} size: 27739 timestamp: 1787703595006 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_5.conda + sha256: 7bdca717c840e17d7dd050f925dd964da61086c2612b8579a4ff4147105f291f + md5: d207a2e9bae9da476bc0a603215d5167 + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_5 + license: LicenseRef-libglvnd + purls: [] + run_exports: {} + size: 50627 + timestamp: 1787310045795 - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-1.27.0-h3133023_1.conda sha256: 8f9281133322e9eb0bd4a5b11330db1c14f3b40c409639dd3805251151ca52e2 md5: f749f223bede1bac7724e49d686ac598 @@ -11935,6 +12660,20 @@ packages: - libparquet >=25.0.0,<25.1.0a0 size: 1412634 timestamp: 1786315223543 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_1.conda + sha256: addc80c69d362a9e6c40305c493139a8e9ee504b2f45a6687dbdaa9da3c6183c + md5: 35fa2b34bbced424e6976d30f5fde576 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - libpciaccess >=0.19,<0.20.0a0 + size: 30070 + timestamp: 1785971678815 - conda: https://conda.anaconda.org/conda-forge/linux-64/libpmix-5.0.8-h31fc519_4.conda sha256: 2837759fb832ab8587622602531f8e4b5d5e5ec12ea9b3936be06f384b933800 md5: bd15ae3916a0cbe005c683bbc33811b7 @@ -11966,6 +12705,23 @@ packages: - libpng >=1.6.58,<1.7.0a0 size: 316643 timestamp: 1786616563127 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.6-h9d76c99_0.conda + sha256: b1eb210c180fda9c445b11cba1286ec250ce2c2a85ccb0551a4ed5423df51e68 + md5: 72e019c04ed02a179da38c56965802d1 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=78.3,<79.0a0 + - krb5 >=1.22.2,<1.23.0a0 + - libgcc >=15 + - openldap >=2.6.13,<2.7.0a0 + - openssl >=3.5.7,<4.0a0 + license: PostgreSQL + purls: [] + run_exports: + weak: + - libpq >=18.6,<19.0a0 + size: 2719680 + timestamp: 1786641133110 - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-7.35.1-h622638d_3.conda sha256: 37c51fbc936a6bb8bf5f67c8f056edacaaa41e8603738bc8a4dee186292bb114 md5: fd307b61cceb36fdca38e1718bdb2893 @@ -12552,6 +13308,25 @@ packages: - libuv >=1.52.1,<2.0a0 size: 420040 timestamp: 1785914567661 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.357.0-h0e34353_2.conda + sha256: a70c25b66321ee77f9892b205e3247263c400803f543dc8aca53d569a59c5a77 + md5: c5f5f159b66332152197893427071695 + depends: + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=15 + - libgcc >=15 + - xorg-libxrandr >=1.5.5,<2.0a0 + - xorg-libx11 >=1.8.13,<2.0a0 + constrains: + - libvulkan-headers 1.4.357.0.* + license: Apache-2.0 + license_family: APACHE + purls: [] + run_exports: + weak: + - libvulkan-loader >=1.4.357.0,<2.0a0 + size: 206957 + timestamp: 1787491938583 - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda sha256: 8415001414f488c85b72b9d8cc2071dfb3981a47bc3c8eb56ef91a57d12eae7f md5: 9332b53d0ea93c5d39e33be03a0c611a @@ -12598,6 +13373,25 @@ packages: - libxcrypt >=4.4.38 size: 101957 timestamp: 1785887123445 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.2-h51789e4_1.conda + sha256: d44878d396713ccf3b355df617f61c40a1442fffcf134392bc6ce0e6c2219369 + md5: f888787e0eab7a8076a67d197e155ffc + depends: + - xkeyboard-config + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libxcb >=1.17.0,<2.0a0 + - xorg-libxau >=1.0.12,<2.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + license: MIT AND MIT-open-group AND HPND AND HPND-sell-variant AND ISC + purls: [] + run_exports: + weak: + - libxkbcommon >=1.13.2,<2.0a0 + size: 942571 + timestamp: 1787178780572 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_1.conda sha256: 087d4de023f22d6a28b9e1b818961dd41e9bda6e9793590600ff16ca150bf9cb md5: 20e4d67ec908f0405124f11612c48305 @@ -12636,6 +13430,22 @@ packages: - libxml2-16 >=2.15.3 size: 46203 timestamp: 1787237584107 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.45-h8e12856_0.conda + sha256: 3257043531b6828cbfe3f1c4d843dd29de3898c4c8780efba792ccdd1464c3b1 + md5: be101a81eab00f1abe854c11a68970de + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - libxml2 + - libxml2-16 >=2.15.3 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - libxslt >=1.1.45,<2.0a0 + size: 250892 + timestamp: 1788362459630 - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda sha256: eb8a0db0aa570124f7d2a93d7c7f596e3390df5e047818d873baad32985fc736 md5: 0de0122d9570a8ab637c6b73db268389 @@ -12749,6 +13559,21 @@ packages: run_exports: {} size: 26100 timestamp: 1772445154165 +- conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.11.1-py312h6a12a82_2.conda + sha256: 72daeb18e9f82509cf6eec25975324c06681127fb94b9b20b53a620a76457e62 + md5: 26c8cd6cbd98ec961a2f8969e14a6e4d + depends: + - matplotlib-base >=3.11.1,<3.11.2.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: [] + run_exports: {} + size: 14938 + timestamp: 1785211151683 - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.11.1-py311he401cf6_2.conda sha256: f9f48162f2553080d041b061b9f6cfec64d79d7c40df0e6379496f9ad2df738c md5: f602c096988d0f744f4f017aa0269e30 @@ -13186,6 +14011,24 @@ packages: - openjpeg >=2.5.4,<3.0a0 size: 391242 timestamp: 1788425045145 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.13-hbde042b_0.conda + sha256: 21c4f6c7f41dc9bec2ea2f9c80440d9a4d45a6f2ac13243e658f10dcf1044146 + md5: 680608784722880fbfe1745067570b00 + depends: + - __glibc >=2.17,<3.0.a0 + - cyrus-sasl >=2.1.28,<3.0a0 + - krb5 >=1.22.2,<1.23.0a0 + - libgcc >=14 + - libstdcxx >=14 + - openssl >=3.5.6,<4.0a0 + license: OLDAP-2.8 + license_family: BSD + purls: [] + run_exports: + weak: + - openldap >=2.6.13,<2.7.0a0 + size: 786149 + timestamp: 1775741359582 - conda: https://conda.anaconda.org/conda-forge/linux-64/openmpi-5.0.10-hee33aa4_2.conda sha256: 20cafe96c3159bd328886552c45561dd52b6cdb844170c54189a1860a88d7997 md5: 7a082f0e3fe2002e187798fd2ed6b1de @@ -13623,6 +14466,33 @@ packages: run_exports: {} size: 5395858 timestamp: 1784258391353 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.2-py312hf143aa3_0.conda + sha256: 4e25d9259683126c377b219a741e081f7b8daa63aa256435d037cdaa3f453589 + md5: bfcdbf7dfa025bf855ffbf44181c143e + depends: + - python + - qt6-main 6.11.2.* + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - libstdcxx >=15 + - python_abi 3.12.* *_cp312 + - libclang13 >=22.1.8 + - libopengl >=1.7.0,<2.0a0 + - libgl >=1.7.0,<2.0a0 + - libegl >=1.7.0,<2.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - libvulkan-loader >=1.4.357.0,<2.0a0 + - qt6-main >=6.11.2,<7.0a0 + - libxslt >=1.1.43,<2.0a0 + license: LGPL-3.0-only + license_family: LGPL + purls: + - pkg:pypi/pyside6?source=hash-mapping + - pkg:pypi/shiboken6?source=hash-mapping + run_exports: {} + size: 13943954 + timestamp: 1787219437874 - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.11.16-h5f976f7_2_cpython.conda build_number: 2 sha256: 35375a255970809e8b0a1c744d56533f4da5339769ef5f5a24fe165ca321fcf3 @@ -13724,21 +14594,20 @@ packages: size: 24369771 timestamp: 1788387840141 python_site_packages_path: lib/python3.13/site-packages -- conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_1.conda - sha256: dc4176b0715a6226663551add60bb4216d2c3e5faafcaf1ac917e9803fa4f7d1 - md5: 37726df0a5491ed021e08006699162e3 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-librt-0.15.0-py312h1b36aeb_2.conda + sha256: 93eefb027858ef06d03e21560b704a4dd9f0a6e4fadeb332c601c16ff5bc54e8 + md5: e4b266d7f7ae5cce6071f51de9d13588 depends: - python - libgcc >=15 - __glibc >=2.17,<3.0.a0 - python_abi 3.12.* *_cp312 license: MIT - license_family: MIT purls: - pkg:pypi/librt?source=compressed-mapping run_exports: {} - size: 169369 - timestamp: 1788228288554 + size: 169277 + timestamp: 1788524688833 - conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.12.1-cpu_mkl_py312_h38523ed_100.conda sha256: 827e69c59252e91a4f15a9b7892d075a73cb4263cbf0faa38f995c6447a267d5 md5: ebe0e6778530b8073f04ccdae79d6e94 @@ -14231,6 +15100,82 @@ packages: - qhull >=2020.2,<2020.3.0a0 size: 552937 timestamp: 1720813982144 +- conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.11.2-pl5321h9df5c37_0.conda + sha256: 00d9bc98d04aeeab6f698ca7fe9d666b157ab39b52cad1af0fa346522705668d + md5: 2ff98660281d1c6bfebda5bffa52cc89 + depends: + - libxcb + - xcb-util + - xcb-util-wm + - xcb-util-keysyms + - xcb-util-image + - xcb-util-renderutil + - xcb-util-cursor + - libgl-devel + - libegl-devel + - libgcc >=15 + - libstdcxx >=15 + - __glibc >=2.17,<3.0.a0 + - libglib >=2.88.3,<3.0a0 + - libbrotlicommon >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libvulkan-loader >=1.4.357.0,<2.0a0 + - wayland >=1.26.0,<2.0a0 + - xorg-libx11 >=1.8.13,<2.0a0 + - libcups >=2.3.3,<2.4.0a0 + - alsa-lib >=1.2.16.1,<1.3.0a0 + - zstd >=1.5.7,<1.6.0a0 + - libtiff >=4.7.2,<4.8.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + - dbus >=1.16.2,<2.0a0 + - libegl >=1.7.0,<2.0a0 + - xorg-libsm >=1.2.6,<2.0a0 + - xcb-util-wm >=0.4.2,<0.5.0a0 + - xorg-libice >=1.1.2,<2.0a0 + - xorg-libxxf86vm >=1.1.7,<2.0a0 + - double-conversion >=3.4.0,<3.5.0a0 + - icu >=78.3,<79.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - krb5 >=1.22.2,<1.23.0a0 + - xorg-libxcursor >=1.2.3,<2.0a0 + - pcre2 >=10.47,<10.48.0a0 + - openssl >=3.5.7,<4.0a0 + - xorg-libxcomposite >=0.4.7,<1.0a0 + - libpng >=1.6.58,<1.7.0a0 + - libjpeg-turbo >=3.2.0,<4.0a0 + - libpq >=18.6,<19.0a0 + - libxcb >=1.17.0,<2.0a0 + - xcb-util >=0.4.1,<0.5.0a0 + - xcb-util-keysyms >=0.4.1,<0.5.0a0 + - xcb-util-renderutil >=0.3.10,<0.4.0a0 + - xcb-util-image >=0.4.0,<0.5.0a0 + - libharfbuzz >=14.3.1 + - fontconfig >=2.18.3,<3.0a0 + - fonts-conda-ecosystem + - xorg-libxdamage >=1.1.6,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - libgl >=1.7.0,<2.0a0 + - xorg-libxrandr >=1.5.5,<2.0a0 + - libsqlite >=3.53.4,<4.0a0 + - xcb-util-cursor >=0.1.6,<0.2.0a0 + - libxkbcommon >=1.13.2,<2.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libdrm >=2.4.129,<2.5.0a0 + - xorg-libxext >=1.3.7,<2.0a0 + - xorg-libxtst >=1.2.5,<2.0a0 + constrains: + - qt ==6.11.2 + license: LGPL-3.0-only + license_family: LGPL + purls: [] + run_exports: + weak: + - qt6-main >=6.11.2,<7.0a0 + size: 60888449 + timestamp: 1787048975419 - conda: https://conda.anaconda.org/conda-forge/linux-64/rattler-build-0.74.0-hf01adef_1.conda sha256: 516a606bdaab6be626036a7a8f5e10db3d3b8a297b03ede0b93222bddce901d9 md5: 184a4c38cd3b9c5326147484450f14f7 @@ -14506,12 +15451,12 @@ packages: - tk >=8.6.13,<8.7.0a0 size: 3566806 timestamp: 1787272857910 -- conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py312h4c3975b_0.conda - sha256: 2ea8f8b10e869b675d2fba157b387eaf4eed1696bfc924bb7b28e67d7aa0a947 - md5: 5423c2b8f82d60320e65b9ff30babbdf +- conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.8-py312h5cc1888_1.conda + sha256: 3480bf39c540d3dfa737ce5df1e14c74fef161ccb2a19485cb211a654921fa68 + md5: e15caa7ad9be633c3cba5a7937b117b7 depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14 + - libgcc >=15 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: Apache-2.0 @@ -14519,8 +15464,8 @@ packages: purls: - pkg:pypi/tornado?source=compressed-mapping run_exports: {} - size: 869730 - timestamp: 1786226754702 + size: 868900 + timestamp: 1788524841516 - conda: https://conda.anaconda.org/conda-forge/linux-64/triton-3.7.1-cuda129py312h811769c_1.conda sha256: f6057dfe20a473f0b1ab7e1a231ebce774ba934a3c54176efb58bf1133aaa44b md5: b32d0cc6a3091264c0b61db49ecc8718 @@ -14700,6 +15645,126 @@ packages: run_exports: {} size: 410641 timestamp: 1770909099497 +- conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-hc1c935e_2.conda + sha256: df65b987979e6d7f88e756494da5a230d4f2d08d17437d04eecd35559c3a04e4 + md5: 88a47e22b53e50865b1cabe2eb648352 + depends: + - __glibc >=2.17,<3.0.a0 + - libexpat >=2.8.1,<3.0a0 + - libffi >=3.7.0,<3.8.0a0 + - libgcc >=15 + - libstdcxx >=15 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - wayland >=1.26.0,<2.0a0 + size: 340058 + timestamp: 1787793849093 +- 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: [] + run_exports: + weak: + - xcb-util >=0.4.1,<0.5.0a0 + 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: [] + run_exports: + weak: + - xcb-util-cursor >=0.1.6,<0.2.0a0 + 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: [] + run_exports: + weak: + - xcb-util-image >=0.4.0,<0.5.0a0 + 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: [] + run_exports: + weak: + - xcb-util-keysyms >=0.4.1,<0.5.0a0 + 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: [] + run_exports: + weak: + - xcb-util-renderutil >=0.3.10,<0.4.0a0 + 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: [] + run_exports: + weak: + - xcb-util-wm >=0.4.2,<0.5.0a0 + size: 51689 + timestamp: 1718844051451 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.48-h280c20c_0.conda + sha256: 3b04afd5d1a65d2d27ac2d49a63b01ab8bcd875776779ec63e337370ed38afdc + md5: b233b41be0bf210989d57160ed39b394 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - xorg-libx11 >=1.8.13,<2.0a0 + license: MIT + license_family: MIT + purls: [] + run_exports: {} + size: 441670 + timestamp: 1782027360439 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-h280c20c_0.conda sha256: 49b532d1df875c6749d9078b56a76f3f5db49a5abe0ca620b593ed474ef0ebf1 md5: 85c9442aec283b4e464fa9ecc484a2f3 @@ -14759,6 +15824,56 @@ packages: - xorg-libxau >=1.0.12,<2.0a0 size: 16419 timestamp: 1786381001122 +- 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: [] + run_exports: + weak: + - xorg-libxcomposite >=0.4.7,<1.0a0 + 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: [] + run_exports: + weak: + - xorg-libxcursor >=1.2.3,<2.0a0 + 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: [] + run_exports: + weak: + - xorg-libxdamage >=1.1.6,<2.0a0 + size: 13217 + timestamp: 1727891438799 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_2.conda sha256: c50a16c05ccd7fe7dd6d6cfb539f4e9a491d50f9ed7a5c902fec638f7d0d27be md5: 2e66c929f3d879708335b6ea4557c838 @@ -14788,6 +15903,55 @@ packages: - xorg-libxext >=1.3.7,<2.0a0 size: 53124 timestamp: 1787100841900 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-h7cc23a3_1.conda + sha256: aec84f554fc897bf4085c7bc8b8f0740e4c224e13bca3cc51c93e7699d56f83a + md5: 09132e874fe0e1f5e4492b0b6b904b6e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - xorg-libx11 >=1.8.13,<2.0a0 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - xorg-libxfixes >=6.0.2,<7.0a0 + size: 21440 + timestamp: 1787248059682 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-h7cc23a3_1.conda + sha256: a523d71344a2f640efe6fc42b88fc261d9cd389d4f9838a5d42dcdb120c2b0c8 + md5: a1412b2b1184dacda45f8476fef2cc25 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - 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: [] + run_exports: + weak: + - xorg-libxi >=1.8.3,<2.0a0 + size: 49165 + timestamp: 1787257060460 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-h7cc23a3_1.conda + sha256: 05a7f25d7f7f5cd32b27a019233ece97167fd8ade255bc13a0e49e53387f4c30 + md5: 798a8c9d171859a022e1cb89e7d0eb10 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - xorg-libx11 >=1.8.13,<2.0a0 + - xorg-libxext >=1.3.7,<2.0a0 + - xorg-libxrender >=0.9.12,<0.10.0a0 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - xorg-libxrandr >=1.5.5,<2.0a0 + size: 31106 + timestamp: 1787246614086 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb03c661_1.conda sha256: 6901f91d398811e4ec89d7e20a69abac02a7bfebfaf073338b7ea3d1a99685b7 md5: e470d224a7a5be1b1d021bded7abb536 @@ -14803,6 +15967,51 @@ packages: - xorg-libxrender >=0.9.12,<0.10.0a0 size: 34645 timestamp: 1787100191192 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-h7cc23a3_4.conda + sha256: d66525e7b1c492aa179c6c55610fc8dfb0a9a62ea7d4fb9f904fa6af62127f61 + md5: 752a5ac9322e0fbbc24146c1ce3ae44e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - xorg-libx11 >=1.8.13,<2.0a0 + - xorg-libxext >=1.3.7,<2.0a0 + - xorg-libxi >=1.8.3,<2.0a0 + license: MIT + license_family: MIT + purls: [] + run_exports: + weak: + - xorg-libxtst >=1.2.5,<2.0a0 + size: 35052 + timestamp: 1787360025506 +- 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: [] + run_exports: + weak: + - xorg-libxxf86vm >=1.1.7,<2.0a0 + size: 18701 + timestamp: 1769434732453 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hebe6cf0_2.conda + sha256: 28be920cbb36bb674f66a23f368b67733dbbb89b2321e5ed7266d9b415156795 + md5: 7386d38083163c5b5c4a92a1582daaef + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + license: MIT + license_family: MIT + purls: [] + run_exports: {} + size: 594980 + timestamp: 1788447117643 - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-hebe6cf0_3.conda sha256: d164dfa75ecd538f6fd68765defcc06aa875bc697b9b215362d79a2a73125dd0 md5: e741576fb8f89821ac7c1c537322a33d @@ -16436,15 +17645,15 @@ packages: run_exports: {} size: 1489572 timestamp: 1787617767130 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglib-2.88.3-hb514e8e_2.conda - sha256: 1a1f854a38a4a0247a0193f220f0aa4f16ce964eed91963be4d0e63ca9e029d4 - md5: de44cb5a4ccf6eccf34811b95a1f247b +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglib-2.88.3-hb514e8e_3.conda + sha256: 90ffb44655cc8ab1d4566ae77be09f887760d859c24358ee926edd56cbdc2e42 + md5: 18837ca7480048a942c7404a36883b08 depends: - libgcc >=15 - - libiconv >=1.18,<2.0a0 - - pcre2 >=10.47,<10.48.0a0 - libzlib >=1.3.2,<2.0a0 - libffi >=3.7.0,<3.8.0a0 + - libiconv >=1.18,<2.0a0 + - pcre2 >=10.47,<10.48.0a0 constrains: - glib >2.66 license: LGPL-2.1-or-later @@ -16452,8 +17661,8 @@ packages: run_exports: weak: - libglib >=2.88.3,<3.0a0 - size: 4830545 - timestamp: 1788398417264 + size: 4830492 + timestamp: 1788525192222 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-16.2.0-h8acb6b2_4.conda sha256: 6d216e6dc9a158b920f6e0c1dfdd6d77575bf79420dadf85d64576298ecb4503 md5: 727c19b26cd43140e4a90a6f8f1cc31a @@ -17764,21 +18973,20 @@ packages: - python size: 22418400 timestamp: 1788392934327 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-librt-0.15.0-py312h085bef5_1.conda - sha256: 8be7607be3d299fd3978f4814bc7414bd220606e2d4a984896a0611bb1e28bce - md5: b258f86969c21b76687c0a01e7dc755d +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-librt-0.15.0-py312h085bef5_2.conda + sha256: 5a2af2a40f3e86f1e89559cf1f9eef23f97b7241172fb774afc265d1a6fd4cf4 + md5: 7a1184f162fed7074bc8eae7c7ec2eba depends: - python - - python 3.12.* *_cpython - libgcc >=15 + - python 3.12.* *_cpython - python_abi 3.12.* *_cp312 license: MIT - license_family: MIT purls: - - pkg:pypi/librt?source=compressed-mapping + - pkg:pypi/librt?source=hash-mapping run_exports: {} - size: 171890 - timestamp: 1788228308212 + size: 172069 + timestamp: 1788524702946 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pytorch-2.12.1-cpu_generic_py312_h9dd5f13_0.conda sha256: 8956bf3e04eaed1056dd011714dcb50d8c2804aeb7ec40ff6e8a739b5d6abdb2 md5: 452a1b67f47bc4ff7bd6c48ad0f20667 @@ -19975,6 +21183,18 @@ packages: run_exports: {} size: 27515 timestamp: 1788271419073 +- conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.11.8-pyh5ded981_0.conda + sha256: 8cbae1fed8437e63c9bcc691927402edb3809fccb26b15eb095df7dd2eb73e5f + md5: 48e9ecf08b0578a4313f13b2e2b0fd72 + depends: + - python >=3.11 + - python + license: MIT + purls: + - pkg:pypi/platformdirs?source=compressed-mapping + run_exports: {} + size: 27718 + timestamp: 1788959137036 - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda sha256: e14aafa63efa0528ca99ba568eaf506eb55a0371d12e6250aaaa61718d2eb62e md5: d7585b6550ad04c8c5e21097ada2888e @@ -22760,16 +23980,16 @@ packages: run_exports: {} size: 554806 timestamp: 1787617465010 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-h89cd0c0_2.conda - sha256: 9c25def7c7f9ab98b265690c80012e917d080538875f9679b46ccd0c042e6201 - md5: 14ef48eb322e0bfb1e5fe31e047359e3 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.88.3-h81decf1_3.conda + sha256: aad240a33afc71c0c9cefea5304c917c256518e6724cd646d924838fdff1fcb3 + md5: f3ee2d5802e47ff391fe9a6d956e76b0 depends: - __osx >=11.0 - - libintl >=0.25.1,<1.0a0 - libzlib >=1.3.2,<2.0a0 + - libffi >=3.7.0,<3.8.0a0 - pcre2 >=10.47,<10.48.0a0 + - libintl >=0.25.1,<1.0a0 - libiconv >=1.18,<2.0a0 - - libffi >=3.7.0,<3.8.0a0 constrains: - glib >2.66 license: LGPL-2.1-or-later @@ -22777,8 +23997,8 @@ packages: run_exports: weak: - libglib >=2.88.3,<3.0a0 - size: 4443052 - timestamp: 1787884141804 + size: 4443129 + timestamp: 1788525252185 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-3.8.0-ha595bda_0.conda sha256: a48050bcf3827e0fc10de5a7d251760b8b84011f3aeea23ae18b5e0ce25aff19 md5: 4f34ca73f16b37fae583ce16c48b5492 @@ -24052,21 +25272,20 @@ packages: - python size: 11705033 timestamp: 1788407558326 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-librt-0.15.0-py312h7ac3c4f_1.conda - sha256: 94aa71abc82ab24c52252997062b117f63149fce14377958ece828cd79011f6a - md5: b8e9ca60f567b679f76d908dcc51628d +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-librt-0.15.0-py312h7ac3c4f_2.conda + sha256: ace7db1c4bc3f8423f01683cd8ad6ad7940277957b688f56eec93e6d9197db60 + md5: af32bad9f75ad4b132017bb2b12d5296 depends: - python - - __osx >=11.0 - python 3.12.* *_cpython + - __osx >=11.0 - python_abi 3.12.* *_cp312 license: MIT - license_family: MIT purls: - - pkg:pypi/librt?source=compressed-mapping + - pkg:pypi/librt?source=hash-mapping run_exports: {} - size: 129888 - timestamp: 1788228436318 + size: 130261 + timestamp: 1788524846305 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pytorch-2.12.1-cpu_generic_py312_h7a5249f_0.conda sha256: 538726375bf71a9f7d776eeba3964ff049fa888c79ed93d76319127e2a02559d md5: 93c511e326295c67ccd7864983a5e87a diff --git a/pixi.toml b/pixi.toml index e86b8d02..788c5ade 100644 --- a/pixi.toml +++ b/pixi.toml @@ -128,6 +128,11 @@ types-PyYAML = "*" memray = "*" py-spy = "*" +[feature.interactive.dependencies] +ipykernel = "*" +ipython = "*" +matplotlib = "*" + [feature.benchmark.dependencies] jinja2 = "*" markdown-it-py = "*" @@ -296,6 +301,7 @@ skalaxc-doxygen = { cmd = "cmake -DSKALAXC_DOXYGEN_SOURCE_DIR=$PIXI_PROJECT_ROOT [environments] default = { features = ["python-runtime", "model-definition", "component-sources", "model-assets", "python-312", "pyscf-214", "dispersion", "torch-213-cpu", "test", "lint", "profiling", "benchmark"], no-default-feature = true, platforms = ["linux-64", "linux-aarch64", "osx-arm64"] } +dev = { features = ["python-runtime", "model-definition", "component-sources", "model-assets", "python-312", "pyscf-214", "dispersion", "torch-213-gpu", "cuda-12", "test", "lint", "profiling", "benchmark", "interactive"], no-default-feature = true, platforms = ["linux-64-cuda12"] } lint = { features = ["python-runtime", "model-definition", "component-sources", "model-assets", "python-312", "pyscf-214", "dispersion", "torch-213-cpu", "test", "lint", "profiling", "benchmark"], no-default-feature = true, platforms = ["linux-64"] } test-py311-pyscf214-torch213 = { features = ["python-runtime", "model-assets", "python-311", "pyscf-214", "dispersion", "torch-213-cpu", "test"], no-default-feature = true, platforms = ["linux-64"] } test-py312-pyscf214-torch212 = { features = ["python-runtime", "component-sources", "model-assets", "python-312", "pyscf-214", "dispersion", "torch-212-cpu", "test", "profiling", "benchmark"], no-default-feature = true, platforms = ["linux-64", "linux-aarch64", "osx-arm64"] } @@ -311,9 +317,9 @@ cpp-integration = { features = ["python-runtime", "model-definition", "component ftorch = { features = ["python-312", "model-assets", "torch-213-cpu", "native-toolchain", "fortran"], no-default-feature = true, platforms = ["linux-64"] } skalaxc-host = { features = ["python-312", "torch-213-cpu", "native-toolchain", "fortran", "skalaxc-core", "skalaxc-mpi", "skalaxc-python", "test"], no-default-feature = true, platforms = ["linux-64", "linux-aarch64", "osx-arm64"] } skalaxc-host-clang = { features = ["python-312", "torch-213-cpu", "fortran", "skalaxc-core", "skalaxc-mpi", "skalaxc-python", "skalaxc-clang", "test"], no-default-feature = true, platforms = ["linux-64"] } -skalaxc-cuda12 = { features = ["python-runtime", "model-assets", "python-312", "pyscf-214", "torch-213-gpu", "cuda-12", "native-toolchain", "fortran", "skalaxc-core", "skalaxc-mpi", "skalaxc-python", "skalaxc-cuda12-build", "test"], no-default-feature = true, platforms = ["linux-64-cuda12"] } -skalaxc-cuda13 = { features = ["python-runtime", "model-assets", "python-312", "pyscf-214", "torch-213-gpu", "cuda-13", "native-toolchain", "fortran", "skalaxc-core", "skalaxc-mpi", "skalaxc-python", "skalaxc-cuda13-build", "test"], no-default-feature = true, platforms = ["linux-64-cuda13"] } -skalaxc-cuda13-clang = { features = ["python-runtime", "model-assets", "python-312", "pyscf-214", "torch-213-gpu", "cuda-13", "fortran", "skalaxc-core", "skalaxc-mpi", "skalaxc-python", "skalaxc-cuda13-clang-build", "test"], no-default-feature = true, platforms = ["linux-64-cuda13"] } -skalaxc-parity = { features = ["python-runtime", "model-assets", "python-312", "pyscf-214", "torch-213-cpu", "native-toolchain", "skalaxc-core", "skalaxc-python", "skalaxc-parity", "test"], no-default-feature = true, platforms = ["linux-64"] } +skalaxc-cuda12 = { features = ["python-runtime", "model-assets", "python-312", "pyscf-214", "torch-213-gpu", "cuda-12", "native-toolchain", "fortran", "skalaxc-core", "skalaxc-mpi", "skalaxc-python", "skalaxc-cuda12-build", "test", "profiling"], no-default-feature = true, platforms = ["linux-64-cuda12"] } +skalaxc-cuda13 = { features = ["python-runtime", "model-assets", "python-312", "pyscf-214", "torch-213-gpu", "cuda-13", "native-toolchain", "fortran", "skalaxc-core", "skalaxc-mpi", "skalaxc-python", "skalaxc-cuda13-build", "test", "profiling"], no-default-feature = true, platforms = ["linux-64-cuda13"] } +skalaxc-cuda13-clang = { features = ["python-runtime", "model-assets", "python-312", "pyscf-214", "torch-213-gpu", "cuda-13", "fortran", "skalaxc-core", "skalaxc-mpi", "skalaxc-python", "skalaxc-cuda13-clang-build", "test", "profiling"], no-default-feature = true, platforms = ["linux-64-cuda13"] } +skalaxc-parity = { features = ["python-runtime", "model-assets", "python-312", "pyscf-214", "torch-213-cpu", "native-toolchain", "skalaxc-core", "skalaxc-python", "skalaxc-parity", "test", "profiling"], no-default-feature = true, platforms = ["linux-64"] } skalaxc-tools = { features = ["python-312", "torch-213-cpu", "skalaxc-core", "skalaxc-tools"], no-default-feature = true, platforms = ["linux-64"] } skalaxc-package = { features = ["skalaxc-package"], no-default-feature = true, platforms = ["linux-64", "linux-aarch64", "osx-arm64"] } diff --git a/skala/README.md b/skala/README.md index 73740500..9af42eda 100644 --- a/skala/README.md +++ b/skala/README.md @@ -77,7 +77,8 @@ pip install skala-cuda12x The `skala-cuda13x` package is available for CUDA 13. -For a reproducible source environment, choose one of the locked GPU environments: +For a reproducible source environment, choose one of the locked GPU compatibility +environments: | Environment | CUDA | PyTorch | |---|---:|---:| diff --git a/skala/tests/utils.py b/skala/tests/utils.py index f20931cd..1bee5d80 100644 --- a/skala/tests/utils.py +++ b/skala/tests/utils.py @@ -89,22 +89,18 @@ def get_exc(self, mol: FeatureMap) -> torch.Tensor: @contextmanager -def force_ao_screening( - enabled: bool, - module: ModuleType = xc_integrator_module, -) -> Generator[None]: +def force_ao_screening(enabled: bool) -> Generator[None]: """Temporarily force the AO-screening route decision. Args: enabled: Whether calls should select screened AO evaluation. - module: Module whose ``_should_screen_aos`` decision function is patched. Yields: Control while the forced decision is active. The previous function is restored when the context exits. """ with patch.object( - module, + xc_integrator_module, "_should_screen_aos", return_value=enabled, ): diff --git a/website/installation.rst b/website/installation.rst index 1bcd56e6..2b18f4a7 100644 --- a/website/installation.rst +++ b/website/installation.rst @@ -64,6 +64,14 @@ always use the selected environment: Set ``OMP_NUM_THREADS=4`` when running tests locally to match CI. +On Linux x86_64 with a CUDA 12-compatible driver, install the GPU-enabled +development superset with profiling and interactive analysis tools: + +.. code-block:: bash + + pixi install --locked -e dev + pixi run -e dev ipython + The locked compatibility environments are: .. list-table:: diff --git a/website/pyscf/gpu4pyscf.rst b/website/pyscf/gpu4pyscf.rst index 663ec7bd..68c02012 100644 --- a/website/pyscf/gpu4pyscf.rst +++ b/website/pyscf/gpu4pyscf.rst @@ -22,9 +22,9 @@ The Skala functional can also be used in GPU4PySCF with an appropriate PyTorch C Installation ------------ -The repository provides three locked GPU environments. They combine conda-forge -PyTorch and CUDA libraries with PyPI PySCF and the latest tested GPU4PySCF -release, 1.8.1: +The repository provides three locked GPU compatibility environments. They +combine conda-forge PyTorch and CUDA libraries with PyPI PySCF and the latest +tested GPU4PySCF release, 1.8.1: * ``gpu-cuda12-torch212`` * ``gpu-cuda12-torch213``