PT-16 — Vericoding : la preuve formelle comme récompense

Position dans la série — PT-05 (RLVR) a remplacé la récompense apprise par un vérificateur exact : SymPy pour l’arithmétique, Z3 pour les contraintes. Ce notebook franchit le cran supérieur : le vérificateur n’est plus un oracle partiel (un test qui passe) mais une preuve formelle complète — le programme généré est prouvé conforme à sa spécification. C’est le vericoding (Bursuc et al. 2025, arXiv:2509.22908, résultat R15) : synthèse de programmes formellement vérifiés par LLM, avec boucle de réparation guidée par les erreurs du vérificateur.

Ce notebook exécute le pipeline en entier, en local :

  1. un LLM local (Ollama, Qwen2.5-7B-Instruct quantifié) reçoit la spécification Dafny/Lean d’une tâche du benchmark public vericoding-benchmark (12 504 tâches, licence MIT) ;
  2. il répond par un remplacement de code au format imposé (tableau JSON, une entrée par section à remplir — format exact du papier) ;
  3. le vrai vérificateur tranche : Dafny verify pour la jambe Dafny, le binaire lean (toolchain officielle Lean 4) pour la jambe Lean ;
  4. en cas d’échec, l’erreur du vérificateur est réinjectée — 5 tentatives au total (1 génération + 4 réparations), comme dans le protocole du papier ;
  5. une garde anti-contournement rejette toute tentative utilisant assume, assume {:axiom} (Dafny) ou sorry, native_decide, axiom (Lean) — le pendant exact du gate proof-integrity de ce dépôt (§6).

Ce que ce notebook mesure honnêtement : le taux de succès d’un modèle local de 7 milliards de paramètres sur un échantillon stratifié du benchmark — très en dessous des 82,2 % (Dafny) du papier obtenus avec des modèles de frontière, et c’est précisément l’objet de la leçon : la chaîne spec → génération → preuve → réparation est reproductible sur un portable, mais la capacité du générateur domine le résultat. Aucun nombre affiché n’est fabriqué : tout vient de l’exécution des cellules.

1. Le protocole du papier et le benchmark

Vericoding (Bursuc, Trimponas, Sato, Nikolić — 2025) mesure la capacité des LLM à produire du code vérifié formellement : le modèle ne reçoit pas un problème en langage naturel mais un squelette à sections :

Section Contenu Rôle
<vc-preamble> fonctions / prédicats donnés contexte réutilisable
<vc-helpers> vide le modèle peut y ajouter des fonctions (Dafny)
<vc-spec> signature + requires / ensures la spécification à satisfaire
<vc-code> assume {:axiom} false; le placeholder à remplacer (Dafny)
<vc-definitions> / <vc-theorems> sorry corps + preuves à produire (Lean)

Le protocole : 5 tentatives par tâche — une génération initiale, puis jusqu’à 4 réparations où le message d’erreur du vérificateur est réinjecté dans le prompt. La réponse exigée est un tableau JSON : exactement un remplacement par section à remplir, dans l’ordre du fichier. Le prompt interdit explicitement les contournements ; le papier ajoute un juge LLM contre les spécifications triviales (nous y revenons en §5 avec la tâche LC0033).

Résultats du papier (cités, non mesurés ici) : Dafny 82,2 %, Verus 44,2 %, Lean 26,8 % (union de modèles, 5 tentatives) ; et la vérification seule — le taux auquel le vérificateur accepte la solution de référence — grimpe de 68 % à 96 % entre générations de modèles : c’est la boucle de réparation qui transforme un générateur imparfait en producteur de preuves.

Le benchmark public (Beneficial-AI-Foundation/vericoding-benchmark, MIT) agrège 12 504 tâches de 9 sources (fvapps, apps, numpy_triple, dafnybench, verified_cogen, verina, humaneval, numpy_simple, bignum, clever). Ce notebook embarque un échantillon stratifié fixe (graine 42) : 30 tâches Dafny (QA-propres : qa-issue=0, qa-score >= 0.9) et 12 tâches Lean sans import Mathlib — vérifiables au binaire lean nu, donc exécutables sans lake ni build Mathlib.

# Environnement : Ollama local (LLM), Dafny et Lean (verificateurs reels).
# Degradation AFFICHE (jamais silencieuse) si un outil manque : le banc
# correspondant est saute, les cellules d'analyse le detectent.
import json
import os
import re
import shutil
import subprocess
import tempfile
import time
import urllib.request
from pathlib import Path

OLLAMA_URL = "http://localhost:11434/api/chat"
MODEL_NAME = "qwen2.5:7b-instruct-q4_K_M"
MAX_TURNS = 5  # protocole papier : 1 generation + 4 reparations


def find_dafny():
    exe = shutil.which("dafny") or shutil.which("Dafny")
    if exe:
        return exe
    env = os.getenv("DAFNY_EXE")
    if env and Path(env).exists():
        return env
    for cand in (Path.home() / "AppData" / "Local" / "dafny" / "Dafny.exe",
                 Path(os.getenv("PROGRAMFILES", "C:\\Program Files")) / "Dafny" / "Dafny.exe"):
        if cand.exists():
            return str(cand)
    return None


def ollama_up():
    try:
        with urllib.request.urlopen("http://localhost:11434/api/tags", timeout=3) as r:
            return any(m["model"].startswith("qwen2.5:7b") for m in json.loads(r.read())["models"])
    except Exception:
        return False


DAFNY_EXE = find_dafny()
LEAN_OK = shutil.which("lean") is not None
LLM_OK = ollama_up()
WORKDIR = Path(tempfile.mkdtemp(prefix="vc_pt14_"))
print(f"Dafny  : {Path(DAFNY_EXE).name + ' (PATH/env)' if DAFNY_EXE else 'INTROUVABLE'}")
print(f"Lean   : {'lean via elan (PATH)' if LEAN_OK else 'INTROUVABLE'}")
print(f"Ollama : {MODEL_NAME if LLM_OK else 'INTROUVABLE'}")
print(f"Workdir: repertoire temporaire isole")
print()
print("Verdict env :", "COMPLET" if (DAFNY_EXE and LEAN_OK and LLM_OK) else "DEGRADE",
      "-- un banc ne tourne que si ses outils sont presents.")
Dafny  : Dafny.exe (PATH/env)
Lean   : lean via elan (PATH)
Ollama : qwen2.5:7b-instruct-q4_K_M
Workdir: repertoire temporaire isole

