Spaces:
Sleeping
Sleeping
| import numpy as np | |
| import random | |
| from src.config import cfg | |
| CODE_OFF = -1 | |
| def mutate(individual, slots_cache, daily_deficit_probs): | |
| """ | |
| Applica operatore di mutazione con logica ibrida (Guided/Random). | |
| """ | |
| mut_rate = cfg.genetic_params.get('mutation_rate', 0.4) | |
| split_rate = cfg.genetic_params.get('guided_mutation_split', 0.4) | |
| # Early exit se non triggeriamo la mutazione (risparmio computazionale) | |
| if random.random() > mut_rate: | |
| return individual | |
| mutated = individual.copy() | |
| num_emps, num_days = mutated.shape | |
| emp_idx = random.randint(0, num_emps - 1) | |
| if random.random() < split_rate: | |
| # --- MUTATION STRATEGY 1: Day Swap (Guided) --- | |
| # Sposta un turno da un giorno all'altro cercando di coprire i deficit operativi | |
| row = mutated[emp_idx] | |
| days_worked = np.where(row >= 0)[0] | |
| days_off = np.where(row == CODE_OFF)[0] | |
| if len(days_worked) > 0 and len(days_off) > 0: | |
| # 1. Selezione del giorno da riempire (pesata sul deficit) | |
| try: | |
| probs_off = daily_deficit_probs[days_off] | |
| if np.sum(probs_off) > 0: | |
| probs_off = probs_off / np.sum(probs_off) | |
| day_to_fill = np.random.choice(days_off, p=probs_off) | |
| else: | |
| day_to_fill = np.random.choice(days_off) | |
| except: | |
| # Safe check in caso di anomalie nei tensori | |
| day_to_fill = np.random.choice(days_off) | |
| # 2. Selezione del giorno da svuotare (inversamente proporzionale al deficit) | |
| try: | |
| probs_work = 1.0 - daily_deficit_probs[days_worked] | |
| probs_work = probs_work + 0.01 # Smoothing per evitare probabilità nulle | |
| probs_work = probs_work / np.sum(probs_work) | |
| day_to_empty = np.random.choice(days_worked, p=probs_work) | |
| except: | |
| day_to_empty = np.random.choice(days_worked) | |
| # Esegue lo swap solo se esistono slot validi nella cache pre-calcolata | |
| valid_slots = slots_cache[emp_idx][day_to_fill] | |
| if len(valid_slots) > 0: | |
| mutated[emp_idx, day_to_fill] = np.random.choice(valid_slots) | |
| mutated[emp_idx, day_to_empty] = CODE_OFF | |
| else: | |
| # --- MUTATION STRATEGY 2: Time Shift --- | |
| # Perturba randomicamente l'orario di inizio turno sullo stesso giorno | |
| day_idx = random.randint(0, 6) | |
| if mutated[emp_idx, day_idx] >= 0: | |
| valid = slots_cache[emp_idx][day_idx] | |
| if len(valid) > 0: | |
| mutated[emp_idx, day_idx] = np.random.choice(valid) | |
| return mutated |