Integer Map Example: Collatz Sequence
Overview
This example shows how to build a map-type simulation that uses an integer state variable. In contrast to the floating-point logistic map used elsewhere in the docs, this model keeps the Collatz iteration in the integer domain, letting you inspect integer arithmetic.
Key Concepts
mapstepper: A discrete-time map that updates the state once per step with no notion of continuous time.- Integer dtype (
int64): Ensures every iterated value remains precise so the Collatz sequence can be compared exactly to a pre-computed reference. - Assertion-based validation:
numpy.testing.assert_array_equalchecks the full trajectory against the expected 1-4-2-1 cycle. - Series plotting: Visualizes the integer trajectory to highlight when it converges to the 4-2-1 cycle.
Collatz Map Definition
The inline Toml definition uses a single integer state n initialized to 27 and an int64 right-hand side that branches on parity:
[model]
type = "map"
dtype = "int64"
name = "Collatz Conjecture"
[states]
n = 27
[equations.rhs]
n = "n//2 if n % 2 == 0 else 3*n + 1"
Running the Simulation
The script instantiates the model with setup(..., stepper="map"), runs len(expected) - 1 iterations, and reads the results table via sim.results(). The plot uses series.plot to show n vs. iteration and checks that the recorded values match the prepared expected array (the known sequence starting at 27 and terminating in the 1-4-2-1 loop). The example also prints the tail of the trajectory and the state dtype so you can confirm that the cycle and datatype are preserved.
Plotting and Export
theme.use("paper") configures matplotlib for publication-ready styles, and export.show() displays the figure. The plot's axis labels (iteration and n) and title (Collatz Conjecture) make it easy to see how the iterates descend before settling into the familiar cycle.
Reference Script
from dynlib import setup
from dynlib.plot import series, export, theme
import numpy as np
from numpy.testing import assert_array_equal
model = '''
inline:
[model]
type = "map"
dtype = "int64"
name = "Collatz Conjecture"
[states]
n = 27
[equations.rhs]
n = "n//2 if n % 2 == 0 else 3*n + 1"
'''
expected = np.array([
27, 82, 41, 124, 62, 31, 94, 47, 142, 71, 214, 107, 322, 161, 484, 242, 121,
364, 182, 91, 274, 137, 412, 206, 103, 310, 155, 466, 233, 700, 350, 175,
526, 263, 790, 395, 1186, 593, 1780, 890, 445, 1336, 668, 334, 167, 502,
251, 754, 377, 1132, 566, 283, 850, 425, 1276, 638, 319, 958, 479, 1438,
719, 2158, 1079, 3238, 1619, 4858, 2429, 7288, 3644, 1822, 911, 2734, 1367,
4102, 2051, 6154, 3077, 9232, 4616, 2308, 1154, 577, 1732, 866, 433, 1300,
650, 325, 976, 488, 244, 122, 61, 184, 92, 46, 23, 70, 35, 106, 53, 160,
80, 40, 20, 10, 5, 16, 8, 4, 2, 1
], dtype=np.int64)
sim = setup(model, stepper="map")
sim.run(N=len(expected)-1)
res = sim.results()
theme.use("paper")
series.plot(x=res.step, y=res["n"], xlabel="iteration", ylabel="n", ylabel_rot=0, title="Collatz Conjecture")
print("1-4-2-1 cycle: ", res["n"][-6:])
print("dtype for n : ", res["n"].dtype)
if res["n"][0] == 27:
assert_array_equal(res["n"], expected)
print("✅ Collatz sequence matches exactly!")
export.show()
sequence validation and plotting logic. The script doubles as a regression test by asserting that every integer in the run matches the expected NumPy array.