Skip to content

Commit 62e55f7

Browse files
committed
feat(ci,ml): Tier-3 live-prover provisioning + uncap Flux training
.github/workflows/live-provers.yml Replace Tier-3 placeholder with a per-backend matrix. Live-provisioned (apt / upstream tarball): tamarin, proverif, metamath, twelf, ortools. Deferred-to-Containerfile with explicit skip note: hol4, acl2, scip, imandra. Each job probes the binary and runs the single matching case from tests/live_prover_suite.rs with continue-on-error while Wave-3 wires up. tests/live_prover_suite.rs Add #[tokio::test] entries for all nine Tier-3 backends and extend kind_label with their display names. Tests SKIP when the binary is absent on PATH (existing `which` probe), so running locally or in PR CI costs nothing; only the weekly tier3 matrix exercises them. src/julia/run_training.jl Drop the hardcoded max_proof_states=50_000 cap that made the expanded corpus pointless. Default 200k on GPU / 50k on CPU, overridable via ECHIDNA_MAX_PROOF_STATES (0 = unlimited). ECHIDNA_NUM_EPOCHS and ECHIDNA_NUM_NEGATIVES gain the same treatment. Required before retraining on the 2026-04 corpus expansion.
1 parent 9c95bd0 commit 62e55f7

3 files changed

Lines changed: 209 additions & 9 deletions

File tree

.github/workflows/live-provers.yml

Lines changed: 112 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -226,21 +226,127 @@ jobs:
226226
run: cargo test --test live_prover_suite --features live-provers -- --nocapture ${{ matrix.backend }}
227227

