Troubleshooting - adylagad/CSCI-561_Genetic-Algorithm GitHub Wiki

Troubleshooting Guide

Solutions to common problems and issues.

๐Ÿšจ Installation Issues

ModuleNotFoundError: No module named 'genetic_algorithm'

Error:


ModuleNotFoundError: No module named 'genetic_algorithm'

Cause: Running from wrong directory

Solution:

# Make sure you're in project root
cd CSCI-561_Genetic-Algorithm
python main.py  # Not in subdirectory!

# Check current directory
pwd  # Should end in CSCI-561_Genetic-Algorithm

Python version too old

Error:

SyntaxError: invalid syntax (type hints)

Cause: Python < 3.7

Solution:

# Check version
python --version

# Upgrade Python (macOS)
brew install [email protected]

# Or use python3
python3 main.py

๐Ÿ› Runtime Errors

FileNotFoundError: input/input1.txt

Error:

FileNotFoundError: [Errno 2] No such file or directory: 'input/input1.txt'

Cause: Missing input file or wrong directory

Solution 1: Check file exists

ls input/input1.txt
# Should show the file

Solution 2: Provide correct path

config = GAConfig(input_file="path/to/your/input.txt")

Solution 3: Create input file

# Create directory
mkdir input

# Create file
echo "3
0 0 0
1 0 0
0 1 0" > input/test.txt

ValueError: Invalid tour

Error:

ValueError: Tour contains duplicate cities

Cause: Operator created invalid tour

Solution: Check custom operators

def crossover(self, parent1: Tour, parent2: Tour) -> Tour:
    offspring = ...

    # Validate before returning
    assert len(offspring) == len(parent1)
    assert len(set(offspring)) == len(offspring)  # No duplicates
    assert set(offspring) == set(parent1)  # Same cities

    return offspring

TypeError: 'NoneType' object is not iterable

Error:

TypeError: 'NoneType' object is not iterable

Cause: Operator returned None instead of valid result

Solution:

def mutate(self, tour: Tour) -> Tour:
    mutated = tour.copy()
    # ... mutation logic ...
    return mutated  # Make sure to return!

๐Ÿ“‰ Performance Issues

Algorithm not improving

Symptoms:

Generation 0:    120,000
Generation 1000: 118,000
Generation 2000: 117,500
Generation 3000: 117,000  # Barely improving!

Diagnosis:

# Check improvement rate
initial = 120000
final = 117000
improvement = (initial - final) / initial * 100
print(f"Improvement: {improvement:.1f}%")
# Should be >50%

Solutions:

1. Verify operators are working:

# Test crossover
parent1 = [0, 1, 2, 3, 4]
parent2 = [4, 3, 2, 1, 0]
offspring = crossover.cross(parent1, parent2)
print(f"Offspring: {offspring}")
# Should be different from parents

# Test mutation
original = [0, 1, 2, 3, 4]
mutated = mutation.mutate([original])[0]
print(f"Original: {original}")
print(f"Mutated:  {mutated}")
# Should be different (usually)

2. Check fitness calculation:

# Lower cost should = higher fitness
tour1_cost = 50000
tour2_cost = 60000

fitness1 = 1 / tour1_cost
fitness2 = 1 / tour2_cost

assert fitness1 > fitness2, "Fitness calculation wrong!"

3. Increase exploration:

config = GAConfig(
    mutation_rate=0.10,  # Higher mutation
    population_size_multiplier=2.0  # Larger population
)

Converges too quickly (premature convergence)

Symptoms:

Generation 0:    120,000
Generation 100:  60,000
Generation 200:  58,000
Generation 300:  57,900  # Stuck early!
...
Generation 3000: 57,900  # No more improvement

Diagnosis:

convergence_gen = stats['convergence_generation']
total_gen = stats['total_generations']
ratio = convergence_gen / total_gen

if ratio < 0.3:
    print("โš ๏ธ Converging too fast!")

Solutions:

1. Increase diversity:

config = GAConfig(
    population_size_multiplier=1.5,  # Bigger population
    mutation_rate=0.05  # More mutation
)

2. Use tournament selection:

# Less selection pressure
selection = TournamentSelection(tournament_size=3)
crossover = OrderCrossover(selection)

3. Reduce elitism (advanced):

# In GeneticAlgorithm.evolve()
# Keep fewer elite individuals
elite_count = int(0.05 * population_size)  # Only 5%

Poor solution quality

Symptoms: Final cost is very high

Expected benchmarks (500 cities):

  • Good: <50,000
  • Acceptable: 50,000-55,000
  • Poor: >55,000

Solutions:

1. Run longer:

config = GAConfig(number_of_generations=10000)

2. Better initialization:

initializer = NearestNeighborInitializer()  # Not Random

3. Better crossover:

crossover = PMXCrossover(selection)  # Try PMX instead of Order

4. Tune parameters:

config = GAConfig(
    population_size_multiplier=2.0,
    mutation_rate=0.03,
    number_of_generations=5000
)

Taking too long

Symptoms: Runtime exceeds expectations

