xbe.at

Miscellany / Embeddings and vectors

Quantifying Complexity in the Coffee Automaton

8 min read, 15 sections

Contents
  1. The Coffee Automaton: Exploring Complexity in Closed Systems
  2. Understanding Closed Systems
  3. Defining Complexity in the Coffee Automaton
  4. The Rise of Complexity: Brewing Process
  5. Peak Complexity: The Perfect Cup
  6. The Fall of Complexity: Entropy Takes Over
  7. Quantifying the Complexity Curve
  8. Emergent Patterns: Identifying Structure
  9. Real-Life Example: Ecosystem Dynamics
  10. Real-Life Example: Urban Development
  11. Measuring Information Content
  12. Feedback Loops and Self-Organization
  13. Analyzing Phase Transitions
  14. Limitations and Future Directions
  15. Additional Resources

The Coffee Automaton: Exploring Complexity in Closed Systems

The Coffee Automaton is a conceptual model used to study the emergence and decay of complexity in closed systems. This model draws inspiration from the process of brewing and consuming coffee, serving as an analogy for more complex systems in nature and society.

import random

class CoffeeAutomaton:
    def __init__(self, size):
        self.grid = [[random.choice(['C', 'W', ' ']) for _ in range(size)] for _ in range(size)]
        self.size = size

    def display(self):
        for row in self.grid:
            print(' '.join(row))
        print()

coffee = CoffeeAutomaton(10)
coffee.display()

Understanding Closed Systems

A closed system is one that does not exchange matter with its surroundings but may exchange energy. In our Coffee Automaton, the coffee cup represents a closed system where complexity arises and eventually dissipates.

def is_closed_system(system):
    initial_matter = sum(cell != ' ' for row in system.grid for cell in row)
    
    # Simulate some steps
    for _ in range(10):
        system.step()
    
    final_matter = sum(cell != ' ' for row in system.grid for cell in row)
    
    return initial_matter == final_matter

coffee = CoffeeAutomaton(10)
print(f"Is closed system: {is_closed_system(coffee)}")

Defining Complexity in the Coffee Automaton

In our model, complexity is defined by the interactions between coffee particles ('C') and water molecules ('W'). The more diverse and structured these interactions, the higher the complexity.

def calculate_complexity(grid):
    complexity = 0
    for i in range(len(grid)):
        for j in range(len(grid)):
            if grid[i][j] == 'C':
                for di, dj in [(-1,0), (1,0), (0,-1), (0,1)]:
                    ni, nj = i + di, j + dj
                    if 0 <= ni < len(grid) and 0 <= nj < len(grid) and grid[ni][nj] == 'W':
                        complexity += 1
    return complexity

coffee = CoffeeAutomaton(10)
print(f"Initial complexity: {calculate_complexity(coffee.grid)}")

The Rise of Complexity: Brewing Process

As we "brew" our coffee, complexity increases. This is analogous to the initial stages of many systems where components begin to interact and form more intricate structures.

def brew(self):
    for i in range(self.size):
        for j in range(self.size):
            if self.grid[i][j] == ' ':
                if random.random() < 0.1:
                    self.grid[i][j] = random.choice(['C', 'W'])

CoffeeAutomaton.brew = brew

coffee = CoffeeAutomaton(10)
print("Before brewing:")
coffee.display()
coffee.brew()
print("After brewing:")
coffee.display()
print(f"Complexity after brewing: {calculate_complexity(coffee.grid)}")

Peak Complexity: The Perfect Cup

At some point, our coffee reaches peak complexity. This represents a state of maximum interaction and structure within the system.

def find_peak_complexity(size, max_steps):
    coffee = CoffeeAutomaton(size)
    peak_complexity = 0
    peak_step = 0
    
    for step in range(max_steps):
        coffee.brew()
        current_complexity = calculate_complexity(coffee.grid)
        if current_complexity > peak_complexity:
            peak_complexity = current_complexity
            peak_step = step
    
    return peak_complexity, peak_step

