Files changed (5) hide show
  1. Dockerfile +0 -13
  2. README.md +10 -12
  3. main.py +0 -256
  4. pyproject.toml +0 -18
  5. requirements.txt +0 -37
Dockerfile DELETED
@@ -1,13 +0,0 @@
1
- FROM python:3.13-slim
2
-
3
- WORKDIR /app
4
-
5
- COPY pyproject.toml .
6
-
7
- RUN pip install .
8
-
9
- COPY . .
10
-
11
- EXPOSE 8000
12
-
13
- CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md CHANGED
@@ -1,12 +1,10 @@
1
- ---
2
- title: AG TSP API
3
- emoji: 🚀
4
- colorFrom: indigo
5
- colorTo: blue
6
- sdk: docker
7
- app_file: main.py
8
- app_port: 8000
9
- ---
10
-
11
- ### Mi API para la PC1 de CC0A2A
12
- Endpoint: `/shortest-path/`
 
1
+ ---
2
+ title: PC1 BE
3
+ emoji: 📚
4
+ colorFrom: red
5
+ colorTo: blue
6
+ sdk: docker
7
+ pinned: false
8
+ ---
9
+
10
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
main.py DELETED
@@ -1,256 +0,0 @@
1
- from fastapi import FastAPI, HTTPException
2
- from pydantic import BaseModel
3
- import numpy as np
4
- import random
5
- from typing import List, Dict
6
-
7
-
8
- class Point(BaseModel):
9
- id: str
10
- x: float
11
- y: float
12
-
13
-
14
- class PathRequest(BaseModel):
15
- points: List[Point]
16
-
17
-
18
- class BezierPoint(BaseModel):
19
- x: float
20
- y: float
21
-
22
-
23
- class PathResponse(BaseModel):
24
- path: List[str]
25
- distance: float
26
- bezierPoints: List[BezierPoint]
27
-
28
- class Config:
29
- allow_population_by_field_name = True
30
- alias_generator = lambda field_name: field_name.replace('_', '')
31
-
32
-
33
- class InputData(BaseModel):
34
- data: List[float] # Lista de características numéricas (flotantes)
35
-
36
-
37
- app = FastAPI()
38
-
39
-
40
- def generate_bezier_points(
41
- path: List[str],
42
- points_dict: Dict[str, Point],
43
- segments: int = 50
44
- ):
45
- bezier_points = []
46
- if len(path) < 3:
47
- return bezier_points
48
-
49
- for i in range(len(path) - 2):
50
- p0 = points_dict[path[i]]
51
- p1 = points_dict[path[i+1]]
52
- p2 = points_dict[path[i+2]]
53
-
54
- for t in np.linspace(0, 1, segments):
55
- # B(t) = (1-t)²P0 + 2(1-t)tP1 + t²P2
56
- x = round((1-t)**2 * p0.x + 2*(1-t)*t * p1.x + t**2 * p2.x, 3)
57
- y = round((1-t)**2 * p0.y + 2*(1-t)*t * p1.y + t**2 * p2.y, 3)
58
- bezier_points.append(BezierPoint(x=x, y=y))
59
- return bezier_points
60
-
61
- # ------------- algoritmo genetico -------------
62
- # Función para generar una población inicial aleatoria
63
-
64
-
65
- def generar_poblacion(num_individuos, num_ciudades):
66
- poblacion = []
67
- for _ in range(num_individuos):
68
- individuo = list(range(num_ciudades))
69
- random.shuffle(individuo)
70
- poblacion.append(individuo)
71
- return poblacion
72
-
73
-
74
- def calcular_aptitud(individuo, distancias, coordenadas):
75
- # Función para evaluar la aptitud de
76
- # un individuo (distancia total del recorrido)
77
- distancia_total = 0
78
- coordenadas_iguales = all(coord == coordenadas[0] for coord in coordenadas)
79
-
80
- if not coordenadas_iguales:
81
- for i in range(len(individuo) - 1):
82
- ciudad_actual = individuo[i]
83
- siguiente_ciudad = individuo[i + 1]
84
- distancia_total += distancias[ciudad_actual][siguiente_ciudad]
85
-
86
- distancia_total += distancias[individuo[-1]][individuo[0]]
87
-
88
- return distancia_total
89
-
90
- # Función para seleccionar individuos para la reproducción (torneo binario)
91
-
92
-
93
- def seleccion_torneo(poblacion, distancias, coordenadas):
94
- seleccionados = []
95
- for _ in range(len(poblacion)):
96
- torneo = random.sample(poblacion, 2)
97
- aptitud_torneo = [
98
- calcular_aptitud(individuo, distancias, coordenadas)
99
- for individuo in torneo
100
- ]
101
- seleccionado = torneo[aptitud_torneo.index(min(aptitud_torneo))]
102
- seleccionados.append(seleccionado)
103
- return seleccionados
104
-
105
- # Función para realizar el cruce de dos padres para producir un hijo
106
-
107
-
108
- def cruzar(padre1, padre2):
109
- punto_cruce = random.randint(0, len(padre1) - 1)
110
- hijo = padre1[:punto_cruce] + [
111
- gen for gen in padre2 if gen not in padre1[:punto_cruce]
112
- ]
113
- return hijo
114
-
115
-
116
- # Función para aplicar mutaciones en la población
117
- def mutar(individuo, probabilidad_mutacion):
118
- if random.random() < probabilidad_mutacion:
119
- indices = random.sample(range(len(individuo)), 2)
120
- individuo[indices[0]], individuo[indices[1]] = (
121
- individuo[indices[1]],
122
- individuo[indices[0]],
123
- )
124
- return individuo
125
-
126
- # Función para generar distancias aleatorias
127
- # entre ciudades y sus coordenadas bidimensionales
128
-
129
-
130
- def generar_distancias(num_ciudades):
131
- distancias = [[0] * num_ciudades for _ in range(num_ciudades)]
132
- coordenadas = [
133
- (random.uniform(0, 100), random.uniform(0, 100))
134
- for _ in range(num_ciudades)
135
- ]
136
-
137
- for i in range(num_ciudades):
138
- for j in range(i + 1, num_ciudades):
139
- distancias[i][j] = distancias[j][i] = (
140
- sum((x - y) ** 2
141
- for x, y in zip(coordenadas[i], coordenadas[j])) ** 0.5
142
- )
143
-
144
- return distancias, coordenadas
145
-
146
-
147
- def algoritmo_genetico(
148
- num_generaciones, num_ciudades,
149
- num_individuos, probabilidad_mutacion, distancias, coordenadas):
150
- poblacion = generar_poblacion(num_individuos, num_ciudades)
151
- for generacion in range(num_generaciones):
152
- poblacion = sorted(
153
- poblacion, key=lambda x: calcular_aptitud(
154
- x, distancias, coordenadas
155
- )
156
- )
157
- mejor_individuo = poblacion[0]
158
- mejor_distancia = calcular_aptitud(
159
- mejor_individuo, distancias, coordenadas
160
- )
161
- seleccionados = seleccion_torneo(poblacion, distancias, coordenadas)
162
- nueva_poblacion = []
163
- for i in range(0, len(seleccionados), 2):
164
- padre1, padre2 = seleccionados[i], seleccionados[i + 1]
165
- hijo1 = cruzar(padre1, padre2)
166
- hijo2 = cruzar(padre2, padre1)
167
- hijo1 = mutar(hijo1, probabilidad_mutacion)
168
- hijo2 = mutar(hijo2, probabilidad_mutacion)
169
- nueva_poblacion.extend([hijo1, hijo2])
170
- poblacion = nueva_poblacion
171
- mejor_solucion = poblacion[0]
172
- mejor_distancia = calcular_aptitud(mejor_solucion, distancias, coordenadas)
173
- return mejor_solucion, mejor_distancia
174
-
175
- # Ruta de predicción
176
-
177
-
178
- @app.post("/predict/")
179
- async def predict(data: InputData):
180
- print(f"Data: {data}")
181
- try:
182
- # Convertir la lista de entrada a un array de NumPy para la predicción
183
- input_data = np.array(data.data).reshape(
184
- 1, -1
185
- ) # Asumiendo que la entrada debe ser de forma (1, num_features)
186
- num_ciudades = int(input_data[0][0])
187
- num_individuos = int(input_data[0][1])
188
- probabilidad_mutacion = float(input_data[0][2])
189
- num_generaciones = int(input_data[0][3])
190
- distancias, coordenadas = generar_distancias(num_ciudades)
191
- mejor_solucion, mejor_distancia = algoritmo_genetico(
192
- num_generaciones,
193
- num_ciudades,
194
- num_individuos,
195
- probabilidad_mutacion,
196
- distancias,
197
- coordenadas
198
- )
199
- # print(type(mejor_solucion),mejor_solucion
200
- respuesta = list(mejor_solucion)
201
- print(respuesta)
202
- prediction = respuesta
203
- # return {"prediction": prediction.tolist()}
204
- return {"prediction": prediction}
205
- except Exception as e:
206
- raise HTTPException(status_code=500, detail=str(e))
207
-
208
-
209
- @app.post("/shortest-path/", response_model=PathResponse)
210
- async def find_shortest_path(
211
- request: PathRequest,
212
- population: int = 50,
213
- mutation_prob: float = 0.1,
214
- generations: int = 100
215
- ):
216
- try:
217
- points = request.points
218
- num_cities = len(points)
219
- if num_cities < 3:
220
- raise HTTPException(
221
- status_code=400,
222
- detail="need at least 3 points"
223
- )
224
-
225
- print(
226
- f"parametros: population={population}, mutation_prob={mutation_prob}, generations={generations}"
227
- )
228
-
229
- distancias = [[0] * num_cities for _ in range(num_cities)]
230
- coordenadas = [(p.x, p.y) for p in points]
231
- points_dict = {p.id: p for p in points}
232
- for i in range(num_cities):
233
- for j in range(i + 1, num_cities):
234
- dist = ((coordenadas[i][0] - coordenadas[j][0])**2 +
235
- (coordenadas[i][1] - coordenadas[j][1])**2)**0.5
236
- distancias[i][j] = distancias[j][i] = dist
237
- mejor_solucion, mejor_distancia = algoritmo_genetico(
238
- num_generaciones=generations,
239
- num_ciudades=num_cities,
240
- num_individuos=population,
241
- probabilidad_mutacion=mutation_prob,
242
- distancias=distancias,
243
- coordenadas=coordenadas
244
- )
245
- path_ids = [points[i].id for i in mejor_solucion]
246
- bezier_points = generate_bezier_points(path_ids, points_dict)
247
- return PathResponse(
248
- path=path_ids,
249
- distance=mejor_distancia,
250
- bezierPoints=bezier_points
251
- )
252
- except Exception as e:
253
- raise HTTPException(
254
- status_code=500,
255
- detail=str(e)
256
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pyproject.toml DELETED
@@ -1,18 +0,0 @@
1
- [project]
2
- name = "backend-ag"
3
- version = "2025.04.16"
4
- dependencies = [
5
- "fastapi[standard]",
6
- "numpy",
7
- "pydantic"
8
- ]
9
- requires-python = ">=3.10"
10
- authors = [
11
- {name = "Alex Vega", email = "avegab@uni.pe"},
12
- ]
13
- maintainers = [
14
- {name = "Alex Vega", email = "avegab@uni.pe"},
15
- ]
16
- description = "Backend para el proyecto PC1 del curso CC0A2A"
17
- license = "MIT"
18
- keywords = ["genetic algorithm", "android", "kotlin", "python"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requirements.txt DELETED
@@ -1,37 +0,0 @@
1
- annotated-types==0.7.0
2
- anyio==4.9.0
3
- backend-ag @ file:///C:/Users/la/repos/github.com/axvg/uni/2025-01/CC0A2A/semanas/04/pc1/backend
4
- certifi==2025.1.31
5
- click==8.1.8
6
- colorama==0.4.6
7
- dnspython==2.7.0
8
- email_validator==2.2.0
9
- fastapi==0.115.12
10
- fastapi-cli==0.0.7
11
- h11==0.14.0
12
- httpcore==1.0.8
13
- httptools==0.6.4
14
- httpx==0.28.1
15
- idna==3.10
16
- Jinja2==3.1.6
17
- markdown-it-py==3.0.0
18
- MarkupSafe==3.0.2
19
- mdurl==0.1.2
20
- numpy==2.2.5
21
- pydantic==2.11.3
22
- pydantic_core==2.33.1
23
- Pygments==2.19.1
24
- python-dotenv==1.1.0
25
- python-multipart==0.0.20
26
- PyYAML==6.0.2
27
- rich==14.0.0
28
- rich-toolkit==0.14.1
29
- shellingham==1.5.4
30
- sniffio==1.3.1
31
- starlette==0.46.2
32
- typer==0.15.2
33
- typing-inspection==0.4.0
34
- typing_extensions==4.13.2
35
- uvicorn==0.34.2
36
- watchfiles==1.0.5
37
- websockets==15.0.1