Spaces:
Sleeping
Sleeping
File size: 7,283 Bytes
639f871 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | """
demo_rome.py — Demo aggiornato: TouristProfile + fix ristoranti + tempi transit realistici.
"""
import sys, os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from core.models import PoI, PoICategory, TimeWindow
from core.distance import DistanceMatrix
from core.profile import (
profile_cultural_walker, profile_foodie_transit,
profile_family_mixed, profile_art_lover_car,
TouristProfile, TransportMode, MobilityLevel,
)
from solver import NSGA2Solver, SolverConfig
ROME_POIS = [
# Monumenti e luoghi
PoI("colosseo", "Colosseo", 41.8902,12.4922,0.98,120,TimeWindow(540,1110),PoICategory.MONUMENT, ["antico","unesco"]),
PoI("foro", "Foro Romano", 41.8925,12.4853,0.90, 90,TimeWindow(540,1110),PoICategory.MONUMENT, ["antico"]),
PoI("vaticano", "Musei Vaticani", 41.9065,12.4534,0.97,180,TimeWindow(540,1080),PoICategory.MUSEUM, ["arte","unesco"]),
PoI("sistina", "Cappella Sistina", 41.9029,12.4545,0.96, 60,TimeWindow(540,1080),PoICategory.MUSEUM, ["arte","rinascimento"]),
PoI("pantheon", "Pantheon", 41.8986,12.4769,0.93, 60,TimeWindow(540,1140),PoICategory.MONUMENT, ["antico","architettura"]),
PoI("trevi", "Fontana di Trevi", 41.9009,12.4833,0.88, 30,TimeWindow(0, 1440),PoICategory.MONUMENT, ["barocco","fotogenico"]),
PoI("spagna", "Piazza di Spagna", 41.9059,12.4823,0.80, 30,TimeWindow(0, 1440),PoICategory.VIEWPOINT, ["shopping","fotogenico"]),
PoI("borghese", "Galleria Borghese", 41.9143,12.4923,0.92,120,TimeWindow(540,1140),PoICategory.MUSEUM, ["arte","scultura"]),
PoI("navona", "Piazza Navona", 41.8992,12.4731,0.85, 45,TimeWindow(0, 1440),PoICategory.VIEWPOINT, ["barocco","fotogenico"]),
PoI("trastevere", "Trastevere", 41.8897,12.4703,0.82, 90,TimeWindow(600,1380),PoICategory.VIEWPOINT, ["quartiere","fotogenico"]),
PoI("castel", "Castel Sant'Angelo", 41.9031,12.4663,0.83, 90,TimeWindow(540,1080),PoICategory.MONUMENT, ["medievale","panorama"]),
PoI("aventino", "Giardino degli Aranci", 41.8837,12.4793,0.75, 40,TimeWindow(480,1200),PoICategory.PARK, ["panorama","fotogenico"]),
PoI("terme", "Terme di Caracalla", 41.8788,12.4924,0.78, 75,TimeWindow(540,1080),PoICategory.MONUMENT, ["antico"]),
# Ristoranti: solo per pranzo (TW 12-15) o cena (TW 19-22)
PoI("rist_rione", "Osteria del Rione", 41.8962,12.4751,0.74, 60,TimeWindow(720, 900),PoICategory.RESTAURANT,["cucina_romana"]),
PoI("rist_prati", "Trattoria Prati", 41.9042,12.4601,0.70, 75,TimeWindow(720, 900),PoICategory.RESTAURANT,["cucina_romana"]),
PoI("rist_testac","Testaccio da Mario", 41.8792,12.4770,0.76, 70,TimeWindow(720, 900),PoICategory.RESTAURANT,["offal","cucina_romana"]),
PoI("rist_cena1", "Ristorante San Pietro", 41.9050,12.4580,0.72, 80,TimeWindow(1140,1320),PoICategory.RESTAURANT,["cucina_romana"]),
PoI("rist_cena2", "Da Enzo al 29", 41.8891,12.4697,0.78, 90,TimeWindow(1140,1320),PoICategory.RESTAURANT,["cucina_romana","trastevere"]),
# Bar e caffè: colazione o pausa (TW ampia)
PoI("bar_greco", "Caffè Greco", 41.9057,12.4818,0.72, 20,TimeWindow(480,1320),PoICategory.BAR, ["storico","caffe"]),
PoI("bar_sant", "Sant'Eustachio il Caffè",41.8990,12.4752,0.75, 20,TimeWindow(480,1380),PoICategory.BAR, ["caffe","storico"]),
PoI("bar_campo", "Bar del Fico", 41.8968,12.4720,0.65, 25,TimeWindow(600,1380),PoICategory.BAR, ["aperitivo","vivace"]),
# Gelaterie: pomeriggio (TW 11-20)
PoI("gel_fatamorgana","Fatamorgana", 41.8993,12.4729,0.70, 20,TimeWindow(660,1200),PoICategory.GELATERIA, ["artigianale","insolito"]),
PoI("gel_giolitti", "Giolitti", 41.9003,12.4765,0.68, 20,TimeWindow(660,1260),PoICategory.GELATERIA, ["storico","classico"]),
PoI("gel_prati", "Fatamorgana Prati", 41.9090,12.4626,0.66, 20,TimeWindow(660,1260),PoICategory.GELATERIA, ["artigianale"]),
]
def profile_foodie_transit_updated() -> TouristProfile:
"""Gastronomico con mezzi: pranzo + cena, bar e gelateria nel pomeriggio."""
return TouristProfile(
transport_mode = TransportMode.TRANSIT,
mobility = MobilityLevel.NORMAL,
allowed_categories = ["restaurant", "bar", "gelateria", "monument", "viewpoint", "park"],
want_lunch = True,
want_dinner = True,
lunch_time = 720, # 12:00
dinner_time = 1200, # 20:00
meal_window = 60,
tag_weights = {"cucina_romana": 1.6, "offal": 0.5, "vivace": 1.2, "caffe": 1.1},
)
def run_profile(name, profile, config, dm):
print(f"\n{'━'*60}")
print(f" Profilo: {name}")
print(f"{'━'*60}")
print(profile.summary())
dm.profile = profile
solver = NSGA2Solver(ROME_POIS, dm, config, profile=profile)
def cb(gen, pareto, stats):
if gen % 30 == 0 or gen == 1:
print(f" gen {gen:3d} | pareto={stats['pareto_size']:2d} | "
f"best={stats['best_scalar']:.4f} | feasible={stats['feasible_pct']:.0f}%")
front = solver.solve(callback=cb)
feasible = [x for x in front if x.fitness.is_feasible] or front
if not feasible:
print(" Nessuna soluzione trovata.")
return
best = max(feasible, key=lambda x: x.fitness.scalar)
sched = solver.evaluator.decode(best)
# Conta categorie per verifica
cats = {}
for stop in sched.stops:
k = stop.poi.category.value
cats[k] = cats.get(k, 0) + 1
print(f"\n ★ Tour: {len(best.genes)} PoI | score={best.fitness.total_score:.2f} | "
f"{best.fitness.total_distance:.1f}km | {best.fitness.total_time}min")
print(f" Composizione: {', '.join(f'{v}×{k}' for k,v in sorted(cats.items()))}")
print()
if sched:
print(sched.summary())
def main():
print("Costruzione matrice distanze...")
dm = DistanceMatrix(ROME_POIS)
dm.build()
config = SolverConfig(
pop_size = 60,
max_generations = 200,
budget = 660, # 11 ore (09:30–20:30)
start_time = 570, # 09:30
start_lat = 41.896,
start_lon = 12.484,
stagnation_limit = 25,
)
profiles = [
("Gastronomico con mezzi (aggiornato)", profile_foodie_transit_updated()),
("Culturale a piedi", profile_cultural_walker()),
("Gastronomico con mezzi (standard)", profile_foodie_transit()),
("Family: misto", profile_family_mixed()),
("Art Lover: con auto", profile_art_lover_car()),
("Custom: solo viste, no pasti", TouristProfile(
transport_mode=TransportMode.WALK,
allowed_categories=["monument","viewpoint","park","bar","gelateria"],
want_lunch=False, want_dinner=False,
tag_weights={"fotogenico":1.5,"panorama":1.4,"caffe":1.1},
)),
]
for name, profile in profiles:
run_profile(name, profile, config, dm)
print(f"\n{'━'*60}\n Completato.\n")
if __name__ == "__main__":
main() |