peak, step = find_peak_complexity(10, 100)
print(f"Peak complexity {peak} reached at step {step}")

The Fall of Complexity: Entropy Takes Over

As time passes, the system tends towards equilibrium. In our coffee model, this is represented by the mixing and cooling of the coffee, leading to a decrease in complexity.

def cool_and_mix(self):
    for i in range(self.size):
        for j in range(self.size):
            if random.random() < 0.05:
                if self.grid[i][j] in ['C', 'W']:
                    self.grid[i][j] = ' '
            elif random.random() < 0.1:
                ni, nj = (i + random.choice([-1, 0, 1])) % self.size, (j + random.choice([-1, 0, 1])) % self.size
                self.grid[i][j], self.grid[ni][nj] = self.grid[ni][nj], self.grid[i][j]

CoffeeAutomaton.cool_and_mix = cool_and_mix

coffee = CoffeeAutomaton(10)
coffee.brew()
print("After brewing:")
coffee.display()
for _ in range(5):
    coffee.cool_and_mix()
print("After cooling and mixing:")
coffee.display()

Quantifying the Complexity Curve

To understand the rise and fall of complexity, we can plot the complexity over time. This gives us a visual representation of how the system evolves.

import matplotlib.pyplot as plt

def plot_complexity_curve(size, steps):
    coffee = CoffeeAutomaton(size)
    complexities = []
    
    for _ in range(steps):
        coffee.brew()
        coffee.cool_and_mix()
        complexities.append(calculate_complexity(coffee.grid))
    
    plt.plot(range(steps), complexities)
    plt.xlabel('Time')
    plt.ylabel('Complexity')
    plt.title('Complexity over Time in Coffee Automaton')
    plt.show()

plot_complexity_curve(10, 100)

Emergent Patterns: Identifying Structure

As our coffee system evolves, we may observe emergent patterns or structures. These can be quantified and analyzed to better understand the system's behavior.

def identify_patterns(grid):
    patterns = {
        'clusters': 0,
        'alternating': 0,
        'empty_regions': 0
    }
    
    for i in range(len(grid) - 1):
        for j in range(len(grid[i]) - 1):
            if grid[i][j] == grid[i+1][j] == grid[i][j+1] == grid[i+1][j+1] != ' ':
                patterns['clusters'] += 1
            if grid[i][j] != grid[i+1][j] and grid[i][j] != grid[i][j+1] and grid[i][j] != ' ':
                patterns['alternating'] += 1
            if grid[i][j] == grid[i+1][j] == grid[i][j+1] == grid[i+1][j+1] == ' ':
                patterns['empty_regions'] += 1
    
    return patterns

coffee = CoffeeAutomaton(10)
coffee.brew()
print("Identified patterns:", identify_patterns(coffee.grid))

Real-Life Example: Ecosystem Dynamics

The Coffee Automaton can be seen as an analogy for ecosystem dynamics. Just as our coffee system has periods of increasing complexity (species interactions) followed by simplification (loss of biodiversity), so too do real ecosystems experience similar patterns.

class Ecosystem(CoffeeAutomaton):
    def __init__(self, size):
        super().__init__(size)
        self.species = {'A': 0, 'B': 0, 'C': 0}
    
    def populate(self):
        for i in range(self.size):
            for j in range(self.size):
                if self.grid[i][j] == ' ':
                    if random.random() < 0.1:
                        species = random.choice(list(self.species.keys()))
                        self.grid[i][j] = species
                        self.species[species] += 1
    
    def interact(self):
        for i in range(self.size):
            for j in range(self.size):
                if self.grid[i][j] != ' ':
                    if random.random() < 0.05:  # Death
                        self.species[self.grid[i][j]] -= 1
                        self.grid[i][j] = ' '
                    elif random.random() < 0.1:  # Reproduction
                        ni, nj = (i + random.choice([-1, 0, 1])) % self.size, (j + random.choice([-1, 0, 1])) % self.size
                        if self.grid[ni][nj] == ' ':
                            self.grid[ni][nj] = self.grid[i][j]
                            self.species[self.grid[i][j]] += 1

