IGJSP 1.1.0__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.0 → igjsp-1.1.2}/PKG-INFO +1 -1
- {igjsp-1.1.0 → igjsp-1.1.2}/pyproject.toml +1 -1
- {igjsp-1.1.0 → igjsp-1.1.2}/src/IGJSP/generador.py +136 -20
- {igjsp-1.1.0 → igjsp-1.1.2}/.gitattributes +0 -0
- {igjsp-1.1.0 → igjsp-1.1.2}/.gitignore +0 -0
- {igjsp-1.1.0 → igjsp-1.1.2}/.vscode/settings.json +0 -0
- {igjsp-1.1.0 → igjsp-1.1.2}/LICENSE +0 -0
- {igjsp-1.1.0 → igjsp-1.1.2}/README.md +0 -0
- {igjsp-1.1.0 → igjsp-1.1.2}/src/IGJSP/Minizinc/Models/RD/JSP0.mzn +0 -0
- {igjsp-1.1.0 → igjsp-1.1.2}/src/IGJSP/Minizinc/Models/RD/JSP1.mzn +0 -0
- {igjsp-1.1.0 → igjsp-1.1.2}/src/IGJSP/Minizinc/Models/RD/JSP2.mzn +0 -0
- {igjsp-1.1.0 → igjsp-1.1.2}/src/IGJSP/Minizinc/Types/RD/FJSP/type0.dzn +0 -0
- {igjsp-1.1.0 → igjsp-1.1.2}/src/IGJSP/Minizinc/Types/RD/FJSP/type1.dzn +0 -0
- {igjsp-1.1.0 → igjsp-1.1.2}/src/IGJSP/Minizinc/Types/RD/FJSP/type2.dzn +0 -0
- {igjsp-1.1.0 → igjsp-1.1.2}/src/IGJSP/Minizinc/Types/RD/JSP/type0.dzn +0 -0
- {igjsp-1.1.0 → igjsp-1.1.2}/src/IGJSP/Minizinc/Types/RD/JSP/type1.dzn +0 -0
- {igjsp-1.1.0 → igjsp-1.1.2}/src/IGJSP/Minizinc/Types/RD/JSP/type2.dzn +0 -0
- {igjsp-1.1.0 → igjsp-1.1.2}/src/IGJSP/Minizinc/Types/RD/JSP0.mzn +0 -0
- {igjsp-1.1.0 → igjsp-1.1.2}/src/IGJSP/Minizinc/Types/RD/JSP1.mzn +0 -0
- {igjsp-1.1.0 → igjsp-1.1.2}/src/IGJSP/Minizinc/Types/RD/JSP2.mzn +0 -0
- {igjsp-1.1.0 → igjsp-1.1.2}/src/IGJSP/main.py +0 -0
|
@@ -26,6 +26,101 @@ def t(c):
|
|
|
26
26
|
return 4.0704 * np.log(2) / np.log(1 + (c* 2.5093)**3)
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
# ------- Helpers internos para el DZN -------
|
|
30
|
+
|
|
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):
|
|
45
|
+
"""
|
|
46
|
+
SPEED puede ir como:
|
|
47
|
+
SPEED = 1;
|
|
48
|
+
o (por si acaso) como 1..SPEED; (aunque en tu ejemplo es un escalar).
|
|
49
|
+
"""
|
|
50
|
+
m = re.search(r'\bSPEED\b\s*=\s*(\d+)\s*;', text)
|
|
51
|
+
if m:
|
|
52
|
+
return int(m.group(1))
|
|
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
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _parse_array_from_arrayXd(text, name):
|
|
63
|
+
"""
|
|
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.
|
|
70
|
+
"""
|
|
71
|
+
m = re.search(
|
|
72
|
+
rf'\b{name}\b\s*=\s*array[123]d\([^[]*\[\s*(.*?)\s*\]\s*\)\s*;',
|
|
73
|
+
text,
|
|
74
|
+
re.DOTALL
|
|
75
|
+
)
|
|
76
|
+
if not m:
|
|
77
|
+
return None
|
|
78
|
+
|
|
79
|
+
inner = m.group(1).strip()
|
|
80
|
+
if not inner:
|
|
81
|
+
return np.array([], dtype=int)
|
|
82
|
+
|
|
83
|
+
# Separar por comas o espacios
|
|
84
|
+
tokens = re.split(r'[\s,]+', inner)
|
|
85
|
+
tokens = [t for t in tokens if t != '']
|
|
86
|
+
|
|
87
|
+
return np.array([int(t) for t in tokens], dtype=int)
|
|
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
|
+
|
|
29
124
|
#################################################################################
|
|
30
125
|
# #
|
|
31
126
|
# JSP #
|
|
@@ -147,24 +242,46 @@ class JSP:
|
|
|
147
242
|
|
|
148
243
|
def loadDznFile(path):
|
|
149
244
|
"""
|
|
150
|
-
Carga un .dzn generado
|
|
151
|
-
|
|
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.
|
|
152
265
|
"""
|
|
153
266
|
with open(path, 'r', encoding='utf-8') as f:
|
|
154
267
|
text = f.read()
|
|
155
268
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
speed = _parse_int_var(text, 'Speed', default=1)
|
|
269
|
+
# Eliminar comentarios de Minizinc (% hasta final de línea)
|
|
270
|
+
text = re.sub(r'%.*', '', text)
|
|
159
271
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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")
|
|
163
281
|
|
|
164
282
|
if time_flat is None or energy_flat is None or prec_flat is None:
|
|
165
|
-
raise ValueError("
|
|
283
|
+
raise ValueError("No se pudieron leer 'time', 'energy' o 'precedence' del fichero DZN.")
|
|
166
284
|
|
|
167
|
-
# Comprobar tamaños
|
|
168
285
|
expected_te = numJobs * numMchs * speed
|
|
169
286
|
if time_flat.size != expected_te or energy_flat.size != expected_te:
|
|
170
287
|
raise ValueError(
|
|
@@ -182,29 +299,27 @@ class JSP:
|
|
|
182
299
|
EnergyConsumption = energy_flat.reshape((numJobs, numMchs, speed))
|
|
183
300
|
precedence = prec_flat.reshape((numJobs, numMchs))
|
|
184
301
|
|
|
185
|
-
# 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
|
|
186
304
|
Orden = np.zeros((numJobs, numMchs), dtype=int)
|
|
187
305
|
for j in range(numJobs):
|
|
188
|
-
# precedence[j, m] = posición de la máquina m en la secuencia
|
|
189
|
-
# argsort da el índice de la máquina por orden de prioridad
|
|
190
306
|
Orden[j, :] = np.argsort(precedence[j, :])
|
|
191
307
|
|
|
192
|
-
# Release / Due dates (
|
|
193
|
-
release_flat =
|
|
194
|
-
due_flat =
|
|
308
|
+
# --- Release / Due dates (si existen) ---
|
|
309
|
+
release_flat = _parse_array_generic(text, "releaseDate")
|
|
310
|
+
due_flat = _parse_array_generic(text, "dueDate")
|
|
195
311
|
|
|
196
312
|
if release_flat is None or due_flat is None:
|
|
197
|
-
# rddd = 0
|
|
313
|
+
# rddd = 0 (no fechas)
|
|
198
314
|
ReleaseDueDate = np.array([])
|
|
199
315
|
else:
|
|
200
|
-
# O bien vector por job, o matriz jobs x machines
|
|
201
316
|
if release_flat.size == numJobs and due_flat.size == numJobs:
|
|
202
|
-
# rddd = 1
|
|
317
|
+
# rddd = 1: job-level
|
|
203
318
|
ReleaseDueDate = np.zeros((numJobs, 2), dtype=int)
|
|
204
319
|
ReleaseDueDate[:, 0] = release_flat
|
|
205
320
|
ReleaseDueDate[:, 1] = due_flat
|
|
206
321
|
elif release_flat.size == numJobs * numMchs and due_flat.size == numJobs * numMchs:
|
|
207
|
-
# rddd = 2
|
|
322
|
+
# rddd = 2: operation-level
|
|
208
323
|
ReleaseDueDate = np.zeros((numJobs, numMchs, 2), dtype=int)
|
|
209
324
|
ReleaseDueDate[:, :, 0] = release_flat.reshape((numJobs, numMchs))
|
|
210
325
|
ReleaseDueDate[:, :, 1] = due_flat.reshape((numJobs, numMchs))
|
|
@@ -221,6 +336,7 @@ class JSP:
|
|
|
221
336
|
'ReleaseDateDueDate': ReleaseDueDate,
|
|
222
337
|
'Orden': Orden
|
|
223
338
|
}
|
|
339
|
+
|
|
224
340
|
return JSP(**sol)
|
|
225
341
|
|
|
226
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
|