Skip to content

Optimization

pangadfs.optimize

OptimizeDefault()

Bases: OptimizeBase

Source code in pangadfs/base.py
def __init__(self):
    logging.getLogger(__name__).addHandler(logging.NullHandler())

optimize(ga, **kwargs)

Creates initial pool

Parameters:

Name Type Description Default
ga GeneticAlgorithm

the ga instance

required
**kwargs

keyword arguments for plugins

{}

Returns:

Type Description
Dict[str, Any]

Dict

Dict[str, Any]

'population': np.ndarray,

Dict[str, Any]

'fitness': np.ndarray,

Dict[str, Any]

'best_lineup': pd.DataFrame,

Dict[str, Any]

'best_score': float

Source code in pangadfs/optimize.py
def optimize(self, ga: GeneticAlgorithm, **kwargs) -> Dict[str, Any]:
    """Creates initial pool

    Args:
        ga (GeneticAlgorithm): the ga instance
        **kwargs: keyword arguments for plugins

    Returns:
        Dict
        'population': np.ndarray,
        'fitness': np.ndarray,
        'best_lineup': pd.DataFrame,
        'best_score': float

    """
    # Start profiling
    ga.profiler.start_optimization()
    # create pool and pospool
    # pospool used to generate initial population
    # is a dict of position_name: DataFrame
    pop_size = ga.ctx['ga_settings']['population_size']
    pool = ga.pool(csvpth=ga.ctx['ga_settings']['csvpth'])
    cmap = {'points': ga.ctx['ga_settings']['points_column'],
            'position': ga.ctx['ga_settings']['position_column'],
            'salary': ga.ctx['ga_settings']['salary_column']}
    posfilter = ga.ctx['site_settings']['posfilter']
    flex_positions = ga.ctx['site_settings']['flex_positions']
    pospool = ga.pospool(pool=pool, posfilter=posfilter, column_mapping=cmap, flex_positions=flex_positions)

    # create salary and points arrays
    # these match indices of pool
    cmap = {'points': ga.ctx['ga_settings']['points_column'],
            'salary': ga.ctx['ga_settings']['salary_column']}
    points = pool[cmap['points']].values
    salaries = pool[cmap['salary']].values

    # create initial population
    initial_population = ga.populate(
        pospool=pospool, 
        posmap=ga.ctx['site_settings']['posmap'], 
        population_size=pop_size
    )

    # apply validators
    # default is to validate duplicates, salary, and positions
    # can add other validators as desired
    initial_population = ga.validate(
        population=initial_population, 
        salaries=salaries,
        salary_cap=ga.ctx['site_settings']['salary_cap'],
        pool=pool,
        posmap=ga.ctx['site_settings']['posmap'],
        position_column=ga.ctx['ga_settings']['position_column'],
        flex_positions=ga.ctx['site_settings']['flex_positions']
    )

    # need fitness to determine best lineup
    # and also for selection when loop starts
    population_fitness = ga.fitness(
        population=initial_population, 
        points=points
    )

    # set overall_max based on initial population
    omidx = population_fitness.argmax()
    best_fitness = population_fitness[omidx]
    best_lineup = initial_population[omidx]

    # Mark setup phase complete
    ga.profiler.mark_setup_complete()

    # Mark initial best solution (generation 0)
    ga.profiler.mark_best_solution(0)

    # create new generations
    n_unimproved = 0
    population = initial_population.copy()

    for i in range(1, ga.ctx['ga_settings']['n_generations'] + 1):
        # Start generation timing
        ga.profiler.start_generation(i)

        # end program after n generations if not improving
        if n_unimproved == ga.ctx['ga_settings']['stop_criteria']:
            break

        # display progress information with verbose parameter
        if ga.ctx['ga_settings'].get('verbose'):
            logging.info(f'Starting generation {i}')
            logging.info(f'Best lineup score {best_fitness}')

        # select the population
        # here, we are holding back the fittest 20% to ensure
        # that crossover and mutation do not overwrite good individuals
        elite = ga.select(
            population=population, 
            population_fitness=population_fitness, 
            n=len(population) // ga.ctx['ga_settings'].get('elite_divisor', 5),
            method=ga.ctx['ga_settings'].get('elite_method', 'fittest')
        )

        selected = ga.select(
            population=population, 
            population_fitness=population_fitness, 
            n=len(population),
            method=ga.ctx['ga_settings'].get('select_method', 'roulette')
        )

        # cross over the population
        # here, we use uniform crossover, which splits the population
        # and randomly exchanges 0 - all chromosomes
        crossed_over = ga.crossover(population=selected, method=ga.ctx['ga_settings'].get('crossover_method', 'uniform'))

        # mutate the crossed over population (leave elite alone)
        # can use fixed rate or variable to reduce mutation over generations
        # here we use a variable rate that increases if no improvement is found
        mutation_rate = ga.ctx['ga_settings'].get('mutation_rate', max(.05, n_unimproved / 50))
        mutated = ga.mutate(population=crossed_over, mutation_rate=mutation_rate)

        # validate the population (elite + mutated)
        population = ga.validate(
            population=np.vstack((elite, mutated)), 
            salaries=salaries, 
            salary_cap=ga.ctx['site_settings']['salary_cap'],
            pool=pool,
            posmap=ga.ctx['site_settings']['posmap'],
            position_column=ga.ctx['ga_settings']['position_column'],
            flex_positions=ga.ctx['site_settings']['flex_positions']
        )

        # assess fitness and get the best score
        population_fitness = ga.fitness(population=population, points=points)
        omidx = population_fitness.argmax()
        generation_max = population_fitness[omidx]

        # if new best score, then set n_unimproved to 0
        # and save the new best score and lineup
        # otherwise increment n_unimproved
        if generation_max > best_fitness:
            logging.info(f'Lineup improved to {generation_max}')
            best_fitness = generation_max
            best_lineup = population[omidx]
            n_unimproved = 0
            # Mark when best solution was found
            ga.profiler.mark_best_solution(i)
        else:
            n_unimproved += 1
            logging.info(f'Lineup unimproved {n_unimproved} times')

        # End generation timing
        ga.profiler.end_generation()

    # End profiling
    ga.profiler.end_optimization()

    # FINALIZE RESULTS
    # will break after n_generations or when stop_criteria reached
    results = {
        'population': population,
        'fitness': population_fitness,
        'best_lineup': pool.loc[best_lineup, :],
        'best_score': best_fitness
    }

    # Add profiling data to results
    if ga.profiler.enabled:
        results['profiling'] = ga.profiler.export_to_dict()

    return results