ecosystem = Ecosystem(10)
for _ in range(10):
    ecosystem.populate()
    ecosystem.interact()
print("Species count:", ecosystem.species)

Real-Life Example: Urban Development

Another real-life application of the Coffee Automaton concept can be seen in urban development. Cities grow in complexity as they develop, but may eventually face challenges that lead to simplification or decay in certain areas.

class UrbanArea(CoffeeAutomaton):
    def __init__(self, size):
        super().__init__(size)
        self.structures = {'R': 0, 'C': 0, 'I': 0}  # Residential, Commercial, Industrial
    
    def develop(self):
        for i in range(self.size):
            for j in range(self.size):
                if self.grid[i][j] == ' ':
                    if random.random() < 0.1:
                        structure = random.choice(list(self.structures.keys()))
                        self.grid[i][j] = structure
                        self.structures[structure] += 1
    
    def urban_dynamics(self):
        for i in range(self.size):
            for j in range(self.size):
                if self.grid[i][j] != ' ':
                    if random.random() < 0.05:  # Decay
                        self.structures[self.grid[i][j]] -= 1
                        self.grid[i][j] = ' '
                    elif random.random() < 0.1:  # Expansion
                        ni, nj = (i + random.choice([-1, 0, 1])) % self.size, (j + random.choice([-1, 0, 1])) % self.size
                        if self.grid[ni][nj] == ' ':
                            self.grid[ni][nj] = self.grid[i][j]
                            self.structures[self.grid[i][j]] += 1

city = UrbanArea(10)
for _ in range(10):
    city.develop()
    city.urban_dynamics()
print("Urban structures:", city.structures)

Measuring Information Content

We can use information theory to quantify the complexity in our system. One way to do this is by calculating the entropy, which measures the unpredictability or randomness in the system.

import math

def calculate_entropy(grid):
    flat_grid = [cell for row in grid for cell in row]
    total = len(flat_grid)
    probabilities = {symbol: flat_grid.count(symbol) / total for symbol in set(flat_grid)}
    return -sum(p * math.log2(p) for p in probabilities.values() if p > 0)

coffee = CoffeeAutomaton(10)
coffee.brew()
entropy = calculate_entropy(coffee.grid)
print(f"System entropy: {entropy}")

Feedback Loops and Self-Organization

Complex systems often exhibit feedback loops and self-organization. In our Coffee Automaton, we can model this by introducing rules that create reinforcing or balancing effects.

def self_organize(self):
    new_grid = [row[:] for row in self.grid]
    for i in range(self.size):
        for j in range(self.size):
            neighbors = self.count_neighbors(i, j)
            if self.grid[i][j] == 'C':
                if neighbors['W'] > 2:  # Coffee dissolves if surrounded by water
                    new_grid[i][j] = 'W'
            elif self.grid[i][j] == 'W':
                if neighbors['C'] > 3:  # Water becomes coffee if surrounded by coffee
                    new_grid[i][j] = 'C'
    self.grid = new_grid

def count_neighbors(self, i, j):
    neighbors = {'C': 0, 'W': 0, ' ': 0}
    for di in [-1, 0, 1]:
        for dj in [-1, 0, 1]:
            if di == 0 and dj == 0:
                continue
            ni, nj = (i + di) % self.size, (j + dj) % self.size
            neighbors[self.grid[ni][nj]] += 1
    return neighbors

CoffeeAutomaton.self_organize = self_organize
CoffeeAutomaton.count_neighbors = count_neighbors

