Skip to content

Commit

Permalink
Complete1
Browse files Browse the repository at this point in the history
  • Loading branch information
Polirecyliente committed May 17, 2021
0 parents commit b8095f1
Show file tree
Hide file tree
Showing 13 changed files with 696 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__pycache__
150 changes: 150 additions & 0 deletions API/functions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import numpy as np

def c_range(llim, ulim):
"""
c_range es por correct range, llim es por lower limit, ulim es por upper limit.
Esta functión toma un range que empieza en llim y devuelve un range que empieza en llim - 1.
"""

return range(llim - 1, ulim)

def PT(T):
"""
P es por ponderación, PT es por Ponderación de Todos los trabajos en todas las máquinas.
Esta función devuelve la ponderación de las tres partes de un número triangular difuso
"""

I = range(len(T))

if (np.array(T).ndim == 3):
P = [[(T[i][u][0] + 2*T[i][u][1] + T[i][u][2])/4 for u in range(len(T[0]))] for i in I]
elif (np.array(T).ndim == 2):
P = [(T[i][0] + 2*T[i][1] + T[i][2])/4 for i in I]
elif (np.array(T).ndim == 1):
P = (T[0] + 2*T[1] + T[2])/4
return P

def ct(pi, i, ex = []):
"""
pi es una secuencia de trabajos.
i es uno de los trabajos.
ex es una lista de trabajos excluidos cuya posición no es cambiada por i
Esta función devuelve una lista de secuencias, poniendo i en cada puesto de la secuencia de trabajos. ct es por cambiar trabajos.
"""
pi2 = pi.copy()
pis = []

if i not in pi2: pi2.append(i)
for j in range(len(pi2)):
if j not in ex:
pi2[pi2.index(i)], pi2[j] = pi2[j], pi2[pi2.index(i)]
pis.append(pi2.copy())
return pis

def st(pi, i, ex = []):
"""
pi es una secuencia de trabajos.
i es uno de los trabajos.
ex es una lista de trabajos excluidos cuya posición no es cambiada por i
Esta función devuelve una lista de secuencias, intercambiando i por cada uno de los otros trabajos de la secuencia. st es por swap trabajos (porque esta función es creada por necesidad de la función swap del algoritmo MNIG).
"""
pi2 = pi.copy()
pis = []
if i not in pi2: pi2.append(i)

for j in range(len(pi2)):
if j not in ex:
k = pi2.index(i)
pi2[k], pi2[j] = pi2[j], pi2[k]
pis.append(pi2.copy())
pi2[k], pi2[j] = pi2[j], pi2[k]
return pis

def makespan(pi, Tn, U_s, Pn, debug = False):
"""
pi es una secuencia de trabajos.
Tn son los tiempos de producción en orden de secuencia natural, 1, 2 ,3, ...
U_s es el conjunto de máquinas o unidades de la etapa s.
Pn es la ponderación de los números triangulares en orden de secuencia natural, 1, 2, 3, ...
Esta función calcula el makespan de una secuencia en el modelo FMMSP
"""

# L es la cantidad total de etapas del sistema de producción.
L = len(U_s)

# I es el conjunto de trabajos.
I = range(len(pi))

T = [Tn[pi[i] - 1] for i in I]
P = [Pn[pi[i] - 1] for i in I]

# S es el conjunto de etapas.
S = range(L)

# EsC es por Early start Comparación, esta variable guarda los tiempos ponderados (para hacer comparaciones entre tiempos diferentes) más tempranos en que un trabajo puede iniciar en cada máquina dada u
UT = np.sum([len(U_s[s]) for s in c_range(1, L)])
U = range(UT)

Ts = [[(0, 0, 0) for s in S] for i in I]
Tf = [[(0, 0, 0) for s in S] for i in I]
UI = [[(0, 0, 0) for s in S] for i in I]
TfU = [[(0, 0, 0) for u in U] for i in I]

for s in S:
if (debug == True): print()
for i in I:
if s == 0 and i == 0:
v = np.argmin([PT(np.add(TfU[i][u], T[i][u])) for u in U_s[s]]) + U_s[s][0]

Ts[i][s] = (0, 0, 0)
Tf[i][s] = np.add(Ts[i][s], T[i][v])
for j in I: TfU[j][v] = Tf[i][s]
if s == 0 and i > 0:
v = np.argmin([PT(np.add(TfU[i][u], T[i][u])) for u in U_s[s]]) + U_s[s][0]

