sofic 0.1.0__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 (150) hide show
  1. sofic/__init__.py +185 -0
  2. sofic/automata/__init__.py +207 -0
  3. sofic/automata/_config_simulation.py +40 -0
  4. sofic/automata/active.py +611 -0
  5. sofic/automata/alergia.py +222 -0
  6. sofic/automata/algorithms.py +376 -0
  7. sofic/automata/atomaton.py +58 -0
  8. sofic/automata/base.py +161 -0
  9. sofic/automata/buchi.py +23 -0
  10. sofic/automata/buchi_simulation.py +67 -0
  11. sofic/automata/canonical_dual.py +18 -0
  12. sofic/automata/canonical_extraction.py +122 -0
  13. sofic/automata/dfa.py +85 -0
  14. sofic/automata/dfasat.py +195 -0
  15. sofic/automata/edsm.py +219 -0
  16. sofic/automata/enumeration.py +44 -0
  17. sofic/automata/icdfa.py +421 -0
  18. sofic/automata/idfa.py +363 -0
  19. sofic/automata/languages/__init__.py +39 -0
  20. sofic/automata/languages/_quotient_utils.py +64 -0
  21. sofic/automata/languages/atoms.py +31 -0
  22. sofic/automata/languages/automaton_ops.py +243 -0
  23. sofic/automata/languages/base.py +67 -0
  24. sofic/automata/languages/operations.py +78 -0
  25. sofic/automata/languages/quotients.py +66 -0
  26. sofic/automata/languages/residuals.py +25 -0
  27. sofic/automata/learning.py +79 -0
  28. sofic/automata/nfa.py +39 -0
  29. sofic/automata/nwa.py +343 -0
  30. sofic/automata/nwa_simulation.py +56 -0
  31. sofic/automata/observation.py +40 -0
  32. sofic/automata/papni.py +301 -0
  33. sofic/automata/regex.py +128 -0
  34. sofic/automata/rfsa.py +35 -0
  35. sofic/automata/rpni.py +193 -0
  36. sofic/automata/subsequential.py +201 -0
  37. sofic/automata/transducer_operations.py +350 -0
  38. sofic/automata/transducer_simulation.py +150 -0
  39. sofic/automata/transducers.py +365 -0
  40. sofic/automata/unifilar.py +107 -0
  41. sofic/automata/vpa.py +1373 -0
  42. sofic/automata/vpa_simulation.py +53 -0
  43. sofic/base.py +153 -0
  44. sofic/core.py +47 -0
  45. sofic/examples/__init__.py +86 -0
  46. sofic/examples/epsilon_machines.py +1089 -0
  47. sofic/examples/processes.py +1491 -0
  48. sofic/examples/shifts.py +144 -0
  49. sofic/exceptions.py +33 -0
  50. sofic/generators/__init__.py +115 -0
  51. sofic/generators/_word_measures.py +94 -0
  52. sofic/generators/alternative_complexity.py +104 -0
  53. sofic/generators/base.py +327 -0
  54. sofic/generators/bidirectional_construction.py +717 -0
  55. sofic/generators/bidirectional_epsilon_machine.py +689 -0
  56. sofic/generators/block_convergence.py +668 -0
  57. sofic/generators/block_entropy.py +578 -0
  58. sofic/generators/channel_measures.py +75 -0
  59. sofic/generators/conversions.py +182 -0
  60. sofic/generators/directional_flow.py +245 -0
  61. sofic/generators/edge_emissions.py +36 -0
  62. sofic/generators/edge_machine.py +178 -0
  63. sofic/generators/epsilon_construction.py +193 -0
  64. sofic/generators/epsilon_inference.py +703 -0
  65. sofic/generators/epsilon_machine.py +557 -0
  66. sofic/generators/epsilon_transducer.py +168 -0
  67. sofic/generators/epsilon_transducer_construction.py +185 -0
  68. sofic/generators/epsilon_transducer_inference.py +499 -0
  69. sofic/generators/hmm_inference.py +719 -0
  70. sofic/generators/information_diagram.py +428 -0
  71. sofic/generators/lumping.py +447 -0
  72. sofic/generators/markov.py +100 -0
  73. sofic/generators/mealy.py +156 -0
  74. sofic/generators/measures.py +257 -0
  75. sofic/generators/minimal_generative_model.py +821 -0
  76. sofic/generators/mixed_state.py +250 -0
  77. sofic/generators/mixed_state_construction.py +163 -0
  78. sofic/generators/moore.py +75 -0
  79. sofic/generators/nmachine.py +78 -0
  80. sofic/generators/nmachine_construction.py +70 -0
  81. sofic/generators/pfa.py +100 -0
  82. sofic/generators/prob.py +291 -0
  83. sofic/generators/process_equivalence.py +207 -0
  84. sofic/generators/quasi_inference.py +74 -0
  85. sofic/generators/quasi_realization.py +97 -0
  86. sofic/generators/reversal.py +66 -0
  87. sofic/generators/stack_hmm.py +426 -0
  88. sofic/generators/stack_inference.py +509 -0
  89. sofic/generators/stationary.py +134 -0
  90. sofic/generators/stochastic.py +65 -0
  91. sofic/generators/synchronization.py +407 -0
  92. sofic/generators/topological_epsilon_enumeration.py +349 -0
  93. sofic/generators/words.py +226 -0
  94. sofic/graph.py +135 -0
  95. sofic/indexing.py +31 -0
  96. sofic/inference/__init__.py +45 -0
  97. sofic/inference/bayesian/__init__.py +68 -0
  98. sofic/inference/bayesian/comparison.py +199 -0
  99. sofic/inference/bayesian/counts.py +219 -0
  100. sofic/inference/bayesian/diversity.py +254 -0
  101. sofic/inference/bayesian/epsilon.py +270 -0
  102. sofic/inference/bayesian/hdp_hmm.py +340 -0
  103. sofic/inference/bayesian/markov.py +294 -0
  104. sofic/inference/bayesian/pymc_backend.py +71 -0
  105. sofic/inference/bayesian/stack_hmm.py +215 -0
  106. sofic/inference/model_selection.py +365 -0
  107. sofic/inference/spectral.py +564 -0
  108. sofic/operations.py +16 -0
  109. sofic/properties.py +339 -0
  110. sofic/serialization.py +450 -0
  111. sofic/shifts/__init__.py +48 -0
  112. sofic/shifts/algorithms.py +84 -0
  113. sofic/shifts/base.py +49 -0
  114. sofic/shifts/cover_construction.py +76 -0
  115. sofic/shifts/covers.py +47 -0
  116. sofic/shifts/dyck_algorithms.py +100 -0
  117. sofic/shifts/dyck_enumeration.py +275 -0
  118. sofic/shifts/markov_dyck.py +172 -0
  119. sofic/shifts/parry_construction.py +82 -0
  120. sofic/shifts/sft.py +104 -0
  121. sofic/shifts/sft_construction.py +52 -0
  122. sofic/shifts/sliding_block_code.py +156 -0
  123. sofic/shifts/sofic.py +111 -0
  124. sofic/shifts/sofic_dyck.py +110 -0
  125. sofic/shifts/sofic_relation.py +64 -0
  126. sofic/shifts/textile.py +104 -0
  127. sofic/shifts/tmc.py +46 -0
  128. sofic/shifts/tmc_construction.py +58 -0
  129. sofic/shifts/topological_anatomy.py +150 -0
  130. sofic/states.py +27 -0
  131. sofic/testing/__init__.py +8 -0
  132. sofic/testing/strategies.py +154 -0
  133. sofic/viz/__init__.py +16 -0
  134. sofic/viz/_context.py +345 -0
  135. sofic/viz/_edge.py +216 -0
  136. sofic/viz/_format.py +89 -0
  137. sofic/viz/_labels.py +34 -0
  138. sofic/viz/_names.py +17 -0
  139. sofic/viz/_rational.py +20 -0
  140. sofic/viz/_tikz_compile.py +177 -0
  141. sofic/viz/_tikz_format.py +122 -0
  142. sofic/viz/_tikz_layout.py +218 -0
  143. sofic/viz/assets/vaucanson.tikz +71 -0
  144. sofic/viz/graphviz.py +158 -0
  145. sofic/viz/idiagram.py +350 -0
  146. sofic/viz/tikz.py +381 -0
  147. sofic-0.1.0.dist-info/METADATA +444 -0
  148. sofic-0.1.0.dist-info/RECORD +150 -0
  149. sofic-0.1.0.dist-info/WHEEL +4 -0
  150. sofic-0.1.0.dist-info/licenses/LICENSE.txt +29 -0