OptimizeMultilineup()

Bases: OptimizeBase

Source code in pangadfs/base.py
def __init__(self):
    logging.getLogger(__name__).addHandler(logging.NullHandler())

optimize(ga, **kwargs)

Optimizes for multiple diverse lineups

Parameters:

Name Type Description Default
ga GeneticAlgorithm

the ga instance

required
**kwargs

keyword arguments for plugins

{}

Returns:

Type Description
Dict[str, Any]

Dict containing:

Dict[str, Any]

'population': np.ndarray,

Dict[str, Any]

'fitness': np.ndarray,

Dict[str, Any]

'best_lineup': pd.DataFrame, # For backward compatibility

Dict[str, Any]

'best_score': float, # For backward compatibility

Dict[str, Any]

'lineups': List[pd.DataFrame], # Multiple lineups

Dict[str, Any]

'scores': List[float], # Corresponding scores

Dict[str, Any]

'diversity_metrics': Dict # Diversity statistics

Source code in pangadfs/optimize.py
def optimize(self, ga: GeneticAlgorithm, **kwargs) -> Dict[str, Any]:
    """Optimizes for multiple diverse lineups

    Args:
        ga (GeneticAlgorithm): the ga instance
        **kwargs: keyword arguments for plugins

    Returns:
        Dict containing:
        'population': np.ndarray,
        'fitness': np.ndarray,
        'best_lineup': pd.DataFrame,  # For backward compatibility
        'best_score': float,          # For backward compatibility
        'lineups': List[pd.DataFrame], # Multiple lineups
        'scores': List[float],        # Corresponding scores
        'diversity_metrics': Dict     # Diversity statistics

    """
    # Get multilineup settings with defaults
    target_lineups = ga.ctx['ga_settings'].get('target_lineups', 1)
    diversity_weight = ga.ctx['ga_settings'].get('diversity_weight', 0.2)
    min_overlap_threshold = ga.ctx['ga_settings'].get('min_overlap_threshold', 0.4)  # More aggressive default
    diversity_method = ga.ctx['ga_settings'].get('diversity_method', 'jaccard')

    # Start profiling
    ga.profiler.start_optimization()

    # Create pool and pospool (same as OptimizeDefault)
    pop_size = ga.ctx['ga_settings']['population_size']
    pool = ga.pool(csvpth=ga.ctx['ga_settings']['csvpth'])
    cmap = {'points': ga.ctx['ga_settings']['points_column'],
            'position': ga.ctx['ga_settings']['position_column'],
            'salary': ga.ctx['ga_settings']['salary_column']}
    posfilter = ga.ctx['site_settings']['posfilter']
    flex_positions = ga.ctx['site_settings']['flex_positions']
    pospool = ga.pospool(pool=pool, posfilter=posfilter, column_mapping=cmap, flex_positions=flex_positions)

    # Create salary and points arrays
    cmap = {'points': ga.ctx['ga_settings']['points_column'],
            'salary': ga.ctx['ga_settings']['salary_column']}
    points = pool[cmap['points']].values
    salaries = pool[cmap['salary']].values

    # Create initial population
    initial_population = ga.populate(
        pospool=pospool, 
        posmap=ga.ctx['site_settings']['posmap'], 
        population_size=pop_size
    )

    # Apply validators
    initial_population = ga.validate(
        population=initial_population, 
        salaries=salaries,
        salary_cap=ga.ctx['site_settings']['salary_cap'],
        pool=pool,
        posmap=ga.ctx['site_settings']['posmap'],
        position_column=ga.ctx['ga_settings']['position_column'],
        flex_positions=ga.ctx['site_settings']['flex_positions']
    )

    # Calculate fitness
    population_fitness = ga.fitness(
        population=initial_population, 
        points=points
    )

    # Set overall_max based on initial population
    omidx = population_fitness.argmax()
    best_fitness = population_fitness[omidx]
    best_lineup = initial_population[omidx]

    # Mark setup phase complete
    ga.profiler.mark_setup_complete()

    # Mark initial best solution (generation 0)
    ga.profiler.mark_best_solution(0)

    # Create new generations (same GA loop as OptimizeDefault)
    n_unimproved = 0
    population = initial_population.copy()

    for i in range(1, ga.ctx['ga_settings']['n_generations'] + 1):
        # Start generation timing
        ga.profiler.start_generation(i)

        # End program after n generations if not improving
        if n_unimproved == ga.ctx['ga_settings']['stop_criteria']:
            break

        # Display progress information with verbose parameter
        if ga.ctx['ga_settings'].get('verbose'):
            logging.info(f'Starting generation {i}')
            logging.info(f'Best lineup score {best_fitness}')

        # Select the population
        elite = ga.select(
            population=population, 
            population_fitness=population_fitness, 
            n=len(population) // ga.ctx['ga_settings'].get('elite_divisor', 5),
            method=ga.ctx['ga_settings'].get('elite_method', 'fittest')
        )

        selected = ga.select(
            population=population, 
            population_fitness=population_fitness, 
            n=len(population),
            method=ga.ctx['ga_settings'].get('select_method', 'roulette')
        )

        # Cross over the population
        crossed_over = ga.crossover(population=selected, method=ga.ctx['ga_settings'].get('crossover_method', 'uniform'))

        # Mutate the crossed over population
        mutation_rate = ga.ctx['ga_settings'].get('mutation_rate', max(.05, n_unimproved / 50))
        mutated = ga.mutate(population=crossed_over, mutation_rate=mutation_rate)

        # Validate the population
        population = ga.validate(
            population=np.vstack((elite, mutated)), 
            salaries=salaries, 
            salary_cap=ga.ctx['site_settings']['salary_cap'],
            pool=pool,
            posmap=ga.ctx['site_settings']['posmap'],
            position_column=ga.ctx['ga_settings']['position_column'],
            flex_positions=ga.ctx['site_settings']['flex_positions']
        )

        # Assess fitness and get the best score
        population_fitness = ga.fitness(population=population, points=points)
        omidx = population_fitness.argmax()
        generation_max = population_fitness[omidx]

        # If new best score, then set n_unimproved to 0
        if generation_max > best_fitness:
            logging.info(f'Lineup improved to {generation_max}')
            best_fitness = generation_max
            best_lineup = population[omidx]
            n_unimproved = 0
            # Mark when best solution was found
            ga.profiler.mark_best_solution(i)
        else:
            n_unimproved += 1
            logging.info(f'Lineup unimproved {n_unimproved} times')

        # End generation timing
        ga.profiler.end_generation()

    # End profiling
    ga.profiler.end_optimization()

    # MULTILINEUP SELECTION
    # Select diverse lineups from final population
    if target_lineups == 1:
        # Single lineup mode - return same structure as OptimizeDefault
        selected_lineups = [population[omidx]]
        selected_scores = [best_fitness]
        diversity_metrics = {'avg_overlap': 0.0, 'min_overlap': 0.0, 'diversity_matrix': np.array([[1.0]])}
    else:
        # Multiple lineup mode - select diverse lineups
        selected_lineups, selected_scores, diversity_metrics = self._select_diverse_lineups(
            population, population_fitness, target_lineups, diversity_weight, min_overlap_threshold, diversity_method
        )

    # FINALIZE RESULTS
    results = {
        'population': population,
        'fitness': population_fitness,
        'best_lineup': pool.loc[best_lineup, :],  # Backward compatibility
        'best_score': best_fitness,               # Backward compatibility
        'lineups': [pool.loc[lineup, :] for lineup in selected_lineups],
        'scores': selected_scores,
        'diversity_metrics': diversity_metrics
    }

    # Add profiling data to results
    if ga.profiler.enabled:
        results['profiling'] = ga.profiler.export_to_dict()

    return results

