process-geometry 0.0.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. aeg_shakespeare/__init__.py +52 -0
  2. aeg_shakespeare/_legacy_api.py +299 -0
  3. aeg_shakespeare/analysis/__init__.py +12 -0
  4. aeg_shakespeare/analysis/abelian.py +69 -0
  5. aeg_shakespeare/analysis/algebraic.py +11 -0
  6. aeg_shakespeare/analysis/am.py +19 -0
  7. aeg_shakespeare/analysis/connection.py +74 -0
  8. aeg_shakespeare/analysis/decomposition.py +37 -0
  9. aeg_shakespeare/analysis/module.py +5 -0
  10. aeg_shakespeare/central.py +20 -0
  11. aeg_shakespeare/constraints.py +121 -0
  12. aeg_shakespeare/construction.py +303 -0
  13. aeg_shakespeare/core.py +59 -0
  14. aeg_shakespeare/cost.py +51 -0
  15. aeg_shakespeare/discovery/__init__.py +53 -0
  16. aeg_shakespeare/discovery/coefficient_extension.py +75 -0
  17. aeg_shakespeare/discovery/polynomial.py +342 -0
  18. aeg_shakespeare/discovery/selection.py +146 -0
  19. aeg_shakespeare/discovery/structured.py +201 -0
  20. aeg_shakespeare/families.py +31 -0
  21. aeg_shakespeare/frame.py +5 -0
  22. aeg_shakespeare/function_theory/__init__.py +100 -0
  23. aeg_shakespeare/function_theory/abel_jacobi.py +246 -0
  24. aeg_shakespeare/function_theory/abelian.py +129 -0
  25. aeg_shakespeare/function_theory/algebraic.py +105 -0
  26. aeg_shakespeare/function_theory/am.py +266 -0
  27. aeg_shakespeare/function_theory/intersection.py +321 -0
  28. aeg_shakespeare/function_theory/module.py +118 -0
  29. aeg_shakespeare/function_theory/period_matrix.py +155 -0
  30. aeg_shakespeare/function_theory/periods.py +216 -0
  31. aeg_shakespeare/function_theory/real_branch_cycles.py +286 -0
  32. aeg_shakespeare/function_theory/weierstrass.py +136 -0
  33. aeg_shakespeare/grammar.py +225 -0
  34. aeg_shakespeare/history_geometry.py +276 -0
  35. aeg_shakespeare/linear.py +70 -0
  36. aeg_shakespeare/presentation/__init__.py +23 -0
  37. aeg_shakespeare/presentation/budget.py +27 -0
  38. aeg_shakespeare/presentation/canonicalization.py +143 -0
  39. aeg_shakespeare/presentation/constraints.py +5 -0
  40. aeg_shakespeare/presentation/construction.py +19 -0
  41. aeg_shakespeare/presentation/grammar.py +15 -0
  42. aeg_shakespeare/presentation/history.py +45 -0
  43. aeg_shakespeare/presentation/morphism.py +66 -0
  44. aeg_shakespeare/presentation/relations.py +31 -0
  45. aeg_shakespeare/presentation/search.py +31 -0
  46. aeg_shakespeare/process/__init__.py +16 -0
  47. aeg_shakespeare/process/finite/__init__.py +43 -0
  48. aeg_shakespeare/process/finite/cocycle.py +166 -0
  49. aeg_shakespeare/process/finite/families.py +318 -0
  50. aeg_shakespeare/process/history.py +53 -0
  51. aeg_shakespeare/process/local/__init__.py +7 -0
  52. aeg_shakespeare/process/local/direction.py +88 -0
  53. aeg_shakespeare/process/local/frame.py +73 -0
  54. aeg_shakespeare/process/local/system.py +43 -0
  55. aeg_shakespeare/relations.py +374 -0
  56. aeg_shakespeare/rewrite.py +157 -0
  57. aeg_shakespeare/search.py +286 -0
  58. aeg_shakespeare/signature.py +155 -0
  59. process_geometry-0.0.3.dist-info/METADATA +305 -0
  60. process_geometry-0.0.3.dist-info/RECORD +63 -0
  61. process_geometry-0.0.3.dist-info/WHEEL +5 -0
  62. process_geometry-0.0.3.dist-info/licenses/LICENSE +24 -0
  63. process_geometry-0.0.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,286 @@
