Thermal and Stochastic Automata

A deterministic rule is an idealization; real systems shake. The three classes here corrupt the elementary automata in physically distinct ways, and each reduces exactly to the clean pyCA.eca.ECA in the appropriate limit. The tests hold them to it.

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. A thermal cell reads its local Ising energy - aligned neighbors mean low energy, frustrated neighbors mean high - and flips with the heat-bath[1] probability \(1/(1 + e^{-2E_i/T})\). Frustration makes it flip eagerly; alignment makes it 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 they stop surviving long enough to collide? 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, only 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.

References