228228
# ============================================================================
229-
# Tier 3 — weekly. Container-provisioned provers. Wave-3 placeholder.
229+
# Tier 3 — weekly. Heavier upstream provers, best-effort provisioning.
230+
#
231+
# Backends grouped as:
232+
# A — live-provisioned today via apt / upstream tarball (tamarin, proverif,
233+
# metamath, twelf, ortools). Version-check runs, skip-if-absent.
234+
# B — heavy-build deferred (hol4, acl2, scip) and proprietary (imandra).
235+
# Provisioning step emits a skip note; test step runs the suite with
236+
# continue-on-error so the matrix stays green and the binary's absence
237+
# is logged as SKIP by the suite's `which` probe.
230238
# ============================================================================
231239
tier3:
232-
name: T3 container provers
240+
name: T3 / ${{ matrix.backend }}
233241
if: github.event_name == 'schedule' && github.event.schedule == '0 5 * * 0' || (github.event_name == 'workflow_dispatch' && (inputs.tier == '3' || inputs.tier == 'all'))
234242
runs-on: ubuntu-latest
235243
continue-on-error: true
244+
strategy:
245+
fail-fast: false
246+
matrix:
247+
backend:
248+
- tamarin
249+
- proverif
250+
- metamath
251+
- twelf
252+
- ortools
253+
- hol4
254+
- acl2
255+
- scip
256+
- imandra
236257
steps:
237258
- name: Checkout
238259
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
239-
- name: Announce Wave-3 TODO
260+
- name: Install Rust
261+
uses: dtolnay/rust-toolchain@4be9e76fd7c4901c61fb841f559994984270fce7 # stable
262+
- name: Cache Cargo
263+
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2
264+
- name: Provision ${{ matrix.backend }} (best-effort)
265+
continue-on-error: true
240266
run: |
241-
echo "Tier-3 backends (Tamarin, ProVerif, Imandra, SCIP, OR-Tools, HOL4, ACL2, Twelf, Metamath)"
242-
echo "are provisioned via per-backend Containerfiles in Wave-3 of L3."
243-
echo "See ~/Desktop/ECHIDNA-L3-LIVE-PROVER-CI-PROMPT.md for scope."
267+
set -euo pipefail
268+
sudo apt-get update
269+
case "${{ matrix.backend }}" in
270+
tamarin)
271+
# Tamarin publishes prebuilt Linux x86_64 tarballs.
272+
curl -fsSL -o /tmp/tamarin.tar.gz \
273+
https://github.com/tamarin-prover/tamarin-prover/releases/latest/download/tamarin-prover-linux64-ubuntu.tar.gz || \
274+
curl -fsSL -o /tmp/tamarin.tar.gz \
275+
https://github.com/tamarin-prover/tamarin-prover/releases/download/1.10.0/tamarin-prover-1.10.0-linux64-ubuntu.tar.gz
276+
sudo mkdir -p /opt/tamarin
277+
sudo tar xzf /tmp/tamarin.tar.gz -C /opt/tamarin
278+
# The tarball lays down tamarin-prover directly or in a version dir; link whichever we find.
279+
TAMARIN_BIN="$(find /opt/tamarin -type f -name tamarin-prover | head -n 1)"
280+
[ -n "$TAMARIN_BIN" ] && sudo ln -sf "$TAMARIN_BIN" /usr/local/bin/tamarin-prover
281+
;;
282+
proverif)
283+
# ProVerif is in the Ubuntu/Debian main repos.
284+
sudo apt-get install -y proverif
285+
;;
286+
metamath)
287+
# metamath-exe is small; apt-installable on Ubuntu with build fallback.
288+
sudo apt-get install -y metamath || {
289+
sudo apt-get install -y build-essential git autoconf automake
290+
git clone --depth=1 https://github.com/metamath/metamath-exe.git /tmp/mm
291+
(cd /tmp/mm && ./build.sh) || \
292+
(cd /tmp/mm && autoreconf -i && ./configure && make)
293+
sudo install -m 0755 /tmp/mm/metamath /usr/local/bin/metamath
294+
}
295+
;;
296+
twelf)
297+
# Twelf is in Debian with SML/NJ; on Ubuntu it may live in universe.
298+
sudo apt-get install -y twelf || \
299+
echo "twelf unavailable via apt on this runner; test will SKIP."
300+
;;
301+
ortools)
302+
# Google OR-Tools ships prebuilt Linux tarballs. The binary we
303+
# expose to echidna is a small wrapper that invokes the solver CLI.
304+
OR_URL="https://github.com/google/or-tools/releases/latest/download/or-tools_amd64_ubuntu-22.04_cpp_v9.11.4210.tar.gz"
305+
curl -fsSL -o /tmp/ortools.tar.gz "$OR_URL" || {
306+
echo "OR-Tools tarball unavailable at expected URL; test will SKIP."
307+
exit 0
308+
}
309+
sudo mkdir -p /opt/ortools
310+
sudo tar xzf /tmp/ortools.tar.gz -C /opt/ortools --strip-components=1
311+
# Echidna's ORTools backend invokes `ortools_solve`. The release
312+
# ships `bin/solve` or similar; create the expected symlink.
313+
OR_BIN="$(find /opt/ortools -type f \( -name ortools_solve -o -name solve \) | head -n 1)"
314+
[ -n "$OR_BIN" ] && sudo ln -sf "$OR_BIN" /usr/local/bin/ortools_solve
315+
;;
316+
hol4)
317+
# HOL4 requires Poly/ML and a full tree build (~15min+); defer to
318+
# container provisioning. Test step will SKIP on this runner.
319+
echo "hol4: heavy Poly/ML build deferred to Containerfile provisioning."
320+
;;
321+
acl2)
322+
# ACL2 requires a Common Lisp image (CCL/SBCL) and a 10min+ build;
323+
# defer to container provisioning. Test step will SKIP.
324+
echo "acl2: heavy SBCL+image build deferred to Containerfile."
325+
;;
326+
scip)
327+
# SCIP Optimization Suite needs a full cmake build (~10min). The
328+
# upstream tarball requires a form-gated download, so CI defers to
329+
# Containerfile provisioning. Test step will SKIP.
330+
echo "scip: form-gated upstream tarball; deferred to Containerfile."
331+
;;
332+
imandra)
333+
# Imandra is proprietary; no public Linux binary. Handled via
334+
# vendor-supplied container where a licence is available.
335+
echo "imandra: proprietary; no public CI provisioning available."
336+
;;
337+
esac
338+
# Best-effort version probe for the matrix log.
339+
case "${{ matrix.backend }}" in
340+
tamarin) tamarin-prover --version 2>&1 | head -n 1 || true ;;
341+
proverif) proverif --version 2>&1 | head -n 1 || true ;;
342+
metamath) echo exit | metamath 2>&1 | head -n 1 || true ;;
343+
twelf) twelf-server --help 2>&1 | head -n 1 || true ;;
344+
ortools) ortools_solve --help 2>&1 | head -n 1 || true ;;
345+
hol4|acl2|scip|imandra) true ;;
346+
esac
347+
- name: Run live test for ${{ matrix.backend }} (allow-fail while Wave-3 wires up)
348+
continue-on-error: true
349+
run: cargo test --test live_prover_suite --features live-provers -- --nocapture ${{ matrix.backend }}
244350

