Skip to content

Commit f6d8dc9

Browse files
committed
Added docstrings to functions
1 parent 0b4508c commit f6d8dc9

12 files changed

Lines changed: 359 additions & 11 deletions

File tree

CLAUDE.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ This is an academic research project investigating algorithmic collusion among L
6767
- Focus on final 50 periods (251-300) for equilibrium analysis
6868
- Bootstrap validation and robustness testing
6969

70+
**`/src/plotting/`** - Visualization Tools
71+
- `final_figures.py` generates publication-ready statistical plots
72+
- `plotting.py` contains core visualization utilities
73+
- `run_animation.py` creates animated visualizations of price dynamics
74+
7075
### Data Flow
7176

7277
1. **Configuration** → Market parameters (α, β, μ, costs) and agent setup

src/README.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Source Code Architecture
2+
3+
This directory contains the core implementation for algorithmic collusion experiments testing Folk Theorem predictions in multi-agent pricing games.
4+
5+
## Module Structure
6+
7+
### `agents/` - LLM Agent Implementation
8+
9+
- `LLMAgent` - Mistral API integration for strategic pricing decisions
10+
- `Agent` - Abstract base class defining agent interface
11+
- `FakeAgent` - Deterministic agent for testing and baselines
12+
13+
### `environment/` - Market Simulation
14+
15+
- `CalvanoDemandEnvironment` - Implements Calvano et al. (2020) demand specification
16+
- Calculates quantities, profits, and theoretical benchmarks
17+
- Supports both static and time-varying cost structures
18+
19+
### `experiment/` - Experiment Orchestration
20+
21+
- `Experiment` - Coordinates multi-agent pricing games
22+
- `StorageManager` - Handles data persistence in Parquet format
23+
- `RateLimiter` - Manages API rate limiting for cost control
24+
25+
### `prompts/` - Prompt Engineering
26+
27+
- `PromptManager` - Dynamic context injection and memory management
28+
- Systematic P1/P2 prompt specifications for coordination testing
29+
- Structured response validation using Pydantic models
30+
31+
### `analysis/` - Statistical Analysis
32+
33+
- `CollusionAnalysis` - Core regression models for Folk Theorem testing
34+
- Bootstrap validation and robustness testing
35+
- Focus on run-level equilibrium analysis (periods 251-300)
36+
37+
### `plotting/` - Visualization Tools
38+
39+
- Publication-ready figures with theoretical benchmark overlays
40+
- Time series analysis and convergence visualization
41+
- Animated price dynamics for presentation
42+
43+
### `utils/` - Utility Functions
44+
45+
- Logging configuration for experiment tracking
46+
- Price convergence analysis and statistical helpers
47+
- Data processing utilities
48+
49+
## Key Design Principles
50+
51+
**Rate Limiting**: Built-in API management prevents cost overruns
52+
**Memory Architecture**: 100-period rolling windows for strategic learning
53+
**Data Integrity**: Parquet storage with comprehensive metadata
54+
**Reproducibility**: Structured logging and configuration management
55+
**Modularity**: Clean separation between agents, environment, and analysis
56+
57+
## Usage Patterns
58+
59+
```python
60+
from src.agents import LLMAgent
61+
from src.environment import CalvanoDemandEnvironment
62+
from src.experiment import Experiment
63+
64+
# Create environment
65+
env = CalvanoDemandEnvironment("oligopoly", "2-agent market")
66+
67+
# Initialize agents
68+
agents = [LLMAgent(f"Agent_{i}", api_key=api_key, ...) for i in range(2)]
69+
70+
# Run experiment
71+
experiment = Experiment(agents, env, n_rounds=300)
72+
results = experiment.run()
73+
```
74+
75+
This architecture supports the systematic testing of Folk Theorem predictions through controlled LLM agent interactions in oligopoly markets.

src/agents/LLM_agent.py

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
# src/agents/LLM_agent.py
1+
"""
2+
LLM Agent Implementation for Market Simulation
3+
4+
This module provides the LLMAgent class that interfaces with Mistral API
5+
for strategic pricing decisions in oligopoly market experiments.
6+
"""
7+
28
import json
39
import logging
410
import asyncio
@@ -14,6 +20,25 @@
1420

1521

