IGJSP 1.1.1__tar.gz → 1.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {igjsp-1.1.1 → igjsp-1.1.2}/PKG-INFO +1 -1
- {igjsp-1.1.1 → igjsp-1.1.2}/pyproject.toml +1 -1
- {igjsp-1.1.1 → igjsp-1.1.2}/src/IGJSP/generador.py +113 -33
- {igjsp-1.1.1 → igjsp-1.1.2}/.gitattributes +0 -0
- {igjsp-1.1.1 → igjsp-1.1.2}/.gitignore +0 -0
- {igjsp-1.1.1 → igjsp-1.1.2}/.vscode/settings.json +0 -0
- {igjsp-1.1.1 → igjsp-1.1.2}/LICENSE +0 -0
- {igjsp-1.1.1 → igjsp-1.1.2}/README.md +0 -0
- {igjsp-1.1.1 → igjsp-1.1.2}/src/IGJSP/Minizinc/Models/RD/JSP0.mzn +0 -0
- {igjsp-1.1.1 → igjsp-1.1.2}/src/IGJSP/Minizinc/Models/RD/JSP1.mzn +0 -0
- {igjsp-1.1.1 → igjsp-1.1.2}/src/IGJSP/Minizinc/Models/RD/JSP2.mzn +0 -0
- {igjsp-1.1.1 → igjsp-1.1.2}/src/IGJSP/Minizinc/Types/RD/FJSP/type0.dzn +0 -0
- {igjsp-1.1.1 → igjsp-1.1.2}/src/IGJSP/Minizinc/Types/RD/FJSP/type1.dzn +0 -0
- {igjsp-1.1.1 → igjsp-1.1.2}/src/IGJSP/Minizinc/Types/RD/FJSP/type2.dzn +0 -0
- {igjsp-1.1.1 → igjsp-1.1.2}/src/IGJSP/Minizinc/Types/RD/JSP/type0.dzn +0 -0
- {igjsp-1.1.1 → igjsp-1.1.2}/src/IGJSP/Minizinc/Types/RD/JSP/type1.dzn +0 -0
- {igjsp-1.1.1 → igjsp-1.1.2}/src/IGJSP/Minizinc/Types/RD/JSP/type2.dzn +0 -0
- {igjsp-1.1.1 → igjsp-1.1.2}/src/IGJSP/Minizinc/Types/RD/JSP0.mzn +0 -0
- {igjsp-1.1.1 → igjsp-1.1.2}/src/IGJSP/Minizinc/Types/RD/JSP1.mzn +0 -0
- {igjsp-1.1.1 → igjsp-1.1.2}/src/IGJSP/Minizinc/Types/RD/JSP2.mzn +0 -0
- {igjsp-1.1.1 → igjsp-1.1.2}/src/IGJSP/main.py +0 -0
|
@@ -28,27 +28,51 @@ def t(c):
|
|
|
28
28
|
|
|
29
29
|
# ------- Helpers internos para el DZN -------
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
|
|
32
|
+
def _parse_set_size(text, name):
|
|
33
|
+
"""
|
|
34
|
+
Obtiene el tamaño de un conjunto tipo:
|
|
35
|
+
JOBS = 1..50;
|
|
36
|
+
Devuelve 50.
|
|
37
|
+
"""
|
|
38
|
+
m = re.search(rf'\b{name}\b\s*=\s*1\.\.(\d+)\s*;', text)
|
|
39
|
+
if not m:
|
|
40
|
+
raise ValueError(f"No se pudo encontrar el conjunto {name} = 1..N; en el fichero DZN.")
|
|
41
|
+
return int(m.group(1))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _parse_speed(text):
|
|
32
45
|
"""
|
|
33
|
-
|
|
46
|
+
SPEED puede ir como:
|
|
47
|
+
SPEED = 1;
|
|
48
|
+
o (por si acaso) como 1..SPEED; (aunque en tu ejemplo es un escalar).
|
|
34
49
|
"""
|
|
35
|
-
m = re.search(
|
|
50
|
+
m = re.search(r'\bSPEED\b\s*=\s*(\d+)\s*;', text)
|
|
36
51
|
if m:
|
|
37
52
|
return int(m.group(1))
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
53
|
+
|
|
54
|
+
m = re.search(r'\bSPEED\b\s*=\s*1\.\.(\d+)\s*;', text)
|
|
55
|
+
if m:
|
|
56
|
+
return int(m.group(1))
|
|
57
|
+
|
|
58
|
+
# Si no aparece, asumimos 1 (como en muchos templates tuyos)
|
|
59
|
+
return 1
|
|
41
60
|
|
|
42
61
|
|
|
43
|
-
def
|
|
62
|
+
def _parse_array_from_arrayXd(text, name):
|
|
44
63
|
"""
|
|
45
|
-
|
|
46
|
-
name = [1,
|
|
47
|
-
|
|
48
|
-
name = [1
|
|
49
|
-
|
|
64
|
+
Extrae la parte entre corchetes de cosas tipo:
|
|
65
|
+
name = array3d(...,[1,2,3,...]);
|
|
66
|
+
name = array2d(...,[1,2,3,...]);
|
|
67
|
+
name = array1d(...,[1,2,3,...]);
|
|
68
|
+
|
|
69
|
+
Devuelve np.array de ints o None si no se encuentra.
|
|
50
70
|
"""
|
|
51
|
-
m = re.search(
|
|
71
|
+
m = re.search(
|
|
72
|
+
rf'\b{name}\b\s*=\s*array[123]d\([^[]*\[\s*(.*?)\s*\]\s*\)\s*;',
|
|
73
|
+
text,
|
|
74
|
+
re.DOTALL
|
|
75
|
+
)
|
|
52
76
|
if not m:
|
|
53
77
|
return None
|
|
54
78
|
|
|
@@ -62,6 +86,41 @@ def _parse_array_var(text, name):
|
|
|
62
86
|
|
|
63
87
|
return np.array([int(t) for t in tokens], dtype=int)
|
|
64
88
|
|
|
89
|
+
|
|
90
|
+
def _parse_array_fallback_plain(text, name):
|
|
91
|
+
"""
|
|
92
|
+
Fallback para cosas tipo:
|
|
93
|
+
name = [1,2,3,...];
|
|
94
|
+
por si en algún template no se usa arrayXd.
|
|
95
|
+
"""
|
|
96
|
+
m = re.search(
|
|
97
|
+
rf'\b{name}\b\s*=\s*\[\s*(.*?)\s*\]\s*;',
|
|
98
|
+
text,
|
|
99
|
+
re.DOTALL
|
|
100
|
+
)
|
|
101
|
+
if not m:
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
inner = m.group(1).strip()
|
|
105
|
+
if not inner:
|
|
106
|
+
return np.array([], dtype=int)
|
|
107
|
+
|
|
108
|
+
tokens = re.split(r'[\s,]+', inner)
|
|
109
|
+
tokens = [t for t in tokens if t != '']
|
|
110
|
+
|
|
111
|
+
return np.array([int(t) for t in tokens], dtype=int)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _parse_array_generic(text, name):
|
|
115
|
+
"""
|
|
116
|
+
Intenta primero arrayXd(...,[...]); y si no lo encuentra,
|
|
117
|
+
prueba el formato plano name = [ ... ];
|
|
118
|
+
"""
|
|
119
|
+
arr = _parse_array_from_arrayXd(text, name)
|
|
120
|
+
if arr is not None:
|
|
121
|
+
return arr
|
|
122
|
+
return _parse_array_fallback_plain(text, name)
|
|
123
|
+
|
|
65
124
|
#################################################################################
|
|
66
125
|
# #
|
|
67
126
|
# JSP #
|
|
@@ -183,24 +242,46 @@ class JSP:
|
|
|
183
242
|
|
|
184
243
|
def loadDznFile(path):
|
|
185
244
|
"""
|
|
186
|
-
Carga un .dzn generado
|
|
187
|
-
|
|
245
|
+
Carga un .dzn generado a partir de tus templates, del estilo:
|
|
246
|
+
|
|
247
|
+
JOBS = 1..J;
|
|
248
|
+
MACHINES = 1..M;
|
|
249
|
+
SPEED = S;
|
|
250
|
+
|
|
251
|
+
time = array3d(JOBS,MACHINES,1..SPEED,[...]);
|
|
252
|
+
energy = array3d(JOBS,MACHINES,1..SPEED,[...]);
|
|
253
|
+
precedence = array2d(JOBS,MACHINES,[...]);
|
|
254
|
+
|
|
255
|
+
Opcionalmente puede contener:
|
|
256
|
+
releaseDate = array1d(JOBS,[...]) (rddd = 1)
|
|
257
|
+
dueDate = array1d(JOBS,[...])
|
|
258
|
+
|
|
259
|
+
o
|
|
260
|
+
|
|
261
|
+
releaseDate = array2d(JOBS,MACHINES,[...]) (rddd = 2)
|
|
262
|
+
dueDate = array2d(JOBS,MACHINES,[...])
|
|
263
|
+
|
|
264
|
+
Devuelve un objeto JSP(**sol) consistente con loadJsonFile.
|
|
188
265
|
"""
|
|
189
266
|
with open(path, 'r', encoding='utf-8') as f:
|
|
190
267
|
text = f.read()
|
|
191
268
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
speed = _parse_int_var(text, 'Speed', default=1)
|
|
269
|
+
# Eliminar comentarios de Minizinc (% hasta final de línea)
|
|
270
|
+
text = re.sub(r'%.*', '', text)
|
|
195
271
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
272
|
+
# Leer tamaños de conjuntos
|
|
273
|
+
numJobs = _parse_set_size(text, "JOBS")
|
|
274
|
+
numMchs = _parse_set_size(text, "MACHINES")
|
|
275
|
+
speed = _parse_speed(text)
|
|
276
|
+
|
|
277
|
+
# Leer arrays principales
|
|
278
|
+
time_flat = _parse_array_generic(text, "time")
|
|
279
|
+
energy_flat = _parse_array_generic(text, "energy")
|
|
280
|
+
prec_flat = _parse_array_generic(text, "precedence")
|
|
199
281
|
|
|
200
282
|
if time_flat is None or energy_flat is None or prec_flat is None:
|
|
201
|
-
raise ValueError("
|
|
283
|
+
raise ValueError("No se pudieron leer 'time', 'energy' o 'precedence' del fichero DZN.")
|
|
202
284
|
|
|
203
|
-
# Comprobar tamaños
|
|
204
285
|
expected_te = numJobs * numMchs * speed
|
|
205
286
|
if time_flat.size != expected_te or energy_flat.size != expected_te:
|
|
206
287
|
raise ValueError(
|
|
@@ -218,29 +299,27 @@ class JSP:
|
|
|
218
299
|
EnergyConsumption = energy_flat.reshape((numJobs, numMchs, speed))
|
|
219
300
|
precedence = prec_flat.reshape((numJobs, numMchs))
|
|
220
301
|
|
|
221
|
-
# Reconstruir Orden a partir de
|
|
302
|
+
# Reconstruir Orden a partir de precedence (igual que en saveDznFile)
|
|
303
|
+
# precedence[j, m] = posición de la máquina m en la secuencia 0..M-1
|
|
222
304
|
Orden = np.zeros((numJobs, numMchs), dtype=int)
|
|
223
305
|
for j in range(numJobs):
|
|
224
|
-
# precedence[j, m] = posición de la máquina m en la secuencia
|
|
225
|
-
# argsort da el índice de la máquina por orden de prioridad
|
|
226
306
|
Orden[j, :] = np.argsort(precedence[j, :])
|
|
227
307
|
|
|
228
|
-
# Release / Due dates (
|
|
229
|
-
release_flat =
|
|
230
|
-
due_flat =
|
|
308
|
+
# --- Release / Due dates (si existen) ---
|
|
309
|
+
release_flat = _parse_array_generic(text, "releaseDate")
|
|
310
|
+
due_flat = _parse_array_generic(text, "dueDate")
|
|
231
311
|
|
|
232
312
|
if release_flat is None or due_flat is None:
|
|
233
|
-
# rddd = 0
|
|
313
|
+
# rddd = 0 (no fechas)
|
|
234
314
|
ReleaseDueDate = np.array([])
|
|
235
315
|
else:
|
|
236
|
-
# O bien vector por job, o matriz jobs x machines
|
|
237
316
|
if release_flat.size == numJobs and due_flat.size == numJobs:
|
|
238
|
-
# rddd = 1
|
|
317
|
+
# rddd = 1: job-level
|
|
239
318
|
ReleaseDueDate = np.zeros((numJobs, 2), dtype=int)
|
|
240
319
|
ReleaseDueDate[:, 0] = release_flat
|
|
241
320
|
ReleaseDueDate[:, 1] = due_flat
|
|
242
321
|
elif release_flat.size == numJobs * numMchs and due_flat.size == numJobs * numMchs:
|
|
243
|
-
# rddd = 2
|
|
322
|
+
# rddd = 2: operation-level
|
|
244
323
|
ReleaseDueDate = np.zeros((numJobs, numMchs, 2), dtype=int)
|
|
245
324
|
ReleaseDueDate[:, :, 0] = release_flat.reshape((numJobs, numMchs))
|
|
246
325
|
ReleaseDueDate[:, :, 1] = due_flat.reshape((numJobs, numMchs))
|
|
@@ -257,6 +336,7 @@ class JSP:
|
|
|
257
336
|
'ReleaseDateDueDate': ReleaseDueDate,
|
|
258
337
|
'Orden': Orden
|
|
259
338
|
}
|
|
339
|
+
|
|
260
340
|
return JSP(**sol)
|
|
261
341
|
|
|
262
342
|
def loadTaillardFile(path):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|