Ts[i][s] = TfU[i][v]
Tf[i][s] = np.add(Ts[i][s], T[i][v])
for j in I: TfU[j][v] = Tf[i][s]
if s > 0 and i == 0:
for u in U_s[s]:
if (PT(Tf[i][s - 1]) > PT(TfU[i][u])):
TfU[i][u] = Tf[i][s - 1]
v = np.argmin([PT(np.add(TfU[i][u], T[i][u])) for u in U_s[s]]) + U_s[s][0]

Ts[i][s] = Tf[i][s - 1]
Tf[i][s] = np.add(Ts[i][s], T[i][v])
for j in I: TfU[j][v] = Tf[i][s]
if s > 0 and i > 0:
for u in U_s[s]:
if (PT(Tf[i][s - 1]) > PT(TfU[i][u])):
TfU[i][u] = Tf[i][s - 1]
v = np.argmin([PT(np.add(TfU[i][u], T[i][u])) for u in U_s[s]]) + U_s[s][0]

if (PT(Tf[i][s - 1]) > PT(TfU[i][v])):
Ts[i][s] = Tf[i][s - 1]
else:
Ts[i][s] = TfU[i][v]

Tf[i][s] = np.add(Ts[i][s], T[i][v])
for j in I: TfU[j][v] = Tf[i][s]
if (debug == True):
piO = "%2d" % (pi[i])
TsO = "(%2d, %2d, %2d)" % (Ts[i][s][0], Ts[i][s][1], Ts[i][s][2])
TfO = "(%2d, %2d, %2d)" % (Tf[i][s][0], Tf[i][s][1], Tf[i][s][2])
print("pi[i]:", piO, " s:", s + 1, " u:", v + 1, " Ts:", TsO, " Tf:", TfO)

n = np.argmax(PT([[Tf[i][L - 1]] for i in I]))

return Tf[n][L - 1]
9 changes: 9 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
The MIT License (MIT)

Copyright © 2021 Polirecyliente

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Multi-Neighborhood Iterated Greedy (MNIG) algorithm, to solve the Fuzzy Multiproduct Multistage Scheduling Problem (FMMSP)

This is the MNIG algorithm written in Python to solve the FMMSP. The
MNIG algorithm is implemented here as defined by the article:
https://www.sciencedirect.com/science/article/abs/pii/S0950705120300344

The FMMSP model is defined in
https://www.sciencedirect.com/science/article/abs/pii/S0925231220302563

This implementation is at least as good as the DBSA-LS algorithm to
solve the FMMSP (as is confirmed using the only available public
instance of the FMMSP, which is the one that comes by default inside
this program).

# Usage

`python3 MNIG_to_FMMSP 5 1.1 4`

This example command makes the algorithm run 250 iterations (which comes
from 10\*5^2^).

The meanings of the arguments are taken from the MNIG algorithm, they
are N, T~0~, and d, respectively.

# Installation

Download the release and execute it as described in the Usage (so you
need to have the `python3` interpreter installed already).

# Details about the code

The comments and the doc-strings are written in Spanish, because this
project was originally created in Spanish.

An optional `--debug` flag can be passed to the program, to print each
iteration with its sequence, the makespan of the sequence, and a few
other info. At the end of the iterations, a table is printed with the
starting times and the finish times of each job in the sequence, among
other info.
146 changes: 146 additions & 0 deletions __main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
from algorithms import DNEH_SMR, destruction_reconstruction, local_search
from API.functions import PT, c_range, makespan

import numpy as np
import random
import math

# Soluciones probadas:
# Makespan: (36, 44, 52)
# Secuencias:
# [7, 8, 6, 10, 1, 9, 5, 4, 2, 3]
# [7, 8, 6, 10, 5, 9, 1, 4, 2, 3]
# [6, 1, 9, 7, 8, 10, 4, 5, 2, 3]
# [1, 6, 9, 10, 7, 8, 5, 4, 2, 3]
# [6, 5, 10, 1, 9, 7, 8, 4, 2, 3]
# [6, 10, 1, 8, 9, 4, 5, 7, 2, 3]
# [6, 5, 7, 10, 8, 1, 4, 9, 2, 3]
# [9, 7, 1, 6, 8, 10, 4, 5, 2, 3]
# [5, 8, 7, 6, 10, 4, 1, 9, 2, 3]
# [7, 1, 6, 10, 5, 9, 8, 4, 2, 3]

