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
sofic/generators/base.py
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"""Stochastic generator base classes."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Hashable, Mapping, Sequence
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Self
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
from sofic.base import StateMachine
|
|
11
|
+
from sofic.exceptions import QuasiStochasticValidationError, StochasticValidationError
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from sofic.automata.dfa import DFA
|
|
15
|
+
from sofic.automata.nfa import NFA
|
|
16
|
+
from sofic.generators.mealy import MealyHMM
|
|
17
|
+
from sofic.generators.prob import SymbolConstraints
|
|
18
|
+
from sofic.shifts.sofic import SoficShift
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class StochasticModel(StateMachine):
|
|
22
|
+
"""Generator with a probability distribution over initial states."""
|
|
23
|
+
|
|
24
|
+
initial_distribution: dict[Hashable, float]
|
|
25
|
+
symbol_constraints: SymbolConstraints | None
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
initial_distribution: Mapping[Hashable, float] | None = None,
|
|
30
|
+
*,
|
|
31
|
+
symbol_constraints: SymbolConstraints | None = None,
|
|
32
|
+
**kwargs: Any,
|
|
33
|
+
) -> None:
|
|
34
|
+
super().__init__(**kwargs)
|
|
35
|
+
self.initial_distribution = dict(initial_distribution or {})
|
|
36
|
+
self.symbol_constraints = symbol_constraints
|
|
37
|
+
|
|
38
|
+
def validate(self) -> None:
|
|
39
|
+
self.validate_stochastic()
|
|
40
|
+
|
|
41
|
+
def validate_stochastic(self) -> None:
|
|
42
|
+
from sofic.generators.prob import is_symbolic, row_sums_to_one
|
|
43
|
+
|
|
44
|
+
probs = list(self.initial_distribution.values())
|
|
45
|
+
if probs and not row_sums_to_one(probs):
|
|
46
|
+
total = sum(probs)
|
|
47
|
+
raise StochasticValidationError(f"initial distribution sums to {total}, not 1")
|
|
48
|
+
for state, prob in self.initial_distribution.items():
|
|
49
|
+
if is_symbolic(prob):
|
|
50
|
+
if getattr(prob, "is_negative", None) is True:
|
|
51
|
+
raise StochasticValidationError(f"negative initial probability at {state!r}")
|
|
52
|
+
elif prob < 0:
|
|
53
|
+
raise StochasticValidationError(f"negative initial probability at {state!r}")
|
|
54
|
+
self._require(self.graph.has_state(state), f"unknown initial state {state!r}")
|
|
55
|
+
|
|
56
|
+
def stationary_distribution(self) -> np.ndarray:
|
|
57
|
+
from sofic.generators.stationary import stationary_distribution_hmm
|
|
58
|
+
|
|
59
|
+
return stationary_distribution_hmm(self)
|
|
60
|
+
|
|
61
|
+
def state_distribution(self) -> Any:
|
|
62
|
+
from sofic.generators.measures import state_distribution
|
|
63
|
+
|
|
64
|
+
return state_distribution(self)
|
|
65
|
+
|
|
66
|
+
def state_entropy(self) -> float:
|
|
67
|
+
from sofic.generators.measures import state_entropy
|
|
68
|
+
|
|
69
|
+
return state_entropy(self)
|
|
70
|
+
|
|
71
|
+
def reverse(self) -> Self:
|
|
72
|
+
"""Return the time-reversed generator (stochastic reversal, not a graph transpose).
|
|
73
|
+
|
|
74
|
+
Overrides :meth:`sofic.base.StateMachine.reverse` (which merely
|
|
75
|
+
transposes the transition graph): for a stochastic process the reversal
|
|
76
|
+
must reweight edges by the stationary distribution
|
|
77
|
+
(:func:`~sofic.generators.reversal.time_reverse_stochastic`). Emission
|
|
78
|
+
machines are routed through :meth:`EpsilonMachine.from_hmm`.
|
|
79
|
+
"""
|
|
80
|
+
from sofic.generators.reversal import is_markov_like, time_reverse_stochastic
|
|
81
|
+
|
|
82
|
+
if not is_markov_like(self):
|
|
83
|
+
from sofic.generators.epsilon_machine import EpsilonMachine
|
|
84
|
+
from sofic.generators.mealy import MealyHMM
|
|
85
|
+
from sofic.generators.moore import MooreHMM
|
|
86
|
+
|
|
87
|
+
if isinstance(self, (MealyHMM, MooreHMM)):
|
|
88
|
+
from sofic.generators.epsilon_machine import EpsilonMachine
|
|
89
|
+
|
|
90
|
+
if isinstance(self, EpsilonMachine):
|
|
91
|
+
return EpsilonMachine.from_time_reversed(self)
|
|
92
|
+
return EpsilonMachine.from_hmm(time_reverse_stochastic(self))
|
|
93
|
+
raise NotImplementedError("time-reversed generators with edge emissions require EpsilonMachine.from_hmm")
|
|
94
|
+
return time_reverse_stochastic(self)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class HiddenMarkovModel(StochasticModel):
|
|
98
|
+
"""Hidden-state generator with an observation alphabet."""
|
|
99
|
+
|
|
100
|
+
observation_alphabet: frozenset[Any]
|
|
101
|
+
|
|
102
|
+
def __init__(self, observation_alphabet: frozenset[Any] | None = None, **kwargs: Any) -> None:
|
|
103
|
+
super().__init__(**kwargs)
|
|
104
|
+
self.observation_alphabet = observation_alphabet if observation_alphabet is not None else frozenset()
|
|
105
|
+
|
|
106
|
+
def sample(self, n: int, rng: np.random.Generator | None = None) -> tuple[list[Any], list[Hashable]]:
|
|
107
|
+
from sofic.generators.hmm_inference import sample
|
|
108
|
+
|
|
109
|
+
return sample(self, n, rng)
|
|
110
|
+
|
|
111
|
+
def log_likelihood(self, observations: Sequence[Any]) -> float:
|
|
112
|
+
from sofic.generators.hmm_inference import log_likelihood
|
|
113
|
+
|
|
114
|
+
return log_likelihood(self, observations)
|
|
115
|
+
|
|
116
|
+
def forward(self, observations: Sequence[Any], *, scaled: bool = False) -> np.ndarray:
|
|
117
|
+
from sofic.generators.hmm_inference import forward
|
|
118
|
+
|
|
119
|
+
return forward(self, observations, scaled=scaled)
|
|
120
|
+
|
|
121
|
+
def backward(self, observations: Sequence[Any], *, scaled: bool = False) -> np.ndarray:
|
|
122
|
+
from sofic.generators.hmm_inference import backward
|
|
123
|
+
|
|
124
|
+
return backward(self, observations, scaled=scaled)
|
|
125
|
+
|
|
126
|
+
def viterbi(self, observations: Sequence[Any]) -> list[Hashable]:
|
|
127
|
+
from sofic.generators.hmm_inference import viterbi
|
|
128
|
+
|
|
129
|
+
return viterbi(self, observations)
|
|
130
|
+
|
|
131
|
+
def smooth(self, observations: Sequence[Any]) -> np.ndarray:
|
|
132
|
+
"""Return fixed-interval smoothed marginals ``gamma[t, s]``."""
|
|
133
|
+
from sofic.generators.hmm_inference import smooth
|
|
134
|
+
|
|
135
|
+
return smooth(self, observations)
|
|
136
|
+
|
|
137
|
+
def two_slice_marginals(self, observations: Sequence[Any]) -> np.ndarray:
|
|
138
|
+
"""Return two-slice smoothed marginals ``xi[t, i, j]``."""
|
|
139
|
+
from sofic.generators.hmm_inference import two_slice_marginals
|
|
140
|
+
|
|
141
|
+
return two_slice_marginals(self, observations)
|
|
142
|
+
|
|
143
|
+
def baum_welch(
|
|
144
|
+
self,
|
|
145
|
+
sequences: Any,
|
|
146
|
+
*,
|
|
147
|
+
max_iter: int = 100,
|
|
148
|
+
tol: float = 1e-6,
|
|
149
|
+
estimate_initial: bool = True,
|
|
150
|
+
) -> tuple[MealyHMM, list[float]]:
|
|
151
|
+
"""Fit parameters by Baum-Welch EM, returning ``(fitted_model, loglik_trace)``."""
|
|
152
|
+
from sofic.generators.hmm_inference import baum_welch
|
|
153
|
+
|
|
154
|
+
return baum_welch(
|
|
155
|
+
self,
|
|
156
|
+
sequences,
|
|
157
|
+
max_iter=max_iter,
|
|
158
|
+
tol=tol,
|
|
159
|
+
estimate_initial=estimate_initial,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
def score(self, observations: Sequence[Any]) -> dict[tuple[Hashable, Any, Hashable], float]:
|
|
163
|
+
"""Return the log-likelihood gradient (Fisher identity) over edge parameters."""
|
|
164
|
+
from sofic.generators.hmm_inference import score
|
|
165
|
+
|
|
166
|
+
return score(self, observations)
|
|
167
|
+
|
|
168
|
+
def observed_information(self, observations: Sequence[Any]) -> np.ndarray:
|
|
169
|
+
"""Return the observed information matrix (Louis' identity)."""
|
|
170
|
+
from sofic.generators.hmm_inference import observed_information
|
|
171
|
+
|
|
172
|
+
return observed_information(self, observations)
|
|
173
|
+
|
|
174
|
+
def standard_errors(self, observations: Sequence[Any]) -> dict[tuple[Hashable, Any, Hashable], float]:
|
|
175
|
+
"""Return asymptotic standard errors of the free edge parameters."""
|
|
176
|
+
from sofic.generators.hmm_inference import standard_errors
|
|
177
|
+
|
|
178
|
+
return standard_errors(self, observations)
|
|
179
|
+
|
|
180
|
+
def to_mealy(self) -> MealyHMM:
|
|
181
|
+
"""Return an equivalent Mealy-style presentation."""
|
|
182
|
+
raise NotImplementedError(f"{type(self).__name__} must implement to_mealy()")
|
|
183
|
+
|
|
184
|
+
def entropy_rate(self) -> float:
|
|
185
|
+
from sofic.generators.measures import entropy_rate_hmm
|
|
186
|
+
|
|
187
|
+
return entropy_rate_hmm(self)
|
|
188
|
+
|
|
189
|
+
def joint_block_distribution(self, history_length: int = 1) -> Any:
|
|
190
|
+
from sofic.generators.measures import joint_block_distribution
|
|
191
|
+
|
|
192
|
+
return joint_block_distribution(self, history_length=history_length)
|
|
193
|
+
|
|
194
|
+
def words_of_length(self, length: int) -> dict[tuple[Any, ...], float]:
|
|
195
|
+
"""Return observed words of ``length`` and their probabilities."""
|
|
196
|
+
from sofic.generators.words import hmm_words_of_length
|
|
197
|
+
|
|
198
|
+
return hmm_words_of_length(self, length)
|
|
199
|
+
|
|
200
|
+
def word_probability(
|
|
201
|
+
self,
|
|
202
|
+
word: Sequence[Any],
|
|
203
|
+
*,
|
|
204
|
+
start: Hashable | Mapping[Hashable, float] | Sequence[float] | np.ndarray | None = None,
|
|
205
|
+
) -> float:
|
|
206
|
+
"""Return the probability of an observed finite word."""
|
|
207
|
+
from sofic.generators.words import hmm_word_probability
|
|
208
|
+
|
|
209
|
+
return hmm_word_probability(self, word, start=start)
|
|
210
|
+
|
|
211
|
+
def log_word_probability(
|
|
212
|
+
self,
|
|
213
|
+
word: Sequence[Any],
|
|
214
|
+
*,
|
|
215
|
+
start: Hashable | Mapping[Hashable, float] | Sequence[float] | np.ndarray | None = None,
|
|
216
|
+
) -> float:
|
|
217
|
+
"""Return ``log2`` of an observed finite-word probability."""
|
|
218
|
+
from sofic.generators.words import hmm_log_word_probability
|
|
219
|
+
|
|
220
|
+
return hmm_log_word_probability(self, word, start=start)
|
|
221
|
+
|
|
222
|
+
def word_probabilities(
|
|
223
|
+
self,
|
|
224
|
+
lengths: int | Sequence[int],
|
|
225
|
+
*,
|
|
226
|
+
start: Hashable | Mapping[Hashable, float] | Sequence[float] | np.ndarray | None = None,
|
|
227
|
+
sparse: bool = True,
|
|
228
|
+
) -> dict[tuple[Any, ...], float]:
|
|
229
|
+
"""Return observed-word probabilities for one or more lengths."""
|
|
230
|
+
from sofic.generators.words import hmm_word_probabilities
|
|
231
|
+
|
|
232
|
+
return hmm_word_probabilities(self, lengths, start=start, sparse=sparse)
|
|
233
|
+
|
|
234
|
+
def conditional_word_probability(
|
|
235
|
+
self,
|
|
236
|
+
word: Sequence[Any],
|
|
237
|
+
condition: Sequence[Any],
|
|
238
|
+
*,
|
|
239
|
+
start: Hashable | Mapping[Hashable, float] | Sequence[float] | np.ndarray | None = None,
|
|
240
|
+
) -> float:
|
|
241
|
+
"""Return ``P(word | condition)``."""
|
|
242
|
+
from sofic.generators.words import hmm_conditional_word_probability
|
|
243
|
+
|
|
244
|
+
return hmm_conditional_word_probability(self, word, condition, start=start)
|
|
245
|
+
|
|
246
|
+
def is_equal_process(
|
|
247
|
+
self,
|
|
248
|
+
other: HiddenMarkovModel,
|
|
249
|
+
*,
|
|
250
|
+
start1: Hashable | Mapping[Hashable, float] | Sequence[float] | np.ndarray | None = None,
|
|
251
|
+
start2: Hashable | Mapping[Hashable, float] | Sequence[float] | np.ndarray | None = None,
|
|
252
|
+
rtol: float | None = None,
|
|
253
|
+
atol: float | None = None,
|
|
254
|
+
) -> bool:
|
|
255
|
+
"""Return whether two HMMs generate the same finite-word process."""
|
|
256
|
+
from sofic.generators.process_equivalence import is_equal_process
|
|
257
|
+
|
|
258
|
+
return is_equal_process(self, other, start1=start1, start2=start2, rtol=rtol, atol=atol)
|
|
259
|
+
|
|
260
|
+
def to_sofic_shift(self) -> SoficShift:
|
|
261
|
+
"""Strip probabilities and return a sofic shift with the same support."""
|
|
262
|
+
from sofic.generators.conversions import hmm_to_sofic_shift
|
|
263
|
+
|
|
264
|
+
return hmm_to_sofic_shift(self)
|
|
265
|
+
|
|
266
|
+
def to_support_nfa(self) -> NFA:
|
|
267
|
+
"""Return an NFA for the support language of this HMM."""
|
|
268
|
+
from sofic.generators.conversions import hmm_to_support_nfa
|
|
269
|
+
|
|
270
|
+
return hmm_to_support_nfa(self)
|
|
271
|
+
|
|
272
|
+
def to_support_dfa(self) -> DFA:
|
|
273
|
+
"""Return a determinized automaton for the support language of this HMM."""
|
|
274
|
+
from sofic.generators.conversions import hmm_to_support_dfa
|
|
275
|
+
|
|
276
|
+
return hmm_to_support_dfa(self)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
class QuasiStochasticModel(StateMachine):
|
|
280
|
+
"""Generator allowing signed quasiprobabilities on internal weights."""
|
|
281
|
+
|
|
282
|
+
initial_quasidistribution: dict[Hashable, float]
|
|
283
|
+
|
|
284
|
+
def __init__(self, initial_quasidistribution: Mapping[Hashable, float] | None = None, **kwargs: Any) -> None:
|
|
285
|
+
super().__init__(**kwargs)
|
|
286
|
+
self.initial_quasidistribution = dict(initial_quasidistribution or {})
|
|
287
|
+
|
|
288
|
+
def validate(self) -> None:
|
|
289
|
+
self.validate_quasistochastic()
|
|
290
|
+
|
|
291
|
+
def validate_quasistochastic(self) -> None:
|
|
292
|
+
total = sum(self.initial_quasidistribution.values())
|
|
293
|
+
if not np.isclose(total, 1.0):
|
|
294
|
+
raise QuasiStochasticValidationError(f"initial quasidistribution sums to {total}, not 1")
|
|
295
|
+
for state in self.initial_quasidistribution:
|
|
296
|
+
self._require(self.graph.has_state(state), f"unknown initial state {state!r}")
|
|
297
|
+
|
|
298
|
+
def word_probability(self, word: Sequence[Any]) -> float:
|
|
299
|
+
from sofic.generators.quasi_inference import word_probability
|
|
300
|
+
|
|
301
|
+
return word_probability(self, word)
|
|
302
|
+
|
|
303
|
+
def stationary_quasidistribution(self) -> np.ndarray:
|
|
304
|
+
from sofic.generators.quasi_inference import stationary_quasidistribution
|
|
305
|
+
|
|
306
|
+
return stationary_quasidistribution(self)
|
|
307
|
+
|
|
308
|
+
def transition_matrices(self) -> dict[Any, np.ndarray]:
|
|
309
|
+
from sofic.generators.quasi_inference import transition_matrices
|
|
310
|
+
|
|
311
|
+
return transition_matrices(self)
|
|
312
|
+
|
|
313
|
+
def words_of_length(self, length: int) -> dict[tuple[Any, ...], float]:
|
|
314
|
+
"""Return words of ``length`` and their signed quasiprobabilities."""
|
|
315
|
+
from sofic.generators.words import quasi_words_of_length
|
|
316
|
+
|
|
317
|
+
return quasi_words_of_length(self, length)
|
|
318
|
+
|
|
319
|
+
def collision_entropy(self) -> float:
|
|
320
|
+
from sofic.generators.measures import collision_entropy
|
|
321
|
+
|
|
322
|
+
return collision_entropy(self)
|
|
323
|
+
|
|
324
|
+
def process_negativity(self) -> float:
|
|
325
|
+
from sofic.generators.measures import process_negativity
|
|
326
|
+
|
|
327
|
+
return process_negativity(self)
|