Skip to content

Validate Reference

The validate module provides validation classes to ensure lineups meet various constraints and requirements.

Available Validators

General Validation

DuplicatesValidate

Removes lineups that contain duplicate players.

Use case: Ensure no player appears multiple times in a lineup.

Key features: - Efficient duplicate detection using sorted arrays - Handles both internal duplicates (within lineup) and external duplicates (between lineups) - Optimized for large populations

FlexDuplicatesValidate

Validates that FLEX positions don't duplicate players already used in other positions.

Use case: Ensure FLEX players are truly additional and don't overlap with required positions.

Key features: - Vectorized approach for efficiency - Handles complex position mapping scenarios - Prevents invalid lineups where FLEX duplicates required positions

SalaryValidate

Ensures all lineups meet salary cap constraints.

Use case: Filter out lineups that exceed the salary cap.

Key features: - Fast salary calculation using numpy operations - Configurable salary cap - Efficient boolean indexing for filtering

Position Validation

PositionValidate

Validates that lineups meet position requirements (QB, RB, WR, TE, etc.).

Use case: Ensure lineups have the correct number of players at each position.

Key features: - Flexible position mapping support - FLEX position handling - Comprehensive position requirement checking

Parameters: - posmap: Position requirements (e.g., {'QB': 1, 'RB': 2, 'WR': 3, 'TE': 1, 'DST': 1, 'FLEX': 1}) - position_column: Column name for positions in player pool - flex_positions: Positions that can fill FLEX slots (default: ('RB', 'WR', 'TE'))

PositionValidateOptimized

Optimized version of position validation using vectorized operations.

Use case: Same as PositionValidate but with better performance for large populations.

Key features: - Vectorized validation for better performance - Pre-computed position arrays for fast lookup - Same functionality as PositionValidate with speed improvements

Usage Examples

Basic Validation Setup

from pangadfs.validate import DuplicatesValidate, SalaryValidate, PositionValidate

# Set up validators
validators = [
    DuplicatesValidate(),
    SalaryValidate(),
    PositionValidate()
]

# Apply validation in sequence
for validator in validators:
    population = validator.validate(
        population=population,
        salaries=salaries,
        salary_cap=50000,
        pool=player_pool,
        posmap={'QB': 1, 'RB': 2, 'WR': 3, 'TE': 1, 'DST': 1, 'FLEX': 1},
        position_column='pos',
        flex_positions=('RB', 'WR', 'TE')
    )

Using with GeneticAlgorithm

from stevedore.named import NamedExtensionManager

# Set up validation extension manager
emgrs = {
    'validate': NamedExtensionManager(
        namespace='pangadfs.validate',
        names=['validate_salary', 'validate_duplicates', 'validate_positions'],
        invoke_on_load=True,
        name_order=True
    )
}

ga = GeneticAlgorithm(ctx=ctx, extension_managers=emgrs)

Module Consolidation

Note: All validation classes are now consolidated in the single pangadfs.validate module. Previously, position validation classes were in a separate validate_positions module, but they have been moved for better organization and easier imports.

Migration: If you were previously importing from pangadfs.validate_positions, simply change your imports to use pangadfs.validate:

# Old (no longer works)
from pangadfs.validate_positions import PositionValidate

# New (current)
from pangadfs.validate import PositionValidate

API Reference

pangadfs.validate

FlexDuplicatesValidate()

Bases: ValidateBase

Validates that FLEX positions don't duplicate other positions. This replaces the expensive duplicate checking that was in PopulateDefault. Uses a more efficient vectorized approach.

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

PositionValidate()

Bases: ValidateBase

Validates that lineups meet position requirements.

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

validate(*, population, pool, posmap, position_column='pos', flex_positions=('RB', 'WR', 'TE'), **kwargs)

Validate every lineup with a compact lookup-based approach.