# Datos de la instancia de prueba
Tn = [
[(10, 12, 13), (11, 12, 14), (9, 10, 12), (6, 7, 9), (8, 9, 10)],
[(7, 8, 10), (8, 9, 10), (5, 6, 8), (4, 5, 6), (4, 5, 6)],
[(10, 11, 12), (9, 10, 12), (2, 3, 4), (5, 6, 8), (5, 6, 7)],
[(8, 9, 10), (6, 7, 8), (7, 8, 9), (4, 5, 6), (5, 6, 8)],
[(6, 7, 8), (8, 9, 10), (4, 5, 6), (6, 7, 8), (6, 7, 9)],
[(4, 5, 6), (2, 3, 4), (15, 16, 19), (13, 14, 15), (15, 16, 20)],
[(11, 13, 15), (1, 2, 3), (11, 13, 14), (10, 11, 13), (9, 10, 12)],
[(10, 11, 12), (18, 19, 23), (5, 6, 7), (6, 7, 9), (7, 8, 10)],
[(5, 6, 8), (4, 5, 6), (14, 15, 16), (19, 21, 25), (10, 12, 13)],
[(15, 17, 20), (12, 14, 15), (16, 17, 20), (17, 18, 21), (18, 19, 21)]
]
# Tn tiene la estructura [ trabajo1, trabajo2, trabajoN ], a su vez cada trabajo tiene la forma [ máquina1, máquina2, máquinaM ], y cada máquina tiene la forma (tiempo_pesimista, tiempo_promedio, tiempo_optimista).

# U_s es el conjunto de máquinas o unidades de la etapa s.
U_s = [[0, 1], [2, 3, 4]]

# L es el total de etapas
L = len(U_s)

# Pn es la ponderación de los números triangulares.
Pn = PT(Tn)

# Parametros para las iteraciones, introducidos como argumentos a este programa

import argparse
parser1 = argparse.ArgumentParser()

parser1.add_argument("N", type = int)
parser1.add_argument("T_0", type = float)
parser1.add_argument("d", type = int)
parser1.add_argument("--debug", action = "store_true")

args1 = parser1.parse_args()

N = args1.N
T_0 = args1.T_0
d = args1.d
debug = args1.debug

# N es un parámetro para el número de iteraciones
# N = 5

# T_0 es un parámetro para crear variación en el algoritmo, diferente de cero
# T_0 = 1.1

# d es un parámetro para la cantidad de trabajos a colocar en pi_d para el algoritmo destruction_reconstruction
# d = 4


# Paso 1

pi_re3, Ta = DNEH_SMR(Tn, U_s, Pn)


# Paso 2

pi_result = pi_re3.copy()
pi_temp = pi_re3.copy()
iter1 = 1


# Paso 3

UT = np.sum([len(U_s[s]) for s in c_range(1, L)])
TT = T_0*(np.sum(Ta))/(10 * N * L)

while (iter1 <= N**2 * L * UT):
iter1 += 1


# Paso 4

pi_temp = local_search(pi_temp, Tn, U_s, Pn)


# Paso 5

if (PT(makespan(pi_temp, Tn, U_s, Pn)) < PT(makespan(pi_re3, Tn, U_s, Pn))):


# Paso 6

pi_re3 = pi_temp.copy()


# Paso 7

if (PT(makespan(pi_temp, Tn, U_s, Pn)) < PT(makespan(pi_result, Tn, U_s, Pn))):


# Paso 8

pi_result = pi_temp.copy()


# Paso 9

else:


# Paso 10

if ( random.random() < math.exp(-(PT(makespan(pi_temp, Tn, U_s, Pn)) - PT(makespan(pi_re3, Tn, U_s, Pn)))/TT) ):


# Paso 11

pi_re3 = pi_temp.copy()


# Paso 12

pi_temp = destruction_reconstruction(pi_temp, d, Tn, U_s, Pn)

if (debug == True):
mk = makespan(pi_re3, Tn, U_s, Pn)
iter1O = "%4d" % (iter1 - 1)
print("Iter:", iter1O, " Secuencia:", pi_re3, " Makespan:", mk, " P:", PT(mk))


# Paso 13

print("\n", pi_result, makespan(pi_result, Tn, U_s, Pn, debug))
Loading

0 comments on commit b8095f1

Please sign in to comment.