Initial commit: Markov Economics Simulation App
This commit is contained in:
25
app/models/__init__.py
Normal file
25
app/models/__init__.py
Normal file
@@ -0,0 +1,25 @@
|
||||
"""
|
||||
Economic simulation models package.
|
||||
|
||||
This package contains the core Markov chain implementations and simulation engine
|
||||
for demonstrating how capitalism "eats the world" when r > g.
|
||||
"""
|
||||
|
||||
from .markov_chain import MarkovChain, CapitalistChain, ConsumerChain, EconomicAgent
|
||||
from .economic_model import (
|
||||
SimulationParameters,
|
||||
SimulationSnapshot,
|
||||
EconomicSimulation,
|
||||
SimulationManager
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'MarkovChain',
|
||||
'CapitalistChain',
|
||||
'ConsumerChain',
|
||||
'EconomicAgent',
|
||||
'SimulationParameters',
|
||||
'SimulationSnapshot',
|
||||
'EconomicSimulation',
|
||||
'SimulationManager'
|
||||
]
|
BIN
app/models/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
app/models/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
app/models/__pycache__/economic_model.cpython-312.pyc
Normal file
BIN
app/models/__pycache__/economic_model.cpython-312.pyc
Normal file
Binary file not shown.
BIN
app/models/__pycache__/markov_chain.cpython-312.pyc
Normal file
BIN
app/models/__pycache__/markov_chain.cpython-312.pyc
Normal file
Binary file not shown.
482
app/models/economic_model.py
Normal file
482
app/models/economic_model.py
Normal file
@@ -0,0 +1,482 @@
|
||||
"""
|
||||
Economic Simulation Engine
|
||||
|
||||
This module implements the main simulation engine that orchestrates multiple
|
||||
economic agents running Markov chains to demonstrate wealth inequality dynamics
|
||||
when capital return rate (r) exceeds economic growth rate (g).
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import uuid
|
||||
from typing import List, Dict, Tuple, Optional
|
||||
from dataclasses import dataclass
|
||||
import time
|
||||
import json
|
||||
|
||||
from .markov_chain import EconomicAgent
|
||||
|
||||
|
||||
@dataclass
|
||||
class SimulationParameters:
|
||||
"""Configuration parameters for economic simulation."""
|
||||
r_rate: float # Capital return rate
|
||||
g_rate: float # Economic growth rate
|
||||
initial_capital: float = 1000.0
|
||||
initial_consumption: float = 1000.0
|
||||
num_agents: int = 100
|
||||
iterations: int = 1000
|
||||
|
||||
def __post_init__(self):
|
||||
"""Validate parameters after initialization."""
|
||||
if self.r_rate < 0 or self.r_rate > 1:
|
||||
raise ValueError("Capital rate must be between 0 and 1")
|
||||
if self.g_rate < 0 or self.g_rate > 1:
|
||||
raise ValueError("Growth rate must be between 0 and 1")
|
||||
if self.num_agents < 1 or self.num_agents > 10000:
|
||||
raise ValueError("Number of agents must be between 1 and 10000")
|
||||
if self.iterations < 1 or self.iterations > 100000:
|
||||
raise ValueError("Iterations must be between 1 and 100000")
|
||||
|
||||
|
||||
@dataclass
|
||||
class SimulationSnapshot:
|
||||
"""Snapshot of simulation state at a specific iteration."""
|
||||
iteration: int
|
||||
timestamp: float
|
||||
wealth_distribution: List[float]
|
||||
consumption_distribution: List[float]
|
||||
total_wealth: float
|
||||
gini_coefficient: float
|
||||
wealth_concentration_top10: float
|
||||
capital_share: float
|
||||
average_wealth: float
|
||||
median_wealth: float
|
||||
|
||||
|
||||
class EconomicSimulation:
|
||||
"""
|
||||
Main simulation engine for demonstrating Piketty's inequality principle.
|
||||
|
||||
Manages multiple economic agents and tracks wealth distribution over time
|
||||
to show how r > g leads to increasing inequality.
|
||||
"""
|
||||
|
||||
def __init__(self, parameters: SimulationParameters):
|
||||
"""
|
||||
Initialize the economic simulation.
|
||||
|
||||
Args:
|
||||
parameters: Configuration parameters for the simulation
|
||||
"""
|
||||
self.parameters = parameters
|
||||
self.simulation_id = str(uuid.uuid4())
|
||||
self.agents: List[EconomicAgent] = []
|
||||
self.snapshots: List[SimulationSnapshot] = []
|
||||
self.current_iteration = 0
|
||||
self.is_running = False
|
||||
self.start_time = None
|
||||
|
||||
self._initialize_agents()
|
||||
|
||||
def _initialize_agents(self):
|
||||
"""Create economic agents with specified parameters."""
|
||||
self.agents = []
|
||||
|
||||
for i in range(self.parameters.num_agents):
|
||||
agent_id = f"agent_{i:04d}"
|
||||
|
||||
# Add some randomness to initial conditions
|
||||
initial_capital = self.parameters.initial_capital * (0.8 + 0.4 * np.random.random())
|
||||
initial_consumption = self.parameters.initial_consumption * (0.8 + 0.4 * np.random.random())
|
||||
|
||||
agent = EconomicAgent(
|
||||
agent_id=agent_id,
|
||||
capital_rate=self.parameters.r_rate,
|
||||
growth_rate=self.parameters.g_rate,
|
||||
initial_capital=initial_capital,
|
||||
initial_consumption=initial_consumption
|
||||
)
|
||||
|
||||
self.agents.append(agent)
|
||||
|
||||
def step(self) -> SimulationSnapshot:
|
||||
"""
|
||||
Perform one simulation step for all agents.
|
||||
|
||||
Returns:
|
||||
Snapshot of the current simulation state
|
||||
"""
|
||||
# Step all agents
|
||||
for agent in self.agents:
|
||||
agent.step()
|
||||
|
||||
# Create snapshot
|
||||
snapshot = self._create_snapshot()
|
||||
self.snapshots.append(snapshot)
|
||||
self.current_iteration += 1
|
||||
|
||||
return snapshot
|
||||
|
||||
def run_simulation(self, iterations: Optional[int] = None) -> List[SimulationSnapshot]:
|
||||
"""
|
||||
Run the complete simulation for specified iterations.
|
||||
|
||||
Args:
|
||||
iterations: Number of iterations to run (uses parameter default if None)
|
||||
|
||||
Returns:
|
||||
List of snapshots for each iteration
|
||||
"""
|
||||
if iterations is None:
|
||||
iterations = self.parameters.iterations
|
||||
|
||||
self.is_running = True
|
||||
self.start_time = time.time()
|
||||
|
||||
try:
|
||||
for _ in range(iterations):
|
||||
if not self.is_running:
|
||||
break
|
||||
self.step()
|
||||
finally:
|
||||
self.is_running = False
|
||||
|
||||
return self.snapshots.copy()
|
||||
|
||||
def _create_snapshot(self) -> SimulationSnapshot:
|
||||
"""Create a snapshot of current simulation state."""
|
||||
# Collect wealth data
|
||||
wealth_data = []
|
||||
consumption_data = []
|
||||
total_wealth_data = []
|
||||
|
||||
for agent in self.agents:
|
||||
if agent.wealth_history and agent.consumption_history:
|
||||
wealth = agent.wealth_history[-1]
|
||||
consumption = agent.consumption_history[-1]
|
||||
else:
|
||||
wealth = agent.initial_capital
|
||||
consumption = agent.initial_consumption
|
||||
|
||||
wealth_data.append(wealth)
|
||||
consumption_data.append(consumption)
|
||||
total_wealth_data.append(wealth + consumption)
|
||||
|
||||
# Calculate metrics
|
||||
total_wealth = sum(total_wealth_data)
|
||||
gini = self._calculate_gini_coefficient(total_wealth_data)
|
||||
wealth_conc = self._calculate_wealth_concentration(total_wealth_data, 0.1)
|
||||
capital_share = sum(wealth_data) / total_wealth if total_wealth > 0 else 0
|
||||
|
||||
return SimulationSnapshot(
|
||||
iteration=self.current_iteration,
|
||||
timestamp=time.time(),
|
||||
wealth_distribution=wealth_data,
|
||||
consumption_distribution=consumption_data,
|
||||
total_wealth=total_wealth,
|
||||
gini_coefficient=gini,
|
||||
wealth_concentration_top10=wealth_conc,
|
||||
capital_share=capital_share,
|
||||
average_wealth=np.mean(total_wealth_data),
|
||||
median_wealth=np.median(total_wealth_data)
|
||||
)
|
||||
|
||||
def _calculate_gini_coefficient(self, wealth_data: List[float]) -> float:
|
||||
"""
|
||||
Calculate the Gini coefficient for wealth inequality.
|
||||
|
||||
Args:
|
||||
wealth_data: List of wealth values
|
||||
|
||||
Returns:
|
||||
Gini coefficient between 0 (perfect equality) and 1 (perfect inequality)
|
||||
"""
|
||||
if not wealth_data or len(wealth_data) < 2:
|
||||
return 0.0
|
||||
|
||||
# Sort wealth data
|
||||
sorted_wealth = sorted(wealth_data)
|
||||
n = len(sorted_wealth)
|
||||
|
||||
# Calculate Gini coefficient
|
||||
cumsum = np.cumsum(sorted_wealth)
|
||||
total_wealth = cumsum[-1]
|
||||
|
||||
if total_wealth == 0:
|
||||
return 0.0
|
||||
|
||||
# Gini coefficient formula
|
||||
gini = (2 * sum((i + 1) * wealth for i, wealth in enumerate(sorted_wealth))) / (n * total_wealth) - (n + 1) / n
|
||||
|
||||
return max(0.0, min(1.0, gini))
|
||||
|
||||
def _calculate_wealth_concentration(self, wealth_data: List[float], top_percentage: float) -> float:
|
||||
"""
|
||||
Calculate the share of total wealth held by the top percentage of agents.
|
||||
|
||||
Args:
|
||||
wealth_data: List of wealth values
|
||||
top_percentage: Percentage of top agents (e.g., 0.1 for top 10%)
|
||||
|
||||
Returns:
|
||||
Share of total wealth held by top agents
|
||||
"""
|
||||
if not wealth_data:
|
||||
return 0.0
|
||||
|
||||
sorted_wealth = sorted(wealth_data, reverse=True)
|
||||
total_wealth = sum(sorted_wealth)
|
||||
|
||||
if total_wealth == 0:
|
||||
return 0.0
|
||||
|
||||
top_count = max(1, int(len(sorted_wealth) * top_percentage))
|
||||
top_wealth = sum(sorted_wealth[:top_count])
|
||||
|
||||
return top_wealth / total_wealth
|
||||
|
||||
def get_latest_snapshot(self) -> Optional[SimulationSnapshot]:
|
||||
"""Get the most recent simulation snapshot."""
|
||||
return self.snapshots[-1] if self.snapshots else None
|
||||
|
||||
def get_snapshot_at_iteration(self, iteration: int) -> Optional[SimulationSnapshot]:
|
||||
"""Get snapshot at specific iteration."""
|
||||
for snapshot in self.snapshots:
|
||||
if snapshot.iteration == iteration:
|
||||
return snapshot
|
||||
return None
|
||||
|
||||
def get_wealth_evolution(self) -> Tuple[List[int], List[float], List[float]]:
|
||||
"""
|
||||
Get wealth evolution data for plotting.
|
||||
|
||||
Returns:
|
||||
Tuple of (iterations, total_wealth_over_time, gini_over_time)
|
||||
"""
|
||||
if not self.snapshots:
|
||||
return [], [], []
|
||||
|
||||
iterations = [s.iteration for s in self.snapshots]
|
||||
total_wealth = [s.total_wealth for s in self.snapshots]
|
||||
gini_coefficients = [s.gini_coefficient for s in self.snapshots]
|
||||
|
||||
return iterations, total_wealth, gini_coefficients
|
||||
|
||||
def get_agent_wealth_distribution(self) -> Dict[str, List[float]]:
|
||||
"""
|
||||
Get current wealth distribution across all agents.
|
||||
|
||||
Returns:
|
||||
Dictionary with agent IDs and their current wealth values
|
||||
"""
|
||||
distribution = {}
|
||||
|
||||
for agent in self.agents:
|
||||
if agent.wealth_history:
|
||||
distribution[agent.agent_id] = agent.wealth_history[-1]
|
||||
else:
|
||||
distribution[agent.agent_id] = agent.initial_capital
|
||||
|
||||
return distribution
|
||||
|
||||
def update_parameters(self, new_parameters: SimulationParameters):
|
||||
"""
|
||||
Update simulation parameters and restart with new settings.
|
||||
|
||||
Args:
|
||||
new_parameters: New simulation configuration
|
||||
"""
|
||||
self.parameters = new_parameters
|
||||
self.current_iteration = 0
|
||||
self.snapshots.clear()
|
||||
self._initialize_agents()
|
||||
|
||||
def stop_simulation(self):
|
||||
"""Stop the running simulation."""
|
||||
self.is_running = False
|
||||
|
||||
def reset_simulation(self):
|
||||
"""Reset simulation to initial state."""
|
||||
self.current_iteration = 0
|
||||
self.snapshots.clear()
|
||||
self.is_running = False
|
||||
self._initialize_agents()
|
||||
|
||||
def export_data(self, format_type: str = 'json') -> str:
|
||||
"""
|
||||
Export simulation data in specified format.
|
||||
|
||||
Args:
|
||||
format_type: Export format ('json' or 'csv')
|
||||
|
||||
Returns:
|
||||
Formatted data string
|
||||
"""
|
||||
if format_type.lower() == 'json':
|
||||
return self._export_json()
|
||||
elif format_type.lower() == 'csv':
|
||||
return self._export_csv()
|
||||
else:
|
||||
raise ValueError("Format must be 'json' or 'csv'")
|
||||
|
||||
def _export_json(self) -> str:
|
||||
"""Export simulation data as JSON."""
|
||||
data = {
|
||||
'simulation_id': self.simulation_id,
|
||||
'parameters': {
|
||||
'r_rate': self.parameters.r_rate,
|
||||
'g_rate': self.parameters.g_rate,
|
||||
'initial_capital': self.parameters.initial_capital,
|
||||
'initial_consumption': self.parameters.initial_consumption,
|
||||
'num_agents': self.parameters.num_agents,
|
||||
'iterations': self.parameters.iterations
|
||||
},
|
||||
'snapshots': []
|
||||
}
|
||||
|
||||
for snapshot in self.snapshots:
|
||||
snapshot_data = {
|
||||
'iteration': snapshot.iteration,
|
||||
'timestamp': snapshot.timestamp,
|
||||
'total_wealth': snapshot.total_wealth,
|
||||
'gini_coefficient': snapshot.gini_coefficient,
|
||||
'wealth_concentration_top10': snapshot.wealth_concentration_top10,
|
||||
'capital_share': snapshot.capital_share,
|
||||
'average_wealth': snapshot.average_wealth,
|
||||
'median_wealth': snapshot.median_wealth
|
||||
}
|
||||
data['snapshots'].append(snapshot_data)
|
||||
|
||||
return json.dumps(data, indent=2)
|
||||
|
||||
def _export_csv(self) -> str:
|
||||
"""Export simulation data as CSV."""
|
||||
if not self.snapshots:
|
||||
return "iteration,total_wealth,gini_coefficient,wealth_concentration_top10,capital_share,average_wealth,median_wealth\n"
|
||||
|
||||
lines = ["iteration,total_wealth,gini_coefficient,wealth_concentration_top10,capital_share,average_wealth,median_wealth"]
|
||||
|
||||
for snapshot in self.snapshots:
|
||||
line = f"{snapshot.iteration},{snapshot.total_wealth},{snapshot.gini_coefficient}," \
|
||||
f"{snapshot.wealth_concentration_top10},{snapshot.capital_share}," \
|
||||
f"{snapshot.average_wealth},{snapshot.median_wealth}"
|
||||
lines.append(line)
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
class SimulationManager:
|
||||
"""
|
||||
Manages multiple simulation instances for concurrent access.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the simulation manager."""
|
||||
self.simulations: Dict[str, EconomicSimulation] = {}
|
||||
self.active_simulations: Dict[str, EconomicSimulation] = {}
|
||||
|
||||
def create_simulation(self, parameters: SimulationParameters) -> str:
|
||||
"""
|
||||
Create a new simulation instance.
|
||||
|
||||
Args:
|
||||
parameters: Simulation configuration
|
||||
|
||||
Returns:
|
||||
Unique simulation ID
|
||||
"""
|
||||
simulation = EconomicSimulation(parameters)
|
||||
simulation_id = simulation.simulation_id
|
||||
|
||||
self.simulations[simulation_id] = simulation
|
||||
|
||||
return simulation_id
|
||||
|
||||
def get_simulation(self, simulation_id: str) -> Optional[EconomicSimulation]:
|
||||
"""
|
||||
Get simulation instance by ID.
|
||||
|
||||
Args:
|
||||
simulation_id: Unique simulation identifier
|
||||
|
||||
Returns:
|
||||
Simulation instance or None if not found
|
||||
"""
|
||||
return self.simulations.get(simulation_id)
|
||||
|
||||
def start_simulation(self, simulation_id: str, iterations: Optional[int] = None) -> bool:
|
||||
"""
|
||||
Start running a simulation.
|
||||
|
||||
Args:
|
||||
simulation_id: Unique simulation identifier
|
||||
iterations: Number of iterations to run
|
||||
|
||||
Returns:
|
||||
True if simulation started successfully
|
||||
"""
|
||||
simulation = self.get_simulation(simulation_id)
|
||||
if not simulation:
|
||||
return False
|
||||
|
||||
self.active_simulations[simulation_id] = simulation
|
||||
# In a real application, this would run in a separate thread
|
||||
simulation.run_simulation(iterations)
|
||||
|
||||
return True
|
||||
|
||||
def stop_simulation(self, simulation_id: str) -> bool:
|
||||
"""
|
||||
Stop a running simulation.
|
||||
|
||||
Args:
|
||||
simulation_id: Unique simulation identifier
|
||||
|
||||
Returns:
|
||||
True if simulation stopped successfully
|
||||
"""
|
||||
simulation = self.get_simulation(simulation_id)
|
||||
if simulation:
|
||||
simulation.stop_simulation()
|
||||
self.active_simulations.pop(simulation_id, None)
|
||||
return True
|
||||
return False
|
||||
|
||||
def delete_simulation(self, simulation_id: str) -> bool:
|
||||
"""
|
||||
Delete a simulation instance.
|
||||
|
||||
Args:
|
||||
simulation_id: Unique simulation identifier
|
||||
|
||||
Returns:
|
||||
True if simulation deleted successfully
|
||||
"""
|
||||
if simulation_id in self.simulations:
|
||||
self.stop_simulation(simulation_id)
|
||||
del self.simulations[simulation_id]
|
||||
return True
|
||||
return False
|
||||
|
||||
def list_simulations(self) -> List[Dict]:
|
||||
"""
|
||||
List all simulation instances with their status.
|
||||
|
||||
Returns:
|
||||
List of simulation information dictionaries
|
||||
"""
|
||||
simulations_info = []
|
||||
|
||||
for sim_id, simulation in self.simulations.items():
|
||||
info = {
|
||||
'simulation_id': sim_id,
|
||||
'is_running': simulation.is_running,
|
||||
'current_iteration': simulation.current_iteration,
|
||||
'total_iterations': simulation.parameters.iterations,
|
||||
'r_rate': simulation.parameters.r_rate,
|
||||
'g_rate': simulation.parameters.g_rate,
|
||||
'num_agents': simulation.parameters.num_agents
|
||||
}
|
||||
simulations_info.append(info)
|
||||
|
||||
return simulations_info
|
339
app/models/markov_chain.py
Normal file
339
app/models/markov_chain.py
Normal file
@@ -0,0 +1,339 @@
|
||||
"""
|
||||
Markov Chain Models for Economic Simulation
|
||||
|
||||
This module implements the core Markov chain models representing:
|
||||
1. Capitalist Chain (M-C-M'): Money → Commodities → More Money
|
||||
2. Consumer Chain (C-M-C): Commodities → Money → Commodities
|
||||
|
||||
Based on Marx's economic theory and Piketty's inequality principle (r > g).
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
from typing import List, Dict, Tuple
|
||||
import random
|
||||
|
||||
|
||||
class MarkovChain:
|
||||
"""
|
||||
Base class for Markov chain implementations.
|
||||
|
||||
Provides the foundation for economic state transitions with configurable
|
||||
transition probabilities and state tracking.
|
||||
"""
|
||||
|
||||
def __init__(self, states: List[str], transition_probability: float, initial_state: str):
|
||||
"""
|
||||
Initialize the Markov chain.
|
||||
|
||||
Args:
|
||||
states: List of possible states
|
||||
transition_probability: Base probability for state transitions
|
||||
initial_state: Starting state for the chain
|
||||
"""
|
||||
self.states = states
|
||||
self.transition_probability = max(0.0, min(1.0, transition_probability))
|
||||
self.current_state = initial_state
|
||||
self.state_history = [initial_state]
|
||||
self.iteration_count = 0
|
||||
|
||||
# Validate initial state
|
||||
if initial_state not in states:
|
||||
raise ValueError(f"Initial state '{initial_state}' not in states list")
|
||||
|
||||
def get_transition_matrix(self) -> np.ndarray:
|
||||
"""
|
||||
Get the transition probability matrix for this chain.
|
||||
Must be implemented by subclasses.
|
||||
|
||||
Returns:
|
||||
numpy array representing the transition matrix
|
||||
"""
|
||||
raise NotImplementedError("Subclasses must implement get_transition_matrix")
|
||||
|
||||
def step(self) -> str:
|
||||
"""
|
||||
Perform one step in the Markov chain.
|
||||
|
||||
Returns:
|
||||
The new current state after the transition
|
||||
"""
|
||||
current_index = self.states.index(self.current_state)
|
||||
transition_matrix = self.get_transition_matrix()
|
||||
probabilities = transition_matrix[current_index]
|
||||
|
||||
# Choose next state based on probabilities
|
||||
next_state_index = np.random.choice(len(self.states), p=probabilities)
|
||||
self.current_state = self.states[next_state_index]
|
||||
|
||||
self.state_history.append(self.current_state)
|
||||
self.iteration_count += 1
|
||||
|
||||
return self.current_state
|
||||
|
||||
def simulate(self, iterations: int) -> List[str]:
|
||||
"""
|
||||
Run the Markov chain for multiple iterations.
|
||||
|
||||
Args:
|
||||
iterations: Number of steps to simulate
|
||||
|
||||
Returns:
|
||||
List of states visited during simulation
|
||||
"""
|
||||
for _ in range(iterations):
|
||||
self.step()
|
||||
|
||||
return self.state_history.copy()
|
||||
|
||||
def get_state_distribution(self) -> Dict[str, float]:
|
||||
"""
|
||||
Calculate the current state distribution from history.
|
||||
|
||||
Returns:
|
||||
Dictionary mapping states to their frequency ratios
|
||||
"""
|
||||
if not self.state_history:
|
||||
return {state: 0.0 for state in self.states}
|
||||
|
||||
total_count = len(self.state_history)
|
||||
distribution = {}
|
||||
|
||||
for state in self.states:
|
||||
count = self.state_history.count(state)
|
||||
distribution[state] = count / total_count
|
||||
|
||||
return distribution
|
||||
|
||||
def reset(self, initial_state: str = None):
|
||||
"""
|
||||
Reset the chain to initial conditions.
|
||||
|
||||
Args:
|
||||
initial_state: New initial state (optional)
|
||||
"""
|
||||
if initial_state and initial_state in self.states:
|
||||
self.current_state = initial_state
|
||||
else:
|
||||
self.current_state = self.state_history[0]
|
||||
|
||||
self.state_history = [self.current_state]
|
||||
self.iteration_count = 0
|
||||
|
||||
|
||||
class CapitalistChain(MarkovChain):
|
||||
"""
|
||||
Represents the capitalist economic cycle: M-C-M' (Money → Commodities → More Money)
|
||||
|
||||
This chain models capital accumulation where money is invested in commodities
|
||||
to generate more money, with returns based on the capital rate (r).
|
||||
"""
|
||||
|
||||
def __init__(self, capital_rate: float):
|
||||
"""
|
||||
Initialize the capitalist chain.
|
||||
|
||||
Args:
|
||||
capital_rate: The rate of return on capital (r)
|
||||
"""
|
||||
states = ['Money', 'Commodities', 'Enhanced_Money']
|
||||
super().__init__(states, capital_rate, 'Money')
|
||||
self.capital_rate = capital_rate
|
||||
self.wealth_multiplier = 1.0
|
||||
|
||||
def get_transition_matrix(self) -> np.ndarray:
|
||||
"""
|
||||
Create transition matrix for M-C-M' cycle.
|
||||
|
||||
The matrix ensures:
|
||||
- Money transitions to Commodities with probability r
|
||||
- Commodities always transition to Enhanced_Money (probability 1)
|
||||
- Enhanced_Money always transitions back to Money (probability 1)
|
||||
|
||||
Returns:
|
||||
3x3 transition probability matrix
|
||||
"""
|
||||
r = self.transition_probability
|
||||
|
||||
# States: ['Money', 'Commodities', 'Enhanced_Money']
|
||||
matrix = np.array([
|
||||
[1-r, r, 0.0], # Money -> stay or go to Commodities
|
||||
[0.0, 0.0, 1.0], # Commodities -> Enhanced_Money (always)
|
||||
[1.0, 0.0, 0.0] # Enhanced_Money -> Money (always)
|
||||
])
|
||||
|
||||
return matrix
|
||||
|
||||
def step(self) -> str:
|
||||
"""
|
||||
Perform one step and update wealth when completing a cycle.
|
||||
|
||||
Returns:
|
||||
The new current state
|
||||
"""
|
||||
previous_state = self.current_state
|
||||
new_state = super().step()
|
||||
|
||||
# If we completed a full cycle (Enhanced_Money -> Money), increase wealth
|
||||
if previous_state == 'Enhanced_Money' and new_state == 'Money':
|
||||
self.wealth_multiplier *= (1 + self.capital_rate)
|
||||
|
||||
return new_state
|
||||
|
||||
def get_current_wealth(self) -> float:
|
||||
"""
|
||||
Get the current accumulated wealth.
|
||||
|
||||
Returns:
|
||||
Current wealth multiplier representing accumulated capital
|
||||
"""
|
||||
return self.wealth_multiplier
|
||||
|
||||
|
||||
class ConsumerChain(MarkovChain):
|
||||
"""
|
||||
Represents the consumer economic cycle: C-M-C (Commodities → Money → Commodities)
|
||||
|
||||
This chain models consumption patterns where commodities are exchanged for money
|
||||
to purchase other commodities, growing with the economic growth rate (g).
|
||||
"""
|
||||
|
||||
def __init__(self, growth_rate: float):
|
||||
"""
|
||||
Initialize the consumer chain.
|
||||
|
||||
Args:
|
||||
growth_rate: The economic growth rate (g)
|
||||
"""
|
||||
states = ['Commodities', 'Money', 'New_Commodities']
|
||||
super().__init__(states, growth_rate, 'Commodities')
|
||||
self.growth_rate = growth_rate
|
||||
self.consumption_value = 1.0
|
||||
|
||||
def get_transition_matrix(self) -> np.ndarray:
|
||||
"""
|
||||
Create transition matrix for C-M-C cycle.
|
||||
|
||||
The matrix ensures:
|
||||
- Commodities transition to Money with probability g
|
||||
- Money always transitions to New_Commodities (probability 1)
|
||||
- New_Commodities always transition back to Commodities (probability 1)
|
||||
|
||||
Returns:
|
||||
3x3 transition probability matrix
|
||||
"""
|
||||
g = self.transition_probability
|
||||
|
||||
# States: ['Commodities', 'Money', 'New_Commodities']
|
||||
matrix = np.array([
|
||||
[1-g, g, 0.0], # Commodities -> stay or go to Money
|
||||
[0.0, 0.0, 1.0], # Money -> New_Commodities (always)
|
||||
[1.0, 0.0, 0.0] # New_Commodities -> Commodities (always)
|
||||
])
|
||||
|
||||
return matrix
|
||||
|
||||
def step(self) -> str:
|
||||
"""
|
||||
Perform one step and update consumption value when completing a cycle.
|
||||
|
||||
Returns:
|
||||
The new current state
|
||||
"""
|
||||
previous_state = self.current_state
|
||||
new_state = super().step()
|
||||
|
||||
# If we completed a full cycle (New_Commodities -> Commodities), grow consumption
|
||||
if previous_state == 'New_Commodities' and new_state == 'Commodities':
|
||||
self.consumption_value *= (1 + self.growth_rate)
|
||||
|
||||
return new_state
|
||||
|
||||
def get_current_consumption(self) -> float:
|
||||
"""
|
||||
Get the current consumption value.
|
||||
|
||||
Returns:
|
||||
Current consumption value representing accumulated consumption capacity
|
||||
"""
|
||||
return self.consumption_value
|
||||
|
||||
|
||||
class EconomicAgent:
|
||||
"""
|
||||
Represents an individual economic agent with both capitalist and consumer behaviors.
|
||||
|
||||
Each agent operates both chains simultaneously, allowing for mixed economic activity
|
||||
and wealth accumulation patterns.
|
||||
"""
|
||||
|
||||
def __init__(self, agent_id: str, capital_rate: float, growth_rate: float,
|
||||
initial_capital: float = 1000.0, initial_consumption: float = 1000.0):
|
||||
"""
|
||||
Initialize an economic agent.
|
||||
|
||||
Args:
|
||||
agent_id: Unique identifier for the agent
|
||||
capital_rate: Capital return rate (r)
|
||||
growth_rate: Economic growth rate (g)
|
||||
initial_capital: Starting capital amount
|
||||
initial_consumption: Starting consumption capacity
|
||||
"""
|
||||
self.agent_id = agent_id
|
||||
self.capitalist_chain = CapitalistChain(capital_rate)
|
||||
self.consumer_chain = ConsumerChain(growth_rate)
|
||||
|
||||
self.initial_capital = initial_capital
|
||||
self.initial_consumption = initial_consumption
|
||||
|
||||
# Track wealth over time
|
||||
self.wealth_history = []
|
||||
self.consumption_history = []
|
||||
|
||||
def step(self) -> Tuple[float, float]:
|
||||
"""
|
||||
Perform one simulation step for both chains.
|
||||
|
||||
Returns:
|
||||
Tuple of (current_wealth, current_consumption)
|
||||
"""
|
||||
# Step both chains
|
||||
self.capitalist_chain.step()
|
||||
self.consumer_chain.step()
|
||||
|
||||
# Calculate current values
|
||||
current_wealth = self.initial_capital * self.capitalist_chain.get_current_wealth()
|
||||
current_consumption = self.initial_consumption * self.consumer_chain.get_current_consumption()
|
||||
|
||||
# Store history
|
||||
self.wealth_history.append(current_wealth)
|
||||
self.consumption_history.append(current_consumption)
|
||||
|
||||
return current_wealth, current_consumption
|
||||
|
||||
def get_total_wealth(self) -> float:
|
||||
"""
|
||||
Get the agent's total economic value.
|
||||
|
||||
Returns:
|
||||
Sum of capital wealth and consumption capacity
|
||||
"""
|
||||
if not self.wealth_history or not self.consumption_history:
|
||||
return self.initial_capital + self.initial_consumption
|
||||
|
||||
return self.wealth_history[-1] + self.consumption_history[-1]
|
||||
|
||||
def get_wealth_ratio(self) -> float:
|
||||
"""
|
||||
Get the ratio of capital wealth to total wealth.
|
||||
|
||||
Returns:
|
||||
Ratio representing the capitalist vs consumer wealth proportion
|
||||
"""
|
||||
total = self.get_total_wealth()
|
||||
if total == 0:
|
||||
return 0.0
|
||||
|
||||
if not self.wealth_history:
|
||||
return self.initial_capital / (self.initial_capital + self.initial_consumption)
|
||||
|
||||
return self.wealth_history[-1] / total
|
Reference in New Issue
Block a user