Verdict env : COMPLET -- un banc ne tourne que si ses outils sont presents.
# L'echantillon embarque (stratifie, graine 42) : 30 Dafny + 12 Lean.
# Le benchmark complet fait 16k fichiers ; on embarque les specs tirees.
SAMPLE_DAFNY = ['DA0121', 'DA0026', 'DA0298', 'DA0265', 'DA0240', 'DA0150', 'DT0107', 'DT0634', 'DT0092', 'DT0495', 'DT0034', 'DT0030', 'DD0075', 'DD0168', 'DD0199', 'DD0606', 'DD0723', 'DD0037', 'DJ0081', 'DJ0140', 'DJ0087', 'DJ0147', 'DH0087', 'DH0002', 'DH0050', 'DH0131', 'DV0128', 'DV0108', 'DV0064', 'DV0085']
SAMPLE_LEAN = ['LA0345', 'LA0104', 'LA0094', 'LD0424', 'LD0073', 'LD0373', 'LJ0088', 'LJ0155', 'LV0084', 'LV0013', 'LB0046', 'LS0029']
SPECS = json.loads('{"DA0121": "// <vc-preamble>\\npredicate ValidInput(x: int, y: int, z: int)\\n{\\n  x >= 0 && y >= 0 && z > 0\\n}\\n\\nfunction MaxCoconuts(x: int, y: int, z: int): int\\n  requires ValidInput(x, y, z)\\n{\\n  (x + y) / z\\n}\\n\\nfunction MinExchange(x: int, y: int, z: int): int\\n  requires ValidInput(x, y, z)\\n{\\n  var rx := x % z;\\n  var ry := y % z;\\n  if rx + ry < z then 0\\n  else z - if rx > ry then rx else ry\\n}\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod solve(x: int, y: int, z: int) returns (coconuts: int, exchange: int)\\n  requires ValidInput(x, y, z)\\n  ensures coconuts == MaxCoconuts(x, y, z)\\n  ensures exchange == MinExchange(x, y, z)\\n  ensures coconuts >= x / z + y / z\\n  ensures coconuts <= x / z + y / z + 1\\n  ensures exchange >= 0 && exchange < z\\n// </vc-spec>\\n// <vc-code>\\n{\\n  assume {:axiom} false;\\n}\\n// </vc-code>\\n", "DA0026": "// <vc-preamble>\\npredicate ValidInput(l1: int, r1: int, l2: int, r2: int, k: int) {\\n    l1 <= r1 && l2 <= r2\\n}\\n\\nfunction IntersectionLeft(l1: int, l2: int): int {\\n    if l1 > l2 then l1 else l2\\n}\\n\\nfunction IntersectionRight(r1: int, r2: int): int {\\n    if r1 < r2 then r1 else r2\\n}\\n\\nfunction IntersectionSize(l1: int, r1: int, l2: int, r2: int): int {\\n    var left := IntersectionLeft(l1, l2);\\n    var right := IntersectionRight(r1, r2);\\n    if right - left + 1 > 0 then right - left + 1 else 0\\n}\\n\\npredicate KInIntersection(l1: int, r1: int, l2: int, r2: int, k: int) {\\n    var left := IntersectionLeft(l1, l2);\\n    var right := IntersectionRight(r1, r2);\\n    left <= k <= right\\n}\\n\\nfunction ExpectedResult(l1: int, r1: int, l2: int, r2: int, k: int): int {\\n    var intersection_size := IntersectionSize(l1, r1, l2, r2);\\n    if KInIntersection(l1, r1, l2, r2, k) then\\n        if intersection_size - 1 > 0 then intersection_size - 1 else 0\\n    else\\n        intersection_size\\n}\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod solve(l1: int, r1: int, l2: int, r2: int, k: int) returns (result: int)\\n    requires ValidInput(l1, r1, l2, r2, k)\\n    ensures result == ExpectedResult(l1, r1, l2, r2, k)\\n    ensures result >= 0\\n// </vc-spec>\\n// <vc-code>\\n{\\n  assume {:axiom} false;\\n}\\n// </vc-code>\\n", "DA0298": "// <vc-preamble>\\npredicate ValidPermutation(p: seq<int>, n: int)\\n{\\n  |p| == n && n >= 1 &&\\n  (forall i :: 0 <= i < n ==> 1 <= p[i] <= n) &&\\n  (forall i, j :: 0 <= i < j < n ==> p[i] != p[j])\\n}\\n\\nfunction countRecords(s: seq<int>): int\\n  ensures countRecords(s) >= 0\\n{\\n  if |s| == 0 then 0\\n  else 1 + countRecordsFromIndex(s, 1, s[0])\\n}\\n\\nfunction countRecordsAfterRemoval(p: seq<int>, toRemove: int): int\\n  requires forall i :: 0 <= i < |p| ==> 1 <= p[i] <= |p|\\n  requires forall i, j :: 0 <= i < j < |p| ==> p[i] != p[j]\\n  requires toRemove in p\\n{\\n  var filtered := seq(|p| - 1, i requires 0 <= i < |p| - 1 => \\n    if indexOf(p, toRemove) <= i then p[i + 1] else p[i]);\\n  countRecords(filtered)\\n}\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod solve(n: int, p: seq<int>) returns (result: int)\\n  requires ValidPermutation(p, n)\\n  ensures 1 <= result <= n\\n  ensures result in p\\n  ensures forall x :: x in p ==> countRecordsAfterRemoval(p, result) >= countRecordsAfterRemoval(p, x)\\n  ensures forall x :: x in p && countRecordsAfterRemoval(p, x) == countRecordsAfterRemoval(p, result) ==> result <= x\\n// </vc-spec>\\n// <vc-code>\\n{\\n  assume {:axiom} false;\\n}\\n// </vc-code>\\n", "DA0265": "// <vc-preamble>\\npredicate ValidInput(columns: seq<(int, int)>)\\n{\\n    forall i :: 0 <= i < |columns| ==> columns[i].0 > 0 && columns[i].1 > 0\\n}\\n\\nfunction abs(x: int): int\\n{\\n    if x >= 0 then x else -x\\n}\\n\\nfunction sum_left(columns: seq<(int, int)>): int\\n{\\n    if |columns| == 0 then 0\\n    else columns[0].0 + sum_left(columns[1..])\\n}\\n\\nfunction sum_right(columns: seq<(int, int)>): int\\n{\\n    if |columns| == 0 then 0\\n    else columns[0].1 + sum_right(columns[1..])\\n}\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod solve(columns: seq<(int, int)>) returns (result: int)\\n    requires ValidInput(columns)\\n    ensures 0 <= result <= |columns|\\n    ensures var L := sum_left(columns);\\n            var R := sum_right(columns);\\n            var original_beauty := abs(L - R);\\n            if result == 0 then\\n                forall i :: 0 <= i < |columns| ==> \\n                    var new_L := L - columns[i].0 + columns[i].1;\\n                    var new_R := R - columns[i].1 + columns[i].0;\\n                    abs(new_L - new_R) <= original_beauty\\n            else\\n                1 <= result <= |columns| &&\\n                var best_idx := result - 1;\\n                var best_L := L - columns[best_idx].0 + columns[best_idx].1;\\n                var best_R := R - columns[best_idx].1 + columns[best_idx].0;\\n                var best_beauty := abs(best_L - best_R);\\n                best_beauty > original_beauty &&\\n                forall i :: 0 <= i < |columns| ==> \\n                    var new_L := L - columns[i].0 + columns[i].1;\\n                    var new_R := R - columns[i].1 + columns[i].0;\\n                    abs(new_L - new_R) <= best_beauty\\n// </vc-spec>\\n// <vc-code>\\n{\\n  assume {:axiom} false;\\n}\\n// </vc-code>\\n", "DA0240": "// <vc-preamble>\\nfunction gcd(a: int, b: int): int\\n  requires a > 0 && b >= 0\\n  decreases b\\n{\\n  if b == 0 then a else gcd(b, a % b)\\n}\\n\\npredicate ValidInput(r: int, b: int, k: int)\\n{\\n  r > 0 && b > 0 && k > 0\\n}\\n\\nfunction MaxConsecutiveSameColor(r: int, b: int): int\\n  requires r > 0 && b > 0\\n{\\n  var a := if r <= b then r else b;\\n  var b_val := if r <= b then b else r;\\n  var n := gcd(a, b_val);\\n  -((n - b_val) / a)\\n}\\n\\npredicate CanAvoidConsecutive(r: int, b: int, k: int)\\n  requires ValidInput(r, b, k)\\n{\\n  MaxConsecutiveSameColor(r, b) < k\\n}\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod solve(r: int, b: int, k: int) returns (result: string)\\n  requires ValidInput(r, b, k)\\n  ensures result == (if CanAvoidConsecutive(r, b, k) then \\"OBEY\\" else \\"REBEL\\")\\n// </vc-spec>\\n// <vc-code>\\n{\\n  assume {:axiom} false;\\n}\\n// </vc-code>\\n", "DA0150": "// <vc-preamble>\\npredicate ValidInput(a: int, b: int, c: int, d: int) {\\n    a > 0 && b > 0 && c > 0 && d > 0\\n}\\n\\npredicate IsValidFractionString(s: string, num: int, den: int) {\\n    num >= 0 && den > 0 && \\n    gcd(num, den) == 1 &&\\n    s == intToString(num) + \\"/\\" + intToString(den)\\n}\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod solve(a: int, b: int, c: int, d: int) returns (result: string)\\n    requires ValidInput(a, b, c, d)\\n    ensures a * d == b * c ==> result == \\"0/1\\"\\n    ensures a * d > b * c ==> exists numerator, denominator :: \\n        numerator > 0 && denominator > 0 && \\n        gcd(numerator, denominator) == 1 &&\\n        result == intToString(numerator) + \\"/\\" + intToString(denominator) &&\\n        numerator * a * d == (a * d - b * c) * denominator\\n    ensures a * d < b * c ==> exists numerator, denominator :: \\n        numerator > 0 && denominator > 0 && \\n        gcd(numerator, denominator) == 1 &&\\n        result == intToString(numerator) + \\"/\\" + intToString(denominator) &&\\n        numerator * b * c == (b * c - a * d) * denominator\\n// </vc-spec>\\n// <vc-code>\\n{\\n  assume {:axiom} false;\\n}\\n// </vc-code>\\n", "DT0107": "// <vc-preamble>\\nLooking at the compilation error, the issue is that the `Ln` function is marked as `:opaque` but has no body, making it impossible to compile. I need to provide a body for this function to enable compilation.\\n\\nHere\'s the corrected Dafny code:\\n\\n\\n\\n// Abstract function for natural logarithm\\nfunction {:opaque} Ln(x: real): real\\n  requires x > 0.0\\n{\\n  0.0  // Placeholder implementation for compilation\\n}\\n\\n// Method to get Euler\'s constant e with mathematical properties\\n// Helper function for absolute value of real numbers\\nfunction {:opaque} Abs(x: real): real\\n{\\n  if x >= 0.0 then x else -x\\n}\\n\\nThe key change is adding a placeholder body `{ 0.0 }` to the `Ln` function. This minimal implementation allows the code to compile while preserving all the original specifications and comments.\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod GetEulersConstant() returns (e: real)\\n  ensures 2.718 < e < 2.719\\n  // Mathematical property: e is approximately 2.718281828459045 (NumPy\'s precision)\\n  ensures Abs(e - 2.718281828459045) < 0.000000000000001\\n  // Mathematical property: e is positive\\n  ensures e > 0.0\\n  // Mathematical property: e is greater than 2 but less than 3\\n  ensures 2.0 < e < 3.0\\n  // Mathematical property: More precise bounds based on known rational approximations\\n  // e is between 2.71828182 and 2.71828183\\n  ensures 2.71828182 < e < 2.71828183\\n  // Mathematical property: e > 5/2 and e < 11/4 (classical rational bounds)\\n  ensures e > 2.5 && e < 2.75\\n  // Mathematical property: e is greater than approximation from limit definition\\n  // This approximates the limit definition of e = lim(n→∞) (1 + 1/n)^n\\n  ensures e > 2.71828\\n  // Fundamental mathematical property: ln(e) = 1 (defining property of Euler\'s constant)\\n  ensures Abs(Ln(e) - 1.0) < 0.000000000000001\\n// </vc-spec>\\n// <vc-code>\\n{\\n  assume {:axiom} false;\\n}\\n// </vc-code>\\n", "DT0634": "// <vc-preamble>\\nHere\'s the corrected Dafny code with the trigger issue fixed:\\n\\n\\n\\n// Helper predicate: checks if pattern occurs at specific position in string\\npredicate OccursAt(s: string, pattern: string, pos: nat)\\n{\\n    pos + |pattern| <= |s| && s[pos..pos + |pattern|] == pattern\\n}\\n\\n// Helper predicate: checks if positions represent non-overlapping occurrences\\npredicate NonOverlappingOccurrences(s: string, pattern: string, positions: seq<nat>)\\n{\\n    (forall i :: 0 <= i < |positions| ==> OccursAt(s, pattern, positions[i])) &&\\n    (forall i, j :: 0 <= i < j < |positions| ==> positions[i] < positions[j]) &&\\n    (forall i, j :: 0 <= i < j < |positions| ==> positions[i] + |pattern| <= positions[j])\\n}\\n\\n// Helper predicate: checks if positions represent all possible non-overlapping occurrences\\npredicate AllNonOverlappingOccurrences(s: string, pattern: string, positions: seq<nat>)\\n{\\n    NonOverlappingOccurrences(s, pattern, positions) &&\\n    (forall pos :: 0 <= pos <= |s| - |pattern| && OccursAt(s, pattern, pos) ==>\\n        exists i :: 0 <= i < |positions| && \\n            (positions[i] <= pos < positions[i] + |pattern| || pos == positions[i]))\\n}\\n\\n// Helper function: performs string replacement at given positions\\nfunction ReplaceAtPositions(s: string, pattern: string, replacement: string, positions: seq<nat>): string\\n    requires NonOverlappingOccurrences(s, pattern, positions)\\n    ensures |ReplaceAtPositions(s, pattern, replacement, positions)| >= 0\\n{\\n    if |positions| == 0 then s\\n    else if |pattern| == 0 then s\\n    else\\n        var pos := positions[0];\\n        var before := s[..pos];\\n        var after := s[pos + |pattern|..];\\n        var remaining_positions := seq(|positions| - 1, i requires 0 <= i < |positions| - 1 => positions[i + 1] - |pattern| + |replacement|);\\n        before + replacement + ReplaceAtPositions(after, pattern, replacement, remaining_positions)\\n}\\nThe only change made was adding the explicit trigger `{:trigger NonOverlappingOccurrences(a[i], oldSeq[i], positions)}` to the quantifier on line 59. This tells Dafny to use the `NonOverlappingOccurrences` predicate as a trigger for instantiating this quantifier, which resolves the warning about not finding a trigger.\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod Replace(a: seq<string>, oldSeq: seq<string>, replacement: seq<string>, count: seq<int>) \\n    returns (result: seq<string>)\\n    requires |a| == |oldSeq| == |replacement| == |count|\\n    requires forall i :: 0 <= i < |a| ==> count[i] == 0 || |oldSeq[i]| > 0\\n    ensures |result| == |a|\\n    ensures forall i :: 0 <= i < |a| ==>\\n        // Zero count behavior: if count is 0, no replacements occur\\n        (count[i] == 0 ==> result[i] == a[i]) &&\\n        \\n        // Identity property: if oldSeq doesn\'t occur, result equals original\\n        ((forall pos :: 0 <= pos <= |a[i]| - |oldSeq[i]| ==> !OccursAt(a[i], oldSeq[i], pos)) ==>\\n            result[i] == a[i]) &&\\n        \\n        // Replacement property: result is formed by valid replacements\\n        (exists num_replacements: nat, positions: seq<nat> :: {:trigger NonOverlappingOccurrences(a[i], oldSeq[i], positions)}\\n            |positions| == num_replacements &&\\n            NonOverlappingOccurrences(a[i], oldSeq[i], positions) &&\\n            \\n            // Count limiting: if count >= 0, at most count replacements\\n            (count[i] >= 0 ==> num_replacements <= count[i]) &&\\n            \\n            // Complete replacement: if count < 0, all occurrences replaced\\n            (count[i] < 0 ==> AllNonOverlappingOccurrences(a[i], oldSeq[i], positions)) &&\\n            \\n            // If count >= 0, we take first min(count, total_occurrences) positions\\n            (count[i] >= 0 ==> \\n                exists all_positions: seq<nat> ::\\n                    AllNonOverlappingOccurrences(a[i], oldSeq[i], all_positions) &&\\n                    num_replacements == (if count[i] <= |all_positions| then count[i] else |all_positions|) &&\\n                    positions == all_positions[..num_replacements]) &&\\n            \\n            // Result is the string with replacements applied\\n            result[i] == ReplaceAtPositions(a[i], oldSeq[i], replacement[i], positions))\\n// </vc-spec>\\n// <vc-code>\\n{\\n  assume {:axiom} false;\\n}\\n// </vc-code>\\n", "DT0092": "// <vc-preamble>\\nLooking at the Dafny compilation errors, the issue is that the quantifiers don\'t have triggers, which Dafny requires for verification. I\'ll add explicit triggers to fix this:\\n\\n\\n\\n// Method representing NumPy\'s False_ boolean constant\\nThe fix adds explicit triggers `{:trigger result || b}` and `{:trigger result && b}` to the quantified expressions to resolve the compilation warnings.\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod False_() returns (result: bool)\\n  // The result must be false\\n  ensures result == false\\n  // False_ is the identity element for logical OR: false || b == b for any boolean b  \\n  ensures forall b: bool {:trigger result || b} :: result || b == b\\n  // False_ is the absorbing element for logical AND: false && b == false for any boolean b\\n  ensures forall b: bool {:trigger result && b} :: result && b == false\\n  // False_ is the negation of true\\n  ensures result == !true\\n// </vc-spec>\\n// <vc-code>\\n{\\n  assume {:axiom} false;\\n}\\n// </vc-code>\\n", "DT0495": "// <vc-preamble>\\n// Method to create a Legendre series representation of a straight line\\n// The line is defined as off + scl*x, where off is the y-intercept and scl is the slope\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod legline(off: real, scl: real) returns (result: array<real>)\\n  // The result is always a 2-element array containing the Legendre coefficients\\n  ensures result.Length == 2\\n  // The first coefficient represents the constant term (off)\\n  ensures result[0] == off\\n  // The second coefficient represents the linear term coefficient (scl)  \\n  ensures result[1] == scl\\n  // Ensures the result array is freshly allocated\\n  ensures fresh(result)\\n// </vc-spec>\\n// <vc-code>\\n{\\n  assume {:axiom} false;\\n}\\n// </vc-code>\\n", "DT0034": "// <vc-preamble>\\n// Ghost function for real number exponentiation with natural number exponents\\nghost function Pow(base: real, exp: nat): real\\n    decreases exp\\n{\\n    if exp == 0 then 1.0\\n    else base * Pow(base, exp - 1)\\n}\\n\\n// Generate a Vandermonde matrix with decreasing powers (default behavior)\\n// The Vandermonde matrix is a matrix with terms of a geometric progression in each row\\n// For input vector x of length n and m columns, entry (i,j) = x[i]^(m-1-j)\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod Vander(x: seq<real>, m: nat) returns (result: seq<seq<real>>)\\n    requires m > 0\\n    ensures |result| == |x|\\n    ensures forall i :: 0 <= i < |result| ==> |result[i]| == m\\n    ensures forall i, j :: 0 <= i < |x| && 0 <= j < m ==> \\n            result[i][j] == Pow(x[i], (m - 1 - j) as nat)\\n// </vc-spec>\\n// <vc-code>\\n{\\n  assume {:axiom} false;\\n}\\n// </vc-code>\\n", "DT0030": "// <vc-preamble>\\n// Method that creates a sequence of ones with the same length as input\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod OnesLike<T>(a: seq<T>, one: T) returns (result: seq<T>)\\n  // Postcondition: result has same length as input\\n  ensures |result| == |a|\\n  // Postcondition: every element in result is the \\"one\\" value\\n  ensures forall i :: 0 <= i < |result| ==> result[i] == one\\n// </vc-spec>\\n// <vc-code>\\n{\\n  assume {:axiom} false;\\n}\\n// </vc-code>\\n", "DD0075": "// <vc-preamble>\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod MultipleReturns(x: int, y: int) returns (more: int, less: int)\\n  ensures more == x+y\\n  ensures less == x-y\\n// </vc-spec>\\n// <vc-code>\\n{\\n  assume {:axiom} false;\\n}\\n// </vc-code>\\n", "DD0168": "// <vc-preamble>\\npredicate  odd(n: nat) { n % 2 == 1 }\\npredicate  even(n: nat) { n % 2 == 0 }\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod partitionOddEven(a: array<nat>) \\n  modifies a\\n  ensures multiset(a[..]) == multiset(old(a[..]))\\n  ensures ! exists i, j :: 0 <= i < j < a.Length && even(a[i]) && odd(a[j])\\n// </vc-spec>\\n// <vc-code>\\n{\\n  assume {:axiom} false;\\n}\\n// </vc-code>\\n", "DD0199": "// <vc-preamble>\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod FindZero(a: array<int>) returns (index: int)\\n   requires a != null\\n   requires forall i :: 0 <= i < a.Length ==> 0 <= a[i]\\n   requires forall i :: 0 < i < a.Length ==> a[i-1]-1 <= a[i]\\n   ensures index < 0  ==> forall i :: 0 <= i < a.Length ==> a[i] != 0\\n   ensures 0 <= index ==> index < a.Length && a[index] == 0\\n// </vc-spec>\\n// <vc-code>\\n{\\n  assume {:axiom} false;\\n}\\n// </vc-code>\\n", "DD0606": "// <vc-preamble>\\nfunction Factorial(n: nat): nat\\n{\\n  if n == 0 then 1 else n * Factorial(n-1)\\n}\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod ComputeFactorial(n: int) returns (u: int)\\n  requires 1 <= n;\\n  ensures u == Factorial(n);\\n// </vc-spec>\\n// <vc-code>\\n{\\n  assume {:axiom} false;\\n}\\n// </vc-code>\\n", "DD0723": "// <vc-preamble>\\npredicate IsNegative(n: int)\\n{\\n    n < 0\\n}\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod FindNegativeNumbers(arr: array<int>) returns (negativeList: seq<int>)\\n\\n    ensures forall i :: 0 <= i < |negativeList| ==> IsNegative(negativeList[i]) && negativeList[i] in arr[..]\\n\\n    ensures forall i :: 0 <= i < arr.Length && IsNegative(arr[i]) ==> arr[i] in negativeList\\n// </vc-spec>\\n// <vc-code>\\n{\\n  assume {:axiom} false;\\n}\\n// </vc-code>\\n", "DD0037": "// <vc-preamble>\\nfunction sum (a:array<int>, i:int, j:int) :int\\ndecreases j\\nreads a\\nrequires 0 <= i <= j <= a.Length\\n{\\n    if i == j then\\n        0\\n    else\\n        a[j-1] + sum(a, i, j-1)\\n}\\n\\npredicate is_prefix_sum_for (a:array<int>, c:array<int>)\\nreads c, a\\n{\\n    a.Length + 1 == c.Length\\n    && c[0] == 0\\n    && forall j :: 1 <= j <= a.Length ==> c[j] == sum(a,0,j)\\n}\\n\\ndatatype List<T> = Nil | Cons(head: T, tail: List<T>)\\n\\nmethod from_array<T>(a: array<T>) returns (l: List<T>)\\nrequires a.Length > 0\\nensures forall j::0 <= j < a.Length ==> mem(a[j],l)\\n{\\n  assume{:axiom} false;\\n}\\n\\nfunction mem<T(==)> (x: T, l:List<T>) : bool\\ndecreases l\\n{\\n    match l\\n    case Nil => false\\n    case Cons(y,r)=> if (x==y) then true else mem(x,r)\\n}\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod queryFast (a:array<int>, c:array<int>, i:int, j:int) returns (r:int)\\nrequires is_prefix_sum_for(a,c) && 0 <= i <= j <= a.Length < c.Length\\nensures r == sum(a, i,j)\\n// </vc-spec>\\n// <vc-code>\\n{\\n  assume {:axiom} false;\\n}\\n// </vc-code>\\n", "DJ0081": "// <vc-preamble>\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod ListDeepClone(arr: array<int>) returns (copied: array<int>)\\n    ensures arr.Length == copied.Length\\n    ensures forall i :: 0 <= i < arr.Length ==> arr[i] == copied[i]\\n// </vc-spec>\\n// <vc-code>\\n{\\n    assume {:axiom} false;\\n}\\n// </vc-code>\\n", "DJ0140": "// <vc-preamble>\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod Barrier(arr: array<int>, p: int) returns (result: bool)\\n    requires\\n        arr.Length > 0 &&\\n        0 <= p < arr.Length\\n    ensures\\n        result == forall k, l :: 0 <= k <= p && p < l < arr.Length ==> arr[k] < arr[l]\\n// </vc-spec>\\n// <vc-code>\\n{\\n    assume {:axiom} false;\\n}\\n// </vc-code>\\n", "DJ0087": "// <vc-preamble>\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod HasCommonElement(list1: array<int>, list2: array<int>) returns (result: bool)\\n    ensures\\n        result == (exists i: int, j: int ::\\n            0 <= i < list1.Length && 0 <= j < list2.Length && (list1[i] == list2[j]))\\n// </vc-spec>\\n// <vc-code>\\n{\\n    assume {:axiom} false;\\n}\\n// </vc-code>\\n", "DJ0147": "// <vc-preamble>\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod IntegerSquareRoot(n: int) returns (result: int)\\n    requires n >= 1\\n    ensures 0 <= result * result\\n    ensures result * result <= n\\n    ensures n < (result + 1) * (result + 1)\\n// </vc-spec>\\n// <vc-code>\\n{\\n    assume {:axiom} false;\\n}\\n// </vc-code>\\n", "DH0087": "// <vc-preamble>\\nfunction sumc(s: seq<int>, p: seq<bool>) : int\\n    requires |s| == |p|\\n    {\\n        if |s| == 0 then 0 else (if p[0] then s[0] else 0) + sumc(s[1..], p[1..])\\n    }\\nfunction add_conditon(lst: seq<int>) : (p : seq<bool>)\\n    ensures |lst| == |p|\\n    {\\n        seq(|lst|, i requires 0 <= i < |lst| => i % 2 == 1 && lst[i] % 2 == 0)\\n    }\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod add(v: seq<int>) returns (r : int)\\n\\n    ensures r == sumc(v, add_conditon(v))\\n// </vc-spec>\\n// <vc-code>\\n{\\n    assume {:axiom} false;\\n  }\\n// </vc-code>\\n", "DH0002": "// <vc-preamble>\\n\\npredicate ValidInput(number: real)\\n{\\n    number >= 0.0\\n}\\n\\npredicate ValidOutput(result: real, input: real)\\n{\\n    0.0 <= result < 1.0 && result == input - Floor(input)\\n}\\n\\nfunction Floor(x: real): real\\n    ensures Floor(x) <= x < Floor(x) + 1.0\\n{\\n    if x >= 0.0 then\\n        FloorNonnegative(x)\\n    else\\n        -CeilNonnegative(-x)\\n}\\n\\nfunction FloorNonnegative(x: real): real\\n    requires x >= 0.0\\n    ensures FloorNonnegative(x) <= x < FloorNonnegative(x) + 1.0\\n    ensures FloorNonnegative(x) >= 0.0\\n{\\n    FloorHelper(x, 0)\\n}\\n\\nfunction FloorHelper(x: real, n: int): real\\n    requires x >= 0.0\\n    requires n >= 0\\n    ensures FloorHelper(x, n) <= x + n as real < FloorHelper(x, n) + 1.0\\n    ensures FloorHelper(x, n) >= n as real\\n    decreases x\\n{\\n    if x < 1.0 then \\n        n as real\\n    else \\n        FloorHelper(x - 1.0, n + 1)\\n}\\n\\nfunction CeilNonnegative(x: real): real\\n    requires x >= 0.0\\n    ensures CeilNonnegative(x) >= x\\n    ensures x > 0.0 ==> CeilNonnegative(x) < x + 1.0\\n{\\n    if x == 0.0 then \\n        0.0\\n    else if FloorNonnegative(x) == x then\\n        x\\n    else\\n        FloorNonnegative(x) + 1.0\\n}\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod truncate_number(number: real) returns (result: real)\\n    requires ValidInput(number)\\n    ensures ValidOutput(result, number)\\n// </vc-spec>\\n// <vc-code>\\n{\\n    assume {:axiom} false;\\n  }\\n// </vc-code>\\n", "DH0050": "// <vc-preamble>\\n\\nfunction to_lower(c: char): char\\n{\\n    if \'A\' <= c <= \'Z\' then\\n        (c as int - \'A\' as int + \'a\' as int) as char\\n    else\\n        c\\n}\\n\\npredicate IsPalindrome(text: string)\\n{\\n    forall i :: 0 <= i < |text| ==> to_lower(text[i]) == to_lower(text[|text| - 1 - i])\\n}\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod is_palindrome(text: string) returns (result: bool)\\n  ensures result <==> IsPalindrome(text)\\n// </vc-spec>\\n// <vc-code>\\n{\\n    assume {:axiom} false;\\n  }\\n// </vc-code>\\n", "DH0131": "// <vc-preamble>\\nfunction IsPrime(n: nat) : bool\\n{\\n  n > 1 &&\\n  forall k :: 2 <= k < n ==> n % k != 0\\n}\\nfunction min(a: int, b: int): int\\n{\\n  if a <= b then a else b\\n}\\nfunction max(a: int, b: int): int\\n{\\n  if a >= b then a else b\\n}\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod Intersection(start1: int, end1: int, start2: int, end2: int) returns (result: string)\\n\\n  requires start1 <= end1 && start2 <= end2\\n\\n  ensures result == \\"YES\\" || result == \\"NO\\"\\n  ensures result == \\"YES\\" <==>\\n    (max(start1, start2) <= min(end1, end2) &&\\n     IsPrime((min(end1, end2) - max(start1, start2) + 1) as nat))\\n// </vc-spec>\\n// <vc-code>\\n{\\n    assume {:axiom} false;\\n  }\\n// </vc-code>\\n", "DV0128": "// <vc-preamble>\\nghost predicate IsPerfectSquare(n: nat)\\n{\\n    exists i: nat :: i * i == n\\n}\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod IsPerfectSquareFn(n: int) returns (result: bool)\\n    requires n >= 0\\n    ensures result <==> IsPerfectSquare(n as nat)\\n// </vc-spec>\\n// <vc-code>\\n{\\n    // impl-start\\n    assume {:axiom} false;\\n    result := false;\\n    // impl-end\\n}\\n// </vc-code>\\n", "DV0108": "// <vc-preamble>\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod IsPrime(n: nat) returns (result: bool)\\n    requires n >= 2\\n    ensures result ==> forall k: nat :: 2 <= k < n ==> n % k != 0\\n    ensures !result ==> exists k: nat :: 2 <= k < n && n % k == 0\\n// </vc-spec>\\n// <vc-code>\\n{\\n    // impl-start\\n    assume {:axiom} false;\\n    result := false;\\n    // impl-end\\n}\\n// </vc-code>\\n", "DV0064": "// <vc-preamble>\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod ReverseString(s: array<char>) returns (result: array<char>)\\n    ensures\\n        result.Length == s.Length &&\\n        forall i :: 0 <= i < s.Length ==> result[i] == s[s.Length - 1 - i]\\n// </vc-spec>\\n// <vc-code>\\n{\\n    // impl-start\\n    assume {:axiom} false;\\n    result := new char[0];\\n    // impl-end\\n}\\n// </vc-code>\\n", "DV0085": "// <vc-preamble>\\n// </vc-preamble>\\n\\n// <vc-helpers>\\n// </vc-helpers>\\n\\n// <vc-spec>\\nmethod multiply(a: int, b: int) returns (result: int)\\n    ensures result == a * b\\n// </vc-spec>\\n// <vc-code>\\n{\\n    // impl-start\\n    assume {:axiom} false;\\n    result := 0;\\n    // impl-end\\n}\\n// </vc-code>\\n", "LA0345": "-- <vc-preamble>\\ndef ValidInput (cards : List Int) : Prop :=\\n  cards.length ≥ 1 ∧\\n  (∀ i, 0 ≤ i ∧ i < cards.length → cards[i]! > 0) ∧\\n  (∀ i j, 0 ≤ i ∧ i < j ∧ j < cards.length → cards[i]! ≠ cards[j]!)\\n\\ndef sum (cards : List Int) : Int :=\\n  cards.sum\\n\\ndef sereja_optimal_score (cards : List Int) (left : Int) (right : Int) (sereja_turn : Bool) : Int :=\\n  if h : 0 ≤ left ∧ left ≤ right ∧ right < cards.length then\\n    if left = right then\\n      if sereja_turn then cards[left.toNat]! else 0\\n    else if cards[left.toNat]! > cards[right.toNat]! then\\n      (if sereja_turn then cards[left.toNat]! else 0) + sereja_optimal_score cards (left+1) right (!sereja_turn)\\n    else\\n      (if sereja_turn then cards[right.toNat]! else 0) + sereja_optimal_score cards left (right-1) (!sereja_turn)\\n  else 0\\ntermination_by (right - left + 1).toNat\\n\\ndef ValidOutput (scores : List Int) (cards : List Int) : Prop :=\\n  scores.length = 2 ∧\\n  scores[0]! ≥ 0 ∧ scores[1]! ≥ 0 ∧\\n  scores[0]! + scores[1]! = sum cards ∧\\n  scores[0]! = sereja_optimal_score cards 0 (cards.length - 1) true ∧\\n  scores[1]! = sum cards - sereja_optimal_score cards 0 (cards.length - 1) true\\n\\n@[reducible, simp]\\ndef solve_precond (cards : List Int) : Prop :=\\n  ValidInput cards\\n-- </vc-preamble>\\n\\n-- <vc-helpers>\\n-- </vc-helpers>\\n\\n-- <vc-definitions>\\ndef solve (cards : List Int) (h_precond : solve_precond cards) : List Int :=\\n  sorry\\n-- </vc-definitions>\\n\\n-- <vc-theorems>\\n@[reducible, simp]\\ndef solve_postcond (cards : List Int) (scores : List Int) (h_precond : solve_precond cards) : Prop :=\\n  ValidOutput scores cards\\n\\ntheorem solve_spec_satisfied (cards : List Int) (h_precond : solve_precond cards) :\\n    solve_postcond cards (solve cards h_precond) h_precond := by\\n  sorry\\n-- </vc-theorems>", "LA0104": "-- <vc-preamble>\\ndef ValidInput (a1 a2 k1 k2 n : Int) : Prop :=\\n  a1 ≥ 1 ∧ a2 ≥ 1 ∧ k1 ≥ 1 ∧ k2 ≥ 1 ∧ n ≥ 1\\n\\ndef MinimumSentOff (a1 a2 k1 k2 n : Int) (h : ValidInput a1 a2 k1 k2 n) : Int :=\\n  let max_non_sendoff_cards := (k1 - 1) * a1 + (k2 - 1) * a2\\n  if n - max_non_sendoff_cards > 0 then n - max_non_sendoff_cards else 0\\n\\ndef MaximumSentOff (a1 a2 k1 k2 n : Int) (h : ValidInput a1 a2 k1 k2 n) : Int :=\\n  if k1 < k2 then\\n    let team1_sent := if n / k1 < a1 then n / k1 else a1\\n    let remaining_cards := n - team1_sent * k1\\n    team1_sent + remaining_cards / k2\\n  else\\n    let team2_sent := if n / k2 < a2 then n / k2 else a2\\n    let remaining_cards := n - team2_sent * k2\\n    team2_sent + remaining_cards / k1\\n\\ndef ValidResult (a1 a2 k1 k2 n minimum maximum : Int) (h : ValidInput a1 a2 k1 k2 n) : Prop :=\\n  minimum ≥ 0 ∧ maximum ≥ 0 ∧\\n  minimum ≤ maximum ∧\\n  maximum ≤ a1 + a2 ∧\\n  minimum ≤ n ∧\\n  maximum ≤ n ∧\\n  minimum = MinimumSentOff a1 a2 k1 k2 n h ∧\\n  maximum = MaximumSentOff a1 a2 k1 k2 n h\\n\\n@[reducible, simp]\\ndef solve_precond (a1 a2 k1 k2 n : Int) : Prop :=\\n  ValidInput a1 a2 k1 k2 n\\n-- </vc-preamble>\\n\\n-- <vc-helpers>\\n-- </vc-helpers>\\n\\n-- <vc-definitions>\\ndef solve (a1 a2 k1 k2 n : Int) (h_precond : solve_precond a1 a2 k1 k2 n) : Int × Int :=\\n  sorry\\n-- </vc-definitions>\\n\\n-- <vc-theorems>\\n@[reducible, simp]\\ndef solve_postcond (a1 a2 k1 k2 n : Int) (result: Int × Int) (h_precond : solve_precond a1 a2 k1 k2 n) : Prop :=\\n  ValidResult a1 a2 k1 k2 n result.1 result.2 h_precond\\n\\ntheorem solve_spec_satisfied (a1 a2 k1 k2 n : Int) (h_precond : solve_precond a1 a2 k1 k2 n) :\\n    solve_postcond a1 a2 k1 k2 n (solve a1 a2 k1 k2 n h_precond) h_precond := by\\n  sorry\\n-- </vc-theorems>", "LA0094": "-- <vc-preamble>\\ndef CharToPosSpec (c : String) : Int :=\\n  if c == \\"v\\" then 0\\n  else if c == \\">\\" then 1\\n  else if c == \\"^\\" then 2\\n  else if c == \\"<\\" then 3\\n  else 0\\n\\npartial def FindNewline (s : String) (start : Nat) : Nat :=\\n  if start >= s.length then s.length\\n  else if s.data[start]! == \'\\\\n\' then start\\n  else FindNewline s (start + 1)\\n\\npartial def SplitLinesSpec (s : String) : List String :=\\n  if s.length == 0 then []\\n  else\\n    let i := FindNewline s 0\\n    if i == s.length then [s]\\n    else [s.take i] ++ SplitLinesSpec (s.drop (i+1))\\n\\npartial def FindSpace (s : String) (start : Nat) : Nat :=\\n  if start >= s.length then s.length\\n  else if s.data[start]! == \' \' then start\\n  else FindSpace s (start + 1)\\n\\npartial def SplitBySpaceSpec (s : String) : List String :=\\n  if s.length == 0 then []\\n  else\\n    let i := FindSpace s 0\\n    if i == s.length then [s]\\n    else [s.take i] ++ SplitBySpaceSpec (s.drop (i+1))\\n\\npartial def StringToIntHelper (s : String) (pos : Nat) (acc : Int) (negative : Bool) : Int :=\\n  if pos >= s.length then (if negative then -acc else acc)\\n  else if pos == 0 && s.data[pos]! == \'-\' then StringToIntHelper s (pos + 1) acc true\\n  else if \'0\' ≤ s.data[pos]! && s.data[pos]! ≤ \'9\' then \\n    StringToIntHelper s (pos + 1) (acc * 10 + (s.data[pos]!).toNat - \'0\'.toNat) negative\\n  else StringToIntHelper s (pos + 1) acc negative\\n\\ndef StringToIntSpec (s : String) : Int :=\\n  StringToIntHelper s 0 0 false\\n\\ndef ValidInput (input : String) : Prop :=\\n  input.length > 0\\n\\ndef ValidOutput (result : String) : Prop :=\\n  result == \\"cw\\" ∨ result == \\"ccw\\" ∨ result == \\"undefined\\"\\n\\n@[reducible, simp]\\ndef solve_precond (input : String) : Prop :=\\n  ValidInput input\\n-- </vc-preamble>\\n\\n-- <vc-helpers>\\n-- </vc-helpers>\\n\\n-- <vc-definitions>\\ndef solve (input : String) (h_precond : solve_precond input) : String :=\\n  sorry\\n-- </vc-definitions>\\n\\n-- <vc-theorems>\\n@[reducible, simp]\\ndef solve_postcond (input : String) (result : String) (h_precond : solve_precond input) : Prop :=\\n  ValidOutput result ∧\\n  (input.length > 0 → (\\n    let lines := SplitLinesSpec input\\n    lines.length ≥ 2 → (\\n      let positions := SplitBySpaceSpec lines[0]!\\n      positions.length ≥ 2 → (\\n        let startChar := positions[0]!\\n        let endChar := positions[1]!\\n        let n := StringToIntSpec lines[1]!\\n        let startPos := CharToPosSpec startChar\\n        let endPos := CharToPosSpec endChar\\n        let ccw := (startPos + n) % 4 = endPos\\n        let cw := (startPos - n) % 4 = endPos\\n        (cw ∧ ¬ccw → result = \\"cw\\") ∧\\n        (ccw ∧ ¬cw → result = \\"ccw\\") ∧\\n        (¬(cw ∧ ¬ccw) ∧ ¬(ccw ∧ ¬cw) → result = \\"undefined\\")\\n      )\\n    )\\n  ))\\n\\ntheorem solve_spec_satisfied (input : String) (h_precond : solve_precond input) :\\n    solve_postcond input (solve input h_precond) h_precond := by\\n  sorry\\n-- </vc-theorems>", "LD0424": "-- <vc-preamble>\\ndef valid_permut (a b : Array Int) : Prop :=\\na.size = b.size ∧ a.toList = b.toList\\ndef sorted (a : Array Int) : Prop :=\\n∀ i j, 0 ≤ i → i ≤ j → j < a.size → a[i]! ≤ a[j]!\\n-- </vc-preamble>\\n\\n-- <vc-helpers>\\n-- </vc-helpers>\\n\\n-- <vc-definitions>\\ndef swap (a : Array Int) (i j : Int) : Array Int :=\\nsorry\\n-- </vc-definitions>\\n\\n-- <vc-theorems>\\ntheorem swap_spec (a : Array Int) (i j : Nat) :\\n0 ≤ i → i < a.size → 0 ≤ j → j < a.size →\\nlet result := swap a i j\\n\\n-- Result is a valid permutation\\n\\nvalid_permut result a ∧\\n\\n-- Elements are swapped correctly\\n\\nresult.size = a.size ∧\\n\\nresult[i]! = a[j]! ∧\\n\\nresult[j]! = a[i]! ∧\\n\\n-- Other elements remain unchanged\\n\\n(∀ k, 0 ≤ k → k < a.size → k ≠ i → k ≠ j → result[k]! = a[k]!) :=\\nsorry\\n-- </vc-theorems>", "LD0073": "-- <vc-preamble>\\n-- </vc-preamble>\\n\\n-- <vc-helpers>\\n-- </vc-helpers>\\n\\n-- <vc-definitions>\\ndef Min_ (x y : Int) : Int :=\\nsorry\\n-- </vc-definitions>\\n\\n-- <vc-theorems>\\ntheorem Min_spec (x y z : Int) :\\nz = Min_ x y →\\n((x ≤ y → z = x) ∧\\n(x > y → z = y)) :=\\nsorry\\n-- </vc-theorems>", "LD0373": "-- <vc-preamble>\\ndef NChoose2 (n : Int) : Int :=\\nn * (n - 1) / 2\\n-- </vc-preamble>\\n\\n-- <vc-helpers>\\n-- </vc-helpers>\\n\\n-- <vc-definitions>\\ndef BubbleSort (a : Array Int) : Nat :=\\nsorry\\n-- </vc-definitions>\\n\\n-- <vc-theorems>\\ntheorem bubbleSort_spec (a : Array Int) (n : Nat) :\\nn ≤ NChoose2 a.size :=\\nsorry\\n-- </vc-theorems>", "LJ0088": "-- <vc-preamble>\\n@[reducible, simp]\\ndef isGreater_precond (arr : Array Int) (number : Int) : Prop :=\\n  True\\n-- </vc-preamble>\\n\\n-- <vc-helpers>\\n-- </vc-helpers>\\n\\n-- <vc-definitions>\\ndef isGreater (arr : Array Int) (number : Int) (h_precond : isGreater_precond arr number) : Bool :=\\n  sorry\\n-- </vc-definitions>\\n\\n-- <vc-theorems>\\n@[reducible, simp]\\ndef isGreater_postcond (arr : Array Int) (number : Int) (result: Bool) (h_precond : isGreater_precond arr number) :=\\n  (∀ i, i < arr.size → number > arr[i]!) ↔ result\\n\\ntheorem isGreater_spec_satisfied (arr: Array Int) (number: Int) (h_precond : isGreater_precond arr number) :\\n    isGreater_postcond arr number (isGreater arr number h_precond) h_precond := by\\n  sorry\\n-- </vc-theorems>", "LJ0155": "-- <vc-preamble>\\n@[reducible, simp]\\ndef removeDuplicates_precond (a : Array Int) := a.size ≥ 1\\n\\ndef inArray (a : Array Int) (x : Int) : Prop :=\\n  ∃ i, i < a.size ∧ a[i]! = x\\n-- </vc-preamble>\\n\\n-- <vc-helpers>\\n-- </vc-helpers>\\n\\n-- <vc-definitions>\\ndef removeDuplicates (a : Array Int) (h_precond : removeDuplicates_precond a) : Array Int :=\\n  sorry\\n-- </vc-definitions>\\n\\n-- <vc-theorems>\\n@[reducible, simp]\\ndef removeDuplicates_postcond (a : Array Int) (result: Array Int) (h_precond : removeDuplicates_precond a) :=\\n  (∀ i, i < result.size → inArray a result[i]!) ∧ \\n  (∀ i j, i < j → j < result.size → result[i]! ≠ result[j]!)\\n\\ntheorem removeDuplicates_spec_satisfied (a: Array Int) (h_precond : removeDuplicates_precond a) :\\n    removeDuplicates_postcond a (removeDuplicates a h_precond) h_precond := by\\n  sorry\\n-- </vc-theorems>", "LV0084": "-- <vc-preamble>\\n@[reducible, simp]\\ndef kthElement_precond (arr : Array Int) (k : Nat) : Prop :=\\n  k ≥ 1 ∧ k ≤ arr.size\\n-- </vc-preamble>\\n\\n-- <vc-helpers>\\n-- </vc-helpers>\\n\\n-- <vc-definitions>\\ndef kthElement (arr : Array Int) (k : Nat) (h_precond : kthElement_precond (arr) (k)) : Int :=\\n  sorry\\n-- </vc-definitions>\\n\\n-- <vc-theorems>\\n@[reducible, simp]\\ndef kthElement_postcond (arr : Array Int) (k : Nat) (result: Int) (h_precond : kthElement_precond (arr) (k)) :=\\n  arr.any (fun x => x = result ∧ x = arr[k - 1]!)\\n\\ntheorem kthElement_spec_satisfied (arr: Array Int) (k: Nat) (h_precond : kthElement_precond (arr) (k)) :\\n    kthElement_postcond (arr) (k) (kthElement (arr) (k) h_precond) h_precond := by\\n  sorry\\n-- </vc-theorems>", "LV0013": "-- <vc-preamble>\\n@[reducible]\\ndef ifPowerOfFour_precond (n : Nat) : Prop :=\\n  True\\n-- </vc-preamble>\\n\\n-- <vc-helpers>\\n-- </vc-helpers>\\n\\n-- <vc-definitions>\\ndef ifPowerOfFour (n : Nat) (h_precond : ifPowerOfFour_precond (n)) : Bool :=\\n  sorry\\n-- </vc-definitions>\\n\\n-- <vc-theorems>\\n@[reducible]\\ndef ifPowerOfFour_postcond (n : Nat) (result: Bool) (h_precond : ifPowerOfFour_precond (n)) : Prop :=\\n  result ↔ (∃ m:Nat, n=4^m)\\n\\ntheorem ifPowerOfFour_spec_satisfied (n: Nat) (h_precond : ifPowerOfFour_precond (n)) :\\n    ifPowerOfFour_postcond (n) (ifPowerOfFour (n) h_precond) h_precond := by\\n  sorry\\n-- </vc-theorems>", "LB0046": "-- <vc-preamble>\\ndef ValidBitString (s : String) : Prop :=\\n  ∀ {i c}, s.get? i = some c → (c = \'0\' ∨ c = \'1\')\\n\\ndef Str2Int (s : String) : Nat :=\\n  s.data.foldl (fun acc ch => 2 * acc + (if ch = \'1\' then 1 else 0)) 0\\n\\ndef Exp_int (x y : Nat) : Nat :=\\n  if y = 0 then 1 else x * Exp_int x (y - 1)\\n\\ndef ModExpPow2 (sx sy : String) (n : Nat) (sz : String) : String :=\\n  sorry\\n\\naxiom ModExpPow2_spec (sx sy : String) (n : Nat) (sz : String)\\n  (hx : ValidBitString sx) (hy : ValidBitString sy) (hz : ValidBitString sz)\\n  (hsy_pow2 : Str2Int sy = Exp_int 2 n ∨ Str2Int sy = 0)\\n  (hsy_len : sy.length = n + 1)\\n  (hsz_gt1 : Str2Int sz > 1) :\\n  ValidBitString (ModExpPow2 sx sy n sz) ∧\\n  Str2Int (ModExpPow2 sx sy n sz) = Exp_int (Str2Int sx) (Str2Int sy) % Str2Int sz\\n\\ndef Mul_ (s1 s2 : String) : String :=\\n  sorry\\n\\naxiom Mul_spec (s1 s2 : String) (h1 : ValidBitString s1) (h2 : ValidBitString s2) :\\n  ValidBitString (Mul_ s1 s2) ∧ Str2Int (Mul_ s1 s2) = Str2Int s1 * Str2Int s2\\n-- </vc-preamble>\\n\\n-- <vc-helpers>\\n-- </vc-helpers>\\n\\n-- <vc-definitions>\\ndef ModExp (sx sy sz : String) : String :=\\n  sorry\\n-- </vc-definitions>\\n\\n-- <vc-theorems>\\ntheorem ModExp_spec (sx sy sz : String) (hx : ValidBitString sx) (hy : ValidBitString sy) (hz : ValidBitString sz)\\n  (hsy_pos : sy.length > 0) (hsz_gt1 : Str2Int sz > 1) :\\n  ValidBitString (ModExp sx sy sz) ∧\\n  Str2Int (ModExp sx sy sz) = Exp_int (Str2Int sx) (Str2Int sy) % Str2Int sz := by\\n  sorry\\n-- </vc-theorems>", "LS0029": "-- <vc-preamble>\\n-- </vc-preamble>\\n\\n-- <vc-helpers>\\n-- </vc-helpers>\\n\\n-- <vc-definitions>\\ndef lcmInt (a b : Int) : Int :=\\nsorry\\n-- </vc-definitions>\\n\\n-- <vc-theorems>\\ntheorem lcmInt_spec (a b : Int) :\\n  lcmInt a b ≥ 0 ∧\\n  lcmInt a b % a = 0 ∧\\n  lcmInt a b % b = 0 ∧\\n  ∀ m : Int, m > 0 → m % a = 0 → m % b = 0 → lcmInt a b ≤ m :=\\nsorry\\n-- </vc-theorems>"}')

