Skip to content

Commit c1c311f

Browse files
hyperpolymathclaude
andcommitted
feat(corpus): add typechecker-ecosystem prover families + vocabulary
Extends the merge_corpus + vocabulary_5x_expansion scripts to cover: - New prover families: TypeLL, KatagoriaVerifier, TropicalTypeChecker, ChoreographicTypeChecker, EpistemicTypeChecker, EchoTypeChecker - New per-prover sources: proof_states_isabelle.jsonl, proof_states_tptp.a2ml, proof_states_typechecker_ecosystem.jsonl - A balanced output cap of 12k entries per prover written to proof_states_UNIFIED_BALANCED.jsonl The vocabulary expansion now also walks the sibling TypeLL, Katagoria, and tropical-resource-typing repos to pull terms directly from source. Stats files updated to reflect the +3000 typechecker_research entries (expansion ratio 11.1 -> 12.4). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent af29ad8 commit c1c311f

4 files changed

Lines changed: 272 additions & 127 deletions

File tree

scripts/merge_corpus.jl

Lines changed: 169 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ using Dates
1818
# ---------------------------------------------------------------------------
1919

2020
const TRAINING_DIR = joinpath(dirname(dirname(abspath(@__FILE__))), "training_data")
21+
const BALANCED_OUTPUT_FILE = joinpath(TRAINING_DIR, "proof_states_UNIFIED_BALANCED.jsonl")
22+
const BALANCED_CAP_PER_PROVER = 12_000
2123