245351
# ============================================================================
246352
# Tier 4 — quarterly, best-effort / allow-fail.

src/julia/run_training.jl

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@
1010
# data_dir = training_data/
1111
# save_dir = models/neural/
1212
#
13+
# Environment overrides (see below):
14+
# ECHIDNA_MAX_PROOF_STATES — cap on proof states loaded (default 200000 on
15+
# GPU, 50000 on CPU). Set to 0 to disable the cap and consume the entire
16+
# expanded corpus; required when re-baselining after corpus growth.
17+
# ECHIDNA_NUM_EPOCHS — training epochs (default 30).
18+
# ECHIDNA_NUM_NEGATIVES — hard-negative premise samples per example
19+
# (default 20).
20+
#
1321
# This script:
1422
# 1. Loads JSONL training data (proof states + premises)
1523
# 2. Builds vocabulary from the corpus
@@ -74,10 +82,24 @@ println("═══════════════════════
7482
println("Loading training data...")
7583
println("═══════════════════════════════════════════════════════════")
7684

85+
# Default cap: 200k on GPU (enough to exercise the expanded corpus without
86+
# OOM on a 24GB card), 50k on CPU (keeps wall-clock finite). An operator
87+
# re-baselining after corpus growth sets ECHIDNA_MAX_PROOF_STATES=0 to lift
88+
# the cap entirely.
89+
default_cap = has_gpu ? 200_000 : 50_000
90+
cap_env = get(ENV, "ECHIDNA_MAX_PROOF_STATES", "")
91+
max_proof_states = isempty(cap_env) ? default_cap : parse(Int, cap_env)
92+
# `load_training_data` treats any value `<= 0` as "load everything".
93+
cap_label = max_proof_states <= 0 ? "unlimited" : string(max_proof_states)
94+
println(" max_proof_states = $cap_label")
95+
96+
num_negatives = parse(Int, get(ENV, "ECHIDNA_NUM_NEGATIVES", "20"))
97+
println(" num_negatives = $num_negatives")
98+
7799
train_data, val_data, vocab = load_training_data(data_dir;
78100
train_split=0.8f0,
79-
max_proof_states=50000, # Cap for reasonable training time
80-
num_negatives=20
101+
max_proof_states=max_proof_states,
102+
num_negatives=num_negatives,
81103
)
82104

83105
if isempty(train_data.examples)
@@ -101,8 +123,9 @@ println("Model created successfully")
101123
println()
102124

103125
# Configure training
126+
num_epochs = parse(Int, get(ENV, "ECHIDNA_NUM_EPOCHS", "30"))
104127
training_config = TrainingConfig(
105-
num_epochs=30,
128+
num_epochs=num_epochs,
106129
learning_rate=1f-4,
107130
lr_schedule=:cosine,
108131
weight_decay=1f-5,

tests/live_prover_suite.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,15 @@ fn kind_label(kind: ProverKind) -> &'static str {
110110
ProverKind::Dafny => "Dafny",
111111
ProverKind::FStar => "F*",
112112
ProverKind::TLAPS => "TLAPS",
113+
ProverKind::Tamarin => "Tamarin",
114+
ProverKind::ProVerif => "ProVerif",
115+
ProverKind::Metamath => "Metamath",
116+
ProverKind::Twelf => "Twelf",
117+
ProverKind::ORTools => "OR-Tools",
118+
ProverKind::HOL4 => "HOL4",
119+
ProverKind::ACL2 => "ACL2",
120+
ProverKind::SCIP => "SCIP",
121+
ProverKind::Imandra => "Imandra",
113122
_ => "<other>",
114123
}
115124
}
@@ -214,3 +223,65 @@ async fn live_tlaps_version() {
214223
// TLA+ Proof System's prover is `tlapm` (per provers/mod.rs).
215224
assert_version_reachable(ProverKind::TLAPS, "tlapm").await;
216225
}
226+
227+
// ==========================================================================
228+
// Tier 3 — weekly. Upstream-tarball or heavier-build provers. Most of
229+
// these SKIP locally and in PR CI; the weekly tier3 matrix in
230+
// live-provers.yml provisions each binary in its own job before running
231+
// `cargo test ... <backend>`, so only the matching test runs per job.
232+
// ==========================================================================
233+
234+
#[tokio::test]
235+
async fn live_tamarin_version() {
236+
assert_version_reachable(ProverKind::Tamarin, "tamarin-prover").await;
237+
}
238+
239+
#[tokio::test]
240+
async fn live_proverif_version() {
241+
assert_version_reachable(ProverKind::ProVerif, "proverif").await;
242+
}
243+
244+
#[tokio::test]
245+
async fn live_metamath_version() {
246+
assert_version_reachable(ProverKind::Metamath, "metamath").await;
247+
}
248+
249+
#[tokio::test]
250+
async fn live_twelf_version() {
251+
// Twelf's CLI entry is `twelf-server` per provers/mod.rs.
252+
assert_version_reachable(ProverKind::Twelf, "twelf-server").await;
253+
}
254+
255+
#[tokio::test]
256+
async fn live_ortools_version() {
257+
// Echidna's ORTools backend invokes `ortools_solve` (wrapper around the
258+
// OR-Tools C++ solve CLI). Provisioned via upstream tarball.
259+
assert_version_reachable(ProverKind::ORTools, "ortools_solve").await;
260+
}
261+
262+
#[tokio::test]
263+
async fn live_hol4_version() {
264+
// HOL4 requires Poly/ML + a tree build; provisioning is deferred to
265+
// Containerfile. Test SKIPs on runners without `hol` on PATH.
266+
assert_version_reachable(ProverKind::HOL4, "hol").await;
267+
}
268+
269+
#[tokio::test]
270+
async fn live_acl2_version() {
271+
// ACL2 requires a Common Lisp image; provisioning deferred to Containerfile.
272+
assert_version_reachable(ProverKind::ACL2, "acl2").await;
273+
}
274+
275+
#[tokio::test]
276+
async fn live_scip_version() {
277+
// SCIP requires a cmake build of SCIP Optimization Suite; deferred to
278+
// Containerfile. Test SKIPs until provisioned.
279+
assert_version_reachable(ProverKind::SCIP, "scip").await;
280+
}
281+
282+
#[tokio::test]
283+
async fn live_imandra_version() {
284+
// Imandra is proprietary; handled via vendor-supplied container where a
285+
// licence is available. Test SKIPs on public CI.
286+
assert_version_reachable(ProverKind::Imandra, "imandra").await;
287+
}

0 commit comments

Comments
 (0)