A Python implementation of a Genetic Algorithm (GA) that models evolutionary firm behavior in a Cournot oligopoly market. Firms evolve their production quantities over 1,000 generations and the simulation tests whether decentralized evolutionary learning converges to the theoretical Nash Equilibrium.
- Overview
- Repository Structure
- Algorithm Details
- Parameters
- Scenarios
- Installation
- Usage
- Expected Output
- Results
This project applies a Genetic Algorithm to simulate an oligopolistic market with 24 firms (Cournot competition). Each firm is represented by a 10-bit binary chromosome that encodes its production quantity. Over 1,000 iterations, firms evolve through selection, crossover, and mutation. The goal is to observe whether the population converges to the Cournot-Nash Equilibrium—the production level that no firm has an incentive to deviate from.
Three scenarios are run to analyse the sensitivity of the GA to its operator rates:
| Scenario | Change |
|---|---|
| Benchmark | Default crossover and mutation rates |
| High Crossover | Crossover rate increased by +0.250 |
| High Mutation | Mutation rate increased by +0.00250 |
GeneticAlgorithm/
├── geneticAlgorithm.py # Main simulation code
├── pyproject.toml # Python project configuration
├── results/ # Generated output plots
│ ├── results_Benchmark.png
│ ├── results_High_Crossover.png
│ └── results_High_Mutation.png
└── README.md
The simulation uses a linear inverse demand function:
price = alpha - total_Q
where alpha = 1023 is the demand intercept and total_Q is the sum of all firms' outputs. Each firm's profit is:
profit = (price × q_i) - (c × q_i)
where c = 0 is the marginal cost and q_i is firm i's individual output.
Theoretical Cournot-Nash Equilibrium (closed-form solution for n firms):
Q_nash_total = n / (n + 1) × (alpha - c) = 24/25 × 1023 ≈ 979.44
q_nash_indiv = Q_nash_total / n ≈ 40.81 per firm
Each firm is represented by a 10-bit binary chromosome. The chromosome is decoded to a production quantity by:
q_i = alpha × (x_it / 2^l)
where x_it is the integer value of the binary string (0 to 1023) and l = 10 is the chromosome length. This maps every chromosome to a quantity in the range [0, alpha].
Each generation executes the following four steps:
-
Selection — Roulette Wheel
Fitness is equal to profit. Firms with higher profit have a proportionally larger probability of being selected into the mating pool. Negative fitness values are shifted to ensure all probabilities are positive. -
Crossover — Single-Point
Two parents produce two children by swapping tails at a random cut-pointk ∈ [1, l-1]. Applied with probabilityp_cross. -
Mutation — Bit-Flip
Each bit in a child's chromosome is independently flipped with probabilityp_mut. -
Replacement — Elitist Election
Each child competes against its corresponding parent. The child replaces the parent only if it would earn a higher hypothetical profit when evaluated against the current behaviour of all other firms. This ensures the population never degrades.
| Parameter | Default | Description |
|---|---|---|
n |
24 |
Number of firms (population size) |
alpha |
1023 |
Demand intercept |
c |
0 |
Marginal cost |
l |
10 |
Chromosome length in bits |
T |
1000 |
Number of generations |
Y |
1 |
ID tuning parameter (second-to-last digit) |
Z |
3 |
ID tuning parameter (last digit) |
p_cross_bench |
0.200 + 0.01·Y + 0.001·Z |
Benchmark crossover probability ≈ 0.213 |
p_mut_bench |
0.00200 + 0.0001·Y + 0.00001·Z |
Benchmark mutation probability ≈ 0.00203 |
| Scenario | p_cross |
p_mut |
|---|---|---|
| Benchmark | p_cross_bench |
p_mut_bench |
| High Crossover | p_cross_bench + 0.250 |
p_mut_bench |
| High Mutation | p_cross_bench |
p_mut_bench + 0.00250 |
Requirements: Python 3.11 or higher.
Install dependencies:
pip install numpy matplotlibpython geneticAlgorithm.pyThis will run all three scenarios sequentially, print a summary to the console, and save one plot per scenario to the results/ directory.
Console output for each scenario:
--- Running Scenario: Benchmark ---
p_cross: 0.21300, p_mut: 0.00203
Simulation completed!
Final Total Q: <value>
Target Nash Total Q: 979.44
Final Variance: <value>
Saved plot to results/results_Benchmark.png
Saved plots (one per scenario):
Each PNG contains two side-by-side charts:
- Left — Industry Output Convergence: Total market quantity
Qper iteration vs. the theoretical Nash Equilibrium (dashed red line). - Right — Population Variance: Variance of individual firm outputs per iteration, showing how much diversity remains in the population.
The results/ directory contains pre-generated plots for each scenario. Below is a summary of what each plot illustrates:
| File | Description |
|---|---|
results_Benchmark.png |
Baseline convergence with default operator rates |
results_High_Crossover.png |
Effect of significantly increasing the crossover probability |
results_High_Mutation.png |
Effect of significantly increasing the mutation probability |
These plots allow direct visual comparison of how different GA operator settings affect the speed and stability of convergence to the Nash Equilibrium.