OptimizeMultiOptimizerFieldOwnership

The OptimizeMultiOptimizerFieldOwnership class is an advanced multi-optimizer that balances three key objectives: - Score optimization: Prioritizes both the best-performing lineups and the overall quality of the entire lineup set. - Diversity: Ensures that the generated lineups are different from each other, reducing overlap and increasing the chances of a unique lineup winning. - Field ownership differentiation: Provides a strategic edge by considering the projected ownership of players in the field. This can be used to create contrarian lineups that avoid popular players or to leverage low-owned players with high upside.

Configuration

The behavior of the optimizer can be controlled through the ga_settings in the context object.

Setting Description Default
target_lineups The number of lineups to generate. 10
score_weight The weight to give to the score component of the fitness function. 0.5
diversity_weight The weight to give to the diversity component of the fitness function. 0.3
field_ownership_weight The weight to give to the field ownership component of the fitness function. 0.2
field_ownership_strategy The strategy to use for field ownership differentiation. Can be contrarian, leverage, or balanced. 'contrarian'
ownership_column The name of the column in the player pool CSV that contains the ownership data. 'ownership'
top_k_focus The number of top lineups to prioritize in the score component. min(10, target_lineups)

Example

ctx = {
    'ga_settings': {
        'target_lineups': 20,
        'score_weight': 0.5,
        'diversity_weight': 0.3,
        'field_ownership_weight': 0.2,
        'field_ownership_strategy': 'contrarian',
        'ownership_column': 'ownership',
        'top_k_focus': 5,
        # ... other GA settings
    }
}
=======
# Optimize Reference

