lean-interact 0.5.0__tar.gz → 0.5.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lean_interact-0.5.0 → lean_interact-0.5.2}/PKG-INFO +2 -2
- {lean_interact-0.5.0 → lean_interact-0.5.2}/README.md +1 -1
- {lean_interact-0.5.0 → lean_interact-0.5.2}/examples/beq_plus.py +26 -8
- {lean_interact-0.5.0 → lean_interact-0.5.2}/pyproject.toml +1 -1
- {lean_interact-0.5.0 → lean_interact-0.5.2}/src/lean_interact/config.py +2 -0
- {lean_interact-0.5.0 → lean_interact-0.5.2}/src/lean_interact/server.py +19 -9
- {lean_interact-0.5.0 → lean_interact-0.5.2}/src/lean_interact/utils.py +2 -2
- {lean_interact-0.5.0 → lean_interact-0.5.2}/tests/test_server.py +27 -1
- {lean_interact-0.5.0 → lean_interact-0.5.2}/.devcontainer/devcontainer.json +0 -0
- {lean_interact-0.5.0 → lean_interact-0.5.2}/.github/workflows/ci.yml +0 -0
- {lean_interact-0.5.0 → lean_interact-0.5.2}/.github/workflows/publish-to-pypi.yml +0 -0
- {lean_interact-0.5.0 → lean_interact-0.5.2}/.gitignore +0 -0
- {lean_interact-0.5.0 → lean_interact-0.5.2}/LICENSE +0 -0
- {lean_interact-0.5.0 → lean_interact-0.5.2}/examples/proof_generation_and_autoformalization.py +0 -0
- {lean_interact-0.5.0 → lean_interact-0.5.2}/examples/type_check.py +0 -0
- {lean_interact-0.5.0 → lean_interact-0.5.2}/src/lean_interact/__init__.py +0 -0
- {lean_interact-0.5.0 → lean_interact-0.5.2}/src/lean_interact/interface.py +0 -0
- {lean_interact-0.5.0 → lean_interact-0.5.2}/tests/test_concurrency.py +0 -0
- {lean_interact-0.5.0 → lean_interact-0.5.2}/tests/test_utils.py +0 -0
- {lean_interact-0.5.0 → lean_interact-0.5.2}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lean-interact
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.2
|
|
4
4
|
Summary: LeanInteract is a Python package that allows you to interact with the Lean theorem prover.
|
|
5
5
|
Author-email: Auguste Poiroux <auguste.poiroux@epfl.ch>
|
|
6
6
|
License: MIT License
|
|
@@ -49,7 +49,7 @@ Description-Content-Type: text/markdown
|
|
|
49
49
|
- **🔗 Interactivity**: Execute Lean code and files directly from Python.
|
|
50
50
|
- **🚀 Ease of Use**: LeanInteract abstracts the complexities of Lean setup and interaction.
|
|
51
51
|
- **💻 Cross-platform**: Works on Windows, macOS, and Linux operating systems.
|
|
52
|
-
- **🔧 Compatibility**: Supports all Lean versions between `v4.7.0-rc1` and `v4.19.0
|
|
52
|
+
- **🔧 Compatibility**: Supports all Lean versions between `v4.7.0-rc1` and `v4.19.0`.
|
|
53
53
|
- We backport the latest features of Lean REPL to older versions of Lean.
|
|
54
54
|
- **📦 Temporary Projects**: Easily instantiate temporary Lean environments.
|
|
55
55
|
- Useful for experimenting with benchmarks depending on [Mathlib](https://github.com/leanprover-community/mathlib4) like [ProofNet#](https://huggingface.co/datasets/PAug/ProofNetSharp) and [MiniF2F](https://github.com/yangky11/miniF2F-lean4).
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
- **🔗 Interactivity**: Execute Lean code and files directly from Python.
|
|
13
13
|
- **🚀 Ease of Use**: LeanInteract abstracts the complexities of Lean setup and interaction.
|
|
14
14
|
- **💻 Cross-platform**: Works on Windows, macOS, and Linux operating systems.
|
|
15
|
-
- **🔧 Compatibility**: Supports all Lean versions between `v4.7.0-rc1` and `v4.19.0
|
|
15
|
+
- **🔧 Compatibility**: Supports all Lean versions between `v4.7.0-rc1` and `v4.19.0`.
|
|
16
16
|
- We backport the latest features of Lean REPL to older versions of Lean.
|
|
17
17
|
- **📦 Temporary Projects**: Easily instantiate temporary Lean environments.
|
|
18
18
|
- Useful for experimenting with benchmarks depending on [Mathlib](https://github.com/leanprover-community/mathlib4) like [ProofNet#](https://huggingface.co/datasets/PAug/ProofNetSharp) and [MiniF2F](https://github.com/yangky11/miniF2F-lean4).
|
|
@@ -156,14 +156,14 @@ def beql(
|
|
|
156
156
|
except ValueError:
|
|
157
157
|
if verbose:
|
|
158
158
|
console.print("Invalid theorems encountered, skipping this pair.")
|
|
159
|
-
|
|
159
|
+
break
|
|
160
160
|
|
|
161
161
|
formal_code = formal_1_code + formal_2_code
|
|
162
162
|
# Preliminary check to ensure the formalization is well-typed.
|
|
163
163
|
if check_proof_sub(server, formal_code, context_env, formal_2_start_line, "sorry", timeout_per_proof) is None:
|
|
164
164
|
if verbose:
|
|
165
165
|
console.print("Ill-typed formalization encountered, skipping this pair.")
|
|
166
|
-
|
|
166
|
+
break
|
|
167
167
|
|
|
168
168
|
proof_exact = check_proof_sub(
|
|
169
169
|
server, formal_code, context_env, formal_2_start_line, "exact?", timeout_per_proof
|
|
@@ -173,6 +173,8 @@ def beql(
|
|
|
173
173
|
if verbose:
|
|
174
174
|
console.print("Proof exact")
|
|
175
175
|
console.print(Syntax(proof_exact, "lean4"))
|
|
176
|
+
else:
|
|
177
|
+
break
|
|
176
178
|
|
|
177
179
|
return res[0] and res[1]
|
|
178
180
|
|
|
@@ -229,13 +231,13 @@ def beq_plus(
|
|
|
229
231
|
except ValueError:
|
|
230
232
|
if verbose:
|
|
231
233
|
console.print("Invalid theorem encountered, skipping this pair.")
|
|
232
|
-
|
|
234
|
+
break
|
|
233
235
|
|
|
234
236
|
formal_code = formal_1_code + formal_2_code
|
|
235
237
|
if check_proof_sub(server, formal_code, context_env, formal_2_start_line, "sorry", timeout_per_proof) is None:
|
|
236
238
|
if verbose:
|
|
237
239
|
console.print("Ill-typed formalization encountered, skipping this pair.")
|
|
238
|
-
|
|
240
|
+
break
|
|
239
241
|
|
|
240
242
|
# 1. Use BEqL
|
|
241
243
|
proof_exact = check_proof_sub(
|
|
@@ -322,6 +324,9 @@ def beq_plus(
|
|
|
322
324
|
console.print(Syntax(proof_convert, "lean4"))
|
|
323
325
|
break
|
|
324
326
|
|
|
327
|
+
if not res[i]:
|
|
328
|
+
break
|
|
329
|
+
|
|
325
330
|
return res[0] and res[1]
|
|
326
331
|
|
|
327
332
|
|
|
@@ -382,22 +387,35 @@ open scoped BigOperators"""
|
|
|
382
387
|
]
|
|
383
388
|
|
|
384
389
|
console.print(f"{metric.__name__} metric on examples:")
|
|
390
|
+
counter_equivalent = 0
|
|
385
391
|
for formalization_1, formalization_2 in formalization_pairs:
|
|
386
392
|
console.print()
|
|
387
393
|
console.rule()
|
|
388
394
|
console.print("Comparing formalizations:")
|
|
389
395
|
console.print(Syntax(formalization_1, "lean4"))
|
|
390
396
|
console.print(Syntax(formalization_2, "lean4"))
|
|
391
|
-
|
|
392
|
-
|
|
397
|
+
equivalent = metric(
|
|
398
|
+
formalization_1,
|
|
399
|
+
formalization_2,
|
|
400
|
+
src_header,
|
|
401
|
+
repl_config,
|
|
402
|
+
timeout_per_proof=DEFAULT_TIMEOUT,
|
|
403
|
+
verbose=True,
|
|
393
404
|
)
|
|
405
|
+
counter_equivalent += equivalent
|
|
406
|
+
if equivalent:
|
|
407
|
+
console.print("[green]Proved equivalent[/]")
|
|
408
|
+
else:
|
|
409
|
+
console.print("[red]Equivalence not proven[/]")
|
|
410
|
+
console.print()
|
|
411
|
+
console.print(f"Total proved equivalent: {counter_equivalent}/{len(formalization_pairs)}")
|
|
394
412
|
|
|
395
413
|
|
|
396
|
-
def proofnetverif(metric):
|
|
414
|
+
def proofnetverif(metric, n_samples=100):
|
|
397
415
|
repl_config = LeanREPLConfig(lean_version="v4.8.0", project=TempRequireProject("mathlib"), verbose=True)
|
|
398
416
|
|
|
399
417
|
dataset = load_dataset("PAug/ProofNetVerif", split="valid")
|
|
400
|
-
dataset = dataset.shuffle(seed=42).select(range(
|
|
418
|
+
dataset = dataset.shuffle(seed=42).select(range(n_samples))
|
|
401
419
|
|
|
402
420
|
metric_results = []
|
|
403
421
|
for example in tqdm(dataset, desc=f"`{metric.__name__}` metric on ProofNetVerif dataset"):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "lean-interact"
|
|
3
|
-
version = "0.5.
|
|
3
|
+
version = "0.5.2"
|
|
4
4
|
description = "LeanInteract is a Python package that allows you to interact with the Lean theorem prover."
|
|
5
5
|
keywords = ["Lean", "theorem proving", "autoformalization", "REPL"]
|
|
6
6
|
license = { file = "LICENSE" }
|
|
@@ -59,6 +59,7 @@ class LocalProject(BaseProject):
|
|
|
59
59
|
|
|
60
60
|
with FileLock(f"{self.directory}.lock"):
|
|
61
61
|
try:
|
|
62
|
+
subprocess.run(["lake", "exe", "cache", "get"], cwd=self.directory, check=False, stdout=stdout, stderr=stderr)
|
|
62
63
|
subprocess.run(["lake", "build"], cwd=self.directory, check=True, stdout=stdout, stderr=stderr)
|
|
63
64
|
except subprocess.CalledProcessError as e:
|
|
64
65
|
logger.error("Failed to build local project: %s", e)
|
|
@@ -102,6 +103,7 @@ class GitProject(BaseProject):
|
|
|
102
103
|
repo.submodule_update(init=True, recursive=True)
|
|
103
104
|
|
|
104
105
|
try:
|
|
106
|
+
subprocess.run(["lake", "exe", "cache", "get"], cwd=project_dir, check=False, stdout=stdout, stderr=stderr)
|
|
105
107
|
subprocess.run(["lake", "build"], cwd=project_dir, check=True, stdout=stdout, stderr=stderr)
|
|
106
108
|
except subprocess.CalledProcessError as e:
|
|
107
109
|
logger.error("Failed to build the git project: %s", e)
|
|
@@ -71,6 +71,7 @@ class LeanServer:
|
|
|
71
71
|
encoding="utf-8",
|
|
72
72
|
text=True,
|
|
73
73
|
bufsize=1,
|
|
74
|
+
start_new_session=True,
|
|
74
75
|
preexec_fn=None
|
|
75
76
|
if platform.system() != "Linux"
|
|
76
77
|
else lambda: _limit_memory(self.config.memory_hard_limit_mb),
|
|
@@ -86,15 +87,24 @@ class LeanServer:
|
|
|
86
87
|
|
|
87
88
|
def kill(self) -> None:
|
|
88
89
|
if self._proc:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
90
|
+
try:
|
|
91
|
+
proc = psutil.Process(self._proc.pid)
|
|
92
|
+
# Terminate the process tree
|
|
93
|
+
children = proc.children(recursive=True)
|
|
94
|
+
for child in children:
|
|
95
|
+
try:
|
|
96
|
+
child.terminate()
|
|
97
|
+
except Exception:
|
|
98
|
+
pass
|
|
99
|
+
proc.terminate()
|
|
100
|
+
_, alive = psutil.wait_procs([proc] + children, timeout=1)
|
|
101
|
+
for p in alive:
|
|
102
|
+
try:
|
|
103
|
+
p.kill()
|
|
104
|
+
except Exception:
|
|
105
|
+
pass
|
|
106
|
+
except Exception:
|
|
107
|
+
pass
|
|
98
108
|
self._proc = None
|
|
99
109
|
gc.collect()
|
|
100
110
|
|
|
@@ -20,7 +20,7 @@ logger.addHandler(handler)
|
|
|
20
20
|
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
21
21
|
DEFAULT_CACHE_DIR = os.path.join(ROOT_DIR, "cache")
|
|
22
22
|
DEFAULT_REPL_GIT_URL = "https://github.com/augustepoiroux/repl"
|
|
23
|
-
DEFAULT_REPL_VERSION = "v1.0.
|
|
23
|
+
DEFAULT_REPL_VERSION = "v1.0.8"
|
|
24
24
|
|
|
25
25
|
os.makedirs(DEFAULT_CACHE_DIR, exist_ok=True)
|
|
26
26
|
|
|
@@ -334,7 +334,7 @@ def clean_theorem_string(theorem_string: str, new_theorem_name: str = "dummy", a
|
|
|
334
334
|
clean_formal = re.sub(r"^[^\s:({\[]+", "", clean_formal).strip()
|
|
335
335
|
clean_formal = f"theorem {new_theorem_name} " + clean_formal
|
|
336
336
|
if add_sorry:
|
|
337
|
-
clean_formal += " := sorry"
|
|
337
|
+
clean_formal += " := by sorry"
|
|
338
338
|
return clean_formal
|
|
339
339
|
except Exception:
|
|
340
340
|
return None
|
|
@@ -295,7 +295,7 @@ lean_exe "dummy" where
|
|
|
295
295
|
],
|
|
296
296
|
),
|
|
297
297
|
)
|
|
298
|
-
result = server.run(ProofStep(tactic="apply
|
|
298
|
+
result = server.run(ProofStep(tactic="apply irrational_add_ratCast_iff.mpr", proof_state=0), verbose=True)
|
|
299
299
|
self.assertEqual(result, ProofStepResponse(proof_state=1, goals=[], proof_status="Completed"))
|
|
300
300
|
|
|
301
301
|
def test_restart_with_env(self):
|
|
@@ -416,6 +416,32 @@ lean_exe "dummy" where
|
|
|
416
416
|
with self.assertRaises(ChildProcessError):
|
|
417
417
|
server.run(Command(cmd="def z := 3"), verbose=True)
|
|
418
418
|
|
|
419
|
+
def test_timeout_respected(self):
|
|
420
|
+
if platform.system() == "Windows":
|
|
421
|
+
self.skipTest("(Temporary) Skipping test on Windows due to long path issues in the CI")
|
|
422
|
+
|
|
423
|
+
config = LeanREPLConfig(project=TempRequireProject("mathlib"))
|
|
424
|
+
server = AutoLeanServer(config)
|
|
425
|
+
|
|
426
|
+
response = server.run(
|
|
427
|
+
Command(cmd="import Mathlib\nset_option maxHeartbeats 0\nset_option maxRecDepth 100000"),
|
|
428
|
+
add_to_session_cache=True,
|
|
429
|
+
)
|
|
430
|
+
assert isinstance(response, CommandResponse)
|
|
431
|
+
root_env = response.env
|
|
432
|
+
|
|
433
|
+
# check that the next command takes less than 3 seconds
|
|
434
|
+
start = time.time()
|
|
435
|
+
with self.assertRaises(TimeoutError):
|
|
436
|
+
server.run(
|
|
437
|
+
Command(
|
|
438
|
+
cmd="theorem amc12a_2003_p1 (u v : ℕ → ℕ) (h₀ : ∀ n, u n = 2 * n + 2) (h₁ : ∀ n, v n = 2 * n + 1) :\n ((∑ k in Finset.range 2003, u k) - ∑ k in Finset.range 2003, v k) = 2003 := by simp only [h₀, h₁, Finset.sum_range_succ, Finset.sum_range_zero]; rfl",
|
|
439
|
+
env=root_env,
|
|
440
|
+
),
|
|
441
|
+
timeout=2,
|
|
442
|
+
)
|
|
443
|
+
self.assertLess(time.time() - start, 3)
|
|
444
|
+
|
|
419
445
|
# def test_run_proof(self):
|
|
420
446
|
# server = AutoLeanServer(config=LeanREPLConfig(verbose=True))
|
|
421
447
|
# result = server.run(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lean_interact-0.5.0 → lean_interact-0.5.2}/examples/proof_generation_and_autoformalization.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|