Spaces:
Sleeping
Sleeping
from fastapi import FastAPI, HTTPException | |
from pydantic import BaseModel | |
import numpy as np | |
import random | |
from typing import List, Dict | |
class Point(BaseModel): | |
id: str | |
x: float | |
y: float | |
class PathRequest(BaseModel): | |
points: List[Point] | |
class BezierPoint(BaseModel): | |
x: float | |
y: float | |
class PathResponse(BaseModel): | |
path: List[str] | |
distance: float | |
bezierPoints: List[BezierPoint] | |
class Config: | |
allow_population_by_field_name = True | |
alias_generator = lambda field_name: field_name.replace('_', '') | |
class InputData(BaseModel): | |
data: List[float] # Lista de caracter铆sticas num茅ricas (flotantes) | |
app = FastAPI() | |
def generate_bezier_points( | |
path: List[str], | |
points_dict: Dict[str, Point], | |
segments: int = 50 | |
): | |
bezier_points = [] | |
if len(path) < 3: | |
return bezier_points | |
for i in range(len(path) - 2): | |
p0 = points_dict[path[i]] | |
p1 = points_dict[path[i+1]] | |
p2 = points_dict[path[i+2]] | |
for t in np.linspace(0, 1, segments): | |
# B(t) = (1-t)虏P0 + 2(1-t)tP1 + t虏P2 | |
x = round((1-t)**2 * p0.x + 2*(1-t)*t * p1.x + t**2 * p2.x, 3) | |
y = round((1-t)**2 * p0.y + 2*(1-t)*t * p1.y + t**2 * p2.y, 3) | |
bezier_points.append(BezierPoint(x=x, y=y)) | |
return bezier_points | |
# ------------- algoritmo genetico ------------- | |
# Funci贸n para generar una poblaci贸n inicial aleatoria | |
def generar_poblacion(num_individuos, num_ciudades): | |
poblacion = [] | |
for _ in range(num_individuos): | |
individuo = list(range(num_ciudades)) | |
random.shuffle(individuo) | |
poblacion.append(individuo) | |
return poblacion | |
def calcular_aptitud(individuo, distancias, coordenadas): | |
# Funci贸n para evaluar la aptitud de | |
# un individuo (distancia total del recorrido) | |
distancia_total = 0 | |
coordenadas_iguales = all(coord == coordenadas[0] for coord in coordenadas) | |
if not coordenadas_iguales: | |
for i in range(len(individuo) - 1): | |
ciudad_actual = individuo[i] | |
siguiente_ciudad = individuo[i + 1] | |
distancia_total += distancias[ciudad_actual][siguiente_ciudad] | |
distancia_total += distancias[individuo[-1]][individuo[0]] | |
return distancia_total | |
# Funci贸n para seleccionar individuos para la reproducci贸n (torneo binario) | |
def seleccion_torneo(poblacion, distancias, coordenadas): | |
seleccionados = [] | |
for _ in range(len(poblacion)): | |
torneo = random.sample(poblacion, 2) | |
aptitud_torneo = [ | |
calcular_aptitud(individuo, distancias, coordenadas) | |
for individuo in torneo | |
] | |
seleccionado = torneo[aptitud_torneo.index(min(aptitud_torneo))] | |
seleccionados.append(seleccionado) | |
return seleccionados | |
# Funci贸n para realizar el cruce de dos padres para producir un hijo | |
def cruzar(padre1, padre2): | |
punto_cruce = random.randint(0, len(padre1) - 1) | |
hijo = padre1[:punto_cruce] + [ | |
gen for gen in padre2 if gen not in padre1[:punto_cruce] | |
] | |
return hijo | |
# Funci贸n para aplicar mutaciones en la poblaci贸n | |
def mutar(individuo, probabilidad_mutacion): | |
if random.random() < probabilidad_mutacion: | |
indices = random.sample(range(len(individuo)), 2) | |
individuo[indices[0]], individuo[indices[1]] = ( | |
individuo[indices[1]], | |
individuo[indices[0]], | |
) | |
return individuo | |
# Funci贸n para generar distancias aleatorias | |
# entre ciudades y sus coordenadas bidimensionales | |
def generar_distancias(num_ciudades): | |
distancias = [[0] * num_ciudades for _ in range(num_ciudades)] | |
coordenadas = [ | |
(random.uniform(0, 100), random.uniform(0, 100)) | |
for _ in range(num_ciudades) | |
] | |
for i in range(num_ciudades): | |
for j in range(i + 1, num_ciudades): | |
distancias[i][j] = distancias[j][i] = ( | |
sum((x - y) ** 2 | |
for x, y in zip(coordenadas[i], coordenadas[j])) ** 0.5 | |
) | |
return distancias, coordenadas | |
def algoritmo_genetico( | |
num_generaciones, num_ciudades, | |
num_individuos, probabilidad_mutacion, distancias, coordenadas): | |
poblacion = generar_poblacion(num_individuos, num_ciudades) | |
for generacion in range(num_generaciones): | |
poblacion = sorted( | |
poblacion, key=lambda x: calcular_aptitud( | |
x, distancias, coordenadas | |
) | |
) | |
mejor_individuo = poblacion[0] | |
mejor_distancia = calcular_aptitud( | |
mejor_individuo, distancias, coordenadas | |
) | |
seleccionados = seleccion_torneo(poblacion, distancias, coordenadas) | |
nueva_poblacion = [] | |
for i in range(0, len(seleccionados), 2): | |
padre1, padre2 = seleccionados[i], seleccionados[i + 1] | |
hijo1 = cruzar(padre1, padre2) | |
hijo2 = cruzar(padre2, padre1) | |
hijo1 = mutar(hijo1, probabilidad_mutacion) | |
hijo2 = mutar(hijo2, probabilidad_mutacion) | |
nueva_poblacion.extend([hijo1, hijo2]) | |
poblacion = nueva_poblacion | |
mejor_solucion = poblacion[0] | |
mejor_distancia = calcular_aptitud(mejor_solucion, distancias, coordenadas) | |
return mejor_solucion, mejor_distancia | |
# Ruta de predicci贸n | |
async def predict(data: InputData): | |
print(f"Data: {data}") | |
try: | |
# Convertir la lista de entrada a un array de NumPy para la predicci贸n | |
input_data = np.array(data.data).reshape( | |
1, -1 | |
) # Asumiendo que la entrada debe ser de forma (1, num_features) | |
num_ciudades = int(input_data[0][0]) | |
num_individuos = int(input_data[0][1]) | |
probabilidad_mutacion = float(input_data[0][2]) | |
num_generaciones = int(input_data[0][3]) | |
distancias, coordenadas = generar_distancias(num_ciudades) | |
mejor_solucion, mejor_distancia = algoritmo_genetico( | |
num_generaciones, | |
num_ciudades, | |
num_individuos, | |
probabilidad_mutacion, | |
distancias, | |
coordenadas | |
) | |
# print(type(mejor_solucion),mejor_solucion | |
respuesta = list(mejor_solucion) | |
print(respuesta) | |
prediction = respuesta | |
# return {"prediction": prediction.tolist()} | |
return {"prediction": prediction} | |
except Exception as e: | |
raise HTTPException(status_code=500, detail=str(e)) | |
async def find_shortest_path( | |
request: PathRequest, | |
population: int = 50, | |
mutation_prob: float = 0.1, | |
generations: int = 100 | |
): | |
try: | |
points = request.points | |
num_cities = len(points) | |
if num_cities < 3: | |
raise HTTPException( | |
status_code=400, | |
detail="need at least 3 points" | |
) | |
print( | |
f"parametros: population={population}, mutation_prob={mutation_prob}, generations={generations}" | |
) | |
distancias = [[0] * num_cities for _ in range(num_cities)] | |
coordenadas = [(p.x, p.y) for p in points] | |
points_dict = {p.id: p for p in points} | |
for i in range(num_cities): | |
for j in range(i + 1, num_cities): | |
dist = ((coordenadas[i][0] - coordenadas[j][0])**2 + | |
(coordenadas[i][1] - coordenadas[j][1])**2)**0.5 | |
distancias[i][j] = distancias[j][i] = dist | |
mejor_solucion, mejor_distancia = algoritmo_genetico( | |
num_generaciones=generations, | |
num_ciudades=num_cities, | |
num_individuos=population, | |
probabilidad_mutacion=mutation_prob, | |
distancias=distancias, | |
coordenadas=coordenadas | |
) | |
path_ids = [points[i].id for i in mejor_solucion] | |
bezier_points = generate_bezier_points(path_ids, points_dict) | |
return PathResponse( | |
path=path_ids, | |
distance=mejor_distancia, | |
bezierPoints=bezier_points | |
) | |
except Exception as e: | |
raise HTTPException( | |
status_code=500, | |
detail=str(e) | |
) | |