Source code in pangadfs/validate.py
def validate(self, *,
             population: np.ndarray,
             pool: pd.DataFrame,
             posmap: Dict[str, int],
             position_column: str = 'pos',
             flex_positions: tuple = ('RB', 'WR', 'TE'),
             **kwargs) -> np.ndarray:
    """Validate every lineup with a compact lookup-based approach."""
    if len(population) == 0:
        return population

    if not isinstance(pool, pd.DataFrame):
        return population

    position_names = tuple(dict.fromkeys(pool[position_column].tolist()))
    position_to_code = {pos: idx for idx, pos in enumerate(position_names)}
    max_id = max(int(pool.index.max()), int(population.max())) + 1
    player_position_code = np.full(max_id, -1, dtype=np.int16)

    for player_id, pos in zip(pool.index.to_numpy(), pool[position_column].to_numpy()):
        if player_id < len(player_position_code):
            player_position_code[player_id] = position_to_code.get(pos, -1)

    non_flex_items = [(pos, count) for pos, count in posmap.items() if pos != 'FLEX']
    required_codes = np.asarray([position_to_code.get(pos, -1) for pos, _ in non_flex_items], dtype=np.int16)
    required_counts = np.asarray([count for _, count in non_flex_items], dtype=np.int16)

    # Missing required positions means no lineup can be valid.
    if np.any(required_codes < 0):
        return population[:0]

    flex_required = int(posmap.get('FLEX', 0))
    flex_codes = np.asarray(
        [position_to_code.get(pos, -1) for pos in flex_positions if position_to_code.get(pos, -1) >= 0],
        dtype=np.int16,
    )

    if flex_required > 0 and flex_codes.size == 0:
        return population[:0]

    valid_mask = _validate_lineups_position_kernel_numba(
        population,
        player_position_code,
        required_codes,
        required_counts,
        flex_codes,
        flex_required,
        len(position_names),
    )

    return population[valid_mask]

PositionValidateOptimized()

Bases: ValidateBase

Optimized version using the same validation logic but exposed as a named class.

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

validate(*, population, pool, posmap, position_column='pos', flex_positions=('RB', 'WR', 'TE'), **kwargs)

Delegates to the faster lookup-based implementation.

Source code in pangadfs/validate.py
def validate(self, *,
             population: np.ndarray,
             pool: pd.DataFrame,
             posmap: Dict[str, int],
             position_column: str = 'pos',
             flex_positions: tuple = ('RB', 'WR', 'TE'),
             **kwargs) -> np.ndarray:
    """Delegates to the faster lookup-based implementation."""
    return PositionValidate().validate(
        population=population,
        pool=pool,
        posmap=posmap,
        position_column=position_column,
        flex_positions=flex_positions,
        **kwargs
    )

SalaryValidate()

Bases: ValidateBase

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

validate(*, population, salaries, salary_cap, **kwargs)

Ensures valid individuals in population

Parameters:

Name Type Description Default
population ndarray

the population to validate

required
salaries ndarray

1D where indices are in same order as player indices

required
salary_cap int

the salary cap, e.g., 50000 or 60000

required
**kwargs

keyword arguments for plugins

{}

Returns:

Type Description
ndarray

np.ndarray: same width as population, likely has less rows

Source code in pangadfs/validate.py
def validate(self,
             *, 
             population: np.ndarray,
             salaries: np.ndarray,
             salary_cap: int, 
             **kwargs) -> np.ndarray:
    """Ensures valid individuals in population

        Args:
            population (np.ndarray): the population to validate
            salaries (np.ndarray): 1D where indices are in same order as player indices
            salary_cap (int): the salary cap, e.g., 50000 or 60000
            **kwargs: keyword arguments for plugins

        Returns:
            np.ndarray: same width as population, likely has less rows

    """
    if len(population) == 0:
        return population

    salary_matrix = np.take(salaries, population)
    popsal = np.sum(salary_matrix, axis=1)
    valid_indices = np.nonzero(popsal <= salary_cap)[0]
    if valid_indices.size > 0:
        return population[valid_indices]

    # Guard against population collapse when an upstream configuration
    # yields no under-cap lineups. Keeping the cheapest lineup preserves
    # GA progress and matches legacy non-empty expectations in tests.
    cheapest_idx = int(np.argmin(popsal))
    return population[cheapest_idx:cheapest_idx + 1]