Skip to content

Commit c78734d

Browse files
authored
Merge pull request #143 from stanford-centaur/version/v0.3.8
chore: Update to v0.3.8
2 parents bd7db55 + 130cc75 commit c78734d

4 files changed

Lines changed: 74 additions & 5 deletions

File tree

pantograph/expr.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,11 @@ class Goal:
6464
mode: TacticMode = TacticMode.TACTIC
6565

6666
@staticmethod
67-
def sentence(target: Expr):
67+
def sentence(id: str, target: Expr):
6868
"""
6969
:meta public:
7070
"""
71-
return Goal(id=None, variables=[], target=target)
71+
return Goal(id=id, variables=[], target=target)
7272

7373
@staticmethod
7474
def parse(payload: dict, sibling_map: dict[str, int]):
@@ -158,6 +158,14 @@ def serial(self) -> dict:
158158
result["autoResume"] = self.auto_resume
159159
return result
160160

161+
class Subsumption(Enum):
162+
"""
163+
Subsumption result
164+
"""
165+
NONE = 1
166+
SUBSUMED = 2
167+
CYCLE = 3
168+
161169
@dataclass(frozen=True)
162170
class TacticHave:
163171
"""

pantograph/server.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
Goal,
1818
GoalState,
1919
Site,
20+
Subsumption,
2021
Tactic,
2122
TacticHave,
2223
TacticLet,
@@ -239,7 +240,7 @@ async def goal_start_async(self, expr: Expr) -> GoalState:
239240
raise ServerError(result)
240241
return GoalState(
241242
state_id=result["stateId"],
242-
goals=[Goal.sentence(expr)],
243+
goals=[Goal.sentence(result["root"], expr)],
243244
messages=[],
244245
_sentinel=self.to_remove_goal_states,
245246
)
@@ -342,6 +343,50 @@ async def goal_resume_async(self, state: GoalState, goals: list[Goal]) -> GoalSt
342343
return GoalState.parse(result, [], self.to_remove_goal_states)
343344
goal_resume = to_sync(goal_resume_async)
344345

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+
345390
async def env_add_async(
346391
self, name: str, levels: list[str],
347392
t: Expr, v: Expr, is_theorem: bool = True):

pantograph/test_server.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ def test_version(self):
77
"""
88
NOTE: Update this after upstream updates.
99
"""
10-
self.assertEqual(get_version(), "0.3.7")
10+
self.assertEqual(get_version(), "0.3.8")
1111

1212
def test_server_init_del(self):
1313
import warnings
@@ -198,6 +198,22 @@ def test_dependent_mvars(self):
198198
self.assertEqual(state.goals[0].sibling_dep, {1})
199199
self.assertEqual(state.goals[1].sibling_dep, set())
200200

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+
201217
def test_env_add_inspect(self):
202218
server = Server()
203219
server.env_add(

0 commit comments

Comments
 (0)