coffee = CoffeeAutomaton(10)
coffee.brew()
print("Before self-organization:")
coffee.display()
coffee.self_organize()
print("After self-organization:")
coffee.display()

Analyzing Phase Transitions

Complex systems often undergo phase transitions, where the system's behavior changes dramatically. In our Coffee Automaton, we can look for these transitions by analyzing how the system's properties change over time.

def analyze_phase_transitions(size, steps):
    coffee = CoffeeAutomaton(size)
    complexities = []
    entropies = []
    
    for _ in range(steps):
        coffee.brew()
        coffee.cool_and_mix()
        coffee.self_organize()
        complexities.append(calculate_complexity(coffee.grid))
        entropies.append(calculate_entropy(coffee.grid))
    
    plt.figure(figsize=(12, 6))
    plt.subplot(2, 1, 1)
    plt.plot(range(steps), complexities)
    plt.ylabel('Complexity')
    plt.subplot(2, 1, 2)
    plt.plot(range(steps), entropies)
    plt.xlabel('Time')
    plt.ylabel('Entropy')
    plt.tight_layout()
    plt.show()

analyze_phase_transitions(10, 100)

Limitations and Future Directions

While the Coffee Automaton provides insights into complexity in closed systems, it has limitations. Future work could explore open systems, multi-scale interactions, and more realistic physical models. These extensions could provide deeper insights into complex systems in nature and society.

class ExtendedCoffeeAutomaton(CoffeeAutomaton):
    def __init__(self, size, open_system=False, multi_scale=False):
        super().__init__(size)
        self.open_system = open_system
        self.multi_scale = multi_scale
    
    def step(self):
        self.brew()
        self.cool_and_mix()
        self.self_organize()
        if self.open_system:
            self.interact_with_environment()
        if self.multi_scale:
            self.update_large_scale_properties()
    
    def interact_with_environment(self):
        # Placeholder for open system interactions
        pass
    
    def update_large_scale_properties(self):
        # Placeholder for multi-scale interactions
        pass

# Example usage
extended_coffee = ExtendedCoffeeAutomaton(10, open_system=True, multi_scale=True)
extended_coffee.step()

Additional Resources

For those interested in exploring these concepts further, here are some relevant academic papers:

  1. "Complexity and Criticality in Cellular Automata" - ArXiv:1809.02577
  2. "Self-organized criticality in non-conserved systems" - ArXiv:cond-mat/0410460
  3. "Emergence of Complexity in Random Networks" - ArXiv:cond-mat/0206130
  4. "Quantifying Complexity in Dynamical Systems" - ArXiv:1708.07400

These papers provide in-depth discussions on complexity, cellular automata, and self-organized criticality, which are closely related to the concepts explored in our Coffee Automaton model. They offer more rigorous mathematical treatments and broader applications of these ideas in various fields of science.

More in Embeddings and vectors

  1. Solving the Traveling Salesman Problem10 min

    A fundamental aspect of solving the Traveling Salesman Problem is representing cities and distances efficiently. We implement a City class to store...

  2. Mathematics The Language of the Universe11 min

    Mathematics is often described as the universal language that unveils the secrets of the universe. This powerful tool allows us to describe, analyze, and...

  3. Structural Limits of the London Eye's Spin6 min

    The London Eye, a iconic Ferris wheel on the South Bank of the River Thames in London, stands as a symbol of modern engineering. This presentation explores a...

  4. Leonhard Euler Pioneering Mathematician and Physicist:8 min

    Leonhard Euler, a Swiss mathematician and physicist, revolutionized multiple fields of mathematics and laid the groundwork for modern mathematical notation....

  5. Text Vectorization Transforming Words into Numbers10 min

    Text vectorization transforms human-readable text into numerical representations that machine learning models can process. The foundation begins with...

  6. Introduction to Ergodic Theory10 min

    Ergodic theory is a branch of mathematics that studies dynamical systems with an invariant measure and related problems. It focuses on the long-term average...