sofic/__init__.py ADDED
@@ -0,0 +1,185 @@
1
+ """
2
+ sofic is a Python package for hidden Markov models, symbolic dynamics,
3
+ finite state machines, and other stochastic symbol generators.
4
+ """
5
+
6
+ try:
7
+ from importlib.metadata import version
8
+
9
+ __version__ = version("sofic")
10
+ except Exception:
11
+ __version__ = "0.0.0"
12
+
13
+ from sofic.automata import (
14
+ DFA,
15
+ NFA,
16
+ Atomaton,
17
+ AtomicAutomaton,
18
+ AutomatonLanguage,
19
+ BuchiAutomaton,
20
+ CanonicalRFSA,
21
+ LabeledAutomaton,
22
+ MaximizedPrimeAtomaton,
23
+ MealyMachine,
24
+ MinimizationAlgorithm,
25
+ MooreMachine,
26
+ NestedWord,
27
+ NestedWordAutomaton,
28
+ ObservationTable,
29
+ RegularLanguage,
30
+ ResidualFiniteStateAutomaton,
31
+ SubsequentialTransducer,
32
+ Transducer,
33
+ UnifilarAutomaton,
34
+ VisiblyPushdownAutomaton,
35
+ WeightedFiniteStateTransducer,
36
+ automaton_to_regex,
37
+ cartesian_product_gg,
38
+ cartesian_product_tt,
39
+ complete,
40
+ compose_tg,
41
+ compose_tt,
42
+ determinize,
43
+ equivalent,
44
+ minimize,
45
+ transduce_generator,
46
+ trim,
47
+ )
48
+ from sofic.core import EPSILON, StateIndex, StateMachine, Transition, TransitionGraph
49
+ from sofic.generators import (
50
+ BidirectionalEpsilonMachine,
51
+ BlockConvergenceDiagram,
52
+ BlockConvergenceEstimates,
53
+ BlockEntropyDiagram,
54
+ BlockEntropyEstimates,
55
+ EpsilonMachine,
56
+ EpsilonTransducer,
57
+ FunctionalGenerativeModel,
58
+ GacsKornerGenerativeModel,
59
+ HiddenMarkovModel,
60
+ HiddenMarkovStackModel,
61
+ MarkovChain,
62
+ MealyHMM,
63
+ MinimalGenerativeModel,
64
+ MixedState,
65
+ MixedStatePresentation,
66
+ MooreHMM,
67
+ NMachine,
68
+ ProbabilisticFiniteAutomaton,
69
+ QuasiRealization,
70
+ QuasiStochasticModel,
71
+ StochasticModel,
72
+ WynerGenerativeModel,
73
+ functional_generative_model,
74
+ gacs_korner_generative_model,
75
+ is_lumpable,
76
+ lump,
77
+ minimal_generative_model,
78
+ wyner_generative_model,
79
+ )
80
+ from sofic.operations import reverse
81
+ from sofic.serialization import from_yaml, model_from_yaml, model_to_yaml, read_yaml
82
+ from sofic.shifts import (
83
+ LeftFischerCover,
84
+ LeftKriegerCover,
85
+ MarkovDyckShift,
86
+ RightFischerCover,
87
+ RightKriegerCover,
88
+ ShiftOfFiniteType,
89
+ SlidingBlockCode,
90
+ SoficDyckShift,
91
+ SoficRelation,
92
+ SoficShift,
93
+ SymbolicModel,
94
+ TextileSystem,
95
+ TopologicalMarkovChain,
96
+ )
97
+
98
+ __all__ = [
99
+ "Atomaton",
100
+ "AtomicAutomaton",
101
+ "AutomatonLanguage",
102
+ "BuchiAutomaton",
103
+ "BidirectionalEpsilonMachine",
104
+ "BlockEntropyDiagram",
105
+ "BlockEntropyEstimates",
106
+ "BlockConvergenceDiagram",
107
+ "BlockConvergenceEstimates",
108
+ "CanonicalRFSA",
109
+ "DFA",
110
+ "EPSILON",
111
+ "EpsilonMachine",
112
+ "EpsilonTransducer",
113
+ "FunctionalGenerativeModel",
114
+ "GacsKornerGenerativeModel",
115
+ "HiddenMarkovModel",
116
+ "HiddenMarkovStackModel",
117
+ "LabeledAutomaton",
118
+ "LeftFischerCover",
119
+ "LeftKriegerCover",
120
+ "MarkovDyckShift",
121
+ "MarkovChain",
122
+ "MaximizedPrimeAtomaton",
123
+ "MealyHMM",
124
+ "MinimalGenerativeModel",
125
+ "MealyMachine",
126
+ "MinimizationAlgorithm",
127
+ "MixedState",
128
+ "MixedStatePresentation",
129
+ "MooreHMM",
130
+ "MooreMachine",
131
+ "NFA",
132
+ "NestedWord",
133
+ "NestedWordAutomaton",
134
+ "NMachine",
135
+ "ObservationTable",
136
+ "ProbabilisticFiniteAutomaton",
137
+ "QuasiRealization",
138
+ "QuasiStochasticModel",
139
+ "RegularLanguage",
140
+ "ResidualFiniteStateAutomaton",
141
+ "RightFischerCover",
142
+ "RightKriegerCover",
143
+ "ShiftOfFiniteType",
144
+ "SlidingBlockCode",
145
+ "SoficDyckShift",
146
+ "SoficRelation",
147
+ "SoficShift",
148
+ "StateIndex",
149
+ "StateMachine",
150
+ "StochasticModel",
151
+ "SubsequentialTransducer",
152
+ "SymbolicModel",
153
+ "TextileSystem",
154
+ "TopologicalMarkovChain",
155
+ "Transducer",
156
+ "Transition",
157
+ "TransitionGraph",
158
+ "UnifilarAutomaton",
159
+ "VisiblyPushdownAutomaton",
160
+ "WeightedFiniteStateTransducer",
161
+ "WynerGenerativeModel",
162
+ "automaton_to_regex",
163
+ "cartesian_product_gg",
164
+ "cartesian_product_tt",
165
+ "complete",
166
+ "compose_tg",
167
+ "compose_tt",
168
+ "determinize",
169
+ "equivalent",
170
+ "minimize",
171
+ "from_yaml",
172
+ "functional_generative_model",
173
+ "gacs_korner_generative_model",
174
+ "is_lumpable",
175
+ "lump",
176
+ "model_from_yaml",
177
+ "model_to_yaml",
178
+ "minimal_generative_model",
179
+ "read_yaml",
180
+ "reverse",
181
+ "transduce_generator",
182
+ "trim",
183
+ "wyner_generative_model",
184
+ "__version__",
185
+ ]
@@ -0,0 +1,207 @@
1
+ """Finite automata and transducers."""
2
+
3
+ # ``atomaton`` is an intentional pun on atomic automaton.
4
+ from sofic.automata.active import (
5
+ EquivalenceOracle,
6
+ ExhaustiveEquivalenceOracle,
7
+ FunctionMealyOracle,
8
+ FunctionMembershipOracle,
9
+ LanguageMembershipOracle,
10
+ MealyEquivalenceOracle,
11
+ MealyExhaustiveEquivalenceOracle,
12
+ MealyMembershipOracle,
13
+ MembershipOracle,
14
+ RandomWalkEquivalenceOracle,
15
+ TransducerOutputOracle,
16
+ learn_dfa_from_language,
17
+ learn_dfa_lstar,
18
+ learn_dfa_ttt,
19
+ learn_mealy_from_transducer,
20
+ learn_mealy_lstar,
21
+ )
22
+ from sofic.automata.alergia import learn_pfa_alergia
23
+ from sofic.automata.algorithms import (
24
+ MinimizationAlgorithm,
25
+ complete,
26
+ determinize,
27
+ equivalent,
28
+ minimize,
29
+ trim,
30
+ )
31
+ from sofic.automata.atomaton import Atomaton, AtomicAutomaton, MaximizedPrimeAtomaton
32
+ from sofic.automata.base import LabeledAutomaton
33
+ from sofic.automata.buchi import BuchiAutomaton
34
+ from sofic.automata.dfa import DFA
35
+ from sofic.automata.dfasat import learn_dfa_sat
36
+ from sofic.automata.edsm import learn_dfa_edsm
37
+ from sofic.automata.icdfa import (
38
+ ICDFAString,
39
+ count_flag_sequences,
40
+ count_icdfa,
41
+ count_icdfa_empty,
42
+ dfa_to_icdfa_string,
43
+ first_icdfa_empty_string,
44
+ flags_from_string,
45
+ icdfa_string_to_dfa,
46
+ iter_icdfa,
47
+ iter_icdfa_empty_strings,
48
+ last_icdfa_empty_string,
49
+ next_flags,
50
+ next_icdfa_empty_string,
51
+ string_from_flags,
52
+ validate_icdfa_empty_string,
53
+ )
54
+ from sofic.automata.idfa import (
55
+ MISSING_TRANSITION,
56
+ count_accessible_idfa,
57
+ first_idfa_string,
58
+ iter_idfa_strings,
59
+ rank_idfa_string,
60
+ reroot_idfa_string,
61
+ unrank_idfa_string,
62
+ validate_idfa_string,
63
+ )
64
+ from sofic.automata.languages import AutomatonLanguage, RegularLanguage
65
+ from sofic.automata.nfa import NFA
66
+ from sofic.automata.nwa import NestedWord, NestedWordAutomaton
67
+ from sofic.automata.observation import ObservationTable
68
+ from sofic.automata.papni import (
69
+ DyckAlphabet,
70
+ is_well_matched,
71
+ learn_sofic_dyck_shift_papni,
72
+ papni_encode,
73
+ papni_encode_samples,
74
+ sofic_dyck_shift_from_papni_dfa,
75
+ )
76
+ from sofic.automata.regex import automaton_to_regex
77
+ from sofic.automata.rfsa import CanonicalRFSA, ResidualFiniteStateAutomaton
78
+ from sofic.automata.rpni import learn_dfa_rpni
79
+ from sofic.automata.subsequential import (
80
+ SubsequentialTransducer,
81
+ WeightedFiniteStateTransducer,
82
+ )
83
+ from sofic.automata.transducer_operations import (
84
+ ERROR_STATE,
85
+ ERROR_SYMBOL,
86
+ cartesian_product_gg,
87
+ cartesian_product_tt,
88
+ compose_tg,
89
+ compose_tt,
90
+ transduce_generator,
91
+ )
92
+ from sofic.automata.transducers import MealyMachine, MooreMachine, Transducer
93
+ from sofic.automata.unifilar import UnifilarAutomaton
94
+ from sofic.automata.vpa import (
95
+ CallDrivenAutomaton,
96
+ CanonicalVisiblyPushdownAutomaton,
97
+ CompositeVisiblyPushdownAutomaton,
98
+ DeterministicVisiblyPushdownAutomaton,
99
+ MultipleEntryVisiblyPushdownAutomaton,
100
+ SingleEntryVisiblyPushdownAutomaton,
101
+ VisiblyPushdownAutomaton,
102
+ complement_vpa,
103
+ concat_vpa,
104
+ difference_vpa,
105
+ intersection_vpa,
106
+ kleene_star_vpa,
107
+ union_vpa,
108
+ )
109
+
110
+ __all__ = [
111
+ "Atomaton",
112
+ "AtomicAutomaton",
113
+ "AutomatonLanguage",
114
+ "BuchiAutomaton",
115
+ "EquivalenceOracle",
116
+ "ExhaustiveEquivalenceOracle",
117
+ "FunctionMealyOracle",
118
+ "FunctionMembershipOracle",
119
+ "LanguageMembershipOracle",
120
+ "MealyEquivalenceOracle",
121
+ "MealyExhaustiveEquivalenceOracle",
122
+ "MealyMembershipOracle",
123
+ "MembershipOracle",
124
+ "RandomWalkEquivalenceOracle",
125
+ "TransducerOutputOracle",
126
+ "CallDrivenAutomaton",
127
+ "CanonicalVisiblyPushdownAutomaton",
128
+ "CanonicalRFSA",
129
+ "CompositeVisiblyPushdownAutomaton",
130
+ "DFA",
131
+ "DeterministicVisiblyPushdownAutomaton",
132
+ "DyckAlphabet",
133
+ "ERROR_STATE",
134
+ "ERROR_SYMBOL",
135
+ "ICDFAString",
136
+ "LabeledAutomaton",
137
+ "MaximizedPrimeAtomaton",
138
+ "MealyMachine",
139
+ "MinimizationAlgorithm",
140
+ "MooreMachine",
141
+ "MultipleEntryVisiblyPushdownAutomaton",
142
+ "NFA",
143
+ "NestedWord",
144
+ "NestedWordAutomaton",
145
+ "ObservationTable",
146
+ "RegularLanguage",
147
+ "ResidualFiniteStateAutomaton",
148
+ "SingleEntryVisiblyPushdownAutomaton",
149
+ "SubsequentialTransducer",
150
+ "Transducer",
151
+ "UnifilarAutomaton",
152
+ "WeightedFiniteStateTransducer",
153
+ "VisiblyPushdownAutomaton",
154
+ "automaton_to_regex",
155
+ "cartesian_product_gg",
156
+ "cartesian_product_tt",
157
+ "complement_vpa",
158
+ "compose_tg",
159
+ "compose_tt",
160
+ "concat_vpa",
161
+ "complete",
162
+ "count_accessible_idfa",
163
+ "count_flag_sequences",
164
+ "count_icdfa",
165
+ "count_icdfa_empty",
166
+ "determinize",
167
+ "difference_vpa",
168
+ "dfa_to_icdfa_string",
169
+ "equivalent",
170
+ "first_icdfa_empty_string",
171
+ "first_idfa_string",
172
+ "flags_from_string",
173
+ "icdfa_string_to_dfa",
174
+ "intersection_vpa",
175
+ "is_well_matched",
176
+ "iter_icdfa",
177
+ "iter_icdfa_empty_strings",
178
+ "iter_idfa_strings",
179
+ "last_icdfa_empty_string",
180
+ "MISSING_TRANSITION",
181
+ "kleene_star_vpa",
182
+ "learn_dfa_edsm",
183
+ "learn_dfa_from_language",
184
+ "learn_dfa_lstar",
185
+ "learn_dfa_rpni",
186
+ "learn_dfa_sat",
187
+ "learn_dfa_ttt",
188
+ "learn_mealy_from_transducer",
189
+ "learn_mealy_lstar",
190
+ "learn_pfa_alergia",
191
+ "learn_sofic_dyck_shift_papni",
192
+ "minimize",
193
+ "next_flags",
194
+ "next_icdfa_empty_string",
195
+ "papni_encode",
196
+ "papni_encode_samples",
197
+ "rank_idfa_string",
198
+ "reroot_idfa_string",
199
+ "sofic_dyck_shift_from_papni_dfa",
200
+ "string_from_flags",
201
+ "trim",
202
+ "transduce_generator",
203
+ "unrank_idfa_string",
204
+ "union_vpa",
205
+ "validate_idfa_string",
206
+ "validate_icdfa_empty_string",
207
+ ]
@@ -0,0 +1,40 @@
1
+ """Shared configuration-set simulation driver for stack/output machines.
2
+
3
+ VPA, NWA, and transducer simulations all advance a set of ``(state, extra)``
4
+ configurations one input step at a time, unioning the successors produced by a
5
+ per-machine step function and stopping if the set ever empties. This captures
6
+ that loop; the per-kind transition logic stays with each caller.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from collections.abc import Callable, Iterable
12
+ from typing import TypeVar
13
+
14
+ Config = TypeVar("Config")
15
+ Step = TypeVar("Step")
16
+
17
+
18
+ def simulate_configs(
19
+ initial: set[Config],
20
+ steps: Iterable[Step],
21
+ step_fn: Callable[[Config, Step], Iterable[Config]],
22
+ *,
23
+ closure: Callable[[set[Config]], set[Config]] | None = None,
24
+ ) -> set[Config]:
25
+ """Advance ``initial`` through ``steps`` via ``step_fn``.
26
+
27
+ ``step_fn(config, step)`` yields successor configurations for one input step.
28
+ An optional ``closure`` is applied to the seed set and after every step (used
29
+ by the transducer's epsilon-input closure). Returns the final configuration
30
+ set, which is empty if the machine had no successors at some step.
31
+ """
32
+ current = closure(initial) if closure is not None else initial
33
+ for step in steps:
34
+ nxt: set[Config] = set()
35
+ for config in current:
36
+ nxt.update(step_fn(config, step))
37
+ current = closure(nxt) if closure is not None else nxt
38
+ if not current:
39
+ return set()
40
+ return current