2224
# Per-prover source files -- the authoritative extractions.
2325
# We list them explicitly to avoid pulling in aggregate/merged files
@@ -41,6 +43,9 @@ const PER_PROVER_FILES = [
4143
"proof_states_twelf.jsonl", # Twelf (synthetic)
4244
"proof_states_imandra.jsonl", # Imandra (synthetic)
4345
"proof_states_minizinc.jsonl", # MiniZinc / constraint solvers
46+
"proof_states_isabelle.jsonl", # Isabelle (tropical + theory extraction)
47+
"proof_states_tptp.a2ml", # Vampire / EProver / SPASS from TPTP
48+
"proof_states_typechecker_ecosystem.jsonl", # Typechecker/prover expansion
4449
"proof_states_mathlib4.jsonl", # Additional mathlib4 (smaller set)
4550
"proof_states_coqgym.jsonl", # Additional CoqGym (smaller set)
4651
"proof_states_2026-03-20.jsonl", # Dated snapshot
@@ -55,6 +60,11 @@ const ALL_PROVERS = Set([
5560
"TLAPS", "Twelf", "Nuprl", "Minlog", "Imandra",
5661
"Vampire", "EProver", "E Prover", "SPASS",
5762
"GLPK", "SCIP", "MiniZinc", "Chuffed", "ORTools", "OR-Tools",
63+
# Typechecker-native and research prover/tool families
64+
"TypeLL", "KatagoriaVerifier", "TropicalTypeChecker",
65+
"ChoreographicTypeChecker", "EpistemicTypeChecker", "EchoTypeChecker",
66+
"SessionTypeChecker", "ModalTypeChecker", "QTTTypeChecker",
67+
"EffectRowTypeChecker", "DependentTypeChecker", "RefinementTypeChecker",
5868
])
5969

6070
# Canonical prover name mapping (normalise variants).
@@ -69,8 +79,8 @@ const PROVER_CANONICAL = Dict(
6979
"OR-Tools" => "ORTools",
7080
)
7181

72-
# Total expected backend count for coverage calculation.
73-
const TOTAL_BACKENDS = 30
82+
# Total expected backend/tool count for coverage calculation.
83+
const TOTAL_BACKENDS = 60
7484

7585
"""
7686
canonical_prover(name::String) -> String
@@ -118,23 +128,164 @@ end
118128
Load a JSONL file, skipping malformed lines.
119129
"""
120130
function load_jsonl(filepath::String)::Vector{Dict{String,Any}}
131+
if endswith(lowercase(filepath), ".a2ml")
132+
return load_tptp_a2ml(filepath)
133+
end
134+
121135
entries = Dict{String,Any}[]
122136
if !isfile(filepath)
123137
return entries
124138
end
139+
malformed = 0
125140
for (lineno, line) in enumerate(eachline(filepath))
126141
stripped = strip(line)
127142
isempty(stripped) && continue
128143
try
129-
entry = Dict{String,Any}(pairs(JSON3.read(stripped)))
144+
entry = JSON3.read(stripped, Dict{String,Any})
130145
push!(entries, entry)
131146
catch
132-
println(" WARN: Skipped malformed JSON at $(basename(filepath)):$lineno")
147+
malformed += 1
148+
if malformed <= 5
149+
println(" WARN: Skipped malformed JSON at $(basename(filepath)):$lineno")
150+
end
133151
end
134152
end
153+
if malformed > 5
154+
println(" WARN: Suppressed $(malformed - 5) additional malformed lines in $(basename(filepath))")
155+
end
135156
return entries
136157
end
137158

159+
"""
160+
parse_a2ml_value(raw::String) -> Any
161+
162+
Parse a basic A2ML scalar/list value used in proof-state blocks.
163+
"""
164+
function parse_a2ml_value(raw::String)::Any
165+
value = strip(raw)
166+
if startswith(value, "\"") && endswith(value, "\"") && length(value) >= 2
167+
return replace(value[2:end-1], "\\\"" => "\"")
168+
elseif value == "[]"
169+
return Any[]
170+
elseif occursin(r"^-?\d+$", value)
171+
try
172+
return parse(Int, value)
173+
catch
174+
return value
175+
end
176+
else
177+
return value
178+
end
179+
end
180+
181+
"""
182+
load_tptp_a2ml(filepath::String) -> Vector{Dict{String,Any}}
183+
184+
Load TPTP proof-state blocks from an A2ML file.
185+
"""
186+
function load_tptp_a2ml(filepath::String)::Vector{Dict{String,Any}}
187+
entries = Dict{String,Any}[]
188+
if !isfile(filepath)
189+
return entries
190+
end
191+
192+
current = Dict{String,Any}()
193+
in_block = false
194+
195+
for line in eachline(filepath)
196+
stripped = strip(line)
197+
isempty(stripped) && continue
198+
startswith(stripped, "#") && continue
199+
200+
if stripped == "[[proof-state]]"
201+
if in_block && !isempty(current)
202+
push!(entries, current)
203+
end
204+
current = Dict{String,Any}()
205+
in_block = true
206+
continue
207+
end
208+
209+
!in_block && continue
210+
m = match(r"^([A-Za-z0-9_.-]+)\s*=\s*(.+)$", stripped)
211+
m === nothing && continue
212+
key = String(m.captures[1])
213+
raw = String(m.captures[2])
214+
current[key] = parse_a2ml_value(raw)
215+
end
216+
217+
if in_block && !isempty(current)
218+
push!(entries, current)
219+
end
220+
221+
return entries
222+
end
223+
224+
"""
225+
evenly_sample(entries::Vector{Dict{String,Any}}, target::Int) -> Vector{Dict{String,Any}}
226+
227+
Sample approximately evenly across a sorted list, preserving breadth.
228+
"""
229+
function evenly_sample(entries::Vector{Dict{String,Any}}, target::Int)::Vector{Dict{String,Any}}
230+
n = length(entries)
231+
n <= target && return entries
232+
target <= 0 && return Dict{String,Any}[]
233+
234+
step = n / target
235+
sampled = Dict{String,Any}[]
236+
seen = Set{Int}()
237+
for i in 1:target
238+
idx = clamp(floor(Int, (i - 1) * step) + 1, 1, n)
239+
if idx seen
240+
push!(sampled, entries[idx])
241+
push!(seen, idx)
242+
end
243+
end
244+
return sampled
245+
end
246+
247+
"""
248+
write_balanced_subset(deduped::Vector{Dict{String,Any}})
249+
250+
Write a capped per-prover balanced subset to reduce dominance by top provers.
251+
"""
252+
function write_balanced_subset(deduped::Vector{Dict{String,Any}})::Dict{String,Int}
253+
grouped = Dict{String,Vector{Dict{String,Any}}}()
254+
for entry in deduped
255+
prover = string(get(entry, "prover", "Unknown"))
256+
if !haskey(grouped, prover)
257+
grouped[prover] = Dict{String,Any}[]
258+
end
259+
push!(grouped[prover], entry)
260+
end
261+
262+
balanced = Dict{String,Any}[]
263+
balanced_counts = Dict{String,Int}()
264+
265+
for prover in sort(collect(keys(grouped)))
266+
entries = grouped[prover]
267+
sort!(entries, by=e -> (string(get(e, "theorem", "")), string(get(e, "source", ""))))
268+
selected = evenly_sample(entries, BALANCED_CAP_PER_PROVER)
269+
append!(balanced, selected)
270+
balanced_counts[prover] = length(selected)
271+
end
272+
273+
# Fresh IDs for balanced file only.
274+
sort!(balanced, by=e -> (string(get(e, "prover", "")), string(get(e, "theorem", ""))))
275+
for (idx, entry) in enumerate(balanced)
276+
entry["id"] = idx
277+
end
278+
279+
open(BALANCED_OUTPUT_FILE, "w") do fh
280+
for entry in balanced
281+
println(fh, JSON3.write(entry))
282+
end
283+
end
284+
285+
println("Wrote balanced corpus to $BALANCED_OUTPUT_FILE ($(length(balanced)) proofs)")
286+
return balanced_counts
287+
end
288+
138289
"""
139290
extract_words(text::String) -> Set{String}
140291
@@ -143,7 +294,7 @@ Split on non-alphanumeric, keep tokens >= 3 chars that are alphabetic.
143294
"""
144295
function extract_words(text::String)::Set{String}
145296
tokens = split(text, r"[^a-zA-Z]+")
146-
return Set(lowercase(t) for t in tokens if length(t) >= 3 && all(isalpha, t))
297+
return Set(lowercase(t) for t in tokens if length(t) >= 3 && all(isletter, t))
147298
end
148299

149300
function main()::Int
@@ -209,6 +360,11 @@ function main()::Int
209360
end
210361
println("\nWrote $(length(deduped)) proofs to $unified_path")
211362

363+
# ------------------------------------------------------------------
364+
# 4b. Write capped balanced subset (per-prover cap)
365+
# ------------------------------------------------------------------
366+
balanced_counts = write_balanced_subset(deduped)
367+
212368
# ------------------------------------------------------------------
213369
# 5. Compute statistics
214370
# ------------------------------------------------------------------
@@ -250,6 +406,12 @@ function main()::Int
250406
"total_backends" => TOTAL_BACKENDS,
251407
"coverage_percentage" => coverage_pct,
252408
"per_prover_counts" => per_prover,
409+
"balanced_output_file" => basename(BALANCED_OUTPUT_FILE),
410+
"balanced_cap_per_prover" => BALANCED_CAP_PER_PROVER,
411+
"balanced_total_proofs" => sum(values(balanced_counts)),
412+
"balanced_per_prover_counts" => Dict(
413+
k => v for (k, v) in sort(collect(balanced_counts); by=x -> -x[2])
414+
),
253415
"per_source_counts_top50" => per_source_top50,
254416
"vocabulary_size" => length(vocab),
255417
"source_files_used" => file_counts,
@@ -283,6 +445,8 @@ function main()::Int
283445
println(" Vocabulary words: $(lpad(string(length(vocab)), 10))")
284446
println(" Provers with data: $(lpad(string(provers_with_data), 10)) / $TOTAL_BACKENDS")
285447
println(" Coverage: $(lpad(string(coverage_pct), 9))%")
448+
println(" Balanced proofs: $(lpad(string(sum(values(balanced_counts))), 10))")
449+
println(" Cap per prover: $(lpad(string(BALANCED_CAP_PER_PROVER), 10))")
286450
println()
287451
println(" Per-prover breakdown:")
288452
for (prover, count) in sorted_prover_counts

scripts/vocabulary_5x_expansion.jl

Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
# Run: julia scripts/vocabulary_5x_expansion.jl
1212
# Output: training_data/vocabulary_5X.txt
1313

14+
const REPO_ROOT = dirname(dirname(abspath(@__FILE__)))
15+
const TYPELL_ROOT = abspath(joinpath(REPO_ROOT, "..", "typell"))
16+
const KATAGORIA_ROOT = abspath(joinpath(REPO_ROOT, "..", "..", "developer-ecosystem", "katagoria"))
17+
const TROPICAL_ROOT = abspath(joinpath(REPO_ROOT, "..", "tropical-resource-typing"))
18+
1419
"""
1520
prover_specific_vocabulary() -> Set{String}
1621
@@ -600,6 +605,82 @@ function proof_term_vocabulary()::Set{String}
600605
])
601606
end
602607

608+
"""
609+
typechecker_research_vocabulary() -> Set{String}
610+
611+
Add high-priority vocabulary for typechecker-heavy ecosystems and extract
612+
additional terms directly from TypeLL, Katagoria, and tropical-resource-typing.
613+
"""
614+
function typechecker_research_vocabulary()::Set{String}
615+
curated = Set{String}([
616+
# Requested type families
617+
"choreographic", "choreography", "endpoint", "endpoint-projection",
618+
"global-type", "local-type", "multiparty", "session-fidelity",
619+
"epistemic", "knowledge", "belief", "common-knowledge",
620+
"noninterference", "declassification", "accessibility-relation",
621+
"tropical", "semiring", "dioid", "kleene-star", "path-weight",
622+
"max-plus", "min-plus", "resource-budget", "latency", "throughput",
623+
"echo-type", "echo-types", "echo-check", "feedback-typing",
624+
"reflection-safety", "trace-stability",
625+
# Typechecker coverage
626+
"typechecker", "bidirectional", "synthesis", "checking", "elaboration",
627+
"constraint-solving", "constraint-generation", "occurs-check",
628+
"principal-type", "subsumption", "coercion", "kind-checking",
629+
"effect-row", "row-polymorphism", "effect-safety", "handler",
630+
"modal", "necessity", "possibility", "box", "dia", "phase-context",
631+
"transaction-scope", "security-level", "context-access",
632+
"qtt", "quantitative", "usage-quantifier", "bounded-usage",
633+
"affine", "linear", "omega-usage", "consumption-check",
634+
"dependent-index", "index-equality", "proof-obligation",
635+
"refinement", "predicate-subtyping", "solver-backed",
636+
"session-duality", "protocol-completeness", "branch-coverage",
637+
"resource-typing", "tropical-session", "typed-wasm-cost",
638+
"invariant-preservation", "progress", "preservation", "soundness",
639+
"completeness", "consistency", "termination", "totality",
640+
])
641+
642+
source_files = [
643+
joinpath(TYPELL_ROOT, "spec/type-system/dependent.adoc"),
644+
joinpath(TYPELL_ROOT, "spec/type-system/linear.adoc"),
645+
joinpath(TYPELL_ROOT, "spec/type-system/session.adoc"),
646+
joinpath(TYPELL_ROOT, "spec/type-system/modal.adoc"),
647+
joinpath(TYPELL_ROOT, "spec/type-system/effects.adoc"),
648+
joinpath(TYPELL_ROOT, "spec/type-system/qtt.adoc"),
649+
joinpath(TYPELL_ROOT, "spec/proof/proof-system.adoc"),
650+
joinpath(TYPELL_ROOT, "crates/typell-core/src/types.rs"),
651+
joinpath(TYPELL_ROOT, "crates/typell-core/src/session.rs"),
652+
joinpath(TYPELL_ROOT, "crates/typell-core/src/effects.rs"),
653+
joinpath(KATAGORIA_ROOT, "research/tropical/TropicalKleene.idr"),
654+
joinpath(KATAGORIA_ROOT, "verification/proofs/idris2/Types.idr"),
655+
joinpath(KATAGORIA_ROOT, "verification/proofs/lean4/ApiTypes.lean"),
656+
joinpath(KATAGORIA_ROOT, "verification/proofs/coq/TypeSafety.v"),
657+
joinpath(TROPICAL_ROOT, "Tropical_v2.thy"),
658+
joinpath(TROPICAL_ROOT, "Tropical_Kleene.thy"),
659+
joinpath(TROPICAL_ROOT, "Tropical_CNO.thy"),
660+
joinpath(TROPICAL_ROOT, "TropicalSessionTypes.lean"),
661+
]
662+
663+
stop = Set([
664+
"the", "and", "for", "with", "that", "this", "from", "into", "where",
665+
"have", "has", "are", "was", "were", "using", "then", "else", "when",
666+
"true", "false", "type", "types", "proof", "theorem", "lemma",
667+
])
668+
669+
extracted = Set{String}()
670+
for path in source_files
671+
isfile(path) || continue
672+
text = read(path, String)
673+
for m in eachmatch(r"[A-Za-z][A-Za-z0-9_+-]{3,}", text)
674+
token = lowercase(m.match)
675+
length(token) > 40 && continue
676+
token in stop && continue
677+
push!(extracted, token)
678+
end
679+
end
680+
681+
return union(curated, extracted)
682+
end
683+
603684
"""
604685
main()
605686
@@ -634,8 +715,18 @@ function main()
634715
proof_vocab = proof_term_vocabulary()
635716
println("Proof term vocabulary: $(length(proof_vocab)) terms")
636717

718+
typechecker_vocab = typechecker_research_vocabulary()
719+
println("Typechecker/research vocabulary: $(length(typechecker_vocab)) terms")
720+
637721
# Combine everything
638-
combined = union(existing, comprehensive, prover_vocab, math_vocab, proof_vocab)
722+
combined = union(
723+
existing,
724+
comprehensive,
725+
prover_vocab,
726+
math_vocab,
727+
proof_vocab,
728+
typechecker_vocab,
729+
)
639730

640731
println("\nCombined vocabulary: $(length(combined)) tokens")
641732
println("New tokens added: $(length(combined) - length(existing))")
@@ -660,10 +751,11 @@ function main()
660751
"prover_specific_added" => length(prover_vocab),
661752
"mathematics_added" => length(math_vocab),
662753
"proof_terms_added" => length(proof_vocab),
754+
"typechecker_research_added" => length(typechecker_vocab),
663755
"total_vocabulary" => length(combined),
664756
"expansion_ratio" => round(length(combined) / max(length(comprehensive), 1); digits=1),
665-
"provers_covered" => 48,
666-
"math_domains" => 10,
757+
"provers_covered" => 60,
758+
"math_domains" => 14,
667759
)
668760

669761
open("training_data/stats_5X.json", "w") do f

0 commit comments

Comments
 (0)