Benchmarks (500 cities):

  • Expected: 3-5 minutes
  • Slow: >10 minutes
  • Very slow: >20 minutes

Diagnosis:

import cProfile

# Profile code
cProfile.run('ga.run(cities, tour_size)')

# Look for bottlenecks

Solutions:

1. Reduce parameters:

config = GAConfig(
    population_size_multiplier=0.5,  # Smaller population
    number_of_generations=1000,  # Fewer generations
    convergence_threshold=100  # Stop earlier
)

2. Use faster operators:

# OrderCrossover is faster than PMX
crossover = OrderCrossover(selection)

# SwapMutation is fastest
mutation = SwapMutation(mutation_rate=0.02)

3. Reduce logging:

config = GAConfig(
    log_interval=500,  # Log less frequently
    verbose=False  # Disable verbose output
)

๐Ÿงช Testing Issues

Tests failing

Error:

FAILED (failures=5)

Solution 1: Check you haven't modified core code

git status  # See what changed
git diff main.py  # See differences

Solution 2: Check Python version

python --version
# Should be 3.7+

Solution 3: Run specific test

python -m unittest test_ga.TestGeneticAlgorithm.test_ga_improves_solution

Random test failures

Symptoms: Tests pass sometimes, fail sometimes

Cause: Stochastic nature of GA

Solution: Set random seed

def test_my_function(self):
    random.seed(42)  # Fixed seed
    result = ga.run(cities, tour_size)
    # Now deterministic

๐Ÿ”ง Configuration Issues

JSON configuration not loading

Error:

JSONDecodeError: Expecting value

Solution: Validate JSON syntax

# Check JSON is valid
python -m json.tool config/my_config.json

# Common issues:
# - Trailing commas
# - Missing quotes
# - Wrong brackets

Valid JSON example:

{
	"mutation_rate": 0.05,
	"number_of_generations": 5000
}

Configuration not taking effect

Problem: Changed config but no difference

Solution: Verify config is loaded

config = GAConfig(mutation_rate=0.10)
print(f"Mutation rate: {config.mutation_rate}")
# Should print: 0.10

# Make sure you're using this config
ga = GeneticAlgorithm(config=config, ...)

๐Ÿ“Š Output Issues

Statistics showing 'N/A' or None

Problem: Statistics dict has None values

Solution: Check algorithm ran completely

try:
    result = ga.run(cities, tour_size)
    stats = ga.get_statistics()
    print(stats)  # Should have all values
except Exception as e:
    print(f"Error: {e}")

Tour has wrong format

Problem: Tour doesn't match expected format

Solution: Understand tour representation

# Tour is list of city INDICES (not coordinates)
tour = [0, 2, 1, 3]  # Visit city 0, then 2, then 1, then 3

# To get coordinates:
tour_coords = [cities[i] for i in tour]
# [(x0,y0,z0), (x2,y2,z2), (x1,y1,z1), (x3,y3,z3)]

๐Ÿž Debugging Strategies

Enable debug logging

config = GAConfig(log_level="DEBUG", verbose=True)

Output:

DEBUG - Reading input from: input/input1.txt
DEBUG - Loaded 500 cities
DEBUG - Initializing population...
DEBUG - Generation 0: Best = 62308.19, Avg = 85432.11
...

Add print statements

def cross(self, parent1: Tour, parent2: Tour) -> Tour:
    print(f"Parent1: {parent1[:5]}...")  # First 5 cities
    offspring = ...
    print(f"Offspring: {offspring[:5]}...")
    return offspring

Validate intermediate results

# After initialization
print(f"Initial population size: {len(population)}")
print(f"Initial best cost: {min_cost}")

# After each generation
print(f"Gen {gen}: Best={best_cost:.2f}, Avg={avg_cost:.2f}")

# After evolution
print(f"Final best: {final_cost:.2f}")
print(f"Improvement: {(initial - final) / initial * 100:.1f}%")

Use assertions

def validate_tour(tour: Tour, expected_size: int):
    """Validate tour is correct."""
    assert len(tour) == expected_size, f"Wrong size: {len(tour)}"
    assert len(set(tour)) == expected_size, "Duplicate cities!"
    assert min(tour) == 0, "City indices should start at 0"
    assert max(tour) == expected_size - 1, "City index out of range"

# Use it
validate_tour(offspring, len(cities))

๐Ÿ†˜ Still Having Issues?

Check these resources

  1. FAQ - Common questions
  2. Configuration Reference - Parameter details
  3. API Reference - Function documentation
  4. Performance Tuning - Optimization guide

Report a bug

If you found a bug:

  1. Check it's reproducible:

    random.seed(42)
    # Run code
    
  2. Create minimal example:

    # Simplest code that shows the bug
    
  3. Open GitHub issue with:

    • Error message
    • Code to reproduce
    • Python version
    • Operating system

Quick checklist:

  • In correct directory?
  • Python 3.7+?
  • Input file exists?
  • Operators return valid tours?
  • Configuration loaded?
  • Tried default config?
  • Checked FAQ?