assert len(SAMPLE_DAFNY) == 30 and len(SAMPLE_LEAN) == 12
assert set(SPECS) == set(SAMPLE_DAFNY) | set(SAMPLE_LEAN)
print(f"{len(SPECS)} specs embarquees : {len(SAMPLE_DAFNY)} Dafny + {len(SAMPLE_LEAN)} Lean")
apercu = SPECS[SAMPLE_DAFNY[0]]
print(f"\n--- {SAMPLE_DAFNY[0]}_specs.dfy ({len(apercu)} caracteres) ---")
print(apercu[:520])
42 specs embarquees : 30 Dafny + 12 Lean

--- DA0121_specs.dfy (825 caracteres) ---
// <vc-preamble>
predicate ValidInput(x: int, y: int, z: int)
{
  x >= 0 && y >= 0 && z > 0
}

function MaxCoconuts(x: int, y: int, z: int): int
  requires ValidInput(x, y, z)
{
  (x + y) / z
}

function MinExchange(x: int, y: int, z: int): int
  requires ValidInput(x, y, z)
{
  var rx := x % z;
  var ry := y % z;
  if rx + ry < z then 0
  else z - if rx > ry then rx else ry
}
// </vc-preamble>

// <vc-helpers>
// </vc-helpers>

// <vc-spec>
method solve(x: int, y: int, z: int) returns (coconuts: int, exchange: int

2. Le pipeline : prompt, parse, substitution, garde, vérification

Quatre briques courtes et lisibles :

  • ollama_chat — un tour du LLM local (température 0,2 : le protocole ne dépend pas d’un échantillonnage créatif mais de la réparation guidée) ;
  • parse_response — la réponse doit être le tableau JSON du papier : une chaîne de remplacement par section, dans l’ordre du fichier. Tolérance unique : les clôtures ```json que les petits modèles rajoutent. Une réponse mal formée consomme une tentative, comme dans le papier ;
  • apply_replacements — substitution du i-ème placeholder actif par le i-ème remplacement, par offsets successifs pour ne pas décaler les suivants ;
  • la garde anti-contournement — le motif interdit est cherché dans le code généré avant toute vérification. Le vérificateur ne sait pas distinguer assume {:axiom} false; d’une vraie preuve : c’est le harnais qui doit le savoir (parallèle complet avec le gate proof-integrity du dépôt, §6).

Les prompts reprennent la structure du papier (instructions CRITICAL, format tableau JSON, interdiction des contournements, tour k sur 5) — condensés pour un modèle de 7B, écart au protocole exact documenté (le papier vise des modèles de frontière).

BYPASS_DAFNY = [r"\bassume\b", r"\{:axiom\}"]
BYPASS_LEAN = [r"\bsorry\b", r"\bnative_decide\b", r"\baxiom\b"]


def has_bypass(code, patterns):
    return any(re.search(p, code) for p in patterns)


def ollama_chat(prompt, timeout=300):
    payload = json.dumps({
        "model": MODEL_NAME,
        "messages": [{"role": "user", "content": prompt}],
        "stream": False,
        "options": {"temperature": 0.2, "num_predict": 2048},
    }).encode()
    req = urllib.request.Request(OLLAMA_URL, data=payload,
                                 headers={"Content-Type": "application/json"})
    t0 = time.time()
    with urllib.request.urlopen(req, timeout=timeout) as resp:
        data = json.loads(resp.read())
    return data["message"]["content"], time.time() - t0


def parse_response(text):
    # Tableau JSON de remplacements (format papier). Tolerance : fences json.
    m = re.search(r"```(?:json)?\s*(.*?)```", text, re.S)
    if m:
        text = m.group(1)
    start, end = text.find("["), text.rfind("]")
    if start == -1 or end == -1:
        return None
    try:
        arr = json.loads(text[start:end + 1])
    except json.JSONDecodeError:
        return None
    if not isinstance(arr, list):
        return None
    strs = []
    for el in arr:
        if isinstance(el, str):
            strs.append(el)
        elif isinstance(el, list) and len(el) == 2 and isinstance(el[1], str):
            strs.append(el[1])  # tolerance : paires [section, code]
    return strs or None


# sections avec placeholder actif : vc-code (Dafny), vc-definitions+vc-theorems (Lean)
DAFY_CODE = (r"//[ \t]*<vc-code>[^\n]*\r?\n(.*?)(?=//[ \t]*</vc-code>)",
             r"assume[^\n]*\{:axiom\}[^\n]*")
LEAN_SECTION = (r"--[ \t]*<(vc-definitions|vc-theorems)>[^\n]*\r?\n"
                r"(.*?)(?=--[ \t]*</\1>)")


def find_placeholders(spec_text, lang):
    if lang == "dfy":
        pat, active = DAFY_CODE
        return [m for m in re.finditer(pat, spec_text, re.S)
                if re.search(active, m.group(1))]
    return [m for m in re.finditer(LEAN_SECTION, spec_text, re.S)
            if re.search(r"\bsorry\b", m.group(2))]


def apply_replacements(spec_text, code_list, lang):
    # Substitue le i-eme placeholder actif par le i-eme remplacement.
    ph = find_placeholders(spec_text, lang)
    group = 1 if lang == "dfy" else 2
    if len(ph) != len(code_list):
        return None
    out, shift = spec_text, 0
    for m, code in zip(ph, code_list):
        a, b = m.span(group)
        body = "\n".join(code.rstrip().splitlines())
        out = out[:a + shift] + body + out[b + shift:]
        shift += len(body) - (b - a)
    return out


print("Briques en place. Auto-test du parseur sur 3 reponses type :")
assert parse_response('["result := 1;"]') == ["result := 1;"]
assert parse_response('```json\n["a", "b"]\n```') == ["a", "b"]
assert parse_response('voici ma reponse ["x"] en effet') == ["x"]
print("parse_response : 3/3 cas conformes au format papier")
Briques en place. Auto-test du parseur sur 3 reponses type :
parse_response : 3/3 cas conformes au format papier
CODEGEN_DAFNY = """CRITICAL: Respond with ONLY a JSON array. No explanations, reasoning, or markdown. Start with [ and end with ].
The task is to generate implementations for '<vc-code>' sections in a Dafny file.
TURN 1 of {n}: This is the initial code generation phase. You have {n} total turns to get this right, so you can iterate and improve.
INPUT: a Dafny file containing {k} placeholder sections ('<vc-code>' tags) that need to be filled in.
OUTPUT: Return a JSON array with EXACTLY {k} replacements (one for each placeholder section in the file), in order from top to bottom:
["function min(a: int, b: int): int {{ if a < b then a else b }}", "{{\n result := ComputeResult(n, pos);\n}}"]
SECTION-SPECIFIC RULES:
**For '<vc-code>' sections:**
- Provide the COMPLETE method/function body (everything between the braces)
- Include variable declarations, assignments, and control flow
CRITICAL RULES:
- The ORIGINAL file contains EXACTLY {k} placeholder sections - your JSON array must have EXACTLY {k} elements
- Each replacement should be the exact code that will replace the placeholder body between the tags
- NEVER use verification bypasses: '{{:axiom}}', 'assume' statements, or other verification shortcuts
- Implement actual logic instead of bypassing verification
- Use valid Dafny syntax for all implementations
- Satisfy all 'requires' and 'ensures' clauses from the method/function specifications
- Return ONLY a valid JSON array, no explanations or markdown
- Your response must start with [ and end with ]
- CRITICAL: Do NOT use 'assume {{:axiom}}', 'assume', or any verification bypass. Implement real logic!
DAFNY FILE WITH PLACEHOLDER SECTIONS:
{code}"""

REPAIR_DAFNY = """CRITICAL: Respond with ONLY a JSON array. No explanations, reasoning, or markdown. Start with [ and end with ].
The task is to fix implementations in '<vc-code>' sections that failed verification.
TURN {i} of {n}: You are making progress and have multiple turns to iterate and improve your implementation.
INPUT: The file below failed Dafny verification with these errors:
{errors}
Fix the code so it verifies. Return a JSON array with EXACTLY {k} fixed replacements (one per placeholder section), in order from top to bottom.
- NEVER use verification bypasses: '{{:axiom}}', 'assume', or any verification shortcut. Implement real logic!
- Satisfy all 'requires' and 'ensures' clauses.
DAFNY FILE:
{code}"""

CODEGEN_LEAN = """CRITICAL: Respond with ONLY a JSON array. No explanations, reasoning, or markdown. Start with [ and end with ].
The task is to generate implementations for '<vc-definitions>' and '<vc-theorems>' sections in a Lean 4 file.
TURN 1 of {n}: This is the initial code generation phase. You have {n} total turns to get this right.
INPUT: a Lean 4 file containing {k} placeholder sections (with `sorry` bodies) that need to be filled in.
OUTPUT: Return a JSON array with EXACTLY {k} replacements (one for each placeholder section), in order from top to bottom:
["def solve (n : Nat) : Nat :=\n  n + 1", "theorem t : True := trivial"]
SECTION-SPECIFIC RULES:
**For '<vc-definitions>' sections:** keep the EXACT signatures, replace `sorry` with a real body implementing the spec.
**For '<vc-theorems>' sections:** keep the EXACT statements, replace `sorry` with a complete proof (tactics).
CRITICAL RULES:
- NEVER use 'sorry', 'native_decide', or any verification bypass. Write real proofs!
- Return ONLY a valid JSON array. Your response must start with [ and end with ].
LEAN FILE WITH PLACEHOLDER SECTIONS:
{code}"""

REPAIR_LEAN = """CRITICAL: Respond with ONLY a JSON array. No explanations, reasoning, or markdown. Start with [ and end with ].
The task is to fix implementations in '<vc-definitions>' and '<vc-theorems>' sections that failed Lean verification.
TURN {i} of {n}: You are making progress and have multiple turns to iterate and improve.
INPUT: The file below failed Lean verification with these errors:
{errors}
Fix the code. Return a JSON array with EXACTLY {k} replacements (one per placeholder section), in order from top to bottom.
- NEVER use 'sorry', 'native_decide', or any verification bypass. Write real proofs!
LEAN FILE:
{code}"""


def verify_src(src, task, attempt, lang):
    # Ecrit le fichier (nom court) et appelle le verificateur REEL.
    # Chemin relatif au subprocess : la sortie du verifier ne fuite pas de chemin.
    suffix = "dfy" if lang == "dfy" else "lean"
    name = f"{task}_try{attempt}.{suffix}"
    (WORKDIR / name).write_text(src, encoding="utf-8")
    t0 = time.time()
    if lang == "dfy":
        cmd = [DAFNY_EXE, "verify", "--cores:2", name]
        limit = 180
    else:
        cmd = ["lean", name]
        limit = 120
    try:
        r = subprocess.run(cmd, capture_output=True, text=True, timeout=limit,
                           cwd=str(WORKDIR), encoding="utf-8", errors="replace")
    except subprocess.TimeoutExpired:
        return False, "TIMEOUT", 0.0
    out = (r.stdout or "") + (r.stderr or "")
    dt = time.time() - t0
    if lang == "dfy":
        ok = r.returncode == 0 and "0 errors" in out
    else:
        ok = r.returncode == 0 and not re.search(r"error", out) \
            and "declaration uses `sorry`" not in out
    return ok, out, dt


def run_task(task, lang):
    # Une tache complete : jusqu'a MAX_TURNS tentatives, protocole papier.
    spec_text = SPECS[task]
    k = len(find_placeholders(spec_text, lang))
    bypass = BYPASS_DAFNY if lang == "dfy" else BYPASS_LEAN
    res = {"task": task, "lang": lang, "k": k, "success": False, "turns": 0,
           "llm_s": 0.0, "verify_s": 0.0, "fail": None}
    prev_error = None
    for turn in range(1, MAX_TURNS + 1):
        base = spec_text
        if turn == 1:
            tmpl = CODEGEN_DAFNY if lang == "dfy" else CODEGEN_LEAN
            prompt = tmpl.format(n=MAX_TURNS, k=k, code=base)
        else:
            tmpl = REPAIR_DAFNY if lang == "dfy" else REPAIR_LEAN
            prompt = tmpl.format(i=turn, n=MAX_TURNS, k=k,
                                 errors=(prev_error or "")[:2500], code=base)
        reply, dt = ollama_chat(prompt)
        res["llm_s"] += dt
        res["turns"] = turn
        codes = parse_response(reply)
        if codes is None:
            prev_error, res["fail"] = "Response was not a valid JSON array.", "json"
            continue
        if len(codes) != k:
            prev_error = f"Expected exactly {k} replacements, got {len(codes)}."
            res["fail"] = "count"
            continue
        if has_bypass(" ".join(codes), bypass):
            prev_error = "Attempt contained a forbidden verification bypass."
            res["fail"] = "bypass"
            continue
        full = apply_replacements(spec_text, codes, lang)
        if full is None:
            prev_error, res["fail"] = "Substitution failed.", "subst"
            continue
        ok, out, dts = verify_src(full, task, turn, lang)
        res["verify_s"] += dts
        if ok:
            res["success"], res["fail"] = True, None
            return res
        prev_error, res["fail"] = out, "verify"
    return res


print("run_task pret ; entete du prompt codegen Dafny :")
print(CODEGEN_DAFNY.splitlines()[0])
run_task pret ; entete du prompt codegen Dafny :
CRITICAL: Respond with ONLY a JSON array. No explanations, reasoning, or markdown. Start with [ and end with ].

3. Jambe Dafny : 30 tâches × 5 tentatives

Le banc tourne sur l’échantillon Dafny stratifié (apps, numpy_triple, dafnybench, verified_cogen, humaneval, verina). À chaque tâche : génération, garde, vérification Dafny verify réelle, puis boucle de réparation. Les échecs sont classés par cause — la répartition des causes est aussi instructive que le taux global : un modèle 7B échoue d’abord parce que le code ne prouve pas la spec (verify), très rarement parce qu’il tente de tricher (bypass) — cette dernière cause est celle que la garde élimine et que le papier surveille par un juge.

results_dafny = []
if DAFNY_EXE and LLM_OK:
    t_start = time.time()
    for i, task in enumerate(SAMPLE_DAFNY, 1):
        r = run_task(task, "dfy")
        results_dafny.append(r)
        cause = "OK" if r["success"] else r["fail"]
        print(f"[{i:2d}/30] {task} : {cause}"
              + (f" (turn {r['turns']})" if r["success"] else ""), flush=True)
    print(f"\nBanc Dafny termine en {time.time() - t_start:.0f}s")
else:
    print("Banc Dafny SAUTE : Dafny ou Ollama indisponible (env degrade, degradation affichee).")
[ 1/30] DA0121 : bypass
[ 2/30] DA0026 : OK (turn 2)
[ 3/30] DA0298 : bypass
[ 4/30] DA0265 : verify
[ 5/30] DA0240 : verify
[ 6/30] DA0150 : bypass
[ 7/30] DT0107 : verify
[ 8/30] DT0634 : bypass
[ 9/30] DT0092 : bypass
[10/30] DT0495 : verify
[11/30] DT0034 : verify
[12/30] DT0030 : verify
[13/30] DD0075 : OK (turn 2)
[14/30] DD0168 : bypass
[15/30] DD0199 : bypass
[16/30] DD0606 : verify
[17/30] DD0723 : verify
[18/30] DD0037 : verify
[19/30] DJ0081 : verify
[20/30] DJ0140 : verify
[21/30] DJ0087 : verify
[22/30] DJ0147 : verify
[23/30] DH0087 : verify
[24/30] DH0002 : verify
[25/30] DH0050 : verify
[26/30] DH0131 : verify
[27/30] DV0128 : verify
[28/30] DV0108 : verify
[29/30] DV0064 : verify
[30/30] DV0085 : OK (turn 2)

Banc Dafny termine en 491s
from collections import Counter


def summarize(results, label):
    # Taux global, succes par tentative, causes d'echec, temps moyens.
    n = len(results)
    if n == 0:
        print(f"[{label}] banc non execute (env degrade)")
        return None
    succ = sum(r["success"] for r in results)
    by_turn = Counter(r["turns"] for r in results if r["success"])
    fails = Counter(r["fail"] for r in results if not r["success"])
    llm = sum(r["llm_s"] for r in results)
    ver = sum(r["verify_s"] for r in results)
    print(f"[{label}] {succ}/{n} = {succ / n * 100:.1f}% de succes")
    print(f"  succes au turn 1 (pass@1) : {by_turn.get(1, 0)}")
    print(f"  succes par tentative      : {dict(sorted(by_turn.items()))}")
    print(f"  echecs par cause          : {dict(fails)}")
    print(f"  temps LLM {llm:.0f}s + verif {ver:.0f}s = {llm + ver:.0f}s "
          f"({(llm + ver) / n:.0f}s/tache)")
    return succ, n


sum_dafny = summarize(results_dafny, "Dafny")
[Dafny] 3/30 = 10.0% de succes
  succes au turn 1 (pass@1) : 0
  succes par tentative      : {2: 3}
  echecs par cause          : {'bypass': 7, 'verify': 20}
  temps LLM 439s + verif 52s = 491s (16s/tache)

Lecture du banc Dafny

Trois questions à poser aux chiffres ci-dessus :

  1. pass@1 vs pass@5 — combien de tâches sauvées par la boucle de réparation ? C’est la valeur ajoutée mesurée du vericoding : dans le papier, l’écart initial/réparation porte une part substantielle des 82,2 %.
  2. La cause dominante d’échec — pour un 7B local, l’échec est presque partout verify : le modèle écrit du Dafny syntaxiquement correct mais la preuve ne passe pas (invariants manquants, terminaison non prouvée). Ce n’est pas un défaut du pipeline : c’est la capacité du générateur.
  3. L’écart au papier — 82,2 % (Dafny, union de modèles de frontière, cité) contre notre taux local : l’écart se creuse là où l’exigence formelle se raidit. Un modèle plus petit ne « raconte » pas la preuve : il la produit ou échoue.

4. Jambe Lean : 12 tâches sans Mathlib

Les tâches Lean du benchmark qui n’importent aucun module sont vérifiables au binaire lean nu — pas de lake, pas de build Mathlib, la toolchain officielle suffit. Le format diffère : le modèle doit produire les corps des définitions ET les preuves des théorèmes (<vc-definitions> et <vc-theorems>), remplaçant chaque sorry. C’est une tâche strictement plus difficile qu’en Dafny : une preuve Lean est un programme tactique, dont la moindre erreur de forme casse la vérification.

Le critère de succès est l’analogue exact du gate proof-integrity du dépôt : sortie du vérificateur sans erreur ET sans declaration uses 'sorry' — un sorry restant ne produit en Lean qu’un warning, pas une erreur : sans cette garde, toute tâche « réussirait » en laissant les sorry en place.

results_lean = []
if LEAN_OK and LLM_OK:
    t_start = time.time()
    for i, task in enumerate(SAMPLE_LEAN, 1):
        r = run_task(task, "lean")
        results_lean.append(r)
        cause = "OK" if r["success"] else r["fail"]
        print(f"[{i:2d}/12] {task} : {cause}"
              + (f" (turn {r['turns']})" if r["success"] else ""), flush=True)
    print(f"\nBanc Lean termine en {time.time() - t_start:.0f}s")
else:
    print("Banc Lean SAUTE : lean ou Ollama indisponible (env degrade, degradation affichee).")

sum_lean = summarize(results_lean, "Lean")
[ 1/12] LA0345 : verify
[ 2/12] LA0104 : verify
[ 3/12] LA0094 : verify
[ 4/12] LD0424 : verify
[ 5/12] LD0073 : verify
[ 6/12] LD0373 : verify
[ 7/12] LJ0088 : bypass
[ 8/12] LJ0155 : count
[ 9/12] LV0084 : verify
[10/12] LV0013 : verify
[11/12] LB0046 : verify
[12/12] LS0029 : verify

Banc Lean termine en 999s
[Lean] 0/12 = 0.0% de succes
  succes au turn 1 (pass@1) : 0
  succes par tentative      : {}
  echecs par cause          : {'verify': 10, 'bypass': 1, 'count': 1}
  temps LLM 920s + verif 79s = 999s (83s/tache)

5. LC0033 — prouver un programme ne prouve pas la spécification

Le cas le plus instructif du papier (figure 8) : la tâche LC0033 du benchmark CLEVER, dont voici la spécification originale, telle qu’elle est dans le benchmark :

let spec (result: List Int) :=
  (∀ x, x ∈ result ↔ x ∈ l ∧ result.count x = 1) ∧
  List.Sorted Int.le result

L’intention est un tri avec déduplication. Le bug : la bi-implication x ∈ result ↔︎ (x ∈ l ∧ count x = 1) n’exige pas la totalité — elle dit seulement que tout élément du résultat est dans l et y apparaît une fois. La liste vide satisfait cette spécification pour toute entrée : pour result = [], la quantification sur x est vide, les deux côtés de la bi-implication sont faux, faux ↔︎ faux est vrai. Un générateur malin (ou paresseux) peut répondre implementation l := [] — et le vérificateur dira prouvé.

La démonstration ci-dessous porte le cas en Dafny (la tâche Lean importe Mathlib ; le portage Dafny est fidèle à la spec et exécutable localement) et vérifie réellement les deux claims :

  1. la solution triviale result := [] vérifie contre la spec originale — le vérificateur a raison, c’est la spécification qui a tort ;
  2. la même solution triviale échoue contre la spec réparée (la totalité ∀x, x ∈ l ↔︎ x ∈ result est ajoutée) — réparer la spec répare le verrou.

Puis l’implémentation correcte est exécutée sur le test du papier : [5,3,5,2,3,3,9,0,123] → [0,2,3,5,9,123]. (Sa vérification formelle complète — invariants de boucle sur le tri — est laissée en exercice ouvert : même pour un humain, faire vérifier une vraie implémentation est exactement le travail que le pipeline automatise.)

LC0033_LEAN = '-- <vc-preamble>\nimport Mathlib\nimport Mathlib.Algebra.Polynomial.Basic\nimport Std.Data.HashMap\n-- </vc-preamble>\n\n-- <vc-helpers>\n-- </vc-helpers>\n\n-- <vc-definitions>\ndef implementation (l: List Int) : List Int :=\n  sorry\n-- </vc-definitions>\n\n-- <vc-theorems>\ndef problem_spec\n-- function signature\n(implementation: List Int → List Int)\n-- inputs\n(l: List Int) :=\n-- spec\nlet spec (result: List Int) :=\n  (∀ x, x ∈ result ↔ x ∈ l ∧ result.count x = 1) ∧\n  List.Sorted Int.le result\n-- program termination\n∃ result,\n  implementation l = result ∧\n  spec result\n\ntheorem correctness\n(l: List Int)\n: problem_spec implementation l\n:= by\n  sorry\n-- </vc-theorems>'

print("Tache LC0033 du benchmark (Lean, importe Mathlib) :")
print(LC0033_LEAN)
Tache LC0033 du benchmark (Lean, importe Mathlib) :
-- <vc-preamble>
import Mathlib
import Mathlib.Algebra.Polynomial.Basic
import Std.Data.HashMap
-- </vc-preamble>

-- <vc-helpers>
-- </vc-helpers>

-- <vc-definitions>
def implementation (l: List Int) : List Int :=
  sorry
-- </vc-definitions>

-- <vc-theorems>
def problem_spec
-- function signature
(implementation: List Int → List Int)
-- inputs
(l: List Int) :=
-- spec
let spec (result: List Int) :=
  (∀ x, x ∈ result ↔ x ∈ l ∧ result.count x = 1) ∧
  List.Sorted Int.le result
-- program termination
∃ result,
  implementation l = result ∧
  spec result

theorem correctness
(l: List Int)
: problem_spec implementation l
:= by
  sorry
-- </vc-theorems>
SPEC_BUGGEE = "// Portage Dafny de LC0033 (benchmark CLEVER, Fig. 8 du papier vericoding)\n// SPEC ORIGINALE, BUGGEE : la bi-implication n'exige pas la totalite\nfunction Occ(s: seq<int>, x: int): int\n{\n  if s == [] then 0\n  else (if s[0] == x then 1 else 0) + Occ(s[1..], x)\n}\n\npredicate IsSorted(s: seq<int>)\n{\n  forall i, j :: 0 <= i <= j < |s| ==> s[i] <= s[j]\n}\n\n// La solution TRIVIALE : renvoyer la liste vide\nmethod SolveTrivial(l: seq<int>) returns (result: seq<int>)\n  ensures forall x :: x in result <==> (x in l && Occ(result, x) == 1)\n  ensures IsSorted(result)\n{\n  result := [];\n}\n"


if DAFNY_EXE:
    ok_bug, out_bug, _ = verify_src(SPEC_BUGGEE, "LC0033_buggy", 1, "dfy")
    last = out_bug.strip().splitlines()[-1] if out_bug.strip() else ""
    verdict = re.search(r"verifier finished with (.+)$", last)
    print("Spec ORIGINALE + solution triviale (liste vide) :")
    print("  ->", verdict.group(1) if verdict else "(pas de ligne de verdict)")
    print("  VERIFIEE :", ok_bug, "-- le bug de spec laisse passer la solution vide")
else:
    print("Dafny indisponible : demonstration sautee (env degrade).")
Spec ORIGINALE + solution triviale (liste vide) :
  -> 3 verified, 0 errors
  VERIFIEE : True -- le bug de spec laisse passer la solution vide
SPEC_REPAREE = 'function Occ(s: seq<int>, x: int): int\n{\n  if s == [] then 0\n  else (if s[0] == x then 1 else 0) + Occ(s[1..], x)\n}\n\npredicate IsSorted(s: seq<int>)\n{\n  forall i, j :: 0 <= i <= j < |s| ==> s[i] <= s[j]\n}\n\n// SPEC REPAREE : totalite explicite (x in l <==> x in result)\nmethod SolveTrivial(l: seq<int>) returns (result: seq<int>)\n  ensures forall x :: x in l <==> x in result\n  ensures forall x :: x in result ==> Occ(result, x) == 1\n  ensures IsSorted(result)\n{\n  result := [];\n}\n'


if DAFNY_EXE:
    ok_fix, out_fix, _ = verify_src(SPEC_REPAREE, "LC0033_fixed", 1, "dfy")
    last = out_fix.strip().splitlines()[-1] if out_fix.strip() else ""
    verdict = re.search(r"verifier finished with (.+)$", last)
    print("Spec REPAREE (totalite explicite) + meme solution triviale :")
    print("  ->", verdict.group(1) if verdict else "(pas de ligne de verdict)")
    print("  VERIFIEE :", ok_fix, "-- la spec reparee rejette la solution vide")
else:
    print("Dafny indisponible : demonstration sautee (env degrade).")
Spec REPAREE (totalite explicite) + meme solution triviale :
  -> 2 verified, 1 error
  VERIFIEE : False -- la spec reparee rejette la solution vide
IMPL_CORRECTE = '// Execution calculatoire de l\'implementation correcte (LC0033 portee en Dafny)\n// Tri par insertion recursif pur, puis dedup sur sequence triee.\n\nfunction Insert(x: int, s: seq<int>): seq<int>\n  decreases |s|\n{\n  if s == [] || x <= s[0] then [x] + s\n  else [s[0]] + Insert(x, s[1..])\n}\n\nfunction Sort(l: seq<int>): seq<int>\n  decreases |l|\n{\n  if |l| <= 1 then l\n  else Insert(l[0], Sort(l[1..]))\n}\n\nfunction DedupSorted(s: seq<int>): seq<int>\n  decreases |s|\n{\n  if |s| <= 1 then s\n  else if s[0] == s[1] then DedupSorted(s[1..])\n  else [s[0]] + DedupSorted(s[1..])\n}\n\nmethod Main()\n{\n  var l := [5, 3, 5, 2, 3, 3, 9, 0, 123];\n  var r := DedupSorted(Sort(l));\n  print "entree   = ", l, "\\n";\n  print "sortie   = ", r, "\\n";\n  print "attendu  = [0, 2, 3, 5, 9, 123] (test du papier, Fig 8)\\n";\n  print "conforme = ", r == [0, 2, 3, 5, 9, 123], "\\n";\n}\n'


if DAFNY_EXE:
    name = "LC0033_run.dfy"
    (WORKDIR / name).write_text(IMPL_CORRECTE, encoding="utf-8")
    r = subprocess.run([DAFNY_EXE, "run", "--cores:2", name],
                       capture_output=True, text=True, timeout=180,
                       cwd=str(WORKDIR), encoding="utf-8", errors="replace")
    print("Execution de l'implementation correcte (tri par insertion + dedup) :")
    print(r.stdout.strip())
else:
    print("Dafny indisponible : execution sautee (env degrade).")
Execution de l'implementation correcte (tri par insertion + dedup) :
Dafny program verifier finished with 3 verified, 0 errors
entree   = [5, 3, 5, 2, 3, 3, 9, 0, 123]
sortie   = [0, 2, 3, 5, 9, 123]
attendu  = [0, 2, 3, 5, 9, 123] (test du papier, Fig 8)
conforme = true

La leçon — et pourquoi le harnais doit surveiller la spec, pas seulement la preuve

Le papier rapporte ce phénomène en système : sur les tâches réussies, l’inspection humaine trouve ~9 % de spécifications trop faibles et ~15 % de traductions douteuses — conditionnellement au succès, c’est-à-dire précisément là où tout semble vert. Le juge LLM du papier existe pour ça ; notre garde anti-assume ne couvre que les contournements de preuve, pas les spécifications creuses. Entre les deux, la même asymétrie que partout en vérification formelle :

Menace Ce que le vérificateur voit Ce qui l’attrape
assume {:axiom} false / sorry une preuve (fausse) la garde du pipeline / le gate proof-integrity
spec triviale (ensures true, LC0033) une preuve vraie rien d’automatique — l’œil sur la spec
test qui passe par chance aucun (pas un test) hors périmètre : c’est ce que le vericoding remplace

C’est la limite structurelle du vericoding, assumée par le papier et par ce notebook : la preuve certifie le programme contre la spec — la qualité de la spec reste un jugement humain (ou un juge LLM, avec ses propres limites).

6. Parallèle avec le gate proof-integrity du dépôt

Ce dépôt fait tourner en CI un gate d’intégrité de preuves Lean (LeanVerifier.check_axioms(module, fail_on_sorry=True)) qui rejette trois classes d’axiomes interdits. Le pipeline de ce notebook est son homologue standalone, transposé à la génération :

Gate proof-integrity (dépôt, CI) Garde du pipeline (ce notebook) Ce que ça attrape
sorry / sorryAx (transitif : un sorry caché dans une lemma appelée contamine tout) \bsorry\b sur le code généré + absence de « declaration uses 'sorry' » dans la sortie lean la preuve inachevée qui paraît finie
native_decide.* (réduction par le noyau natif sans preuve — vide le théorème) \bnative_decide\b la décision non prouvée
Classical.choice (non-constructif, légitime en général — se whiteliste par nom) hors scope : choix de fondation, pas une triche —
— (spécifique Dafny) \bassume\b / \{:axiom\} l’axiome local qui court-circuite la preuve

La colonne manquante est la plus importante : aucun des deux n’attrape la spécification creuse — c’est la leçon de LC0033, et la raison pour laquelle le dépôt exige des revues humaines sur les énoncés, pas seulement des gates sur les preuves.

7. Le langage naturel n’aide pas — débat de clôture

L’abstract du papier pose le contraste fondateur : le vericoding génère du code depuis une spécification formelle, le vibe coding depuis une description en langage naturel. La section résultats mesure ce que l’information « vibe » apporte réellement : sur les tâches Verina (l’une des neuf sources du benchmark), les auteurs ont rejoué le protocole en injectant les descriptions informelles dans le prompt, en plus de la spécification formelle.

Ce qui a été mesuré : aucune amélioration statistiquement significative du taux de succès — la tendance est même légèrement négative en moyenne. Les auteurs n’ont pas étendu l’expérience au benchmark complet (12 504 tâches) et la renvoient au future work : la phrase de l’abstract (« adding natural-language descriptions does not significantly improve performance ») repose sur ce sous-ensemble.

Ce que ça ne dit pas :

  1. Pas « le langage naturel nuit » — « pas de gain significatif » laisse la tendance négative dans le bruit ; la conclusion correcte est l’absence de gain mesuré, pas un effet délétère.
  2. Une direction seulement — la mesure porte sur la génération de code et de preuve étant donné la spécification formelle, avec la description en plus. Elle ne dit rien de la direction inverse (produire la spécification depuis du langage naturel), que ce protocole exclut et que d’autres benchmarks explorent.
  3. Des modèles de frontière — les taux du papier viennent de LLM 2025 ; notre banc local 7B n’injecte pas les descriptions et ne mesure donc rien de cet effet.
  4. Rien sur les humains — le résultat mesure l’effet sur la machine à prouver. Pour l’ingénieur qui écrit la spécification, le langage naturel reste le medium de l’intention.

Le débat honnête : pour le vérificateur, la preuve est autoportante — la description est redondante dès que la spécification formelle est là. Mais la leçon LC0033 (§5) montre l’envers : une spécification formelle peut être vraie et creuse, prouvée et vide d’intention. Le langage naturel n’aide pas la machine à prouver ; son absence de la boucle humaine est précisément le maillon faible que le bug de spécification expose. Vericoding contre vibe coding n’est donc pas formel contre informel : c’est la question de où vit l’intention — dans une spécification qu’on peut auditer, ou dans une phrase qu’on espère.

8. Exercices

Trois exercices, stubs sans erreur volontaire (le notebook s’exécute de bout en bout même non complété).

# Exercice 1 : detecter une spec incomplete.
# Ecrire spec_exige_totalite(spec_text) -> bool : True ssi la spec exige que
# TOUT element de l'entree l se retrouve dans le resultat (la totalite qui
# manque dans LC0033). Indice : la forme totale contient une quantification
# dont la MEME variable apparait des deux cotes d'une implication ou
# bi-implication l/result (x in l <==> x in result).
# Etape 1 : chercher les lignes avec 'forall'.
# Etape 2 : tester la presence de la double appartenance l / result.

def spec_exige_totalite(spec_text):
    # TODO etudiant
    result = None  # TODO etudiant
    return result


# Tests sur les DEUX specs du notebook (en memoire apres la section 5) :
# SPEC_BUGGEE ne doit PAS exiger la totalite ; SPEC_REPAREE doit l'exiger.
for spec_text, attendu in ((SPEC_BUGGEE, False), (SPEC_REPAREE, True)):
    obtenu = spec_exige_totalite(spec_text)
    print(f"attendu={attendu} obtenu={obtenu}")
print("Exercice a completer : chaque ligne doit afficher attendu == obtenu.")
attendu=False obtenu=None
attendu=True obtenu=None
Exercice a completer : chaque ligne doit afficher attendu == obtenu.
# Exercice 2 : predire le verdict du pipeline SANS executer le verificateur.
# Ecrire verdict_tentative(reply, k) -> str parmi 'json', 'count', 'bypass',
# 'verify' : les trois premieres causes se jugent sur la reponse seule
# (format, nombre de remplacements, motifs interdits Dafny). Seul 'verify'
# exige reellement Dafny -- la question : quand peut-on trancher sans ?
# Etape 1 : reutiliser parse_response et has_bypass.
# Etape 2 : ordonner les checks exactement comme run_task les applique.

def verdict_tentative(reply, k):
    # TODO etudiant
    return None  # TODO etudiant


cas = [
    ('["result := 1;"]', 1),             # format valide -> 'verify'
    ('["a", "b", "c"]', 1),              # 3 remplacements pour 1 section
    ('["assume {:axiom} false;"]', 1),   # contournement evident
    ('le code est: result := 1;', 1),    # pas de tableau JSON
]
for reply, k in cas:
    print(f"k={k} reply={reply[:40]!r:44s} -> {verdict_tentative(reply, k)}")
print("Exercice a completer : attendus 'verify', 'count', 'bypass', 'json'.")
k=1 reply='["result := 1;"]'                           -> None
k=1 reply='["a", "b", "c"]'                            -> None
k=1 reply='["assume {:axiom} false;"]'                 -> None
k=1 reply='le code est: result := 1;'                  -> None
Exercice a completer : attendus 'verify', 'count', 'bypass', 'json'.
# Exercice 3 : pass@k et valeur de la boucle de reparation.
# Depuis results_dafny : pass_1 (succes au turn 1), pass_5 (succes global),
# sauvetage (part des succes obtenus PAR la reparation).
# Etape 1 : compter les tours des succes. Etape 2 : en deduire les trois
# nombres. Etape 3 : un verdict honnete en une phrase.

if results_dafny:
    pass_1 = None     # TODO etudiant
    pass_5 = None     # TODO etudiant
    sauvetage = None  # TODO etudiant : part des succes apres le turn 1
    print(f"pass@1 = {pass_1}, pass@5 = {pass_5}, sauves par reparation = {sauvetage}")
    verdict = None  # TODO etudiant : une phrase honnete
    print("Verdict :", verdict)
else:
    print("Banc non execute (env degrade) : exercice sans donnees.")
pass@1 = None, pass@5 = None, sauves par reparation = None
Verdict : None

9. Ce qu’il faut retenir

Question Réponse mesurée ici Référence citée (papier)
Le pipeline est-il reproductible en local ? oui — LLM 7B local + Dafny verify + lean réels, zéro API distante pipeline identique, modèles de frontière
Taux Dafny d’un 7B local (5 tentatives) ? §3 ci-dessus 82,2 % (union, modèles 2025)
Taux Lean du même modèle ? §4 ci-dessus 26,8 %
La boucle de réparation aide-t-elle ? exercice 3 (pass@1 vs pass@5) oui, substantiellement
Un vérificateur vert garantit-il la correction ? non — LC0033 : la liste vide est prouvée conforme à une spec incomplète ~9 % de specs trop faibles sur les tâches réussies
Que protège la garde anti-contournement ? les assume/sorry/native_decide — pas les specs creuses juge LLM (limite assumée)

Le vericoding en une phrase : la preuve formelle rend la récompense incontournable — le modèle ne peut pas gagner sans satisfaire la spécification — mais la qualité de la spécification devient le nouveau maillon faible, et c’est un travail d’ingénieur, pas de vérificateur.

Références — Bursuc, S., Ehrenborg, T., Lin, S., et al. (13 auteurs, incl. M. Tegmark), A Benchmark for Vericoding: Formally Verified Program Synthesis (arXiv:2509.22908, 2025) ; benchmark : github.com/Beneficial-AI-Foundation/vericoding-benchmark (MIT) ; tâche LC0033 : benchmark CLEVER (fig. 8 du papier) ; gate du dépôt : workflow lean-axiom / LeanVerifier.check_axioms.

Séquence suivante : PT-11b/PT-11c appliquent RLVR à des vérificateurs SymPy/Z3 — l’extension naturelle de ce notebook serait d’entraîner (GRPO) le petit modèle local sur la récompense Dafny, en bouclant la chaîne : génération → preuve → gradient.

Retour au sommet