Thermal and Stochastic Automata
A deterministic rule is an idealization — real systems shake. The three
classes in this guide corrupt the elementary automata in three physically
distinct ways, and each reduces exactly to the clean
pyCA.eca.ECA in the appropriate limit. The tests in the repository
hold them to that promise.
The Ising cellular automaton
pyCA.ica.ICA makes the lattice a hybrid: each step, every cell
independently chooses (with probability stochfrac) whether to behave as an
Ising spin in contact with a heat bath or as an obedient cell of the
underlying rule. The thermal cells look at their local Ising energy —
aligned neighbors mean low energy, frustrated neighbors mean high — and flip
with the heat-bath[1] probability
\(1/(1 + e^{-2E_i/T})\). Frustrated
cells flip eagerly; aligned cells hold fast, absolutely so as
\(T \to 0\).
from pyCA import ICA
ica = ICA(110, N=256, temperature=1.5, stochfrac=0.3)
ica.run(300)
print(ica.energy) # mean Ising energy per site
Sweep the temperature at fixed stochfrac and watch the energy respond; then sweep stochfrac at fixed temperature and watch rule 110’s gliders fight the noise. At what noise level do the gliders stop surviving long enough to collide? That question is not rhetorical — map it out.
Noisy rules
pyCA.stochastic.NoisyECA applies the rule everywhere, then flips
each output bit independently with probability noise — the
epsilon-perturbed automata. Small noise turns sharp class boundaries into
genuine phase transitions[2]:
a Class II texture can survive small epsilon and
dissolve at large, with a critical point in between.
from pyCA import NoisyECA
noisy = NoisyECA(90, N=512, noise=0.005)
noisy.run(500)
At noise = 0 you have the deterministic rule; at noise = 1, its complement; at noise = 0.5 the rule is forgotten entirely and every cell is a fair coin.
Asynchronous updating
pyCA.stochastic.AsyncECA never corrupts the rule — it corrupts the
clock. Each step, each cell updates with probability update_fraction and
otherwise holds its value. Synchrony is a strong
assumption[3], and some
celebrated CA behaviors lean on it harder than you might expect:
from pyCA import AsyncECA
lazy = AsyncECA(110, N=256, update_fraction=0.7)
lazy.run(300)
Run rule 110 at a few update fractions and watch what survives. The gliders that carry rule 110’s computation are creatures of the synchronous clock; how gracefully they degrade is best seen with your own eyes.