Releases: maxencefaldor/cax
v0.3.3
CAX v0.3.3 Release Notes
✍️ Improve docstrings
Improve docstrings across the repository.
📚 Add documentation
Add documentation with mkdocs, available with GitHub Pages here: https://maxencefaldor.github.io/cax/
v0.3.0
CAX v0.3.0 Release Notes
🎯 Major Refactoring: From Cellular Automata to Complex Systems
This release represents a significant architectural evolution that makes CAX more general and flexible while maintaining its core performance benefits. The library now supports a broader range of self-organizing systems beyond traditional cellular automata.
🔄 Key Changes
1. Generalized Naming: Cellular Automata → Complex Systems
- What changed:
ca.py→cs.pycax.systems→cax.csCA→ComplexSystem
- Why: CAX now supports particle systems, neural networks, and other self-organizing systems beyond traditional cellular automata
- Impact: More accurate representation of the library's capabilities
2. Unified Metrics with Flax NNX Sowing
- What changed: Replaced custom metrics functions with
nnx.sowfrom Flax NNX - Why: Better integration with the Flax NNX ecosystem and more efficient intermediate value tracking
- Impact: Cleaner code and better performance for monitoring system behavior
# Before
def _step(self, state, input=None, *, sow=False):
perception = self.perceive(state)
next_state = self.update(state, perception, input)
return next_state, self.metrics_fn(next_state, state, perception, input)
# After
def _step(self, state, input=None, *, sow=False):
perception = self.perceive(state)
next_state = self.update(state, perception, input)
if sow:
metrics = metrics_fn(next_state, R=self.perceive.R)
self.sow(nnx.Intermediate, "state", next_state)
self.sow(nnx.Intermediate, "metrics", metrics)
return next_state3. Flexible System Architecture: Optional Perceive/Update Pattern
- What changed: The
ComplexSystembase class now has an empty_stepmethod - Why: Not all complex systems follow the perceive-update pattern (e.g., particle systems)
- Impact: More flexibility for custom system implementations while keeping perceive/update modules available as building blocks
# Custom systems can now implement any _step logic
class CustomSystem(ComplexSystem):
def _step(self, state, input, *, sow=False):
# Any computation logic here
...
return state4. Genotype/Phenotype Pattern for System Instantiation 🧬
This is the most significant architectural improvement. All complex systems now follow a genotype/phenotype pattern that makes system instantiation and sampling much more uniform and intuitive.
The Genotype/Phenotype Analogy
Think of complex systems like biological organisms:
- Simulation Parameters: The "environment" or "world" - spatial dimensions, resolution, and other simulation-specific settings
- Genotype: The "genetic code" - compact parameters that define the system's behavior
- Phenotype: The "expressed traits" - all internal attributes needed for computation
How It Works: Lenia Example
Let's trace through how this works with Lenia:
1. Simulation Parameters:
# These define the "environment" where the system operates
spatial_dims = (128, 128) # Grid size
channel_size = 3 # Number of channels
R = 12 # Space resolution
T = 2 # Time resolution
state_scale = 2 # Scaling factor2. Genotype:
# The "genetic code" includes function choices
kernel_fn = gaussian_kernel_fn # How to compute the kernel
growth_fn = exponential_growth_fn # How to compute growth
# The "genetic code" also includes parameters
rule_params = LeniaRuleParams(
channel_source=0, # Which channel to read from
channel_target=1, # Which channel to write to
weight=1.0, # Interaction strength
kernel_params=KernelParams( # Kernel params
r=0.82,
b=jnp.array([1/6, 1.0, 0.2]),
),
growth_params=GrowthParams( # Growth params
mean=0.0,
std=0.05,
)
)3. Phenotype Expression:
# The __init__ method "expresses" the genotype into phenotype
self.channel_size = channel_size
self.T = T
self.weight = rule_params.weight
self.reshape_kernel_to_channel = self._reshape_kernel_to_channel(rule_params)
self.growth_fn = growth_fn
self.growth_params = nnx.data(rule_params.growth_params)4. The Magic:
- Sampling: You can easily sample new "genotypes" (rule parameters + functions) to create different system behaviors
- Efficiency: The phenotype (internal state) is pre-computed during initialization, making simulation fast
- Uniformity: All systems follow the same pattern, making the API consistent
- Flexibility: Same genotype can be expressed in different simulation environments
Benefits of This Pattern:
- Easy Sampling: Create variations by sampling different rule parameters and function choices
# Sample different Lenia "genotypes"
rule_params = sample_rule_params(key)
kernel_fn = random.choice([gaussian_kernel_fn, polynomial_kernel_fn])
growth_fn = random.choice([exponential_growth_fn, linear_growth_fn])
cs = Lenia(spatial_dims, channel_size, R=R, T=T, kernel_fn=kernel_fn, growth_fn=growth_fn, rule_params=rule_params)- Efficient Computation: Internal attributes are pre-computed once during initialization
- Clean Separation: Simulation/Genotype/Phenotype
- Consistent API: All complex systems follow the same instantiation pattern
The genotype/phenotype pattern makes CAX more powerful for research into open-ended evolution and emergent complexity, as you can now easily sample and evolve the "genetic code" of complex systems!
v0.2.1
v0.2.0
CHANGELOG
- New Getting Started notebook https://colab.research.google.com/github/maxencefaldor/cax/blob/main/examples/00_getting_started.ipynb
- Implemented hardware-accelerated rendering methods for all systems
- Better metrics logging during system scan
- Refactor existing systems such as Elementary CA, Life and Lenia to be more general and easy to use
- Added Flow-Lenia
- Added Particle Lenia
- Added Particle Life
- Added Boids
- Added Attention-based Neural Cellular Automata