diff --git a/.gitignore b/.gitignore index 11901563..972062a1 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ scripts/venv_unsloth/ **/venv_wgpu/ **/*.venv/ **/venv/ +.venv-science/ # API Keys and credentials (NEVER track in git) API KEYS/ diff --git a/docs/optional-science-toolbelt.md b/docs/optional-science-toolbelt.md new file mode 100644 index 00000000..9899c6b0 --- /dev/null +++ b/docs/optional-science-toolbelt.md @@ -0,0 +1,79 @@ +# Optional Science Toolbelt + +These tools are optional reference surfaces for math-first work. They are not +default repo dependencies. Add them to a local environment only when a task +needs an executable witness, conversion adapter, or independent solver. + +The operating rule is simple: Lean remains the source of truth; these tools +produce receipts, counterexamples, fixtures, and sanity checks. + +## Install Manifests + +Python packages that are reasonable to install into a task-specific virtual +environment: + +```bash +uv venv .venv-science +source .venv-science/bin/activate +uv pip install -r requirements-optional-science.txt +``` + +Equivalent npm convenience commands: + +```bash +npm run setup-science-light +npm run probe-science +``` + +Native command-line tools are listed in: + +```text +system-packages-optional-science.txt +``` + +Do not wire these into default CI unless the workflow is explicitly optional or +the tool is already installed on the runner. + +## Probe What Is Available + +Use the probe before asking an agent to rely on a domain tool: + +```bash +python3 scripts/probe_science_toolbelt.py +python3 scripts/probe_science_toolbelt.py --json +python3 scripts/probe_science_toolbelt.py --json --out shared-data/artifacts/science_toolbelt/probe.json +``` + +The probe exits successfully even when optional tools are missing. Missing tools +are data, not failure. + +## Domain Priorities + +| Domain | First tools | Use in this stack | +| --- | --- | --- | +| Genetics / bioinformatics | Biopython, pysam, samtools, bcftools, minimap2 | Validate FASTA/FASTQ/GenBank/SAM/BAM/VCF fixtures for the genetic-code, Hachimoji, and PIST surfaces. | +| CFD / PDE | Dedalus, ParaView | Produce spectral PDE reference traces for Burgers/KdV/hyperfluid claims without committing to a heavyweight engineering CFD stack. | +| Cryptography | liboqs-python, PyCryptodome, galois | Check post-quantum KEM/signature examples, hashes, finite-field arithmetic, and receipt digests. | +| Chemistry / materials | RDKit, Open Babel | Parse/canonicalize SMILES, compute descriptors, and turn molecular claims into inspectable fixtures. | +| Formal bridge | Z3, cvc5 | Search bounded counterexamples before spending Lean effort. | +| Algebra / graph theory | SageMath, GAP, NetworkX, Graphviz | Generate lattice/group/graph witnesses and diagrams for later Lean or receipt promotion. | +| Compression / signal | zstandard, PyWavelets | Provide compression baselines and spectral/wavelet witnesses for signal-shaping claims. | + +## What Not To Do + +- Do not add these packages to the default repo environment. +- Do not treat a solver result as a theorem. +- Do not commit generated datasets unless they are promoted as small, + receipt-bearing evidence. +- Do not copy implementation code from external tools into the repo. + +## Receipt Pattern + +For any adapter built on this toolbelt, prefer: + +1. Input fixture path. +2. Tool name and version from `probe_science_toolbelt.py`. +3. Exact command or Python module call. +4. Output hash and short human-readable summary. +5. Link to the Lean theorem, claim registry entry, or distilled doc that the + receipt supports. diff --git a/package.json b/package.json index fe68c255..a5472dd1 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,9 @@ "scripts": { "install-python": "uv python install 3.11.15", "setup-cad-env": "cd 5-Applications/text-to-cad && python3.11 -m venv .venv && ./.venv/bin/pip install -r requirements-cad.txt", - "verify-cad": "cd 5-Applications/text-to-cad && ./.venv/bin/python -c \"import build123d; import OCP; print('CAD dependencies OK')\"" + "verify-cad": "cd 5-Applications/text-to-cad && ./.venv/bin/python -c \"import build123d; import OCP; print('CAD dependencies OK')\"", + "setup-science-light": "uv venv .venv-science && . .venv-science/bin/activate && uv pip install -r requirements-optional-science.txt", + "probe-science": "python3 scripts/probe_science_toolbelt.py" }, "dependencies": { "better-sqlite3": "^12.4.1" diff --git a/requirements-optional-science.txt b/requirements-optional-science.txt new file mode 100644 index 00000000..867f2166 --- /dev/null +++ b/requirements-optional-science.txt @@ -0,0 +1,34 @@ +# Optional science toolbelt for receipt-producing adapters. +# +# Install only when a task needs these domains: +# +# uv pip install -r requirements-optional-science.txt +# +# These packages are intentionally not part of the default repo setup. They +# provide reference implementations and data-format adapters for math-first +# verification receipts. + +# Genetics / bioinformatics +biopython>=1.87 +pysam>=0.24.0 + +# Chemistry / materials +rdkit>=2026.3.1 + +# SMT / formal-methods bridge +z3-solver>=4.16.0.0 +cvc5>=1.3.4 + +# Cryptography / finite fields / symbolic checks +pycryptodome>=3.23.0 +galois>=0.4.6 + +# Graphs, signal analysis, and compression baselines +networkx>=3.5 +PyWavelets>=1.9.0 +zstandard>=0.25.0 + +# Heavy or native-stack options. Keep commented unless a task explicitly needs +# the solver and the host has the native prerequisites. +# dedalus>=3.0.5 +# liboqs-python>=0.14.1 diff --git a/scripts/probe_science_toolbelt.py b/scripts/probe_science_toolbelt.py new file mode 100755 index 00000000..46db5a87 --- /dev/null +++ b/scripts/probe_science_toolbelt.py @@ -0,0 +1,242 @@ +#!/usr/bin/env python3 +"""Probe optional science tools without requiring them. + +The goal is not to make these tools part of the default stack. The goal is to +let agents and CI-adjacent smoke checks discover which reference solvers, +format adapters, and verification backends are available on the current host. + +Exit code: + 0 probe completed; missing optional tools are reported in the payload. + 2 invalid CLI arguments or unable to write output. +""" +from __future__ import annotations + +import argparse +import importlib +import json +import shutil +import subprocess +import sys +from dataclasses import asdict, dataclass +from pathlib import Path +from typing import Any + + +@dataclass(frozen=True) +class PythonTool: + key: str + domain: str + module: str + purpose: str + + +@dataclass(frozen=True) +class CommandTool: + key: str + domain: str + command: str + purpose: str + version_args: tuple[str, ...] = ("--version",) + + +PYTHON_TOOLS: tuple[PythonTool, ...] = ( + PythonTool( + "biopython", + "genetics", + "Bio", + "FASTA/FASTQ/GenBank parsing and sequence manipulation", + ), + PythonTool("pysam", "genetics", "pysam", "SAM/BAM/CRAM/VCF access from Python"), + PythonTool( + "rdkit", + "chemistry", + "rdkit", + "SMILES parsing, canonicalization, descriptors, fingerprints", + ), + PythonTool("z3-solver", "formal-methods", "z3", "SMT checks before Lean proof work"), + PythonTool("cvc5", "formal-methods", "cvc5", "SMT checks and model finding"), + PythonTool( + "pycryptodome", + "cryptography", + "Crypto", + "hash/signature/cipher reference primitives", + ), + PythonTool("galois", "cryptography", "galois", "finite-field arithmetic"), + PythonTool( + "networkx", + "graph-theory", + "networkx", + "graph certificates and topology receipts", + ), + PythonTool( + "pywavelets", + "signal-processing", + "pywt", + "wavelet decompositions for spectral receipts", + ), + PythonTool( + "zstandard", + "compression", + "zstandard", + "zstd baseline and dictionary-training adapter", + ), + PythonTool("dedalus", "cfd-pde", "dedalus", "spectral PDE reference solver"), + PythonTool( + "liboqs-python", + "cryptography", + "oqs", + "Open Quantum Safe KEM/signature reference adapter", + ), +) + +COMMAND_TOOLS: tuple[CommandTool, ...] = ( + CommandTool( + "samtools", + "genetics", + "samtools", + "SAM/BAM/CRAM command-line verification", + ), + CommandTool("bcftools", "genetics", "bcftools", "VCF/BCF command-line verification"), + CommandTool("minimap2", "genetics", "minimap2", "sequence alignment smoke checks"), + CommandTool( + "sage", + "algebra-crypto", + "sage", + "lattice, group, and symbolic mathematics", + ("--version",), + ), + CommandTool("gap", "algebra", "gap", "computational group-theory checks", ("--version",)), + CommandTool("z3", "formal-methods", "z3", "SMT solver CLI", ("--version",)), + CommandTool("cvc5", "formal-methods", "cvc5", "SMT solver CLI", ("--version",)), + CommandTool("zstd", "compression", "zstd", "compression baseline CLI", ("--version",)), + CommandTool("dot", "graph-theory", "dot", "Graphviz graph rendering for witness diagrams", ("-V",)), + CommandTool("obabel", "chemistry", "obabel", "Open Babel format conversion", ("-V",)), + CommandTool("paraview", "cfd-pde", "paraview", "CFD/PDE visualization frontend", ("--version",)), +) + + +def _module_version(module: Any) -> str | None: + for attr in ("__version__", "VERSION", "version"): + value = getattr(module, attr, None) + if value is None: + continue + if callable(value): + try: + value = value() + except Exception: + continue + return str(value) + return None + + +def _probe_python(tool: PythonTool) -> dict[str, Any]: + try: + module = importlib.import_module(tool.module) + except Exception as exc: + return { + **asdict(tool), + "available": False, + "error": f"{type(exc).__name__}: {exc}", + } + return { + **asdict(tool), + "available": True, + "version": _module_version(module), + "path": getattr(module, "__file__", None), + } + + +def _run_version(path: str, args: tuple[str, ...]) -> str | None: + try: + result = subprocess.run( + [path, *args], + capture_output=True, + text=True, + timeout=5, + check=False, + ) + except Exception: + return None + text = (result.stdout or result.stderr).strip() + if not text: + return None + return text.splitlines()[0] + + +def _probe_command(tool: CommandTool) -> dict[str, Any]: + path = shutil.which(tool.command) + if path is None: + return { + **asdict(tool), + "available": False, + } + return { + **asdict(tool), + "available": True, + "path": path, + "version": _run_version(path, tool.version_args), + } + + +def build_report() -> dict[str, Any]: + python_results = [_probe_python(tool) for tool in PYTHON_TOOLS] + command_results = [_probe_command(tool) for tool in COMMAND_TOOLS] + available = sum(1 for item in python_results + command_results if item["available"]) + total = len(python_results) + len(command_results) + by_domain: dict[str, dict[str, int]] = {} + for item in python_results + command_results: + bucket = by_domain.setdefault(item["domain"], {"available": 0, "total": 0}) + bucket["total"] += 1 + if item["available"]: + bucket["available"] += 1 + return { + "schema": "research_stack_optional_science_toolbelt_probe_v1", + "summary": { + "available": available, + "total": total, + "by_domain": by_domain, + }, + "python": python_results, + "commands": command_results, + } + + +def _print_text(report: dict[str, Any]) -> None: + summary = report["summary"] + print(f"optional science toolbelt: {summary['available']}/{summary['total']} available") + for section in ("python", "commands"): + print(f"\n{section}:") + for item in report[section]: + mark = "OK" if item["available"] else "--" + version = item.get("version") + suffix = f" ({version})" if version else "" + print(f" {mark} {item['key']}: {item['purpose']}{suffix}") + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--json", action="store_true", help="emit JSON instead of text") + parser.add_argument("--out", type=Path, help="write the JSON report to this path") + args = parser.parse_args(argv) + + report = build_report() + if args.out: + try: + args.out.parent.mkdir(parents=True, exist_ok=True) + args.out.write_text( + json.dumps(report, indent=2, sort_keys=True) + "\n", + encoding="utf-8", + ) + except OSError as exc: + print(f"error: failed to write {args.out}: {exc}", file=sys.stderr) + return 2 + + if args.json: + print(json.dumps(report, indent=2, sort_keys=True)) + else: + _print_text(report) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/system-packages-optional-science.txt b/system-packages-optional-science.txt new file mode 100644 index 00000000..b6d3ca6a --- /dev/null +++ b/system-packages-optional-science.txt @@ -0,0 +1,26 @@ +# Optional system packages for science toolbelt adapters. +# +# These are command-line tools or heavyweight native stacks that Python +# requirements cannot reliably install. Names are Debian/Ubuntu-style package +# names where available; distro packages vary. + +# Genetics / bioinformatics +samtools +bcftools +minimap2 + +# Formal methods / algebra +cvc5 +z3 +gap +sagemath + +# Compression and receipt payload baselines +zstd + +# Visualization / data inspection +graphviz +paraview + +# Chemistry / materials native helpers +openbabel