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.
- sofic/__init__.py +185 -0
- sofic/automata/__init__.py +207 -0
- sofic/automata/_config_simulation.py +40 -0
- sofic/automata/active.py +611 -0
- sofic/automata/alergia.py +222 -0
- sofic/automata/algorithms.py +376 -0
- sofic/automata/atomaton.py +58 -0
- sofic/automata/base.py +161 -0
- sofic/automata/buchi.py +23 -0
- sofic/automata/buchi_simulation.py +67 -0
- sofic/automata/canonical_dual.py +18 -0
- sofic/automata/canonical_extraction.py +122 -0
- sofic/automata/dfa.py +85 -0
- sofic/automata/dfasat.py +195 -0
- sofic/automata/edsm.py +219 -0
- sofic/automata/enumeration.py +44 -0
- sofic/automata/icdfa.py +421 -0
- sofic/automata/idfa.py +363 -0
- sofic/automata/languages/__init__.py +39 -0
- sofic/automata/languages/_quotient_utils.py +64 -0
- sofic/automata/languages/atoms.py +31 -0
- sofic/automata/languages/automaton_ops.py +243 -0
- sofic/automata/languages/base.py +67 -0
- sofic/automata/languages/operations.py +78 -0
- sofic/automata/languages/quotients.py +66 -0
- sofic/automata/languages/residuals.py +25 -0
- sofic/automata/learning.py +79 -0
- sofic/automata/nfa.py +39 -0
- sofic/automata/nwa.py +343 -0
- sofic/automata/nwa_simulation.py +56 -0
- sofic/automata/observation.py +40 -0
- sofic/automata/papni.py +301 -0
- sofic/automata/regex.py +128 -0
- sofic/automata/rfsa.py +35 -0
- sofic/automata/rpni.py +193 -0
- sofic/automata/subsequential.py +201 -0
- sofic/automata/transducer_operations.py +350 -0
- sofic/automata/transducer_simulation.py +150 -0
- sofic/automata/transducers.py +365 -0
- sofic/automata/unifilar.py +107 -0
- sofic/automata/vpa.py +1373 -0
- sofic/automata/vpa_simulation.py +53 -0
- sofic/base.py +153 -0
- sofic/core.py +47 -0
- sofic/examples/__init__.py +86 -0
- sofic/examples/epsilon_machines.py +1089 -0
- sofic/examples/processes.py +1491 -0
- sofic/examples/shifts.py +144 -0
- sofic/exceptions.py +33 -0
- sofic/generators/__init__.py +115 -0
- sofic/generators/_word_measures.py +94 -0
- sofic/generators/alternative_complexity.py +104 -0
- sofic/generators/base.py +327 -0
- sofic/generators/bidirectional_construction.py +717 -0
- sofic/generators/bidirectional_epsilon_machine.py +689 -0
- sofic/generators/block_convergence.py +668 -0
- sofic/generators/block_entropy.py +578 -0
- sofic/generators/channel_measures.py +75 -0
- sofic/generators/conversions.py +182 -0
- sofic/generators/directional_flow.py +245 -0
- sofic/generators/edge_emissions.py +36 -0
- sofic/generators/edge_machine.py +178 -0
- sofic/generators/epsilon_construction.py +193 -0
- sofic/generators/epsilon_inference.py +703 -0
- sofic/generators/epsilon_machine.py +557 -0
- sofic/generators/epsilon_transducer.py +168 -0
- sofic/generators/epsilon_transducer_construction.py +185 -0
- sofic/generators/epsilon_transducer_inference.py +499 -0
- sofic/generators/hmm_inference.py +719 -0
- sofic/generators/information_diagram.py +428 -0
- sofic/generators/lumping.py +447 -0
- sofic/generators/markov.py +100 -0
- sofic/generators/mealy.py +156 -0
- sofic/generators/measures.py +257 -0
- sofic/generators/minimal_generative_model.py +821 -0
- sofic/generators/mixed_state.py +250 -0
- sofic/generators/mixed_state_construction.py +163 -0
- sofic/generators/moore.py +75 -0
- sofic/generators/nmachine.py +78 -0
- sofic/generators/nmachine_construction.py +70 -0
- sofic/generators/pfa.py +100 -0
- sofic/generators/prob.py +291 -0
- sofic/generators/process_equivalence.py +207 -0
- sofic/generators/quasi_inference.py +74 -0
- sofic/generators/quasi_realization.py +97 -0
- sofic/generators/reversal.py +66 -0
- sofic/generators/stack_hmm.py +426 -0
- sofic/generators/stack_inference.py +509 -0
- sofic/generators/stationary.py +134 -0
- sofic/generators/stochastic.py +65 -0
- sofic/generators/synchronization.py +407 -0
- sofic/generators/topological_epsilon_enumeration.py +349 -0
- sofic/generators/words.py +226 -0
- sofic/graph.py +135 -0
- sofic/indexing.py +31 -0
- sofic/inference/__init__.py +45 -0
- sofic/inference/bayesian/__init__.py +68 -0
- sofic/inference/bayesian/comparison.py +199 -0
- sofic/inference/bayesian/counts.py +219 -0
- sofic/inference/bayesian/diversity.py +254 -0
- sofic/inference/bayesian/epsilon.py +270 -0
- sofic/inference/bayesian/hdp_hmm.py +340 -0
- sofic/inference/bayesian/markov.py +294 -0
- sofic/inference/bayesian/pymc_backend.py +71 -0
- sofic/inference/bayesian/stack_hmm.py +215 -0
- sofic/inference/model_selection.py +365 -0
- sofic/inference/spectral.py +564 -0
- sofic/operations.py +16 -0
- sofic/properties.py +339 -0
- sofic/serialization.py +450 -0
- sofic/shifts/__init__.py +48 -0
- sofic/shifts/algorithms.py +84 -0
- sofic/shifts/base.py +49 -0
- sofic/shifts/cover_construction.py +76 -0
- sofic/shifts/covers.py +47 -0
- sofic/shifts/dyck_algorithms.py +100 -0
- sofic/shifts/dyck_enumeration.py +275 -0
- sofic/shifts/markov_dyck.py +172 -0
- sofic/shifts/parry_construction.py +82 -0
- sofic/shifts/sft.py +104 -0
- sofic/shifts/sft_construction.py +52 -0
- sofic/shifts/sliding_block_code.py +156 -0
- sofic/shifts/sofic.py +111 -0
- sofic/shifts/sofic_dyck.py +110 -0
- sofic/shifts/sofic_relation.py +64 -0
- sofic/shifts/textile.py +104 -0
- sofic/shifts/tmc.py +46 -0
- sofic/shifts/tmc_construction.py +58 -0
- sofic/shifts/topological_anatomy.py +150 -0
- sofic/states.py +27 -0
- sofic/testing/__init__.py +8 -0
- sofic/testing/strategies.py +154 -0
- sofic/viz/__init__.py +16 -0
- sofic/viz/_context.py +345 -0
- sofic/viz/_edge.py +216 -0
- sofic/viz/_format.py +89 -0
- sofic/viz/_labels.py +34 -0
- sofic/viz/_names.py +17 -0
- sofic/viz/_rational.py +20 -0
- sofic/viz/_tikz_compile.py +177 -0
- sofic/viz/_tikz_format.py +122 -0
- sofic/viz/_tikz_layout.py +218 -0
- sofic/viz/assets/vaucanson.tikz +71 -0
- sofic/viz/graphviz.py +158 -0
- sofic/viz/idiagram.py +350 -0
- sofic/viz/tikz.py +381 -0
- sofic-0.1.0.dist-info/METADATA +444 -0
- sofic-0.1.0.dist-info/RECORD +150 -0
- sofic-0.1.0.dist-info/WHEEL +4 -0
- sofic-0.1.0.dist-info/licenses/LICENSE.txt +29 -0
|
@@ -0,0 +1,1491 @@
|
|
|
1
|
+
"""cmpy-compatible process constructors.
|
|
2
|
+
|
|
3
|
+
This module ports the public constructors from ``cmpy.machines.processes`` to
|
|
4
|
+
sofic-native objects. Quantum and elementary-cellular-automaton helpers are
|
|
5
|
+
intentionally out of scope.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import math
|
|
11
|
+
from collections.abc import Hashable, Iterable, Mapping, Sequence
|
|
12
|
+
from itertools import product
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
|
|
17
|
+
from sofic.automata.transducers import MealyMachine
|
|
18
|
+
from sofic.generators.base import QuasiStochasticModel
|
|
19
|
+
from sofic.generators.epsilon_machine import EpsilonMachine
|
|
20
|
+
from sofic.generators.mealy import MealyHMM
|
|
21
|
+
from sofic.graph import ATTR_EMISSION, ATTR_OUTPUT, ATTR_PROB, ATTR_QUASIPROB, ATTR_SYMBOL
|
|
22
|
+
|
|
23
|
+
RecurrentEpsilonMachine = EpsilonMachine
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _require_machine_type(machine_type: Any, *allowed: type) -> None:
|
|
27
|
+
"""Raise ``NotImplementedError`` unless ``machine_type`` is ``None`` or allowed.
|
|
28
|
+
|
|
29
|
+
``None`` always selects the constructor's default output type, so it is
|
|
30
|
+
accepted implicitly alongside the explicitly-supported classes.
|
|
31
|
+
"""
|
|
32
|
+
if machine_type not in (*allowed, None):
|
|
33
|
+
raise NotImplementedError
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _as_alphabet(symbols: int | Sequence[Any]) -> tuple[Any, ...]:
|
|
37
|
+
if isinstance(symbols, int):
|
|
38
|
+
return tuple(range(symbols))
|
|
39
|
+
return tuple(symbols)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _uniform_initial(states: Sequence[Hashable]) -> dict[Hashable, float]:
|
|
43
|
+
if not states:
|
|
44
|
+
return {}
|
|
45
|
+
mass = 1.0 / len(states)
|
|
46
|
+
return dict.fromkeys(states, mass)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _stationary_initial(
|
|
50
|
+
states: Sequence[Hashable],
|
|
51
|
+
edges: Sequence[tuple[Hashable, Hashable, Any, float]],
|
|
52
|
+
) -> dict[Hashable, float]:
|
|
53
|
+
from sofic.generators.stationary import stationary_distribution_from_transition
|
|
54
|
+
|
|
55
|
+
if not states:
|
|
56
|
+
return {}
|
|
57
|
+
index = {state: i for i, state in enumerate(states)}
|
|
58
|
+
transition = np.zeros((len(states), len(states)), dtype=float)
|
|
59
|
+
for source, target, _symbol, prob in edges:
|
|
60
|
+
transition[index[source], index[target]] += float(prob)
|
|
61
|
+
row_sums = transition.sum(axis=1)
|
|
62
|
+
if np.any(row_sums <= 0.0):
|
|
63
|
+
return _uniform_initial(states)
|
|
64
|
+
transition = transition / row_sums[:, None]
|
|
65
|
+
try:
|
|
66
|
+
pi = stationary_distribution_from_transition(transition)
|
|
67
|
+
except Exception:
|
|
68
|
+
return _uniform_initial(states)
|
|
69
|
+
return {state: float(pi[i]) for i, state in enumerate(states)}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _normalize_edges(
|
|
73
|
+
edges: Sequence[tuple[Hashable, Hashable, Any, float]],
|
|
74
|
+
) -> list[tuple[Hashable, Hashable, Any, float]]:
|
|
75
|
+
row_totals: dict[Hashable, float] = {}
|
|
76
|
+
for source, _target, _symbol, prob in edges:
|
|
77
|
+
row_totals[source] = row_totals.get(source, 0.0) + float(prob)
|
|
78
|
+
normalized = []
|
|
79
|
+
for source, target, symbol, prob in edges:
|
|
80
|
+
total = row_totals[source]
|
|
81
|
+
normalized.append((source, target, symbol, float(prob) / total if total else 0.0))
|
|
82
|
+
return normalized
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _edge_machine(
|
|
86
|
+
edges: Iterable[tuple[Hashable, Hashable, Any, float]],
|
|
87
|
+
*,
|
|
88
|
+
machine_type: type[MealyHMM] = EpsilonMachine,
|
|
89
|
+
name: str | None = None,
|
|
90
|
+
initial_distribution: Mapping[Hashable, float] | None = None,
|
|
91
|
+
normalize: bool = True,
|
|
92
|
+
validate: bool = True,
|
|
93
|
+
) -> MealyHMM:
|
|
94
|
+
edge_list = list(edges)
|
|
95
|
+
if normalize:
|
|
96
|
+
edge_list = _normalize_edges(edge_list)
|
|
97
|
+
|
|
98
|
+
states = list(dict.fromkeys([source for source, *_ in edge_list] + [target for _source, target, *_ in edge_list]))
|
|
99
|
+
symbols = frozenset(symbol for _source, _target, symbol, _prob in edge_list)
|
|
100
|
+
initial = dict(initial_distribution) if initial_distribution is not None else _stationary_initial(states, edge_list)
|
|
101
|
+
|
|
102
|
+
machine = machine_type(initial_distribution=initial, observation_alphabet=symbols)
|
|
103
|
+
if name is not None:
|
|
104
|
+
machine.name = name
|
|
105
|
+
for state in states:
|
|
106
|
+
machine.graph.add_state(state)
|
|
107
|
+
for source, target, symbol, prob in edge_list:
|
|
108
|
+
if prob > 0.0:
|
|
109
|
+
machine.graph.add_transition(source, target, **{ATTR_EMISSION: symbol, ATTR_PROB: float(prob)})
|
|
110
|
+
if validate:
|
|
111
|
+
machine.validate()
|
|
112
|
+
return machine
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _from_string(
|
|
116
|
+
spec: str,
|
|
117
|
+
*,
|
|
118
|
+
machine_type: type[MealyHMM] = EpsilonMachine,
|
|
119
|
+
name: str | None = None,
|
|
120
|
+
normalize: bool = True,
|
|
121
|
+
) -> MealyHMM:
|
|
122
|
+
edges = []
|
|
123
|
+
for chunk in spec.replace("\n", " ").split(";"):
|
|
124
|
+
fields = chunk.split()
|
|
125
|
+
if not fields:
|
|
126
|
+
continue
|
|
127
|
+
if len(fields) not in (3, 4):
|
|
128
|
+
raise ValueError(f"invalid transition specification {chunk!r}")
|
|
129
|
+
source, target, symbol = fields[:3]
|
|
130
|
+
prob = float(fields[3]) if len(fields) == 4 else 1.0
|
|
131
|
+
edges.append((source, target, symbol, prob))
|
|
132
|
+
return _edge_machine(edges, machine_type=machine_type, name=name, normalize=normalize)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _word_period(word: Sequence[Any]) -> Sequence[Any]:
|
|
136
|
+
for period in range(1, len(word) + 1):
|
|
137
|
+
base = word[:period]
|
|
138
|
+
if all(word[i] == base[i % period] for i in range(len(word))):
|
|
139
|
+
return base
|
|
140
|
+
return word
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _words(alphabet: Sequence[Any], length: int) -> Iterable[tuple[Any, ...]]:
|
|
144
|
+
return product(alphabet, repeat=length)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _dirichlet(n: int, rng: Any = None) -> np.ndarray:
|
|
148
|
+
if rng is None:
|
|
149
|
+
rng = np.random.default_rng()
|
|
150
|
+
if hasattr(rng, "dirichlet"):
|
|
151
|
+
return np.asarray(rng.dirichlet(np.ones(n)), dtype=float)
|
|
152
|
+
if hasattr(rng, "rand"):
|
|
153
|
+
values = np.asarray(rng.rand(n), dtype=float)
|
|
154
|
+
else:
|
|
155
|
+
values = np.asarray(np.random.default_rng().random(n), dtype=float)
|
|
156
|
+
return values / values.sum()
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _compatible_machine_type(machine_type: Any, default: type[MealyHMM] = EpsilonMachine) -> type[MealyHMM]:
|
|
160
|
+
if machine_type is None:
|
|
161
|
+
return default
|
|
162
|
+
if machine_type in (EpsilonMachine, RecurrentEpsilonMachine, MealyHMM):
|
|
163
|
+
return machine_type
|
|
164
|
+
if isinstance(machine_type, str):
|
|
165
|
+
lowered = machine_type.lower()
|
|
166
|
+
if lowered in {"epsilon", "em", "recurrentepsilonmachine"}:
|
|
167
|
+
return EpsilonMachine
|
|
168
|
+
if lowered in {"mealy", "mealyhmm"}:
|
|
169
|
+
return MealyHMM
|
|
170
|
+
return default
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def ABC(p: float = 0.75, q: float = 0.25) -> EpsilonMachine:
|
|
174
|
+
if math.isclose(p, q):
|
|
175
|
+
return _from_string(f"A A 0 {p}; A A 1 {1 - p}", name="ABC Process")
|
|
176
|
+
return _from_string(f"A B 0 {p}; A B 1 {1 - p}; B A 0 {q}; B A 1 {1 - q}", name="ABC Process")
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def AFC(n: int) -> EpsilonMachine:
|
|
180
|
+
if n < 1:
|
|
181
|
+
raise ValueError("n >= 1 required")
|
|
182
|
+
spec = "B B 0; "
|
|
183
|
+
for i in range(1, n):
|
|
184
|
+
spec += f"A{i} A{i + 1} 1; A{i + 1} B 0;"
|
|
185
|
+
spec += "B A1 1; A1 B 0;"
|
|
186
|
+
return _from_string(spec, name=f"Almost Fair Coin Process, order {n}")
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def AFC2(n: int) -> EpsilonMachine:
|
|
190
|
+
if n < 1:
|
|
191
|
+
raise ValueError("n >= 1 required")
|
|
192
|
+
spec = "A1 B1 1; B1 A1 0; "
|
|
193
|
+
for i in range(1, n):
|
|
194
|
+
spec += f"A{i} A{i + 1} 0; A{i + 1} B1 1; B{i} B{i + 1} 1; B{i + 1} A1 0; "
|
|
195
|
+
return _from_string(spec, name=f"Almost Fair Coin 2 Process, order {n}")
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def BandMerging(machine_type: Any = MealyHMM) -> MealyHMM:
|
|
199
|
+
cls = _compatible_machine_type(machine_type, MealyHMM)
|
|
200
|
+
if cls is EpsilonMachine:
|
|
201
|
+
edges = [
|
|
202
|
+
("A", "B", "1", 1),
|
|
203
|
+
("B", "A", "0", 0.5),
|
|
204
|
+
("B", "A", "1", 0.5),
|
|
205
|
+
("T1", "A", "0", 0.25),
|
|
206
|
+
("T1", "T2", "1", 0.75),
|
|
207
|
+
("T2", "A", "0", 1 / 3),
|
|
208
|
+
("T2", "T1", "1", 2 / 3),
|
|
209
|
+
("E", "E", "1", math.sqrt(2) / 2),
|
|
210
|
+
("E", "A", "0", 1 - math.sqrt(2) / 2),
|
|
211
|
+
]
|
|
212
|
+
return _edge_machine(edges, machine_type=EpsilonMachine, name="Band Merging Process", normalize=False)
|
|
213
|
+
return _edge_machine(
|
|
214
|
+
[("A", "B", "1", 1), ("B", "A", "0", 0.5), ("B", "A", "1", 0.5)],
|
|
215
|
+
machine_type=MealyHMM,
|
|
216
|
+
name="Band Merging Process",
|
|
217
|
+
normalize=False,
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def BeadsOnNecklace(
|
|
222
|
+
machine_type: Any = MealyHMM,
|
|
223
|
+
beads: Sequence[Any] | Sequence[Sequence[Any]] = ("a", "b"),
|
|
224
|
+
necklace: Sequence[Any] = ("0", "1", "3"),
|
|
225
|
+
probs: float | Sequence[float] = 0.5,
|
|
226
|
+
) -> MealyHMM:
|
|
227
|
+
_require_machine_type(machine_type, MealyHMM)
|
|
228
|
+
if not necklace or not beads:
|
|
229
|
+
raise ValueError("beads and necklace are required")
|
|
230
|
+
bead_rows: list[Sequence[Any]] = (
|
|
231
|
+
list(beads) if isinstance(beads[0], (list, tuple)) else [beads for _ in range(len(necklace))]
|
|
232
|
+
) # type: ignore[index,arg-type,list-item]
|
|
233
|
+
if len(necklace) != len(bead_rows):
|
|
234
|
+
raise ValueError("necklace and beads must have the same length")
|
|
235
|
+
prob_rows = [float(probs)] * len(necklace) if isinstance(probs, (int, float)) else [float(p) for p in probs]
|
|
236
|
+
if len(prob_rows) != len(necklace):
|
|
237
|
+
raise ValueError("necklace and probs must have the same length")
|
|
238
|
+
|
|
239
|
+
edges = []
|
|
240
|
+
curr_state = 0
|
|
241
|
+
for j, bead in enumerate(bead_rows):
|
|
242
|
+
attach_state = curr_state
|
|
243
|
+
bead = tuple(bead)
|
|
244
|
+
if len(bead) == 0:
|
|
245
|
+
prob_rows[j] = 1.0
|
|
246
|
+
elif len(bead) == 1:
|
|
247
|
+
edges.append((curr_state, curr_state, bead[0], 1.0 - prob_rows[j]))
|
|
248
|
+
else:
|
|
249
|
+
for i, symbol in enumerate(bead):
|
|
250
|
+
if i == 0:
|
|
251
|
+
next_state = curr_state + 1
|
|
252
|
+
edges.append((curr_state, next_state, symbol, 1.0 - prob_rows[j]))
|
|
253
|
+
curr_state += 1
|
|
254
|
+
elif i == len(bead) - 1:
|
|
255
|
+
edges.append((curr_state, attach_state, symbol, 1.0))
|
|
256
|
+
else:
|
|
257
|
+
next_state = curr_state + 1
|
|
258
|
+
edges.append((curr_state, next_state, symbol, 1.0))
|
|
259
|
+
curr_state += 1
|
|
260
|
+
new_attach_state = 0 if j == len(bead_rows) - 1 else curr_state + 1
|
|
261
|
+
edges.append((attach_state, new_attach_state, necklace[j], prob_rows[j]))
|
|
262
|
+
curr_state = new_attach_state
|
|
263
|
+
return _edge_machine(edges, machine_type=MealyHMM, name="Beads On Necklace", normalize=False)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def BeforeAfter(machine_type: Any = MealyHMM, style: str = "simple") -> MealyHMM:
|
|
267
|
+
_require_machine_type(machine_type, MealyHMM)
|
|
268
|
+
if style == "simple":
|
|
269
|
+
edges = [("A", "A", "0", 0.5), ("A", "B", "1", 0.5), ("B", "B", "0", 0.5), ("B", "A", "2", 0.5)]
|
|
270
|
+
elif style == "cayley":
|
|
271
|
+
a, b = 0.6, 0.4
|
|
272
|
+
edges = [
|
|
273
|
+
("a", "a", "0", a),
|
|
274
|
+
("a", "ca", "2", b / 2),
|
|
275
|
+
("a", "ba", "1", b / 2),
|
|
276
|
+
("ca", "ca", "0", a),
|
|
277
|
+
("ca", "bca", "1", b),
|
|
278
|
+
("bca", "bca", "0", a),
|
|
279
|
+
("bca", "ca", "2", b),
|
|
280
|
+
("ba", "ba", "0", a),
|
|
281
|
+
("ba", "cba", "2", b),
|
|
282
|
+
("cba", "cba", "0", a),
|
|
283
|
+
("cba", "ba", "1", b),
|
|
284
|
+
]
|
|
285
|
+
else:
|
|
286
|
+
raise ValueError("style must be 'simple' or 'cayley'")
|
|
287
|
+
return _edge_machine(edges, machine_type=MealyHMM, name="BeforeAfter", normalize=False)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def BiasedCoin(bias: float, machine_type: Any = EpsilonMachine) -> EpsilonMachine:
|
|
291
|
+
cls = _compatible_machine_type(machine_type)
|
|
292
|
+
return _edge_machine(
|
|
293
|
+
[("A", "A", "1", bias), ("A", "A", "0", 1 - bias)],
|
|
294
|
+
machine_type=cls,
|
|
295
|
+
name=f"Coin, p = {bias}",
|
|
296
|
+
normalize=False,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def FairCoin(machine_type: Any = EpsilonMachine) -> EpsilonMachine:
|
|
301
|
+
return BiasedCoin(0.5, machine_type=machine_type)
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def RandomBiasedCoin(machine_type: Any = EpsilonMachine, rng: np.random.Generator | None = None) -> EpsilonMachine:
|
|
305
|
+
generator = rng if rng is not None else np.random.default_rng()
|
|
306
|
+
return BiasedCoin(float(generator.random()), machine_type=machine_type)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def IID(k: int | Sequence[Any], machine_type: Any = EpsilonMachine) -> EpsilonMachine:
|
|
310
|
+
alphabet = _as_alphabet(k)
|
|
311
|
+
return _edge_machine(
|
|
312
|
+
[("A", "A", symbol, 1.0) for symbol in alphabet],
|
|
313
|
+
machine_type=_compatible_machine_type(machine_type),
|
|
314
|
+
name=f"IID({len(alphabet)})",
|
|
315
|
+
normalize=True,
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def BinaryMarkovChain(
|
|
320
|
+
p: float = 0.4,
|
|
321
|
+
q: float = 0.3,
|
|
322
|
+
a: float | None = None,
|
|
323
|
+
b: float | None = None,
|
|
324
|
+
machine_type: Any = EpsilonMachine,
|
|
325
|
+
branch: Any = None,
|
|
326
|
+
) -> MealyHMM:
|
|
327
|
+
if machine_type in (EpsilonMachine, RecurrentEpsilonMachine, None):
|
|
328
|
+
return BMC_eM(p, q)
|
|
329
|
+
if isinstance(machine_type, str):
|
|
330
|
+
lowered = machine_type.lower()
|
|
331
|
+
if lowered == "generative":
|
|
332
|
+
return BMC_gen(p, q, branch=branch)
|
|
333
|
+
if lowered == "parametrized":
|
|
334
|
+
if a is None or b is None:
|
|
335
|
+
raise ValueError("a and b are required for parametrized BinaryMarkovChain")
|
|
336
|
+
return BMC_param(p, q, a, b)
|
|
337
|
+
if lowered == "lohr":
|
|
338
|
+
return BMC_lohr(p)
|
|
339
|
+
raise NotImplementedError
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def BMC_eM(p: float, q: float) -> EpsilonMachine:
|
|
343
|
+
if math.isclose(p, 1 - q):
|
|
344
|
+
return BiasedCoin(bias=p)
|
|
345
|
+
return _edge_machine(
|
|
346
|
+
[("A", "A", "0", 1 - p), ("A", "B", "1", p), ("B", "A", "0", q), ("B", "B", "1", 1 - q)],
|
|
347
|
+
machine_type=EpsilonMachine,
|
|
348
|
+
name="BinaryMarkovChain eM",
|
|
349
|
+
normalize=False,
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def BMC_gen(p: float, q: float, branch: Any = None) -> MealyHMM:
|
|
354
|
+
del branch
|
|
355
|
+
if p == 1 and q == 1:
|
|
356
|
+
spec = "A B 1 1.; B A 0 1."
|
|
357
|
+
elif p == 0 and q == 0:
|
|
358
|
+
spec = "A A 0 1.; B B 1 1."
|
|
359
|
+
elif p + q > 1:
|
|
360
|
+
spec = f"A B 0 1; B A 1 {p + q - 1}; B B 0 {1 - p}; B B 1 {1 - q}"
|
|
361
|
+
elif p < q:
|
|
362
|
+
spec = (
|
|
363
|
+
f"A A 1 {(1 - p - q) / (1 - p)}; A B 1 {q / (1 - p)}; "
|
|
364
|
+
f"B A 1 {p * (1 - p - q) / (1 - p)}; B B 0 {1 - p}; B B 1 {p * q / (1 - p)}"
|
|
365
|
+
)
|
|
366
|
+
else:
|
|
367
|
+
spec = (
|
|
368
|
+
f"A A 0 {(1 - p - q) / (1 - q)}; A B 0 {p / (1 - q)}; "
|
|
369
|
+
f"B A 0 {q * (1 - p - q) / (1 - q)}; B B 0 {p * q / (1 - q)}; B B 1 {1 - q}"
|
|
370
|
+
)
|
|
371
|
+
return _from_string(spec, machine_type=MealyHMM, name="BinaryMarkovChain gen", normalize=False)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def _BMC_param_get_a_range(p: float, q: float) -> list[float]:
|
|
375
|
+
return [0, min(q, 1 - p)]
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def _BMC_param_get_b_range(p: float, q: float) -> list[float]:
|
|
379
|
+
return [max(q, 1 - p), 1]
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def _BMC_param_check_a_range(p: float, q: float, a: float) -> bool:
|
|
383
|
+
low, high = _BMC_param_get_a_range(p, q)
|
|
384
|
+
return low <= a <= high
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def _BMC_param_check_b_range(p: float, q: float, b: float) -> bool:
|
|
388
|
+
low, high = _BMC_param_get_b_range(p, q)
|
|
389
|
+
return low <= b <= high
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def BMC_param(p: float, q: float, a: float, b: float) -> MealyHMM:
|
|
393
|
+
if not _BMC_param_check_a_range(p, q, a) or not _BMC_param_check_b_range(p, q, b):
|
|
394
|
+
raise ValueError("a or b is outside the allowed range")
|
|
395
|
+
probs = [
|
|
396
|
+
a * (b + p - 1) / (b - a),
|
|
397
|
+
(1 - a) * (b - q) / (b - a),
|
|
398
|
+
a * (1 - p - a) / (b - a),
|
|
399
|
+
(1 - a) * (q - a) / (b - a),
|
|
400
|
+
b * (b + p - 1) / (b - a),
|
|
401
|
+
(1 - b) * (b - q) / (b - a),
|
|
402
|
+
b * (1 - p - a) / (b - a),
|
|
403
|
+
(1 - b) * (q - a) / (b - a),
|
|
404
|
+
]
|
|
405
|
+
edges = [
|
|
406
|
+
("A", "A", "0", probs[0]),
|
|
407
|
+
("A", "A", "1", probs[1]),
|
|
408
|
+
("A", "B", "0", probs[2]),
|
|
409
|
+
("A", "B", "1", probs[3]),
|
|
410
|
+
("B", "A", "0", probs[4]),
|
|
411
|
+
("B", "A", "1", probs[5]),
|
|
412
|
+
("B", "B", "0", probs[6]),
|
|
413
|
+
("B", "B", "1", probs[7]),
|
|
414
|
+
]
|
|
415
|
+
return _edge_machine(edges, machine_type=MealyHMM, name="BinaryMarkovChain parametrized", normalize=False)
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def BMC_lohr(p: float) -> MealyHMM:
|
|
419
|
+
if p > 0.5:
|
|
420
|
+
raise ValueError("currently requires p <= 0.5")
|
|
421
|
+
return _edge_machine(
|
|
422
|
+
[
|
|
423
|
+
("0", "0", "0", 1 - 2 * p),
|
|
424
|
+
("0", "2", "0", 2 * p),
|
|
425
|
+
("1", "1", "1", 1 - 2 * p),
|
|
426
|
+
("1", "2", "1", 2 * p),
|
|
427
|
+
("2", "0", "0", 0.5 - p),
|
|
428
|
+
("2", "1", "1", 0.5 - p),
|
|
429
|
+
("2", "2", "0", p),
|
|
430
|
+
("2", "2", "1", p),
|
|
431
|
+
],
|
|
432
|
+
machine_type=MealyHMM,
|
|
433
|
+
name="BinaryMarkovChain Lohr",
|
|
434
|
+
normalize=False,
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def Butterfly() -> EpsilonMachine:
|
|
439
|
+
return _from_string(
|
|
440
|
+
"""
|
|
441
|
+
B C 0 .5; D E 0 .5; C A 1 .5; E A 1 .5; A B 2 .5;
|
|
442
|
+
A D 3 .5; B B 4 .5; D D 5 .5; C C 6 .5; E E 7 .5;
|
|
443
|
+
""",
|
|
444
|
+
name="Butterfly Process",
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def Cantor(machine_type: Any = MealyHMM) -> MealyHMM:
|
|
449
|
+
_require_machine_type(machine_type, MealyHMM)
|
|
450
|
+
edges = [
|
|
451
|
+
("A", "A", "0", 0.55),
|
|
452
|
+
("B", "A", "0", 0.30),
|
|
453
|
+
("B", "B", "0", 0.15),
|
|
454
|
+
("A", "A", "1", 0.15),
|
|
455
|
+
("A", "B", "1", 0.30),
|
|
456
|
+
("B", "B", "1", 0.55),
|
|
457
|
+
]
|
|
458
|
+
return _edge_machine(edges, machine_type=MealyHMM, name="Cantor Process", normalize=False)
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
def CoupledGMPs(
|
|
462
|
+
epsilon: float = 0.01, p: float = 0.5, alt: bool = True, machine_type: Any = EpsilonMachine
|
|
463
|
+
) -> EpsilonMachine:
|
|
464
|
+
if epsilon < 0 or p < 0:
|
|
465
|
+
raise ValueError("epsilon and p cannot be less than zero")
|
|
466
|
+
if alt:
|
|
467
|
+
q1 = epsilon * (1 - p)
|
|
468
|
+
q2 = epsilon * p
|
|
469
|
+
edges = [
|
|
470
|
+
("A", "A", "1", 0.5),
|
|
471
|
+
("A", "B", "0", 0.5),
|
|
472
|
+
("B", "A", "1", 1 - q1),
|
|
473
|
+
("B", "C", "0", q1),
|
|
474
|
+
("C", "C", "0", 0.5),
|
|
475
|
+
("C", "D", "1", 0.5),
|
|
476
|
+
("D", "C", "0", 1 - q2),
|
|
477
|
+
("D", "A", "1", q2),
|
|
478
|
+
]
|
|
479
|
+
else:
|
|
480
|
+
q1 = epsilon * (1 - p)
|
|
481
|
+
q2 = epsilon * p
|
|
482
|
+
edges = [
|
|
483
|
+
("A", "A", "1", (1 - q1) / 2),
|
|
484
|
+
("A", "B", "0", (1 - q1) / 2),
|
|
485
|
+
("B", "A", "1", 1),
|
|
486
|
+
("C", "C", "0", (1 - q2) / 2),
|
|
487
|
+
("C", "D", "1", (1 - q2) / 2),
|
|
488
|
+
("D", "C", "0", 1),
|
|
489
|
+
]
|
|
490
|
+
if q1:
|
|
491
|
+
edges.append(("A", "C", "2", q1))
|
|
492
|
+
if q2:
|
|
493
|
+
edges.append(("C", "A", "2", q2))
|
|
494
|
+
initial = None
|
|
495
|
+
if epsilon == 0:
|
|
496
|
+
initial = {"A": p * 2 / 3, "B": p * 1 / 3, "C": (1 - p) * 2 / 3, "D": (1 - p) * 1 / 3}
|
|
497
|
+
return _edge_machine(
|
|
498
|
+
edges,
|
|
499
|
+
machine_type=_compatible_machine_type(machine_type),
|
|
500
|
+
name="Coupled Golden Mean Processes",
|
|
501
|
+
initial_distribution=initial,
|
|
502
|
+
normalize=False,
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
def UncoupledGMPs(p: float = 0.5, machine_type: Any = EpsilonMachine) -> EpsilonMachine:
|
|
507
|
+
if not 0 <= p <= 1:
|
|
508
|
+
raise ValueError("p must be in [0, 1]")
|
|
509
|
+
return _edge_machine(
|
|
510
|
+
[
|
|
511
|
+
("A", "A", "1", 0.5),
|
|
512
|
+
("A", "B", "0", 0.5),
|
|
513
|
+
("B", "A", "1", 1),
|
|
514
|
+
("C", "C", "0", 0.5),
|
|
515
|
+
("C", "D", "1", 0.5),
|
|
516
|
+
("D", "C", "0", 1),
|
|
517
|
+
],
|
|
518
|
+
machine_type=_compatible_machine_type(machine_type),
|
|
519
|
+
name="Uncoupled Golden Mean Processes",
|
|
520
|
+
initial_distribution={"A": p * 2 / 3, "B": p * 1 / 3, "C": (1 - p) * 2 / 3, "D": (1 - p) * 1 / 3},
|
|
521
|
+
normalize=False,
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
def CyclicBranching(num_states: int, num_branchings: int, num_symbols: int = 2) -> MealyHMM:
|
|
526
|
+
if num_branchings >= num_states:
|
|
527
|
+
raise ValueError("number of branchings must be less than number of states")
|
|
528
|
+
edges = []
|
|
529
|
+
for x in range(num_states):
|
|
530
|
+
y = 0 if x == num_states - 1 else x + 1
|
|
531
|
+
symbol = 1 if x == num_states - 1 else 0
|
|
532
|
+
if x >= num_states - num_branchings:
|
|
533
|
+
for s in range(num_symbols):
|
|
534
|
+
edges.append((x, y, s, 1 / num_symbols))
|
|
535
|
+
else:
|
|
536
|
+
edges.append((x, y, symbol, 1))
|
|
537
|
+
return _edge_machine(
|
|
538
|
+
edges,
|
|
539
|
+
machine_type=MealyHMM,
|
|
540
|
+
name=f"Noisy Period-{num_states} with {num_branchings} branchings",
|
|
541
|
+
normalize=False,
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
def Ehrenfest(p: float = 0.5, N: int = 5, machine_type: Any = EpsilonMachine) -> EpsilonMachine:
|
|
546
|
+
_require_machine_type(machine_type, EpsilonMachine, RecurrentEpsilonMachine)
|
|
547
|
+
edges = []
|
|
548
|
+
for state in range(N + 1):
|
|
549
|
+
edges.append((state, state, state, 1 - p))
|
|
550
|
+
for state in range(N):
|
|
551
|
+
edges.append((state, state + 1, state + 1, p * (N - state) / N))
|
|
552
|
+
for state in range(1, N + 1):
|
|
553
|
+
edges.append((state, state - 1, state - 1, p * state / N))
|
|
554
|
+
return _edge_machine(edges, machine_type=EpsilonMachine, name="Ehrenfest", normalize=False)
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
def Even(machine_type: Any = EpsilonMachine, bias: float = 0.5) -> EpsilonMachine:
|
|
558
|
+
if machine_type in (EpsilonMachine, RecurrentEpsilonMachine, None):
|
|
559
|
+
edges = [("A", "A", "0", bias), ("A", "B", "1", 1 - bias), ("B", "A", "1", 1)]
|
|
560
|
+
return _edge_machine(edges, machine_type=EpsilonMachine, name="Even Process", normalize=False)
|
|
561
|
+
raise NotImplementedError
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
def RandomEven(machine_type: Any = EpsilonMachine, rng: np.random.Generator | None = None) -> EpsilonMachine:
|
|
565
|
+
return uniform_mealyhmm(
|
|
566
|
+
Even(), name="Random Even Process", create_using=_compatible_machine_type(machine_type), prng=rng
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
def EvenRedundant(machine_type: Any = EpsilonMachine, bias: float = 0.5) -> EpsilonMachine:
|
|
571
|
+
_require_machine_type(machine_type, EpsilonMachine, RecurrentEpsilonMachine)
|
|
572
|
+
return _from_string(
|
|
573
|
+
f"A A 0 {bias}; A B 1 {1 - bias}; B C 1 1.; C C 0 {bias}; C D 1 {1 - bias}; D A 1 1.",
|
|
574
|
+
name="Even Process (4-state)",
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
def ThreEven(machine_type: Any = EpsilonMachine) -> EpsilonMachine:
|
|
579
|
+
_require_machine_type(machine_type, EpsilonMachine, RecurrentEpsilonMachine)
|
|
580
|
+
return _from_string("A A 0 1; A B 1 1; A C 2 1; B A 1 1; C A 2 1;", name="ThreEven Process")
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
def Flower(
|
|
584
|
+
N: int = 4,
|
|
585
|
+
M: int = 3,
|
|
586
|
+
forward_dist: Sequence[float] | None = None,
|
|
587
|
+
reverse_dists: np.ndarray | None = None,
|
|
588
|
+
machine_type: Any = EpsilonMachine,
|
|
589
|
+
) -> EpsilonMachine:
|
|
590
|
+
_require_machine_type(machine_type, EpsilonMachine, RecurrentEpsilonMachine)
|
|
591
|
+
if N < 2 or M < 2:
|
|
592
|
+
raise ValueError("N and M must be at least 2")
|
|
593
|
+
if forward_dist is None:
|
|
594
|
+
forward_dist = [1.0 / (N - 1)] * (N - 1)
|
|
595
|
+
if reverse_dists is None:
|
|
596
|
+
reverse_dists = np.vstack([_dirichlet(M - 1) for _ in range(N - 1)])
|
|
597
|
+
edges = []
|
|
598
|
+
for i in range(N - 1):
|
|
599
|
+
petal = str(i + 1)
|
|
600
|
+
edges.append(("0", petal, petal, float(forward_dist[i])))
|
|
601
|
+
for j in range(M - 1):
|
|
602
|
+
edges.append((petal, "0", str(N + j), float(reverse_dists[i, j])))
|
|
603
|
+
return _edge_machine(edges, machine_type=EpsilonMachine, name="Flower Process", normalize=False)
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
def FourStateAlmostIID(delta: float = 0.2) -> EpsilonMachine:
|
|
607
|
+
delta = max(0.0, min(float(delta), 0.24))
|
|
608
|
+
p, q, r, s = 0.50 - 2 * delta, 0.50 - delta, 0.50 + delta, 0.50 + 2 * delta
|
|
609
|
+
spec = f"A A 0 {p}; A B 1 {1 - p}; B C 0 {q}; B C 1 {1 - q}; C A 0 {r}; C D 1 {1 - r}; D A 0 {s}; D D 1 {1 - s};"
|
|
610
|
+
return _from_string(spec, name="FourStateAlmostIID Process")
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
def Girvan_fig6b(machine_type: Any = EpsilonMachine, alpha: float = 0.5, pi: float = 0.4) -> EpsilonMachine:
|
|
614
|
+
return _edge_machine(
|
|
615
|
+
[("A", "A", "1", alpha), ("A", "P", "0", 1 - alpha), ("P", "A", "1", 1 - pi), ("P", "P", "0", pi)],
|
|
616
|
+
machine_type=_compatible_machine_type(machine_type),
|
|
617
|
+
name="Girvan_fig6b",
|
|
618
|
+
normalize=False,
|
|
619
|
+
)
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
def Girvan_fig6c(
|
|
623
|
+
machine_type: Any = EpsilonMachine, alpha: float = 0.5, pi: float = 0.4, rho: float = 0.3
|
|
624
|
+
) -> EpsilonMachine:
|
|
625
|
+
return _edge_machine(
|
|
626
|
+
[
|
|
627
|
+
("A", "A", "1", alpha),
|
|
628
|
+
("A", "P", "0", 1 - alpha),
|
|
629
|
+
("P", "A", "1", 1 - pi),
|
|
630
|
+
("P", "R", "0", pi),
|
|
631
|
+
("R", "R", "0", rho),
|
|
632
|
+
("R", "A", "1", 1 - rho),
|
|
633
|
+
],
|
|
634
|
+
machine_type=_compatible_machine_type(machine_type),
|
|
635
|
+
name="Girvan_fig6c",
|
|
636
|
+
normalize=False,
|
|
637
|
+
)
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
def Girvan_fig6d(
|
|
641
|
+
machine_type: Any = EpsilonMachine,
|
|
642
|
+
alpha: float = 0.5,
|
|
643
|
+
pi: float = 0.4,
|
|
644
|
+
rho: float = 0.3,
|
|
645
|
+
iota: float = 0.2,
|
|
646
|
+
) -> EpsilonMachine:
|
|
647
|
+
return _edge_machine(
|
|
648
|
+
[
|
|
649
|
+
("A", "I", "1", alpha),
|
|
650
|
+
("A", "P", "0", 1 - alpha),
|
|
651
|
+
("P", "A", "1", 1 - pi),
|
|
652
|
+
("P", "R", "0", pi),
|
|
653
|
+
("R", "R", "0", rho),
|
|
654
|
+
("R", "A", "1", 1 - rho),
|
|
655
|
+
("I", "I", "1", iota),
|
|
656
|
+
("I", "P", "0", 1 - iota),
|
|
657
|
+
],
|
|
658
|
+
machine_type=_compatible_machine_type(machine_type),
|
|
659
|
+
name="Girvan_fig6d",
|
|
660
|
+
normalize=False,
|
|
661
|
+
)
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
def GoldenMean(bias: float = 0.5, machine_type: Any = EpsilonMachine) -> EpsilonMachine:
|
|
665
|
+
_require_machine_type(machine_type, EpsilonMachine, RecurrentEpsilonMachine)
|
|
666
|
+
return _edge_machine(
|
|
667
|
+
[("A", "A", "1", 1 - bias), ("A", "B", "0", bias), ("B", "A", "1", 1)],
|
|
668
|
+
machine_type=EpsilonMachine,
|
|
669
|
+
name="Golden Mean Process",
|
|
670
|
+
normalize=False,
|
|
671
|
+
)
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
def RestrictedGM(k: int) -> EpsilonMachine:
|
|
675
|
+
if k <= 0:
|
|
676
|
+
raise ValueError("minimum k is 1")
|
|
677
|
+
spec = "0 0 1 0.5; 0 1 0 0.5;"
|
|
678
|
+
for kk in range(2, k + 1):
|
|
679
|
+
spec += f"{kk - 1} {kk} 1 1.0;"
|
|
680
|
+
spec += f"{k} 0 1 1.0"
|
|
681
|
+
return _from_string(spec, name=f"Restricted Golden Mean Process, k={k}")
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
def StretchedGM(k: int) -> EpsilonMachine:
|
|
685
|
+
if k <= 0:
|
|
686
|
+
raise ValueError("minimum k is 1")
|
|
687
|
+
spec = "0 0 1 0.5; 0 1 0 0.5;"
|
|
688
|
+
for kk in range(2, k + 1):
|
|
689
|
+
spec += f"{kk - 1} {kk} 0 1.0;"
|
|
690
|
+
spec += f"{k} 0 1 1.0"
|
|
691
|
+
return _from_string(spec, name=f"Stretched Golden Mean Process, k={k}")
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
def RNGM(R: int, N: int, p: float = 0.5) -> EpsilonMachine:
|
|
695
|
+
if R <= 0 or N <= 0 or R < N:
|
|
696
|
+
raise ValueError("requires 1 <= N <= R")
|
|
697
|
+
spec = f"0 0 1 {p}; 0 1 0 {1 - p};"
|
|
698
|
+
for kk in range(2, N + 1):
|
|
699
|
+
spec += f"{kk - 1} {kk} 0 1.0;"
|
|
700
|
+
for kk in range(N + 1, N + R):
|
|
701
|
+
spec += f"{kk - 1} {kk} 1 1.0;"
|
|
702
|
+
spec += f"{N + R - 1} 0 1 1.0"
|
|
703
|
+
return _from_string(spec, name=f"R-N Golden Mean Process, R={R} N={N}")
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
def RkGM(R: int, k: int, p: float = 0.5) -> EpsilonMachine:
|
|
707
|
+
if R <= 0 or k <= 0:
|
|
708
|
+
raise ValueError("R and k must be positive")
|
|
709
|
+
spec = f"0 0 1 {p}; 0 1 0 {1 - p};"
|
|
710
|
+
for kk in range(2, R + 1):
|
|
711
|
+
spec += f"{kk - 1} {kk} 0 1.0;"
|
|
712
|
+
for kk in range(R + 1, R + k):
|
|
713
|
+
spec += f"{kk - 1} {kk} 1 1.0;"
|
|
714
|
+
spec += f"{R + k - 1} 0 1 1.0"
|
|
715
|
+
return _from_string(spec, name=f"R-k Golden Mean Process, R={R} k={k}")
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
def RandomGoldenMean(machine_type: Any = EpsilonMachine, rng: np.random.Generator | None = None) -> EpsilonMachine:
|
|
719
|
+
return uniform_mealyhmm(
|
|
720
|
+
GoldenMean(), name="Random Golden Mean Process", create_using=_compatible_machine_type(machine_type), prng=rng
|
|
721
|
+
)
|
|
722
|
+
|
|
723
|
+
|
|
724
|
+
def GoldenMeanGHMM() -> QuasiStochasticModel:
|
|
725
|
+
q = QuasiStochasticModel(initial_quasidistribution={"A": 2 / 3, "B": 1 / 3})
|
|
726
|
+
q.observation_alphabet = frozenset({"0", "1"})
|
|
727
|
+
q.name = "Golden Mean Process"
|
|
728
|
+
for state in ("A", "B"):
|
|
729
|
+
q.graph.add_state(state)
|
|
730
|
+
for source, target, symbol, prob in [
|
|
731
|
+
("A", "A", "0", 1.0),
|
|
732
|
+
("A", "B", "0", -0.5),
|
|
733
|
+
("B", "A", "0", 2.0),
|
|
734
|
+
("B", "B", "0", -1.0),
|
|
735
|
+
("A", "A", "1", 0.5),
|
|
736
|
+
]:
|
|
737
|
+
q.graph.add_transition(source, target, **{ATTR_EMISSION: symbol, ATTR_QUASIPROB: prob})
|
|
738
|
+
q.validate()
|
|
739
|
+
return q
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
def NonunifilarGoldenMean(bias: float = 0.5, free: float = 2 / 3) -> MealyHMM:
|
|
743
|
+
pGM = bias
|
|
744
|
+
pA = 1 / (1 + pGM)
|
|
745
|
+
pB = pGM / (1 + pGM)
|
|
746
|
+
p0 = pA * pGM
|
|
747
|
+
p010 = pA * pGM**2
|
|
748
|
+
p_min = pGM
|
|
749
|
+
p_max = min(1, pA / pB / pGM)
|
|
750
|
+
if not p_min <= free <= p_max:
|
|
751
|
+
raise ValueError(f"free must be in [{p_min:.2f}, {p_max:.2f}]")
|
|
752
|
+
tab0 = free
|
|
753
|
+
tab1 = p010 / p0**2 - tab0 - p010 / p0 / tab0
|
|
754
|
+
tba1 = p010 / p0 / tab0
|
|
755
|
+
taa1 = 1 - tab1 - tab0
|
|
756
|
+
tbb1 = 1 - tba1
|
|
757
|
+
edges = []
|
|
758
|
+
for edge in [
|
|
759
|
+
("A", "B", "0", tab0),
|
|
760
|
+
("A", "B", "1", tab1),
|
|
761
|
+
("B", "A", "1", tba1),
|
|
762
|
+
("A", "A", "1", taa1),
|
|
763
|
+
("B", "B", "1", tbb1),
|
|
764
|
+
]:
|
|
765
|
+
if not math.isclose(edge[3], 0.0):
|
|
766
|
+
edges.append(edge)
|
|
767
|
+
return _edge_machine(edges, machine_type=MealyHMM, name=f"Nonunifilar Golden Mean, p = {bias:.2f}", normalize=False)
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
def IrreversibleTwoState(p: float = 0.5, q: float = 0.5, machine_type: Any = EpsilonMachine) -> EpsilonMachine:
|
|
771
|
+
return _edge_machine(
|
|
772
|
+
[("A", "A", "0", p), ("A", "B", "1", 1 - p), ("B", "B", "1", q), ("B", "A", "2", 1 - q)],
|
|
773
|
+
machine_type=_compatible_machine_type(machine_type),
|
|
774
|
+
name="IrrevTwoState",
|
|
775
|
+
normalize=False,
|
|
776
|
+
)
|
|
777
|
+
|
|
778
|
+
|
|
779
|
+
def Ising(machine_type: Any = EpsilonMachine, J: float = 1.0, B: float = 0.3, T: float = 1.0) -> EpsilonMachine:
|
|
780
|
+
_require_machine_type(machine_type, EpsilonMachine, RecurrentEpsilonMachine)
|
|
781
|
+
beta = 1.0 / T
|
|
782
|
+
rad = (np.sinh(beta * B) ** 2 + np.exp(-4 * beta * J)) ** 0.5
|
|
783
|
+
two_p = 1.0 - (2 * np.exp(-4 * beta * J)) / (rad * (np.cosh(beta * B) + rad))
|
|
784
|
+
one_p = np.sinh(beta * B) / rad
|
|
785
|
+
p = (two_p / 2 + one_p + 0.5) / (one_p + 1)
|
|
786
|
+
q = (two_p / 2 - one_p + 0.5) / (1 - one_p)
|
|
787
|
+
if not np.all(np.isfinite([p, q])):
|
|
788
|
+
raise FloatingPointError("non-finite Ising transition probability")
|
|
789
|
+
return _edge_machine(
|
|
790
|
+
[("A", "A", "0", p), ("A", "B", "1", 1 - p), ("B", "B", "1", q), ("B", "A", "0", 1 - q)],
|
|
791
|
+
machine_type=EpsilonMachine,
|
|
792
|
+
name="Ising Process",
|
|
793
|
+
normalize=False,
|
|
794
|
+
)
|
|
795
|
+
|
|
796
|
+
|
|
797
|
+
def Lollipop(
|
|
798
|
+
N: int, M: int, p: float = 0.5, q: float = 0.5, r: float = 0.1, machine_type: Any = EpsilonMachine
|
|
799
|
+
) -> EpsilonMachine:
|
|
800
|
+
_require_machine_type(machine_type, EpsilonMachine, RecurrentEpsilonMachine)
|
|
801
|
+
hns = [str(ind) for ind in range(N)]
|
|
802
|
+
sns = [str(ind) for ind in range(N, N + 2 * (M - 1) + 1)]
|
|
803
|
+
edges = []
|
|
804
|
+
for ind in range(N - 1):
|
|
805
|
+
prob = 1 - p if ind == 0 else 1 - q if ind == 1 else 1
|
|
806
|
+
edges.append((hns[ind], hns[ind + 1], "0", prob))
|
|
807
|
+
edges.append((hns[N - 1], hns[0], "0", 1 - p if N == 2 else 1))
|
|
808
|
+
for ind in range(M - 1):
|
|
809
|
+
edges.append((hns[0] if ind == 0 else sns[ind - 1], sns[ind], "1", p if ind == 0 else 1))
|
|
810
|
+
for ind in range(M - 1, 2 * (M - 1)):
|
|
811
|
+
edges.append((hns[1] if ind == M - 1 else sns[ind - 1], sns[ind], "1", q if ind == M - 1 else 1))
|
|
812
|
+
last = 2 * (M - 1)
|
|
813
|
+
edges.append((sns[M - 2], sns[last], "1", 1))
|
|
814
|
+
edges.append((sns[2 * (M - 1) - 1], sns[last], "1", 1 - r))
|
|
815
|
+
edges.append((sns[2 * (M - 1) - 1], sns[last], "0", r))
|
|
816
|
+
edges.append((sns[last], hns[0], "2", 1))
|
|
817
|
+
return _edge_machine(edges, machine_type=EpsilonMachine, name="Lollipop", normalize=False)
|
|
818
|
+
|
|
819
|
+
|
|
820
|
+
def LogicMachine(
|
|
821
|
+
logic: str, bias: float | Sequence[float] = 0.5, noise: float | Sequence[float] = 0.5, minimize: bool = True
|
|
822
|
+
) -> MealyHMM:
|
|
823
|
+
del minimize
|
|
824
|
+
if logic == "RRX":
|
|
825
|
+
return RRX(machine_type=MealyHMM)
|
|
826
|
+
if logic == "Rn1C":
|
|
827
|
+
return Rn1C(noise=float(np.atleast_1d(noise)[0]), bias=float(np.atleast_1d(bias)[0]))
|
|
828
|
+
raise NotImplementedError("LogicMachine currently supports the common RRX and Rn1C constructors")
|
|
829
|
+
|
|
830
|
+
|
|
831
|
+
def markov_skeleton(R: int, k: int | Sequence[Any], join: bool | None = None) -> MealyHMM:
|
|
832
|
+
alphabet = _as_alphabet(k)
|
|
833
|
+
if join is None:
|
|
834
|
+
join = len(alphabet) <= 10
|
|
835
|
+
if join:
|
|
836
|
+
alphabet = tuple(map(str, alphabet))
|
|
837
|
+
if any(len(symbol) > 1 for symbol in alphabet):
|
|
838
|
+
raise ValueError("cannot join symbols with more than one character")
|
|
839
|
+
if R == 0:
|
|
840
|
+
return _edge_machine(
|
|
841
|
+
[("A", "A", symbol, 1.0) for symbol in alphabet], machine_type=MealyHMM, name="Markov skeleton"
|
|
842
|
+
)
|
|
843
|
+
states: list[Hashable] = ["".join(word) if join else word for word in _words(alphabet, R)]
|
|
844
|
+
edges = []
|
|
845
|
+
for state in states:
|
|
846
|
+
for symbol in alphabet:
|
|
847
|
+
target = state[1:] + symbol if join else tuple(state[1:]) + (symbol,)
|
|
848
|
+
edges.append((state, target, symbol, 1.0))
|
|
849
|
+
return _edge_machine(edges, machine_type=MealyHMM, name=f"Order-{R} Markov skeleton")
|
|
850
|
+
|
|
851
|
+
|
|
852
|
+
def Misiurewicz(machine_type: Any = MealyHMM) -> MealyHMM:
|
|
853
|
+
_require_machine_type(machine_type, MealyHMM)
|
|
854
|
+
return _edge_machine(
|
|
855
|
+
[
|
|
856
|
+
("A", "B", "0", 0.364),
|
|
857
|
+
("B", "C", "0", 0.276),
|
|
858
|
+
("D", "B", "0", 0.521),
|
|
859
|
+
("A", "A", "1", 0.636),
|
|
860
|
+
("B", "A", "1", 0.724),
|
|
861
|
+
("C", "D", "1", 1),
|
|
862
|
+
("D", "C", "1", 0.479),
|
|
863
|
+
],
|
|
864
|
+
machine_type=MealyHMM,
|
|
865
|
+
name="Misiurewicz Process (Forward)",
|
|
866
|
+
normalize=False,
|
|
867
|
+
)
|
|
868
|
+
|
|
869
|
+
|
|
870
|
+
def MisiurewiczSimplified(machine_type: Any = MealyHMM) -> MealyHMM:
|
|
871
|
+
_require_machine_type(machine_type, MealyHMM)
|
|
872
|
+
return _edge_machine(
|
|
873
|
+
[
|
|
874
|
+
("A", "B", "0", 0.4),
|
|
875
|
+
("B", "C", "0", 0.25),
|
|
876
|
+
("D", "B", "0", 0.5),
|
|
877
|
+
("A", "A", "1", 0.6),
|
|
878
|
+
("B", "A", "1", 0.75),
|
|
879
|
+
("C", "D", "1", 1),
|
|
880
|
+
("D", "C", "1", 0.5),
|
|
881
|
+
],
|
|
882
|
+
machine_type=MealyHMM,
|
|
883
|
+
name="Simplified Misiurewicz Process (Forward)",
|
|
884
|
+
normalize=False,
|
|
885
|
+
)
|
|
886
|
+
|
|
887
|
+
|
|
888
|
+
def MisiurewiczUniform(machine_type: Any = MealyHMM) -> MealyHMM:
|
|
889
|
+
_require_machine_type(machine_type, MealyHMM)
|
|
890
|
+
return _edge_machine(
|
|
891
|
+
[
|
|
892
|
+
("A", "B", "0", 0.5),
|
|
893
|
+
("B", "C", "0", 0.5),
|
|
894
|
+
("D", "B", "0", 0.5),
|
|
895
|
+
("A", "A", "1", 0.5),
|
|
896
|
+
("B", "A", "1", 0.5),
|
|
897
|
+
("C", "D", "1", 1),
|
|
898
|
+
("D", "C", "1", 0.5),
|
|
899
|
+
],
|
|
900
|
+
machine_type=MealyHMM,
|
|
901
|
+
name="Uniform Misiurewicz Process (Forward)",
|
|
902
|
+
normalize=False,
|
|
903
|
+
)
|
|
904
|
+
|
|
905
|
+
|
|
906
|
+
def Multiple3(machine_type: Any = MealyHMM, bias: float = 0.5) -> MealyHMM:
|
|
907
|
+
return MultipleN(3, machine_type=machine_type, bias=bias)
|
|
908
|
+
|
|
909
|
+
|
|
910
|
+
def Multiple4(machine_type: Any = MealyHMM, bias: float = 0.5) -> MealyHMM:
|
|
911
|
+
return MultipleN(4, machine_type=machine_type, bias=bias)
|
|
912
|
+
|
|
913
|
+
|
|
914
|
+
def MultipleN(n: int, machine_type: Any = MealyHMM, bias: float = 0.5) -> MealyHMM:
|
|
915
|
+
_require_machine_type(machine_type, MealyHMM, EpsilonMachine, RecurrentEpsilonMachine)
|
|
916
|
+
edges = [(0, 0, "0", bias), (0, 1, "1", 1 - bias)]
|
|
917
|
+
for x in range(1, int(n)):
|
|
918
|
+
edges.append((x, 0 if x == n - 1 else x + 1, "1", 1))
|
|
919
|
+
cls = EpsilonMachine if machine_type in (EpsilonMachine, RecurrentEpsilonMachine) else MealyHMM
|
|
920
|
+
return _edge_machine(edges, machine_type=cls, name=f"'Multiples of {n}' Process", normalize=False)
|
|
921
|
+
|
|
922
|
+
|
|
923
|
+
def Nemo(machine_type: Any = EpsilonMachine, p: float = 0.5, q: float = 0.5) -> EpsilonMachine:
|
|
924
|
+
_require_machine_type(machine_type, EpsilonMachine, RecurrentEpsilonMachine)
|
|
925
|
+
return _from_string(
|
|
926
|
+
f"A A 1 {p}; A B 0 {1 - p}; B C 0 1; C A 0 {1 - q}; C A 1 {q};",
|
|
927
|
+
name="Nemo Process",
|
|
928
|
+
)
|
|
929
|
+
|
|
930
|
+
|
|
931
|
+
def NemoRedundant(machine_type: Any = MealyHMM, p: float = 0.5, q: float = 0.5) -> MealyHMM:
|
|
932
|
+
_require_machine_type(machine_type, MealyHMM)
|
|
933
|
+
return _from_string(
|
|
934
|
+
f"""
|
|
935
|
+
A A 1 {p}; A B 0 {1 - p}; B C 0 1; C D 0 {1 - q}; C D 1 {q};
|
|
936
|
+
D D 1 {p}; D E 0 {1 - p}; E F 0 1; F A 0 {1 - q}; F A 1 {q};
|
|
937
|
+
""",
|
|
938
|
+
machine_type=MealyHMM,
|
|
939
|
+
name="Nemo Process (6-state)",
|
|
940
|
+
)
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
def NoisyPeriod2(noise: float = 0.5) -> EpsilonMachine:
|
|
944
|
+
return _edge_machine(
|
|
945
|
+
[("A", "B", "0", 1), ("B", "A", "0", noise), ("B", "A", "1", 1 - noise)],
|
|
946
|
+
machine_type=EpsilonMachine,
|
|
947
|
+
name="Noisy Period-2",
|
|
948
|
+
normalize=False,
|
|
949
|
+
)
|
|
950
|
+
|
|
951
|
+
|
|
952
|
+
def NRPS() -> EpsilonMachine:
|
|
953
|
+
from sofic.examples.epsilon_machines import noisy_random_phase_slip
|
|
954
|
+
|
|
955
|
+
return noisy_random_phase_slip()
|
|
956
|
+
|
|
957
|
+
|
|
958
|
+
def Odd(machine_type: Any = EpsilonMachine, bias1: float = 0.5, bias2: float = 0.5) -> EpsilonMachine:
|
|
959
|
+
_require_machine_type(machine_type, EpsilonMachine, RecurrentEpsilonMachine)
|
|
960
|
+
edges = [
|
|
961
|
+
("A", "A", "0", bias1),
|
|
962
|
+
("A", "B", "1", 1 - bias1),
|
|
963
|
+
("B", "A", "0", bias2),
|
|
964
|
+
("B", "C", "1", 1 - bias2),
|
|
965
|
+
("C", "B", "1", 1),
|
|
966
|
+
]
|
|
967
|
+
return _edge_machine(edges, machine_type=EpsilonMachine, name="Odd Process", normalize=False)
|
|
968
|
+
|
|
969
|
+
|
|
970
|
+
def OddGHMM(variant: int = 1) -> QuasiStochasticModel:
|
|
971
|
+
matrices = (
|
|
972
|
+
{"0": [[0.5, 0, 0], [1.0, 0, 0], [1.0, 0, 0]], "1": [[0.5, -0.5, 0.5], [0, 0, 0], [0.5, 0, -0.5]]}
|
|
973
|
+
if variant == 1
|
|
974
|
+
else {"0": [[0.5, 0.5, -0.5], [1, 1, -1], [1, 1, -1]], "1": [[1, 0.5, -1], [0, 0, 0], [0.5, 0.5, -1]]}
|
|
975
|
+
)
|
|
976
|
+
states = ("A", "B", "C")
|
|
977
|
+
q = QuasiStochasticModel(initial_quasidistribution={"A": 1.0, "B": 0.0, "C": 0.0})
|
|
978
|
+
q.observation_alphabet = frozenset(matrices)
|
|
979
|
+
q.name = "Odd Process"
|
|
980
|
+
for state in states:
|
|
981
|
+
q.graph.add_state(state)
|
|
982
|
+
for symbol, matrix in matrices.items():
|
|
983
|
+
arr = np.asarray(matrix, dtype=float)
|
|
984
|
+
for i, source in enumerate(states):
|
|
985
|
+
for j, target in enumerate(states):
|
|
986
|
+
prob = float(arr[i, j])
|
|
987
|
+
if prob:
|
|
988
|
+
q.graph.add_transition(source, target, **{ATTR_EMISSION: symbol, ATTR_QUASIPROB: prob})
|
|
989
|
+
q.validate()
|
|
990
|
+
return q
|
|
991
|
+
|
|
992
|
+
|
|
993
|
+
def EvenOdd(machine_type: Any = EpsilonMachine) -> EpsilonMachine:
|
|
994
|
+
_require_machine_type(machine_type, EpsilonMachine, RecurrentEpsilonMachine)
|
|
995
|
+
return _from_string("A B 0 0.5; B A 0 1; A C 1 0.5; C B 0 0.5; C D 1 0.5; D C 1 1;", name="EvenOdd Process")
|
|
996
|
+
|
|
997
|
+
|
|
998
|
+
def ThreEvenOdd(machine_type: Any = EpsilonMachine) -> EpsilonMachine:
|
|
999
|
+
_require_machine_type(machine_type, EpsilonMachine, RecurrentEpsilonMachine)
|
|
1000
|
+
return _from_string(
|
|
1001
|
+
"A A 0 1; A B 1 1; B A 1 1; A C 2 1; C A 0 1; C B 1 1; C D 2 1; D C 2 1;", name="ThreEvenOdd Process"
|
|
1002
|
+
)
|
|
1003
|
+
|
|
1004
|
+
|
|
1005
|
+
def Period(P: int) -> MealyHMM:
|
|
1006
|
+
return Periodic("0" * (P - 1) + "1")
|
|
1007
|
+
|
|
1008
|
+
|
|
1009
|
+
def Periodic(word: Sequence[Any], reduce: bool = True) -> MealyHMM:
|
|
1010
|
+
base = _word_period(word) if reduce else word
|
|
1011
|
+
edges = []
|
|
1012
|
+
for i, symbol in enumerate(base):
|
|
1013
|
+
edges.append((i, (i + 1) % len(base), symbol, 1.0))
|
|
1014
|
+
return _edge_machine(
|
|
1015
|
+
edges, machine_type=MealyHMM, name=f"Period-{len(base)} Process ({''.join(map(str, base))})", normalize=False
|
|
1016
|
+
)
|
|
1017
|
+
|
|
1018
|
+
|
|
1019
|
+
def Period1(machine_type: Any = MealyHMM) -> MealyHMM:
|
|
1020
|
+
return Periodic("1") if machine_type is MealyHMM else _from_string("A A 1 1", name="Period-1 Process")
|
|
1021
|
+
|
|
1022
|
+
|
|
1023
|
+
def Period2(machine_type: Any = MealyHMM) -> MealyHMM:
|
|
1024
|
+
return Periodic("01") if machine_type is MealyHMM else _from_string("A B 0 1; B A 1 1", name="Period-2 Process")
|
|
1025
|
+
|
|
1026
|
+
|
|
1027
|
+
def Period4(machine_type: Any = MealyHMM) -> MealyHMM:
|
|
1028
|
+
return (
|
|
1029
|
+
Periodic("1110")
|
|
1030
|
+
if machine_type is MealyHMM
|
|
1031
|
+
else _from_string("A B 1 1; B C 1 1; C D 1 1; D A 0 1", name="Period-4 Process")
|
|
1032
|
+
)
|
|
1033
|
+
|
|
1034
|
+
|
|
1035
|
+
def Period7(machine_type: Any = MealyHMM) -> MealyHMM:
|
|
1036
|
+
if machine_type is not MealyHMM:
|
|
1037
|
+
raise NotImplementedError
|
|
1038
|
+
return Periodic("10101110")
|
|
1039
|
+
|
|
1040
|
+
|
|
1041
|
+
def Period8(machine_type: Any = MealyHMM) -> MealyHMM:
|
|
1042
|
+
if machine_type is not MealyHMM:
|
|
1043
|
+
raise NotImplementedError
|
|
1044
|
+
return Periodic("10101110")
|
|
1045
|
+
|
|
1046
|
+
|
|
1047
|
+
def Period12(machine_type: Any = MealyHMM) -> MealyHMM:
|
|
1048
|
+
if machine_type is not MealyHMM:
|
|
1049
|
+
raise NotImplementedError
|
|
1050
|
+
return Periodic("101011101110")
|
|
1051
|
+
|
|
1052
|
+
|
|
1053
|
+
def Period16(machine_type: Any = MealyHMM) -> MealyHMM:
|
|
1054
|
+
if machine_type is not MealyHMM:
|
|
1055
|
+
raise NotImplementedError
|
|
1056
|
+
return Periodic("1010111011101110")
|
|
1057
|
+
|
|
1058
|
+
|
|
1059
|
+
def PerturbedCoin(p: float = 0.2, q: float | None = None, machine_type: Any = EpsilonMachine) -> MealyHMM:
|
|
1060
|
+
if p == 0.5:
|
|
1061
|
+
return FairCoin()
|
|
1062
|
+
if q is None:
|
|
1063
|
+
q = p
|
|
1064
|
+
if machine_type in (EpsilonMachine, RecurrentEpsilonMachine, None):
|
|
1065
|
+
return _edge_machine(
|
|
1066
|
+
[("A", "A", "0", 1 - p), ("A", "B", "1", p), ("B", "A", "0", q), ("B", "B", "1", 1 - q)],
|
|
1067
|
+
machine_type=EpsilonMachine,
|
|
1068
|
+
name="Perturbed Coin",
|
|
1069
|
+
normalize=False,
|
|
1070
|
+
)
|
|
1071
|
+
lowered = machine_type.lower() if isinstance(machine_type, str) else ""
|
|
1072
|
+
if lowered in {"lohr", "generative"}:
|
|
1073
|
+
if q != p:
|
|
1074
|
+
raise ValueError("Lohr/generative variants only support q == p")
|
|
1075
|
+
return BMC_lohr(p) if lowered == "lohr" else BMC_gen(p, p)
|
|
1076
|
+
raise NotImplementedError(f"cannot build machine type {machine_type!r}")
|
|
1077
|
+
|
|
1078
|
+
|
|
1079
|
+
def PSB() -> EpsilonMachine:
|
|
1080
|
+
return _from_string("A B 1; A D 0; B B 1; B C 0; C D 0; D A 1", name="Phase-Slip Backtrack")
|
|
1081
|
+
|
|
1082
|
+
|
|
1083
|
+
def RIP(p: float = 0.5, q: float = 0.5, reverse: bool = False) -> EpsilonMachine:
|
|
1084
|
+
if reverse:
|
|
1085
|
+
spec = f"D E 1 {1 - p * q}; D F 0 {p * q}; E D 1 {(1 - p) / (1 - p * q)}; E G 0 {p * (1 - q) / (1 - p * q)}; F G 0 1; G D 1 1"
|
|
1086
|
+
else:
|
|
1087
|
+
spec = f"A B 0 {p}; A C 1 {1 - p}; B C 0 {q}; B C 1 {1 - q}; C A 1 1"
|
|
1088
|
+
return _from_string(spec, name="Random Insertion Process")
|
|
1089
|
+
|
|
1090
|
+
|
|
1091
|
+
def Rn1C(noise: float = 0.5, bias: float = 0.5) -> EpsilonMachine:
|
|
1092
|
+
return _edge_machine(
|
|
1093
|
+
[
|
|
1094
|
+
("A", "B", "0", bias),
|
|
1095
|
+
("B", "A", "0", 1),
|
|
1096
|
+
("A", "C", "1", 1 - bias),
|
|
1097
|
+
("C", "A", "0", noise),
|
|
1098
|
+
("C", "A", "1", 1 - noise),
|
|
1099
|
+
],
|
|
1100
|
+
machine_type=EpsilonMachine,
|
|
1101
|
+
name=f"Random Noisy-1 Copy, p(0|flip is 1) = {noise:.02f}",
|
|
1102
|
+
normalize=False,
|
|
1103
|
+
)
|
|
1104
|
+
|
|
1105
|
+
|
|
1106
|
+
def Rn1N(machine_type: Any = EpsilonMachine, bias: float = 0.5, noise: float = 0.1) -> EpsilonMachine:
|
|
1107
|
+
_require_machine_type(machine_type, EpsilonMachine, RecurrentEpsilonMachine)
|
|
1108
|
+
return _edge_machine(
|
|
1109
|
+
[
|
|
1110
|
+
("A", "B", "0", bias),
|
|
1111
|
+
("A", "C", "1", 1 - bias),
|
|
1112
|
+
("B", "A", "1", 1),
|
|
1113
|
+
("C", "A", "1", noise),
|
|
1114
|
+
("C", "A", "0", 1 - noise),
|
|
1115
|
+
],
|
|
1116
|
+
machine_type=EpsilonMachine,
|
|
1117
|
+
name="Rn1N Process",
|
|
1118
|
+
normalize=False,
|
|
1119
|
+
)
|
|
1120
|
+
|
|
1121
|
+
|
|
1122
|
+
def RRX(machine_type: Any = EpsilonMachine) -> MealyHMM:
|
|
1123
|
+
if machine_type in (EpsilonMachine, RecurrentEpsilonMachine, None):
|
|
1124
|
+
edges = [
|
|
1125
|
+
("S", "0", "0", 0.5),
|
|
1126
|
+
("S", "1", "1", 0.5),
|
|
1127
|
+
("0", "01|10", "1", 0.5),
|
|
1128
|
+
("0", "00|11", "0", 0.5),
|
|
1129
|
+
("1", "01|10", "0", 0.5),
|
|
1130
|
+
("1", "00|11", "1", 0.5),
|
|
1131
|
+
("00|11", "S", "0", 1),
|
|
1132
|
+
("01|10", "S", "1", 1),
|
|
1133
|
+
]
|
|
1134
|
+
return _edge_machine(edges, machine_type=EpsilonMachine, name="RRX", normalize=False)
|
|
1135
|
+
if machine_type is MealyHMM:
|
|
1136
|
+
return _edge_machine(
|
|
1137
|
+
[
|
|
1138
|
+
("A", "B", 0, 0.5),
|
|
1139
|
+
("A", "C", 1, 0.5),
|
|
1140
|
+
("B", "D", 0, 0.5),
|
|
1141
|
+
("B", "E", 1, 0.5),
|
|
1142
|
+
("C", "F", 0, 0.5),
|
|
1143
|
+
("C", "G", 1, 0.5),
|
|
1144
|
+
("D", "A", 0, 1),
|
|
1145
|
+
("E", "A", 1, 1),
|
|
1146
|
+
("F", "A", 1, 1),
|
|
1147
|
+
("G", "A", 0, 1),
|
|
1148
|
+
],
|
|
1149
|
+
machine_type=MealyHMM,
|
|
1150
|
+
name="Non-Minimal RRX",
|
|
1151
|
+
normalize=False,
|
|
1152
|
+
)
|
|
1153
|
+
raise NotImplementedError
|
|
1154
|
+
|
|
1155
|
+
|
|
1156
|
+
def RRXRO(machine_type: Any = EpsilonMachine) -> EpsilonMachine:
|
|
1157
|
+
_require_machine_type(machine_type, EpsilonMachine, RecurrentEpsilonMachine)
|
|
1158
|
+
return _from_string(
|
|
1159
|
+
"""
|
|
1160
|
+
S 0 0 .5; S 1 1 .5; 0 00 0 .5; 0 01 1 .5; 1 10 0 .5; 1 11 1 .5;
|
|
1161
|
+
01 orTrue 1 1; 10 orTrue 1 1; 00 000 0 1; 000 orFalse 0 .5; 000 orTrue 1 .5;
|
|
1162
|
+
11 110 0 1; 110 orFalse 0 .5; 110 orTrue 1 .5; orFalse S 0 1; orTrue S 1 1;
|
|
1163
|
+
""",
|
|
1164
|
+
name="RRXRO",
|
|
1165
|
+
)
|
|
1166
|
+
|
|
1167
|
+
|
|
1168
|
+
def SNS(machine_type: Any = MealyHMM) -> MealyHMM:
|
|
1169
|
+
_require_machine_type(machine_type, MealyHMM)
|
|
1170
|
+
return _edge_machine(
|
|
1171
|
+
[("A", "A", "1", 0.5), ("A", "B", "1", 0.5), ("B", "A", "0", 0.5), ("B", "B", "1", 0.5)],
|
|
1172
|
+
machine_type=MealyHMM,
|
|
1173
|
+
name="Simple Nondeterministic Source",
|
|
1174
|
+
normalize=False,
|
|
1175
|
+
)
|
|
1176
|
+
|
|
1177
|
+
|
|
1178
|
+
def ThreeHundred(
|
|
1179
|
+
machine_type: Any = EpsilonMachine, biases: tuple[float, float, float] = (0.5, 0.5, 0.5)
|
|
1180
|
+
) -> EpsilonMachine:
|
|
1181
|
+
_require_machine_type(machine_type, EpsilonMachine, RecurrentEpsilonMachine)
|
|
1182
|
+
p, q, r = biases
|
|
1183
|
+
spec = f"A B 0 {p}; A C 1 {1 - p}; B D 0 1; C E 0 1; D F 0 {q}; D F 1 {1 - q}; E A 0 1; F A 0 {r}; F A 1 {1 - r};"
|
|
1184
|
+
return _from_string(spec, name="ThreeHundred Process")
|
|
1185
|
+
|
|
1186
|
+
|
|
1187
|
+
def uniform_mealyhmm(
|
|
1188
|
+
topology: MealyHMM | Mapping[Any, np.ndarray] | Sequence[np.ndarray],
|
|
1189
|
+
name: str | None = None,
|
|
1190
|
+
nodes: Sequence[Hashable] | None = None,
|
|
1191
|
+
symbols: Sequence[Any] | None = None,
|
|
1192
|
+
create_using: type[MealyHMM] | None = None,
|
|
1193
|
+
prng: Any = None,
|
|
1194
|
+
) -> MealyHMM:
|
|
1195
|
+
cls = create_using or MealyHMM
|
|
1196
|
+
if isinstance(topology, MealyHMM):
|
|
1197
|
+
grouped: dict[Hashable, list[tuple[Hashable, Any]]] = {}
|
|
1198
|
+
for transition in topology.transitions():
|
|
1199
|
+
grouped.setdefault(transition.source, []).append((transition.target, transition.data[ATTR_EMISSION]))
|
|
1200
|
+
edges = []
|
|
1201
|
+
for source, targets in grouped.items():
|
|
1202
|
+
probs = _dirichlet(len(targets), prng)
|
|
1203
|
+
for prob, (target, symbol) in zip(probs, targets, strict=True):
|
|
1204
|
+
edges.append((source, target, symbol, float(prob)))
|
|
1205
|
+
return _edge_machine(edges, machine_type=cls, name=name or "Random Mealy HMM", normalize=False)
|
|
1206
|
+
|
|
1207
|
+
if isinstance(topology, Mapping):
|
|
1208
|
+
matrices = {symbol: np.asarray(matrix, dtype=float) for symbol, matrix in topology.items()}
|
|
1209
|
+
else:
|
|
1210
|
+
matrices = {
|
|
1211
|
+
symbol: np.asarray(matrix, dtype=float)
|
|
1212
|
+
for symbol, matrix in zip(symbols or range(len(topology)), topology, strict=False)
|
|
1213
|
+
}
|
|
1214
|
+
first = next(iter(matrices.values()))
|
|
1215
|
+
if nodes is None:
|
|
1216
|
+
nodes = tuple(range(first.shape[0]))
|
|
1217
|
+
edges = []
|
|
1218
|
+
for i, source in enumerate(nodes):
|
|
1219
|
+
support = []
|
|
1220
|
+
for symbol, matrix in matrices.items():
|
|
1221
|
+
for j, target in enumerate(nodes):
|
|
1222
|
+
if matrix[i, j] > 0:
|
|
1223
|
+
support.append((target, symbol))
|
|
1224
|
+
probs = _dirichlet(len(support), prng)
|
|
1225
|
+
for prob, (target, symbol) in zip(probs, support, strict=True):
|
|
1226
|
+
edges.append((source, target, symbol, float(prob)))
|
|
1227
|
+
return _edge_machine(edges, machine_type=cls, name=name or "Random Mealy HMM", normalize=False)
|
|
1228
|
+
|
|
1229
|
+
|
|
1230
|
+
def uniform_mealymc(
|
|
1231
|
+
order: int,
|
|
1232
|
+
symbols: int | Sequence[Any],
|
|
1233
|
+
name: str | None = None,
|
|
1234
|
+
create_using: type[MealyHMM] | None = None,
|
|
1235
|
+
prng: Any = None,
|
|
1236
|
+
) -> MealyHMM:
|
|
1237
|
+
alphabet = _as_alphabet(symbols)
|
|
1238
|
+
cls = create_using or MealyHMM
|
|
1239
|
+
edges = []
|
|
1240
|
+
for state in _words(alphabet, order):
|
|
1241
|
+
probs = _dirichlet(len(alphabet), prng)
|
|
1242
|
+
for prob, symbol in zip(probs, alphabet, strict=True):
|
|
1243
|
+
target = (*state[1:], symbol) if order else ()
|
|
1244
|
+
edges.append((state, target, symbol, float(prob)))
|
|
1245
|
+
return _edge_machine(
|
|
1246
|
+
edges, machine_type=cls, name=(name or "Random Markov Chain") + f" (k={order})", normalize=False
|
|
1247
|
+
)
|
|
1248
|
+
|
|
1249
|
+
|
|
1250
|
+
def _transducer(
|
|
1251
|
+
edges: Iterable[tuple[Hashable, Hashable, Any, Any, float]],
|
|
1252
|
+
*,
|
|
1253
|
+
initial: Hashable | None = None,
|
|
1254
|
+
name: str | None = None,
|
|
1255
|
+
) -> MealyMachine:
|
|
1256
|
+
edge_list = list(edges)
|
|
1257
|
+
states = list(dict.fromkeys([source for source, *_ in edge_list] + [target for _source, target, *_ in edge_list]))
|
|
1258
|
+
inputs = frozenset(input_symbol for _source, _target, input_symbol, _output_symbol, _prob in edge_list)
|
|
1259
|
+
outputs = frozenset(output_symbol for _source, _target, _input_symbol, output_symbol, _prob in edge_list)
|
|
1260
|
+
start = initial if initial is not None else (states[0] if states else None)
|
|
1261
|
+
machine = MealyMachine(
|
|
1262
|
+
input_alphabet=inputs,
|
|
1263
|
+
output_alphabet=outputs,
|
|
1264
|
+
initial_states=frozenset({start} if start is not None else set()),
|
|
1265
|
+
)
|
|
1266
|
+
if name is not None:
|
|
1267
|
+
machine.name = name
|
|
1268
|
+
for state in states:
|
|
1269
|
+
machine.graph.add_state(state)
|
|
1270
|
+
for source, target, input_symbol, output_symbol, prob in edge_list:
|
|
1271
|
+
machine.graph.add_transition(
|
|
1272
|
+
source, target, **{ATTR_SYMBOL: input_symbol, ATTR_OUTPUT: output_symbol, ATTR_PROB: float(prob)}
|
|
1273
|
+
)
|
|
1274
|
+
machine.validate()
|
|
1275
|
+
return machine
|
|
1276
|
+
|
|
1277
|
+
|
|
1278
|
+
def GMtoEven(bias: float = 0.5, create_using: Any = None) -> MealyMachine:
|
|
1279
|
+
del bias, create_using
|
|
1280
|
+
return _transducer([("A", "A", "1", "0", 1), ("A", "B", "0", "1", 1), ("B", "A", "1", "1", 1)], name="GM to Even")
|
|
1281
|
+
|
|
1282
|
+
|
|
1283
|
+
def RCT(bias: float = 0.5, create_using: Any = None) -> MealyMachine:
|
|
1284
|
+
del create_using
|
|
1285
|
+
return _transducer(
|
|
1286
|
+
[("A", "B", "0", "0", bias), ("A", "C", "0", "1", bias), ("B", "A", "0", "0", 1), ("C", "A", "1", "1", 1)]
|
|
1287
|
+
)
|
|
1288
|
+
|
|
1289
|
+
|
|
1290
|
+
def BitFlip(create_using: Any = None) -> MealyMachine:
|
|
1291
|
+
del create_using
|
|
1292
|
+
return _transducer([("A", "A", "0", "1", 1), ("A", "A", "1", "0", 1)])
|
|
1293
|
+
|
|
1294
|
+
|
|
1295
|
+
def FlipEveryOther(parity: str = "Even", create_using: Any = None) -> MealyMachine:
|
|
1296
|
+
del create_using
|
|
1297
|
+
initial = "A" if parity == "Even" else "B"
|
|
1298
|
+
return _transducer(
|
|
1299
|
+
[("A", "B", "0", "1", 1), ("A", "B", "1", "0", 1), ("B", "A", "0", "0", 1), ("B", "A", "1", "1", 1)],
|
|
1300
|
+
initial=initial,
|
|
1301
|
+
)
|
|
1302
|
+
|
|
1303
|
+
|
|
1304
|
+
def Delay(length: int = 2, symbols: int | Sequence[Any] = 2, create_using: Any = None) -> MealyMachine:
|
|
1305
|
+
del create_using
|
|
1306
|
+
alphabet = tuple(map(str, _as_alphabet(symbols)))
|
|
1307
|
+
edges = []
|
|
1308
|
+
for state in product(alphabet, repeat=length):
|
|
1309
|
+
source = "".join(state)
|
|
1310
|
+
for input_symbol in alphabet:
|
|
1311
|
+
full = source + input_symbol
|
|
1312
|
+
edges.append((source, full[1:], input_symbol, full[0], 1))
|
|
1313
|
+
return _transducer(edges)
|
|
1314
|
+
|
|
1315
|
+
|
|
1316
|
+
def TwoPerm(symbols: int | Sequence[Any] = 2, create_using: Any = None) -> MealyMachine:
|
|
1317
|
+
del create_using
|
|
1318
|
+
alphabet = tuple(map(str, _as_alphabet(symbols)))
|
|
1319
|
+
edges = []
|
|
1320
|
+
for even_symbol in alphabet:
|
|
1321
|
+
edges.append(("??", even_symbol + "?", even_symbol, "?", 1))
|
|
1322
|
+
for odd_symbol in alphabet:
|
|
1323
|
+
edges.append((even_symbol + "?", even_symbol + odd_symbol, odd_symbol, odd_symbol, 1))
|
|
1324
|
+
for next_even in alphabet:
|
|
1325
|
+
edges.append((even_symbol + odd_symbol, next_even + "?", next_even, even_symbol, 1))
|
|
1326
|
+
return _transducer(edges, initial="??")
|
|
1327
|
+
|
|
1328
|
+
|
|
1329
|
+
def BinaryChannel(p: float = 0.0, q: float = 0.0, create_using: Any = None) -> MealyMachine:
|
|
1330
|
+
del create_using
|
|
1331
|
+
return _transducer(
|
|
1332
|
+
[("A", "A", "0", "0", 1 - p), ("A", "A", "0", "1", p), ("A", "A", "1", "1", 1 - q), ("A", "A", "1", "0", q)]
|
|
1333
|
+
)
|
|
1334
|
+
|
|
1335
|
+
|
|
1336
|
+
def SlidingNOR(create_using: Any = None) -> MealyMachine:
|
|
1337
|
+
del create_using
|
|
1338
|
+
return _transducer(
|
|
1339
|
+
[("A", "A", "0", "1", 1), ("A", "B", "1", "0", 1), ("B", "A", "0", "0", 1), ("B", "B", "1", "0", 1)]
|
|
1340
|
+
)
|
|
1341
|
+
|
|
1342
|
+
|
|
1343
|
+
def Parity(create_using: Any = None) -> MealyMachine:
|
|
1344
|
+
del create_using
|
|
1345
|
+
return _transducer(
|
|
1346
|
+
[("A", "A", "0", "0", 1), ("A", "B", "1", "1", 1), ("B", "A", "0", "0", 1), ("B", "A", "1", "0", 1)]
|
|
1347
|
+
)
|
|
1348
|
+
|
|
1349
|
+
|
|
1350
|
+
def GME(create_using: Any = None) -> MealyMachine:
|
|
1351
|
+
del create_using
|
|
1352
|
+
return _transducer(
|
|
1353
|
+
[
|
|
1354
|
+
("A", "A", "0", "1", 0.5),
|
|
1355
|
+
("A", "B", "0", "0", 0.5),
|
|
1356
|
+
("B", "A", "0", "1", 1),
|
|
1357
|
+
("A", "A", "1", "0", 0.5),
|
|
1358
|
+
("A", "B", "1", "1", 0.5),
|
|
1359
|
+
("B", "A", "1", "1", 1),
|
|
1360
|
+
]
|
|
1361
|
+
)
|
|
1362
|
+
|
|
1363
|
+
|
|
1364
|
+
def BinaryChannelET(p: float = 0.1, q: float = 0.2) -> Any:
|
|
1365
|
+
"""Memoryless binary channel as its minimal (single-state) ε-transducer."""
|
|
1366
|
+
from sofic.generators.epsilon_transducer import EpsilonTransducer
|
|
1367
|
+
|
|
1368
|
+
return EpsilonTransducer.from_channel(BinaryChannel(p, q))
|
|
1369
|
+
|
|
1370
|
+
|
|
1371
|
+
def GMtoEvenET() -> Any:
|
|
1372
|
+
"""Golden-mean-to-even map as an ε-transducer (Barnett & Crutchfield 2015)."""
|
|
1373
|
+
from sofic.generators.epsilon_transducer import EpsilonTransducer
|
|
1374
|
+
|
|
1375
|
+
return EpsilonTransducer.from_channel(GMtoEven())
|
|
1376
|
+
|
|
1377
|
+
|
|
1378
|
+
processes = [
|
|
1379
|
+
"ABC",
|
|
1380
|
+
"BandMerging",
|
|
1381
|
+
"BeadsOnNecklace",
|
|
1382
|
+
"BeforeAfter",
|
|
1383
|
+
"BiasedCoin",
|
|
1384
|
+
"BinaryMarkovChain",
|
|
1385
|
+
"Butterfly",
|
|
1386
|
+
"Cantor",
|
|
1387
|
+
"CoupledGMPs",
|
|
1388
|
+
"GoldenMean",
|
|
1389
|
+
"IrreversibleTwoState",
|
|
1390
|
+
"NonunifilarGoldenMean",
|
|
1391
|
+
"Ehrenfest",
|
|
1392
|
+
"Even",
|
|
1393
|
+
"EvenOdd",
|
|
1394
|
+
"EvenRedundant",
|
|
1395
|
+
"FairCoin",
|
|
1396
|
+
"Flower",
|
|
1397
|
+
"FourStateAlmostIID",
|
|
1398
|
+
"Girvan_fig6b",
|
|
1399
|
+
"Girvan_fig6c",
|
|
1400
|
+
"Girvan_fig6d",
|
|
1401
|
+
"Ising",
|
|
1402
|
+
"Lollipop",
|
|
1403
|
+
"Misiurewicz",
|
|
1404
|
+
"MisiurewiczSimplified",
|
|
1405
|
+
"MisiurewiczUniform",
|
|
1406
|
+
"Multiple3",
|
|
1407
|
+
"Multiple4",
|
|
1408
|
+
"Nemo",
|
|
1409
|
+
"NemoRedundant",
|
|
1410
|
+
"NoisyPeriod2",
|
|
1411
|
+
"NRPS",
|
|
1412
|
+
"Odd",
|
|
1413
|
+
"Period1",
|
|
1414
|
+
"Period2",
|
|
1415
|
+
"Period4",
|
|
1416
|
+
"Period7",
|
|
1417
|
+
"Period8",
|
|
1418
|
+
"Period12",
|
|
1419
|
+
"Period16",
|
|
1420
|
+
"PerturbedCoin",
|
|
1421
|
+
"PSB",
|
|
1422
|
+
"RandomBiasedCoin",
|
|
1423
|
+
"RandomGoldenMean",
|
|
1424
|
+
"RandomEven",
|
|
1425
|
+
"RIP",
|
|
1426
|
+
"Rn1C",
|
|
1427
|
+
"Rn1N",
|
|
1428
|
+
"RRX",
|
|
1429
|
+
"RRXRO",
|
|
1430
|
+
"SNS",
|
|
1431
|
+
"ThreeHundred",
|
|
1432
|
+
"ThreEven",
|
|
1433
|
+
"ThreEvenOdd",
|
|
1434
|
+
]
|
|
1435
|
+
nonergodic_generators = ["UncoupledGMPs"]
|
|
1436
|
+
transducers = [
|
|
1437
|
+
"GMtoEven",
|
|
1438
|
+
"RCT",
|
|
1439
|
+
"BitFlip",
|
|
1440
|
+
"FlipEveryOther",
|
|
1441
|
+
"Delay",
|
|
1442
|
+
"TwoPerm",
|
|
1443
|
+
"BinaryChannel",
|
|
1444
|
+
"SlidingNOR",
|
|
1445
|
+
"Parity",
|
|
1446
|
+
"GME",
|
|
1447
|
+
]
|
|
1448
|
+
epsilon_transducers = [
|
|
1449
|
+
"BinaryChannelET",
|
|
1450
|
+
"GMtoEvenET",
|
|
1451
|
+
]
|
|
1452
|
+
|
|
1453
|
+
process_list = [globals()[name] for name in processes]
|
|
1454
|
+
process_list.extend(globals()[name] for name in nonergodic_generators)
|
|
1455
|
+
transducer_list = [globals()[name] for name in transducers]
|
|
1456
|
+
epsilon_transducer_list = [globals()[name] for name in epsilon_transducers]
|
|
1457
|
+
|
|
1458
|
+
__all__ = processes + nonergodic_generators + transducers + epsilon_transducers
|
|
1459
|
+
__all__ += [
|
|
1460
|
+
"AFC",
|
|
1461
|
+
"AFC2",
|
|
1462
|
+
"BMC_eM",
|
|
1463
|
+
"BMC_gen",
|
|
1464
|
+
"BMC_param",
|
|
1465
|
+
"BMC_lohr",
|
|
1466
|
+
"CyclicBranching",
|
|
1467
|
+
"GoldenMeanGHMM",
|
|
1468
|
+
"IID",
|
|
1469
|
+
"LogicMachine",
|
|
1470
|
+
"markov_skeleton",
|
|
1471
|
+
"MultipleN",
|
|
1472
|
+
"OddGHMM",
|
|
1473
|
+
"Period",
|
|
1474
|
+
"Periodic",
|
|
1475
|
+
"RestrictedGM",
|
|
1476
|
+
"StretchedGM",
|
|
1477
|
+
"RkGM",
|
|
1478
|
+
"RNGM",
|
|
1479
|
+
"uniform_mealyhmm",
|
|
1480
|
+
"uniform_mealymc",
|
|
1481
|
+
"processes",
|
|
1482
|
+
"process_list",
|
|
1483
|
+
"transducers",
|
|
1484
|
+
"transducer_list",
|
|
1485
|
+
"epsilon_transducers",
|
|
1486
|
+
"epsilon_transducer_list",
|
|
1487
|
+
"_BMC_param_get_a_range",
|
|
1488
|
+
"_BMC_param_get_b_range",
|
|
1489
|
+
"_BMC_param_check_a_range",
|
|
1490
|
+
"_BMC_param_check_b_range",
|
|
1491
|
+
]
|