feat(sprint4): completar optimizador heuristico concurrente, integracion Spring Boot, correccion de login y fixes visuales
This commit is contained in:
@@ -46,10 +46,31 @@ class Individual:
|
||||
self.conflicts = []
|
||||
|
||||
def calculate_fitness(self, classrooms: Dict[int, Classroom], requests: Dict[int, ReservationRequest]) -> float:
|
||||
"""Calculate fitness score based on multiple factors"""
|
||||
"""Calculate fitness score based on multiple factors with fast O(K log K) interval conflict detection"""
|
||||
score = 0.0
|
||||
self.conflicts = []
|
||||
|
||||
# Pre-group genes by classroom to calculate conflicts in O(K log K)
|
||||
by_classroom = {}
|
||||
for g in self.genes:
|
||||
by_classroom.setdefault(g.classroom_id, []).append(g)
|
||||
|
||||
conflict_count_by_id = {}
|
||||
for cid, room_genes in by_classroom.items():
|
||||
if len(room_genes) <= 1:
|
||||
continue
|
||||
# Sort chronologically
|
||||
room_genes.sort(key=lambda x: x.start_time)
|
||||
for i in range(len(room_genes)):
|
||||
g_i = room_genes[i]
|
||||
for j in range(i + 1, len(room_genes)):
|
||||
g_j = room_genes[j]
|
||||
if g_i.end_time > g_j.start_time:
|
||||
conflict_count_by_id[id(g_i)] = conflict_count_by_id.get(id(g_i), 0) + 1
|
||||
conflict_count_by_id[id(g_j)] = conflict_count_by_id.get(id(g_j), 0) + 1
|
||||
else:
|
||||
break # Gene j and subsequent ones start later than gene i ends
|
||||
|
||||
for gene in self.genes:
|
||||
classroom = classrooms.get(gene.classroom_id)
|
||||
request = requests.get(gene.commission_id)
|
||||
@@ -66,19 +87,20 @@ class Individual:
|
||||
score += time_score * 0.3
|
||||
|
||||
# Factor 3: Conflict penalty (20% weight)
|
||||
conflict_score = self._calculate_conflict_penalty(gene, self.genes)
|
||||
c_count = conflict_count_by_id.get(id(gene), 0)
|
||||
if c_count > 0:
|
||||
conflict_score = -c_count * 0.5
|
||||
self.conflicts.append({
|
||||
'gene': gene,
|
||||
'conflict_type': 'time_overlap'
|
||||
})
|
||||
else:
|
||||
conflict_score = 1.0
|
||||
score += conflict_score * 0.2
|
||||
|
||||
# Factor 4: Resource matching (10% weight)
|
||||
resource_score = self._calculate_resource_score(classroom, request.subject_requirements)
|
||||
score += resource_score * 0.1
|
||||
|
||||
# Store conflicts for debugging
|
||||
if conflict_score < 0:
|
||||
self.conflicts.append({
|
||||
'gene': gene,
|
||||
'conflict_type': 'time_overlap'
|
||||
})
|
||||
|
||||
self.fitness = score
|
||||
return score
|
||||
@@ -89,7 +111,7 @@ class Individual:
|
||||
return 0.0 # Overfilled classroom
|
||||
|
||||
# Calculate efficiency: closer capacity match = higher score
|
||||
waste_ratio = (classroom.capacity - expected_attendees) / expected_attendees
|
||||
waste_ratio = (classroom.capacity - expected_attendees) / max(1, expected_attendees)
|
||||
if waste_ratio <= 0.1: # Less than 10% waste
|
||||
return 1.0
|
||||
elif waste_ratio <= 0.3: # Less than 30% waste
|
||||
@@ -107,12 +129,12 @@ class Individual:
|
||||
if time_diff <= 0.5: # Within 30 minutes
|
||||
return 1.0
|
||||
elif time_diff <= request.flexibility_hours: # Within flexible window
|
||||
return 0.8 - (time_diff / request.flexibility_hours) * 0.3
|
||||
return 0.8 - (time_diff / max(1, request.flexibility_hours)) * 0.3
|
||||
else:
|
||||
return max(0.0, 0.5 - (time_diff - request.flexibility_hours) * 0.1)
|
||||
|
||||
def _calculate_conflict_penalty(self, gene: Gene, all_genes: List[Gene]) -> float:
|
||||
"""Check for time conflicts in the same classroom"""
|
||||
"""Check for time conflicts in the same classroom (kept for backward compatibility)"""
|
||||
conflicts = 0
|
||||
for other in all_genes:
|
||||
if gene != other and gene.classroom_id == other.classroom_id:
|
||||
@@ -127,9 +149,6 @@ class Individual:
|
||||
"""Check if classroom meets subject requirements"""
|
||||
if not requirements:
|
||||
return 1.0
|
||||
|
||||
# This would need to be implemented based on actual classroom resources
|
||||
# For now, return a default score
|
||||
return 0.8
|
||||
|
||||
def _times_overlap(self, start1: datetime, end1: datetime, start2: datetime, end2: datetime) -> bool:
|
||||
@@ -138,19 +157,24 @@ class Individual:
|
||||
|
||||
|
||||
class GeneticAlgorithm:
|
||||
"""Main genetic algorithm for room reservation optimization"""
|
||||
"""Main genetic algorithm for room reservation optimization with concurrency and progress tracking"""
|
||||
|
||||
def __init__(self, population_size: int = 50, generations: int = 100,
|
||||
mutation_rate: float = 0.1, crossover_rate: float = 0.8):
|
||||
mutation_rate: float = 0.1, crossover_rate: float = 0.8,
|
||||
workers: int = 4):
|
||||
self.population_size = population_size
|
||||
self.generations = generations
|
||||
self.mutation_rate = mutation_rate
|
||||
self.crossover_rate = crossover_rate
|
||||
self.workers = max(1, workers)
|
||||
|
||||
def optimize_reservations(self, requests: List[ReservationRequest],
|
||||
available_classrooms: List[Classroom],
|
||||
start_date: datetime, end_date: datetime) -> Individual:
|
||||
"""Run genetic algorithm to find optimal reservation schedule"""
|
||||
start_date: datetime, end_date: datetime,
|
||||
progress_callback=None) -> Individual:
|
||||
"""Run genetic algorithm with high concurrency to find optimal reservation schedule"""
|
||||
import concurrent.futures
|
||||
|
||||
classrooms_dict = {c.id: c for c in available_classrooms}
|
||||
requests_dict = {r.commission_id: r for r in requests}
|
||||
|
||||
@@ -159,18 +183,29 @@ class GeneticAlgorithm:
|
||||
|
||||
# Evolve population
|
||||
for generation in range(self.generations):
|
||||
# Calculate fitness for all individuals
|
||||
for individual in population:
|
||||
individual.calculate_fitness(classrooms_dict, requests_dict)
|
||||
# Calculate fitness for all individuals concurrently when workers > 1
|
||||
if self.workers > 1 and len(population) > 10:
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=self.workers) as executor:
|
||||
futures = [executor.submit(ind.calculate_fitness, classrooms_dict, requests_dict) for ind in population]
|
||||
concurrent.futures.wait(futures)
|
||||
else:
|
||||
for individual in population:
|
||||
individual.calculate_fitness(classrooms_dict, requests_dict)
|
||||
|
||||
# Sort by fitness (best first)
|
||||
population.sort(key=lambda x: x.fitness, reverse=True)
|
||||
|
||||
# Report progress if callback is provided
|
||||
if progress_callback:
|
||||
pct = int(((generation + 1) / self.generations) * 100)
|
||||
best_fit = population[0].fitness if population else 0.0
|
||||
progress_callback(generation + 1, self.generations, pct, best_fit)
|
||||
|
||||
# Create new generation
|
||||
new_population = []
|
||||
|
||||
# Elitism: keep best 10%
|
||||
elite_size = int(self.population_size * 0.1)
|
||||
elite_size = max(2, int(self.population_size * 0.1))
|
||||
new_population.extend(population[:elite_size])
|
||||
|
||||
# Crossover and mutation for remaining
|
||||
@@ -196,7 +231,9 @@ class GeneticAlgorithm:
|
||||
|
||||
population = new_population[:self.population_size]
|
||||
|
||||
# Return best individual from final generation
|
||||
# Calculate fitness for final generation
|
||||
for individual in population:
|
||||
individual.calculate_fitness(classrooms_dict, requests_dict)
|
||||
population.sort(key=lambda x: x.fitness, reverse=True)
|
||||
return population[0]
|
||||
|
||||
@@ -284,13 +321,14 @@ class GeneticAlgorithm:
|
||||
class ReservationOptimizer:
|
||||
"""High-level interface for the reservation optimization system"""
|
||||
|
||||
def __init__(self):
|
||||
self.ga = GeneticAlgorithm()
|
||||
def __init__(self, population_size: int = 50, generations: int = 100, workers: int = 4):
|
||||
self.ga = GeneticAlgorithm(population_size=population_size, generations=generations, workers=workers)
|
||||
|
||||
def optimize_schedule(self, commission_ids: List[int],
|
||||
admin_user_id: int,
|
||||
start_date: datetime = None,
|
||||
end_date: datetime = None) -> Dict:
|
||||
end_date: datetime = None,
|
||||
progress_callback = None) -> Dict:
|
||||
"""Optimize reservation schedule for given commissions"""
|
||||
|
||||
# Default date range if not provided
|
||||
@@ -325,7 +363,7 @@ class ReservationOptimizer:
|
||||
classrooms = Classroom.query.filter_by(is_active=True).all()
|
||||
|
||||
# Run genetic algorithm
|
||||
best_individual = self.ga.optimize_reservations(requests, classrooms, start_date, end_date)
|
||||
best_individual = self.ga.optimize_reservations(requests, classrooms, start_date, end_date, progress_callback=progress_callback)
|
||||
|
||||
# Convert to reservation format
|
||||
optimized_reservations = []
|
||||
|
||||
Reference in New Issue
Block a user