add GeneticAlgorithm
This commit is contained in:
parent
81a5a3696c
commit
a0045c341b
|
@ -1 +1 @@
|
|||
Order.py
|
||||
ga_new.py
|
105
Environment.py
105
Environment.py
|
@ -9,6 +9,7 @@ import json
|
|||
from Firm import Firm
|
||||
# import passive agents
|
||||
from Order import Order
|
||||
from ga_new import GeneticAlgorithm
|
||||
|
||||
from fake_api import get_plan_by_pp_id, get_bom_by_prd_id
|
||||
|
||||
|
@ -23,7 +24,7 @@ class FMSEnv(ap.Model):
|
|||
# record data, define below
|
||||
# op_os_n_total_order: int
|
||||
# op_os_n_total_order_delayed: int
|
||||
op_os_all_delay_time: list
|
||||
op_os_all_delay_time: float
|
||||
# op_os_delay_ratio: float
|
||||
# op_is_flt_material_room_left: float
|
||||
# op_is_flt_product_room_left: float
|
||||
|
@ -55,6 +56,7 @@ class FMSEnv(ap.Model):
|
|||
# self.ev_n_order_created = 0
|
||||
self.op_os_n_total_order = 0
|
||||
self.op_os_int_status = 0
|
||||
self.op_os_all_delay_time = 0
|
||||
|
||||
self.running = True
|
||||
self.t = 0
|
||||
|
@ -82,9 +84,10 @@ class FMSEnv(ap.Model):
|
|||
if self.t >= self.int_stop_time:
|
||||
self.running = False
|
||||
self.stop()
|
||||
else:
|
||||
print(f"running the {self.t} step")
|
||||
print("当期延误时长为:{}".format(self.the_firm.the_os.ev_ave_delay_time))
|
||||
# else:
|
||||
#
|
||||
# # print(f"running the {self.t} step")
|
||||
# # print("当期延误时长为:{}".format(self.the_firm.the_os.ev_ave_delay_time))
|
||||
|
||||
# Record data after each simulation
|
||||
def update(self): # ?
|
||||
|
@ -106,10 +109,20 @@ class FMSEnv(ap.Model):
|
|||
|
||||
self.record([att for att in self.__dict__.keys() if att.startswith('op_')]) # ?
|
||||
|
||||
self.op_os_all_delay_time += self.the_firm.the_os.ev_ave_delay_time
|
||||
|
||||
# pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
def GA_run(inventory_bound=None):
|
||||
material = tuple(pd.read_excel("initial_material.xlsx").iloc[:, 0])
|
||||
s = tuple(tuple([i, j]) for i, j in
|
||||
zip(material, inventory_bound[: len(pd.read_excel("initial_material.xlsx").to_numpy())]))
|
||||
S = tuple(tuple([i, j]) for i, j in zip(material, inventory_bound[
|
||||
len(pd.read_excel("initial_material.xlsx").to_numpy()): len(
|
||||
pd.read_excel("initial_material.xlsx").to_numpy()) * 2]))
|
||||
# print(s)
|
||||
# print(S)
|
||||
dct_para = {
|
||||
'time': 300, # 进行总时间数
|
||||
# 'xv_int_max_order': random.randint(30, 50),
|
||||
|
@ -134,8 +147,8 @@ if __name__ == '__main__':
|
|||
# 初始原材料库存 115x2
|
||||
'xv_ary_bom': tuple([tuple(x) for x in pd.read_excel("bom23.xlsx").values]), # bom表
|
||||
'xv_ary_plan': tuple([tuple(x) for x in pd.read_excel("plan.xlsx").values]), # plan表
|
||||
'xv_ary_s': tuple([tuple(x) for x in pd.read_excel("rawmaterial - s.xlsx").values]), # s
|
||||
'xv_ary_S': tuple([tuple(x) for x in pd.read_excel("rawmaterialS.xlsx").values]), # S
|
||||
'xv_ary_s': s, # s
|
||||
'xv_ary_S': S, # S
|
||||
# 应读取遗传算法中随机生成的s,暂写为'1' 创建两个excel分别存储产品和原材料的库存 每个excel中存系统代码和库存
|
||||
# 'xv_flt_initial_cash': 50000.0,
|
||||
# 'dct_status_info': json.dumps({ #需要引入生产状态表
|
||||
|
@ -176,5 +189,83 @@ if __name__ == '__main__':
|
|||
|
||||
exp = ap.Experiment(FMSEnv, sample, iterations=1, record=True)
|
||||
results = exp.run()
|
||||
return results['variables']['FMSEnv']['op_os_all_delay_time'][dct_para['time']] / 2
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# dct_para = {
|
||||
# 'time': 60, # 进行总时间数
|
||||
# # 'xv_int_max_order': random.randint(30, 50),
|
||||
# # 'xv_dlv_product_para': tuple([(30, 100), (30, 50)]),
|
||||
# # 'xv_dlv_product_para': tuple([30,40,30,20]), # 读取生产率 np.read.
|
||||
# # 'xv_int_dlv_period_lam': 8.5,
|
||||
# # 'xv_int_create_order_lam': 2,
|
||||
# # 'xv_ary_price_product': tuple([0.3,0.2,0.5,1]),
|
||||
# # 'xv_ary_cost_material_per': tuple([0.1,0.1,0.2,0.4]),
|
||||
# # 'xv_ary_volume_material': tuple([1.0, 1.5]),
|
||||
# # 'xv_ary_volume_product': tuple([3.0, 5.0]),
|
||||
# # 'xv_array_lead_time': 2, # 读取原材料表格 np.read, 暂时不读 变量代表的含义
|
||||
# # 'xv_int_lead_time_c': 3,
|
||||
# # 'xv_int_lead_time_d': 1,
|
||||
# 'xv_ary_product_id': tuple(pd.read_excel("initial_product.xlsx").iloc[:, 0]), # 产成品id顺序
|
||||
# 'xv_ary_material_id': tuple(pd.read_excel("initial_material.xlsx").iloc[:, 0]), # 原材料id顺序
|
||||
# 'xv_product_num': len(pd.read_excel("initial_product.xlsx").to_numpy()), # 产成品个数
|
||||
# 'xv_material_num': len(pd.read_excel("initial_material.xlsx").to_numpy()), # 原材料个数
|
||||
# 'xv_ary_initial_product_num': tuple([tuple(x) for x in pd.read_excel("initial_product.xlsx").values]),
|
||||
# # 初始产成品库存 23x2
|
||||
# 'xv_ary_initial_material_num': tuple([tuple(x) for x in pd.read_excel("initial_material.xlsx").values]),
|
||||
# # 初始原材料库存 115x2
|
||||
# 'xv_ary_bom': tuple([tuple(x) for x in pd.read_excel("bom23.xlsx").values]), # bom表
|
||||
# 'xv_ary_plan': tuple([tuple(x) for x in pd.read_excel("plan.xlsx").values]), # plan表
|
||||
# 'xv_ary_s': tuple([tuple(x) for x in pd.read_excel("rawmaterial - s.xlsx").values]), # s
|
||||
# 'xv_ary_S': tuple([tuple(x) for x in pd.read_excel("rawmaterialS.xlsx").values]), # S
|
||||
# # 应读取遗传算法中随机生成的s,暂写为'1' 创建两个excel分别存储产品和原材料的库存 每个excel中存系统代码和库存
|
||||
# # 'xv_flt_initial_cash': 50000.0,
|
||||
# # 'dct_status_info': json.dumps({ #需要引入生产状态表
|
||||
# # "0": {"xv_flt_produce_rate": tuple([0.0, 0.0]),
|
||||
# # "xv_ary_mat_material": tuple([0.0, 0.0]),
|
||||
# # "xv_flt_broken_rate": 0,
|
||||
# # "xv_flt_run_cost": 0.0,
|
||||
# # "name": "wait"
|
||||
# # },
|
||||
# # "1": {"xv_flt_produce_rate": tuple([90.0, 0.0]),
|
||||
# # "xv_ary_mat_material": tuple([4.0, 1.0]),
|
||||
# # "xv_flt_broken_rate": 0.03,
|
||||
# # "xv_flt_run_cost": 40.0,
|
||||
# # "name": "produceA"
|
||||
# # },
|
||||
# # "2": {"xv_flt_produce_rate": tuple([0.0, 60.0]),
|
||||
# # "xv_ary_mat_material": tuple([1.5, 5.0]),
|
||||
# # "xv_flt_broken_rate": 0.05,
|
||||
# # "xv_flt_run_cost": 50.0,
|
||||
# # "name": "produceB"
|
||||
# # },
|
||||
# # "3": {"xv_flt_produce_rate": tuple([55.0, 30.0]),
|
||||
# # "xv_ary_mat_material": tuple([2.0, 1.5]),
|
||||
# # "xv_flt_broken_rate": 0.07,
|
||||
# # "xv_flt_run_cost": 60.0,
|
||||
# # "name": "produceAB"
|
||||
# # },
|
||||
# # "-1": {"xv_flt_produce_rate": 0.0,
|
||||
# # "xv_ary_mat_material": tuple([0.0, 0.0]),
|
||||
# # "xv_flt_broken_rate": 0.1,
|
||||
# # "xv_flt_run_cost": 100.0,
|
||||
# # "name": "failed"
|
||||
# # }
|
||||
# # })
|
||||
#
|
||||
# }
|
||||
# sample = ap.Sample(dct_para)
|
||||
#
|
||||
# exp = ap.Experiment(FMSEnv, sample, iterations=1, record=True)
|
||||
# results = exp.run()
|
||||
# print(results['variables']['FMSEnv']['op_os_all_delay_time'])
|
||||
# print(results['variables']['FMSEnv']['op_os_all_delay_time'][dct_para['time']])
|
||||
# results['variables']['FMSEnv'].to_excel(f"simulation-results-{datetime.today().strftime('%Y-%m-%d-%H-%M-%S')}.xlsx",
|
||||
# engine='openpyxl')
|
||||
|
||||
material_num = len(pd.read_excel("initial_material.xlsx").to_numpy()) # 原材料个数
|
||||
GA = GeneticAlgorithm(function=GA_run, dim=material_num * 2, lb=[10 for i in range(material_num * 2)],
|
||||
ub=[100 for i in range(material_num * 2)], int_var=[i for i in range(material_num * 2)])
|
||||
GA.optimize()
|
||||
# print(result1, result2)
|
||||
|
|
BIN
demand23.xlsx
BIN
demand23.xlsx
Binary file not shown.
|
@ -0,0 +1,227 @@
|
|||
import numpy
|
||||
import numpy as np
|
||||
|
||||
|
||||
class GeneticAlgorithm:
|
||||
"""Genetic algorithm.
|
||||
Implementation of the real-valued Genetic algorithm. The mutations are
|
||||
normally distributed perturbations, the selection mechanism is a tournament
|
||||
selection, and the crossover oepration is the standard linear combination
|
||||
taken at a randomly generated cutting point.
|
||||
The total number of evaluations are popsize x ngen
|
||||
:param function: Function that can be used to evaluate the entire
|
||||
population. It needs to take an input of size pop_size x dim and
|
||||
return a numpy.array of size pop_size x 1
|
||||
:type function: Object
|
||||
:param dim: Number of dimensions
|
||||
:type dim: int
|
||||
:param lb: Lower variable bounds, of length dim
|
||||
:type lb: numpy.array
|
||||
:param ub: Lower variable bounds, of length dim
|
||||
:type ub: numpy.array
|
||||
:param int_var: List of indices with the integer valued variables
|
||||
(e.g., [0, 1, 5])
|
||||
:type int_var: list
|
||||
:param pop_size: Population size
|
||||
:type pop_size: int
|
||||
:param num_gen: Number of generations
|
||||
:type num_gen: int
|
||||
:param start: Method for generating the initial population
|
||||
:type start: string
|
||||
:ivar nvariables: Number of variables (dimensions)
|
||||
:ivar nindividuals: population size
|
||||
:ivar lower_boundary: lower bounds for the optimization problem
|
||||
:ivar upper_boundary: upper bounds for the optimization problem
|
||||
:ivar integer_variables: List of variables that are integer valued
|
||||
:ivar start: Method for generating the initial population
|
||||
:ivar sigma: Perturbation radius. Each pertubation is N(0, sigma)
|
||||
:ivar p_mutation: Mutation probability (1/dim)
|
||||
:ivar tournament_size: Size of the tournament (5)
|
||||
:ivar p_cross: Cross-over probability (0.9)
|
||||
:ivar ngenerations: Number of generations
|
||||
:ivar function: Object that can be used to evaluate the objective function
|
||||
"""
|
||||
|
||||
def __init__(self, function, dim, lb, ub, int_var=None, pop_size=20, num_gen=300, start="Random"):
|
||||
|
||||
self.nvariables = dim # column
|
||||
self.nindividuals = pop_size + (pop_size % 2) # Make sure this is even row
|
||||
self.lower_boundary = np.array(lb)
|
||||
self.upper_boundary = np.array(ub)
|
||||
self.integer_variables = []
|
||||
if int_var is not None:
|
||||
self.integer_variables = np.array(int_var)
|
||||
self.start = start
|
||||
self.sigma = 0.2
|
||||
self.p_mutation = 1.0 / dim
|
||||
self.tournament_size = 5
|
||||
self.p_cross = 0.9
|
||||
self.ngenerations = num_gen
|
||||
self.function = function
|
||||
|
||||
def optimize(self):
|
||||
"""Method used to run the Genetic algorithm
|
||||
:return: Returns the best individual and its function value
|
||||
:rtype: numpy.array, float
|
||||
"""
|
||||
# Initialize population
|
||||
if isinstance(self.start, np.ndarray):
|
||||
if self.start.shape[0] != self.nindividuals or self.start.shape[1] != self.nvariables:
|
||||
raise ValueError("Initial population has incorrect size")
|
||||
if any(np.min(self.start, axis=0) < self.lower_boundary) or any(
|
||||
np.max(self.start, axis=0) > self.upper_boundary
|
||||
):
|
||||
raise ValueError("Initial population is outside the domain", self.lower_boundary, self.upper_boundary,
|
||||
self.start)
|
||||
population = self.start
|
||||
elif self.start == "SLHD":
|
||||
from pySOT.experimental_design import SymmetricLatinHypercube
|
||||
|
||||
exp_des = SymmetricLatinHypercube(self.nvariables, self.nindividuals)
|
||||
population = self.lower_boundary + exp_des.generate_points() * (self.upper_boundary - self.lower_boundary)
|
||||
elif self.start == "LHD":
|
||||
from pySOT.experimental_design import LatinHypercube
|
||||
|
||||
exp_des = LatinHypercube(self.nvariables, self.nindividuals)
|
||||
population = self.lower_boundary + exp_des.generate_points() * (self.upper_boundary - self.lower_boundary)
|
||||
elif self.start == "Random":
|
||||
population = self.lower_boundary + np.random.rand(self.nindividuals, self.nvariables) * (
|
||||
self.upper_boundary - self.lower_boundary
|
||||
)
|
||||
else:
|
||||
raise ValueError("Unknown argument for initial population")
|
||||
|
||||
new_population = []
|
||||
# Round positions
|
||||
if len(self.integer_variables) > 0: # 对特定列进行操作
|
||||
new_population = np.copy(population)
|
||||
population[:, self.integer_variables] = np.round(population[:, self.integer_variables])
|
||||
for i in self.integer_variables:
|
||||
ind = np.where(population[:, i] < self.lower_boundary[i])
|
||||
population[ind, i] += 1
|
||||
ind = np.where(population[:, i] > self.upper_boundary[i])
|
||||
population[ind, i] -= 1
|
||||
|
||||
# Evaluate all individuals
|
||||
# function_values = self.function(population) we cannot compute in this way to ensure x is one-dim in policy
|
||||
n_row, n_dim = population.shape
|
||||
function_values = []
|
||||
for r in range(n_row):
|
||||
function_values.append(self.function(population[r, :]))
|
||||
function_values = np.array(function_values)
|
||||
|
||||
if len(function_values.shape) == 2:
|
||||
function_values = np.squeeze(np.asarray(function_values))
|
||||
|
||||
# Save the best individual
|
||||
ind = np.argmin(function_values)
|
||||
best_individual = np.copy(population[ind, :]) # 找到最优个体
|
||||
best_value = function_values[ind]
|
||||
|
||||
if len(self.integer_variables) > 0:
|
||||
population = new_population
|
||||
|
||||
# Main loop
|
||||
for _ in range(self.ngenerations):
|
||||
print('------------------------------')
|
||||
print("当前为第{}代".format(_))
|
||||
print("最优个体为:{}".format(best_individual))
|
||||
print("最优值为:{}".format(best_value))
|
||||
print("------------------------------")
|
||||
# Do tournament selection to select the parents
|
||||
competitors = np.random.randint(0, self.nindividuals, (self.nindividuals, self.tournament_size))
|
||||
ind = np.argmin(function_values[competitors], axis=1)
|
||||
winner_indices = np.zeros(self.nindividuals, dtype=int)
|
||||
for i in range(self.tournament_size): # This loop is short
|
||||
winner_indices[np.where(ind == i)] = competitors[np.where(ind == i), i]
|
||||
|
||||
parent1 = population[winner_indices[0: self.nindividuals // 2], :]
|
||||
parent2 = population[winner_indices[self.nindividuals // 2: self.nindividuals], :]
|
||||
|
||||
# Averaging Crossover
|
||||
cross = np.where(np.random.rand(self.nindividuals // 2) < self.p_cross)[0]
|
||||
nn = len(cross) # Number of crossovers
|
||||
alpha = np.random.rand(nn, 1)
|
||||
|
||||
# Create the new chromosomes
|
||||
parent1_new = np.multiply(alpha, parent1[cross, :]) + np.multiply(1 - alpha, parent2[cross, :])
|
||||
parent2_new = np.multiply(alpha, parent2[cross, :]) + np.multiply(1 - alpha, parent1[cross, :])
|
||||
parent1[cross, :] = parent1_new
|
||||
parent2[cross, :] = parent2_new
|
||||
population = np.concatenate((parent1, parent2))
|
||||
|
||||
# Apply mutation
|
||||
scale_factors = self.sigma * (self.upper_boundary - self.lower_boundary) # Scale
|
||||
perturbation = np.random.randn(self.nindividuals, self.nvariables) # Generate perturbations
|
||||
perturbation = np.multiply(perturbation, scale_factors) # Scale accordingly
|
||||
perturbation = np.multiply(
|
||||
perturbation, (np.random.rand(self.nindividuals, self.nvariables) < self.p_mutation)
|
||||
)
|
||||
|
||||
perturbation = round_vars(perturbation, self.integer_variables, self.lower_boundary, self.upper_boundary)
|
||||
population = round_vars(population, self.integer_variables, self.lower_boundary, self.upper_boundary)
|
||||
|
||||
population += perturbation # Add perturbation
|
||||
population = np.maximum(np.reshape(self.lower_boundary, (1, self.nvariables)), population)
|
||||
population = np.minimum(np.reshape(self.upper_boundary, (1, self.nvariables)), population)
|
||||
|
||||
# Round chromosomes
|
||||
new_population = []
|
||||
if len(self.integer_variables) > 0:
|
||||
new_population = np.copy(population)
|
||||
population = round_vars(population, self.integer_variables, self.lower_boundary, self.upper_boundary)
|
||||
|
||||
# Keep the best individual
|
||||
population[0, :] = best_individual
|
||||
|
||||
# Evaluate all individuals
|
||||
# function_values = self.function(population) we cannot compute in this way to ensure x is one-dim in policy
|
||||
n_row, n_dim = population.shape
|
||||
function_values = []
|
||||
for r in range(n_row):
|
||||
function_values.append(self.function(population[r, :]))
|
||||
function_values = np.array(function_values)
|
||||
|
||||
if len(function_values.shape) == 2:
|
||||
function_values = np.squeeze(np.asarray(function_values))
|
||||
|
||||
# Save the best individual
|
||||
ind = np.argmin(function_values)
|
||||
best_individual = np.copy(population[ind, :])
|
||||
best_value = function_values[ind]
|
||||
|
||||
# Use the positions that are not rounded
|
||||
if len(self.integer_variables) > 0:
|
||||
population = new_population
|
||||
|
||||
# return best_individual, best_value
|
||||
|
||||
|
||||
def round_vars(x: np.ndarray, int_var, lb, ub):
|
||||
"""Round integer variables to closest integer in the domain.
|
||||
:param x: Set of points, of size npts x dim
|
||||
:type x: numpy.array
|
||||
:param int_var: Set of indices of integer variables
|
||||
:type int_var: numpy.array
|
||||
:param lb: Lower bounds, of size 1 x dim
|
||||
:type lb: numpy.array
|
||||
:param ub: Upper bounds, of size 1 x dim
|
||||
:type ub: numpy.array
|
||||
:return: The set of points with the integer variables
|
||||
rounded to the closest integer in the domain
|
||||
:rtype: numpy.array
|
||||
"""
|
||||
# Make sure we don't violate the bound constraints
|
||||
for i in int_var:
|
||||
ind = np.where(x[:, i] < lb[i])
|
||||
x[ind, i] = lb[i]
|
||||
ind = np.where(x[:, i] > ub[i])
|
||||
x[ind, i] = ub[i]
|
||||
|
||||
if len(int_var) > 0:
|
||||
# Round the original ranged integer variables
|
||||
x[:, int_var] = np.round(x[:, int_var])
|
||||
x = x.astype(numpy.int32, copy=True)
|
||||
else:
|
||||
x = x.astype(numpy.float64, copy=True)
|
||||
return x
|
Loading…
Reference in New Issue