1622
class LLMAgent(Agent):
23+
"""
24+
LLM-based agent for strategic pricing decisions in oligopoly markets.
25+
26+
This agent uses Mistral API to make pricing decisions based on market history
27+
and strategic context. It maintains a rolling memory window and validates
28+
responses using Pydantic models.
29+
30+
Args:
31+
name: Unique identifier for the agent
32+
prefix: Prompt prefix defining agent's strategic context
33+
api_key: Mistral API key for authentication
34+
model_name: Mistral model to use (e.g., 'mistral-large-2411')
35+
response_model: Pydantic model for response validation
36+
prompt_template: Optional template for prompt formatting
37+
memory_length: Number of periods to maintain in memory (default: 100)
38+
env_params: Market environment parameters
39+
logger: Logger instance for experiment tracking
40+
"""
41+
1742
def __init__(
1843
self,
1944
name: str,
@@ -54,6 +79,22 @@ def __init__(
5479
)
5580

5681
async def act(self, prompt: str) -> Dict:
82+
"""
83+
Execute pricing decision using LLM API.
84+
85+
Makes API call to Mistral with retry logic and validates response
86+
using the configured Pydantic model.
87+
88+
Args:
89+
prompt: Market context and decision prompt
90+
91+
Returns:
92+
Dict containing agent name and validated response content
93+
94+
Raises:
95+
ValidationError: If response validation fails after all retries
96+
Exception: If API call fails after all retries
97+
"""
5798
async with Mistral(api_key=self.api_key) as client:
5899
for attempt in range(1, MAX_RETRIES + 1):
59100
self.logger.info(f"🔄 Attempt {attempt} for agent {self.name}")
@@ -90,6 +131,18 @@ async def act(self, prompt: str) -> Dict:
90131
await asyncio.sleep(RETRY_DELAY_SECONDS * attempt)
91132

92133
def __filter_in_answer_fields(self, model: Type[BaseModel]) -> Type[BaseModel]:
134+
"""
135+
Filter Pydantic model fields marked for inclusion in responses.
136+
137+
Creates a new model containing only fields with 'in_answer=True'
138+
in their json_schema_extra metadata.
139+
140+
Args:
141+
model: Source Pydantic model to filter
142+
143+
Returns:
144+
New Pydantic model with only filtered fields
145+
"""
93146
# Filter the fields that have 'in_answer=True'
94147
in_answer_fields = {
95148
name: (field.annotation, field.default)
@@ -100,5 +153,6 @@ def __filter_in_answer_fields(self, model: Type[BaseModel]) -> Type[BaseModel]:
100153
return create_model(model.__name__ + "Filtered", **in_answer_fields)
101154

102155
@property
103-
def type(self) -> bool:
156+
def type(self) -> str:
157+
"""Return agent type identifier."""
104158
return "LLM_agent"

src/agents/base_agent.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,30 @@
1-
# src/agents/base_agent.py
1+
"""
2+
Base Agent Interface for Market Simulation
3+
4+
This module defines the abstract base class for all market agents,
5+
providing common functionality for pricing decisions and market interaction.
6+
"""
7+
28
from abc import ABC, abstractmethod
39
from typing import Dict, Any
410

511

612
class Agent(ABC):
13+
"""
14+
Abstract base class for market agents in oligopoly experiments.
15+
16+
Defines the interface for agents that make pricing decisions based on
17+
market context and strategic parameters.
18+
19+
Args:
20+
name: Unique identifier for the agent
21+
prefix: Strategic context prefix for decision-making
22+
prompt_template: Template for formatting decision prompts
23+
env_index: Agent's index in the market environment
24+
env_params: Market environment parameters (a, alpha, c)
25+
logger: Logger instance for experiment tracking
26+
"""
27+
728
def __init__(
829
self,
930
name: str,
@@ -35,14 +56,25 @@ def __init__(
3556

3657
@property
3758
def requires_prompt(self) -> bool:
59+
"""Whether agent requires prompt for decision-making."""
3860
return True # default for LLM-based agents
3961

4062
@property
4163
def type(self) -> str:
64+
"""Return agent type identifier."""
4265
return None
4366

4467
@abstractmethod
4568
async def act(self, prompt: str) -> Dict[str, Any]:
69+
"""
70+
Execute pricing decision based on market context.
71+
72+
Args:
73+
prompt: Market context and decision prompt
74+
75+
Returns:
76+
Dict containing agent response and metadata
77+
"""
4678
pass
4779

4880
def get_marginal_cost(self, round_num: int) -> float:

src/agents/fake_agent.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,28 @@
1-
# src/agents/fake_agent.py
1+
"""
2+
Fake Agent for Testing and Simulation
3+
4+
This module provides a deterministic agent that follows pre-defined price
5+
sequences, useful for testing and baseline comparisons.
6+
"""
7+
28
import numpy as np
39
from src.agents.base_agent import Agent
410

511

612
class FakeAgent(Agent):
13+
"""
14+
Deterministic agent that follows pre-defined price sequences.
15+
16+
Used for testing market mechanisms and providing baseline comparisons
17+
with deterministic pricing behavior.
18+
19+
Args:
20+
name: Unique identifier for the agent
21+
time_series_data: Array of predetermined prices to follow
22+
nbr_rounds: Number of rounds the agent will participate in
23+
**kwargs: Additional arguments passed to parent Agent class
24+
"""
25+
726
def __init__(
827
self, name: str, time_series_data: np.ndarray, nbr_rounds: int, **kwargs
928
):
@@ -19,6 +38,15 @@ def __init__(
1938
self.current_index = 0
2039

2140
async def act(self, prompt: str) -> dict:
41+
"""
42+
Return the next predetermined price in the sequence.
43+
44+
Args:
45+
prompt: Ignored, as fake agent doesn't use prompts
46+
47+
Returns:
48+
Dict containing agent name and next price in sequence
49+
"""
2250
chosen_price = self.time_series_data[self.current_index]
2351
self.current_index += 1
2452
return {
@@ -28,8 +56,10 @@ async def act(self, prompt: str) -> dict:
2856

2957
@property
3058
def requires_prompt(self) -> bool:
59+
"""Fake agents don't require prompts."""
3160
return False
3261

3362
@property
3463
def type(self) -> str:
64+
"""Return agent type identifier."""
3565
return "fake_agent"

src/analysis/group_size.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
"""
2+
Statistical Analysis for Folk Theorem Testing
3+
4+
This module provides core regression models and statistical analysis
5+
for testing Folk Theorem predictions in multi-agent pricing experiments.
6+
"""
7+
18
import warnings
29

310
import matplotlib.pyplot as plt
@@ -12,6 +19,18 @@
1219

1320

1421
class CollusionAnalysis:
22+
"""
23+
Statistical analysis framework for testing Folk Theorem predictions.
24+
25+
Provides regression models, robustness tests, and visualization tools
26+
for analyzing pricing coordination breakdown as group size increases.
27+
28+
Args:
29+
df: Polars DataFrame with experimental data
30+
Required columns: run_id, period, agent_id, group_size,
31+
prompt_type, price, alpha, monopoly_prices, nash_prices
32+
"""
33+
1534
def __init__(self, df: pl.DataFrame):
1635
"""
1736
Initialize with a Polars DataFrame containing experimental data

0 commit comments

Comments
 (0)