The optimize module provides different optimization strategies for lineup generation.

## Available Optimizers

### OptimizeDefault
The standard single-lineup optimizer that uses a genetic algorithm to find the best possible lineup.

**Use case**: When you need one optimal lineup.

**Key features**:
- Fast convergence to optimal solution
- Proven genetic algorithm implementation
- Suitable for most single-lineup scenarios

### OptimizeMultilineup
Advanced optimizer for generating multiple diverse lineups using a post-processing approach.

**Use case**: When you need multiple diverse lineups (50-150 lineups).

**Key features**:
- Quality-first approach: Runs standard GA first, then selects diverse lineups
- Aggressive diversity selection with configurable thresholds
- Scalable for large lineup sets
- Comprehensive diversity metrics

**Configuration parameters**:
- `target_lineups`: Number of lineups to generate (default: 1)
- `diversity_weight`: Weight for diversity vs quality (default: 0.2)
- `min_overlap_threshold`: Minimum diversity requirement (default: 0.4)
- `diversity_method`: 'jaccard' or 'hamming' similarity (default: 'jaccard')

**Example usage**:
```python
from pangadfs.optimize import OptimizeMultilineup

# Configure for multiple diverse lineups
ga_settings = {
    'target_lineups': 100,
    'diversity_weight': 0.25,
    'min_overlap_threshold': 0.4,
    'diversity_method': 'jaccard',
    'population_size': 1000,
    'n_generations': 150,
    # ... other GA settings
}

optimizer = OptimizeMultilineup()
ga = GeneticAlgorithm(ctx={'ga_settings': ga_settings, ...}, optimize=optimizer)
results = ga.optimize()

# Access multiple lineups
lineups = results['lineups']  # List of DataFrames
scores = results['scores']    # List of scores
diversity_metrics = results['diversity_metrics']  # Diversity statistics

Removed Optimizers

The following optimizers have been removed to streamline the codebase:

  • OptimizeMultilineupSets - Replaced by OptimizeMultilineup's superior post-processing approach
  • OptimizePoolBased - Removed due to complexity without clear benefits
  • OptimizeMultiObjective - Removed as academic approach not suitable for practical use

API Reference

pangadfs.optimize

OptimizeDefault()

Bases: OptimizeBase

Source code in pangadfs/base.py
def __init__(self):
    logging.getLogger(__name__).addHandler(logging.NullHandler())

optimize(ga, **kwargs)

Creates initial pool

Parameters:

Name Type Description Default
ga GeneticAlgorithm

the ga instance

required
**kwargs

keyword arguments for plugins

{}

Returns:

Type Description
Dict[str, Any]

Dict

Dict[str, Any]

'population': np.ndarray,

Dict[str, Any]

'fitness': np.ndarray,

Dict[str, Any]

'best_lineup': pd.DataFrame,

Dict[str, Any]

'best_score': float

Source code in pangadfs/optimize.py
def optimize(self, ga: GeneticAlgorithm, **kwargs) -> Dict[str, Any]:
    """Creates initial pool

    Args:
        ga (GeneticAlgorithm): the ga instance
        **kwargs: keyword arguments for plugins

    Returns:
        Dict
        'population': np.ndarray,
        'fitness': np.ndarray,
        'best_lineup': pd.DataFrame,
        'best_score': float

    """
    # Start profiling
    ga.profiler.start_optimization()
    # create pool and pospool
    # pospool used to generate initial population
    # is a dict of position_name: DataFrame
    pop_size = ga.ctx['ga_settings']['population_size']
    pool = ga.pool(csvpth=ga.ctx['ga_settings']['csvpth'])
    cmap = {'points': ga.ctx['ga_settings']['points_column'],
            'position': ga.ctx['ga_settings']['position_column'],
            'salary': ga.ctx['ga_settings']['salary_column']}
    posfilter = ga.ctx['site_settings']['posfilter']
    flex_positions = ga.ctx['site_settings']['flex_positions']
    pospool = ga.pospool(pool=pool, posfilter=posfilter, column_mapping=cmap, flex_positions=flex_positions)

    # create salary and points arrays
    # these match indices of pool
    cmap = {'points': ga.ctx['ga_settings']['points_column'],
            'salary': ga.ctx['ga_settings']['salary_column']}
    points = pool[cmap['points']].values
    salaries = pool[cmap['salary']].values

    # create initial population
    initial_population = ga.populate(
        pospool=pospool, 
        posmap=ga.ctx['site_settings']['posmap'], 
        population_size=pop_size
    )

    # apply validators
    # default is to validate duplicates, salary, and positions
    # can add other validators as desired
    initial_population = ga.validate(
        population=initial_population, 
        salaries=salaries,
        salary_cap=ga.ctx['site_settings']['salary_cap'],
        pool=pool,
        posmap=ga.ctx['site_settings']['posmap'],
        position_column=ga.ctx['ga_settings']['position_column'],
        flex_positions=ga.ctx['site_settings']['flex_positions']
    )

    # need fitness to determine best lineup
    # and also for selection when loop starts
    population_fitness = ga.fitness(
        population=initial_population, 
        points=points
    )

    # set overall_max based on initial population
    omidx = population_fitness.argmax()
    best_fitness = population_fitness[omidx]
    best_lineup = initial_population[omidx]

    # Mark setup phase complete
    ga.profiler.mark_setup_complete()

    # Mark initial best solution (generation 0)
    ga.profiler.mark_best_solution(0)

    # create new generations
    n_unimproved = 0
    population = initial_population.copy()

    for i in range(1, ga.ctx['ga_settings']['n_generations'] + 1):
        # Start generation timing
        ga.profiler.start_generation(i)

        # end program after n generations if not improving
        if n_unimproved == ga.ctx['ga_settings']['stop_criteria']:
            break

        # display progress information with verbose parameter
        if ga.ctx['ga_settings'].get('verbose'):
            logging.info(f'Starting generation {i}')
            logging.info(f'Best lineup score {best_fitness}')

        # select the population
        # here, we are holding back the fittest 20% to ensure
        # that crossover and mutation do not overwrite good individuals
        elite = ga.select(
            population=population, 
            population_fitness=population_fitness, 
            n=len(population) // ga.ctx['ga_settings'].get('elite_divisor', 5),
            method=ga.ctx['ga_settings'].get('elite_method', 'fittest')
        )

        selected = ga.select(
            population=population, 
            population_fitness=population_fitness, 
            n=len(population),
            method=ga.ctx['ga_settings'].get('select_method', 'roulette')
        )

        # cross over the population
        # here, we use uniform crossover, which splits the population
        # and randomly exchanges 0 - all chromosomes
        crossed_over = ga.crossover(population=selected, method=ga.ctx['ga_settings'].get('crossover_method', 'uniform'))

        # mutate the crossed over population (leave elite alone)
        # can use fixed rate or variable to reduce mutation over generations
        # here we use a variable rate that increases if no improvement is found
        mutation_rate = ga.ctx['ga_settings'].get('mutation_rate', max(.05, n_unimproved / 50))
        mutated = ga.mutate(population=crossed_over, mutation_rate=mutation_rate)

        # validate the population (elite + mutated)
        population = ga.validate(
            population=np.vstack((elite, mutated)), 
            salaries=salaries, 
            salary_cap=ga.ctx['site_settings']['salary_cap'],
            pool=pool,
            posmap=ga.ctx['site_settings']['posmap'],
            position_column=ga.ctx['ga_settings']['position_column'],
            flex_positions=ga.ctx['site_settings']['flex_positions']
        )

        # assess fitness and get the best score
        population_fitness = ga.fitness(population=population, points=points)
        omidx = population_fitness.argmax()
        generation_max = population_fitness[omidx]

        # if new best score, then set n_unimproved to 0
        # and save the new best score and lineup
        # otherwise increment n_unimproved
        if generation_max > best_fitness:
            logging.info(f'Lineup improved to {generation_max}')
            best_fitness = generation_max
            best_lineup = population[omidx]
            n_unimproved = 0
            # Mark when best solution was found
            ga.profiler.mark_best_solution(i)
        else:
            n_unimproved += 1
            logging.info(f'Lineup unimproved {n_unimproved} times')

        # End generation timing
        ga.profiler.end_generation()

    # End profiling
    ga.profiler.end_optimization()

    # FINALIZE RESULTS
    # will break after n_generations or when stop_criteria reached
    results = {
        'population': population,
        'fitness': population_fitness,
        'best_lineup': pool.loc[best_lineup, :],
        'best_score': best_fitness
    }

    # Add profiling data to results
    if ga.profiler.enabled:
        results['profiling'] = ga.profiler.export_to_dict()

    return results

OptimizeMultilineup()

Bases: OptimizeBase

Source code in pangadfs/base.py
def __init__(self):
    logging.getLogger(__name__).addHandler(logging.NullHandler())

optimize(ga, **kwargs)

Optimizes for multiple diverse lineups

Parameters:

Name Type Description Default
ga GeneticAlgorithm

the ga instance

required
**kwargs

keyword arguments for plugins

{}

Returns:

Type Description
Dict[str, Any]

Dict containing:

Dict[str, Any]

'population': np.ndarray,

Dict[str, Any]

'fitness': np.ndarray,

Dict[str, Any]

'best_lineup': pd.DataFrame, # For backward compatibility

Dict[str, Any]

'best_score': float, # For backward compatibility

Dict[str, Any]

'lineups': List[pd.DataFrame], # Multiple lineups

Dict[str, Any]

'scores': List[float], # Corresponding scores

Dict[str, Any]

'diversity_metrics': Dict # Diversity statistics

Source code in pangadfs/optimize.py
def optimize(self, ga: GeneticAlgorithm, **kwargs) -> Dict[str, Any]:
    """Optimizes for multiple diverse lineups

    Args:
        ga (GeneticAlgorithm): the ga instance
        **kwargs: keyword arguments for plugins

    Returns:
        Dict containing:
        'population': np.ndarray,
        'fitness': np.ndarray,
        'best_lineup': pd.DataFrame,  # For backward compatibility
        'best_score': float,          # For backward compatibility
        'lineups': List[pd.DataFrame], # Multiple lineups
        'scores': List[float],        # Corresponding scores
        'diversity_metrics': Dict     # Diversity statistics

    """
    # Get multilineup settings with defaults
    target_lineups = ga.ctx['ga_settings'].get('target_lineups', 1)
    diversity_weight = ga.ctx['ga_settings'].get('diversity_weight', 0.2)
    min_overlap_threshold = ga.ctx['ga_settings'].get('min_overlap_threshold', 0.4)  # More aggressive default
    diversity_method = ga.ctx['ga_settings'].get('diversity_method', 'jaccard')

    # Start profiling
    ga.profiler.start_optimization()

    # Create pool and pospool (same as OptimizeDefault)
    pop_size = ga.ctx['ga_settings']['population_size']
    pool = ga.pool(csvpth=ga.ctx['ga_settings']['csvpth'])
    cmap = {'points': ga.ctx['ga_settings']['points_column'],
            'position': ga.ctx['ga_settings']['position_column'],
            'salary': ga.ctx['ga_settings']['salary_column']}
    posfilter = ga.ctx['site_settings']['posfilter']
    flex_positions = ga.ctx['site_settings']['flex_positions']
    pospool = ga.pospool(pool=pool, posfilter=posfilter, column_mapping=cmap, flex_positions=flex_positions)

    # Create salary and points arrays
    cmap = {'points': ga.ctx['ga_settings']['points_column'],
            'salary': ga.ctx['ga_settings']['salary_column']}
    points = pool[cmap['points']].values
    salaries = pool[cmap['salary']].values

    # Create initial population
    initial_population = ga.populate(
        pospool=pospool, 
        posmap=ga.ctx['site_settings']['posmap'], 
        population_size=pop_size
    )

    # Apply validators
    initial_population = ga.validate(
        population=initial_population, 
        salaries=salaries,
        salary_cap=ga.ctx['site_settings']['salary_cap'],
        pool=pool,
        posmap=ga.ctx['site_settings']['posmap'],
        position_column=ga.ctx['ga_settings']['position_column'],
        flex_positions=ga.ctx['site_settings']['flex_positions']
    )

    # Calculate fitness
    population_fitness = ga.fitness(
        population=initial_population, 
        points=points
    )

    # Set overall_max based on initial population
    omidx = population_fitness.argmax()
    best_fitness = population_fitness[omidx]
    best_lineup = initial_population[omidx]

    # Mark setup phase complete
    ga.profiler.mark_setup_complete()

    # Mark initial best solution (generation 0)
    ga.profiler.mark_best_solution(0)

    # Create new generations (same GA loop as OptimizeDefault)
    n_unimproved = 0
    population = initial_population.copy()

    for i in range(1, ga.ctx['ga_settings']['n_generations'] + 1):
        # Start generation timing
        ga.profiler.start_generation(i)

        # End program after n generations if not improving
        if n_unimproved == ga.ctx['ga_settings']['stop_criteria']:
            break

        # Display progress information with verbose parameter
        if ga.ctx['ga_settings'].get('verbose'):
            logging.info(f'Starting generation {i}')
            logging.info(f'Best lineup score {best_fitness}')

        # Select the population
        elite = ga.select(
            population=population, 
            population_fitness=population_fitness, 
            n=len(population) // ga.ctx['ga_settings'].get('elite_divisor', 5),
            method=ga.ctx['ga_settings'].get('elite_method', 'fittest')
        )

        selected = ga.select(
            population=population, 
            population_fitness=population_fitness, 
            n=len(population),
            method=ga.ctx['ga_settings'].get('select_method', 'roulette')
        )

        # Cross over the population
        crossed_over = ga.crossover(population=selected, method=ga.ctx['ga_settings'].get('crossover_method', 'uniform'))

        # Mutate the crossed over population
        mutation_rate = ga.ctx['ga_settings'].get('mutation_rate', max(.05, n_unimproved / 50))
        mutated = ga.mutate(population=crossed_over, mutation_rate=mutation_rate)

        # Validate the population
        population = ga.validate(
            population=np.vstack((elite, mutated)), 
            salaries=salaries, 
            salary_cap=ga.ctx['site_settings']['salary_cap'],
            pool=pool,
            posmap=ga.ctx['site_settings']['posmap'],
            position_column=ga.ctx['ga_settings']['position_column'],
            flex_positions=ga.ctx['site_settings']['flex_positions']
        )

        # Assess fitness and get the best score
        population_fitness = ga.fitness(population=population, points=points)
        omidx = population_fitness.argmax()
        generation_max = population_fitness[omidx]

        # If new best score, then set n_unimproved to 0
        if generation_max > best_fitness:
            logging.info(f'Lineup improved to {generation_max}')
            best_fitness = generation_max
            best_lineup = population[omidx]
            n_unimproved = 0
            # Mark when best solution was found
            ga.profiler.mark_best_solution(i)
        else:
            n_unimproved += 1
            logging.info(f'Lineup unimproved {n_unimproved} times')

        # End generation timing
        ga.profiler.end_generation()

    # End profiling
    ga.profiler.end_optimization()

    # MULTILINEUP SELECTION
    # Select diverse lineups from final population
    if target_lineups == 1:
        # Single lineup mode - return same structure as OptimizeDefault
        selected_lineups = [population[omidx]]
        selected_scores = [best_fitness]
        diversity_metrics = {'avg_overlap': 0.0, 'min_overlap': 0.0, 'diversity_matrix': np.array([[1.0]])}
    else:
        # Multiple lineup mode - select diverse lineups
        selected_lineups, selected_scores, diversity_metrics = self._select_diverse_lineups(
            population, population_fitness, target_lineups, diversity_weight, min_overlap_threshold, diversity_method
        )

    # FINALIZE RESULTS
    results = {
        'population': population,
        'fitness': population_fitness,
        'best_lineup': pool.loc[best_lineup, :],  # Backward compatibility
        'best_score': best_fitness,               # Backward compatibility
        'lineups': [pool.loc[lineup, :] for lineup in selected_lineups],
        'scores': selected_scores,
        'diversity_metrics': diversity_metrics
    }

    # Add profiling data to results
    if ga.profiler.enabled:
        results['profiling'] = ga.profiler.export_to_dict()

    return results