1
+ """Costed search over reusable process-presentation candidates.
2
+
3
+ The search layer does not define one universal scalar objective. It evaluates
4
+ candidate presentations into the public multi-axis ``PresentationCost`` and
5
+ returns the Pareto frontier. The symbolic adapters implement exact
6
+ reconstruction tasks over ``GeneratedPresentation`` objects; downstream users
7
+ can reuse the generic candidate/frontier machinery for history codes, task
8
+ quotients, or other presentation families.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from dataclasses import dataclass
14
+ import math
15
+ from typing import Callable, Generic, Sequence, TypeVar
16
+
17
+ import sympy as sp
18
+
19
+ from .construction import PrimitiveProposal
20
+ from .cost import PresentationCost
21
+ from .grammar import GeneratedPresentation, discover_generated_presentation
22
+ from .presentation.budget import SearchBudget
23
+ from .process.local import ProcessSystem
24
+ from .relations import decompose
25
+
26
+ PayloadT = TypeVar("PayloadT")
27
+
28
+
29
+ @dataclass(frozen=True)
30
+ class PresentationCandidate(Generic[PayloadT]):
31
+ """One evaluated representation candidate."""
32
+
33
+ payload: PayloadT
34
+ cost: PresentationCost
35
+ sufficient: bool = True
36
+ label: str | None = None
37
+ certificate: object | None = None
38
+
39
+
40
+ @dataclass(frozen=True)
41
+ class PresentationSearchResult(Generic[PayloadT]):
42
+ """All evaluated candidates and the task-sufficient Pareto frontier."""
43
+
44
+ evaluated: tuple[PresentationCandidate[PayloadT], ...]
45
+ pareto: tuple[PresentationCandidate[PayloadT], ...]
46
+
47
+ @property
48
+ def rejected(self) -> tuple[PresentationCandidate[PayloadT], ...]:
49
+ return tuple(candidate for candidate in self.evaluated if not candidate.sufficient)
50
+
51
+
52
+ def pareto_frontier(
53
+ candidates: Sequence[PresentationCandidate[PayloadT]],
54
+ *,
55
+ require_sufficient: bool = True,
56
+ ) -> tuple[PresentationCandidate[PayloadT], ...]:
57
+ """Return candidates not Pareto-dominated by another admissible candidate."""
58
+ admissible = tuple(
59
+ candidate
60
+ for candidate in candidates
61
+ if candidate.sufficient or not require_sufficient
62
+ )
63
+ frontier: list[PresentationCandidate[PayloadT]] = []
64
+ for candidate in admissible:
65
+ if any(
66
+ other is not candidate and other.cost.dominates(candidate.cost)
67
+ for other in admissible
68
+ ):
69
+ continue
70
+ frontier.append(candidate)
71
+ return tuple(frontier)
72
+
73
+
74
+ @dataclass(frozen=True)
75
+ class ExactReconstructionPresentation:
76
+ """Generated symbolic presentation evaluated for exact target decoding."""
77
+
78
+ proposal_seeds: tuple[sp.Expr, ...]
79
+ targets: tuple[sp.Expr, ...]
80
+ presentation: GeneratedPresentation
81
+ target_coordinates: tuple[tuple[sp.Expr, ...], ...]
82
+ failure: str | None = None
83
+
84
+ @property
85
+ def sufficient(self) -> bool:
86
+ return self.failure is None and len(self.target_coordinates) == len(self.targets)
87
+
88
+
89
+ @dataclass(frozen=True)
90
+ class ConstructedPrimitivePresentation:
91
+ """One construction-history-preserving primitive proposal after evaluation."""
92
+
93
+ proposal: PrimitiveProposal
94
+ evaluation: ExactReconstructionPresentation
95
+
96
+ @property
97
+ def sufficient(self) -> bool:
98
+ return self.evaluation.sufficient
99
+
100
+
101
+ def _expression_complexity(expr: sp.Expr) -> float:
102
+ """Small structural proxy used only by the default search cost model."""
103
+ expr = sp.expand(sp.sympify(expr))
104
+ return float(int(sp.count_ops(expr, visual=False)) + 1)
105
+
106
+
107
+ def structural_exact_reconstruction_cost(
108
+ candidate: ExactReconstructionPresentation,
109
+ ) -> PresentationCost:
110
+ """Default transparent structural cost for exact symbolic candidates."""
111
+ presentation = candidate.presentation
112
+ grammar_cost = sum(_expression_complexity(seed) for seed in candidate.proposal_seeds)
113
+ grammar_cost += sum(_expression_complexity(item) for item in presentation.primitives)
114
+
115
+ relation_cost = 0.0
116
+ if presentation.relations is not None:
117
+ relation_cost += sum(
118
+ 1.0
119
+ for coefficient in presentation.relations.global_relation.coefficients
120
+ if sp.simplify(coefficient) != 0
121
+ )
122
+ for component in presentation.relations.components:
123
+ relation_cost += sum(
124
+ 1.0
125
+ for coefficient in component.coefficients
126
+ if sp.simplify(coefficient) != 0
127
+ )
128
+
129
+ history_cost = float(sum(presentation.grammar.depths))
130
+ decoder_cost = float(
131
+ sum(
132
+ 1
133
+ for coordinates in candidate.target_coordinates
134
+ for coefficient in coordinates
135
+ if sp.simplify(coefficient) != 0
136
+ )
137
+ )
138
+
139
+ return PresentationCost(
140
+ grammar=grammar_cost,
141
+ relations=relation_cost,
142
+ history=history_cost,
143
+ decoder=decoder_cost,
144
+ task_error=0.0 if candidate.sufficient else math.inf,
145
+ )
146
+
147
+
148
+ def construction_aware_exact_reconstruction_cost(
149
+ candidate: ConstructedPrimitivePresentation,
150
+ ) -> PresentationCost:
151
+ """Baseline exact-reconstruction cost including proposal construction depth."""
152
+ base = structural_exact_reconstruction_cost(candidate.evaluation)
153
+ return PresentationCost(
154
+ grammar=base.grammar + candidate.proposal.cost,
155
+ relations=base.relations,
156
+ history=base.history,
157
+ decoder=base.decoder,
158
+ task_error=base.task_error,
159
+ )
160
+
161
+
162
+ def evaluate_exact_reconstruction_presentation(
163
+ system: ProcessSystem,
164
+ proposal_seeds: Sequence[sp.Expr],
165
+ targets: Sequence[sp.Expr],
166
+ *,
167
+ budget: SearchBudget | None = None,
168
+ ) -> ExactReconstructionPresentation:
169
+ """Build one generated presentation and certify exact target reconstruction."""
170
+ normalized_seeds = tuple(sp.expand(sp.sympify(seed)) for seed in proposal_seeds)
171
+ normalized_targets = tuple(sp.expand(sp.sympify(target)) for target in targets)
172
+ if not normalized_targets:
173
+ raise ValueError("at least one reconstruction target is required")
174
+
175
+ presentation = discover_generated_presentation(system, normalized_seeds, budget=budget)
176
+ if not presentation.complete:
177
+ return ExactReconstructionPresentation(
178
+ proposal_seeds=normalized_seeds,
179
+ targets=normalized_targets,
180
+ presentation=presentation,
181
+ target_coordinates=(),
182
+ failure="generated presentation is incomplete",
183
+ )
184
+
185
+ coordinates: list[tuple[sp.Expr, ...]] = []
186
+ try:
187
+ for target in normalized_targets:
188
+ coordinates.append(
189
+ decompose(target, presentation.primitives, system.assignments)
190
+ )
191
+ except ValueError as exc:
192
+ return ExactReconstructionPresentation(
193
+ proposal_seeds=normalized_seeds,
194
+ targets=normalized_targets,
195
+ presentation=presentation,
196
+ target_coordinates=tuple(coordinates),
197
+ failure=str(exc),
198
+ )
199
+
200
+ return ExactReconstructionPresentation(
201
+ proposal_seeds=normalized_seeds,
202
+ targets=normalized_targets,
203
+ presentation=presentation,
204
+ target_coordinates=tuple(coordinates),
205
+ )
206
+
207
+
208
+ def search_exact_reconstruction_presentations(
209
+ system: ProcessSystem,
210
+ seed_proposals: Sequence[Sequence[sp.Expr]],
211
+ targets: Sequence[sp.Expr],
212
+ *,
213
+ budget: SearchBudget | None = None,
214
+ cost_model: Callable[[ExactReconstructionPresentation], PresentationCost] | None = None,
215
+ ) -> PresentationSearchResult[ExactReconstructionPresentation]:
216
+ """Evaluate caller-proposed seed grammars and return their Pareto frontier."""
217
+ if not seed_proposals:
218
+ raise ValueError("at least one seed proposal is required")
219
+ score = cost_model or structural_exact_reconstruction_cost
220
+
221
+ evaluated: list[PresentationCandidate[ExactReconstructionPresentation]] = []
222
+ for index, proposal in enumerate(seed_proposals):
223
+ payload = evaluate_exact_reconstruction_presentation(
224
+ system,
225
+ proposal,
226
+ targets,
227
+ budget=budget,
228
+ )
229
+ cost = score(payload)
230
+ evaluated.append(
231
+ PresentationCandidate(
232
+ payload=payload,
233
+ cost=cost,
234
+ sufficient=payload.sufficient,
235
+ label=f"proposal-{index}",
236
+ certificate=payload.failure or payload.target_coordinates,
237
+ )
238
+ )
239
+
240
+ evaluated_tuple = tuple(evaluated)
241
+ return PresentationSearchResult(
242
+ evaluated=evaluated_tuple,
243
+ pareto=pareto_frontier(evaluated_tuple),
244
+ )
245
+
246
+
247
+ def search_primitive_proposals(
248
+ system: ProcessSystem,
249
+ proposals: Sequence[PrimitiveProposal],
250
+ targets: Sequence[sp.Expr],
251
+ *,
252
+ budget: SearchBudget | None = None,
253
+ cost_model: Callable[[ConstructedPrimitivePresentation], PresentationCost] | None = None,
254
+ ) -> PresentationSearchResult[ConstructedPrimitivePresentation]:
255
+ """Evaluate construction-preserving primitive proposals as one-seed grammars."""
256
+ if not proposals:
257
+ raise ValueError("at least one primitive proposal is required")
258
+ score = cost_model or construction_aware_exact_reconstruction_cost
259
+
260
+ evaluated: list[PresentationCandidate[ConstructedPrimitivePresentation]] = []
261
+ for proposal in proposals:
262
+ evaluation = evaluate_exact_reconstruction_presentation(
263
+ system,
264
+ (proposal.expression,),
265
+ targets,
266
+ budget=budget,
267
+ )
268
+ payload = ConstructedPrimitivePresentation(
269
+ proposal=proposal,
270
+ evaluation=evaluation,
271
+ )
272
+ evaluated.append(
273
+ PresentationCandidate(
274
+ payload=payload,
275
+ cost=score(payload),
276
+ sufficient=payload.sufficient,
277
+ label=proposal.construction.recipe(),
278
+ certificate=evaluation.failure or evaluation.target_coordinates,
279
+ )
280
+ )
281
+
282
+ evaluated_tuple = tuple(evaluated)
283
+ return PresentationSearchResult(
284
+ evaluated=evaluated_tuple,
285
+ pareto=pareto_frontier(evaluated_tuple),
286
+ )
@@ -0,0 +1,155 @@
1
+ """Finite future signatures for task-sufficient process quotients.
2
+
3
+ A process representation should not merge histories merely because their
4
+ current symbolic values happen to match. This module provides a bounded test:
5
+ two histories are indistinguishable for a declared task only when their task
6
+ observations agree under every allowed continuation up to a finite depth.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from dataclasses import dataclass
12
+ from itertools import product
13
+ from typing import Callable, Generic, Sequence, TypeVar
14
+
15
+ from .process.history import ProcessWord, interpret_history
16
+
17
+ StepT = TypeVar("StepT")
18
+ StateT = TypeVar("StateT")
19
+ ObservationT = TypeVar("ObservationT")
20
+
21
+
22
+ @dataclass(frozen=True)
23
+ class ProcessJetSignature(Generic[StepT, ObservationT]):
24
+ """Task observations under all bounded process continuations."""
25
+
26
+ depth: int
27
+ entries: tuple[tuple[ProcessWord[StepT], ObservationT], ...]
28
+
29
+ @property
30
+ def observations(self) -> tuple[ObservationT, ...]:
31
+ return tuple(observation for _word, observation in self.entries)
32
+
33
+
34
+ def enumerate_process_words(
35
+ steps: Sequence[StepT],
36
+ max_depth: int,
37
+ ) -> tuple[ProcessWord[StepT], ...]:
38
+ """Enumerate the free ordered continuation tree through ``max_depth``.
39
+
40
+ The empty continuation is included first. No commutativity or relation
41
+ quotient is assumed; callers may normalize words separately when that is
42
+ part of the chosen presentation.
43
+ """
44
+
45
+ if max_depth < 0:
46
+ raise ValueError("max_depth must be non-negative")
47
+ alphabet = tuple(steps)
48
+ words: list[ProcessWord[StepT]] = [ProcessWord()]
49
+ for depth in range(1, max_depth + 1):
50
+ words.extend(ProcessWord(tuple(items)) for items in product(alphabet, repeat=depth))
51
+ return tuple(words)
52
+
53
+
54
+ def process_jet_signature(
55
+ state: StateT,
56
+ steps: Sequence[StepT],
57
+ transition: Callable[[StateT, StepT], StateT],
58
+ observe: Callable[[StateT], ObservationT],
59
+ *,
60
+ depth: int,
61
+ ) -> ProcessJetSignature[StepT, ObservationT]:
62
+ """Compute a finite task signature from a current state."""
63
+
64
+ continuations = enumerate_process_words(steps, depth)
65
+ entries = tuple(
66
+ (
67
+ continuation,
68
+ observe(interpret_history(continuation, state, transition)),
69
+ )
70
+ for continuation in continuations
71
+ )
72
+ return ProcessJetSignature(depth=depth, entries=entries)
73
+
74
+
75
+ def history_process_jet_signature(
76
+ history: ProcessWord[StepT],
77
+ initial: StateT,
78
+ steps: Sequence[StepT],
79
+ transition: Callable[[StateT, StepT], StateT],
80
+ observe: Callable[[StateT], ObservationT],
81
+ *,
82
+ depth: int,
83
+ ) -> ProcessJetSignature[StepT, ObservationT]:
84
+ """Compute the finite task signature after one literal process history."""
85
+
86
+ state = interpret_history(history, initial, transition)
87
+ return process_jet_signature(
88
+ state,
89
+ steps,
90
+ transition,
91
+ observe,
92
+ depth=depth,
93
+ )
94
+
95
+
96
+ def signatures_equivalent(
97
+ left: ProcessJetSignature[StepT, ObservationT],
98
+ right: ProcessJetSignature[StepT, ObservationT],
99
+ *,
100
+ equivalent: Callable[[ObservationT, ObservationT], bool] | None = None,
101
+ ) -> bool:
102
+ """Compare two finite task signatures with an optional observation metric."""
103
+
104
+ if left.depth != right.depth or len(left.entries) != len(right.entries):
105
+ return False
106
+ compare = equivalent or (lambda a, b: bool(a == b))
107
+ for (left_word, left_value), (right_word, right_value) in zip(
108
+ left.entries, right.entries
109
+ ):
110
+ if left_word != right_word:
111
+ return False
112
+ if not compare(left_value, right_value):
113
+ return False
114
+ return True
115
+
116
+
117
+ def histories_task_equivalent(
118
+ left: ProcessWord[StepT],
119
+ right: ProcessWord[StepT],
120
+ initial: StateT,
121
+ steps: Sequence[StepT],
122
+ transition: Callable[[StateT, StepT], StateT],
123
+ observe: Callable[[StateT], ObservationT],
124
+ *,
125
+ depth: int,
126
+ equivalent: Callable[[ObservationT, ObservationT], bool] | None = None,
127
+ ) -> bool:
128
+ """Bounded task congruence test for two literal histories.
129
+
130
+ Equality of current task observations is not enough. The histories are
131
+ merged only when every allowed continuation up to ``depth`` remains
132
+ task-indistinguishable.
133
+ """
134
+
135
+ left_signature = history_process_jet_signature(
136
+ left,
137
+ initial,
138
+ steps,
139
+ transition,
140
+ observe,
141
+ depth=depth,
142
+ )
143
+ right_signature = history_process_jet_signature(
144
+ right,
145
+ initial,
146
+ steps,
147
+ transition,
148
+ observe,
149
+ depth=depth,
150
+ )
151
+ return signatures_equivalent(
152
+ left_signature,
153
+ right_signature,
154
+ equivalent=equivalent,
155
+ )