update to include tracks, remove scoring on hf
Browse filesThis view is limited to 50 files because it contains too many changes. Β See raw diff
- .dockerignore +9 -11
- .gitattributes +2 -0
- .gitignore +0 -20
- Dockerfile +7 -48
- PyPantograph/.github/workflows/doc.yaml +0 -54
- PyPantograph/.github/workflows/test.yaml +0 -38
- PyPantograph/.gitignore +0 -11
- PyPantograph/LICENSE +0 -190
- PyPantograph/README.md +0 -77
- PyPantograph/build-pantograph.py +0 -25
- PyPantograph/doc/.gitignore +0 -1
- PyPantograph/doc/_config.yml +0 -71
- PyPantograph/doc/_toc.yml +0 -16
- PyPantograph/doc/agent-search.ipynb +0 -155
- PyPantograph/doc/api-data.rst +0 -5
- PyPantograph/doc/api-expr.rst +0 -8
- PyPantograph/doc/api-message.rst +0 -5
- PyPantograph/doc/api-search.rst +0 -5
- PyPantograph/doc/api-server.rst +0 -5
- PyPantograph/doc/frontend.ipynb +0 -401
- PyPantograph/doc/goal.ipynb +0 -662
- PyPantograph/doc/intro.md +0 -79
- PyPantograph/doc/setup.md +0 -85
- PyPantograph/examples/Example/.gitignore +0 -3
- PyPantograph/examples/Example/Example.lean +0 -14
- PyPantograph/examples/Example/lake-manifest.json +0 -25
- PyPantograph/examples/Example/lakefile.lean +0 -10
- PyPantograph/examples/Example/lean-toolchain +0 -1
- PyPantograph/examples/README.md +0 -24
- PyPantograph/examples/aesop.py +0 -14
- PyPantograph/examples/branch-sorry.py +0 -13
- PyPantograph/examples/simple.py +0 -7
- PyPantograph/examples/sketch.py +0 -41
- PyPantograph/pantograph/.gitignore +0 -2
- PyPantograph/pantograph/__init__.py +0 -1
- PyPantograph/pantograph/data.py +0 -113
- PyPantograph/pantograph/expr.py +0 -202
- PyPantograph/pantograph/message.py +0 -73
- PyPantograph/pantograph/search.py +0 -450
- PyPantograph/pantograph/server.py +0 -695
- PyPantograph/pantograph/test_message.py +0 -25
- PyPantograph/pantograph/test_search.py +0 -55
- PyPantograph/pantograph/test_server.py +0 -381
- PyPantograph/pantograph/utils.py +0 -48
- PyPantograph/pyproject.toml +0 -39
- PyPantograph/src/.gitignore +0 -6
- PyPantograph/src/.pre-commit-config.yaml +0 -30
- PyPantograph/src/LICENSE +0 -190
- PyPantograph/src/Main.lean +0 -80
- PyPantograph/src/Pantograph.lean +0 -9
.dockerignore
CHANGED
|
@@ -1,12 +1,10 @@
|
|
| 1 |
-
PyPantograph/.venv
|
| 2 |
-
PyPantograph/dist
|
| 3 |
-
PyPantograph/pantograph/pantograph-repl
|
| 4 |
-
PyPantograph/src/.lake
|
| 5 |
-
lean-project-copy
|
| 6 |
-
lean-project/.lake
|
| 7 |
-
cslib/.lake
|
| 8 |
-
cslib/.git
|
| 9 |
-
cslib_docker_test
|
| 10 |
-
**/__pycache__
|
| 11 |
-
**/*.pyc
|
| 12 |
.git
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
.git
|
| 2 |
+
.gitignore
|
| 3 |
+
.gitattributes
|
| 4 |
+
.claude
|
| 5 |
+
CLAUDE.md
|
| 6 |
+
__pycache__
|
| 7 |
+
*.pyc
|
| 8 |
+
.DS_Store
|
| 9 |
+
README.md
|
| 10 |
+
leaderboard.json
|
.gitattributes
CHANGED
|
@@ -36,3 +36,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 36 |
assets/icon.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
assets/sponsors/cslib.png filter=lfs diff=lfs merge=lfs -text
|
| 38 |
assets/sponsors/PhysLibLogo.jpeg filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
| 36 |
assets/icon.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
assets/sponsors/cslib.png filter=lfs diff=lfs merge=lfs -text
|
| 38 |
assets/sponsors/PhysLibLogo.jpeg filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
assets/benchmarks/cslib.png filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
assets/benchmarks/putnambench.png filter=lfs diff=lfs merge=lfs -text
|
.gitignore
CHANGED
|
@@ -7,23 +7,3 @@ gradio_cached_examples/
|
|
| 7 |
flagged/
|
| 8 |
leaderboard.json
|
| 9 |
.leaderboard.*.json
|
| 10 |
-
|
| 11 |
-
# Lean build artifacts β rebuilt inside the Docker image (lake cache get / build),
|
| 12 |
-
# never pushed to the Space.
|
| 13 |
-
lean-project/.lake/
|
| 14 |
-
lean-project-copy/
|
| 15 |
-
cslib/.lake/
|
| 16 |
-
|
| 17 |
-
# Local cslib compilation-test scaffolding (Docker build contexts + logs).
|
| 18 |
-
cslib_docker_test/build_ctx/
|
| 19 |
-
cslib_docker_test/build_ctx_submit/
|
| 20 |
-
cslib_docker_test/*.log
|
| 21 |
-
|
| 22 |
-
# cslib files not needed by the Space (kept on disk, excluded from the repo).
|
| 23 |
-
cslib/.github/
|
| 24 |
-
cslib/.vscode/
|
| 25 |
-
cslib/scripts/
|
| 26 |
-
cslib/docs/
|
| 27 |
-
cslib/eval_cslib_long.jsonl
|
| 28 |
-
cslib/eval_cslib_long_optimized.jsonl
|
| 29 |
-
cslib/make_eval_jsonl.py
|
|
|
|
| 7 |
flagged/
|
| 8 |
leaderboard.json
|
| 9 |
.leaderboard.*.json
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Dockerfile
CHANGED
|
@@ -1,60 +1,19 @@
|
|
| 1 |
FROM python:3.11-slim
|
| 2 |
|
| 3 |
-
# System packages: curl for elan install, git for lake deps later, ca-certs for HTTPS
|
| 4 |
-
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 5 |
-
curl ca-certificates git \
|
| 6 |
-
&& rm -rf /var/lib/apt/lists/*
|
| 7 |
-
|
| 8 |
RUN useradd -m -u 1000 user
|
| 9 |
USER user
|
| 10 |
ENV HOME=/home/user \
|
| 11 |
-
PATH=/home/user/.
|
| 12 |
|
| 13 |
WORKDIR $HOME/app
|
| 14 |
|
| 15 |
-
#
|
| 16 |
-
#
|
| 17 |
-
#
|
| 18 |
-
RUN pip install --no-cache-dir --user "gradio>=5,<6"
|
| 19 |
-
|
| 20 |
-
# Install elan (Lean toolchain manager). No default toolchain; the
|
| 21 |
-
# project's `lean-toolchain` file controls the version.
|
| 22 |
-
RUN curl -sSfL https://raw.githubusercontent.com/leanprover/elan/master/elan-init.sh \
|
| 23 |
-
| sh -s -- -y --default-toolchain none --no-modify-path
|
| 24 |
-
|
| 25 |
-
# Copy the Lean project skeleton, materialize the toolchain, fetch Mathlib
|
| 26 |
-
# source + transitive deps, and download pre-built oleans (~5 GB). All of
|
| 27 |
-
# this is baked into a Docker layer so cold starts on HF are instant.
|
| 28 |
-
COPY --chown=user lean-project /home/user/lean-project
|
| 29 |
-
WORKDIR /home/user/lean-project
|
| 30 |
-
RUN lake --version \
|
| 31 |
-
&& lake update \
|
| 32 |
-
&& lake exe cache get
|
| 33 |
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
# .dockerignore β so deps are materialized from the committed manifest, Mathlib
|
| 37 |
-
# oleans are fetched, and the library is built here. app.py compiles
|
| 38 |
-
# project-resident submissions against /home/user/cslib (CSLIB_PROJECT_ROOT).
|
| 39 |
-
COPY --chown=user cslib /home/user/cslib
|
| 40 |
-
WORKDIR /home/user/cslib
|
| 41 |
-
RUN lake exe cache get \
|
| 42 |
-
&& lake build
|
| 43 |
-
|
| 44 |
-
# Build PyPantograph: copy the source, invoke `uv build` which runs the
|
| 45 |
-
# poetry-core build hook (build-pantograph.py β `lake build repl` inside
|
| 46 |
-
# PyPantograph/src/) and packages the resulting binary into a wheel. Then
|
| 47 |
-
# `pip install` that wheel into the user site-packages alongside gradio.
|
| 48 |
-
COPY --chown=user PyPantograph /home/user/PyPantograph
|
| 49 |
-
WORKDIR /home/user/PyPantograph
|
| 50 |
-
RUN uv build \
|
| 51 |
-
&& pip install --no-cache-dir --user /home/user/PyPantograph/dist/pantograph-*.whl
|
| 52 |
-
|
| 53 |
-
# App
|
| 54 |
-
WORKDIR $HOME/app
|
| 55 |
-
COPY --chown=user app.py utils.py benchmark.py leaderboard.py versions.py \
|
| 56 |
-
pantograph_server.py project_compile.py \
|
| 57 |
-
benchmark_data_new.jsonl benchmark_heartbeats.jsonl .
|
| 58 |
COPY --chown=user assets /home/user/app/assets
|
| 59 |
|
| 60 |
EXPOSE 7860
|
|
|
|
| 1 |
FROM python:3.11-slim
|
| 2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
RUN useradd -m -u 1000 user
|
| 4 |
USER user
|
| 5 |
ENV HOME=/home/user \
|
| 6 |
+
PATH=/home/user/.local/bin:$PATH
|
| 7 |
|
| 8 |
WORKDIR $HOME/app
|
| 9 |
|
| 10 |
+
# UI-only image: all Lean compilation and scoring runs on a dedicated
|
| 11 |
+
# evaluation worker that exchanges jobs/results with this Space through the
|
| 12 |
+
# shared storage bucket mounted at /data. Nothing Lean-related is installed.
|
| 13 |
+
RUN pip install --no-cache-dir --user "gradio>=5,<6"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
|
| 15 |
+
COPY --chown=user app.py benchmark.py leaderboard.py contribution.md \
|
| 16 |
+
benchmark_data_warmup.jsonl benchmark_heartbeats.jsonl .
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
COPY --chown=user assets /home/user/app/assets
|
| 18 |
|
| 19 |
EXPOSE 7860
|
PyPantograph/.github/workflows/doc.yaml
DELETED
|
@@ -1,54 +0,0 @@
|
|
| 1 |
-
name: deploy-doc
|
| 2 |
-
|
| 3 |
-
on:
|
| 4 |
-
push:
|
| 5 |
-
branches:
|
| 6 |
-
- main
|
| 7 |
-
|
| 8 |
-
# This job installs dependencies, builds the book, and pushes it to `gh-pages`
|
| 9 |
-
jobs:
|
| 10 |
-
deploy-book:
|
| 11 |
-
runs-on: ubuntu-latest
|
| 12 |
-
permissions:
|
| 13 |
-
pages: write
|
| 14 |
-
id-token: write
|
| 15 |
-
steps:
|
| 16 |
-
- uses: actions/checkout@v4
|
| 17 |
-
with:
|
| 18 |
-
submodules: true
|
| 19 |
-
|
| 20 |
-
- name: Install Python
|
| 21 |
-
uses: actions/setup-python@v4
|
| 22 |
-
with:
|
| 23 |
-
python-version: 3.11
|
| 24 |
-
|
| 25 |
-
- name: Install elan
|
| 26 |
-
run: |
|
| 27 |
-
set -o pipefail
|
| 28 |
-
curl -sSfL https://github.com/leanprover/elan/releases/download/v3.1.1/elan-x86_64-unknown-linux-gnu.tar.gz | tar xz
|
| 29 |
-
./elan-init -y --default-toolchain none
|
| 30 |
-
echo "$HOME/.elan/bin" >> "${GITHUB_PATH}"
|
| 31 |
-
|
| 32 |
-
- name: Install Lean
|
| 33 |
-
run: |
|
| 34 |
-
elan toolchain install $(<src/lean-toolchain)
|
| 35 |
-
|
| 36 |
-
- name: Install uv
|
| 37 |
-
run: |
|
| 38 |
-
curl -LsSf https://astral.sh/uv/install.sh | sh
|
| 39 |
-
uv sync
|
| 40 |
-
|
| 41 |
-
- name: Build documentations
|
| 42 |
-
run: |
|
| 43 |
-
uv run --group dev jupyter-book build doc
|
| 44 |
-
|
| 45 |
-
# Upload the book's HTML as an artifact
|
| 46 |
-
- name: Upload artifact
|
| 47 |
-
uses: actions/upload-pages-artifact@v3
|
| 48 |
-
with:
|
| 49 |
-
path: "doc/_build/html"
|
| 50 |
-
|
| 51 |
-
# Deploy the book's HTML to GitHub Pages
|
| 52 |
-
- name: Deploy to GitHub Pages
|
| 53 |
-
id: deployment
|
| 54 |
-
uses: actions/deploy-pages@v4
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/.github/workflows/test.yaml
DELETED
|
@@ -1,38 +0,0 @@
|
|
| 1 |
-
name: Unit Tests
|
| 2 |
-
on: [push]
|
| 3 |
-
|
| 4 |
-
jobs:
|
| 5 |
-
build:
|
| 6 |
-
runs-on: ubuntu-latest
|
| 7 |
-
permissions:
|
| 8 |
-
pages: write
|
| 9 |
-
id-token: write
|
| 10 |
-
steps:
|
| 11 |
-
- uses: actions/checkout@v4
|
| 12 |
-
with:
|
| 13 |
-
submodules: true
|
| 14 |
-
|
| 15 |
-
- name: Install Python
|
| 16 |
-
uses: actions/setup-python@v4
|
| 17 |
-
with:
|
| 18 |
-
python-version: 3.11
|
| 19 |
-
|
| 20 |
-
- name: Install elan
|
| 21 |
-
run: |
|
| 22 |
-
set -o pipefail
|
| 23 |
-
curl -sSfL https://github.com/leanprover/elan/releases/download/v3.1.1/elan-x86_64-unknown-linux-gnu.tar.gz | tar xz
|
| 24 |
-
./elan-init -y --default-toolchain none
|
| 25 |
-
echo "$HOME/.elan/bin" >> "${GITHUB_PATH}"
|
| 26 |
-
|
| 27 |
-
- name: Install Lean
|
| 28 |
-
run: |
|
| 29 |
-
elan toolchain install $(<src/lean-toolchain)
|
| 30 |
-
|
| 31 |
-
- name: Install uv
|
| 32 |
-
run: |
|
| 33 |
-
curl -LsSf https://astral.sh/uv/install.sh | sh
|
| 34 |
-
uv sync
|
| 35 |
-
|
| 36 |
-
- name: Run tests
|
| 37 |
-
run: |
|
| 38 |
-
uv run pytest
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/.gitignore
DELETED
|
@@ -1,11 +0,0 @@
|
|
| 1 |
-
.*
|
| 2 |
-
!.gitignore
|
| 3 |
-
!.github
|
| 4 |
-
|
| 5 |
-
# Python
|
| 6 |
-
*.py[cod]
|
| 7 |
-
*.egg-info
|
| 8 |
-
|
| 9 |
-
# Output
|
| 10 |
-
/dist
|
| 11 |
-
/venv
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/LICENSE
DELETED
|
@@ -1,190 +0,0 @@
|
|
| 1 |
-
Apache License
|
| 2 |
-
Version 2.0, January 2004
|
| 3 |
-
http://www.apache.org/licenses/
|
| 4 |
-
|
| 5 |
-
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 6 |
-
|
| 7 |
-
1. Definitions.
|
| 8 |
-
|
| 9 |
-
"License" shall mean the terms and conditions for use, reproduction,
|
| 10 |
-
and distribution as defined by Sections 1 through 9 of this document.
|
| 11 |
-
|
| 12 |
-
"Licensor" shall mean the copyright owner or entity authorized by
|
| 13 |
-
the copyright owner that is granting the License.
|
| 14 |
-
|
| 15 |
-
"Legal Entity" shall mean the union of the acting entity and all
|
| 16 |
-
other entities that control, are controlled by, or are under common
|
| 17 |
-
control with that entity. For the purposes of this definition,
|
| 18 |
-
"control" means (i) the power, direct or indirect, to cause the
|
| 19 |
-
direction or management of such entity, whether by contract or
|
| 20 |
-
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 21 |
-
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 22 |
-
|
| 23 |
-
"You" (or "Your") shall mean an individual or Legal Entity
|
| 24 |
-
exercising permissions granted by this License.
|
| 25 |
-
|
| 26 |
-
"Source" form shall mean the preferred form for making modifications,
|
| 27 |
-
including but not limited to software source code, documentation
|
| 28 |
-
source, and configuration files.
|
| 29 |
-
|
| 30 |
-
"Object" form shall mean any form resulting from mechanical
|
| 31 |
-
transformation or translation of a Source form, including but
|
| 32 |
-
not limited to compiled object code, generated documentation,
|
| 33 |
-
and conversions to other media types.
|
| 34 |
-
|
| 35 |
-
"Work" shall mean the work of authorship, whether in Source or
|
| 36 |
-
Object form, made available under the License, as indicated by a
|
| 37 |
-
copyright notice that is included in or attached to the work
|
| 38 |
-
(an example is provided in the Appendix below).
|
| 39 |
-
|
| 40 |
-
"Derivative Works" shall mean any work, whether in Source or Object
|
| 41 |
-
form, that is based on (or derived from) the Work and for which the
|
| 42 |
-
editorial revisions, annotations, elaborations, or other modifications
|
| 43 |
-
represent, as a whole, an original work of authorship. For the purposes
|
| 44 |
-
of this License, Derivative Works shall not include works that remain
|
| 45 |
-
separable from, or merely link (or bind by name) to the interfaces of,
|
| 46 |
-
the Work and Derivative Works thereof.
|
| 47 |
-
|
| 48 |
-
"Contribution" shall mean any work of authorship, including
|
| 49 |
-
the original version of the Work and any modifications or additions
|
| 50 |
-
to that Work or Derivative Works thereof, that is intentionally
|
| 51 |
-
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 52 |
-
or by an individual or Legal Entity authorized to submit on behalf of
|
| 53 |
-
the copyright owner. For the purposes of this definition, "submitted"
|
| 54 |
-
means any form of electronic, verbal, or written communication sent
|
| 55 |
-
to the Licensor or its representatives, including but not limited to
|
| 56 |
-
communication on electronic mailing lists, source code control systems,
|
| 57 |
-
and issue tracking systems that are managed by, or on behalf of, the
|
| 58 |
-
Licensor for the purpose of discussing and improving the Work, but
|
| 59 |
-
excluding communication that is conspicuously marked or otherwise
|
| 60 |
-
designated in writing by the copyright owner as "Not a Contribution."
|
| 61 |
-
|
| 62 |
-
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 63 |
-
on behalf of whom a Contribution has been received by Licensor and
|
| 64 |
-
subsequently incorporated within the Work.
|
| 65 |
-
|
| 66 |
-
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 67 |
-
this License, each Contributor hereby grants to You a perpetual,
|
| 68 |
-
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 69 |
-
copyright license to reproduce, prepare Derivative Works of,
|
| 70 |
-
publicly display, publicly perform, sublicense, and distribute the
|
| 71 |
-
Work and such Derivative Works in Source or Object form.
|
| 72 |
-
|
| 73 |
-
3. Grant of Patent License. Subject to the terms and conditions of
|
| 74 |
-
this License, each Contributor hereby grants to You a perpetual,
|
| 75 |
-
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 76 |
-
(except as stated in this section) patent license to make, have made,
|
| 77 |
-
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 78 |
-
where such license applies only to those patent claims licensable
|
| 79 |
-
by such Contributor that are necessarily infringed by their
|
| 80 |
-
Contribution(s) alone or by combination of their Contribution(s)
|
| 81 |
-
with the Work to which such Contribution(s) was submitted. If You
|
| 82 |
-
institute patent litigation against any entity (including a
|
| 83 |
-
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 84 |
-
or a Contribution incorporated within the Work constitutes direct
|
| 85 |
-
or contributory patent infringement, then any patent licenses
|
| 86 |
-
granted to You under this License for that Work shall terminate
|
| 87 |
-
as of the date such litigation is filed.
|
| 88 |
-
|
| 89 |
-
4. Redistribution. You may reproduce and distribute copies of the
|
| 90 |
-
Work or Derivative Works thereof in any medium, with or without
|
| 91 |
-
modifications, and in Source or Object form, provided that You
|
| 92 |
-
meet the following conditions:
|
| 93 |
-
|
| 94 |
-
(a) You must give any other recipients of the Work or
|
| 95 |
-
Derivative Works a copy of this License; and
|
| 96 |
-
|
| 97 |
-
(b) You must cause any modified files to carry prominent notices
|
| 98 |
-
stating that You changed the files; and
|
| 99 |
-
|
| 100 |
-
(c) You must retain, in the Source form of any Derivative Works
|
| 101 |
-
that You distribute, all copyright, patent, trademark, and
|
| 102 |
-
attribution notices from the Source form of the Work,
|
| 103 |
-
excluding those notices that do not pertain to any part of
|
| 104 |
-
the Derivative Works; and
|
| 105 |
-
|
| 106 |
-
(d) If the Work includes a "NOTICE" text file as part of its
|
| 107 |
-
distribution, then any Derivative Works that You distribute must
|
| 108 |
-
include a readable copy of the attribution notices contained
|
| 109 |
-
within such NOTICE file, excluding those notices that do not
|
| 110 |
-
pertain to any part of the Derivative Works, in at least one
|
| 111 |
-
of the following places: within a NOTICE text file distributed
|
| 112 |
-
as part of the Derivative Works; within the Source form or
|
| 113 |
-
documentation, if provided along with the Derivative Works; or,
|
| 114 |
-
within a display generated by the Derivative Works, if and
|
| 115 |
-
wherever such third-party notices normally appear. The contents
|
| 116 |
-
of the NOTICE file are for informational purposes only and
|
| 117 |
-
do not modify the License. You may add Your own attribution
|
| 118 |
-
notices within Derivative Works that You distribute, alongside
|
| 119 |
-
or as an addendum to the NOTICE text from the Work, provided
|
| 120 |
-
that such additional attribution notices cannot be construed
|
| 121 |
-
as modifying the License.
|
| 122 |
-
|
| 123 |
-
You may add Your own copyright statement to Your modifications and
|
| 124 |
-
may provide additional or different license terms and conditions
|
| 125 |
-
for use, reproduction, or distribution of Your modifications, or
|
| 126 |
-
for any such Derivative Works as a whole, provided Your use,
|
| 127 |
-
reproduction, and distribution of the Work otherwise complies with
|
| 128 |
-
the conditions stated in this License.
|
| 129 |
-
|
| 130 |
-
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 131 |
-
any Contribution intentionally submitted for inclusion in the Work
|
| 132 |
-
by You to the Licensor shall be under the terms and conditions of
|
| 133 |
-
this License, without any additional terms or conditions.
|
| 134 |
-
Notwithstanding the above, nothing herein shall supersede or modify
|
| 135 |
-
the terms of any separate license agreement you may have executed
|
| 136 |
-
with Licensor regarding such Contributions.
|
| 137 |
-
|
| 138 |
-
6. Trademarks. This License does not grant permission to use the trade
|
| 139 |
-
names, trademarks, service marks, or product names of the Licensor,
|
| 140 |
-
except as required for reasonable and customary use in describing the
|
| 141 |
-
origin of the Work and reproducing the content of the NOTICE file.
|
| 142 |
-
|
| 143 |
-
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 144 |
-
agreed to in writing, Licensor provides the Work (and each
|
| 145 |
-
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 146 |
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 147 |
-
implied, including, without limitation, any warranties or conditions
|
| 148 |
-
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 149 |
-
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 150 |
-
appropriateness of using or redistributing the Work and assume any
|
| 151 |
-
risks associated with Your exercise of permissions under this License.
|
| 152 |
-
|
| 153 |
-
8. Limitation of Liability. In no event and under no legal theory,
|
| 154 |
-
whether in tort (including negligence), contract, or otherwise,
|
| 155 |
-
unless required by applicable law (such as deliberate and grossly
|
| 156 |
-
negligent acts) or agreed to in writing, shall any Contributor be
|
| 157 |
-
liable to You for damages, including any direct, indirect, special,
|
| 158 |
-
incidental, or consequential damages of any character arising as a
|
| 159 |
-
result of this License or out of the use or inability to use the
|
| 160 |
-
Work (including but not limited to damages for loss of goodwill,
|
| 161 |
-
work stoppage, computer failure or malfunction, or any and all
|
| 162 |
-
other commercial damages or losses), even if such Contributor
|
| 163 |
-
has been advised of the possibility of such damages.
|
| 164 |
-
|
| 165 |
-
9. Accepting Warranty or Additional Liability. While redistributing
|
| 166 |
-
the Work or Derivative Works thereof, You may choose to offer,
|
| 167 |
-
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 168 |
-
or other liability obligations and/or rights consistent with this
|
| 169 |
-
License. However, in accepting such obligations, You may act only
|
| 170 |
-
on Your own behalf and on Your sole responsibility, not on behalf
|
| 171 |
-
of any other Contributor, and only if You agree to indemnify,
|
| 172 |
-
defend, and hold each Contributor harmless for any liability
|
| 173 |
-
incurred by, or claims asserted against, such Contributor by reason
|
| 174 |
-
of your accepting any such warranty or additional liability.
|
| 175 |
-
|
| 176 |
-
END OF TERMS AND CONDITIONS
|
| 177 |
-
|
| 178 |
-
Copyright 2024 Leni Aniva
|
| 179 |
-
|
| 180 |
-
Licensed under the Apache License, Version 2.0 (the "License");
|
| 181 |
-
you may not use this file except in compliance with the License.
|
| 182 |
-
You may obtain a copy of the License at
|
| 183 |
-
|
| 184 |
-
http://www.apache.org/licenses/LICENSE-2.0
|
| 185 |
-
|
| 186 |
-
Unless required by applicable law or agreed to in writing, software
|
| 187 |
-
distributed under the License is distributed on an "AS IS" BASIS,
|
| 188 |
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 189 |
-
See the License for the specific language governing permissions and
|
| 190 |
-
limitations under the License.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/README.md
DELETED
|
@@ -1,77 +0,0 @@
|
|
| 1 |
-
# PyPantograph
|
| 2 |
-
|
| 3 |
-
[](https://arxiv.org/abs/2410.16429)
|
| 4 |
-
|
| 5 |
-
A Machine-to-Machine Interaction System for Lean 4.
|
| 6 |
-
|
| 7 |
-
Features:
|
| 8 |
-
- Programmatically execute tactics (`goal_tactic`)
|
| 9 |
-
- Handling of metavariable coupling
|
| 10 |
-
- Whole file specification conformity check (`check_track`)
|
| 11 |
-
- Extraction of tactic invocation data
|
| 12 |
-
- Inspection of Lean constants
|
| 13 |
-
|
| 14 |
-
## Installation
|
| 15 |
-
|
| 16 |
-
1. Install `uv`
|
| 17 |
-
2. Install `elan`: See [Lean Manual](https://docs.lean-lang.org/lean4/doc/setup.html)
|
| 18 |
-
|
| 19 |
-
### Install as a project dependency
|
| 20 |
-
|
| 21 |
-
3. Add the package to your project:
|
| 22 |
-
```sh
|
| 23 |
-
uv add git+https://github.com/stanford-centaur/PyPantograph
|
| 24 |
-
uv sync
|
| 25 |
-
```
|
| 26 |
-
|
| 27 |
-
### Build wheels from source
|
| 28 |
-
|
| 29 |
-
3. Clone this repository with submodules:
|
| 30 |
-
```sh
|
| 31 |
-
git clone --recurse-submodules <repo-path>
|
| 32 |
-
```
|
| 33 |
-
4. Execute
|
| 34 |
-
```sh
|
| 35 |
-
cd <repo-path>
|
| 36 |
-
uv build
|
| 37 |
-
```
|
| 38 |
-
5. Built wheels can be found at `dist/*.whl`
|
| 39 |
-
|
| 40 |
-
## Documentation
|
| 41 |
-
|
| 42 |
-
Build the documentations by
|
| 43 |
-
```sh
|
| 44 |
-
uv run --group dev jupyter-book build doc
|
| 45 |
-
```
|
| 46 |
-
Then serve
|
| 47 |
-
```sh
|
| 48 |
-
python3 -m http.server -d doc/_build/html
|
| 49 |
-
```
|
| 50 |
-
|
| 51 |
-
### Examples
|
| 52 |
-
|
| 53 |
-
For API interaction examples, see `examples/README.md`.
|
| 54 |
-
|
| 55 |
-
### Contributing
|
| 56 |
-
|
| 57 |
-
Execute unit tests with
|
| 58 |
-
|
| 59 |
-
```sh
|
| 60 |
-
uv run pytest
|
| 61 |
-
```
|
| 62 |
-
|
| 63 |
-
## Reference
|
| 64 |
-
|
| 65 |
-
[Paper Link](https://arxiv.org/abs/2410.16429)
|
| 66 |
-
|
| 67 |
-
```bib
|
| 68 |
-
@misc{pantograph,
|
| 69 |
-
title={Pantograph: A Machine-to-Machine Interaction Interface for Advanced Theorem Proving, High Level Reasoning, and Data Extraction in Lean 4},
|
| 70 |
-
author={Leni Aniva and Chuyue Sun and Brando Miranda and Clark Barrett and Sanmi Koyejo},
|
| 71 |
-
year={2024},
|
| 72 |
-
eprint={2410.16429},
|
| 73 |
-
archivePrefix={arXiv},
|
| 74 |
-
primaryClass={cs.LO},
|
| 75 |
-
url={https://arxiv.org/abs/2410.16429},
|
| 76 |
-
}
|
| 77 |
-
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/build-pantograph.py
DELETED
|
@@ -1,25 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
|
| 3 |
-
import subprocess, shutil, os, stat
|
| 4 |
-
from pathlib import Path
|
| 5 |
-
|
| 6 |
-
# -- Define paths for Pantograph source and Pantograph Python interface
|
| 7 |
-
PATH_PANTOGRAPH = Path("./src")
|
| 8 |
-
PATH_PY = Path("./pantograph")
|
| 9 |
-
|
| 10 |
-
# -- Build the REPL
|
| 11 |
-
with subprocess.Popen(["lake", "build", "repl"], cwd=PATH_PANTOGRAPH) as p:
|
| 12 |
-
p.wait()
|
| 13 |
-
if p.returncode != 0:
|
| 14 |
-
raise Exception(f"Error: 'lake build repl' failed with exit code {p.returncode}.")
|
| 15 |
-
|
| 16 |
-
# -- Copy the REPL executable to the specified path
|
| 17 |
-
path_executable = PATH_PY / "pantograph-repl"
|
| 18 |
-
repl_src = "repl.exe" if os.name == "nt" else "repl"
|
| 19 |
-
shutil.copyfile(PATH_PANTOGRAPH / f".lake/build/bin/{repl_src}", path_executable)
|
| 20 |
-
|
| 21 |
-
# -- Make the REPL executable executable
|
| 22 |
-
os.chmod(path_executable, os.stat(path_executable).st_mode | stat.S_IEXEC)
|
| 23 |
-
|
| 24 |
-
# -- Copy the Lean toolchain file to the specified path
|
| 25 |
-
shutil.copyfile(PATH_PANTOGRAPH / "lean-toolchain", PATH_PY / "lean-toolchain")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/doc/.gitignore
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
/_build
|
|
|
|
|
|
PyPantograph/doc/_config.yml
DELETED
|
@@ -1,71 +0,0 @@
|
|
| 1 |
-
# Book settings
|
| 2 |
-
# Learn more at https://jupyterbook.org/customize/config.html
|
| 3 |
-
# Comprehensive example: https://github.com/executablebooks/jupyter-book/blob/master/docs/_config.yml
|
| 4 |
-
|
| 5 |
-
title: PyPantograph
|
| 6 |
-
author: Leni Aniva
|
| 7 |
-
#logo: logo.png
|
| 8 |
-
|
| 9 |
-
# Force re-execution of notebooks on each build.
|
| 10 |
-
# See https://jupyterbook.org/content/execute.html
|
| 11 |
-
execute:
|
| 12 |
-
execute_notebooks: 'off'
|
| 13 |
-
|
| 14 |
-
# Define the name of the latex output file for PDF builds
|
| 15 |
-
latex:
|
| 16 |
-
latex_documents:
|
| 17 |
-
targetname: book.tex
|
| 18 |
-
|
| 19 |
-
# Add a bibtex file so that we can create citations
|
| 20 |
-
#bibtex_bibfiles:
|
| 21 |
-
# - references.bib
|
| 22 |
-
|
| 23 |
-
# Information about where the book exists on the web
|
| 24 |
-
repository:
|
| 25 |
-
url: https://github.com/lenianiva/PyPantograph # Online location of your book
|
| 26 |
-
path_to_book: docs # Optional path to your book, relative to the repository root
|
| 27 |
-
branch: main # Which branch of the repository should be used when creating links (optional)
|
| 28 |
-
|
| 29 |
-
# Add GitHub buttons to your book
|
| 30 |
-
# See https://jupyterbook.org/customize/config.html#add-a-link-to-your-repository
|
| 31 |
-
html:
|
| 32 |
-
use_issues_button: true
|
| 33 |
-
use_repository_button: true
|
| 34 |
-
|
| 35 |
-
sphinx:
|
| 36 |
-
config:
|
| 37 |
-
intersphinx_mapping:
|
| 38 |
-
ebp:
|
| 39 |
-
- "https://executablebooks.org/en/latest/"
|
| 40 |
-
- null
|
| 41 |
-
myst-parser:
|
| 42 |
-
- "https://myst-parser.readthedocs.io/en/latest/"
|
| 43 |
-
- null
|
| 44 |
-
myst-nb:
|
| 45 |
-
- "https://myst-nb.readthedocs.io/en/latest/"
|
| 46 |
-
- null
|
| 47 |
-
sphinx:
|
| 48 |
-
- "https://www.sphinx-doc.org/en/master"
|
| 49 |
-
- null
|
| 50 |
-
nbformat:
|
| 51 |
-
- "https://nbformat.readthedocs.io/en/latest"
|
| 52 |
-
- null
|
| 53 |
-
sd:
|
| 54 |
-
- "https://sphinx-design.readthedocs.io/en/latest"
|
| 55 |
-
- null
|
| 56 |
-
sphinxproof:
|
| 57 |
-
- "https://sphinx-proof.readthedocs.io/en/latest/"
|
| 58 |
-
- null
|
| 59 |
-
hoverxref_intersphinx:
|
| 60 |
-
- "sphinxproof"
|
| 61 |
-
mathjax3_config:
|
| 62 |
-
tex:
|
| 63 |
-
macros:
|
| 64 |
-
"N": "\\mathbb{N}"
|
| 65 |
-
"floor": ["\\lfloor#1\\rfloor", 1]
|
| 66 |
-
"bmat": ["\\left[\\begin{array}"]
|
| 67 |
-
"emat": ["\\end{array}\\right]"]
|
| 68 |
-
|
| 69 |
-
extra_extensions:
|
| 70 |
-
- sphinx.ext.intersphinx
|
| 71 |
-
- sphinx.ext.autodoc
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/doc/_toc.yml
DELETED
|
@@ -1,16 +0,0 @@
|
|
| 1 |
-
format: jb-book
|
| 2 |
-
root: intro
|
| 3 |
-
parts:
|
| 4 |
-
- caption: Features
|
| 5 |
-
chapters:
|
| 6 |
-
- file: setup
|
| 7 |
-
- file: goal
|
| 8 |
-
- file: agent-search
|
| 9 |
-
- file: frontend
|
| 10 |
-
- caption: API Documentation
|
| 11 |
-
chapters:
|
| 12 |
-
- file: api-message
|
| 13 |
-
- file: api-server
|
| 14 |
-
- file: api-expr
|
| 15 |
-
- file: api-data
|
| 16 |
-
- file: api-search
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/doc/agent-search.ipynb
DELETED
|
@@ -1,155 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"cells": [
|
| 3 |
-
{
|
| 4 |
-
"cell_type": "markdown",
|
| 5 |
-
"id": "ec3abb52-d7cd-471f-b3b7-2d9681c79360",
|
| 6 |
-
"metadata": {},
|
| 7 |
-
"source": [
|
| 8 |
-
"# Search\n",
|
| 9 |
-
"\n",
|
| 10 |
-
"Pantograph supports basic proof search. In this case, Pantograph treats goals as nodes on an and-or tree. The user supplies an agent which should provide two functions:\n",
|
| 11 |
-
"\n",
|
| 12 |
-
"1. *Tactic*: Which tactic should be used on a goal?\n",
|
| 13 |
-
"2. *Guidance*: What is the search priority on a goal?\n",
|
| 14 |
-
"\n",
|
| 15 |
-
"The user agent should inherit from `pantograph.search.Agent`. Here is a brute force agent example:"
|
| 16 |
-
]
|
| 17 |
-
},
|
| 18 |
-
{
|
| 19 |
-
"cell_type": "code",
|
| 20 |
-
"execution_count": 1,
|
| 21 |
-
"id": "959458f5-02e4-4f73-ae28-16a756aebed9",
|
| 22 |
-
"metadata": {},
|
| 23 |
-
"outputs": [],
|
| 24 |
-
"source": [
|
| 25 |
-
"from typing import Optional\n",
|
| 26 |
-
"import collections\n",
|
| 27 |
-
"from pantograph import Server\n",
|
| 28 |
-
"from pantograph.search import Agent\n",
|
| 29 |
-
"from pantograph.expr import GoalState, Tactic"
|
| 30 |
-
]
|
| 31 |
-
},
|
| 32 |
-
{
|
| 33 |
-
"cell_type": "code",
|
| 34 |
-
"execution_count": 2,
|
| 35 |
-
"id": "8b402602-3ae5-43e4-9a62-2fa9e2c039fa",
|
| 36 |
-
"metadata": {},
|
| 37 |
-
"outputs": [],
|
| 38 |
-
"source": [
|
| 39 |
-
"class DumbAgent(Agent):\n",
|
| 40 |
-
"\n",
|
| 41 |
-
" def __init__(self):\n",
|
| 42 |
-
" super().__init__()\n",
|
| 43 |
-
"\n",
|
| 44 |
-
" self.goal_tactic_id_map = collections.defaultdict(lambda : 0)\n",
|
| 45 |
-
" self.intros = [\n",
|
| 46 |
-
" \"intro\",\n",
|
| 47 |
-
" ]\n",
|
| 48 |
-
" self.tactics = [\n",
|
| 49 |
-
" \"intro h\",\n",
|
| 50 |
-
" \"cases h\",\n",
|
| 51 |
-
" \"apply Or.inl\",\n",
|
| 52 |
-
" \"apply Or.inr\",\n",
|
| 53 |
-
" ]\n",
|
| 54 |
-
" self.no_space_tactics = [\n",
|
| 55 |
-
" \"assumption\",\n",
|
| 56 |
-
" ]\n",
|
| 57 |
-
"\n",
|
| 58 |
-
" def next_tactic(\n",
|
| 59 |
-
" self,\n",
|
| 60 |
-
" state: GoalState,\n",
|
| 61 |
-
" goal_id: int,\n",
|
| 62 |
-
" ) -> Optional[Tactic]:\n",
|
| 63 |
-
" key = (state.state_id, goal_id)\n",
|
| 64 |
-
" i = self.goal_tactic_id_map[key]\n",
|
| 65 |
-
"\n",
|
| 66 |
-
" target = state.goals[goal_id].target\n",
|
| 67 |
-
" if target.startswith('β'):\n",
|
| 68 |
-
" tactics = self.intros\n",
|
| 69 |
-
" elif ' ' in target:\n",
|
| 70 |
-
" tactics = self.tactics\n",
|
| 71 |
-
" else:\n",
|
| 72 |
-
" tactics = self.no_space_tactics\n",
|
| 73 |
-
"\n",
|
| 74 |
-
" if i >= len(tactics):\n",
|
| 75 |
-
" return None\n",
|
| 76 |
-
"\n",
|
| 77 |
-
" self.goal_tactic_id_map[key] = i + 1\n",
|
| 78 |
-
" return tactics[i]"
|
| 79 |
-
]
|
| 80 |
-
},
|
| 81 |
-
{
|
| 82 |
-
"cell_type": "markdown",
|
| 83 |
-
"id": "665db9d0-5fff-4b26-9cea-32d06a6e1e04",
|
| 84 |
-
"metadata": {},
|
| 85 |
-
"source": [
|
| 86 |
-
"Execute the search with `agent.search`."
|
| 87 |
-
]
|
| 88 |
-
},
|
| 89 |
-
{
|
| 90 |
-
"cell_type": "code",
|
| 91 |
-
"execution_count": 3,
|
| 92 |
-
"id": "1c7961d1-b1fa-498c-ab75-16feb784ca2c",
|
| 93 |
-
"metadata": {},
|
| 94 |
-
"outputs": [
|
| 95 |
-
{
|
| 96 |
-
"data": {
|
| 97 |
-
"text/plain": [
|
| 98 |
-
"SearchResult(n_goals_root=1, duration=0.7717759609222412, success=True, steps=16)"
|
| 99 |
-
]
|
| 100 |
-
},
|
| 101 |
-
"execution_count": 3,
|
| 102 |
-
"metadata": {},
|
| 103 |
-
"output_type": "execute_result"
|
| 104 |
-
}
|
| 105 |
-
],
|
| 106 |
-
"source": [
|
| 107 |
-
"server = Server()\n",
|
| 108 |
-
"agent = DumbAgent()\n",
|
| 109 |
-
"goal_state = server.goal_start(\"β (p q: Prop), Or p q -> Or q p\")\n",
|
| 110 |
-
"agent.search(server=server, goal_state=goal_state, verbose=False)"
|
| 111 |
-
]
|
| 112 |
-
},
|
| 113 |
-
{
|
| 114 |
-
"cell_type": "markdown",
|
| 115 |
-
"id": "141e0116-cbb6-4957-aaea-2a1100f80ece",
|
| 116 |
-
"metadata": {},
|
| 117 |
-
"source": [
|
| 118 |
-
"## Automatic and Manual Modes\n",
|
| 119 |
-
"\n",
|
| 120 |
-
"The agent chooses one goal and executes a tactic on this goal. What happens to the other goals that are not chosen? By default, the server runs in automatic mode. In automatic mode, all other goals are automatically inherited by a child state, so a user agent could declare a proof finished when there are no more goals remaining in the current goal state.\n",
|
| 121 |
-
"\n",
|
| 122 |
-
"Some users may wish to handle sibling goals manually. For example, Aesop's treatment of metavariable coupling is not automatic. To do this, pass the flag `options={ \"automaticMode\" : False }` to the `Server` constructor."
|
| 123 |
-
]
|
| 124 |
-
},
|
| 125 |
-
{
|
| 126 |
-
"cell_type": "code",
|
| 127 |
-
"execution_count": null,
|
| 128 |
-
"id": "2090e538-d196-4923-937c-b83fedf1d9a2",
|
| 129 |
-
"metadata": {},
|
| 130 |
-
"outputs": [],
|
| 131 |
-
"source": []
|
| 132 |
-
}
|
| 133 |
-
],
|
| 134 |
-
"metadata": {
|
| 135 |
-
"kernelspec": {
|
| 136 |
-
"display_name": "Python 3 (ipykernel)",
|
| 137 |
-
"language": "python",
|
| 138 |
-
"name": "python3"
|
| 139 |
-
},
|
| 140 |
-
"language_info": {
|
| 141 |
-
"codemirror_mode": {
|
| 142 |
-
"name": "ipython",
|
| 143 |
-
"version": 3
|
| 144 |
-
},
|
| 145 |
-
"file_extension": ".py",
|
| 146 |
-
"mimetype": "text/x-python",
|
| 147 |
-
"name": "python",
|
| 148 |
-
"nbconvert_exporter": "python",
|
| 149 |
-
"pygments_lexer": "ipython3",
|
| 150 |
-
"version": "3.12.6"
|
| 151 |
-
}
|
| 152 |
-
},
|
| 153 |
-
"nbformat": 4,
|
| 154 |
-
"nbformat_minor": 5
|
| 155 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/doc/api-data.rst
DELETED
|
@@ -1,5 +0,0 @@
|
|
| 1 |
-
Data
|
| 2 |
-
=============
|
| 3 |
-
|
| 4 |
-
.. automodule:: pantograph.data
|
| 5 |
-
:members:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/doc/api-expr.rst
DELETED
|
@@ -1,8 +0,0 @@
|
|
| 1 |
-
Expr
|
| 2 |
-
=============
|
| 3 |
-
|
| 4 |
-
.. automodule:: pantograph.expr
|
| 5 |
-
:members:
|
| 6 |
-
|
| 7 |
-
.. autodata:: pantograph.expr.Expr
|
| 8 |
-
.. autodata:: pantograph.expr.Tactic
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/doc/api-message.rst
DELETED
|
@@ -1,5 +0,0 @@
|
|
| 1 |
-
Message
|
| 2 |
-
=============
|
| 3 |
-
|
| 4 |
-
.. automodule:: pantograph.message
|
| 5 |
-
:members:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/doc/api-search.rst
DELETED
|
@@ -1,5 +0,0 @@
|
|
| 1 |
-
Search
|
| 2 |
-
=============
|
| 3 |
-
|
| 4 |
-
.. automodule:: pantograph.search
|
| 5 |
-
:members:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/doc/api-server.rst
DELETED
|
@@ -1,5 +0,0 @@
|
|
| 1 |
-
Server
|
| 2 |
-
=============
|
| 3 |
-
|
| 4 |
-
.. automodule:: pantograph.server
|
| 5 |
-
:members:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/doc/frontend.ipynb
DELETED
|
@@ -1,401 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"cells": [
|
| 3 |
-
{
|
| 4 |
-
"cell_type": "markdown",
|
| 5 |
-
"id": "fe7a3037-5c49-4097-9a5d-575b958cc7f8",
|
| 6 |
-
"metadata": {},
|
| 7 |
-
"source": [
|
| 8 |
-
"# Data Extraction"
|
| 9 |
-
]
|
| 10 |
-
},
|
| 11 |
-
{
|
| 12 |
-
"cell_type": "code",
|
| 13 |
-
"execution_count": 1,
|
| 14 |
-
"id": "fc68ad1d-e64c-48b7-9461-50d872d30473",
|
| 15 |
-
"metadata": {},
|
| 16 |
-
"outputs": [],
|
| 17 |
-
"source": [
|
| 18 |
-
"import os\n",
|
| 19 |
-
"from pathlib import Path\n",
|
| 20 |
-
"from pantograph.server import Server"
|
| 21 |
-
]
|
| 22 |
-
},
|
| 23 |
-
{
|
| 24 |
-
"cell_type": "markdown",
|
| 25 |
-
"id": "fd13c644-d731-4f81-964e-584bbd43e51c",
|
| 26 |
-
"metadata": {},
|
| 27 |
-
"source": [
|
| 28 |
-
"## Tactic Invocation\n",
|
| 29 |
-
"\n",
|
| 30 |
-
"Pantograph can extract tactic invocation data from a Lean file. A **tactic\n",
|
| 31 |
-
"invocation** is a tuple containing the before and after goal states, and the\n",
|
| 32 |
-
"tactic which converts the \"before\" state to the \"after\" state.\n",
|
| 33 |
-
"\n",
|
| 34 |
-
"To extract tactic invocation data, use `server.tactic_invocations(file_name)`\n",
|
| 35 |
-
"and supply the file name of the input Lean file."
|
| 36 |
-
]
|
| 37 |
-
},
|
| 38 |
-
{
|
| 39 |
-
"cell_type": "code",
|
| 40 |
-
"execution_count": 2,
|
| 41 |
-
"id": "6282dc6f-4eac-4263-8277-9d54d19ad1a5",
|
| 42 |
-
"metadata": {},
|
| 43 |
-
"outputs": [
|
| 44 |
-
{
|
| 45 |
-
"name": "stdout",
|
| 46 |
-
"output_type": "stream",
|
| 47 |
-
"text": [
|
| 48 |
-
"$PWD: /Users/aniva/Projects/matp/PyPantograph/examples/Example\n"
|
| 49 |
-
]
|
| 50 |
-
}
|
| 51 |
-
],
|
| 52 |
-
"source": [
|
| 53 |
-
"project_path = Path(os.getcwd()).parent.resolve() / 'examples/Example'\n",
|
| 54 |
-
"print(f\"$PWD: {project_path}\")\n",
|
| 55 |
-
"server = await Server.create(imports=['Example'], project_path=project_path)\n",
|
| 56 |
-
"units = await server.tactic_invocations_async(project_path / \"Example.lean\")"
|
| 57 |
-
]
|
| 58 |
-
},
|
| 59 |
-
{
|
| 60 |
-
"cell_type": "markdown",
|
| 61 |
-
"id": "c3c1be91-27a5-4481-b09d-a32dbb94b058",
|
| 62 |
-
"metadata": {},
|
| 63 |
-
"source": [
|
| 64 |
-
"The function returns a list of `CompilationUnit` objects, corresponding to each compilation unit in the input Lean file. For performance reasons only the text boundaries are loaded into `CompilationUnit`s."
|
| 65 |
-
]
|
| 66 |
-
},
|
| 67 |
-
{
|
| 68 |
-
"cell_type": "code",
|
| 69 |
-
"execution_count": 3,
|
| 70 |
-
"id": "e994aa2b-5d5e-4f86-af6c-40e0b3a032d2",
|
| 71 |
-
"metadata": {},
|
| 72 |
-
"outputs": [
|
| 73 |
-
{
|
| 74 |
-
"name": "stdout",
|
| 75 |
-
"output_type": "stream",
|
| 76 |
-
"text": [
|
| 77 |
-
"#0: [14,85]\n",
|
| 78 |
-
"/-- Ensure that Aesop is running -/\n",
|
| 79 |
-
"example : Ξ± β Ξ± :=\n",
|
| 80 |
-
" by aesop\n",
|
| 81 |
-
"\n",
|
| 82 |
-
"\n",
|
| 83 |
-
"#1: [85,254]\n",
|
| 84 |
-
"example : β (p q: Prop), p β¨ q β q β¨ p := by\n",
|
| 85 |
-
" intro p q h\n",
|
| 86 |
-
" -- Here are some comments\n",
|
| 87 |
-
" cases h\n",
|
| 88 |
-
" . apply Or.inr\n",
|
| 89 |
-
" assumption\n",
|
| 90 |
-
" . apply Or.inl\n",
|
| 91 |
-
" assumption\n",
|
| 92 |
-
"\n"
|
| 93 |
-
]
|
| 94 |
-
}
|
| 95 |
-
],
|
| 96 |
-
"source": [
|
| 97 |
-
"with open(project_path / \"Example.lean\", 'rb') as f:\n",
|
| 98 |
-
" content = f.read()\n",
|
| 99 |
-
" for i, unit in enumerate(units):\n",
|
| 100 |
-
" print(f\"#{i}: [{unit.i_begin},{unit.i_end}]\")\n",
|
| 101 |
-
" unit_text = content[unit.i_begin:unit.i_end].decode('utf-8')\n",
|
| 102 |
-
" print(unit_text)"
|
| 103 |
-
]
|
| 104 |
-
},
|
| 105 |
-
{
|
| 106 |
-
"cell_type": "markdown",
|
| 107 |
-
"id": "52e650fc-4a87-445f-8aa8-707ed9e36c03",
|
| 108 |
-
"metadata": {},
|
| 109 |
-
"source": [
|
| 110 |
-
"Each `CompilationUnit` includes a list of `TacticInvocation`s, which contains the `.before` (corresponding to the state before the tactic), `.after` (corresponding to the state after the tactic), and `.tactic` (tactic executed) fields. "
|
| 111 |
-
]
|
| 112 |
-
},
|
| 113 |
-
{
|
| 114 |
-
"cell_type": "code",
|
| 115 |
-
"execution_count": 4,
|
| 116 |
-
"id": "8e0f0def-dd3c-4550-8a7c-b4aec6c7fd7f",
|
| 117 |
-
"metadata": {},
|
| 118 |
-
"outputs": [
|
| 119 |
-
{
|
| 120 |
-
"name": "stdout",
|
| 121 |
-
"output_type": "stream",
|
| 122 |
-
"text": [
|
| 123 |
-
"[Before]\n",
|
| 124 |
-
"Ξ± : Sort ?u.7\n",
|
| 125 |
-
"β’ Ξ± β Ξ±\n",
|
| 126 |
-
"[Tactic]\n",
|
| 127 |
-
"aesop (using [])\n",
|
| 128 |
-
"[After]\n",
|
| 129 |
-
"\n"
|
| 130 |
-
]
|
| 131 |
-
}
|
| 132 |
-
],
|
| 133 |
-
"source": [
|
| 134 |
-
"for i in units[0].invocations:\n",
|
| 135 |
-
" print(f\"[Before]\\n{i.before}\")\n",
|
| 136 |
-
" print(f\"[Tactic]\\n{i.tactic} (using {i.used_constants})\")\n",
|
| 137 |
-
" print(f\"[After]\\n{i.after}\")"
|
| 138 |
-
]
|
| 139 |
-
},
|
| 140 |
-
{
|
| 141 |
-
"cell_type": "code",
|
| 142 |
-
"execution_count": 5,
|
| 143 |
-
"id": "51f5398b-5416-4dc1-81cd-6d2514758232",
|
| 144 |
-
"metadata": {
|
| 145 |
-
"scrolled": true
|
| 146 |
-
},
|
| 147 |
-
"outputs": [
|
| 148 |
-
{
|
| 149 |
-
"name": "stdout",
|
| 150 |
-
"output_type": "stream",
|
| 151 |
-
"text": [
|
| 152 |
-
"[Before]\n",
|
| 153 |
-
"β’ β (p q : Prop), p β¨ q β q β¨ p\n",
|
| 154 |
-
"[Tactic]\n",
|
| 155 |
-
"intro p q h (using [])\n",
|
| 156 |
-
"[After]\n",
|
| 157 |
-
"p q : Prop\n",
|
| 158 |
-
"h : p β¨ q\n",
|
| 159 |
-
"β’ q β¨ p\n",
|
| 160 |
-
"[Before]\n",
|
| 161 |
-
"p q : Prop\n",
|
| 162 |
-
"h : p β¨ q\n",
|
| 163 |
-
"β’ q β¨ p\n",
|
| 164 |
-
"[Tactic]\n",
|
| 165 |
-
"cases h (using ['Eq.refl', 'Or'])\n",
|
| 166 |
-
"[After]\n",
|
| 167 |
-
"case inl\n",
|
| 168 |
-
"p q : Prop\n",
|
| 169 |
-
"hβ : p\n",
|
| 170 |
-
"β’ q β¨ p\n",
|
| 171 |
-
"case inr\n",
|
| 172 |
-
"p q : Prop\n",
|
| 173 |
-
"hβ : q\n",
|
| 174 |
-
"β’ q β¨ p\n",
|
| 175 |
-
"[Before]\n",
|
| 176 |
-
"case inl\n",
|
| 177 |
-
"p q : Prop\n",
|
| 178 |
-
"hβ : p\n",
|
| 179 |
-
"β’ q β¨ p\n",
|
| 180 |
-
"[Tactic]\n",
|
| 181 |
-
"apply Or.inr (using ['Or.inr'])\n",
|
| 182 |
-
"[After]\n",
|
| 183 |
-
"case inl.h\n",
|
| 184 |
-
"p q : Prop\n",
|
| 185 |
-
"hβ : p\n",
|
| 186 |
-
"β’ p\n",
|
| 187 |
-
"[Before]\n",
|
| 188 |
-
"case inl.h\n",
|
| 189 |
-
"p q : Prop\n",
|
| 190 |
-
"hβ : p\n",
|
| 191 |
-
"β’ p\n",
|
| 192 |
-
"[Tactic]\n",
|
| 193 |
-
"assumption (using [])\n",
|
| 194 |
-
"[After]\n",
|
| 195 |
-
"\n",
|
| 196 |
-
"[Before]\n",
|
| 197 |
-
"case inr\n",
|
| 198 |
-
"p q : Prop\n",
|
| 199 |
-
"hβ : q\n",
|
| 200 |
-
"β’ q β¨ p\n",
|
| 201 |
-
"[Tactic]\n",
|
| 202 |
-
"apply Or.inl (using ['Or.inl'])\n",
|
| 203 |
-
"[After]\n",
|
| 204 |
-
"case inr.h\n",
|
| 205 |
-
"p q : Prop\n",
|
| 206 |
-
"hβ : q\n",
|
| 207 |
-
"β’ q\n",
|
| 208 |
-
"[Before]\n",
|
| 209 |
-
"case inr.h\n",
|
| 210 |
-
"p q : Prop\n",
|
| 211 |
-
"hβ : q\n",
|
| 212 |
-
"β’ q\n",
|
| 213 |
-
"[Tactic]\n",
|
| 214 |
-
"assumption (using [])\n",
|
| 215 |
-
"[After]\n",
|
| 216 |
-
"\n"
|
| 217 |
-
]
|
| 218 |
-
}
|
| 219 |
-
],
|
| 220 |
-
"source": [
|
| 221 |
-
"for i in units[1].invocations:\n",
|
| 222 |
-
" print(f\"[Before]\\n{i.before}\")\n",
|
| 223 |
-
" print(f\"[Tactic]\\n{i.tactic} (using {i.used_constants})\")\n",
|
| 224 |
-
" print(f\"[After]\\n{i.after}\")"
|
| 225 |
-
]
|
| 226 |
-
},
|
| 227 |
-
{
|
| 228 |
-
"cell_type": "markdown",
|
| 229 |
-
"id": "3a8b5b28-a012-4d83-a6a9-ed017a8e1fc2",
|
| 230 |
-
"metadata": {},
|
| 231 |
-
"source": [
|
| 232 |
-
"## Check Compilation\n",
|
| 233 |
-
"\n",
|
| 234 |
-
"Use `check_compile` to check if some Lean code compiles.\n",
|
| 235 |
-
"\n",
|
| 236 |
-
"Keep in mind that Lean compilation can execute arbitrary code."
|
| 237 |
-
]
|
| 238 |
-
},
|
| 239 |
-
{
|
| 240 |
-
"cell_type": "code",
|
| 241 |
-
"execution_count": 6,
|
| 242 |
-
"id": "87e99da0-6e16-48c3-a462-1bfacb0d0cc7",
|
| 243 |
-
"metadata": {},
|
| 244 |
-
"outputs": [
|
| 245 |
-
{
|
| 246 |
-
"data": {
|
| 247 |
-
"text/plain": [
|
| 248 |
-
"[CompilationUnit(i_begin=0, i_end=31, messages=[], invocations=None, goal_state=None, goal_src_boundaries=None, new_constants=None)]"
|
| 249 |
-
]
|
| 250 |
-
},
|
| 251 |
-
"execution_count": 6,
|
| 252 |
-
"metadata": {},
|
| 253 |
-
"output_type": "execute_result"
|
| 254 |
-
}
|
| 255 |
-
],
|
| 256 |
-
"source": [
|
| 257 |
-
"server = await Server.create()\n",
|
| 258 |
-
"code = \"\"\"\n",
|
| 259 |
-
"example : 1 + 1 = 2 := by rfl\n",
|
| 260 |
-
"\"\"\"\n",
|
| 261 |
-
"await server.check_compile_async(code)"
|
| 262 |
-
]
|
| 263 |
-
},
|
| 264 |
-
{
|
| 265 |
-
"cell_type": "markdown",
|
| 266 |
-
"id": "adf0dee9-33de-4640-b35a-3e093e590963",
|
| 267 |
-
"metadata": {},
|
| 268 |
-
"source": [
|
| 269 |
-
"If there are no error messages, it means the unit compiles."
|
| 270 |
-
]
|
| 271 |
-
},
|
| 272 |
-
{
|
| 273 |
-
"cell_type": "markdown",
|
| 274 |
-
"id": "14e42793-e177-447b-89d3-edb16931f6e9",
|
| 275 |
-
"metadata": {},
|
| 276 |
-
"source": [
|
| 277 |
-
"## Loading Definitions\n",
|
| 278 |
-
"\n",
|
| 279 |
-
"Pantograph keeps track of a global environment. `Server.load_definitions` adds new definitions to the environment."
|
| 280 |
-
]
|
| 281 |
-
},
|
| 282 |
-
{
|
| 283 |
-
"cell_type": "code",
|
| 284 |
-
"execution_count": 7,
|
| 285 |
-
"id": "9d322474-6a6e-4c32-945b-9e7942b78826",
|
| 286 |
-
"metadata": {},
|
| 287 |
-
"outputs": [
|
| 288 |
-
{
|
| 289 |
-
"data": {
|
| 290 |
-
"text/plain": [
|
| 291 |
-
"{'type': {'pp': 'Nat β Nat'},\n",
|
| 292 |
-
" 'sourceStart': {'line': 2, 'column': 0},\n",
|
| 293 |
-
" 'sourceEnd': {'line': 2, 'column': 42},\n",
|
| 294 |
-
" 'isUnsafe': False}"
|
| 295 |
-
]
|
| 296 |
-
},
|
| 297 |
-
"execution_count": 7,
|
| 298 |
-
"metadata": {},
|
| 299 |
-
"output_type": "execute_result"
|
| 300 |
-
}
|
| 301 |
-
],
|
| 302 |
-
"source": [
|
| 303 |
-
"code = \"\"\"\n",
|
| 304 |
-
"def mystery : Nat -> Nat := fun x => x + 1\n",
|
| 305 |
-
"\"\"\"\n",
|
| 306 |
-
"await server.load_definitions_async(code)\n",
|
| 307 |
-
"await server.env_inspect_async(\"mystery\")"
|
| 308 |
-
]
|
| 309 |
-
},
|
| 310 |
-
{
|
| 311 |
-
"cell_type": "markdown",
|
| 312 |
-
"id": "93536793-c7df-4caa-bd03-74fd3b97d987",
|
| 313 |
-
"metadata": {},
|
| 314 |
-
"source": [
|
| 315 |
-
"## Track Checking\n",
|
| 316 |
-
"\n",
|
| 317 |
-
"We can check if one file conforms to the definition and theorems of another. If the result object has no `failure`s or error messages, the check has passed."
|
| 318 |
-
]
|
| 319 |
-
},
|
| 320 |
-
{
|
| 321 |
-
"cell_type": "code",
|
| 322 |
-
"execution_count": 8,
|
| 323 |
-
"id": "67aa959c-3c7c-4844-9ffb-9b730ae07f8a",
|
| 324 |
-
"metadata": {},
|
| 325 |
-
"outputs": [
|
| 326 |
-
{
|
| 327 |
-
"data": {
|
| 328 |
-
"text/plain": [
|
| 329 |
-
"CheckTrackResult(src_messages=[], dst_messages=[], failure=None)"
|
| 330 |
-
]
|
| 331 |
-
},
|
| 332 |
-
"execution_count": 8,
|
| 333 |
-
"metadata": {},
|
| 334 |
-
"output_type": "execute_result"
|
| 335 |
-
}
|
| 336 |
-
],
|
| 337 |
-
"source": [
|
| 338 |
-
"src = \"\"\"\n",
|
| 339 |
-
"def f : Nat -> Nat := sorry\n",
|
| 340 |
-
"theorem property (n : Nat) : f n = n + 1 := sorry\n",
|
| 341 |
-
"\"\"\"\n",
|
| 342 |
-
"dst = \"\"\"\n",
|
| 343 |
-
"def f (x : Nat) := x + 1\n",
|
| 344 |
-
"theorem property (n : Nat) : f n = n + 1 := rfl\n",
|
| 345 |
-
"\"\"\"\n",
|
| 346 |
-
"await server.check_track_async(src, dst)"
|
| 347 |
-
]
|
| 348 |
-
},
|
| 349 |
-
{
|
| 350 |
-
"cell_type": "code",
|
| 351 |
-
"execution_count": 9,
|
| 352 |
-
"id": "5d56a22d-feb8-4d3b-bd5b-4f675991a550",
|
| 353 |
-
"metadata": {},
|
| 354 |
-
"outputs": [
|
| 355 |
-
{
|
| 356 |
-
"data": {
|
| 357 |
-
"text/plain": [
|
| 358 |
-
"CheckTrackResult(src_messages=[], dst_messages=[], failure='Type clash of property')"
|
| 359 |
-
]
|
| 360 |
-
},
|
| 361 |
-
"execution_count": 9,
|
| 362 |
-
"metadata": {},
|
| 363 |
-
"output_type": "execute_result"
|
| 364 |
-
}
|
| 365 |
-
],
|
| 366 |
-
"source": [
|
| 367 |
-
"src = \"\"\"\n",
|
| 368 |
-
"def f : Nat -> Nat := sorry\n",
|
| 369 |
-
"theorem property (n : Nat) : f n = n + 1 := sorry\n",
|
| 370 |
-
"\"\"\"\n",
|
| 371 |
-
"# Tampering!\n",
|
| 372 |
-
"dst = \"\"\"\n",
|
| 373 |
-
"def f (x : Nat) := x + 1\n",
|
| 374 |
-
"theorem property (n : Nat) : 0 = 0 := rfl\n",
|
| 375 |
-
"\"\"\"\n",
|
| 376 |
-
"await server.check_track_async(src, dst)"
|
| 377 |
-
]
|
| 378 |
-
}
|
| 379 |
-
],
|
| 380 |
-
"metadata": {
|
| 381 |
-
"kernelspec": {
|
| 382 |
-
"display_name": "Python 3 (ipykernel)",
|
| 383 |
-
"language": "python",
|
| 384 |
-
"name": "python3"
|
| 385 |
-
},
|
| 386 |
-
"language_info": {
|
| 387 |
-
"codemirror_mode": {
|
| 388 |
-
"name": "ipython",
|
| 389 |
-
"version": 3
|
| 390 |
-
},
|
| 391 |
-
"file_extension": ".py",
|
| 392 |
-
"mimetype": "text/x-python",
|
| 393 |
-
"name": "python",
|
| 394 |
-
"nbconvert_exporter": "python",
|
| 395 |
-
"pygments_lexer": "ipython3",
|
| 396 |
-
"version": "3.12.7"
|
| 397 |
-
}
|
| 398 |
-
},
|
| 399 |
-
"nbformat": 4,
|
| 400 |
-
"nbformat_minor": 5
|
| 401 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/doc/goal.ipynb
DELETED
|
@@ -1,662 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"cells": [
|
| 3 |
-
{
|
| 4 |
-
"cell_type": "markdown",
|
| 5 |
-
"id": "c5106980-4850-4bea-a333-5a1b2e4d1dc5",
|
| 6 |
-
"metadata": {},
|
| 7 |
-
"source": [
|
| 8 |
-
"# Goals and Tactics\n",
|
| 9 |
-
"\n",
|
| 10 |
-
"Executing tactics in Pantograph is simple. To start a proof, call the\n",
|
| 11 |
-
"`Server.goal_start` function and supply an expression."
|
| 12 |
-
]
|
| 13 |
-
},
|
| 14 |
-
{
|
| 15 |
-
"cell_type": "code",
|
| 16 |
-
"execution_count": 1,
|
| 17 |
-
"id": "3257de2b-41ca-4cfe-b66c-1ef4781c98b0",
|
| 18 |
-
"metadata": {},
|
| 19 |
-
"outputs": [],
|
| 20 |
-
"source": [
|
| 21 |
-
"from pantograph import Server\n",
|
| 22 |
-
"from pantograph.expr import Site, TacticHave, TacticExpr, TacticMode"
|
| 23 |
-
]
|
| 24 |
-
},
|
| 25 |
-
{
|
| 26 |
-
"cell_type": "code",
|
| 27 |
-
"execution_count": 2,
|
| 28 |
-
"id": "6783d478-d8c7-4c4e-a56e-8170384297ef",
|
| 29 |
-
"metadata": {},
|
| 30 |
-
"outputs": [],
|
| 31 |
-
"source": [
|
| 32 |
-
"server = await Server.create()\n",
|
| 33 |
-
"state0 = await server.goal_start_async(\"forall (p q: Prop), Or p q -> Or q p\")"
|
| 34 |
-
]
|
| 35 |
-
},
|
| 36 |
-
{
|
| 37 |
-
"cell_type": "markdown",
|
| 38 |
-
"id": "bfe5a9df-33c2-4538-a9ce-fc0e02c92ff2",
|
| 39 |
-
"metadata": {},
|
| 40 |
-
"source": [
|
| 41 |
-
"This creates a *goal state*, which consists of some goals. In this\n",
|
| 42 |
-
"case since it is the beginning of a state, it has only one goal."
|
| 43 |
-
]
|
| 44 |
-
},
|
| 45 |
-
{
|
| 46 |
-
"cell_type": "code",
|
| 47 |
-
"execution_count": 3,
|
| 48 |
-
"id": "eefc9094-9574-4f92-9aa2-c39beb85389b",
|
| 49 |
-
"metadata": {},
|
| 50 |
-
"outputs": [
|
| 51 |
-
{
|
| 52 |
-
"name": "stdout",
|
| 53 |
-
"output_type": "stream",
|
| 54 |
-
"text": [
|
| 55 |
-
"\n",
|
| 56 |
-
"β’ forall (p q: Prop), Or p q -> Or q p\n"
|
| 57 |
-
]
|
| 58 |
-
}
|
| 59 |
-
],
|
| 60 |
-
"source": [
|
| 61 |
-
"print(state0)"
|
| 62 |
-
]
|
| 63 |
-
},
|
| 64 |
-
{
|
| 65 |
-
"cell_type": "markdown",
|
| 66 |
-
"id": "26dbe212-e09e-42dd-ab15-65ee2fba6234",
|
| 67 |
-
"metadata": {},
|
| 68 |
-
"source": [
|
| 69 |
-
"To execute a tactic on a goal state, use `Server.goal_tactic`. This function\n",
|
| 70 |
-
"takes a state, a tactic, and an optional site (see below). Most Lean tactics are strings."
|
| 71 |
-
]
|
| 72 |
-
},
|
| 73 |
-
{
|
| 74 |
-
"cell_type": "code",
|
| 75 |
-
"execution_count": 4,
|
| 76 |
-
"id": "c907dbb6-4d6a-4aa7-b173-60220165ba9e",
|
| 77 |
-
"metadata": {},
|
| 78 |
-
"outputs": [
|
| 79 |
-
{
|
| 80 |
-
"name": "stdout",
|
| 81 |
-
"output_type": "stream",
|
| 82 |
-
"text": [
|
| 83 |
-
"a : Prop\n",
|
| 84 |
-
"β’ β (q : Prop), a β¨ q β q β¨ a\n"
|
| 85 |
-
]
|
| 86 |
-
}
|
| 87 |
-
],
|
| 88 |
-
"source": [
|
| 89 |
-
"state1 = await server.goal_tactic_async(state0, \"intro a\")\n",
|
| 90 |
-
"print(state1)"
|
| 91 |
-
]
|
| 92 |
-
},
|
| 93 |
-
{
|
| 94 |
-
"cell_type": "markdown",
|
| 95 |
-
"id": "9978fdcf-a12b-4f22-9551-5e04c262e5e0",
|
| 96 |
-
"metadata": {},
|
| 97 |
-
"source": [
|
| 98 |
-
"Executing a tactic produces a new goal state. If this goal state has no goals,\n",
|
| 99 |
-
"the proof is complete. You can recover the usual form of a goal with `str()`"
|
| 100 |
-
]
|
| 101 |
-
},
|
| 102 |
-
{
|
| 103 |
-
"cell_type": "code",
|
| 104 |
-
"execution_count": 5,
|
| 105 |
-
"id": "16595c5e-2285-49d5-8340-397ad1e6c9e7",
|
| 106 |
-
"metadata": {},
|
| 107 |
-
"outputs": [
|
| 108 |
-
{
|
| 109 |
-
"name": "stdout",
|
| 110 |
-
"output_type": "stream",
|
| 111 |
-
"text": [
|
| 112 |
-
"a : Prop\n",
|
| 113 |
-
"β’ β (q : Prop), a β¨ q β q β¨ a\n"
|
| 114 |
-
]
|
| 115 |
-
}
|
| 116 |
-
],
|
| 117 |
-
"source": [
|
| 118 |
-
"print(state1.goals[0])"
|
| 119 |
-
]
|
| 120 |
-
},
|
| 121 |
-
{
|
| 122 |
-
"cell_type": "markdown",
|
| 123 |
-
"id": "837dae74-b2d7-4e81-a318-86641dd016f9",
|
| 124 |
-
"metadata": {},
|
| 125 |
-
"source": [
|
| 126 |
-
"Starting in v0.3.5, you can run multiple tactics in one shot. Use `?_` to mark goals to be solved later."
|
| 127 |
-
]
|
| 128 |
-
},
|
| 129 |
-
{
|
| 130 |
-
"cell_type": "code",
|
| 131 |
-
"execution_count": 6,
|
| 132 |
-
"id": "6c70dbe0-e7ad-4bc2-bafe-0e59d78ccd3a",
|
| 133 |
-
"metadata": {},
|
| 134 |
-
"outputs": [
|
| 135 |
-
{
|
| 136 |
-
"name": "stdout",
|
| 137 |
-
"output_type": "stream",
|
| 138 |
-
"text": [
|
| 139 |
-
"inl\n",
|
| 140 |
-
"p : Prop\n",
|
| 141 |
-
"q : Prop\n",
|
| 142 |
-
"hβ : p\n",
|
| 143 |
-
"β’ q β¨ p\n",
|
| 144 |
-
"inr\n",
|
| 145 |
-
"p : Prop\n",
|
| 146 |
-
"q : Prop\n",
|
| 147 |
-
"hβ : q\n",
|
| 148 |
-
"β’ q β¨ p\n"
|
| 149 |
-
]
|
| 150 |
-
}
|
| 151 |
-
],
|
| 152 |
-
"source": [
|
| 153 |
-
"state2 = await server.goal_tactic_async(state0, \"intro p q\\nintro h\\ncases h\")\n",
|
| 154 |
-
"print(state2)"
|
| 155 |
-
]
|
| 156 |
-
},
|
| 157 |
-
{
|
| 158 |
-
"cell_type": "code",
|
| 159 |
-
"execution_count": 7,
|
| 160 |
-
"id": "529edf1a-27bc-45a2-bbac-01e8756310a2",
|
| 161 |
-
"metadata": {},
|
| 162 |
-
"outputs": [
|
| 163 |
-
{
|
| 164 |
-
"name": "stdout",
|
| 165 |
-
"output_type": "stream",
|
| 166 |
-
"text": [
|
| 167 |
-
"refine_2.inl\n",
|
| 168 |
-
"p : Prop\n",
|
| 169 |
-
"q : Prop\n",
|
| 170 |
-
"random : 1 + 1 = 2\n",
|
| 171 |
-
"hβ : p\n",
|
| 172 |
-
"β’ q β¨ p\n",
|
| 173 |
-
"refine_2.inr\n",
|
| 174 |
-
"p : Prop\n",
|
| 175 |
-
"q : Prop\n",
|
| 176 |
-
"random : 1 + 1 = 2\n",
|
| 177 |
-
"hβ : q\n",
|
| 178 |
-
"β’ q β¨ p\n",
|
| 179 |
-
"refine_1\n",
|
| 180 |
-
"p : Prop\n",
|
| 181 |
-
"q : Prop\n",
|
| 182 |
-
"h : p β¨ q\n",
|
| 183 |
-
"β’ 1 + 1 = 2\n"
|
| 184 |
-
]
|
| 185 |
-
}
|
| 186 |
-
],
|
| 187 |
-
"source": [
|
| 188 |
-
"state2 = await server.goal_tactic_async(state0, \"intro p q h\\nhave random : 1 + 1 = 2 := ?_\\ncases h\")\n",
|
| 189 |
-
"print(state2)"
|
| 190 |
-
]
|
| 191 |
-
},
|
| 192 |
-
{
|
| 193 |
-
"cell_type": "markdown",
|
| 194 |
-
"id": "67f2a75d-6851-4393-bac9-a091400f1906",
|
| 195 |
-
"metadata": {},
|
| 196 |
-
"source": [
|
| 197 |
-
"## Error Handling and GC\n",
|
| 198 |
-
"\n",
|
| 199 |
-
"When a tactic fails, it throws an exception (`TacticFailure`) which contains a list of either `str`s or `Message` objects in `e.args[0]`."
|
| 200 |
-
]
|
| 201 |
-
},
|
| 202 |
-
{
|
| 203 |
-
"cell_type": "code",
|
| 204 |
-
"execution_count": 8,
|
| 205 |
-
"id": "c9784ba2-3810-4f80-a6c4-33d5eef3003e",
|
| 206 |
-
"metadata": {
|
| 207 |
-
"scrolled": true
|
| 208 |
-
},
|
| 209 |
-
"outputs": [
|
| 210 |
-
{
|
| 211 |
-
"name": "stdout",
|
| 212 |
-
"output_type": "stream",
|
| 213 |
-
"text": [
|
| 214 |
-
"[Message(data=\"tactic 'assumption' failed\\na : Prop\\nβ’ β (q : Prop), a β¨ q β q β¨ a\", pos=Position(line=0, column=0), pos_end=None, severity=<Severity.ERROR: 3>, kind=None)]\n",
|
| 215 |
-
"0:0: error: tactic 'assumption' failed\n",
|
| 216 |
-
"a : Prop\n",
|
| 217 |
-
"β’ β (q : Prop), a β¨ q β q β¨ a\n"
|
| 218 |
-
]
|
| 219 |
-
}
|
| 220 |
-
],
|
| 221 |
-
"source": [
|
| 222 |
-
"from pantograph.message import TacticFailure\n",
|
| 223 |
-
"try:\n",
|
| 224 |
-
" state2 = await server.goal_tactic_async(state1, \"assumption\")\n",
|
| 225 |
-
" print(\"Should not reach this\")\n",
|
| 226 |
-
"except TacticFailure as e:\n",
|
| 227 |
-
" print(e)\n",
|
| 228 |
-
" for msg in e.args[0]:\n",
|
| 229 |
-
" print(msg)"
|
| 230 |
-
]
|
| 231 |
-
},
|
| 232 |
-
{
|
| 233 |
-
"cell_type": "markdown",
|
| 234 |
-
"id": "1ae60d9e-8656-4f26-b495-d04bced250fc",
|
| 235 |
-
"metadata": {},
|
| 236 |
-
"source": [
|
| 237 |
-
"A state with no goals is considered solved"
|
| 238 |
-
]
|
| 239 |
-
},
|
| 240 |
-
{
|
| 241 |
-
"cell_type": "code",
|
| 242 |
-
"execution_count": 9,
|
| 243 |
-
"id": "1cb96b19-d3bb-4533-abeb-a7dbc5bc8c3e",
|
| 244 |
-
"metadata": {},
|
| 245 |
-
"outputs": [
|
| 246 |
-
{
|
| 247 |
-
"data": {
|
| 248 |
-
"text/plain": [
|
| 249 |
-
"GoalState(#7, goals=[], _sentinel=#4"
|
| 250 |
-
]
|
| 251 |
-
},
|
| 252 |
-
"execution_count": 9,
|
| 253 |
-
"metadata": {},
|
| 254 |
-
"output_type": "execute_result"
|
| 255 |
-
}
|
| 256 |
-
],
|
| 257 |
-
"source": [
|
| 258 |
-
"state0 = await server.goal_start_async(\"forall (p : Prop), p -> p\")\n",
|
| 259 |
-
"state1 = await server.goal_tactic_async(state0, \"intro\")\n",
|
| 260 |
-
"state2 = await server.goal_tactic_async(state1, \"intro h\")\n",
|
| 261 |
-
"state3 = await server.goal_tactic_async(state2, \"exact h\")\n",
|
| 262 |
-
"state3"
|
| 263 |
-
]
|
| 264 |
-
},
|
| 265 |
-
{
|
| 266 |
-
"cell_type": "markdown",
|
| 267 |
-
"id": "a2945e71-e583-4ae0-9c0f-83035f0492f2",
|
| 268 |
-
"metadata": {},
|
| 269 |
-
"source": [
|
| 270 |
-
"Execute `server.gc()` once in a while to delete unused goals."
|
| 271 |
-
]
|
| 272 |
-
},
|
| 273 |
-
{
|
| 274 |
-
"cell_type": "code",
|
| 275 |
-
"execution_count": 10,
|
| 276 |
-
"id": "d53624ff-c720-4847-98f7-28e109eb76e7",
|
| 277 |
-
"metadata": {},
|
| 278 |
-
"outputs": [],
|
| 279 |
-
"source": [
|
| 280 |
-
"await server.gc_async()"
|
| 281 |
-
]
|
| 282 |
-
},
|
| 283 |
-
{
|
| 284 |
-
"cell_type": "markdown",
|
| 285 |
-
"id": "0b59e05e-7d8c-4fad-b8ca-375ea995ea5b",
|
| 286 |
-
"metadata": {},
|
| 287 |
-
"source": [
|
| 288 |
-
"## Special Tactics\n",
|
| 289 |
-
"\n",
|
| 290 |
-
"Lean has special provisions for some tactics. This includes `have`, `let`,\n",
|
| 291 |
-
"`calc`. To execute one of these tactics, create a `TacticHave`, `TacticLet`,\n",
|
| 292 |
-
"instance and feed it into `server.goal_tactic`.\n",
|
| 293 |
-
"\n",
|
| 294 |
-
"Technically speaking `have` and `let` are not tactics in Lean, so their execution requires special attention. In v0.3.5, they can be run under the normal tactic function as well (see above)."
|
| 295 |
-
]
|
| 296 |
-
},
|
| 297 |
-
{
|
| 298 |
-
"cell_type": "code",
|
| 299 |
-
"execution_count": 11,
|
| 300 |
-
"id": "526d620b-064f-4ec0-a7b2-6a1ef3c6f6e7",
|
| 301 |
-
"metadata": {},
|
| 302 |
-
"outputs": [
|
| 303 |
-
{
|
| 304 |
-
"name": "stdout",
|
| 305 |
-
"output_type": "stream",
|
| 306 |
-
"text": [
|
| 307 |
-
"\n",
|
| 308 |
-
"β’ 2 = 1 + 1\n",
|
| 309 |
-
"h : 2 = 1 + 1\n",
|
| 310 |
-
"β’ 1 + 1 = 2\n"
|
| 311 |
-
]
|
| 312 |
-
}
|
| 313 |
-
],
|
| 314 |
-
"source": [
|
| 315 |
-
"state0 = await server.goal_start_async(\"1 + 1 = 2\")\n",
|
| 316 |
-
"state1 = await server.goal_tactic_async(state0, TacticHave(branch=\"2 = 1 + 1\", binder_name=\"h\"))\n",
|
| 317 |
-
"print(state1)"
|
| 318 |
-
]
|
| 319 |
-
},
|
| 320 |
-
{
|
| 321 |
-
"cell_type": "markdown",
|
| 322 |
-
"id": "c415d436-ed0d-475f-bf5e-b8dc63954c7e",
|
| 323 |
-
"metadata": {},
|
| 324 |
-
"source": [
|
| 325 |
-
"The `TacticExpr` \"tactic\" parses an expression and assigns it to the current\n",
|
| 326 |
-
"goal. This leverages Lean's type unification system and is as expressive as\n",
|
| 327 |
-
"Lean expressions. Many proofs in Mathlib4 are written in a mixture of expression\n",
|
| 328 |
-
"and tactic forms."
|
| 329 |
-
]
|
| 330 |
-
},
|
| 331 |
-
{
|
| 332 |
-
"cell_type": "code",
|
| 333 |
-
"execution_count": 12,
|
| 334 |
-
"id": "e1f06441-4d77-45a7-a1c3-b800b96a8105",
|
| 335 |
-
"metadata": {},
|
| 336 |
-
"outputs": [
|
| 337 |
-
{
|
| 338 |
-
"name": "stdout",
|
| 339 |
-
"output_type": "stream",
|
| 340 |
-
"text": [
|
| 341 |
-
"\n"
|
| 342 |
-
]
|
| 343 |
-
}
|
| 344 |
-
],
|
| 345 |
-
"source": [
|
| 346 |
-
"state0 = await server.goal_start_async(\"forall (p : Prop), p -> p\")\n",
|
| 347 |
-
"state1 = await server.goal_tactic_async(state0, \"intro p\")\n",
|
| 348 |
-
"state2 = await server.goal_tactic_async(state1, TacticExpr(\"fun h => h\"))\n",
|
| 349 |
-
"print(state2)"
|
| 350 |
-
]
|
| 351 |
-
},
|
| 352 |
-
{
|
| 353 |
-
"cell_type": "markdown",
|
| 354 |
-
"id": "2ed8ea8f-708d-4d30-a7c4-fae20f07fd91",
|
| 355 |
-
"metadata": {},
|
| 356 |
-
"source": [
|
| 357 |
-
"### Drafting\n",
|
| 358 |
-
"\n",
|
| 359 |
-
"Pantograph supports drafting (technically the sketch step) from\n",
|
| 360 |
-
"[Draft-Sketch-Prove](https://github.com/wellecks/ntptutorial/tree/main/partII_dsp).\n",
|
| 361 |
-
"Pantograph's drafting feature is more powerful. At any place in the proof, you\n",
|
| 362 |
-
"can replace an expression with `sorry`, and the `sorry` will become a goal. Any type errors will also become goals. In order to detect whether type errors have occurred, the user can look at the messages from each compilation unit.\n",
|
| 363 |
-
"\n",
|
| 364 |
-
"At this point we must introduce the idea of compilation units. Each Lean\n",
|
| 365 |
-
"definition, theorem, constant, etc., is a *compilation unit*. When Pantograph\n",
|
| 366 |
-
"extracts data from Lean source code, it sections the data into these compilation\n",
|
| 367 |
-
"units.\n",
|
| 368 |
-
"\n",
|
| 369 |
-
"For example, consider this sketch produced by a language model prover:\n",
|
| 370 |
-
"```lean\n",
|
| 371 |
-
"by\n",
|
| 372 |
-
" intros n m\n",
|
| 373 |
-
" induction n with\n",
|
| 374 |
-
" | zero =>\n",
|
| 375 |
-
" have h_base: 0 + m = m := sorry\n",
|
| 376 |
-
" have h_symm: m + 0 = m := sorry\n",
|
| 377 |
-
" sorry\n",
|
| 378 |
-
" | succ n ih =>\n",
|
| 379 |
-
" have h_inductive: n + m = m + n := sorry\n",
|
| 380 |
-
" have h_pull_succ_out_from_right: m + Nat.succ n = Nat.succ (m + n) := sorry\n",
|
| 381 |
-
" have h_flip_n_plus_m: Nat.succ (n + m) = Nat.succ (m + n) := sorry\n",
|
| 382 |
-
" have h_pull_succ_out_from_left: Nat.succ n + m = Nat.succ (n + m) := sorry\n",
|
| 383 |
-
" sorry\n",
|
| 384 |
-
"```\n",
|
| 385 |
-
"There are some `sorry`s that we want to solve automatically with hammer tactics. We can do this by drafting.\n",
|
| 386 |
-
"\n",
|
| 387 |
-
"Pantograph can also load `sorry`s from a code snippet, which provides an alternative way for proof initiation. Warning: `load_sorry` does not work with `example` declarations."
|
| 388 |
-
]
|
| 389 |
-
},
|
| 390 |
-
{
|
| 391 |
-
"cell_type": "code",
|
| 392 |
-
"execution_count": 13,
|
| 393 |
-
"id": "e435456b-892a-42e7-a8a9-4968c8202c83",
|
| 394 |
-
"metadata": {},
|
| 395 |
-
"outputs": [
|
| 396 |
-
{
|
| 397 |
-
"name": "stdout",
|
| 398 |
-
"output_type": "stream",
|
| 399 |
-
"text": [
|
| 400 |
-
"\n",
|
| 401 |
-
"β’ β (n m : Nat), n + m = m + n\n"
|
| 402 |
-
]
|
| 403 |
-
}
|
| 404 |
-
],
|
| 405 |
-
"source": [
|
| 406 |
-
"sketch = \"\"\"\n",
|
| 407 |
-
"theorem add_comm_proved_formal_sketch : β n m : Nat, n + m = m + n := sorry\n",
|
| 408 |
-
"\"\"\"\n",
|
| 409 |
-
"unit, = await server.load_sorry_async(sketch)\n",
|
| 410 |
-
"print(unit.goal_state)"
|
| 411 |
-
]
|
| 412 |
-
},
|
| 413 |
-
{
|
| 414 |
-
"cell_type": "code",
|
| 415 |
-
"execution_count": 14,
|
| 416 |
-
"id": "efce284c-35d0-453f-9857-aebe7b09bf3e",
|
| 417 |
-
"metadata": {},
|
| 418 |
-
"outputs": [
|
| 419 |
-
{
|
| 420 |
-
"name": "stdout",
|
| 421 |
-
"output_type": "stream",
|
| 422 |
-
"text": [
|
| 423 |
-
"n : Nat\n",
|
| 424 |
-
"m : Nat\n",
|
| 425 |
-
"β’ 0 + m = m\n",
|
| 426 |
-
"n : Nat\n",
|
| 427 |
-
"m : Nat\n",
|
| 428 |
-
"h_base : 0 + m = m\n",
|
| 429 |
-
"β’ m + 0 = m\n",
|
| 430 |
-
"n : Nat\n",
|
| 431 |
-
"m : Nat\n",
|
| 432 |
-
"h_base : 0 + m = m\n",
|
| 433 |
-
"h_symm : m + 0 = m\n",
|
| 434 |
-
"β’ 0 + m = m + 0\n",
|
| 435 |
-
"nβ : Nat\n",
|
| 436 |
-
"m : Nat\n",
|
| 437 |
-
"n : Nat\n",
|
| 438 |
-
"ih : n + m = m + n\n",
|
| 439 |
-
"β’ n + 1 + m = m + (n + 1)\n"
|
| 440 |
-
]
|
| 441 |
-
}
|
| 442 |
-
],
|
| 443 |
-
"source": [
|
| 444 |
-
"step = \"\"\"\n",
|
| 445 |
-
"by\n",
|
| 446 |
-
" -- Consider some n and m in Nats.\n",
|
| 447 |
-
" intros n m\n",
|
| 448 |
-
" -- Perform induction on n.\n",
|
| 449 |
-
" induction n with\n",
|
| 450 |
-
" | zero =>\n",
|
| 451 |
-
" -- Base case: When n = 0, we need to show 0 + m = m + 0.\n",
|
| 452 |
-
" -- We have the fact 0 + m = m by the definition of addition.\n",
|
| 453 |
-
" have h_base: 0 + m = m := sorry\n",
|
| 454 |
-
" -- We also have the fact m + 0 = m by the definition of addition.\n",
|
| 455 |
-
" have h_symm: m + 0 = m := sorry\n",
|
| 456 |
-
" -- Combine facts to close goal\n",
|
| 457 |
-
" sorry\n",
|
| 458 |
-
" | succ n ih =>\n",
|
| 459 |
-
" sorry\n",
|
| 460 |
-
"\"\"\"\n",
|
| 461 |
-
"from pantograph.expr import TacticDraft\n",
|
| 462 |
-
"tactic = TacticDraft(step)\n",
|
| 463 |
-
"state1 = await server.goal_tactic_async(unit.goal_state, tactic)\n",
|
| 464 |
-
"print(state1)"
|
| 465 |
-
]
|
| 466 |
-
},
|
| 467 |
-
{
|
| 468 |
-
"cell_type": "markdown",
|
| 469 |
-
"id": "2cb11505-4076-481f-8736-e0d6d4e67ffc",
|
| 470 |
-
"metadata": {},
|
| 471 |
-
"source": [
|
| 472 |
-
"### Search Target Distillation\n",
|
| 473 |
-
"\n",
|
| 474 |
-
"Sometimes, we want to search for an object (witness) along with proofs (companions) of properties about the object. This problem is known as **companion generation**. In Pantograph, `load_sorry` will automatically pair companions to create coupled search targets. Note that this is only available for flat dependency structures, where one object has a list of properties."
|
| 475 |
-
]
|
| 476 |
-
},
|
| 477 |
-
{
|
| 478 |
-
"cell_type": "code",
|
| 479 |
-
"execution_count": 15,
|
| 480 |
-
"id": "d66afe22-541e-4464-86ce-b462272d76d2",
|
| 481 |
-
"metadata": {},
|
| 482 |
-
"outputs": [
|
| 483 |
-
{
|
| 484 |
-
"name": "stdout",
|
| 485 |
-
"output_type": "stream",
|
| 486 |
-
"text": [
|
| 487 |
-
"\n",
|
| 488 |
-
"β’ { f // β (n : Nat), f n = n + 1 }\n"
|
| 489 |
-
]
|
| 490 |
-
}
|
| 491 |
-
],
|
| 492 |
-
"source": [
|
| 493 |
-
"sketch = \"\"\"\n",
|
| 494 |
-
"def f : Nat -> Nat := sorry\n",
|
| 495 |
-
"theorem property (n : Nat) : f n = n + 1 := sorry\n",
|
| 496 |
-
"\"\"\"\n",
|
| 497 |
-
"target, = await server.load_sorry_async(sketch, ignore_values=True)\n",
|
| 498 |
-
"print(target.goal_state)"
|
| 499 |
-
]
|
| 500 |
-
},
|
| 501 |
-
{
|
| 502 |
-
"cell_type": "markdown",
|
| 503 |
-
"id": "efabd1ad-88aa-4bc1-90ac-2d7af58d9a07",
|
| 504 |
-
"metadata": {},
|
| 505 |
-
"source": [
|
| 506 |
-
"## Sites\n",
|
| 507 |
-
"\n",
|
| 508 |
-
"The optional `site` argument to `goal_tactic` controls the area of effect of a tactic. Site controls what the tactic sees when it asks Lean for the current goal. Most tactics only act on a single goal, but tactics acting on multiple goals are plausible as well.\n",
|
| 509 |
-
"\n",
|
| 510 |
-
"The `auto_resume` field defaults to the server option's `automaticMode` (which defaults to `True`). When this field is true, Pantograph will not deliberately hide other goals away from the tactic. This is the usual modus operandi of tactic proofs in Lean. When `auto_resume` is set to `False`, Pantograph will set other goals to dormant. This can be useful in limiting the area of effect of a tactic. However, dormanting a goal comes with the extra burden that it has to be activated (\"resume\") later, via `goal_resume`."
|
| 511 |
-
]
|
| 512 |
-
},
|
| 513 |
-
{
|
| 514 |
-
"cell_type": "code",
|
| 515 |
-
"execution_count": 16,
|
| 516 |
-
"id": "6806db59-bf2e-4c72-9903-200621ec1661",
|
| 517 |
-
"metadata": {},
|
| 518 |
-
"outputs": [
|
| 519 |
-
{
|
| 520 |
-
"name": "stdout",
|
| 521 |
-
"output_type": "stream",
|
| 522 |
-
"text": [
|
| 523 |
-
"left\n",
|
| 524 |
-
"p : Prop\n",
|
| 525 |
-
"h : p\n",
|
| 526 |
-
"β’ p\n",
|
| 527 |
-
"right\n",
|
| 528 |
-
"p : Prop\n",
|
| 529 |
-
"h : p\n",
|
| 530 |
-
"β’ p β¨ p\n"
|
| 531 |
-
]
|
| 532 |
-
}
|
| 533 |
-
],
|
| 534 |
-
"source": [
|
| 535 |
-
"state = await server.goal_start_async(\"forall (p : Prop), p -> And p (Or p p)\")\n",
|
| 536 |
-
"state = await server.goal_tactic_async(state, \"intro p h\")\n",
|
| 537 |
-
"state = await server.goal_tactic_async(state, \"apply And.intro\")\n",
|
| 538 |
-
"print(state)"
|
| 539 |
-
]
|
| 540 |
-
},
|
| 541 |
-
{
|
| 542 |
-
"cell_type": "markdown",
|
| 543 |
-
"id": "4e2f9539-bac0-4721-9f86-752090f3ed24",
|
| 544 |
-
"metadata": {},
|
| 545 |
-
"source": [
|
| 546 |
-
"In the example below, we set `auto_resume` to `False`, and the sibling goal is dormanted."
|
| 547 |
-
]
|
| 548 |
-
},
|
| 549 |
-
{
|
| 550 |
-
"cell_type": "code",
|
| 551 |
-
"execution_count": 17,
|
| 552 |
-
"id": "aebb6f3d-e2b2-4104-8d67-5adce5d2ea78",
|
| 553 |
-
"metadata": {
|
| 554 |
-
"scrolled": true
|
| 555 |
-
},
|
| 556 |
-
"outputs": [
|
| 557 |
-
{
|
| 558 |
-
"name": "stdout",
|
| 559 |
-
"output_type": "stream",
|
| 560 |
-
"text": [
|
| 561 |
-
"\n"
|
| 562 |
-
]
|
| 563 |
-
}
|
| 564 |
-
],
|
| 565 |
-
"source": [
|
| 566 |
-
"state1 = await server.goal_tactic_async(state, \"exact h\", site=Site(goal_id=0, auto_resume=False))\n",
|
| 567 |
-
"print(state1)"
|
| 568 |
-
]
|
| 569 |
-
},
|
| 570 |
-
{
|
| 571 |
-
"cell_type": "markdown",
|
| 572 |
-
"id": "a1db5a24-1f98-47f6-ab26-d00e94d3b57f",
|
| 573 |
-
"metadata": {},
|
| 574 |
-
"source": [
|
| 575 |
-
"In the example below, we preferentially operate on the second goal. Note that the first goal is still here."
|
| 576 |
-
]
|
| 577 |
-
},
|
| 578 |
-
{
|
| 579 |
-
"cell_type": "code",
|
| 580 |
-
"execution_count": 18,
|
| 581 |
-
"id": "cb0e30db-f7fd-49df-82a9-09e2eeef2885",
|
| 582 |
-
"metadata": {},
|
| 583 |
-
"outputs": [
|
| 584 |
-
{
|
| 585 |
-
"name": "stdout",
|
| 586 |
-
"output_type": "stream",
|
| 587 |
-
"text": [
|
| 588 |
-
"right.h\n",
|
| 589 |
-
"p : Prop\n",
|
| 590 |
-
"h : p\n",
|
| 591 |
-
"β’ p\n",
|
| 592 |
-
"left\n",
|
| 593 |
-
"p : Prop\n",
|
| 594 |
-
"h : p\n",
|
| 595 |
-
"β’ p\n"
|
| 596 |
-
]
|
| 597 |
-
}
|
| 598 |
-
],
|
| 599 |
-
"source": [
|
| 600 |
-
"state2 = await server.goal_tactic_async(state, \"apply Or.inl\", site=Site(goal_id=1))\n",
|
| 601 |
-
"print(state2)"
|
| 602 |
-
]
|
| 603 |
-
},
|
| 604 |
-
{
|
| 605 |
-
"cell_type": "markdown",
|
| 606 |
-
"id": "acd74351-e9d5-497c-af6f-fc5709edb54d",
|
| 607 |
-
"metadata": {},
|
| 608 |
-
"source": [
|
| 609 |
-
"## Tactic Modes\n",
|
| 610 |
-
"\n",
|
| 611 |
-
"Pantograph has special provisions for handling `conv` and `calc` tactics. The commonality of these tactics is incremental feedback: The tactic can run half way and produce some goal. Pantograph supports this via tactic modes. Every goal carries around with it a `TacticMode`, and the user is free to switch between modes. By default, the mode is `TacticMode.TACTIC`."
|
| 612 |
-
]
|
| 613 |
-
},
|
| 614 |
-
{
|
| 615 |
-
"cell_type": "code",
|
| 616 |
-
"execution_count": 19,
|
| 617 |
-
"id": "30b85bc4-a4ff-4156-b0bb-5c3496bd9472",
|
| 618 |
-
"metadata": {},
|
| 619 |
-
"outputs": [
|
| 620 |
-
{
|
| 621 |
-
"data": {
|
| 622 |
-
"text/plain": [
|
| 623 |
-
"GoalState(#24, goals=[Goal(id='_uniq.381', variables=[Variable(t='Nat', v=None, name='a'), Variable(t='Nat', v=None, name='b'), Variable(t='b = 2', v=None, name='h')], target='1 + a + 1 = a + 1 + 1', sibling_dep=None, name='calc', mode=<TacticMode.TACTIC: 1>), Goal(id='_uniq.400', variables=[Variable(t='Nat', v=None, name='a'), Variable(t='Nat', v=None, name='b'), Variable(t='b = 2', v=None, name='h')], target='a + 1 + 1 = a + b', sibling_dep=None, name=None, mode=<TacticMode.CALC: 3>)], _sentinel=#14"
|
| 624 |
-
]
|
| 625 |
-
},
|
| 626 |
-
"execution_count": 19,
|
| 627 |
-
"metadata": {},
|
| 628 |
-
"output_type": "execute_result"
|
| 629 |
-
}
|
| 630 |
-
],
|
| 631 |
-
"source": [
|
| 632 |
-
"state = await server.goal_start_async(\"β (a b: Nat), (b = 2) -> 1 + a + 1 = a + b\")\n",
|
| 633 |
-
"\n",
|
| 634 |
-
"state = await server.goal_tactic_async(state, \"intro a b h\")\n",
|
| 635 |
-
"state = await server.goal_tactic_async(state, TacticMode.CALC)\n",
|
| 636 |
-
"state = await server.goal_tactic_async(state, \"1 + a + 1 = a + 1 + 1\")\n",
|
| 637 |
-
"state"
|
| 638 |
-
]
|
| 639 |
-
}
|
| 640 |
-
],
|
| 641 |
-
"metadata": {
|
| 642 |
-
"kernelspec": {
|
| 643 |
-
"display_name": "Python 3 (ipykernel)",
|
| 644 |
-
"language": "python",
|
| 645 |
-
"name": "python3"
|
| 646 |
-
},
|
| 647 |
-
"language_info": {
|
| 648 |
-
"codemirror_mode": {
|
| 649 |
-
"name": "ipython",
|
| 650 |
-
"version": 3
|
| 651 |
-
},
|
| 652 |
-
"file_extension": ".py",
|
| 653 |
-
"mimetype": "text/x-python",
|
| 654 |
-
"name": "python",
|
| 655 |
-
"nbconvert_exporter": "python",
|
| 656 |
-
"pygments_lexer": "ipython3",
|
| 657 |
-
"version": "3.12.7"
|
| 658 |
-
}
|
| 659 |
-
},
|
| 660 |
-
"nbformat": 4,
|
| 661 |
-
"nbformat_minor": 5
|
| 662 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/doc/intro.md
DELETED
|
@@ -1,79 +0,0 @@
|
|
| 1 |
-
# Introduction
|
| 2 |
-
|
| 3 |
-
This is Pantograph, an machine-to-machine interaction interface for Lean 4.
|
| 4 |
-
Its main purpose is to train and evaluate theorem proving agents. The main
|
| 5 |
-
features of Pantograph are:
|
| 6 |
-
|
| 7 |
-
1. Writing mixed expression and tactic style proofs
|
| 8 |
-
2. Exposing the minimum amount of information for a search agent
|
| 9 |
-
3. Handling of metavariable coupling
|
| 10 |
-
4. Reading/Adding symbols from the environment
|
| 11 |
-
5. Extraction of tactic training data
|
| 12 |
-
6. Drafting incomplete proofs
|
| 13 |
-
|
| 14 |
-
## Name
|
| 15 |
-
|
| 16 |
-
The name Pantograph is a pun. It means two things
|
| 17 |
-
- A pantograph is an instrument for copying down writing. As an agent explores
|
| 18 |
-
the vast proof search space, Pantograph records the current state to ensure
|
| 19 |
-
the proof is sound.
|
| 20 |
-
- A pantograph is also an equipment for an electric train. It supplies power to
|
| 21 |
-
a locomotive. In comparison the (relatively) simple Pantograph software powers
|
| 22 |
-
theorem proving projects.
|
| 23 |
-
|
| 24 |
-
## Design Rationale
|
| 25 |
-
|
| 26 |
-
The Lean 4 interface is not conducive to search. Readers familiar with Coq may
|
| 27 |
-
know that the Coq Serapi was superseded by CoqLSP. In the opinion of the
|
| 28 |
-
authors, this is a mistake. An interface conducive for human operators to write
|
| 29 |
-
proofs is often not an interface conductive to machine learning agents for
|
| 30 |
-
searching.
|
| 31 |
-
|
| 32 |
-
All of Pantograph's business logic is written in Lean, allowing coupling between
|
| 33 |
-
the data extraction and proof search components.
|
| 34 |
-
|
| 35 |
-
## Caveats and Limitations
|
| 36 |
-
|
| 37 |
-
Pantograph does not exactly mimic Lean LSP's behaviour. That would not grant the
|
| 38 |
-
flexibility it offers. To support tree search means Pantograph has to act
|
| 39 |
-
differently from Lean in some times, but never at the sacrifice of soundness.
|
| 40 |
-
|
| 41 |
-
- When Lean LSP says "don't know how to synthesize placeholder", this indicates
|
| 42 |
-
the human operator needs to manually move the cursor to the placeholder and
|
| 43 |
-
type in the correct expression. This error therefore should not halt the proof
|
| 44 |
-
process, and the placeholder should be turned into a goal.
|
| 45 |
-
- When Lean LSP says "unresolved goals", that means a proof cannot finish where
|
| 46 |
-
it is supposed to finish at the end of a `by` block. Pantograph will raise the
|
| 47 |
-
error in this case, since it indicates the termination of a proof search branch.
|
| 48 |
-
|
| 49 |
-
Pantograph cannot perform things that are inherently constrained by Lean. These
|
| 50 |
-
include:
|
| 51 |
-
|
| 52 |
-
- If a tactic loses track of metavariables, it will not be caught until the end
|
| 53 |
-
of the proof search. This is a bug in the tactic itself.
|
| 54 |
-
- Lean's concurrency model is coΓΆperative, which means a tactic is responsible
|
| 55 |
-
for checking a cancellation flag if it runs for a long time. Pantograph's
|
| 56 |
-
built-in timeout feature requires such behaviour. A tactic which hangs without
|
| 57 |
-
checking the flag cannot be timeouted.
|
| 58 |
-
- Interceptions of parsing errors generally cannot be turned into goals (e.g.
|
| 59 |
-
`def mystery : Nat := :=`) due to Lean's parsing system.
|
| 60 |
-
|
| 61 |
-
Each Pantograph version is anchored to a Lean version specified in
|
| 62 |
-
`src/lean-toolchain`. Features can be backported to older Lean versions upon
|
| 63 |
-
request.
|
| 64 |
-
|
| 65 |
-
## Referencing
|
| 66 |
-
|
| 67 |
-
[Paper Link](https://arxiv.org/abs/2410.16429)
|
| 68 |
-
|
| 69 |
-
```bib
|
| 70 |
-
@misc{pantograph,
|
| 71 |
-
title={Pantograph: A Machine-to-Machine Interaction Interface for Advanced Theorem Proving, High Level Reasoning, and Data Extraction in Lean 4},
|
| 72 |
-
author={Leni Aniva and Chuyue Sun and Brando Miranda and Clark Barrett and Sanmi Koyejo},
|
| 73 |
-
year={2024},
|
| 74 |
-
eprint={2410.16429},
|
| 75 |
-
archivePrefix={arXiv},
|
| 76 |
-
primaryClass={cs.LO},
|
| 77 |
-
url={https://arxiv.org/abs/2410.16429},
|
| 78 |
-
}
|
| 79 |
-
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/doc/setup.md
DELETED
|
@@ -1,85 +0,0 @@
|
|
| 1 |
-
# Setup
|
| 2 |
-
|
| 3 |
-
1. Install `uv`
|
| 4 |
-
2. Clone this repository with submodules:
|
| 5 |
-
```sh
|
| 6 |
-
git clone --recurse-submodules <repo-path>
|
| 7 |
-
```
|
| 8 |
-
3. Install `elan` and `lake`: See [Lean Manual](https://docs.lean-lang.org/lean4/doc/setup.html)
|
| 9 |
-
4. Execute
|
| 10 |
-
```sh
|
| 11 |
-
cd <repo-path>
|
| 12 |
-
uv sync
|
| 13 |
-
```
|
| 14 |
-
|
| 15 |
-
`uv build` builds a wheel of Pantograph in `dist` which can then be installed. For
|
| 16 |
-
example, a downstream project could have this line in its `pyproject.toml`
|
| 17 |
-
|
| 18 |
-
```toml
|
| 19 |
-
pantograph = { file = "path/to/wheel/dist/pantograph-0.3.0-cp312-cp312-manylinux_2_40_x86_64.whl" }
|
| 20 |
-
```
|
| 21 |
-
|
| 22 |
-
All interactions with Lean pass through the `Server` class. Create an instance of Pantograph using
|
| 23 |
-
```python
|
| 24 |
-
from pantograph import Server
|
| 25 |
-
server = Server()
|
| 26 |
-
```
|
| 27 |
-
|
| 28 |
-
## Lean Dependencies
|
| 29 |
-
|
| 30 |
-
The server created from `Server()` is sufficient for basic theorem proving tasks
|
| 31 |
-
reliant on Lean's `Init` library. Some users may find this insufficient and want
|
| 32 |
-
to use non-builtin libraries such as Aesop or Mathlib4. In this case, feed in a
|
| 33 |
-
list of module names via the `imports` parameter e.g. `imports=["Mathlib"]`. Due
|
| 34 |
-
to inherent restrictions in Lean, importing a module that has not been imported
|
| 35 |
-
before after the server has already started is not allowed and will trigger
|
| 36 |
-
initializer exceptions. It may be possible to circumvent this if Lean relaxes
|
| 37 |
-
this constraint.
|
| 38 |
-
|
| 39 |
-
To use external Lean dependencies such as
|
| 40 |
-
[Mathlib4](https://github.com/leanprover-community/mathlib4), Pantograph relies
|
| 41 |
-
on an existing Lean repository. Instructions for creating this repository can be
|
| 42 |
-
found [here](https://docs.lean-lang.org/lean4/doc/setup.html#lake).
|
| 43 |
-
|
| 44 |
-
After creating this initial Lean repository, execute in the repository
|
| 45 |
-
```sh
|
| 46 |
-
lake build
|
| 47 |
-
```
|
| 48 |
-
|
| 49 |
-
to build all files from the repository. This step is necessary after any file in
|
| 50 |
-
the repository is modified.
|
| 51 |
-
|
| 52 |
-
Then, feed the repository's path to the server
|
| 53 |
-
```python
|
| 54 |
-
server = Server(project_path="./path-to-lean-repo/")
|
| 55 |
-
```
|
| 56 |
-
|
| 57 |
-
For a complete example, see `examples/`.
|
| 58 |
-
|
| 59 |
-
## Server Parameters
|
| 60 |
-
|
| 61 |
-
The server has some additional options.
|
| 62 |
-
|
| 63 |
-
- `core_options`: These options are passed to Lean's kernel. For example
|
| 64 |
-
`set_option pp.all true` in Lean corresponds to passing `pp.all=true` to
|
| 65 |
-
`core_options`.
|
| 66 |
-
- `options`: These options are given to Pantograph itself. See below.
|
| 67 |
-
- `timeout`: This timeout controls the maximum wait time for the server
|
| 68 |
-
instance. If the server instance does not respond within this timeout limit,
|
| 69 |
-
it gets terminated. In some cases it is necessary to increase this if loading
|
| 70 |
-
a Lean project takes too long.
|
| 71 |
-
|
| 72 |
-
A special note about running in Jupyter: Use the asynchronous version of each
|
| 73 |
-
function.
|
| 74 |
-
|
| 75 |
-
```python
|
| 76 |
-
server = await Server.create()
|
| 77 |
-
unit, = await server.load_sorry_async(sketch)
|
| 78 |
-
print(unit.goal_state)
|
| 79 |
-
```
|
| 80 |
-
|
| 81 |
-
### Options
|
| 82 |
-
|
| 83 |
-
- `automaticMode`: Set to false to disable automatic goal continuation.
|
| 84 |
-
- `timeout`: Set to a positive integer to set tactic execution timeout.
|
| 85 |
-
- `printDependentMVars`: Set to true to explicitly store goal inter-dependencies
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/examples/Example/.gitignore
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
/build
|
| 2 |
-
/lakefile.olean
|
| 3 |
-
/lake-packages/*
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/examples/Example/Example.lean
DELETED
|
@@ -1,14 +0,0 @@
|
|
| 1 |
-
import Aesop
|
| 2 |
-
|
| 3 |
-
/-- Ensure that Aesop is running -/
|
| 4 |
-
example : Ξ± β Ξ± :=
|
| 5 |
-
by aesop
|
| 6 |
-
|
| 7 |
-
example : β (p q: Prop), p β¨ q β q β¨ p := by
|
| 8 |
-
intro p q h
|
| 9 |
-
-- Here are some comments
|
| 10 |
-
cases h
|
| 11 |
-
. apply Or.inr
|
| 12 |
-
assumption
|
| 13 |
-
. apply Or.inl
|
| 14 |
-
assumption
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/examples/Example/lake-manifest.json
DELETED
|
@@ -1,25 +0,0 @@
|
|
| 1 |
-
{"version": "1.1.0",
|
| 2 |
-
"packagesDir": ".lake/packages",
|
| 3 |
-
"packages":
|
| 4 |
-
[{"url": "https://github.com/leanprover-community/aesop.git",
|
| 5 |
-
"type": "git",
|
| 6 |
-
"subDir": null,
|
| 7 |
-
"scope": "",
|
| 8 |
-
"rev": "1256a18522728c2eeed6109b02dd2b8f207a2a3c",
|
| 9 |
-
"name": "aesop",
|
| 10 |
-
"manifestFile": "lake-manifest.json",
|
| 11 |
-
"inputRev": "v4.22.0",
|
| 12 |
-
"inherited": false,
|
| 13 |
-
"configFile": "lakefile.toml"},
|
| 14 |
-
{"url": "https://github.com/leanprover-community/batteries",
|
| 15 |
-
"type": "git",
|
| 16 |
-
"subDir": null,
|
| 17 |
-
"scope": "",
|
| 18 |
-
"rev": "e96b5eca4fcfe2e0e96a1511a6cd5747515aba82",
|
| 19 |
-
"name": "batteries",
|
| 20 |
-
"manifestFile": "lake-manifest.json",
|
| 21 |
-
"inputRev": "v4.22.0-rc4",
|
| 22 |
-
"inherited": true,
|
| 23 |
-
"configFile": "lakefile.toml"}],
|
| 24 |
-
"name": "Example",
|
| 25 |
-
"lakeDir": ".lake"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/examples/Example/lakefile.lean
DELETED
|
@@ -1,10 +0,0 @@
|
|
| 1 |
-
import Lake
|
| 2 |
-
open Lake DSL
|
| 3 |
-
|
| 4 |
-
require aesop from git
|
| 5 |
-
"https://github.com/leanprover-community/aesop.git" @ "v4.22.0"
|
| 6 |
-
|
| 7 |
-
package Example
|
| 8 |
-
|
| 9 |
-
@[default_target]
|
| 10 |
-
lean_lib Example
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/examples/Example/lean-toolchain
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
../../src/lean-toolchain
|
|
|
|
|
|
PyPantograph/examples/README.md
DELETED
|
@@ -1,24 +0,0 @@
|
|
| 1 |
-
# Examples
|
| 2 |
-
|
| 3 |
-
This example showcases how to bind library dependencies and execute the `Aesop`
|
| 4 |
-
tactic in Lean. First build the example project:
|
| 5 |
-
``` sh
|
| 6 |
-
pushd Example
|
| 7 |
-
lake build
|
| 8 |
-
popd
|
| 9 |
-
```
|
| 10 |
-
This would generate compiled `.olean` files. Then run one of the examples from the
|
| 11 |
-
project root:
|
| 12 |
-
``` sh
|
| 13 |
-
poetry run examples/aesop.py
|
| 14 |
-
poetry run examples/sketch.py
|
| 15 |
-
```
|
| 16 |
-
|
| 17 |
-
Warning: If you make modifications to any Lean files, you must re-run `lake
|
| 18 |
-
build`! Moreover, the version of the Lean used in the example folder (including
|
| 19 |
-
dependencies in `lakefile.lean` and `lean-toolchain`) **must match exactly**
|
| 20 |
-
with the version in `src/`!
|
| 21 |
-
|
| 22 |
-
* `aesop.py`: Example of how to use the `aesop` tactic
|
| 23 |
-
* `sketch.py`: Example of loading a sketch
|
| 24 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/examples/aesop.py
DELETED
|
@@ -1,14 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
|
| 3 |
-
from pathlib import Path
|
| 4 |
-
from pantograph.server import Server
|
| 5 |
-
|
| 6 |
-
# This example shows how to use project dependencies
|
| 7 |
-
|
| 8 |
-
if __name__ == '__main__':
|
| 9 |
-
project_path = Path(__file__).parent.resolve() / 'Example'
|
| 10 |
-
print(f"$PWD: {project_path}")
|
| 11 |
-
server = Server(imports=['Example'], project_path=project_path)
|
| 12 |
-
state0 = server.goal_start("forall (p q: Prop), Or p q -> Or q p")
|
| 13 |
-
state1 = server.goal_tactic(state0, tactic="aesop")
|
| 14 |
-
assert state1.is_solved
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/examples/branch-sorry.py
DELETED
|
@@ -1,13 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
|
| 3 |
-
from pantograph.server import Server
|
| 4 |
-
from pantograph.expr import TacticHave
|
| 5 |
-
|
| 6 |
-
# This example shows what happens when a tactic generates a sorry.
|
| 7 |
-
if __name__ == '__main__':
|
| 8 |
-
server = Server(imports=['Init'])
|
| 9 |
-
state0 = server.goal_start("1 = 0")
|
| 10 |
-
state1 = server.goal_tactic(state0, tactic=TacticHave("1 = 0"))
|
| 11 |
-
print(state1)
|
| 12 |
-
state1b = server.goal_tactic(state1, tactic="apply?")
|
| 13 |
-
print(state1b)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/examples/simple.py
DELETED
|
@@ -1,7 +0,0 @@
|
|
| 1 |
-
from pantograph.server import Server
|
| 2 |
-
|
| 3 |
-
if __name__ == '__main__':
|
| 4 |
-
server = Server(imports=['Init'])
|
| 5 |
-
state0 = server.goal_start("forall (p q: Prop), Or p q -> Or q p")
|
| 6 |
-
state1 = server.goal_tactic(state0, tactic="intro")
|
| 7 |
-
print(state1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/examples/sketch.py
DELETED
|
@@ -1,41 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
|
| 3 |
-
from pantograph.server import Server
|
| 4 |
-
from pantograph.expr import TacticDraft
|
| 5 |
-
|
| 6 |
-
root = """
|
| 7 |
-
theorem add_comm_proved_formal_sketch : β n m : Nat, n + m = m + n := sorry
|
| 8 |
-
"""
|
| 9 |
-
|
| 10 |
-
sketch = """
|
| 11 |
-
by
|
| 12 |
-
-- Consider some n and m in Nats.
|
| 13 |
-
intros n m
|
| 14 |
-
-- Perform induction on n.
|
| 15 |
-
induction n with
|
| 16 |
-
| zero =>
|
| 17 |
-
sorry
|
| 18 |
-
| succ n ih =>
|
| 19 |
-
-- Inductive step: Assume n + m = m + n, we need to show succ n + m = m + succ n.
|
| 20 |
-
-- By the inductive hypothesis, we have n + m = m + n.
|
| 21 |
-
have h_inductive: n + m = m + n := sorry
|
| 22 |
-
-- 1. Note we start with: Nat.succ n + m = m + Nat.succ n, so, pull the succ out from m + Nat.succ n on the right side from the addition using addition facts Nat.add_succ.
|
| 23 |
-
have h_pull_succ_out_from_right: m + Nat.succ n = Nat.succ (m + n) := sorry
|
| 24 |
-
-- 2. then to flip m + S n to something like S (n + m) we need to use the IH.
|
| 25 |
-
have h_flip_n_plus_m: Nat.succ (n + m) = Nat.succ (m + n) := sorry
|
| 26 |
-
-- 3. Now the n & m are on the correct sides Nat.succ n + m = Nat.succ (n + m), so let's use the def of addition to pull out the succ from the addition on the left using Nat.succ_add.
|
| 27 |
-
have h_pull_succ_out_from_left: Nat.succ n + m = Nat.succ (n + m) := sorry
|
| 28 |
-
-- Combine facts to close goal
|
| 29 |
-
sorry
|
| 30 |
-
"""
|
| 31 |
-
|
| 32 |
-
if __name__ == '__main__':
|
| 33 |
-
server = Server()
|
| 34 |
-
unit, = server.load_sorry(root)
|
| 35 |
-
print(unit.goal_state)
|
| 36 |
-
|
| 37 |
-
# Send the draft payload using `TacticDraft`
|
| 38 |
-
state1 = server.goal_tactic(
|
| 39 |
-
unit.goal_state,
|
| 40 |
-
tactic=TacticDraft(sketch))
|
| 41 |
-
print(state1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/pantograph/.gitignore
DELETED
|
@@ -1,2 +0,0 @@
|
|
| 1 |
-
pantograph-repl
|
| 2 |
-
lean-toolchain
|
|
|
|
|
|
|
|
|
PyPantograph/pantograph/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
from pantograph.server import Server
|
|
|
|
|
|
PyPantograph/pantograph/data.py
DELETED
|
@@ -1,113 +0,0 @@
|
|
| 1 |
-
from pantograph.message import Severity, Message
|
| 2 |
-
from pantograph.expr import GoalState
|
| 3 |
-
|
| 4 |
-
from typing import Optional, Tuple
|
| 5 |
-
from dataclasses import dataclass, field
|
| 6 |
-
from enum import Enum
|
| 7 |
-
|
| 8 |
-
@dataclass(frozen=True)
|
| 9 |
-
class TacticInvocation:
|
| 10 |
-
"""
|
| 11 |
-
One tactic invocation with the before/after goals extracted from Lean source
|
| 12 |
-
code.
|
| 13 |
-
"""
|
| 14 |
-
before: str
|
| 15 |
-
after: str
|
| 16 |
-
tactic: str
|
| 17 |
-
used_constants: list[str]
|
| 18 |
-
|
| 19 |
-
@staticmethod
|
| 20 |
-
def parse(payload: dict):
|
| 21 |
-
return TacticInvocation(
|
| 22 |
-
before=payload["goalBefore"],
|
| 23 |
-
after=payload["goalAfter"],
|
| 24 |
-
tactic=payload["tactic"],
|
| 25 |
-
used_constants=payload.get('usedConstants', []),
|
| 26 |
-
)
|
| 27 |
-
|
| 28 |
-
@dataclass(frozen=True)
|
| 29 |
-
class CompilationUnit:
|
| 30 |
-
|
| 31 |
-
# Byte boundaries [begin, end[ of each compilation unit.
|
| 32 |
-
i_begin: int
|
| 33 |
-
i_end: int
|
| 34 |
-
|
| 35 |
-
messages: list[Message] = field(default_factory=lambda: [])
|
| 36 |
-
|
| 37 |
-
invocations: Optional[list[TacticInvocation]] = None
|
| 38 |
-
# If `goal_state` is none, maybe error has occurred. See `messages`
|
| 39 |
-
goal_state: Optional[GoalState] = None
|
| 40 |
-
goal_src_boundaries: Optional[list[Tuple[int, int]]] = None
|
| 41 |
-
|
| 42 |
-
new_constants: Optional[list[str]] = None
|
| 43 |
-
|
| 44 |
-
@staticmethod
|
| 45 |
-
def parse(payload: dict, goal_state_sentinel=None, invocations=None):
|
| 46 |
-
i_begin = payload["boundary"][0]
|
| 47 |
-
i_end = payload["boundary"][1]
|
| 48 |
-
messages = [Message.parse(m) for m in payload["messages"]]
|
| 49 |
-
|
| 50 |
-
if invocations:
|
| 51 |
-
invocations = [
|
| 52 |
-
TacticInvocation.parse(i) for i in invocations
|
| 53 |
-
]
|
| 54 |
-
else:
|
| 55 |
-
invocations = None
|
| 56 |
-
|
| 57 |
-
if (state_id := payload.get("goalStateId")) is not None:
|
| 58 |
-
goal_state = GoalState.parse_inner(int(state_id), payload["goals"], [], goal_state_sentinel)
|
| 59 |
-
goal_src_boundaries = payload["goalSrcBoundaries"]
|
| 60 |
-
else:
|
| 61 |
-
goal_state = None
|
| 62 |
-
goal_src_boundaries = None
|
| 63 |
-
|
| 64 |
-
new_constants = payload.get("newConstants")
|
| 65 |
-
|
| 66 |
-
return CompilationUnit(
|
| 67 |
-
i_begin,
|
| 68 |
-
i_end,
|
| 69 |
-
messages,
|
| 70 |
-
invocations,
|
| 71 |
-
goal_state,
|
| 72 |
-
goal_src_boundaries,
|
| 73 |
-
new_constants
|
| 74 |
-
)
|
| 75 |
-
|
| 76 |
-
@dataclass
|
| 77 |
-
class CheckTrackResult:
|
| 78 |
-
|
| 79 |
-
src_messages : list[Message]
|
| 80 |
-
dst_messages : list[Message]
|
| 81 |
-
failure : Optional[str] = None
|
| 82 |
-
|
| 83 |
-
@property
|
| 84 |
-
def hasSrcError(self):
|
| 85 |
-
return any(m.severity == Severity.ERROR for m in self.src_messages)
|
| 86 |
-
@property
|
| 87 |
-
def hasDstError(self):
|
| 88 |
-
return any(m.severity == Severity.ERROR for m in self.dst_messages)
|
| 89 |
-
@property
|
| 90 |
-
def succeeded(self):
|
| 91 |
-
return not self.hasSrcError and not self.hasDstError and self.failure is None
|
| 92 |
-
@property
|
| 93 |
-
def feedback(self):
|
| 94 |
-
"""
|
| 95 |
-
Feedback based on the dst
|
| 96 |
-
"""
|
| 97 |
-
result = [str(s) for s in self.dst_messages]
|
| 98 |
-
if self.failure:
|
| 99 |
-
result.append(self.failure)
|
| 100 |
-
return result
|
| 101 |
-
|
| 102 |
-
@dataclass(frozen=True)
|
| 103 |
-
class SearchTarget:
|
| 104 |
-
goal_state: GoalState
|
| 105 |
-
|
| 106 |
-
@staticmethod
|
| 107 |
-
def parse(payload: dict, goal_state_sentinel=None):
|
| 108 |
-
state_id = payload.get("stateId")
|
| 109 |
-
goal_state = GoalState.parse_inner(int(state_id), payload["goals"], [], goal_state_sentinel)
|
| 110 |
-
|
| 111 |
-
return SearchTarget(
|
| 112 |
-
goal_state,
|
| 113 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/pantograph/expr.py
DELETED
|
@@ -1,202 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Data structuers for expressions and goals
|
| 3 |
-
"""
|
| 4 |
-
from pantograph.message import Message
|
| 5 |
-
|
| 6 |
-
from dataclasses import dataclass, field
|
| 7 |
-
from enum import Enum
|
| 8 |
-
from typing import Optional, TypeAlias
|
| 9 |
-
|
| 10 |
-
Expr: TypeAlias = str
|
| 11 |
-
|
| 12 |
-
def parse_expr(payload: dict) -> Expr:
|
| 13 |
-
"""
|
| 14 |
-
:meta private:
|
| 15 |
-
"""
|
| 16 |
-
return payload["pp"]
|
| 17 |
-
|
| 18 |
-
class TacticMode(Enum):
|
| 19 |
-
"""
|
| 20 |
-
Current execution mode
|
| 21 |
-
"""
|
| 22 |
-
TACTIC = 1
|
| 23 |
-
CONV = 2
|
| 24 |
-
CALC = 3
|
| 25 |
-
|
| 26 |
-
def serial(self):
|
| 27 |
-
match self:
|
| 28 |
-
case TacticMode.TACTIC: return "tactic"
|
| 29 |
-
case TacticMode.CONV: return "conv"
|
| 30 |
-
case TacticMode.CALC: return "calc"
|
| 31 |
-
|
| 32 |
-
@dataclass(frozen=True)
|
| 33 |
-
class Variable:
|
| 34 |
-
t: Expr
|
| 35 |
-
v: Optional[Expr] = None
|
| 36 |
-
name: Optional[str] = None
|
| 37 |
-
|
| 38 |
-
@staticmethod
|
| 39 |
-
def parse(payload: dict):
|
| 40 |
-
name = payload.get("userName")
|
| 41 |
-
t = parse_expr(payload["type"])
|
| 42 |
-
v = payload.get("value")
|
| 43 |
-
if v:
|
| 44 |
-
v = parse_expr(v)
|
| 45 |
-
return Variable(t, v, name)
|
| 46 |
-
|
| 47 |
-
def __str__(self):
|
| 48 |
-
"""
|
| 49 |
-
:meta public:
|
| 50 |
-
"""
|
| 51 |
-
result = self.name if self.name else "_"
|
| 52 |
-
result += f" : {self.t}"
|
| 53 |
-
if self.v:
|
| 54 |
-
result += f" := {self.v}"
|
| 55 |
-
return result
|
| 56 |
-
|
| 57 |
-
@dataclass(frozen=True)
|
| 58 |
-
class Goal:
|
| 59 |
-
id: str
|
| 60 |
-
variables: list[Variable]
|
| 61 |
-
target: Expr
|
| 62 |
-
sibling_dep: Optional[set[int]] = field(default_factory=lambda: None)
|
| 63 |
-
name: Optional[str] = None
|
| 64 |
-
mode: TacticMode = TacticMode.TACTIC
|
| 65 |
-
|
| 66 |
-
@staticmethod
|
| 67 |
-
def sentence(id: str, target: Expr):
|
| 68 |
-
"""
|
| 69 |
-
:meta public:
|
| 70 |
-
"""
|
| 71 |
-
return Goal(id=id, variables=[], target=target)
|
| 72 |
-
|
| 73 |
-
@staticmethod
|
| 74 |
-
def parse(payload: dict, sibling_map: dict[str, int]):
|
| 75 |
-
id = payload["name"]
|
| 76 |
-
name = payload.get("userName")
|
| 77 |
-
variables = [Variable.parse(v) for v in payload["vars"]]
|
| 78 |
-
target = parse_expr(payload["target"])
|
| 79 |
-
mode = TacticMode[payload["fragment"].upper()]
|
| 80 |
-
|
| 81 |
-
sibling_dep = None
|
| 82 |
-
for e in [payload["target"]] \
|
| 83 |
-
+ [v["type"] for v in payload["vars"]] \
|
| 84 |
-
+ [v["value"] for v in payload["vars"] if "value" in v]:
|
| 85 |
-
dependents = e.get("dependentMVars")
|
| 86 |
-
if dependents is None:
|
| 87 |
-
continue
|
| 88 |
-
deps = [sibling_map[d] for d in dependents if d in sibling_map]
|
| 89 |
-
if sibling_dep:
|
| 90 |
-
sibling_dep = {*sibling_dep, *deps}
|
| 91 |
-
else:
|
| 92 |
-
sibling_dep = {*deps}
|
| 93 |
-
|
| 94 |
-
return Goal(id, variables, target, sibling_dep, name, mode)
|
| 95 |
-
|
| 96 |
-
def __str__(self):
|
| 97 |
-
head = f"case {self.name}\n" if self.name else ""
|
| 98 |
-
front = "|" if self.mode == TacticMode.CONV else "β’"
|
| 99 |
-
return head +\
|
| 100 |
-
"\n".join(str(v) for v in self.variables) +\
|
| 101 |
-
f"\n{front} {self.target}"
|
| 102 |
-
|
| 103 |
-
@dataclass(frozen=True)
|
| 104 |
-
class GoalState:
|
| 105 |
-
state_id: int
|
| 106 |
-
goals: list[Goal]
|
| 107 |
-
messages: list[Message]
|
| 108 |
-
|
| 109 |
-
# For tracking memory usage
|
| 110 |
-
_sentinel: list[int]
|
| 111 |
-
|
| 112 |
-
def __del__(self):
|
| 113 |
-
self._sentinel.append(self.state_id)
|
| 114 |
-
def __repr__(self):
|
| 115 |
-
cls = self.__class__.__name__
|
| 116 |
-
messages = f"messages={repr(self.messages)}, " if self.messages else ""
|
| 117 |
-
return f"{cls}(#{self.state_id}, goals={repr(self.goals)}{messages}, _sentinel=#{len(self._sentinel)}"
|
| 118 |
-
|
| 119 |
-
@property
|
| 120 |
-
def is_solved(self) -> bool:
|
| 121 |
-
"""
|
| 122 |
-
WARNING: Does not handle dormant goals.
|
| 123 |
-
|
| 124 |
-
:meta public:
|
| 125 |
-
"""
|
| 126 |
-
return not self.goals
|
| 127 |
-
|
| 128 |
-
@staticmethod
|
| 129 |
-
def parse_inner(state_id: int, goals: list, messages: list[dict], _sentinel: list[int]):
|
| 130 |
-
assert _sentinel is not None
|
| 131 |
-
goal_names = { g["name"]: i for i, g in enumerate(goals) }
|
| 132 |
-
goals = [Goal.parse(g, goal_names) for g in goals]
|
| 133 |
-
messages = [Message.parse(m) for m in messages]
|
| 134 |
-
return GoalState(state_id, goals, messages, _sentinel)
|
| 135 |
-
@staticmethod
|
| 136 |
-
def parse(payload: dict, messages: list[dict], _sentinel: list[int]):
|
| 137 |
-
return GoalState.parse_inner(payload["nextStateId"], payload["goals"], messages, _sentinel)
|
| 138 |
-
|
| 139 |
-
def __str__(self):
|
| 140 |
-
"""
|
| 141 |
-
:meta public:
|
| 142 |
-
"""
|
| 143 |
-
return "\n".join([str(g) for g in self.goals])
|
| 144 |
-
|
| 145 |
-
@dataclass(frozen=True)
|
| 146 |
-
class Site:
|
| 147 |
-
"""
|
| 148 |
-
Acting area of a tactic
|
| 149 |
-
"""
|
| 150 |
-
goal_id: Optional[int] = None
|
| 151 |
-
auto_resume: Optional[bool] = None
|
| 152 |
-
|
| 153 |
-
def serial(self) -> dict:
|
| 154 |
-
result = {}
|
| 155 |
-
if self.goal_id is not None:
|
| 156 |
-
result["goalId"] = self.goal_id
|
| 157 |
-
if self.auto_resume is not None:
|
| 158 |
-
result["autoResume"] = self.auto_resume
|
| 159 |
-
return result
|
| 160 |
-
|
| 161 |
-
class Subsumption(Enum):
|
| 162 |
-
"""
|
| 163 |
-
Subsumption result
|
| 164 |
-
"""
|
| 165 |
-
NONE = 1
|
| 166 |
-
SUBSUMED = 2
|
| 167 |
-
CYCLE = 3
|
| 168 |
-
|
| 169 |
-
@dataclass(frozen=True)
|
| 170 |
-
class TacticHave:
|
| 171 |
-
"""
|
| 172 |
-
The `have` tactic, equivalent to
|
| 173 |
-
```lean
|
| 174 |
-
have {binder_name} : {branch} := ...
|
| 175 |
-
```
|
| 176 |
-
"""
|
| 177 |
-
branch: str
|
| 178 |
-
binder_name: Optional[str] = None
|
| 179 |
-
@dataclass(frozen=True)
|
| 180 |
-
class TacticLet:
|
| 181 |
-
"""
|
| 182 |
-
The `let` tactic, equivalent to
|
| 183 |
-
```lean
|
| 184 |
-
let {binder_name} : {branch} := ...
|
| 185 |
-
```
|
| 186 |
-
"""
|
| 187 |
-
branch: str
|
| 188 |
-
binder_name: Optional[str] = None
|
| 189 |
-
@dataclass(frozen=True)
|
| 190 |
-
class TacticExpr:
|
| 191 |
-
"""
|
| 192 |
-
Assigns an expression to the current goal
|
| 193 |
-
"""
|
| 194 |
-
expr: str
|
| 195 |
-
@dataclass(frozen=True)
|
| 196 |
-
class TacticDraft:
|
| 197 |
-
"""
|
| 198 |
-
Assigns an expression to the current goal
|
| 199 |
-
"""
|
| 200 |
-
expr: str
|
| 201 |
-
|
| 202 |
-
Tactic: TypeAlias = str | TacticHave | TacticLet | TacticExpr | TacticDraft | TacticMode
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/pantograph/message.py
DELETED
|
@@ -1,73 +0,0 @@
|
|
| 1 |
-
from dataclasses import dataclass
|
| 2 |
-
from enum import Enum
|
| 3 |
-
from typing import Optional, Self
|
| 4 |
-
|
| 5 |
-
class Severity(Enum):
|
| 6 |
-
INFORMATION = 1
|
| 7 |
-
WARNING = 2
|
| 8 |
-
ERROR = 3
|
| 9 |
-
|
| 10 |
-
def __str__(self):
|
| 11 |
-
cls = self.__class__.__name__
|
| 12 |
-
return super(Severity, self).__str__()[len(cls)+1:].lower()
|
| 13 |
-
|
| 14 |
-
@dataclass(frozen=True)
|
| 15 |
-
class Position:
|
| 16 |
-
"""
|
| 17 |
-
Position in a file
|
| 18 |
-
"""
|
| 19 |
-
line: int
|
| 20 |
-
column: int
|
| 21 |
-
|
| 22 |
-
@staticmethod
|
| 23 |
-
def parse(d: Optional[dict]) -> Optional[Self]:
|
| 24 |
-
if d is None:
|
| 25 |
-
return None
|
| 26 |
-
return Position(line=d["line"], column=d["column"])
|
| 27 |
-
|
| 28 |
-
@dataclass(frozen=True)
|
| 29 |
-
class Message:
|
| 30 |
-
data: str
|
| 31 |
-
pos: Position
|
| 32 |
-
pos_end: Optional[Position] = None
|
| 33 |
-
severity: Severity = Severity.ERROR
|
| 34 |
-
kind: Optional[str] = None
|
| 35 |
-
|
| 36 |
-
@staticmethod
|
| 37 |
-
def parse(d: dict) -> Self:
|
| 38 |
-
kind = None if d["kind"] == "[anonymous]" else d["kind"]
|
| 39 |
-
return Message(
|
| 40 |
-
severity=Severity[d["severity"].upper()],
|
| 41 |
-
pos=Position.parse(d["pos"]),
|
| 42 |
-
pos_end=Position.parse(d.get("endPos")),
|
| 43 |
-
kind=kind,
|
| 44 |
-
data=d["data"],
|
| 45 |
-
)
|
| 46 |
-
|
| 47 |
-
def __str__(self) -> str:
|
| 48 |
-
if self.pos_end is not None:
|
| 49 |
-
pos_end = f"-{self.pos_end.line}:{self.pos_end.column}"
|
| 50 |
-
else:
|
| 51 |
-
pos_end = ""
|
| 52 |
-
match self.severity:
|
| 53 |
-
case Severity.INFORMATION:
|
| 54 |
-
prefix = ""
|
| 55 |
-
case Severity.WARNING:
|
| 56 |
-
prefix = "warning: "
|
| 57 |
-
case Severity.ERROR:
|
| 58 |
-
prefix = "error: "
|
| 59 |
-
return f"{self.pos.line}:{self.pos.column}{pos_end}: {prefix}{self.data}"
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
class TacticFailure(Exception):
|
| 63 |
-
"""
|
| 64 |
-
Indicates a tactic failed to execute
|
| 65 |
-
"""
|
| 66 |
-
class ParseError(Exception):
|
| 67 |
-
"""
|
| 68 |
-
Indicates a logical error in the server.
|
| 69 |
-
"""
|
| 70 |
-
class ServerError(Exception):
|
| 71 |
-
"""
|
| 72 |
-
Indicates a logical error in the server.
|
| 73 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/pantograph/search.py
DELETED
|
@@ -1,450 +0,0 @@
|
|
| 1 |
-
from .server import Server, TacticFailure, ServerError
|
| 2 |
-
from .expr import Expr, Tactic, GoalState, Site
|
| 3 |
-
|
| 4 |
-
import random
|
| 5 |
-
from abc import abstractmethod
|
| 6 |
-
import time
|
| 7 |
-
from dataclasses import dataclass
|
| 8 |
-
from typing import Optional, List
|
| 9 |
-
import collections
|
| 10 |
-
from math import log, sqrt
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
@dataclass
|
| 15 |
-
class SearchState:
|
| 16 |
-
|
| 17 |
-
goal_state: GoalState
|
| 18 |
-
parent: Optional["SearchState"]
|
| 19 |
-
parent_goal_id: Optional[int]
|
| 20 |
-
priorities: list[float]
|
| 21 |
-
children: Optional[List["SearchState"]] = None
|
| 22 |
-
tested_tactics: Optional[List[Tactic]] = None
|
| 23 |
-
total_value: Optional[float] = None
|
| 24 |
-
tactic_feedback: Optional[str] = None
|
| 25 |
-
|
| 26 |
-
def __post_init__(self):
|
| 27 |
-
assert len(self.priorities) == len(self.goal_state.goals)
|
| 28 |
-
self.solved = [False for _ in self.goal_state.goals]
|
| 29 |
-
self.trials = [0 for _ in self.goal_state.goals]
|
| 30 |
-
self.tested_tactics = [] if self.tested_tactics is None else self.tested_tactics
|
| 31 |
-
self.children = [] if self.children is None else self.children
|
| 32 |
-
self.visit_count = 1
|
| 33 |
-
self.exhausted = False
|
| 34 |
-
self.subtree_exhausted = False
|
| 35 |
-
|
| 36 |
-
@property
|
| 37 |
-
def next_goal_id(self) -> int:
|
| 38 |
-
goal_id, _ = max(
|
| 39 |
-
((i, prio) for i, prio in enumerate(self.priorities) if not self.solved[i]),
|
| 40 |
-
key=lambda x: x[1])
|
| 41 |
-
return goal_id
|
| 42 |
-
|
| 43 |
-
@property
|
| 44 |
-
def is_root(self) -> bool:
|
| 45 |
-
return self.parent is None
|
| 46 |
-
|
| 47 |
-
@property
|
| 48 |
-
def is_solved(self) -> bool:
|
| 49 |
-
return all(self.solved)
|
| 50 |
-
|
| 51 |
-
@dataclass(frozen=True)
|
| 52 |
-
class SearchResult:
|
| 53 |
-
|
| 54 |
-
n_goals_root: int
|
| 55 |
-
duration: float
|
| 56 |
-
success: bool
|
| 57 |
-
steps: int
|
| 58 |
-
|
| 59 |
-
class Agent:
|
| 60 |
-
"""
|
| 61 |
-
An agent interface for proof search
|
| 62 |
-
"""
|
| 63 |
-
|
| 64 |
-
@abstractmethod
|
| 65 |
-
def next_tactic(
|
| 66 |
-
self,
|
| 67 |
-
state: GoalState,
|
| 68 |
-
goal_id: int,
|
| 69 |
-
) -> Optional[Tactic]:
|
| 70 |
-
"""
|
| 71 |
-
Implement this function to generate the next tactic for a goal
|
| 72 |
-
"""
|
| 73 |
-
|
| 74 |
-
@abstractmethod
|
| 75 |
-
def guidance(self, state: GoalState) -> list[float]:
|
| 76 |
-
"""
|
| 77 |
-
Return a list of priorities determining which goal should be searched
|
| 78 |
-
first. This will not be called on states with one or zero goals.
|
| 79 |
-
"""
|
| 80 |
-
return [0.0 for _ in state.goals]
|
| 81 |
-
@abstractmethod
|
| 82 |
-
def reset(self):
|
| 83 |
-
"""
|
| 84 |
-
Called after search
|
| 85 |
-
"""
|
| 86 |
-
|
| 87 |
-
def search(self,
|
| 88 |
-
server: Server,
|
| 89 |
-
goal_state: GoalState,
|
| 90 |
-
max_steps: int = 100,
|
| 91 |
-
max_trials_per_goal: int = 5,
|
| 92 |
-
verbose: bool = False) -> SearchResult:
|
| 93 |
-
"""
|
| 94 |
-
Executes proof search on this state
|
| 95 |
-
"""
|
| 96 |
-
|
| 97 |
-
assert server.is_automatic(), "Search must be run in automatic mode"
|
| 98 |
-
|
| 99 |
-
n_goals_root = len(goal_state.goals)
|
| 100 |
-
time_start = time.time()
|
| 101 |
-
|
| 102 |
-
initial_state = SearchState(
|
| 103 |
-
goal_state,
|
| 104 |
-
parent=None,
|
| 105 |
-
parent_goal_id=None,
|
| 106 |
-
priorities=[0.0 for _ in goal_state.goals]
|
| 107 |
-
)
|
| 108 |
-
search_stack = [initial_state]
|
| 109 |
-
for i_step in range(max_steps):
|
| 110 |
-
assert search_stack, "No states in search stack"
|
| 111 |
-
|
| 112 |
-
if verbose:
|
| 113 |
-
print(f"I={i_step}: len(S) = {len(search_stack)}")
|
| 114 |
-
search_state = search_stack[-1]
|
| 115 |
-
|
| 116 |
-
assert isinstance(search_state, SearchState)
|
| 117 |
-
|
| 118 |
-
if search_state.is_solved:
|
| 119 |
-
return SearchResult(
|
| 120 |
-
n_goals_root=n_goals_root,
|
| 121 |
-
duration=time.time() - time_start,
|
| 122 |
-
success=True,
|
| 123 |
-
steps=i_step,
|
| 124 |
-
)
|
| 125 |
-
|
| 126 |
-
# Find the unsolved goal with the highest priority
|
| 127 |
-
goal_id = search_state.next_goal_id
|
| 128 |
-
|
| 129 |
-
if search_state.trials[goal_id] > max_trials_per_goal:
|
| 130 |
-
# force halt the search
|
| 131 |
-
tactic = None
|
| 132 |
-
else:
|
| 133 |
-
# Generate tactic for this goal
|
| 134 |
-
tactic = self.next_tactic(search_state.goal_state, goal_id)
|
| 135 |
-
|
| 136 |
-
if verbose:
|
| 137 |
-
print(f"Next tactic: {tactic}")
|
| 138 |
-
if not tactic:
|
| 139 |
-
# resets the feedback
|
| 140 |
-
search_state.tactic_feedback = None
|
| 141 |
-
# pop the current state and continue to the next
|
| 142 |
-
search_stack.pop(-1)
|
| 143 |
-
if not search_stack:
|
| 144 |
-
if verbose:
|
| 145 |
-
print("Search stack has been exhausted")
|
| 146 |
-
self.reset()
|
| 147 |
-
return SearchResult(
|
| 148 |
-
n_goals_root=n_goals_root,
|
| 149 |
-
duration=time.time() - time_start,
|
| 150 |
-
success=False,
|
| 151 |
-
steps=i_step,
|
| 152 |
-
)
|
| 153 |
-
continue
|
| 154 |
-
|
| 155 |
-
try:
|
| 156 |
-
search_state.trials[goal_id] += 1
|
| 157 |
-
goal_state = search_state.goal_state
|
| 158 |
-
if verbose:
|
| 159 |
-
print(f"{goal_state.state_id}.{goal_id}: {tactic} on {goal_state.goals[goal_id]}")
|
| 160 |
-
next_goal_state = server.goal_tactic(goal_state, tactic, site=Site(goal_id, auto_resume=False))
|
| 161 |
-
# Generate priorities for the next goal state
|
| 162 |
-
priorities = [0.0 for _ in next_goal_state.goals] \
|
| 163 |
-
if len(next_goal_state.goals) <= 1 else \
|
| 164 |
-
self.guidance(next_goal_state)
|
| 165 |
-
parent = len(search_stack) - 1
|
| 166 |
-
next_state = SearchState(
|
| 167 |
-
goal_state=next_goal_state,
|
| 168 |
-
parent=search_state,
|
| 169 |
-
parent_goal_id=goal_id,
|
| 170 |
-
priorities=priorities
|
| 171 |
-
)
|
| 172 |
-
search_stack.append(next_state)
|
| 173 |
-
|
| 174 |
-
except TacticFailure as t:
|
| 175 |
-
if verbose:
|
| 176 |
-
print(f"Tactic failed: {t}")
|
| 177 |
-
search_state.tactic_feedback = str(t)
|
| 178 |
-
# try the next tactic. this one failed
|
| 179 |
-
except ServerError as e:
|
| 180 |
-
raise RuntimeError(f"While executing tactic: {tactic}") from e
|
| 181 |
-
|
| 182 |
-
if verbose:
|
| 183 |
-
print("Search iteration limit exhausted")
|
| 184 |
-
|
| 185 |
-
self.reset()
|
| 186 |
-
return SearchResult(
|
| 187 |
-
n_goals_root=n_goals_root,
|
| 188 |
-
duration=time.time() - time_start,
|
| 189 |
-
success=False,
|
| 190 |
-
steps=max_steps,
|
| 191 |
-
)
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
class MCTSAgent(Agent):
|
| 195 |
-
"""
|
| 196 |
-
An agent interface for proof search using monte carlo tree search
|
| 197 |
-
"""
|
| 198 |
-
|
| 199 |
-
@abstractmethod
|
| 200 |
-
def next_tactic(
|
| 201 |
-
self,
|
| 202 |
-
state: GoalState,
|
| 203 |
-
goal_id: int,
|
| 204 |
-
tested: Optional[List[Tactic]] = None,
|
| 205 |
-
) -> Optional[Tactic]:
|
| 206 |
-
"""
|
| 207 |
-
Implement this function to generate the next tactic for a goal given tactics already tested
|
| 208 |
-
"""
|
| 209 |
-
|
| 210 |
-
@abstractmethod
|
| 211 |
-
def reset(self):
|
| 212 |
-
"""
|
| 213 |
-
Called after search
|
| 214 |
-
"""
|
| 215 |
-
|
| 216 |
-
@abstractmethod
|
| 217 |
-
def estimate(self, state: SearchState) -> SearchState:
|
| 218 |
-
"""
|
| 219 |
-
Implement this function to estimate the value of a state
|
| 220 |
-
"""
|
| 221 |
-
|
| 222 |
-
@abstractmethod
|
| 223 |
-
def select(self, state: SearchState) -> list[SearchState]:
|
| 224 |
-
"""
|
| 225 |
-
Implement this function to select the best node within the subtree of the state.
|
| 226 |
-
Returns the path to the selected node from the given state.
|
| 227 |
-
"""
|
| 228 |
-
|
| 229 |
-
def backup(self, states: list[SearchState], value: float):
|
| 230 |
-
"""
|
| 231 |
-
Backup value of the state at the end of the states list.
|
| 232 |
-
"""
|
| 233 |
-
for state in states:
|
| 234 |
-
state.total_value += value
|
| 235 |
-
state.visit_count += 1
|
| 236 |
-
state.subtree_exhausted = all(child.subtree_exhausted for child in state.children) and state.exhausted
|
| 237 |
-
|
| 238 |
-
def search(self,
|
| 239 |
-
server: Server,
|
| 240 |
-
goal_state: GoalState,
|
| 241 |
-
max_steps: int = 100,
|
| 242 |
-
max_trials_per_goal: int = 5,
|
| 243 |
-
verbose: bool = False) -> SearchResult:
|
| 244 |
-
"""
|
| 245 |
-
Executes proof search on this state
|
| 246 |
-
"""
|
| 247 |
-
|
| 248 |
-
assert server.is_automatic(), "Search must be run in automatic mode"
|
| 249 |
-
|
| 250 |
-
n_goals_root = len(goal_state.goals)
|
| 251 |
-
time_start = time.time()
|
| 252 |
-
|
| 253 |
-
initial_state = SearchState(
|
| 254 |
-
goal_state=goal_state,
|
| 255 |
-
parent=None,
|
| 256 |
-
parent_goal_id=None,
|
| 257 |
-
priorities=[0.0 for _ in goal_state.goals]
|
| 258 |
-
)
|
| 259 |
-
initial_state = self.estimate(initial_state)
|
| 260 |
-
search_root = initial_state
|
| 261 |
-
|
| 262 |
-
for i_step in range(max_steps):
|
| 263 |
-
search_trajectory = self.select(search_root)
|
| 264 |
-
search_state = search_trajectory[-1]
|
| 265 |
-
assert isinstance(search_state, SearchState)
|
| 266 |
-
|
| 267 |
-
if search_state.is_solved:
|
| 268 |
-
return SearchResult(
|
| 269 |
-
n_goals_root=n_goals_root,
|
| 270 |
-
duration=time.time() - time_start,
|
| 271 |
-
success=True,
|
| 272 |
-
steps=i_step,
|
| 273 |
-
)
|
| 274 |
-
|
| 275 |
-
# Find the unsolved goal with the highest priority
|
| 276 |
-
goal_id = search_state.next_goal_id
|
| 277 |
-
|
| 278 |
-
if search_state.trials[goal_id] > max_trials_per_goal:
|
| 279 |
-
# force halt the search
|
| 280 |
-
tactic = None
|
| 281 |
-
else:
|
| 282 |
-
# Generate tactic for this goal
|
| 283 |
-
tactic = self.next_tactic(search_state.goal_state, goal_id, search_state.tested_tactics)
|
| 284 |
-
|
| 285 |
-
if verbose:
|
| 286 |
-
print(f"Next tactic: {tactic}")
|
| 287 |
-
if not tactic:
|
| 288 |
-
# resets the feedback
|
| 289 |
-
search_state.tactic_feedback = None
|
| 290 |
-
search_state.exhausted = True
|
| 291 |
-
search_state.subtree_exhausted = all(child.subtree_exhausted for child in search_state.children)
|
| 292 |
-
continue
|
| 293 |
-
assert tactic not in search_state.tested_tactics, "Tactic already seen!"
|
| 294 |
-
search_state.tested_tactics.append(tactic)
|
| 295 |
-
|
| 296 |
-
try:
|
| 297 |
-
search_state.trials[goal_id] += 1
|
| 298 |
-
state = search_state.goal_state
|
| 299 |
-
if verbose:
|
| 300 |
-
print(f"{state.state_id}.{goal_id}: {tactic} on {search_state.goal_state.goals[goal_id]}")
|
| 301 |
-
next_goal_state = server.goal_tactic(goal_state, tactic, site=Site(goal_id, auto_resume=False))
|
| 302 |
-
# Generate priorities for the next goal state
|
| 303 |
-
priorities = [0.0 for _ in next_goal_state.goals] \
|
| 304 |
-
if len(next_goal_state.goals) <= 1 else \
|
| 305 |
-
self.guidance(next_goal_state)
|
| 306 |
-
parent = -1
|
| 307 |
-
next_state = SearchState(
|
| 308 |
-
goal_state=next_goal_state,
|
| 309 |
-
parent=parent,
|
| 310 |
-
parent_goal_id=goal_id,
|
| 311 |
-
priorities=priorities
|
| 312 |
-
)
|
| 313 |
-
next_state = self.estimate(next_state)
|
| 314 |
-
search_state.children.append(next_state)
|
| 315 |
-
self.backup(search_trajectory, next_state.total_value)
|
| 316 |
-
except TacticFailure as t:
|
| 317 |
-
if verbose:
|
| 318 |
-
print(f"Tactic failed: {t}")
|
| 319 |
-
search_state.tactic_feedback = str(t)
|
| 320 |
-
# try the next tactic. this one failed
|
| 321 |
-
except ServerError as e:
|
| 322 |
-
raise RuntimeError(f"While executing tactic: {tactic}") from e
|
| 323 |
-
|
| 324 |
-
if verbose:
|
| 325 |
-
print("Search iteration limit exhausted")
|
| 326 |
-
|
| 327 |
-
self.reset()
|
| 328 |
-
return SearchResult(
|
| 329 |
-
n_goals_root=n_goals_root,
|
| 330 |
-
duration=time.time() - time_start,
|
| 331 |
-
success=False,
|
| 332 |
-
steps=max_steps,
|
| 333 |
-
)
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
class DumbAgent(Agent):
|
| 337 |
-
|
| 338 |
-
def __init__(self):
|
| 339 |
-
super().__init__()
|
| 340 |
-
|
| 341 |
-
self.goal_tactic_id_map = collections.defaultdict(lambda : 0)
|
| 342 |
-
self.intros = [
|
| 343 |
-
"intro",
|
| 344 |
-
]
|
| 345 |
-
self.tactics = [
|
| 346 |
-
"intro h",
|
| 347 |
-
"cases h",
|
| 348 |
-
"apply Or.inl",
|
| 349 |
-
"apply Or.inr",
|
| 350 |
-
]
|
| 351 |
-
self.no_space_tactics = [
|
| 352 |
-
"assumption",
|
| 353 |
-
]
|
| 354 |
-
|
| 355 |
-
def next_tactic(
|
| 356 |
-
self,
|
| 357 |
-
state: GoalState,
|
| 358 |
-
goal_id: int,
|
| 359 |
-
) -> Optional[Tactic]:
|
| 360 |
-
key = (state.state_id, goal_id)
|
| 361 |
-
i = self.goal_tactic_id_map[key]
|
| 362 |
-
|
| 363 |
-
target = state.goals[goal_id].target
|
| 364 |
-
if target.startswith('β'):
|
| 365 |
-
tactics = self.intros
|
| 366 |
-
elif ' ' in target:
|
| 367 |
-
tactics = self.tactics
|
| 368 |
-
else:
|
| 369 |
-
tactics = self.no_space_tactics
|
| 370 |
-
|
| 371 |
-
if i >= len(tactics):
|
| 372 |
-
return None
|
| 373 |
-
|
| 374 |
-
self.goal_tactic_id_map[key] = i + 1
|
| 375 |
-
return tactics[i]
|
| 376 |
-
|
| 377 |
-
class DumbMCTSAgent(MCTSAgent):
|
| 378 |
-
def __init__(self):
|
| 379 |
-
super().__init__()
|
| 380 |
-
|
| 381 |
-
self.goal_tactic_id_map = collections.defaultdict(lambda : 0)
|
| 382 |
-
self.intros = [
|
| 383 |
-
"intro",
|
| 384 |
-
]
|
| 385 |
-
self.tactics = [
|
| 386 |
-
"intro h",
|
| 387 |
-
"cases h",
|
| 388 |
-
"apply Or.inl",
|
| 389 |
-
"apply Or.inr",
|
| 390 |
-
]
|
| 391 |
-
self.no_space_tactics = [
|
| 392 |
-
"assumption",
|
| 393 |
-
]
|
| 394 |
-
self.c = 0.6
|
| 395 |
-
|
| 396 |
-
def estimate(self, state: SearchState) -> SearchState:
|
| 397 |
-
state.total_value = random.random()
|
| 398 |
-
return state
|
| 399 |
-
|
| 400 |
-
def select(self, state: SearchState) -> list[SearchState]:
|
| 401 |
-
"""
|
| 402 |
-
UCB scoring with taking the current state as one option, i.e. one child
|
| 403 |
-
"""
|
| 404 |
-
state_trajectory = [state]
|
| 405 |
-
current_state = state
|
| 406 |
-
current_state_ucb = (state.total_value / state.visit_count) + self.c * sqrt((log(state.visit_count) / state.visit_count))
|
| 407 |
-
while current_state.children:
|
| 408 |
-
avg_val = [child.total_value / child.visit_count for child in current_state.children]
|
| 409 |
-
visit_portions = [sqrt(log(current_state.visit_count) / child.visit_count) for child in current_state.children]
|
| 410 |
-
ucbs = [avg + self.c * visit for avg, visit in zip(avg_val, visit_portions, strict=True)]
|
| 411 |
-
child_idcs = [idx for idx in range(len(current_state.children)) if not current_state.children[idx].subtree_exhausted]
|
| 412 |
-
if not child_idcs:
|
| 413 |
-
return state_trajectory
|
| 414 |
-
child_idx = child_idcs[0]
|
| 415 |
-
for i in child_idcs:
|
| 416 |
-
if ucbs[i] > ucbs[child_idx]:
|
| 417 |
-
child_idx = i
|
| 418 |
-
if ucbs[child_idx] < current_state_ucb and not current_state.exhausted:
|
| 419 |
-
return state_trajectory
|
| 420 |
-
current_state_ucb = ucbs[child_idx]
|
| 421 |
-
current_state = current_state.children[child_idx]
|
| 422 |
-
state_trajectory.append(current_state)
|
| 423 |
-
return state_trajectory
|
| 424 |
-
|
| 425 |
-
def next_tactic(
|
| 426 |
-
self,
|
| 427 |
-
state: GoalState,
|
| 428 |
-
goal_id: int,
|
| 429 |
-
tested: Optional[List[Tactic]] = None
|
| 430 |
-
) -> Optional[Tactic]:
|
| 431 |
-
key = (state.state_id, goal_id)
|
| 432 |
-
i = self.goal_tactic_id_map[key]
|
| 433 |
-
target = state.goals[goal_id].target
|
| 434 |
-
if target.startswith('β'):
|
| 435 |
-
tactics = self.intros
|
| 436 |
-
elif ' ' in target:
|
| 437 |
-
tactics = self.tactics
|
| 438 |
-
else:
|
| 439 |
-
tactics = self.no_space_tactics
|
| 440 |
-
|
| 441 |
-
if i >= len(tactics):
|
| 442 |
-
return None
|
| 443 |
-
self.goal_tactic_id_map[key] = i + 1
|
| 444 |
-
while tactics[i] in tested:
|
| 445 |
-
i += 1
|
| 446 |
-
if i >= len(tactics):
|
| 447 |
-
return None
|
| 448 |
-
return tactics[i]
|
| 449 |
-
|
| 450 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/pantograph/server.py
DELETED
|
@@ -1,695 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Class which manages a Pantograph instance. All calls to the kernel uses this
|
| 3 |
-
interface.
|
| 4 |
-
"""
|
| 5 |
-
from .message import (
|
| 6 |
-
Position,
|
| 7 |
-
Severity,
|
| 8 |
-
Message,
|
| 9 |
-
TacticFailure,
|
| 10 |
-
ParseError,
|
| 11 |
-
ServerError,
|
| 12 |
-
)
|
| 13 |
-
from .expr import (
|
| 14 |
-
parse_expr,
|
| 15 |
-
Expr,
|
| 16 |
-
Variable,
|
| 17 |
-
Goal,
|
| 18 |
-
GoalState,
|
| 19 |
-
Site,
|
| 20 |
-
Subsumption,
|
| 21 |
-
Tactic,
|
| 22 |
-
TacticHave,
|
| 23 |
-
TacticLet,
|
| 24 |
-
TacticMode,
|
| 25 |
-
TacticExpr,
|
| 26 |
-
TacticDraft,
|
| 27 |
-
)
|
| 28 |
-
from .utils import (
|
| 29 |
-
to_sync,
|
| 30 |
-
_get_proc_cwd,
|
| 31 |
-
_get_proc_path,
|
| 32 |
-
get_lean_path_async,
|
| 33 |
-
get_lean_path,
|
| 34 |
-
)
|
| 35 |
-
from .data import (
|
| 36 |
-
CheckTrackResult,
|
| 37 |
-
CompilationUnit,
|
| 38 |
-
SearchTarget,
|
| 39 |
-
)
|
| 40 |
-
|
| 41 |
-
import json, os, asyncio, tempfile
|
| 42 |
-
from typing import Union, List, Optional, Dict, List, Any, Tuple
|
| 43 |
-
from pathlib import Path
|
| 44 |
-
|
| 45 |
-
class Server:
|
| 46 |
-
"""
|
| 47 |
-
Main interaction instance with Pantograph.
|
| 48 |
-
|
| 49 |
-
Asynchronous and synchronous versions are provided for each function.
|
| 50 |
-
"""
|
| 51 |
-
|
| 52 |
-
def __init__(
|
| 53 |
-
self,
|
| 54 |
-
imports: List[str]=["Init"],
|
| 55 |
-
project_path: Optional[str]=None,
|
| 56 |
-
lean_path: Optional[str]=None,
|
| 57 |
-
# Options for executing the REPL.
|
| 58 |
-
options: Dict[str, Any]={},
|
| 59 |
-
# Options supplied to the Lean core
|
| 60 |
-
core_options: List[str]=[],
|
| 61 |
-
timeout: int=60,
|
| 62 |
-
buffer_limit: Optional[int]=1000000,
|
| 63 |
-
_sync_init: bool=True):
|
| 64 |
-
"""
|
| 65 |
-
options: Given to Pantograph
|
| 66 |
-
core_options: Given to Lean core
|
| 67 |
-
timeout: Amount of time to wait for execution (in seconds)
|
| 68 |
-
maxread: Maximum number of characters to read (especially important for large proofs and catalogs)
|
| 69 |
-
"""
|
| 70 |
-
self.timeout = timeout
|
| 71 |
-
self.imports = imports
|
| 72 |
-
self.project_path = project_path if project_path else _get_proc_cwd()
|
| 73 |
-
if _sync_init and project_path and not lean_path:
|
| 74 |
-
lean_path = get_lean_path(project_path)
|
| 75 |
-
self.lean_path = lean_path
|
| 76 |
-
self.buffer_limit = buffer_limit
|
| 77 |
-
self.proc_path = _get_proc_path()
|
| 78 |
-
|
| 79 |
-
self.options = options
|
| 80 |
-
self.core_options = core_options
|
| 81 |
-
self.args = imports + [f'--{opt}' for opt in core_options]
|
| 82 |
-
self.proc = None
|
| 83 |
-
if _sync_init:
|
| 84 |
-
self.restart()
|
| 85 |
-
|
| 86 |
-
# List of goal states that should be garbage collected
|
| 87 |
-
self.to_remove_goal_states = []
|
| 88 |
-
|
| 89 |
-
@classmethod
|
| 90 |
-
async def create(
|
| 91 |
-
cls,
|
| 92 |
-
imports: List[str]=["Init"],
|
| 93 |
-
project_path: Optional[str]=None,
|
| 94 |
-
lean_path: Optional[str]=None,
|
| 95 |
-
# Options for executing the REPL.
|
| 96 |
-
# Set `{ "automaticMode" : False }` to handle resumption by yourself.
|
| 97 |
-
options: Dict[str, Any]={},
|
| 98 |
-
core_options: List[str]=[],
|
| 99 |
-
timeout: int=120,
|
| 100 |
-
buffer_limit: Optional[int]=1000000,
|
| 101 |
-
start: bool=True) -> 'Server':
|
| 102 |
-
"""
|
| 103 |
-
timeout: Amount of time to wait for execution (in seconds)
|
| 104 |
-
maxread: Maximum number of characters to read (especially important for large proofs and catalogs)
|
| 105 |
-
"""
|
| 106 |
-
self = cls(
|
| 107 |
-
imports,
|
| 108 |
-
project_path,
|
| 109 |
-
lean_path,
|
| 110 |
-
options,
|
| 111 |
-
core_options,
|
| 112 |
-
timeout,
|
| 113 |
-
buffer_limit,
|
| 114 |
-
_sync_init=False
|
| 115 |
-
)
|
| 116 |
-
if project_path and not lean_path:
|
| 117 |
-
lean_path = await get_lean_path_async(project_path)
|
| 118 |
-
self.lean_path = lean_path
|
| 119 |
-
if start:
|
| 120 |
-
await self.restart_async()
|
| 121 |
-
return self
|
| 122 |
-
|
| 123 |
-
def __enter__(self) -> "Server":
|
| 124 |
-
return self
|
| 125 |
-
|
| 126 |
-
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
| 127 |
-
self._close()
|
| 128 |
-
|
| 129 |
-
def __del__(self):
|
| 130 |
-
pass #self._close()
|
| 131 |
-
|
| 132 |
-
def _close(self):
|
| 133 |
-
if not self.proc:
|
| 134 |
-
return
|
| 135 |
-
|
| 136 |
-
self.proc.terminate()
|
| 137 |
-
self.proc = None
|
| 138 |
-
|
| 139 |
-
def is_automatic(self):
|
| 140 |
-
"""
|
| 141 |
-
Check if the server is running in automatic mode
|
| 142 |
-
"""
|
| 143 |
-
return self.options.get("automaticMode", True)
|
| 144 |
-
|
| 145 |
-
async def restart_async(self):
|
| 146 |
-
"""
|
| 147 |
-
Restart the server
|
| 148 |
-
"""
|
| 149 |
-
self._close()
|
| 150 |
-
env = os.environ
|
| 151 |
-
if self.lean_path:
|
| 152 |
-
env = env | {'LEAN_PATH': self.lean_path}
|
| 153 |
-
|
| 154 |
-
self.proc = await asyncio.create_subprocess_exec(
|
| 155 |
-
self.proc_path,
|
| 156 |
-
*self.args,
|
| 157 |
-
stdin=asyncio.subprocess.PIPE,
|
| 158 |
-
stdout=asyncio.subprocess.PIPE,
|
| 159 |
-
#stderr=asyncio.subprocess.PIPE,
|
| 160 |
-
cwd=self.project_path,
|
| 161 |
-
env=env,
|
| 162 |
-
limit=self.buffer_limit,
|
| 163 |
-
)
|
| 164 |
-
await self.proc.stdin.drain()
|
| 165 |
-
try:
|
| 166 |
-
ready = await asyncio.wait_for(self.proc.stdout.readline(), self.timeout)
|
| 167 |
-
ready = ready.decode().strip()
|
| 168 |
-
assert ready == "ready.", f"Server failed to emit ready signal: {ready}; This could be caused by Lean version mismatch between the project and Pantograph or insufficient timeout."
|
| 169 |
-
except asyncio.TimeoutError as ex:
|
| 170 |
-
raise RuntimeError("Server failed to emit ready signal in time") from ex
|
| 171 |
-
|
| 172 |
-
if self.options:
|
| 173 |
-
await self.run_async("options.set", self.options)
|
| 174 |
-
|
| 175 |
-
restart = to_sync(restart_async)
|
| 176 |
-
|
| 177 |
-
async def run_async(self, cmd, payload):
|
| 178 |
-
"""
|
| 179 |
-
Runs a raw JSON command. Preferably use one of the commands below.
|
| 180 |
-
|
| 181 |
-
:meta private:
|
| 182 |
-
"""
|
| 183 |
-
assert self.proc, "Server not running."
|
| 184 |
-
|
| 185 |
-
s = json.dumps(payload, ensure_ascii=False)
|
| 186 |
-
command = f"{cmd} {s}\n"
|
| 187 |
-
self.proc.stdin.write(command.encode())
|
| 188 |
-
await self.proc.stdin.drain()
|
| 189 |
-
line = ""
|
| 190 |
-
try:
|
| 191 |
-
line = await asyncio.wait_for(self.proc.stdout.readline(), self.timeout)
|
| 192 |
-
except asyncio.TimeoutError as e:
|
| 193 |
-
self._close()
|
| 194 |
-
raise ServerError("Server reached timeout limit") from e
|
| 195 |
-
|
| 196 |
-
try:
|
| 197 |
-
line = line.decode().strip()
|
| 198 |
-
return json.loads(line)
|
| 199 |
-
except UnicodeDecodeError as e:
|
| 200 |
-
self._close()
|
| 201 |
-
raise ServerError(f"Could not decode process output: {line}") from e
|
| 202 |
-
except json.JSONDecodeError as e:
|
| 203 |
-
self._close()
|
| 204 |
-
raise ServerError(f"Cannot decode Json object from: {line}") from e
|
| 205 |
-
|
| 206 |
-
run = to_sync(run_async)
|
| 207 |
-
|
| 208 |
-
async def gc_async(self):
|
| 209 |
-
"""
|
| 210 |
-
Garbage collect deleted goal states to free up memory.
|
| 211 |
-
"""
|
| 212 |
-
if not self.to_remove_goal_states:
|
| 213 |
-
return
|
| 214 |
-
result = await self.run_async('goal.delete', {'stateIds': self.to_remove_goal_states})
|
| 215 |
-
self.to_remove_goal_states.clear()
|
| 216 |
-
if "error" in result:
|
| 217 |
-
raise ServerError(result)
|
| 218 |
-
|
| 219 |
-
gc = to_sync(gc_async)
|
| 220 |
-
|
| 221 |
-
async def expr_type_async(self, expr: Expr) -> Expr:
|
| 222 |
-
"""
|
| 223 |
-
Evaluate the type of a given expression. This gives an error if the
|
| 224 |
-
input `expr` is ill-formed.
|
| 225 |
-
"""
|
| 226 |
-
result = await self.run_async('expr.echo', {"expr": expr})
|
| 227 |
-
if "error" in result:
|
| 228 |
-
raise ServerError(result)
|
| 229 |
-
return parse_expr(result["type"])
|
| 230 |
-
|
| 231 |
-
expr_type = to_sync(expr_type_async)
|
| 232 |
-
|
| 233 |
-
async def goal_start_async(self, expr: Expr) -> GoalState:
|
| 234 |
-
"""
|
| 235 |
-
Create a goal state with one root goal, whose target is `expr`
|
| 236 |
-
"""
|
| 237 |
-
result = await self.run_async('goal.start', {"expr": str(expr)})
|
| 238 |
-
if "error" in result:
|
| 239 |
-
print(f"Cannot start goal: {expr}")
|
| 240 |
-
raise ServerError(result)
|
| 241 |
-
return GoalState(
|
| 242 |
-
state_id=result["stateId"],
|
| 243 |
-
goals=[Goal.sentence(result["root"], expr)],
|
| 244 |
-
messages=[],
|
| 245 |
-
_sentinel=self.to_remove_goal_states,
|
| 246 |
-
)
|
| 247 |
-
|
| 248 |
-
goal_start = to_sync(goal_start_async)
|
| 249 |
-
|
| 250 |
-
async def goal_root_async(self, state: GoalState) -> Optional[Expr]:
|
| 251 |
-
"""
|
| 252 |
-
Print the root expression of a goal state
|
| 253 |
-
"""
|
| 254 |
-
args = {"stateId": state.state_id, "rootExpr": True}
|
| 255 |
-
result = await self.run_async('goal.print', args)
|
| 256 |
-
if "error" in result:
|
| 257 |
-
raise ServerError(result)
|
| 258 |
-
root = result.get('root')
|
| 259 |
-
if root is None:
|
| 260 |
-
return None
|
| 261 |
-
return parse_expr(root)
|
| 262 |
-
|
| 263 |
-
goal_root = to_sync(goal_root_async)
|
| 264 |
-
|
| 265 |
-
async def goal_tactic_async(self, state: GoalState, tactic: Tactic, site: Site = Site()) -> GoalState:
|
| 266 |
-
"""
|
| 267 |
-
Execute a tactic on `goal_id` of `state`
|
| 268 |
-
"""
|
| 269 |
-
args = {"stateId": state.state_id, **site.serial()}
|
| 270 |
-
match tactic:
|
| 271 |
-
case str():
|
| 272 |
-
args["tactic"] = tactic
|
| 273 |
-
case TacticHave():
|
| 274 |
-
args["have"] = tactic.branch
|
| 275 |
-
if tactic.binder_name:
|
| 276 |
-
args["binderName"] = tactic.binder_name
|
| 277 |
-
case TacticLet():
|
| 278 |
-
args["let"] = tactic.branch
|
| 279 |
-
if tactic.binder_name:
|
| 280 |
-
args["binderName"] = tactic.binder_name
|
| 281 |
-
case TacticExpr():
|
| 282 |
-
args["expr"] = tactic.expr
|
| 283 |
-
case TacticDraft():
|
| 284 |
-
args["draft"] = tactic.expr
|
| 285 |
-
case TacticMode():
|
| 286 |
-
args["mode"] = tactic.serial()
|
| 287 |
-
case _:
|
| 288 |
-
raise RuntimeError(f"Invalid tactic type: {type(tactic)}")
|
| 289 |
-
result = await self.run_async('goal.tactic', args)
|
| 290 |
-
next_state_id = result.get("nextStateId")
|
| 291 |
-
if "error" in result:
|
| 292 |
-
raise ServerError(result)
|
| 293 |
-
if "parseError" in result:
|
| 294 |
-
raise TacticFailure(result)
|
| 295 |
-
|
| 296 |
-
messages = result.get("messages")
|
| 297 |
-
if "goals" not in result:
|
| 298 |
-
raise TacticFailure([Message.parse(m) for m in messages])
|
| 299 |
-
|
| 300 |
-
if result["hasSorry"]:
|
| 301 |
-
await self.run_async('goal.delete', {'stateIds': [next_state_id]})
|
| 302 |
-
raise TacticFailure("Tactic generated sorry", messages)
|
| 303 |
-
if result["hasUnsafe"]:
|
| 304 |
-
await self.run_async('goal.delete', {'stateIds': [next_state_id]})
|
| 305 |
-
raise TacticFailure("Tactic generated unsafe", messages)
|
| 306 |
-
|
| 307 |
-
return GoalState.parse(result, messages, self.to_remove_goal_states)
|
| 308 |
-
|
| 309 |
-
goal_tactic = to_sync(goal_tactic_async)
|
| 310 |
-
|
| 311 |
-
async def goal_continue_async(self, target: GoalState, branch: GoalState) -> GoalState:
|
| 312 |
-
"""
|
| 313 |
-
After finish searching `target`, resume search on `branch`
|
| 314 |
-
"""
|
| 315 |
-
result = await self.run_async('goal.continue', {
|
| 316 |
-
"target": target.state_id,
|
| 317 |
-
"branch": branch.state_id,
|
| 318 |
-
})
|
| 319 |
-
if "error" in result:
|
| 320 |
-
raise ServerError(result)
|
| 321 |
-
if "tacticErrors" in result:
|
| 322 |
-
raise ServerError(result)
|
| 323 |
-
if "parseError" in result:
|
| 324 |
-
raise ServerError(result)
|
| 325 |
-
return GoalState.parse(result, [], self.to_remove_goal_states)
|
| 326 |
-
|
| 327 |
-
goal_continue = to_sync(goal_continue_async)
|
| 328 |
-
|
| 329 |
-
async def goal_resume_async(self, state: GoalState, goals: list[Goal]) -> GoalState:
|
| 330 |
-
"""
|
| 331 |
-
Bring `goals` back into scope
|
| 332 |
-
"""
|
| 333 |
-
result = await self.run_async('goal.continue', {
|
| 334 |
-
"target": state.state_id,
|
| 335 |
-
"goals": [goal.name for goal in goals],
|
| 336 |
-
})
|
| 337 |
-
if "error" in result:
|
| 338 |
-
raise ServerError(result)
|
| 339 |
-
if "tacticErrors" in result:
|
| 340 |
-
raise ServerError(result)
|
| 341 |
-
if "parseError" in result:
|
| 342 |
-
raise ServerError(result)
|
| 343 |
-
return GoalState.parse(result, [], self.to_remove_goal_states)
|
| 344 |
-
goal_resume = to_sync(goal_resume_async)
|
| 345 |
-
|
| 346 |
-
async def goal_subsume_async(
|
| 347 |
-
self,
|
| 348 |
-
state: GoalState,
|
| 349 |
-
goal: Goal,
|
| 350 |
-
candidates: list[Goal],
|
| 351 |
-
src_state: Optional[GoalState]=None
|
| 352 |
-
) -> (Subsumption, Optional[GoalState], Optional[Goal]):
|
| 353 |
-
"""
|
| 354 |
-
Detect subsumption by candidate goals
|
| 355 |
-
|
| 356 |
-
The candidate goals must all exist in `src_state`. If `src_state` is not
|
| 357 |
-
provided, they must exist in `state`. Returns a new goal state if a
|
| 358 |
-
subsumption does not lead to a cycle, and the subsumptor if there is any
|
| 359 |
-
subsumption happening.
|
| 360 |
-
"""
|
| 361 |
-
args = {
|
| 362 |
-
"stateId": state.state_id,
|
| 363 |
-
"goal": goal.id,
|
| 364 |
-
"candidates": [g.id for g in candidates],
|
| 365 |
-
}
|
| 366 |
-
if src_state:
|
| 367 |
-
args["srcStateId"] = src_state.state_id
|
| 368 |
-
result = await self.run_async('goal.subsume', args)
|
| 369 |
-
if "error" in result:
|
| 370 |
-
raise ServerError(result)
|
| 371 |
-
nextState = None
|
| 372 |
-
if state_id := result.get("stateId"):
|
| 373 |
-
nextState = GoalState(
|
| 374 |
-
state_id=state_id,
|
| 375 |
-
goals=[g for g in state.goals if g.id != goal.id],
|
| 376 |
-
messages=[],
|
| 377 |
-
_sentinel=self.to_remove_goal_states,
|
| 378 |
-
)
|
| 379 |
-
sub = Subsumption[result["result"].upper()]
|
| 380 |
-
subsumptor = None
|
| 381 |
-
if subsumptor := result.get("subsumptor"):
|
| 382 |
-
gen = (g for g in candidates if g.id == subsumptor)
|
| 383 |
-
subsumptor = next(gen)
|
| 384 |
-
if subsumptor is None:
|
| 385 |
-
raise ServerError("Subsumptor should not be none")
|
| 386 |
-
return (sub, nextState, subsumptor)
|
| 387 |
-
|
| 388 |
-
goal_subsume = to_sync(goal_subsume_async)
|
| 389 |
-
|
| 390 |
-
async def env_add_async(
|
| 391 |
-
self, name: str, levels: list[str],
|
| 392 |
-
t: Expr, v: Expr, is_theorem: bool = True):
|
| 393 |
-
"""
|
| 394 |
-
Adds a definition to the environment.
|
| 395 |
-
|
| 396 |
-
NOTE: May have to accept additional parameters if the definition
|
| 397 |
-
contains universe mvars.
|
| 398 |
-
"""
|
| 399 |
-
result = await self.run_async('env.add', {
|
| 400 |
-
"name": name,
|
| 401 |
-
"levels": levels,
|
| 402 |
-
"type": t,
|
| 403 |
-
"value": v,
|
| 404 |
-
"isTheorem": is_theorem,
|
| 405 |
-
"typeErrorsAsGoals": False,
|
| 406 |
-
})
|
| 407 |
-
if "error" in result:
|
| 408 |
-
raise ServerError(result["desc"])
|
| 409 |
-
|
| 410 |
-
env_add = to_sync(env_add_async)
|
| 411 |
-
|
| 412 |
-
async def env_catalog_async(
|
| 413 |
-
self,
|
| 414 |
-
module_prefix: str | None = None,
|
| 415 |
-
invert_filter: bool = False
|
| 416 |
-
) -> list[str]:
|
| 417 |
-
"""
|
| 418 |
-
Print all symbols in environment.
|
| 419 |
-
"""
|
| 420 |
-
with tempfile.NamedTemporaryFile('r') as tmp_file:
|
| 421 |
-
result = await self.run_async('env.catalog', {
|
| 422 |
-
'filename': tmp_file.name,
|
| 423 |
-
'modulePrefix': module_prefix,
|
| 424 |
-
'invertFilter': invert_filter,
|
| 425 |
-
})
|
| 426 |
-
if "error" in result:
|
| 427 |
-
raise ServerError(result["desc"])
|
| 428 |
-
return [line.strip() for line in tmp_file.readlines()]
|
| 429 |
-
env_catalog = to_sync(env_catalog_async)
|
| 430 |
-
|
| 431 |
-
async def env_inspect_async(
|
| 432 |
-
self,
|
| 433 |
-
name: str,
|
| 434 |
-
print_value: bool = False,
|
| 435 |
-
print_dependency: bool = False) -> Dict:
|
| 436 |
-
"""
|
| 437 |
-
Print the type and dependencies of a constant.
|
| 438 |
-
"""
|
| 439 |
-
result = await self.run_async('env.inspect', {
|
| 440 |
-
"name": name,
|
| 441 |
-
"value": print_value,
|
| 442 |
-
"dependency": print_dependency,
|
| 443 |
-
"source": True,
|
| 444 |
-
})
|
| 445 |
-
if "error" in result:
|
| 446 |
-
raise ServerError(result["desc"])
|
| 447 |
-
return result
|
| 448 |
-
env_inspect = to_sync(env_inspect_async)
|
| 449 |
-
|
| 450 |
-
async def env_module_read_async(self, module: str) -> dict:
|
| 451 |
-
"""
|
| 452 |
-
Reads the content from one Lean module including what constants are in
|
| 453 |
-
it.
|
| 454 |
-
"""
|
| 455 |
-
result = await self.run_async('env.module_read', {
|
| 456 |
-
"module": module
|
| 457 |
-
})
|
| 458 |
-
if "error" in result:
|
| 459 |
-
raise ServerError(result["desc"])
|
| 460 |
-
return result
|
| 461 |
-
env_module_read = to_sync(env_module_read_async)
|
| 462 |
-
|
| 463 |
-
async def env_parse_async(self, src: str, category: str = "tactic") -> Tuple[str, str]:
|
| 464 |
-
"""
|
| 465 |
-
Parse an input using a syntax category's parser. Returns the parsed
|
| 466 |
-
component and the tail.
|
| 467 |
-
"""
|
| 468 |
-
result = await self.run_async('env.parse', {
|
| 469 |
-
"input": src,
|
| 470 |
-
"category": category,
|
| 471 |
-
})
|
| 472 |
-
if "error" in result:
|
| 473 |
-
if result['error'] == 'parse':
|
| 474 |
-
raise ParseError(result["desc"])
|
| 475 |
-
raise ServerError(result["desc"])
|
| 476 |
-
pos = result["pos"]
|
| 477 |
-
s = src.encode()
|
| 478 |
-
return s[:pos].decode(), s[pos:].decode()
|
| 479 |
-
|
| 480 |
-
env_parse = to_sync(env_parse_async)
|
| 481 |
-
|
| 482 |
-
async def env_save_async(self, path: str):
|
| 483 |
-
"""
|
| 484 |
-
Save the current environment to a file
|
| 485 |
-
"""
|
| 486 |
-
result = await self.run_async('env.save', {
|
| 487 |
-
"path": os.path.abspath(path),
|
| 488 |
-
})
|
| 489 |
-
if "error" in result:
|
| 490 |
-
raise ServerError(result["desc"])
|
| 491 |
-
env_save = to_sync(env_save_async)
|
| 492 |
-
|
| 493 |
-
async def env_load_async(self, path: str):
|
| 494 |
-
"""
|
| 495 |
-
Load the current environment from a file
|
| 496 |
-
"""
|
| 497 |
-
result = await self.run_async('env.load', {
|
| 498 |
-
"path": os.path.abspath(path),
|
| 499 |
-
})
|
| 500 |
-
if "error" in result:
|
| 501 |
-
raise ServerError(result["desc"])
|
| 502 |
-
|
| 503 |
-
env_load = to_sync(env_load_async)
|
| 504 |
-
|
| 505 |
-
async def goal_save_async(self, goal_state: GoalState, path: str):
|
| 506 |
-
"""
|
| 507 |
-
Save a goal state to a file
|
| 508 |
-
"""
|
| 509 |
-
result = await self.run_async('goal.save', {
|
| 510 |
-
"id": goal_state.state_id,
|
| 511 |
-
"path": os.path.abspath(path),
|
| 512 |
-
})
|
| 513 |
-
if "error" in result:
|
| 514 |
-
raise ServerError(result["desc"])
|
| 515 |
-
|
| 516 |
-
goal_save = to_sync(goal_save_async)
|
| 517 |
-
|
| 518 |
-
async def goal_load_async(self, path: str) -> GoalState:
|
| 519 |
-
"""
|
| 520 |
-
Load a goal state from a file.
|
| 521 |
-
|
| 522 |
-
User is responsible for keeping track of the environment.
|
| 523 |
-
"""
|
| 524 |
-
result = await self.run_async('goal.load', {
|
| 525 |
-
"path": os.path.abspath(path),
|
| 526 |
-
})
|
| 527 |
-
if "error" in result:
|
| 528 |
-
raise ServerError(result["desc"])
|
| 529 |
-
state_id = result['id']
|
| 530 |
-
result = await self.run_async('goal.print', {
|
| 531 |
-
'stateId': state_id,
|
| 532 |
-
'goals': True,
|
| 533 |
-
})
|
| 534 |
-
if "error" in result:
|
| 535 |
-
raise ServerError(result["desc"])
|
| 536 |
-
return GoalState.parse_inner(
|
| 537 |
-
state_id,
|
| 538 |
-
result['goals'], [],
|
| 539 |
-
self.to_remove_goal_states,
|
| 540 |
-
)
|
| 541 |
-
|
| 542 |
-
goal_load = to_sync(goal_load_async)
|
| 543 |
-
|
| 544 |
-
async def tactic_invocations_async(self, file_name: Union[str, Path]) -> list[CompilationUnit]:
|
| 545 |
-
"""
|
| 546 |
-
Collect tactic invocation points in file, and return them.
|
| 547 |
-
"""
|
| 548 |
-
with tempfile.TemporaryDirectory() as tempdirname:
|
| 549 |
-
invocation_file_name = f"{tempdirname}/invocations.json"
|
| 550 |
-
result = await self.run_async('frontend.process', {
|
| 551 |
-
'fileName': str(file_name),
|
| 552 |
-
'invocations': invocation_file_name,
|
| 553 |
-
"readHeader": True,
|
| 554 |
-
"inheritEnv": False,
|
| 555 |
-
"newConstants": False,
|
| 556 |
-
})
|
| 557 |
-
if "error" in result:
|
| 558 |
-
raise ServerError(result)
|
| 559 |
-
|
| 560 |
-
with open(invocation_file_name, "r") as f:
|
| 561 |
-
data_units = json.load(f)
|
| 562 |
-
units = [
|
| 563 |
-
CompilationUnit.parse(payload, invocations=data_unit["invocations"])
|
| 564 |
-
for payload, data_unit in zip(result['units'], data_units['units'])
|
| 565 |
-
]
|
| 566 |
-
return units
|
| 567 |
-
|
| 568 |
-
tactic_invocations = to_sync(tactic_invocations_async)
|
| 569 |
-
|
| 570 |
-
async def load_header_async(self, header: str):
|
| 571 |
-
"""
|
| 572 |
-
Loads the environment from a header. Set `imports` to `[]` during
|
| 573 |
-
server creation to use this function.
|
| 574 |
-
"""
|
| 575 |
-
result = await self.run_async('frontend.process', {
|
| 576 |
-
'file': header,
|
| 577 |
-
"newConstants": False,
|
| 578 |
-
"readHeader": True,
|
| 579 |
-
"inheritEnv": True,
|
| 580 |
-
})
|
| 581 |
-
if "error" in result:
|
| 582 |
-
raise ServerError(result)
|
| 583 |
-
|
| 584 |
-
load_header = to_sync(load_header_async)
|
| 585 |
-
|
| 586 |
-
async def load_definitions_async(self, snippet: str):
|
| 587 |
-
"""
|
| 588 |
-
Loads definitions in some Lean code and update the environment.
|
| 589 |
-
|
| 590 |
-
Existing goal states will not automatically inherit said definitions.
|
| 591 |
-
"""
|
| 592 |
-
result = await self.run_async('frontend.process', {
|
| 593 |
-
'file': snippet,
|
| 594 |
-
"newConstants": False,
|
| 595 |
-
"readHeader": False,
|
| 596 |
-
"inheritEnv": True,
|
| 597 |
-
})
|
| 598 |
-
if "error" in result:
|
| 599 |
-
raise ServerError(result)
|
| 600 |
-
|
| 601 |
-
load_definitions = to_sync(load_definitions_async)
|
| 602 |
-
|
| 603 |
-
async def check_compile_async(
|
| 604 |
-
self,
|
| 605 |
-
code: str,
|
| 606 |
-
new_constants: bool = False,
|
| 607 |
-
read_header: bool = False):
|
| 608 |
-
"""
|
| 609 |
-
Check if some Lean code compiles
|
| 610 |
-
"""
|
| 611 |
-
result = await self.run_async('frontend.process', {
|
| 612 |
-
'file': code,
|
| 613 |
-
"newConstants": new_constants,
|
| 614 |
-
"readHeader": read_header,
|
| 615 |
-
"inheritEnv": False,
|
| 616 |
-
})
|
| 617 |
-
if "error" in result:
|
| 618 |
-
raise ServerError(result)
|
| 619 |
-
units = [
|
| 620 |
-
CompilationUnit.parse(payload, goal_state_sentinel=self.to_remove_goal_states)
|
| 621 |
-
for payload in result['units']
|
| 622 |
-
]
|
| 623 |
-
return units
|
| 624 |
-
|
| 625 |
-
check_compile = to_sync(check_compile_async)
|
| 626 |
-
|
| 627 |
-
async def load_sorry_async(
|
| 628 |
-
self,
|
| 629 |
-
src: str,
|
| 630 |
-
binder_name: Optional[str] = None,
|
| 631 |
-
ignore_values: bool = False) -> list[SearchTarget]:
|
| 632 |
-
"""
|
| 633 |
-
Condense search target into goals
|
| 634 |
-
"""
|
| 635 |
-
args = {"file": src, "ignoreValues": ignore_values}
|
| 636 |
-
if binder_name is not None:
|
| 637 |
-
args["binderName"] = binder_name
|
| 638 |
-
result = await self.run_async('frontend.distil', args)
|
| 639 |
-
if "error" in result:
|
| 640 |
-
raise ServerError(result)
|
| 641 |
-
units = [
|
| 642 |
-
SearchTarget.parse(payload, goal_state_sentinel=self.to_remove_goal_states)
|
| 643 |
-
for payload in result['targets']
|
| 644 |
-
]
|
| 645 |
-
return units
|
| 646 |
-
|
| 647 |
-
load_sorry = to_sync(load_sorry_async)
|
| 648 |
-
|
| 649 |
-
async def check_track_async(self, src: str, dst: str) -> CheckTrackResult:
|
| 650 |
-
"""
|
| 651 |
-
Checks if `dst` file conforms to the specifications in `src`
|
| 652 |
-
"""
|
| 653 |
-
result = await self.run_async('frontend.track', {"src": src, "dst": dst})
|
| 654 |
-
if "error" in result:
|
| 655 |
-
raise ServerError(result)
|
| 656 |
-
src_messages = [Message.parse(d) for d in result["srcMessages"]]
|
| 657 |
-
dst_messages = [Message.parse(d) for d in result["dstMessages"]]
|
| 658 |
-
return CheckTrackResult(
|
| 659 |
-
src_messages,
|
| 660 |
-
dst_messages,
|
| 661 |
-
failure=result.get("failure"),
|
| 662 |
-
)
|
| 663 |
-
|
| 664 |
-
check_track = to_sync(check_track_async)
|
| 665 |
-
|
| 666 |
-
async def refactor_search_target_async(
|
| 667 |
-
self,
|
| 668 |
-
code: str,
|
| 669 |
-
core_options: list[str] = []) -> str:
|
| 670 |
-
"""
|
| 671 |
-
Combine multiple `sorry`s into one `sorry` using subtyping. It only
|
| 672 |
-
supports flat dependency structures.
|
| 673 |
-
|
| 674 |
-
This feature is experimental and depends on the round-trip capabilities
|
| 675 |
-
of the delaborator.
|
| 676 |
-
"""
|
| 677 |
-
result = await self.run_async('frontend.refactor', {
|
| 678 |
-
'file': code,
|
| 679 |
-
'coreOptions': core_options,
|
| 680 |
-
})
|
| 681 |
-
if "error" in result:
|
| 682 |
-
raise ServerError(result)
|
| 683 |
-
return result["file"]
|
| 684 |
-
|
| 685 |
-
refactor_search_target = to_sync(refactor_search_target_async)
|
| 686 |
-
|
| 687 |
-
def get_version() -> str:
|
| 688 |
-
"""
|
| 689 |
-
Returns the current Pantograph version for diagnostics purposes.
|
| 690 |
-
"""
|
| 691 |
-
import subprocess
|
| 692 |
-
with subprocess.Popen([_get_proc_path(), "--version"],
|
| 693 |
-
stdout=subprocess.PIPE,
|
| 694 |
-
cwd=_get_proc_cwd()) as p:
|
| 695 |
-
return p.communicate()[0].decode('utf-8').strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/pantograph/test_message.py
DELETED
|
@@ -1,25 +0,0 @@
|
|
| 1 |
-
from .message import *
|
| 2 |
-
import unittest
|
| 3 |
-
|
| 4 |
-
class TestMessage(unittest.TestCase):
|
| 5 |
-
|
| 6 |
-
def test_severity(self):
|
| 7 |
-
self.assertEqual(str(Severity.ERROR), "error")
|
| 8 |
-
def test_message(self):
|
| 9 |
-
m = Message(
|
| 10 |
-
data="hi",
|
| 11 |
-
pos=Position(12, 34),
|
| 12 |
-
pos_end=Position(56, 78),
|
| 13 |
-
severity=Severity.WARNING,
|
| 14 |
-
)
|
| 15 |
-
self.assertEqual(str(m), "12:34-56:78: warning: hi")
|
| 16 |
-
|
| 17 |
-
m = Message(
|
| 18 |
-
data="hi",
|
| 19 |
-
pos=Position(0, 0),
|
| 20 |
-
severity=Severity.ERROR,
|
| 21 |
-
)
|
| 22 |
-
self.assertEqual(str(m), "0:0: error: hi")
|
| 23 |
-
|
| 24 |
-
if __name__ == '__main__':
|
| 25 |
-
unittest.main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/pantograph/test_search.py
DELETED
|
@@ -1,55 +0,0 @@
|
|
| 1 |
-
from .search import *
|
| 2 |
-
import unittest
|
| 3 |
-
|
| 4 |
-
class TestSearch(unittest.TestCase):
|
| 5 |
-
|
| 6 |
-
def test_solve(self):
|
| 7 |
-
|
| 8 |
-
server = Server()
|
| 9 |
-
agent = DumbAgent()
|
| 10 |
-
goal_state = server.goal_start("β (p q: Prop), p -> p")
|
| 11 |
-
flag = agent.search(
|
| 12 |
-
server=server,
|
| 13 |
-
goal_state=goal_state,
|
| 14 |
-
verbose=False)
|
| 15 |
-
#flag = agent.search(server=server, target="β (p q: Prop), Or p q -> Or q p", verbose=True)
|
| 16 |
-
self.assertTrue(flag)
|
| 17 |
-
def test_solve_big(self):
|
| 18 |
-
|
| 19 |
-
server = Server()
|
| 20 |
-
agent = DumbAgent()
|
| 21 |
-
goal_state = server.goal_start("β (p q: Prop), Or p q -> Or q p")
|
| 22 |
-
flag = agent.search(
|
| 23 |
-
server=server,
|
| 24 |
-
goal_state=goal_state,
|
| 25 |
-
verbose=False)
|
| 26 |
-
self.assertTrue(flag)
|
| 27 |
-
|
| 28 |
-
class TestMCTSSearch(unittest.TestCase):
|
| 29 |
-
|
| 30 |
-
def test_solve(self):
|
| 31 |
-
|
| 32 |
-
server = Server()
|
| 33 |
-
agent = DumbMCTSAgent()
|
| 34 |
-
goal_state = server.goal_start("β (p q: Prop), p -> p")
|
| 35 |
-
flag = agent.search(
|
| 36 |
-
server=server,
|
| 37 |
-
goal_state=goal_state,
|
| 38 |
-
verbose=False)
|
| 39 |
-
#flag = agent.search(server=server, target="β (p q: Prop), Or p q -> Or q p", verbose=True)
|
| 40 |
-
self.assertTrue(flag)
|
| 41 |
-
def test_solve_big(self):
|
| 42 |
-
|
| 43 |
-
server = Server()
|
| 44 |
-
agent = DumbMCTSAgent()
|
| 45 |
-
goal_state = server.goal_start("β (p q: Prop), Or p q -> Or q p")
|
| 46 |
-
flag = agent.search(
|
| 47 |
-
server=server,
|
| 48 |
-
goal_state=goal_state,
|
| 49 |
-
max_steps=200,
|
| 50 |
-
verbose=False)
|
| 51 |
-
self.assertTrue(flag)
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
if __name__ == '__main__':
|
| 55 |
-
unittest.main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/pantograph/test_server.py
DELETED
|
@@ -1,381 +0,0 @@
|
|
| 1 |
-
from .server import *
|
| 2 |
-
import unittest
|
| 3 |
-
|
| 4 |
-
class TestServer(unittest.TestCase):
|
| 5 |
-
|
| 6 |
-
def test_version(self):
|
| 7 |
-
"""
|
| 8 |
-
NOTE: Update this after upstream updates.
|
| 9 |
-
"""
|
| 10 |
-
self.assertEqual(get_version(), "0.3.10")
|
| 11 |
-
|
| 12 |
-
def test_server_init_del(self):
|
| 13 |
-
import warnings
|
| 14 |
-
with warnings.catch_warnings():
|
| 15 |
-
warnings.simplefilter("error", ResourceWarning)
|
| 16 |
-
server = Server()
|
| 17 |
-
server.expr_type("forall (n m: Nat), n + m = m + n")
|
| 18 |
-
del server
|
| 19 |
-
server = Server()
|
| 20 |
-
server.expr_type("forall (n m: Nat), n + m = m + n")
|
| 21 |
-
del server
|
| 22 |
-
server = Server()
|
| 23 |
-
server.expr_type("forall (n m: Nat), n + m = m + n")
|
| 24 |
-
del server
|
| 25 |
-
|
| 26 |
-
def test_expr_type(self):
|
| 27 |
-
server = Server()
|
| 28 |
-
t = server.expr_type("forall (n m: Nat), n + m = m + n")
|
| 29 |
-
self.assertEqual(t, "Prop")
|
| 30 |
-
|
| 31 |
-
def test_goal_start(self):
|
| 32 |
-
server = Server()
|
| 33 |
-
state0 = server.goal_start("forall (p q: Prop), Or p q -> Or q p")
|
| 34 |
-
self.assertEqual(len(server.to_remove_goal_states), 0)
|
| 35 |
-
self.assertEqual(state0.state_id, 0)
|
| 36 |
-
state1 = server.goal_tactic(state0, tactic="intro a")
|
| 37 |
-
self.assertEqual(state1.state_id, 1)
|
| 38 |
-
self.assertEqual(state1.goals, [Goal(
|
| 39 |
-
"_uniq.11",
|
| 40 |
-
variables=[Variable(name="a", t="Prop")],
|
| 41 |
-
target="β (q : Prop), a β¨ q β q β¨ a",
|
| 42 |
-
name=None,
|
| 43 |
-
)])
|
| 44 |
-
self.assertEqual(str(state1.goals[0]),"a : Prop\nβ’ β (q : Prop), a β¨ q β q β¨ a")
|
| 45 |
-
|
| 46 |
-
del state0
|
| 47 |
-
self.assertEqual(len(server.to_remove_goal_states), 1)
|
| 48 |
-
server.gc()
|
| 49 |
-
self.assertEqual(len(server.to_remove_goal_states), 0)
|
| 50 |
-
|
| 51 |
-
state0b = server.goal_start("forall (p: Prop), p -> p")
|
| 52 |
-
del state0b
|
| 53 |
-
self.assertEqual(len(server.to_remove_goal_states), 1)
|
| 54 |
-
server.gc()
|
| 55 |
-
self.assertEqual(len(server.to_remove_goal_states), 0)
|
| 56 |
-
|
| 57 |
-
def test_goal_root(self):
|
| 58 |
-
server = Server()
|
| 59 |
-
state0 = server.goal_start("forall (p: Prop), p -> p")
|
| 60 |
-
e = server.goal_root(state0)
|
| 61 |
-
self.assertEqual(e, None)
|
| 62 |
-
state1 = server.goal_tactic(state0, tactic="exact fun z p => p")
|
| 63 |
-
e = server.goal_root(state1)
|
| 64 |
-
self.assertEqual(e, "fun z p => p")
|
| 65 |
-
|
| 66 |
-
def test_automatic_mode(self):
|
| 67 |
-
server = Server()
|
| 68 |
-
state0 = server.goal_start("forall (p q: Prop), Or p q -> Or q p")
|
| 69 |
-
self.assertEqual(len(server.to_remove_goal_states), 0)
|
| 70 |
-
self.assertEqual(state0.state_id, 0)
|
| 71 |
-
state1 = server.goal_tactic(state0, tactic="intro a b h")
|
| 72 |
-
self.assertEqual(state1.state_id, 1)
|
| 73 |
-
self.assertEqual(state1.goals, [Goal(
|
| 74 |
-
"_uniq.17",
|
| 75 |
-
variables=[
|
| 76 |
-
Variable(name="a", t="Prop"),
|
| 77 |
-
Variable(name="b", t="Prop"),
|
| 78 |
-
Variable(name="h", t="a β¨ b"),
|
| 79 |
-
],
|
| 80 |
-
target="b β¨ a",
|
| 81 |
-
name=None,
|
| 82 |
-
)])
|
| 83 |
-
state2 = server.goal_tactic(state1, tactic="cases h")
|
| 84 |
-
self.assertEqual(state2.goals, [
|
| 85 |
-
Goal(
|
| 86 |
-
"_uniq.57",
|
| 87 |
-
variables=[
|
| 88 |
-
Variable(name="a", t="Prop"),
|
| 89 |
-
Variable(name="b", t="Prop"),
|
| 90 |
-
Variable(name="hβ", t="a"),
|
| 91 |
-
],
|
| 92 |
-
target="b β¨ a",
|
| 93 |
-
name="inl",
|
| 94 |
-
),
|
| 95 |
-
Goal(
|
| 96 |
-
"_uniq.70",
|
| 97 |
-
variables=[
|
| 98 |
-
Variable(name="a", t="Prop"),
|
| 99 |
-
Variable(name="b", t="Prop"),
|
| 100 |
-
Variable(name="hβ", t="b"),
|
| 101 |
-
],
|
| 102 |
-
target="b β¨ a",
|
| 103 |
-
name="inr",
|
| 104 |
-
),
|
| 105 |
-
])
|
| 106 |
-
state3 = server.goal_tactic(state2, tactic="apply Or.inl", site=Site(goal_id=1))
|
| 107 |
-
state4 = server.goal_tactic(state3, tactic="assumption")
|
| 108 |
-
self.assertEqual(state4.goals, [
|
| 109 |
-
Goal(
|
| 110 |
-
"_uniq.57",
|
| 111 |
-
variables=[
|
| 112 |
-
Variable(name="a", t="Prop"),
|
| 113 |
-
Variable(name="b", t="Prop"),
|
| 114 |
-
Variable(name="hβ", t="a"),
|
| 115 |
-
],
|
| 116 |
-
target="b β¨ a",
|
| 117 |
-
name="inl",
|
| 118 |
-
)
|
| 119 |
-
])
|
| 120 |
-
|
| 121 |
-
def test_have(self):
|
| 122 |
-
server = Server()
|
| 123 |
-
state0 = server.goal_start("1 + 1 = 2")
|
| 124 |
-
state1 = server.goal_tactic(state0, tactic=TacticHave(branch="2 = 1 + 1", binder_name="h"))
|
| 125 |
-
self.assertEqual(state1.goals, [
|
| 126 |
-
Goal(
|
| 127 |
-
"_uniq.251",
|
| 128 |
-
variables=[],
|
| 129 |
-
target="2 = 1 + 1",
|
| 130 |
-
),
|
| 131 |
-
Goal(
|
| 132 |
-
"_uniq.253",
|
| 133 |
-
variables=[Variable(name="h", t="2 = 1 + 1")],
|
| 134 |
-
target="1 + 1 = 2",
|
| 135 |
-
),
|
| 136 |
-
])
|
| 137 |
-
def test_let(self):
|
| 138 |
-
server = Server()
|
| 139 |
-
state0 = server.goal_start("1 + 1 = 2")
|
| 140 |
-
state1 = server.goal_tactic(
|
| 141 |
-
state0, tactic=TacticLet(branch="2 = 1 + 1", binder_name="h"))
|
| 142 |
-
self.assertEqual(state1.goals, [
|
| 143 |
-
Goal(
|
| 144 |
-
"_uniq.251",
|
| 145 |
-
variables=[],
|
| 146 |
-
name="h",
|
| 147 |
-
target="2 = 1 + 1",
|
| 148 |
-
),
|
| 149 |
-
Goal(
|
| 150 |
-
"_uniq.253",
|
| 151 |
-
variables=[Variable(name="h", t="2 = 1 + 1", v="?h")],
|
| 152 |
-
target="1 + 1 = 2",
|
| 153 |
-
),
|
| 154 |
-
])
|
| 155 |
-
|
| 156 |
-
def test_conv_calc(self):
|
| 157 |
-
server = Server(options={"automaticMode": False})
|
| 158 |
-
state0 = server.goal_start("β (a b: Nat), (b = 2) -> 1 + a + 1 = a + b")
|
| 159 |
-
|
| 160 |
-
variables = [
|
| 161 |
-
Variable(name="a", t="Nat"),
|
| 162 |
-
Variable(name="b", t="Nat"),
|
| 163 |
-
Variable(name="h", t="b = 2"),
|
| 164 |
-
]
|
| 165 |
-
state1 = server.goal_tactic(state0, "intro a b h")
|
| 166 |
-
state1b = server.goal_tactic(state1, TacticMode.CALC)
|
| 167 |
-
state2 = server.goal_tactic(state1b, "1 + a + 1 = a + 1 + 1")
|
| 168 |
-
self.assertEqual(state2.goals, [
|
| 169 |
-
Goal(
|
| 170 |
-
"_uniq.342",
|
| 171 |
-
variables,
|
| 172 |
-
target="1 + a + 1 = a + 1 + 1",
|
| 173 |
-
name='calc',
|
| 174 |
-
),
|
| 175 |
-
Goal(
|
| 176 |
-
"_uniq.356",
|
| 177 |
-
variables,
|
| 178 |
-
target="a + 1 + 1 = a + b",
|
| 179 |
-
mode=TacticMode.CALC,
|
| 180 |
-
),
|
| 181 |
-
])
|
| 182 |
-
state_c1 = server.goal_tactic(state2, TacticMode.CONV)
|
| 183 |
-
state_c2 = server.goal_tactic(state_c1, "rhs")
|
| 184 |
-
state_c3 = server.goal_tactic(state_c2, "rw [Nat.add_comm]")
|
| 185 |
-
state_c4 = server.goal_tactic(state_c3, TacticMode.TACTIC)
|
| 186 |
-
#state_c4b = server.goal_resume(state_c4, [state2.goals[0]])
|
| 187 |
-
state_c5 = server.goal_tactic(state_c4, "rfl")
|
| 188 |
-
self.assertTrue(state_c5.is_solved)
|
| 189 |
-
|
| 190 |
-
state3 = server.goal_tactic(state2, "_ = a + 2", site=Site(1))
|
| 191 |
-
state4 = server.goal_tactic(state3, "rw [Nat.add_assoc]")
|
| 192 |
-
self.assertTrue(state4.is_solved)
|
| 193 |
-
|
| 194 |
-
def test_dependent_mvars(self):
|
| 195 |
-
server = Server(options={"printDependentMVars": True})
|
| 196 |
-
state = server.goal_start("β (x : Nat), x + 1 = 0")
|
| 197 |
-
state = server.goal_tactic(state, "apply Exists.intro")
|
| 198 |
-
self.assertEqual(state.goals[0].sibling_dep, {1})
|
| 199 |
-
self.assertEqual(state.goals[1].sibling_dep, set())
|
| 200 |
-
|
| 201 |
-
def test_subsume(self):
|
| 202 |
-
server = Server()
|
| 203 |
-
state0 = server.goal_start("forall (p : Prop), p -> p")
|
| 204 |
-
state1 = server.goal_tactic(state0, "intro p")
|
| 205 |
-
state2 = server.goal_tactic(state1, "intro h")
|
| 206 |
-
state3 = server.goal_tactic(state2, "revert h")
|
| 207 |
-
src = state1.goals[0]
|
| 208 |
-
(sub, state, subsumptor) = server.goal_subsume(
|
| 209 |
-
state3,
|
| 210 |
-
state3.goals[0],
|
| 211 |
-
[state1.goals[0], state2.goals[0]],
|
| 212 |
-
)
|
| 213 |
-
self.assertEqual(sub, Subsumption.CYCLE)
|
| 214 |
-
self.assertEqual(state, None)
|
| 215 |
-
self.assertEqual(subsumptor, src)
|
| 216 |
-
|
| 217 |
-
def test_env_add_inspect(self):
|
| 218 |
-
server = Server()
|
| 219 |
-
server.env_add(
|
| 220 |
-
name="mystery",
|
| 221 |
-
levels=[],
|
| 222 |
-
t="forall (n: Nat), Nat",
|
| 223 |
-
v="fun (n: Nat) => n + 1",
|
| 224 |
-
is_theorem=False,
|
| 225 |
-
)
|
| 226 |
-
inspect_result = server.env_inspect(name="mystery")
|
| 227 |
-
self.assertEqual(inspect_result['type'], {'pp': 'Nat β Nat'})
|
| 228 |
-
|
| 229 |
-
def test_env_catalog(self):
|
| 230 |
-
server = Server()
|
| 231 |
-
server.load_definitions("def foo: Nat -> Nat | 0 => 1 | n + 1 => foo n")
|
| 232 |
-
definitions = server.env_catalog(module_prefix="Init", invert_filter=True)
|
| 233 |
-
self.assertEqual(
|
| 234 |
-
set(definitions),
|
| 235 |
-
{'dfoo._sunfold', 'dfoo._unsafe_rec', 'dfoo', 'dfoo.match_1'}
|
| 236 |
-
)
|
| 237 |
-
|
| 238 |
-
def test_env_parse(self):
|
| 239 |
-
server = Server()
|
| 240 |
-
head, tail = server.env_parse("intro x; apply a", category="tactic")
|
| 241 |
-
self.assertEqual(head, "intro x")
|
| 242 |
-
self.assertEqual(tail, "; apply a")
|
| 243 |
-
|
| 244 |
-
def test_goal_state_pickling(self):
|
| 245 |
-
import tempfile
|
| 246 |
-
server = Server()
|
| 247 |
-
state0 = server.goal_start("forall (p q: Prop), Or p q -> Or q p")
|
| 248 |
-
with tempfile.TemporaryDirectory() as td:
|
| 249 |
-
path = td + "/goal-state.pickle"
|
| 250 |
-
server.goal_save(state0, path)
|
| 251 |
-
state0b = server.goal_load(path)
|
| 252 |
-
self.assertEqual(state0b.goals, [
|
| 253 |
-
Goal(
|
| 254 |
-
"_uniq.9",
|
| 255 |
-
variables=[
|
| 256 |
-
],
|
| 257 |
-
target="β (p q : Prop), p β¨ q β q β¨ p",
|
| 258 |
-
)
|
| 259 |
-
])
|
| 260 |
-
|
| 261 |
-
def test_load_header(self):
|
| 262 |
-
server = Server(imports=[])
|
| 263 |
-
server.load_header("import Init\nopen Nat")
|
| 264 |
-
state0 = server.goal_start("forall (n : Nat), n + 1 = n.succ")
|
| 265 |
-
state1 = server.goal_tactic(state0, "intro")
|
| 266 |
-
state2 = server.goal_tactic(state1, "apply add_one")
|
| 267 |
-
self.assertTrue(state2.is_solved)
|
| 268 |
-
|
| 269 |
-
def test_check_compile(self):
|
| 270 |
-
server = Server()
|
| 271 |
-
unit, = server.check_compile("example (p: Prop) : p -> p := id")
|
| 272 |
-
self.assertEqual(unit.messages, [])
|
| 273 |
-
unit, = server.check_compile("example (p: Prop) : p -> p := 1")
|
| 274 |
-
self.assertEqual(unit.messages, [Message(
|
| 275 |
-
pos=Position(1, 30),
|
| 276 |
-
pos_end=Position(1, 31),
|
| 277 |
-
data=
|
| 278 |
-
"numerals are data in Lean, but the expected type is "
|
| 279 |
-
"a proposition\n"
|
| 280 |
-
" p β p : Prop"
|
| 281 |
-
)
|
| 282 |
-
])
|
| 283 |
-
unit, = server.check_compile("import Lean\nexample (p: Prop) : p -> p := id", read_header=True)
|
| 284 |
-
self.assertEqual(unit.messages, [])
|
| 285 |
-
|
| 286 |
-
def test_load_definitions(self):
|
| 287 |
-
server = Server()
|
| 288 |
-
server.load_definitions(
|
| 289 |
-
"def mystery (x : Nat) := x + 123"
|
| 290 |
-
)
|
| 291 |
-
inspect_result = server.env_inspect(name="mystery")
|
| 292 |
-
self.assertEqual(inspect_result['type'], {'pp': 'Nat β Nat'})
|
| 293 |
-
|
| 294 |
-
def test_load_sorry(self):
|
| 295 |
-
server = Server()
|
| 296 |
-
unit, = server.load_sorry("theorem mystery (p: Prop) : p β p := sorry")
|
| 297 |
-
#self.assertIsNotNone(unit.goal_state, f"{unit.messages}")
|
| 298 |
-
state0 = unit.goal_state
|
| 299 |
-
self.assertEqual(state0.goals, [
|
| 300 |
-
Goal(
|
| 301 |
-
"_uniq.5",
|
| 302 |
-
[Variable(name="p", t="Prop")],
|
| 303 |
-
target="p β p",
|
| 304 |
-
),
|
| 305 |
-
])
|
| 306 |
-
state1 = server.goal_tactic(state0, tactic="intro h")
|
| 307 |
-
state2 = server.goal_tactic(state1, tactic="exact h")
|
| 308 |
-
self.assertTrue(state2.is_solved)
|
| 309 |
-
|
| 310 |
-
state1b = server.goal_tactic(state0, tactic=TacticDraft("by\nhave h1 : Or p p := sorry\nsorry"))
|
| 311 |
-
self.assertEqual(state1b.goals, [
|
| 312 |
-
Goal(
|
| 313 |
-
"_uniq.19",
|
| 314 |
-
[Variable(name="p", t="Prop")],
|
| 315 |
-
target="p β¨ p",
|
| 316 |
-
),
|
| 317 |
-
Goal(
|
| 318 |
-
"_uniq.21",
|
| 319 |
-
[
|
| 320 |
-
Variable(name="p", t="Prop"),
|
| 321 |
-
Variable(name="h1", t="p β¨ p", v="?m.7"),
|
| 322 |
-
],
|
| 323 |
-
target="p β p",
|
| 324 |
-
),
|
| 325 |
-
])
|
| 326 |
-
|
| 327 |
-
def test_distil_search_target(self):
|
| 328 |
-
server = Server()
|
| 329 |
-
unit, = server.load_sorry("theorem mystery (p: Prop) : p β p := sorry", ignore_values = True)
|
| 330 |
-
state0 = unit.goal_state
|
| 331 |
-
self.assertEqual(state0.goals, [
|
| 332 |
-
Goal(
|
| 333 |
-
"_uniq.3",
|
| 334 |
-
[],
|
| 335 |
-
target="β (p : Prop), p β p",
|
| 336 |
-
),
|
| 337 |
-
])
|
| 338 |
-
state1 = server.goal_tactic(state0, tactic="intro p h")
|
| 339 |
-
state2 = server.goal_tactic(state1, tactic="exact h")
|
| 340 |
-
self.assertTrue(state2.is_solved)
|
| 341 |
-
|
| 342 |
-
def test_distil_coupled(self):
|
| 343 |
-
server = Server()
|
| 344 |
-
code = """
|
| 345 |
-
def f : Nat -> Nat := sorry
|
| 346 |
-
theorem property (n : Nat) : f n = n := sorry"""
|
| 347 |
-
unit, = server.load_sorry(code, ignore_values=False)
|
| 348 |
-
state0 = unit.goal_state
|
| 349 |
-
self.assertEqual(state0.goals, [
|
| 350 |
-
Goal(
|
| 351 |
-
"_uniq.7",
|
| 352 |
-
[],
|
| 353 |
-
name='f',
|
| 354 |
-
target="Nat β Nat",
|
| 355 |
-
),
|
| 356 |
-
Goal(
|
| 357 |
-
"_uniq.10",
|
| 358 |
-
[Variable(name='n', t='Nat')],
|
| 359 |
-
target="?f n = n",
|
| 360 |
-
),
|
| 361 |
-
])
|
| 362 |
-
|
| 363 |
-
def test_check_track(self):
|
| 364 |
-
server = Server()
|
| 365 |
-
src = "def f : Nat -> Nat := sorry"
|
| 366 |
-
dst = "def f : Nat -> Nat := fun y => y + y"
|
| 367 |
-
self.assertTrue(server.check_track(src, dst).succeeded)
|
| 368 |
-
|
| 369 |
-
def test_refactor_search_target(self):
|
| 370 |
-
code = """
|
| 371 |
-
def f : Nat -> Nat := sorry
|
| 372 |
-
theorem property (n : Nat) : f n = n := sorry"""
|
| 373 |
-
target = """
|
| 374 |
-
def f_composite : { f : Nat β Nat // β (n : Nat), f n = n } :=
|
| 375 |
-
sorry"""
|
| 376 |
-
server = Server()
|
| 377 |
-
result = server.refactor_search_target(code)
|
| 378 |
-
self.assertEqual(result, target)
|
| 379 |
-
|
| 380 |
-
if __name__ == '__main__':
|
| 381 |
-
unittest.main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/pantograph/utils.py
DELETED
|
@@ -1,48 +0,0 @@
|
|
| 1 |
-
import asyncio
|
| 2 |
-
from pathlib import Path
|
| 3 |
-
import functools as F
|
| 4 |
-
|
| 5 |
-
DEFAULT_EVENT_LOOP = asyncio.new_event_loop()
|
| 6 |
-
|
| 7 |
-
def get_event_loop():
|
| 8 |
-
try:
|
| 9 |
-
return asyncio.get_event_loop()
|
| 10 |
-
except RuntimeError:
|
| 11 |
-
asyncio.set_event_loop(DEFAULT_EVENT_LOOP)
|
| 12 |
-
return loop
|
| 13 |
-
|
| 14 |
-
def to_sync(func):
|
| 15 |
-
loop = get_event_loop()
|
| 16 |
-
@F.wraps(func)
|
| 17 |
-
def wrapper(*args, **kwargs):
|
| 18 |
-
return loop.run_until_complete(func(*args, **kwargs))
|
| 19 |
-
return wrapper
|
| 20 |
-
|
| 21 |
-
async def check_output(*args, **kwargs):
|
| 22 |
-
p = await asyncio.create_subprocess_exec(
|
| 23 |
-
*args,
|
| 24 |
-
stdout=asyncio.subprocess.PIPE,
|
| 25 |
-
stderr=asyncio.subprocess.PIPE,
|
| 26 |
-
**kwargs,
|
| 27 |
-
)
|
| 28 |
-
stdout_data, stderr_data = await p.communicate()
|
| 29 |
-
if p.returncode == 0:
|
| 30 |
-
return stdout_data
|
| 31 |
-
|
| 32 |
-
def _get_proc_cwd():
|
| 33 |
-
return Path(__file__).parent
|
| 34 |
-
|
| 35 |
-
def _get_proc_path():
|
| 36 |
-
return _get_proc_cwd() / "pantograph-repl"
|
| 37 |
-
|
| 38 |
-
async def get_lean_path_async(project_path):
|
| 39 |
-
"""
|
| 40 |
-
Extracts the `LEAN_PATH` variable from a project path.
|
| 41 |
-
"""
|
| 42 |
-
p = await check_output(
|
| 43 |
-
'lake', 'env', 'printenv', 'LEAN_PATH',
|
| 44 |
-
cwd=project_path,
|
| 45 |
-
)
|
| 46 |
-
return p
|
| 47 |
-
|
| 48 |
-
get_lean_path = to_sync(get_lean_path_async)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/pyproject.toml
DELETED
|
@@ -1,39 +0,0 @@
|
|
| 1 |
-
[project]
|
| 2 |
-
name = "pantograph"
|
| 3 |
-
version = "0.3.9"
|
| 4 |
-
description = "A machine-to-machine interaction system for Lean"
|
| 5 |
-
license = "Apache-2.0"
|
| 6 |
-
readme = "README.md"
|
| 7 |
-
authors = [
|
| 8 |
-
{name = "Leni Aniva", email = "aniva@stanford.edu"},
|
| 9 |
-
{name = "contributors"}
|
| 10 |
-
]
|
| 11 |
-
requires-python = ">=3.11.0"
|
| 12 |
-
dependencies = []
|
| 13 |
-
[dependency-groups]
|
| 14 |
-
dev = [
|
| 15 |
-
"jupyter-book>=1.0.4.post1",
|
| 16 |
-
"notebook",
|
| 17 |
-
"pytest>=8.4.1",
|
| 18 |
-
]
|
| 19 |
-
|
| 20 |
-
[build-system]
|
| 21 |
-
requires = ["poetry-core>=2,<3"]
|
| 22 |
-
build-backend = "poetry.core.masonry.api"
|
| 23 |
-
|
| 24 |
-
[tool.poetry]
|
| 25 |
-
include = [
|
| 26 |
-
{ path = "pantograph/pantograph-repl", format = ["sdist", "wheel"] },
|
| 27 |
-
{ path = "pantograph/lean-toolchain", format = ["sdist", "wheel"] },
|
| 28 |
-
{ path = "src", format = ["sdist", "wheel"] },
|
| 29 |
-
]
|
| 30 |
-
|
| 31 |
-
[tool.poetry.build]
|
| 32 |
-
generate-setup-file = false
|
| 33 |
-
script = "build-pantograph.py"
|
| 34 |
-
|
| 35 |
-
[tool.poetry.group.doc]
|
| 36 |
-
optional = true
|
| 37 |
-
[tool.poetry.group.doc.dependencies]
|
| 38 |
-
notebook = "^7.2.1"
|
| 39 |
-
jupyter-book = "^1.0.3"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/src/.gitignore
DELETED
|
@@ -1,6 +0,0 @@
|
|
| 1 |
-
.*
|
| 2 |
-
!.gitignore
|
| 3 |
-
!.pre-commit-config.yaml
|
| 4 |
-
|
| 5 |
-
*.[io]lean
|
| 6 |
-
/result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/src/.pre-commit-config.yaml
DELETED
|
@@ -1,30 +0,0 @@
|
|
| 1 |
-
default_install_hook_types:
|
| 2 |
-
- pre-commit
|
| 3 |
-
- commit-msg
|
| 4 |
-
repos:
|
| 5 |
-
- repo: https://github.com/pre-commit/pre-commit-hooks
|
| 6 |
-
rev: v6.0.0
|
| 7 |
-
hooks:
|
| 8 |
-
- id: check-yaml
|
| 9 |
-
- id: end-of-file-fixer
|
| 10 |
-
- id: trailing-whitespace
|
| 11 |
-
- id: check-symlinks
|
| 12 |
-
- id: check-added-large-files
|
| 13 |
-
- id: check-merge-conflict
|
| 14 |
-
- id: detect-private-key
|
| 15 |
-
- id: forbid-submodules
|
| 16 |
-
- id: no-commit-to-branch
|
| 17 |
-
args: [ --branch, main, --branch, dev ]
|
| 18 |
-
- repo: https://github.com/compilerla/conventional-pre-commit
|
| 19 |
-
rev: v4.3.0
|
| 20 |
-
hooks:
|
| 21 |
-
- id: conventional-pre-commit
|
| 22 |
-
stages: [commit-msg]
|
| 23 |
-
args: [feat, fix, doc, chore, test, build, merge, refactor]
|
| 24 |
-
- repo: local
|
| 25 |
-
hooks:
|
| 26 |
-
- id: format nix
|
| 27 |
-
name: Format Nix Code
|
| 28 |
-
language: system
|
| 29 |
-
entry: nix fmt
|
| 30 |
-
files: .*\.nix$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/src/LICENSE
DELETED
|
@@ -1,190 +0,0 @@
|
|
| 1 |
-
Apache License
|
| 2 |
-
Version 2.0, January 2004
|
| 3 |
-
http://www.apache.org/licenses/
|
| 4 |
-
|
| 5 |
-
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 6 |
-
|
| 7 |
-
1. Definitions.
|
| 8 |
-
|
| 9 |
-
"License" shall mean the terms and conditions for use, reproduction,
|
| 10 |
-
and distribution as defined by Sections 1 through 9 of this document.
|
| 11 |
-
|
| 12 |
-
"Licensor" shall mean the copyright owner or entity authorized by
|
| 13 |
-
the copyright owner that is granting the License.
|
| 14 |
-
|
| 15 |
-
"Legal Entity" shall mean the union of the acting entity and all
|
| 16 |
-
other entities that control, are controlled by, or are under common
|
| 17 |
-
control with that entity. For the purposes of this definition,
|
| 18 |
-
"control" means (i) the power, direct or indirect, to cause the
|
| 19 |
-
direction or management of such entity, whether by contract or
|
| 20 |
-
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 21 |
-
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 22 |
-
|
| 23 |
-
"You" (or "Your") shall mean an individual or Legal Entity
|
| 24 |
-
exercising permissions granted by this License.
|
| 25 |
-
|
| 26 |
-
"Source" form shall mean the preferred form for making modifications,
|
| 27 |
-
including but not limited to software source code, documentation
|
| 28 |
-
source, and configuration files.
|
| 29 |
-
|
| 30 |
-
"Object" form shall mean any form resulting from mechanical
|
| 31 |
-
transformation or translation of a Source form, including but
|
| 32 |
-
not limited to compiled object code, generated documentation,
|
| 33 |
-
and conversions to other media types.
|
| 34 |
-
|
| 35 |
-
"Work" shall mean the work of authorship, whether in Source or
|
| 36 |
-
Object form, made available under the License, as indicated by a
|
| 37 |
-
copyright notice that is included in or attached to the work
|
| 38 |
-
(an example is provided in the Appendix below).
|
| 39 |
-
|
| 40 |
-
"Derivative Works" shall mean any work, whether in Source or Object
|
| 41 |
-
form, that is based on (or derived from) the Work and for which the
|
| 42 |
-
editorial revisions, annotations, elaborations, or other modifications
|
| 43 |
-
represent, as a whole, an original work of authorship. For the purposes
|
| 44 |
-
of this License, Derivative Works shall not include works that remain
|
| 45 |
-
separable from, or merely link (or bind by name) to the interfaces of,
|
| 46 |
-
the Work and Derivative Works thereof.
|
| 47 |
-
|
| 48 |
-
"Contribution" shall mean any work of authorship, including
|
| 49 |
-
the original version of the Work and any modifications or additions
|
| 50 |
-
to that Work or Derivative Works thereof, that is intentionally
|
| 51 |
-
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 52 |
-
or by an individual or Legal Entity authorized to submit on behalf of
|
| 53 |
-
the copyright owner. For the purposes of this definition, "submitted"
|
| 54 |
-
means any form of electronic, verbal, or written communication sent
|
| 55 |
-
to the Licensor or its representatives, including but not limited to
|
| 56 |
-
communication on electronic mailing lists, source code control systems,
|
| 57 |
-
and issue tracking systems that are managed by, or on behalf of, the
|
| 58 |
-
Licensor for the purpose of discussing and improving the Work, but
|
| 59 |
-
excluding communication that is conspicuously marked or otherwise
|
| 60 |
-
designated in writing by the copyright owner as "Not a Contribution."
|
| 61 |
-
|
| 62 |
-
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 63 |
-
on behalf of whom a Contribution has been received by Licensor and
|
| 64 |
-
subsequently incorporated within the Work.
|
| 65 |
-
|
| 66 |
-
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 67 |
-
this License, each Contributor hereby grants to You a perpetual,
|
| 68 |
-
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 69 |
-
copyright license to reproduce, prepare Derivative Works of,
|
| 70 |
-
publicly display, publicly perform, sublicense, and distribute the
|
| 71 |
-
Work and such Derivative Works in Source or Object form.
|
| 72 |
-
|
| 73 |
-
3. Grant of Patent License. Subject to the terms and conditions of
|
| 74 |
-
this License, each Contributor hereby grants to You a perpetual,
|
| 75 |
-
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 76 |
-
(except as stated in this section) patent license to make, have made,
|
| 77 |
-
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 78 |
-
where such license applies only to those patent claims licensable
|
| 79 |
-
by such Contributor that are necessarily infringed by their
|
| 80 |
-
Contribution(s) alone or by combination of their Contribution(s)
|
| 81 |
-
with the Work to which such Contribution(s) was submitted. If You
|
| 82 |
-
institute patent litigation against any entity (including a
|
| 83 |
-
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 84 |
-
or a Contribution incorporated within the Work constitutes direct
|
| 85 |
-
or contributory patent infringement, then any patent licenses
|
| 86 |
-
granted to You under this License for that Work shall terminate
|
| 87 |
-
as of the date such litigation is filed.
|
| 88 |
-
|
| 89 |
-
4. Redistribution. You may reproduce and distribute copies of the
|
| 90 |
-
Work or Derivative Works thereof in any medium, with or without
|
| 91 |
-
modifications, and in Source or Object form, provided that You
|
| 92 |
-
meet the following conditions:
|
| 93 |
-
|
| 94 |
-
(a) You must give any other recipients of the Work or
|
| 95 |
-
Derivative Works a copy of this License; and
|
| 96 |
-
|
| 97 |
-
(b) You must cause any modified files to carry prominent notices
|
| 98 |
-
stating that You changed the files; and
|
| 99 |
-
|
| 100 |
-
(c) You must retain, in the Source form of any Derivative Works
|
| 101 |
-
that You distribute, all copyright, patent, trademark, and
|
| 102 |
-
attribution notices from the Source form of the Work,
|
| 103 |
-
excluding those notices that do not pertain to any part of
|
| 104 |
-
the Derivative Works; and
|
| 105 |
-
|
| 106 |
-
(d) If the Work includes a "NOTICE" text file as part of its
|
| 107 |
-
distribution, then any Derivative Works that You distribute must
|
| 108 |
-
include a readable copy of the attribution notices contained
|
| 109 |
-
within such NOTICE file, excluding those notices that do not
|
| 110 |
-
pertain to any part of the Derivative Works, in at least one
|
| 111 |
-
of the following places: within a NOTICE text file distributed
|
| 112 |
-
as part of the Derivative Works; within the Source form or
|
| 113 |
-
documentation, if provided along with the Derivative Works; or,
|
| 114 |
-
within a display generated by the Derivative Works, if and
|
| 115 |
-
wherever such third-party notices normally appear. The contents
|
| 116 |
-
of the NOTICE file are for informational purposes only and
|
| 117 |
-
do not modify the License. You may add Your own attribution
|
| 118 |
-
notices within Derivative Works that You distribute, alongside
|
| 119 |
-
or as an addendum to the NOTICE text from the Work, provided
|
| 120 |
-
that such additional attribution notices cannot be construed
|
| 121 |
-
as modifying the License.
|
| 122 |
-
|
| 123 |
-
You may add Your own copyright statement to Your modifications and
|
| 124 |
-
may provide additional or different license terms and conditions
|
| 125 |
-
for use, reproduction, or distribution of Your modifications, or
|
| 126 |
-
for any such Derivative Works as a whole, provided Your use,
|
| 127 |
-
reproduction, and distribution of the Work otherwise complies with
|
| 128 |
-
the conditions stated in this License.
|
| 129 |
-
|
| 130 |
-
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 131 |
-
any Contribution intentionally submitted for inclusion in the Work
|
| 132 |
-
by You to the Licensor shall be under the terms and conditions of
|
| 133 |
-
this License, without any additional terms or conditions.
|
| 134 |
-
Notwithstanding the above, nothing herein shall supersede or modify
|
| 135 |
-
the terms of any separate license agreement you may have executed
|
| 136 |
-
with Licensor regarding such Contributions.
|
| 137 |
-
|
| 138 |
-
6. Trademarks. This License does not grant permission to use the trade
|
| 139 |
-
names, trademarks, service marks, or product names of the Licensor,
|
| 140 |
-
except as required for reasonable and customary use in describing the
|
| 141 |
-
origin of the Work and reproducing the content of the NOTICE file.
|
| 142 |
-
|
| 143 |
-
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 144 |
-
agreed to in writing, Licensor provides the Work (and each
|
| 145 |
-
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 146 |
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 147 |
-
implied, including, without limitation, any warranties or conditions
|
| 148 |
-
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 149 |
-
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 150 |
-
appropriateness of using or redistributing the Work and assume any
|
| 151 |
-
risks associated with Your exercise of permissions under this License.
|
| 152 |
-
|
| 153 |
-
8. Limitation of Liability. In no event and under no legal theory,
|
| 154 |
-
whether in tort (including negligence), contract, or otherwise,
|
| 155 |
-
unless required by applicable law (such as deliberate and grossly
|
| 156 |
-
negligent acts) or agreed to in writing, shall any Contributor be
|
| 157 |
-
liable to You for damages, including any direct, indirect, special,
|
| 158 |
-
incidental, or consequential damages of any character arising as a
|
| 159 |
-
result of this License or out of the use or inability to use the
|
| 160 |
-
Work (including but not limited to damages for loss of goodwill,
|
| 161 |
-
work stoppage, computer failure or malfunction, or any and all
|
| 162 |
-
other commercial damages or losses), even if such Contributor
|
| 163 |
-
has been advised of the possibility of such damages.
|
| 164 |
-
|
| 165 |
-
9. Accepting Warranty or Additional Liability. While redistributing
|
| 166 |
-
the Work or Derivative Works thereof, You may choose to offer,
|
| 167 |
-
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 168 |
-
or other liability obligations and/or rights consistent with this
|
| 169 |
-
License. However, in accepting such obligations, You may act only
|
| 170 |
-
on Your own behalf and on Your sole responsibility, not on behalf
|
| 171 |
-
of any other Contributor, and only if You agree to indemnify,
|
| 172 |
-
defend, and hold each Contributor harmless for any liability
|
| 173 |
-
incurred by, or claims asserted against, such Contributor by reason
|
| 174 |
-
of your accepting any such warranty or additional liability.
|
| 175 |
-
|
| 176 |
-
END OF TERMS AND CONDITIONS
|
| 177 |
-
|
| 178 |
-
Copyright 2024 Leni Aniva
|
| 179 |
-
|
| 180 |
-
Licensed under the Apache License, Version 2.0 (the "License");
|
| 181 |
-
you may not use this file except in compliance with the License.
|
| 182 |
-
You may obtain a copy of the License at
|
| 183 |
-
|
| 184 |
-
http://www.apache.org/licenses/LICENSE-2.0
|
| 185 |
-
|
| 186 |
-
Unless required by applicable law or agreed to in writing, software
|
| 187 |
-
distributed under the License is distributed on an "AS IS" BASIS,
|
| 188 |
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 189 |
-
See the License for the specific language governing permissions and
|
| 190 |
-
limitations under the License.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/src/Main.lean
DELETED
|
@@ -1,80 +0,0 @@
|
|
| 1 |
-
import Lean.Data.Json
|
| 2 |
-
import Lean.Environment
|
| 3 |
-
|
| 4 |
-
import Pantograph
|
| 5 |
-
import Repl
|
| 6 |
-
|
| 7 |
-
-- Main IO functions
|
| 8 |
-
open Pantograph.Repl
|
| 9 |
-
open Pantograph.Protocol
|
| 10 |
-
|
| 11 |
-
/-- Print a string to stdout without buffering -/
|
| 12 |
-
def printImmediate (s : String) : IO Unit := do
|
| 13 |
-
let stdout β IO.getStdout
|
| 14 |
-
stdout.putStr (s ++ "\n")
|
| 15 |
-
stdout.flush
|
| 16 |
-
|
| 17 |
-
/-- Parse a command either in `{ "cmd": ..., "payload": ... }` form or `cmd { ... }` form. -/
|
| 18 |
-
def parseCommand (s: String): Except String Command := do
|
| 19 |
-
match s.trim.get? 0 with
|
| 20 |
-
| .some '{' =>
|
| 21 |
-
-- Parse in Json mode
|
| 22 |
-
Lean.fromJson? (β Lean.Json.parse s)
|
| 23 |
-
| .some _ =>
|
| 24 |
-
-- Parse in line mode
|
| 25 |
-
let offset := s.posOf ' ' |> s.offsetOfPos
|
| 26 |
-
if offset = s.length then
|
| 27 |
-
return { cmd := s.take offset, payload := Lean.Json.null }
|
| 28 |
-
else
|
| 29 |
-
let payload β s.drop offset |> Lean.Json.parse
|
| 30 |
-
return { cmd := s.take offset, payload := payload }
|
| 31 |
-
| .none =>
|
| 32 |
-
throw "Command is empty"
|
| 33 |
-
|
| 34 |
-
partial def loop : MainM Unit := do repeat do
|
| 35 |
-
let state β get
|
| 36 |
-
let command β (β IO.getStdin).getLine
|
| 37 |
-
-- Halt the program if empty line is given
|
| 38 |
-
if command.trim.length = 0 then break
|
| 39 |
-
match parseCommand command with
|
| 40 |
-
| .error error =>
|
| 41 |
-
let error := Lean.toJson ({ error := "command", desc := error }: InteractionError)
|
| 42 |
-
-- Using `Lean.Json.compress` here to prevent newline
|
| 43 |
-
printImmediate error.compress
|
| 44 |
-
| .ok command =>
|
| 45 |
-
try
|
| 46 |
-
let ret β execute command
|
| 47 |
-
let str := match state.options.printJsonPretty with
|
| 48 |
-
| true => ret.pretty
|
| 49 |
-
| false => ret.compress
|
| 50 |
-
printImmediate str
|
| 51 |
-
catch e =>
|
| 52 |
-
let message := e.toString
|
| 53 |
-
let error := Lean.toJson ({ error := "main", desc := message }: InteractionError)
|
| 54 |
-
printImmediate error.compress
|
| 55 |
-
|
| 56 |
-
def main (args: List String): IO Unit := do
|
| 57 |
-
-- NOTE: A more sophisticated scheme of command line argument handling is needed.
|
| 58 |
-
if args == ["--version"] then do
|
| 59 |
-
IO.println s!"{Pantograph.version}"
|
| 60 |
-
return
|
| 61 |
-
|
| 62 |
-
unsafe do
|
| 63 |
-
Pantograph.initSearch
|
| 64 |
-
|
| 65 |
-
-- Separate imports and options
|
| 66 |
-
let (options, imports) := args.partition (Β·.startsWith "--")
|
| 67 |
-
let coreContext β options.map (Β·.drop 2) |>.toArray |> Pantograph.createCoreContext
|
| 68 |
-
let env β Lean.importModules
|
| 69 |
-
(imports := imports.toArray.map ({ module := Β·.toName }))
|
| 70 |
-
(opts := {})
|
| 71 |
-
(trustLevel := 1)
|
| 72 |
-
(loadExts := true)
|
| 73 |
-
try
|
| 74 |
-
let mainM := loop.run { coreContext } |>.run' { env }
|
| 75 |
-
printImmediate "ready."
|
| 76 |
-
mainM
|
| 77 |
-
catch ex =>
|
| 78 |
-
let message := ex.toString
|
| 79 |
-
let error := Lean.toJson ({ error := "io", desc := message }: InteractionError)
|
| 80 |
-
IO.println error.compress
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyPantograph/src/Pantograph.lean
DELETED
|
@@ -1,9 +0,0 @@
|
|
| 1 |
-
import Pantograph.Delate
|
| 2 |
-
import Pantograph.Elab
|
| 3 |
-
import Pantograph.Environment
|
| 4 |
-
import Pantograph.Frontend
|
| 5 |
-
import Pantograph.Goal
|
| 6 |
-
import Pantograph.Library
|
| 7 |
-
import Pantograph.Protocol
|
| 8 |
-
import Pantograph.Serial
|
| 9 |
-
import Pantograph.Version
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|