Skip to content

Releases: maxencefaldor/cax

v0.3.3

09 Nov 12:14

Choose a tag to compare

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

25 Oct 13:52

Choose a tag to compare

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.pycs.py
    • cax.systemscax.cs
    • CAComplexSystem
  • 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.sow from 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_state

3. Flexible System Architecture: Optional Perceive/Update Pattern

  • What changed: The ComplexSystem base class now has an empty _step method
  • 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 state

4. 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 factor

2. 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:

  1. 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)
  1. Efficient Computation: Internal attributes are pre-computed once during initialization
  2. Clean Separation: Simulation/Genotype/Phenotype
  3. 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

13 May 10:46

Choose a tag to compare

  • Add notebook to evolve NCA with an evolution strategy
  • Fix issue #17
  • Fix issue #18

v0.2.0

21 Apr 03:42
08e7df2

Choose a tag to compare

CHANGELOG