{ "cells": [ { "cell_type": "markdown", "id": "c5106980-4850-4bea-a333-5a1b2e4d1dc5", "metadata": {}, "source": [ "# Goals and Tactics\n", "\n", "Executing tactics in Pantograph is simple. To start a proof, call the\n", "`Server.goal_start` function and supply an expression." ] }, { "cell_type": "code", "execution_count": 1, "id": "3257de2b-41ca-4cfe-b66c-1ef4781c98b0", "metadata": {}, "outputs": [], "source": [ "from pantograph import Server\n", "from pantograph.expr import TacticHave, TacticCalc, TacticExpr" ] }, { "cell_type": "code", "execution_count": 2, "id": "6783d478-d8c7-4c4e-a56e-8170384297ef", "metadata": {}, "outputs": [], "source": [ "server = Server()\n", "state0 = server.goal_start(\"forall (p q: Prop), Or p q -> Or q p\")" ] }, { "cell_type": "markdown", "id": "bfe5a9df-33c2-4538-a9ce-fc0e02c92ff2", "metadata": {}, "source": [ "This creates a *goal state*, which consists of a finite number of goals. In this\n", "case since it is the beginning of a state, it has only one goal." ] }, { "cell_type": "code", "execution_count": 3, "id": "eefc9094-9574-4f92-9aa2-c39beb85389b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "⊢ forall (p q: Prop), Or p q -> Or q p\n" ] } ], "source": [ "print(state0)" ] }, { "cell_type": "markdown", "id": "26dbe212-e09e-42dd-ab15-65ee2fba6234", "metadata": {}, "source": [ "To execute a tactic on a goal state, use `Server.goal_tactic`. This function\n", "takes a goal id and a tactic. Most Lean tactics are strings." ] }, { "cell_type": "code", "execution_count": 4, "id": "c907dbb6-4d6a-4aa7-b173-60220165ba9e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a : Prop\n", "⊢ ∀ (q : Prop), a ∨ q → q ∨ a\n" ] } ], "source": [ "state1 = server.goal_tactic(state0, goal_id=0, tactic=\"intro a\")\n", "print(state1)" ] }, { "cell_type": "markdown", "id": "9978fdcf-a12b-4f22-9551-5e04c262e5e0", "metadata": {}, "source": [ "Executing a tactic produces a new goal state. If this goal state has no goals,\n", "the proof is complete. You can recover the usual form of a goal with `str()`" ] }, { "cell_type": "code", "execution_count": 5, "id": "16595c5e-2285-49d5-8340-397ad1e6c9e7", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'a : Prop\\n⊢ ∀ (q : Prop), a ∨ q → q ∨ a'" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "str(state1.goals[0])" ] }, { "cell_type": "markdown", "id": "67f2a75d-6851-4393-bac9-a091400f1906", "metadata": {}, "source": [ "## Error Handling and GC\n", "\n", "When a tactic fails, it throws an exception:" ] }, { "cell_type": "code", "execution_count": 6, "id": "c9784ba2-3810-4f80-a6c4-33d5eef3003e", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[\"tactic 'assumption' failed\\na : Prop\\n⊢ ∀ (q : Prop), a ∨ q → q ∨ a\"]\n" ] } ], "source": [ "try:\n", " state2 = server.goal_tactic(state1, goal_id=0, tactic=\"assumption\")\n", " print(\"Should not reach this\")\n", "except Exception as e:\n", " print(e)" ] }, { "cell_type": "markdown", "id": "1ae60d9e-8656-4f26-b495-d04bced250fc", "metadata": {}, "source": [ "A state with no goals is considered solved" ] }, { "cell_type": "code", "execution_count": 7, "id": "1cb96b19-d3bb-4533-abeb-a7dbc5bc8c3e", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "GoalState(state_id=5, goals=[], _sentinel=[0, 1])" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "state0 = server.goal_start(\"forall (p : Prop), p -> p\")\n", "state1 = server.goal_tactic(state0, goal_id=0, tactic=\"intro\")\n", "state2 = server.goal_tactic(state1, goal_id=0, tactic=\"intro h\")\n", "state3 = server.goal_tactic(state2, goal_id=0, tactic=\"exact h\")\n", "state3" ] }, { "cell_type": "markdown", "id": "a2945e71-e583-4ae0-9c0f-83035f0492f2", "metadata": {}, "source": [ "Execute `server.gc()` once in a while to delete unused goals." ] }, { "cell_type": "code", "execution_count": 8, "id": "d53624ff-c720-4847-98f7-28e109eb76e7", "metadata": {}, "outputs": [], "source": [ "server.gc()" ] }, { "cell_type": "markdown", "id": "0b59e05e-7d8c-4fad-b8ca-375ea995ea5b", "metadata": {}, "source": [ "## Special Tactics\n", "\n", "Lean has special provisions for some tactics. This includes `have`, `let`,\n", "`calc`. To execute one of these tactics, create a `TacticHave`, `TacticLet`,\n", "`TacticCalc` instance and feed it into `server.goal_tactic`.\n", "\n", "Technically speaking `have` and `let` are not tactics in Lean, so their execution requires special attention." ] }, { "cell_type": "code", "execution_count": 9, "id": "526d620b-064f-4ec0-a7b2-6a1ef3c6f6e7", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "⊢ 2 = 1 + 1\n", "h : 2 = 1 + 1\n", "⊢ 1 + 1 = 2\n" ] } ], "source": [ "state0 = server.goal_start(\"1 + 1 = 2\")\n", "state1 = server.goal_tactic(state0, goal_id=0, tactic=TacticHave(branch=\"2 = 1 + 1\", binder_name=\"h\"))\n", "print(state1)" ] }, { "cell_type": "markdown", "id": "c415d436-ed0d-475f-bf5e-b8dc63954c7e", "metadata": {}, "source": [ "The `TacticExpr` \"tactic\" parses an expression and assigns it to the current\n", "goal. This leverages Lean's type unification system and is as expressive as\n", "Lean expressions. Many proofs in Mathlib4 are written in a mixture of expression\n", "and tactic forms." ] }, { "cell_type": "code", "execution_count": 10, "id": "e1f06441-4d77-45a7-a1c3-b800b96a8105", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "state0 = server.goal_start(\"forall (p : Prop), p -> p\")\n", "state1 = server.goal_tactic(state0, goal_id=0, tactic=\"intro p\")\n", "state2 = server.goal_tactic(state1, goal_id=0, tactic=TacticExpr(\"fun h => h\"))\n", "print(state2)" ] }, { "cell_type": "markdown", "id": "d6bcd026-507b-4b1c-8dee-006df53636b0", "metadata": {}, "source": [ "To execute the `conv` tactic, use `server.goal_conv_begin` to enter conv mode on\n", "one goal, and use `server.goal_conv_end` to exit from conv mode. Pantograph will\n", "provide interactive feedback upon every tactic executed in `conv` mode." ] }, { "cell_type": "code", "execution_count": null, "id": "2b0b27c6-0c69-4255-aed1-c0713c227ccc", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.7" } }, "nbformat": 4, "nbformat_minor": 5 }