static void Main(string[] args) { if (args.Length < 1) { Console.Out.WriteLine("Usage: lpmethod_cs filename"); return; } try { // Read model GRBEnv env = new GRBEnv(); GRBModel model = new GRBModel(env, args[0]); GRBEnv menv = model.GetEnv(); // Solve the model with different values of Method int bestMethod = -1; double bestTime = menv.Get(GRB.DoubleParam.TimeLimit); for (int i = 0; i <= 2; ++i) { model.Reset(); menv.Set(GRB.IntParam.Method, i); model.Optimize(); if (model.Get(GRB.IntAttr.Status) == GRB.Status.OPTIMAL) { bestTime = model.Get(GRB.DoubleAttr.Runtime); bestMethod = i; // Reduce the TimeLimit parameter to save time // with other methods menv.Set(GRB.DoubleParam.TimeLimit, bestTime); } } // Report which method was fastest if (bestMethod == -1) { Console.WriteLine("Unable to solve this model"); } else { Console.WriteLine("Solved in " + bestTime + " seconds with Method: " + bestMethod); } // Dispose of model and env model.Dispose(); env.Dispose(); } catch (GRBException e) { Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message); } }
/// <summary> /// Configures a <see cref="GurobiRunner" /> using the given parameters. /// </summary> /// <param name="parameters">The parameters to configure the Gurobi run with.</param> /// <returns>The configured <see cref="GurobiRunner" />.</returns> public GurobiRunner ConfigureTargetAlgorithm(Dictionary <string, IAllele> parameters) { // Create a new gurobi environment. var gurobiEnvironment = new GRBEnv(); // Add all simple, non-artificial parameters to it. foreach (var param in parameters) { gurobiEnvironment.Set(param.Key, param.Value.ToString()); } // Log which settings will be used for the next Gurobi run(s). LoggingHelper.WriteLine(VerbosityLevel.Info, string.Join(", ", parameters.Select(parameter => $"{parameter.Key}: {parameter.Value}"))); gurobiEnvironment.Threads = this.GurobiSettings.ThreadCount; gurobiEnvironment.NodefileStart = this.GurobiSettings.NodefileStartSizeGigabyte; if (!Directory.Exists(this.GurobiSettings.NodefileDirectory.FullName)) { this.GurobiSettings.NodefileDirectory.Create(); } gurobiEnvironment.NodefileDir = this.GurobiSettings.NodefileDirectory.FullName; // Return the new gurobi runner. return(new GurobiRunner(gurobiEnvironment, this.GurobiSettings)); }
void BuildModel_SubProblem1() { _env = new GRBEnv("SolutionLog.log"); _env.Set(GRB.DoubleParam.MIPGap, 0.0); _env.Set(GRB.DoubleParam.TimeLimit, 500); _grbModel = new GRBModel(_env); //决策变量 foreach (Node n in Data.NodeSet) { n.Result_IsServerLoacationSelected = _grbModel.AddVar(0.0, 1.0, 0.0, GRB.BINARY, "x_" + n.ID); } _grbModel.Update(); //目标函数 GRBLinExpr expr1 = 0; foreach (Node n in Data.NodeSet) { expr1 += n.Result_IsServerLoacationSelected * (Data.ServerInstalationFee - multiplier_p[n] * Data.M); } _grbModel.SetObjective(expr1, GRB.MINIMIZE); }
private GRBVar[,] _x; // starting time of job j on machine a #endregion Fields #region Constructors public GurobiJspModel(ProblemInstance prob, string name, int tmlim_min) { _n = prob.NumJobs; _m = prob.NumMachines; Info = "Starting gurobi optimisation"; _fileName = String.Format("jssp.{0}.log", name); // Model try { _env = new GRBEnv(_fileName); if (tmlim_min > 0) _env.Set(GRB.DoubleParam.TimeLimit, tmlim_min*60); // time limit is set to seconds! _env.Set(GRB.IntParam.LogToConsole, 0); _model = new GRBModel(_env); _model.Set(GRB.StringAttr.ModelName, "jsp"); } catch (GRBException e) { Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message); } DecisionVariables(); ProcessingOrder(prob.Procs, prob.Sigma); DisjunctiveCondition(prob.Procs); Objective(prob.Procs, prob.Sigma); //TrueOptimumDecVars = Optimise(out TrueOptimum); //if (TrueOptimum > 0) // Objective cutoff // _model.GetEnv().Set(GRB.DoubleParam.Cutoff, TrueOptimum + 0.5); /* Indicates that you aren't interested in solutions whose objective values * are worse than the specified value. If the objective value for the optimal * solution is better than the specified cutoff, the solver will return the * optimal solution. Otherwise, it will terminate with a CUTOFF status. */ // seems to be only for LP relaxation, not MIP objective }
public void mip1_cs() { try { // Create an empty environment, set options and start GRBEnv env = new GRBEnv(true); env.Set("LogFile", "mip1.log"); env.Start(); // Create empty model GRBModel model = new GRBModel(env); // Create variables GRBVar x = model.AddVar(0.0, 1.0, 1.0, GRB.BINARY, "x"); GRBVar y = model.AddVar(0.0, 1.0, 1.0, GRB.BINARY, "y"); GRBVar z = model.AddVar(0.0, 1.0, 1.0, GRB.BINARY, "z"); // Set objective: maximize x + y + 2 z model.SetObjective(x + y + 2 * z, GRB.MAXIMIZE); // Add constraint: x + 2 y + 3 z <= 4 model.AddConstr(x + 2 * y + 3 * z <= 4.0, "c0"); // Add constraint: x + y >= 1 model.AddConstr(x + y >= 1.0, "c1"); // Optimize model model.Optimize(); Console.WriteLine(x.VarName + " " + x.X); Console.WriteLine(y.VarName + " " + y.X); Console.WriteLine(z.VarName + " " + z.X); Console.WriteLine("Obj: " + model.ObjVal); // Dispose of model and env model.Dispose(); env.Dispose(); } catch (GRBException e) { Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message); } }
public void ILP_Janssen() { try { // Number of plants and warehouses int nJobs = 750; int nMachines = 12; double[,] processingTime = new double[nMachines, nJobs]; for (int i = 0; i < nMachines; ++i) { for (int j = 0; j < nJobs; ++j) { processingTime[i, j] = new Random().Next(1000, 1500); } } // Number of plants and warehouses //int nJobs = 20; //int nMachines = 3; //double[,] processingTime = // new double[nMachines, nJobs]; //Random Rand = new Random(1); //for (int i = 0; i < nMachines; ++i) //{ // if (i==0) // { // for (int j = 0; j < nJobs; ++j) // { // processingTime[i, j] = Rand.Next(100, 201); // } // } // else if (i==1) // { // for (int j = 0; j < nJobs; ++j) // { // processingTime[i, j] = Rand.Next(100, 201); // } // } // else if (i == 2) // { // for (int j = 0; j < nJobs; ++j) // { // processingTime[i, j] = Rand.Next(100, 201); // } // } //} // Create an empty environment, set options and start GRBEnv env = new GRBEnv(true); env.Set("LogFile", "mip1.log"); env.Start(); // Create empty model GRBModel model = new GRBModel(env); // Decision Variable GRBVar[,,] x = new GRBVar[nMachines, nJobs, nJobs]; for (int i = 0; i < nMachines; ++i) { for (int j = 0; j < nJobs; ++j) { for (int k = 0; k < nJobs; ++k) { x[i, j, k] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, "x"); } } } //Set objective: GRBLinExpr exp1 = 0.0; for (int i = 0; i < nMachines; ++i) { for (int j = 0; j < nJobs; ++j) { for (int k = 0; k < nJobs; ++k) { GRBLinExpr exp2 = (k + 1) * processingTime[i, j] * x[i, j, k]; exp1.Add(exp2); } } } model.SetObjective(exp1, GRB.MINIMIZE); // Constraints for (int j = 0; j < nJobs; ++j) { GRBLinExpr exp3 = 0.0; for (int i = 0; i < nMachines; ++i) { for (int k = 0; k < nJobs; ++k) { exp3.AddTerm(1.0, x[i, j, k]); } } model.AddConstr(exp3 == 1.0, "c1"); } for (int i = 0; i < nMachines; ++i) { for (int k = 0; k < nJobs; ++k) { GRBLinExpr exp4 = 0.0; for (int j = 0; j < nJobs; ++j) { exp4.AddTerm(1.0, x[i, j, k]); } model.AddConstr(exp4 <= 1.0, "c2"); } } // Solve model.Optimize(); for (int i = 0; i < nMachines; ++i) { double totalMachineTime = 0.0; for (int j = 0; j < nJobs; ++j) { for (int k = 0; k < nJobs; ++k) { if (x[i, j, k].X == 1.0) { totalMachineTime += processingTime[i, j]; Console.WriteLine($"({i},{j},{k})" + "ProcessingTime: " + processingTime[i, j]); } } } Console.WriteLine(totalMachineTime); } Console.WriteLine("Obj: " + model.ObjVal); // Dispose of model and env model.Dispose(); env.Dispose(); } catch (GRBException e) { Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message); } }
public MasterModelOutputs buildModel( InputData inputData, int nCUT, MasterModelParameters masterModelParameters, string outputFolder ) { try { // Create an empty environment, set options and start GRBEnv env = new GRBEnv(true); env.Set("LogFile", "LogFileGurobiModel.log"); env.Start(); int T = 4; List <T_param> Tlist2 = inputData.TList.FindAll(tl => tl.T >= 2); List <T_param> Tlist3 = inputData.TList.FindAll(tl => tl.T >= 3); // Create empty Gurobi model GRBModel gModel = new GRBModel(env); // Create an empty linear expression object. //Parameters and variables will be added and then used to add model components //(Objective Function and Constraints). GRBLinExpr expr1 = 0.0; GRBLinExpr expr2 = 0.0; // Crear la variables GRBVar[] Make1 = gModel.AddVars(inputData.ProdList.Count, GRB.CONTINUOUS); GRBVar[] Inv1 = gModel.AddVars(inputData.ProdList.Count, GRB.CONTINUOUS); GRBVar[] Sell1 = gModel.AddVars(inputData.ProdList.Count, GRB.CONTINUOUS); //MAKE1 VARNAME inputData.ProdList.ForEach(pl => { int ixP = inputData.ProdList.IndexOf(pl); Make1[ixP].VarName = "MAKE1_Prod: " + pl.PROD; }); //INV1 VARNAME inputData.ProdList.ForEach(pl => { int ixP = inputData.ProdList.IndexOf(pl); Inv1[ixP].VarName = "INV1_Prod: " + pl.PROD; }); //MAKE1 VARNAME inputData.ProdList.ForEach(pl => { int ixP = inputData.ProdList.IndexOf(pl); Sell1[ixP].VarName = "SELL1_Prod: " + pl.PROD; }); //MAKE1 => 0 (constrain) //GRB.CONTINUOUS for (int a = 0; a < Make1.Length; a++) { expr1.Clear(); expr1.AddTerm(1, Make1[a]); gModel.AddConstr(expr1, GRB.GREATER_EQUAL, 0, ">=0"); } //INV1 => 0 (constrain) for (int a = 0; a < Inv1.Length; a++) { expr1.Clear(); expr1.AddTerm(1, Inv1[a]); gModel.AddConstr(expr1, GRB.GREATER_EQUAL, 0, ">=0"); } //SELL1 => 0 (constrain) for (int a = 0; a < Sell1.Length; a++) { expr1.Clear(); expr1.AddTerm(1, Sell1[a]); gModel.AddConstr(expr1, GRB.GREATER_EQUAL, 0, ">=0"); } //SELL1 <= market[p,1] expr1.Clear(); inputData.ProdList.ForEach(pl => { double market = inputData .MarketList .Find(ml => ml.PROD.Equals(pl.PROD) && ml.T.Equals(1)).MARKET; int ixP = inputData.ProdList.IndexOf(pl); expr1.Clear(); expr1.AddTerm(1, Sell1[ixP]); gModel.AddConstr(expr1, GRB.LESS_EQUAL, market, "<=Market"); }); GRBVar Min_Stage2_Profit = gModel.AddVar(double.MinValue, double.MaxValue, masterModelParameters.Min_Stage2_Profit, GRB.CONTINUOUS, "Min_Stage2_Profit"); //Funcion objetivo //maximize Expected_Profit: //sum { s in SCEN} //prob[s] * //sum { p in PROD} (revenue[p, 1, s] * Sell1[p] - //prodcost[p] * Make1[p] - invcost[p] * Inv1[p]) + //Min_Stage2_Profit; expr1.Clear(); inputData.ScenList.ForEach(sl => { double prob = inputData.ProbList.Find(x => x.SCEN.Equals(sl.SCEN)).PROB; inputData.ProdList.ForEach(pl => { double revenue = inputData .RevenueList .Find(rl => rl.PROD.Equals(pl.PROD) && rl.T.Equals(1) && rl.SCEN.Equals(sl.SCEN)) .REVENUE; int ixP = inputData.ProdList.IndexOf(pl); double invcost = inputData.InvCostList.Find(inv => inv.PROD.Equals(pl.PROD)).INVCOST; double prodcost = inputData.ProdCostList.Find(inv => inv.PROD.Equals(pl.PROD)).PRODCOST; expr1.AddTerm(prob * revenue, Sell1[ixP]); expr1.AddTerm(-prob * prodcost, Make1[ixP]); expr1.AddTerm(-prob * invcost, Inv1[ixP]); expr1.AddTerm(1, Min_Stage2_Profit); }); }); //Insertar funcion objetivo gModel.SetObjective(expr1, GRB.MAXIMIZE); //subj to Cut_Defn {k in 1..nCUT}: KE ESTA PASANDO // Min_Stage2_Profit <= // sum { t in 2..T, s in SCEN} // time_price[t, s, k] * avail[t] + // sum { p in PROD, s in SCEN} // bal2_price[p, s, k] * (-Inv1[p]) + // sum { p in PROD, t in 2..T, s in SCEN} // sell_lim_price[p, t, s, k] * market[p, t]; expr1.Clear(); double sumPriceAvail = 0; double sumPriceMarket = 0; expr1.AddTerm(1, Min_Stage2_Profit); for (int k = 1; k <= nCUT; k++) { // sum { t in 2..T, s in SCEN} // time_price[t, s, k] * avail[t] + Tlist2.ForEach(tl => { inputData.ScenList.ForEach(sl => { double timePrice = masterModelParameters.timePriceList.Find(x => x.T.Equals(tl.T) && x.SCEN.Equals(sl.SCEN) && x.nCUT.Equals(k)).TIMEPRICE; double avail = inputData.AvailList.Find(al => al.T.Equals(tl.T)).AVAIL; sumPriceAvail += timePrice * avail; }); }); // sum { p in PROD, t in 2..T, s in SCEN} // sell_lim_price[p, t, s, k] * market[p, t]; inputData.ProdList.ForEach(pl => { Tlist2.ForEach(tl => { inputData.ScenList.ForEach(sl => { double sellLimPrice = masterModelParameters.sellLimPriceList.Find(slpl => slpl.PROD.Equals(pl.PROD) && slpl.T.Equals(tl.T) && slpl.SCEN.Equals(sl.SCEN) && slpl.nCUT.Equals(k)).SELLLIMPRICE; double market = inputData.MarketList.Find(ml => ml.PROD.Equals(pl.PROD) && ml.T.Equals(tl.T)).MARKET; sumPriceMarket += sellLimPrice * market; }); }); }); // sum { p in PROD, s in SCEN} // bal2_price[p, s, k] * (-Inv1[p]) + inputData.ProdList.ForEach(pl => { inputData.ScenList.ForEach(sl => { double bal2Price = masterModelParameters.balance2PriceList.Find(bp => bp.PROD.Equals(pl.PROD) && bp.SCEN.Equals(sl.SCEN) && bp.nCUT.Equals(k)).BALANCE2PRICE; int ixP = inputData.ProdList.IndexOf(pl); expr1.AddTerm(bal2Price, Inv1[ixP]); }); }); } gModel.AddConstr(expr1, GRB.LESS_EQUAL, sumPriceAvail + sumPriceMarket, "Cut_Defn"); //subject to Time1: //sum { p in PROD} (1 / rate[p]) * Make1[p] <= avail[1]; expr1.Clear(); inputData.ProdList.ForEach(pl => { double rate = 1 / inputData.RateList.Find(rl => rl.PROD.Equals(pl.PROD)).RATE; double avail = inputData.AvailList.Find(al => al.T.Equals(1)).AVAIL; double rateAvail = avail / rate; int ixP = inputData.ProdList.IndexOf(pl); gModel.AddConstr(Make1[ixP], GRB.GREATER_EQUAL, rateAvail, "TIME1"); }); //subject to Balance1 { p in PROD}: //Make1[p] + inv0[p] = Sell1[p] + Inv1[p]; expr1.Clear(); inputData.ProdList.ForEach(pl => { expr1.Clear(); int ixP = inputData.ProdList.IndexOf(pl); expr1.AddTerm(1, Sell1[ixP]); expr1.AddTerm(1, Inv1[ixP]); expr1.AddTerm(-1, Make1[ixP]); double inv0 = inputData.Inv0List.Find(il => il.PROD.Equals(pl.PROD)).INV0; gModel.AddConstr(inv0, GRB.EQUAL, expr1, "Balance1"); }); //carpeta donde se expande el modelo master gModel.Write(outputFolder + "Master_Model.lp"); // RESOLVER EL MODELO try { Console.WriteLine("Solving the master model with gurobi.."); gModel.Optimize(); if (gModel.Status == 2) { for (int m = 0; m < Make1.Length; m++) { Console.WriteLine("Make1 Var: " + Make1[m].VarName + " = " + Make1[m].X); } for (int m = 0; m < Inv1.Length; m++) { Console.WriteLine("Inv1 Var: " + Inv1[m].VarName + " = " + Inv1[m].X); } for (int m = 0; m < Sell1.Length; m++) { Console.WriteLine("Sell Var: " + Sell1[m].VarName + " = " + Sell1[m].X); } } gModel.Dispose(); env.Dispose(); return(new MasterModelOutputs() { make1 = Make1, sell1 = Sell1, inv1 = Inv1, expectedProfit = gModel.ObjVal, gModel = gModel }); } catch { Console.WriteLine("ERROR SOLVING THE MODEL"); } gModel.Dispose(); env.Dispose(); } catch (GRBException ex) { Console.WriteLine("Error code: " + ex.ErrorCode + ". " + ex.Message); } return(new MasterModelOutputs()); }
public Dictionary <string, string> Solve(bool tune, bool debug, string tuneOutputFile = null, string paramFile = null) { var times = new Dictionary <string, string>(); var totalTime = Utils.TimeAction(() => { try { // Create an empty environment, set options and start GRBEnv env = new GRBEnv(true); // Use gurobi's parameter reader to load the best file if (paramFile != null) { env.ReadParams(paramFile); } if (!debug) { env.Set(GRB.IntParam.OutputFlag, 0); } env.Start(); // Create empty model GRBModel model = new GRBModel(env); (var grbSeated, var addDecisionVariableTime) = Utils.TimeFunction(() => AddSeatedBinaryVariables(model), "Add Decision Variables"); var addConstraintsTime = Utils.TimeAction(() => AddContraints(model, grbSeated), "Add Constraints"); var addObjectiveTime = Utils.TimeAction(() => AddObjective(model, grbSeated), "Add Objective"); var optimizeTime = Utils.TimeAction(() => model.Optimize(), "Optimizing"); if (tune) { model.Tune(); model.GetTuneResult(0); model.Write(tuneOutputFile); } SeatGroups(grbSeated); // Dispose of model and env model.Dispose(); env.Dispose(); times.Add("Add Decision Variables", addDecisionVariableTime); times.Add("Add Constraints", addConstraintsTime); times.Add("Add Objective", addObjectiveTime); times.Add("Optimizing", optimizeTime); } catch (GRBException e) { Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message); throw e; } }, "Total"); times.Add("Total", totalTime); return(times); }
public override Schedule Generate(Scenario scenario, FilledBaseline baseline = null) { var opportunities = scenario.Projects.Where(p => p is Opportunity).ToArray(); var batches = scenario.Projects.SelectMany(p => p.Batches).ToArray(); var batchesBothLines = batches.Where(b => b.Compatibility == Batch.LineCompatibility.Both).ToArray(); var batchesLine1 = batches.Where(b => b.Compatibility == Batch.LineCompatibility.Line1).ToArray(); var batchesLine2 = batches.Where(b => b.Compatibility == Batch.LineCompatibility.Line2).ToArray(); int bBc = batchesBothLines.Count(); var earliestDate = scenario.Projects.Min(p => p.DeliveryDate); var lastDate = scenario.Projects.Max(p => p.DeliveryDate.AddDays(7 * p.Batches.Count())).AddDays(7 * 3); // add an additional buffer of 3 weeks. double maxDays = (lastDate - earliestDate).Days; var maxWeek = maxDays / 7 + 2; const int weeksBuffer = 3; const double numericScale = 100d; const double delayPenaltyMultiplier = 0.01; // 1% of revenue per week const double interestPenaltyMultiplier = (0.045 / 52.14); // 4.5% / 52.14 * (revenue – margin) var env = new GRBEnv(); env.Set(GRB.IntParam.UpdateMode, 1); env.Set(GRB.IntParam.LogToConsole, 1); //env.Set(GRB.IntParam.ScaleFlag, 2); //env.Set(GRB.IntParam.Presolve, 0); //env.Set(GRB.DoubleParam.TimeLimit, _config.TimeLimit.Value); //env.Set(GRB.DoubleParam.MIPGap, 0.02); var m = new GRBModel(env); // decide: which batch is when, on which line // [batch, time] var varBatchTimeLine1 = m.AddVars(bBc + batchesLine1.Count(), bBc + batchesLine1.Count(), 0, 1, GRB.BINARY, "varBatchTimeLine1"); var varBatchTimeLine2 = m.AddVars(bBc + batchesLine2.Count(), bBc + batchesLine2.Count(), 0, 1, GRB.BINARY, "varBatchTimeLine2"); // what are the time differences between times var varBatchTimeDiffsLine1 = m.AddVars(bBc + batchesLine1.Count() - 1, 0, maxDays, GRB.CONTINUOUS, "varBatchTimeDiffsLine1"); var varBatchTimeDiffsLine2 = m.AddVars(bBc + batchesLine2.Count() - 1, 0, maxDays, GRB.CONTINUOUS, "varBatchTimeDiffsLine2"); var varLineDecision = m.AddVars(bBc, 0, 1, GRB.BINARY, "varLineDecision"); // 0 = Line1, 1 = Line2 // make stupid solution: // assign fixed projects, set the rest as unused m.Update(); for (int l = 0; l < 2; l++) { var line = l == 0 ? varBatchTimeLine1 : varBatchTimeLine2; var diffs = l == 0 ? varBatchTimeDiffsLine1 : varBatchTimeDiffsLine2; for (int i = 0; i < diffs.GetLength(0); i++) { diffs[i].Set(GRB.DoubleAttr.Start, 0); } //m.AddConstr(diffs[i] == 0, "initial solution diffs"); int lineT = 0; for (int bi = bBc; bi < line.GetLength(0); bi++) { var batch = (l == 0 ? batchesLine1 : batchesLine2)[bi - bBc]; bool isFixedProject = scenario.Projects.First(p => p.Batches.Contains(batch)) is FixedProject; if (isFixedProject) { line[bi, lineT].Set(GRB.DoubleAttr.Start, 1); //m.AddConstr(line[bi, lineT] == 1, "assign batches of project in succession"); for (int j = 0; j < line.GetLength(1); j++) { if (j != lineT) { line[bi, j].Set(GRB.DoubleAttr.Start, 0); } } //m.AddConstr(line[bi, j] == 0, "initial solution"); lineT++; } else { for (int j = 0; j < line.GetLength(1); j++) { line[bi, j].Set(GRB.DoubleAttr.Start, 0); } //m.AddConstr(line[bi, j] == 0, "initial solution"); } } // make zeros also for both line batches for (int i = 0; i < bBc; i++) { for (int j = 0; j < line.GetLength(1); j++) { line[i, j].Set(GRB.DoubleAttr.Start, 0); } } //m.AddConstr(line[i, j] == 0, "initial solution"); } // line constraints: // assign batch only once (constraint for single lines then both lines) // if it's a fixed project though, then it must be allocated for (int l = 0; l < 2; l++) { var line = l == 0 ? varBatchTimeLine1 : varBatchTimeLine2; for (int bi = bBc; bi < line.GetLength(0); bi++) { var batchTotal = new GRBLinExpr(); for (int t = 0; t < line.GetLength(1); t++) { batchTotal += line[bi, t]; } var batch = (l == 0 ? batchesLine1 : batchesLine2)[bi - bBc]; bool isFixedProject = scenario.Projects.First(p => p.Batches.Contains(batch)) is FixedProject; if (!isFixedProject) { m.AddConstr(batchTotal <= 1, "assign batch only once"); } else { m.AddConstr(batchTotal == 1, "assign batch exactly once"); } } } for (int bi = 0; bi < bBc; bi++) { var batchTotal = new GRBLinExpr(); for (int t = 0; t < varBatchTimeLine1.GetLength(1); t++) { batchTotal += varBatchTimeLine1[bi, t]; } for (int t = 0; t < varBatchTimeLine2.GetLength(1); t++) { batchTotal += varBatchTimeLine2[bi, t]; } var batch = batchesBothLines[bi]; bool isFixedProject = scenario.Projects.First(p => p.Batches.Contains(batch)) is FixedProject; if (!isFixedProject) { m.AddConstr(batchTotal <= 1, "assign batch only once"); } else { m.AddConstr(batchTotal == 1, "assign batch exactly once"); } } // a time slot can only be used once on each line for (int l = 0; l < 2; l++) { var line = l == 0 ? varBatchTimeLine1 : varBatchTimeLine2; for (int t = 0; t < line.GetLength(1); t++) { var timeTotal = new GRBLinExpr(); for (int bi = 0; bi < line.GetLength(0); bi++) { timeTotal += line[bi, t]; } m.AddConstr(timeTotal <= 1, "assign time slot only once on line " + (l + 1).ToString()); } } // for all batches which aren't assigned to a line yet, limit the allocation of yet unassigned batches of a project to one line // TODO: If project has e.g. line1 and both lines, limit both lines to line 1? for (int pi = 0; pi < scenario.Projects.Count(); pi++) { var p = scenario.Projects[pi]; if (!p.Batches.Any(b => b.Compatibility == Batch.LineCompatibility.Both)) { continue; } int lineRestriction = -1; if (p.Batches.Any(b => b.Compatibility != Batch.LineCompatibility.Both)) { lineRestriction = p.Batches.First(b => b.Compatibility != Batch.LineCompatibility.Both).Compatibility == Batch.LineCompatibility.Line1 ? 0 : 1; } int i_prev = -1; for (int j = 0; j < p.Batches.Count(); j++) { if (p.Batches[j].Compatibility != Batch.LineCompatibility.Both) { continue; } var i = batchesBothLines.IndexOf(p.Batches[j]); if (lineRestriction == -1) { if (i_prev != -1) { m.AddConstr(varLineDecision[i] == varLineDecision[i_prev], "lineDecisionRestrictionAllSame"); } } else { // if there are other batches on this project which are already on a specific line, limit to the same line m.AddConstr(varLineDecision[i] == lineRestriction, "lineDecisionRestrictionSpecific"); } i_prev = i; } } // for each project, either all or no batches must be assigned var totalBatchesOfProject = new Dictionary <Project, List <GRBLinExpr> >(); foreach (var p in scenario.Projects) { var allBatches = new List <GRBLinExpr>(); // gather the total of all batches GRBLinExpr previousBatchTotal = null; for (int bi = 0; bi < p.Batches.Count(); bi++) { var b = p.Batches[bi]; var batchTotal = new GRBLinExpr(); if (b.Compatibility == Batch.LineCompatibility.Line1) { var bIndex = bBc + batchesLine1.IndexOf(b); for (int ti = 0; ti < varBatchTimeLine1.GetLength(1); ti++) { batchTotal += varBatchTimeLine1[bIndex, ti]; } } else if (b.Compatibility == Batch.LineCompatibility.Line2) { var bIndex = bBc + batchesLine2.IndexOf(b); for (int ti = 0; ti < varBatchTimeLine2.GetLength(1); ti++) { batchTotal += varBatchTimeLine2[bIndex, ti]; } } else { var bIndex = batchesBothLines.IndexOf(b); for (int t = 0; t < varBatchTimeLine1.GetLength(1); t++) { batchTotal += varBatchTimeLine1[bIndex, t]; } for (int t = 0; t < varBatchTimeLine2.GetLength(1); t++) { batchTotal += varBatchTimeLine2[bIndex, t]; } } // the sum of this batch over all times (0 or 1) has to be the same as the sum of the previous one if (bi > 0) { //m.AddConstr(previousBatchTotal == batchTotal, "allBatchesAllocatedOrNot"); } previousBatchTotal = batchTotal; allBatches.Add(batchTotal); } totalBatchesOfProject.Add(p, allBatches); } // Tbd: Only half of the batch slots of line 1 may be occupied. Sometimes existst as internal projects. // fill gap between 50% and internal projects, monthly resolution // gather the time of time slots GRBLinExpr[] startTimesLine1 = new GRBLinExpr[varBatchTimeLine1.GetLength(1)]; GRBLinExpr[] startTimesLine2 = new GRBLinExpr[varBatchTimeLine2.GetLength(1)]; var varStartTimesLine1 = m.AddVars(startTimesLine1.Count(), 0, maxDays, GRB.CONTINUOUS, "varStartTimesLine1"); var varStartTimesLine2 = m.AddVars(startTimesLine2.Count(), 0, maxDays, GRB.CONTINUOUS, "varStartTimesLine2"); for (int l = 0; l < 2; l++) { var batchLine = l == 0 ? varBatchTimeLine1 : varBatchTimeLine2; var startTimes = l == 0 ? startTimesLine1 : startTimesLine2; var batchesLine = l == 0 ? batchesLine1 : batchesLine2; var batchTimeDiffs = l == 0 ? varBatchTimeDiffsLine1 : varBatchTimeDiffsLine2; for (int t = 0; t < batchLine.GetLength(1); t++) { startTimes[t] = new GRBLinExpr(); // sum up all durations and buffer times before this time instance for (int previous_t = 0; previous_t < t; previous_t++) { var duration_prev_t = new GRBLinExpr(); for (int bi = 0; bi < batchLine.GetLength(0); bi++) { var batch = bi < bBc ? batchesBothLines[bi] : batchesLine[bi]; var duration = batch.UsedWorkHours / 24d; duration_prev_t += batchLine[bi, previous_t] * duration; } duration_prev_t += batchTimeDiffs[previous_t]; startTimes[t] += duration_prev_t; } } var varStartTime = l == 0 ? varStartTimesLine1 : varStartTimesLine2; for (int i = 0; i < varStartTime.Count(); i++) { m.AddConstr(varStartTime[i] == startTimes[i], "varStartTimeAssignment"); } } // gather the start time of the batches var varStartTimesBatches1 = m.AddVars(varBatchTimeLine1.GetLength(0), 0, maxDays, GRB.CONTINUOUS, "varBatchStartTimesLine1"); var varStartTimesBatches2 = m.AddVars(varBatchTimeLine2.GetLength(0), 0, maxDays, GRB.CONTINUOUS, "varBatchStartTimesLine2"); for (int l = 0; l < 2; l++) { var batchLine = l == 0 ? varBatchTimeLine1 : varBatchTimeLine2; var batchStartTimes = l == 0 ? varStartTimesBatches1 : varStartTimesBatches2; var batchesLine = l == 0 ? batchesLine1 : batchesLine2; var varStartTimes = l == 0 ? varStartTimesLine1 : varStartTimesLine2; for (int bi = 0; bi < batchLine.GetLength(0); bi++) { // make the batch take the value of its time slot for (int ti = 0; ti < batchLine.GetLength(1); ti++) { m.AddConstr(batchStartTimes[bi] <= varStartTimes[ti] + maxDays * (1 - batchLine[bi, ti]), "batchTimeAssignment"); m.AddConstr(batchStartTimes[bi] >= varStartTimes[ti] - maxDays * (1 - batchLine[bi, ti]), "batchTimeAssignment"); } } } // scheduling formulation for (int l = 0; l < 2; l++) { var varBatchStartTimes = l == 0 ? varStartTimesBatches1 : varStartTimesBatches2; var line = batchesBothLines.ToList(); line.AddRange(l == 0 ? batchesLine1 : batchesLine2); for (int bi1 = 0; bi1 < line.Count() - 1; bi1++) { var batch1 = line[bi1]; var o1_exists = opportunities.FirstOrDefault(o => o.Batches.Contains(batch1)); var oi1 = o1_exists != null ? (1 - totalBatchesOfProject[o1_exists][0]) : new GRBLinExpr(); var s1 = varBatchStartTimes[bi1]; var bothLineSlack1 = (bi1 < bBc) ? (l == 0 ? varLineDecision[bi1] - 0 : 1 - varLineDecision[bi1]) : new GRBLinExpr(); for (int bi2 = bi1 + 1; bi2 < line.Count(); bi2++) { var batch2 = line[bi2]; var o2_exists = opportunities.FirstOrDefault(o => o.Batches.Contains(batch2)); var oi2 = o2_exists != null ? (1 - totalBatchesOfProject[o1_exists][0]) : new GRBLinExpr(); var s2 = varBatchStartTimes[bi2]; var bothLineSlack2 = (bi2 < bBc) ? (l == 0 ? varLineDecision[bi2] - l : l - varLineDecision[bi2]) : new GRBLinExpr(); // S1 - E2 >= 0 OR S2 - E1 >= 0 // IF both batches are used var decisionVar = m.AddVar(0, 1, GRB.BINARY, "schedulingORvar"); var opportunityNotUsedSlack = oi1 + oi2; var varBothLinesLineDecisionSlack = bothLineSlack1 + bothLineSlack2; // LineDecisionSlack for both lines batches, i.e. if a bothline batch is used, don't restrict unused line. m.AddConstr(s1 - (s2 + batch2.UsedWorkHours / 24d) >= -maxDays * (decisionVar + opportunityNotUsedSlack + varBothLinesLineDecisionSlack), "batchesScheduling"); m.AddConstr(s2 - (s1 + batch1.UsedWorkHours / 24d) >= -maxDays * (1 - decisionVar + opportunityNotUsedSlack + varBothLinesLineDecisionSlack), "batchesScheduling"); } } } // Maximize the margin (including delay and interest penalties) and the workload // Only the delivery of the first batch is really important. The rest is less important. var margin = new GRBLinExpr(); var delays = new List <GRBLinExpr>(); var interests = new List <GRBLinExpr>(); var weekValues = new List <GRBLinExpr>(); var weekDiffs = new List <GRBLinExpr>(); foreach (var p in scenario.Projects) { // if the project is used add the margin if (p is Opportunity) { margin += totalBatchesOfProject[p][0] * p.Margin * (1d / numericScale); } else { margin += p.Margin / numericScale; } // deduct the delay penalty for each batch. var startDayOfProject = (p.DeliveryDate - earliestDate).TotalDays; var varMaxedValue = m.AddVars(p.Batches.Count(), 0, maxWeek, GRB.CONTINUOUS, "penaltyIndicator" + p.Description); //var varDecisionVar = m.AddVars(p.Batches.Count(), 0, 1, GRB.BINARY, "penaltyIndicator" + p.Description); //m.Update(); GRBLinExpr previousWeekValue = new GRBLinExpr(); for (int pbi = 0; pbi < p.Batches.Count(); pbi++) { var b = p.Batches[pbi]; // compare the minimal batch time (3 + 1 weeks) to the actual delivery time GRBLinExpr weekValue; if (batchesLine1.Contains(b)) { var bi = batchesLine1.IndexOf(b) + bBc; weekValue = varStartTimesBatches1[bi] / 7d; } else if (batchesLine2.Contains(b)) { var bi = batchesLine2.IndexOf(b) + bBc; weekValue = varStartTimesBatches2[bi] / 7d; } else { var bi = batchesBothLines.IndexOf(b); // create a new var var bothLineWeekVar = m.AddVar(0, maxWeek, GRB.CONTINUOUS, "weekValueBothLines"); //m.Update(); m.AddConstr(bothLineWeekVar >= varStartTimesBatches1[bi] / 7d - (varLineDecision[bi]) * maxWeek, "weekValueBothLinesDefinitionByConstraints"); m.AddConstr(bothLineWeekVar <= varStartTimesBatches1[bi] / 7d + (varLineDecision[bi]) * maxWeek, "weekValueBothLinesDefinitionByConstraints"); m.AddConstr(bothLineWeekVar >= varStartTimesBatches2[bi] / 7d - (1 - varLineDecision[bi]) * maxWeek, "weekValueBothLinesDefinitionByConstraints"); m.AddConstr(bothLineWeekVar <= varStartTimesBatches2[bi] / 7d + (1 - varLineDecision[bi]) * maxWeek, "weekValueBothLinesDefinitionByConstraints"); weekValue = bothLineWeekVar; } if (true || pbi < p.Batches.Count() - 1) { // for positive difference add delay penalty, for negative difference add interest penalty // x = opportunity used ? x : 0 // var = max(0, x) // margin += var * delay // margin += (x - var) * interest var plannedWeek = startDayOfProject / 7d + pbi * (weeksBuffer + 1); GRBLinExpr weekDiff; if (p is Opportunity) { var weekDiffIfUsed = m.AddVar(-maxWeek, maxWeek, GRB.CONTINUOUS, "weekDiffIfUsed"); //m.Update(); var wD = weekValue - plannedWeek; m.AddConstr(weekDiffIfUsed >= wD - (1 - totalBatchesOfProject[p][0]) * maxWeek, "weekDiffConstraintDefinition1"); m.AddConstr(weekDiffIfUsed <= wD + (1 - totalBatchesOfProject[p][0]) * maxWeek, "weekDiffConstraintDefinition2"); m.AddConstr(weekDiffIfUsed <= (totalBatchesOfProject[p][0]) * maxWeek, "weekDiffConstraintDefinition3"); m.AddConstr(weekDiffIfUsed >= -(totalBatchesOfProject[p][0]) * maxWeek, "weekDiffConstraintDefinition4"); weekDiff = weekDiffIfUsed; } else { weekDiff = weekValue - plannedWeek; } m.AddConstr(varMaxedValue[pbi] >= weekDiff, "maxedWeekDiffValueConstraintDefinition"); //m.AddConstr(varMaxedValue[pbi] >= 0, "maxedWeekDiffValueConstraintDefinition"); //m.AddConstr(varMaxedValue[pbi] <= weekDiff + maxWeek * (varDecisionVar[pbi]), "maxedWeekDiffValueConstraintDefinition"); //m.AddConstr(varMaxedValue[pbi] <= 0 + maxWeek * (1 - varDecisionVar[pbi]), "maxedWeekDiffValueConstraintDefinition"); double firstBatchImportance = pbi == 0 ? 1 : 0.2; // mainly the first batch is important, the rest is meh weekValues.Add(weekValue); weekDiffs.Add(weekDiff); double revenueBase = p.Revenue == 0 ? 1E+8 : p.Revenue; // if it's an internal project it must not be shifted, therefore we make the penalty high revenueBase /= numericScale; delays.Add(varMaxedValue[pbi] * delayPenaltyMultiplier * revenueBase * firstBatchImportance); interests.Add(-(weekDiff - varMaxedValue[pbi]) * interestPenaltyMultiplier * revenueBase * firstBatchImportance); margin -= delays.Last() + interests.Last(); } // constraint: batches of a project have to be in succession, i.e. batch2 can't come after batch3 chronologically if (pbi > 0) { m.AddConstr(weekValue - previousWeekValue >= 0, "batchesOfProjectHaveToBeInSuccession"); } previousWeekValue = weekValue; } } //m.SetObjective(margin, GRB.MAXIMIZE); m.Update(); m.Write("ilproman3.mps"); m.Write("ilproman3.mst"); //m.Tune(); //m.GetTuneResult(0); m.Optimize(); //env.Set(GRB.IntParam.IISMethod, 0); // makes IIS computation fast but potentially inaccurate m.ComputeIIS(); m.Write("ilp.ilp"); // TODO: Max 5 weeks delay per project // build the solution from the optimization var sol = new Schedule(); int batchCount = 0; double cumulatedDelays = 0; double cumulatedInterests = 0; foreach (var p in scenario.Projects) { List <Schedule.BatchAllocation> allocatedBatches = new List <Schedule.BatchAllocation>(); var data = new List <double[]>(); for (int bi = 0; bi < p.Batches.Count(); bi++) { var b = p.Batches[bi]; var delay = delays[batchCount].Value; var interest = interests[batchCount].Value; cumulatedDelays += delay; cumulatedInterests += interest; var weekValue = weekValues[batchCount].Value; var weekDiff = weekDiffs[batchCount].Value; // figure out on which week and which line the batch is allocated double dayLine1 = 0; double dayLine2 = 0; if (b.Compatibility == Batch.LineCompatibility.Line1) { dayLine1 = varStartTimesBatches1[bBc + batchesLine1.IndexOf(b)].Get(GRB.DoubleAttr.X); } else if (b.Compatibility == Batch.LineCompatibility.Line2) { dayLine2 = varStartTimesBatches2[bBc + batchesLine2.IndexOf(b)].Get(GRB.DoubleAttr.X); } else { var bbi = batchesBothLines.IndexOf(b); var lineDecision = varLineDecision[bbi].Get(GRB.DoubleAttr.X); dayLine1 = varStartTimesBatches1[bbi].Get(GRB.DoubleAttr.X) * (1 - lineDecision); dayLine2 = varStartTimesBatches2[bbi].Get(GRB.DoubleAttr.X) * lineDecision; } data.Add(new double[] { delay, interest, weekValue, weekDiff, dayLine1 + dayLine2 }); if (dayLine1 > 0 && dayLine2 > 0) { throw new InvalidOperationException(); } var alloc = Schedule.LineAllocation.None; if (p is FixedProject || (p is Opportunity && totalBatchesOfProject[p][0].Value > 0.5)) { if (b.Compatibility == Batch.LineCompatibility.Both) { alloc = varLineDecision[batchesBothLines.IndexOf(b)].Get(GRB.DoubleAttr.X) > 0.5 ? Schedule.LineAllocation.Line2 : Schedule.LineAllocation.Line1; } else { alloc = b.Compatibility == Batch.LineCompatibility.Line1 ? Schedule.LineAllocation.Line1 : Schedule.LineAllocation.Line2; } } //if (dayLine1 > 0) alloc = Solution.LineAllocation.Line1; //if (dayLine2 > 0) alloc = Solution.LineAllocation.Line2; allocatedBatches.Add(new Schedule.BatchAllocation(b, alloc, earliestDate.AddDays(dayLine1 + dayLine2))); batchCount++; } sol.Add(new Schedule.ProjectAllocation(p, allocatedBatches)); } /*var maxMustWinDate = mustWinOpportunitites.Max(p => p.DeliveryDate.AddDays(7 * p.Batches.Count())).AddDays(7 * 3); * var minMustWinDate = mustWinOpportunitites.Min(p2 => p2.DeliveryDate); * var sevenmonthsrange = sol.Where(p => p.Project.DeliveryDate >= minMustWinDate && p.Project.DeliveryDate <= maxMustWinDate).ToArray(); * var ops = sevenmonthsrange.Where(p => mustWinOpportunitites.Contains(p.Project) || p.Project is FixedProject); * var s = ops.Sum(p => p.Project.Revenue);*/ return(sol); }
public SubModelOutputs Build_Model( InputData inputData, string outputFolder ) { try { // Create an empty environment, set options and start GRBEnv env = new GRBEnv(true); env.Set("LogFile", "LogFileGurobiModel.log"); env.Start(); int T = 4; List <T_param> Tlist2 = inputData.TList.FindAll(tl => tl.T >= 2); List <T_param> Tlist3 = inputData.TList.FindAll(tl => tl.T >= 3); // Create empty Gurobi model GRBModel gModel = new GRBModel(env); // Create an empty linear expression object. //Parameters and variables will be added and then used to add model components //(Objective Function and Constraints). GRBLinExpr expr1 = 0.0; GRBLinExpr expr2 = 0.0; // Crear la variables GRBVar[] Make = gModel.AddVars(inputData.ProdList.Count * Tlist2.Count * inputData.ScenList.Count, GRB.CONTINUOUS); GRBVar[] Inv = gModel.AddVars(inputData.ProdList.Count * Tlist2.Count * inputData.ScenList.Count, GRB.CONTINUOUS); GRBVar[] Sell = gModel.AddVars(inputData.ProdList.Count * Tlist2.Count * inputData.ScenList.Count, GRB.CONTINUOUS); //MAKE VARNAME inputData.ProdList.ForEach(pl => { Tlist2.ForEach(tl => { inputData.ScenList.ForEach(sl => { int ixP = inputData.ProdList.IndexOf(pl); int ixT = Tlist2.IndexOf(tl); int ixS = inputData.ScenList.IndexOf(sl); int ix = ixVar.getIx3(ixP, ixT, ixS, inputData.ProdList.Count, Tlist2.Count, inputData.ScenList.Count); Make[ix].VarName = "MAKE_PROD: " + pl.PROD + " T: " + tl.T + " SCEN: " + sl.SCEN; }); }); }); //INV VARNAME inputData.ProdList.ForEach(pl => { Tlist2.ForEach(tl => { inputData.ScenList.ForEach(sl => { int ixP = inputData.ProdList.IndexOf(pl); int ixT = Tlist2.IndexOf(tl); int ixS = inputData.ScenList.IndexOf(sl); int ix = ixVar.getIx3(ixP, ixT, ixS, inputData.ProdList.Count, Tlist2.Count, inputData.ScenList.Count); Inv[ix].VarName = "INV_PROD: " + pl.PROD + " T: " + tl.T + " SCEN: " + sl.SCEN; }); }); }); //SELL VARNAME inputData.ProdList.ForEach(pl => { Tlist2.ForEach(tl => { inputData.ScenList.ForEach(sl => { int ixP = inputData.ProdList.IndexOf(pl); int ixT = Tlist2.IndexOf(tl); int ixS = inputData.ScenList.IndexOf(sl); int ix = ixVar.getIx3(ixP, ixT, ixS, inputData.ProdList.Count, Tlist2.Count, inputData.ScenList.Count); Sell[ix].VarName = "SELL_PROD: " + pl.PROD + " T: " + tl.T + " SCEN: " + sl.SCEN; }); }); }); //MAKE => 0 (constrain) //GRB.CONTINUOUS for (int a = 0; a < Make.Length; a++) { expr1.Clear(); expr1.AddTerm(1, Make[a]); gModel.AddConstr(expr1, GRB.GREATER_EQUAL, 0, ">=0"); } //INV => 0 (constrain) for (int a = 0; a < Inv.Length; a++) { expr1.Clear(); expr1.AddTerm(1, Inv[a]); gModel.AddConstr(expr1, GRB.GREATER_EQUAL, 0, ">=0"); } //SELL => 0 (constrain) for (int a = 0; a < Sell.Length; a++) { expr1.Clear(); expr1.AddTerm(1, Sell[a]); gModel.AddConstr(expr1, GRB.GREATER_EQUAL, 0, ">=0"); } //SELL <= market[p,t] expr1.Clear(); inputData.ProdList.ForEach(pl => { Tlist2.ForEach(tl => { inputData.ScenList.ForEach(sl => { double market = inputData .MarketList .Find(ml => ml.PROD.Equals(pl.PROD) && ml.T.Equals(tl.T)).MARKET; int ixP = inputData.ProdList.IndexOf(pl); int ixT = Tlist2.IndexOf(tl); int ixS = inputData.ScenList.IndexOf(sl); int index = ixVar.getIx3(ixP, ixT, ixS, inputData.ProdList.Count, Tlist2.Count, inputData.ScenList.Count); expr1.Clear(); expr1.AddTerm(1, Sell[index]); gModel.AddConstr(expr1, GRB.LESS_EQUAL, market, "<=Market"); }); }); }); //Construir funcion objetivo //maximize Stage2_Profit: // sum {s in SCEN} prob[s] * // sum {p in PROD, t in 2..T} (revenue[p,t,s]*Sell[p,t,s] - // prodcost[p]*Make[p,t,s] - invcost[p]*Inv[p,t,s]); expr1.Clear(); inputData.ScenList.ForEach(s => { double prob = inputData.ProbList.Find(x => x.SCEN.Equals(s.SCEN)).PROB; inputData.ProdList.ForEach(p => { Tlist2.ForEach(tl => { //SELL double revenue = inputData .RevenueList .Find(r => r.PROD.Equals(p.PROD) && r.T.Equals(tl.T) && r.SCEN.Equals(s.SCEN) ).REVENUE; double prodCost = inputData .ProdCostList .Find(pc => pc.PROD.Equals(p.PROD) ).PRODCOST; double invCost = inputData .InvCostList .Find(ic => ic.PROD.Equals(p.PROD) ).INVCOST; int ixP = inputData.ProdList.IndexOf(p); int ixT = Tlist2.IndexOf(tl); int ixS = inputData.ScenList.IndexOf(s); int indexVar = ixVar.getIx3(ixP, ixT, ixS, inputData.ProdList.Count, Tlist2.Count, inputData.ScenList.Count); expr1.AddTerm(prob * revenue, Sell[indexVar]); expr1.AddTerm(-1 * prob * prodCost, Make[indexVar]); expr1.AddTerm(-1 * prob * invCost, Inv[indexVar]); }); }); }); //Insertar funcion objetivo gModel.SetObjective(expr1, GRB.MAXIMIZE); //Insertar Restricciones //subject to Time {t in 2..T, s in SCEN}: //sum {p in PROD} (1/rate[p]) * Make[p,t,s] <= avail[t]; expr1.Clear(); Tlist2.ForEach(tl => { inputData.ScenList.ForEach(sl => { inputData.ProdList.ForEach(pl => { double rate = 1 / inputData.RateList.Find(rl => rl.PROD.Equals(pl.PROD)).RATE; int ixP = inputData.ProdList.IndexOf(pl); int ixT = Tlist2.IndexOf(tl); int ixS = inputData.ScenList.IndexOf(sl); int ixMake = ixVar.getIx3(ixP, ixT, ixS, inputData.ProdList.Count, Tlist2.Count, inputData.ScenList.Count); expr1.AddTerm(rate, Make[ixMake]); }); double avail = inputData.AvailList.Find(al => al.T.Equals(tl.T)).AVAIL; gModel.AddConstr(expr1, GRB.LESS_EQUAL, avail, "TIME_" + tl.T + "_" + sl.SCEN); }); }); //subject to Balance2 { p in PROD, s in SCEN}: //Make[p, 2, s] + inv1[p] = Sell[p, 2, s] + Inv[p, 2, s]; //2 => index T => 0 expr1.Clear(); inputData.ProdList.ForEach(pl => { inputData.ScenList.ForEach(sl => { int ixP = inputData.ProdList.IndexOf(pl); int ixT = 0; int ixS = inputData.ScenList.IndexOf(sl); int ix = ixVar.getIx3(ixP, ixT, ixS, inputData.ProdList.Count, Tlist2.Count, inputData.ScenList.Count); double inv1 = subModelParams.inv1.Find(il => il.PROD.Equals(pl.PROD)).INV0; expr1.Clear(); expr2.Clear(); //como sumar un param ???? expr1.AddTerm(1, Make[ix]); //expr1.AddTerm(inv1, null); ???? como??? // Se despeja vars & params expr1.AddTerm(-1, Sell[ix]); expr1.AddTerm(-1, Inv[ix]); gModel.AddConstr(expr1, GRB.EQUAL, inv1, "BALANCE2_" + pl.PROD + "_" + sl.SCEN); }); }); //subject to Balance { p in PROD, t in 3..T, s in SCEN}: //Make[p, t, s] + Inv[p, t - 1, s] = Sell[p, t, s] + Inv[p, t, s]; inputData.ProdList.ForEach(pl => { Tlist3.ForEach(tl => { inputData.ScenList.ForEach(sl => { int ixP = inputData.ProdList.IndexOf(pl); int ixT = Tlist2.IndexOf(tl); int ixS = inputData.ScenList.IndexOf(sl); int ix = ixVar.getIx3(ixP, ixT, ixS, inputData.ProdList.Count, Tlist2.Count, inputData.ScenList.Count); int ix2 = ixVar.getIx3(ixP, ixT - 1, ixS, inputData.ProdList.Count, Tlist2.Count, inputData.ScenList.Count); expr1.Clear(); expr2.Clear(); expr1.AddTerm(1, Make[ix]); expr1.AddTerm(1, Inv[ix2]); expr2.AddTerm(1, Sell[ix]); expr2.AddTerm(1, Inv[ix]); gModel.AddConstr(expr1, GRB.EQUAL, expr2, "SUBJECT TO BALANCE"); }); }); }); //carpeta donde se expande el modelo gModel.Write(outputFolder + "Submodel_Model.lp"); // RESOLVER EL MODELO try { Console.WriteLine("Solving the sub model with gurobi.."); gModel.Optimize(); if (gModel.Status == 2) { for (int m = 0; m < Make.Length; m++) { Console.WriteLine("Make Var: " + Make[m].VarName + " = " + Make[m].X); } for (int m = 0; m < Inv.Length; m++) { Console.WriteLine("Inv Var: " + Inv[m].VarName + " = " + Inv[m].X); } for (int m = 0; m < Sell.Length; m++) { Console.WriteLine("Sell Var: " + Sell[m].VarName + " = " + Sell[m].X); } } SubModelOutputs smo = new SubModelOutputs() { make = Make, sell = Sell, inv = Inv, stage2Profit = gModel.ObjVal, gModel = gModel }; //gModel.Dispose(); //env.Dispose(); return(smo); } catch { Console.WriteLine("ERROR SOLVING THE MODEL"); } gModel.Dispose(); env.Dispose(); } catch (GRBException ex) { Console.WriteLine("Error code: " + ex.ErrorCode + ". " + ex.Message); } return(new SubModelOutputs()); }
void BuildGRBModel() { _env = new GRBEnv("SolutionLog.log"); _env.Set(GRB.DoubleParam.MIPGap, 0.0); _env.Set(GRB.DoubleParam.TimeLimit, 500); _env.Set(GRB.DoubleParam.Heuristics, 0.5); _grbModel = new GRBModel(_env); //决策变量 foreach (Node n in Data.NodeSet) { n.Result_GenerateFlow = _grbModel.AddVar(0.0, Data.M, 0.0, GRB.CONTINUOUS, "g_" + n.ID); n.Result_IsServerLoacationSelected = _grbModel.AddVar(0.0, 1.0, 0.0, GRB.BINARY, "x_" + n.ID); } foreach (Arc a in Data.ArcSet) { a.Result_FlowF = _grbModel.AddVar(0.0, Data.M, 0.0, GRB.CONTINUOUS, "fF_" + a.FromNode.ID + "_" + a.ToNode.ID); a.Result_FlowR = _grbModel.AddVar(0.0, Data.M, 0.0, GRB.CONTINUOUS, "fR_" + a.FromNode.ID + "_" + a.ToNode.ID); } _grbModel.Update(); //目标函数 GRBLinExpr expr1 = 0; foreach (Node n in Data.NodeSet) { expr1 += n.Result_IsServerLoacationSelected * Data.ServerInstalationFee; } GRBLinExpr expr2 = 0; foreach (Arc a in Data.ArcSet) { expr2 += (a.Result_FlowF + a.Result_FlowR) * Data.FlowFeePerUnit; } _grbModel.SetObjective(expr1 + expr2, GRB.MINIMIZE); //约束条件 foreach (Node n in Data.NodeSet) { _grbModel.AddConstr(n.Result_GenerateFlow <= n.Result_IsServerLoacationSelected * Data.M, "ct1_" + n.ID); } foreach (Node n in Data.NodeSet) { GRBLinExpr sum1 = 0; GRBLinExpr sum2 = 0; GRBLinExpr sum3 = 0; GRBLinExpr sum4 = 0; foreach (Arc a in n.ArcSet) { if (a.ToNode == n)//进 { sum1 += a.Result_FlowF; sum3 += a.Result_FlowR; } else//出 { sum2 += a.Result_FlowR; sum4 += a.Result_FlowF; } } _grbModel.AddConstr(n.Result_GenerateFlow + sum1 + sum2 == n.Demand + sum3 + sum4, "ct2_" + n.ID); } foreach (Arc a in Data.ArcSet) { _grbModel.AddConstr(a.Result_FlowF + a.Result_FlowR <= a.Capacity, "ct3_" + a.FromNode.ID + "_" + a.ToNode.ID); } }
public static Graph RunSolver(Graph graph) { GRBEnv env = new GRBEnv(); env.Set(GRB.IntParam.OutputFlag, 0); env.Set(GRB.IntParam.LogToConsole, 0); env.Set(GRB.IntParam.Presolve, 2); env.Set(GRB.DoubleParam.Heuristics, 0.0); GRBModel model = new GRBModel(env); GRBVar[] variables = new GRBVar[graph.NumberOfEdges]; model.SetCallback(new LPSolverCallback()); Dictionary<Edge, GRBVar> edgeVars = new Dictionary<Edge, GRBVar>(); // Add variables to the LP model for (int i = 0; i < graph.NumberOfEdges; i++) { variables[i] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, "x_" + i); edgeVars.Add(graph.Edges[i], variables[i]); } model.Update(); // Add constraints to the LP model Console.Write("\rRunning LP. Creating constraints...\r"); //var nonTerminals = graph.Vertices.Except(graph.Terminals).ToList(); ulong conNr = 0; //var terminalCombinations = new List<List<Vertex>>(); // Assume, without loss of generality, that Terminals[0] is the root, and thus is always included int rootNr = 1; foreach (var rootTerminal in graph.Terminals) //var rootTerminal = graph.Terminals[0]; { Console.Write("\rRunning LP. Creating constraints... {0}/{1}\r", rootNr, graph.Terminals.Count); foreach (var combination in GetBFS(graph, rootTerminal)) { var nodes = combination.ToList(); //new HashSet<Vertex>(combination); if (nodes.Count == graph.NumberOfVertices || graph.Terminals.All(nodes.Contains)) continue; //Debug.WriteLine("Combination: {0}", string.Join(" ", nodes)); //for (int i = 1; i <= nodes.Count; i++) { var edges = nodes//.Take(i) .SelectMany(graph.GetEdgesForVertex) .Distinct() .Where(x => x.WhereOne(y => !nodes.Contains(y))); GRBLinExpr expression = 0; foreach (var edge in edges) expression.AddTerm(1, edgeVars[edge]); model.AddConstr(expression >= 1.0, "subset_" + conNr); conNr++; if (conNr % 100000 == 0) { //model = model.Presolve(); //Pre-solve the model every 1000 constraints. int constrBefore = model.GetConstrs().Length, varsBefore = model.GetVars().Length; Debug.WriteLine("Presolve called."); var presolved = model.Presolve(); Debug.WriteLine("Model has {0} constraints, {1} variables. Presolve has {2} constraints, {3} variables", constrBefore, varsBefore, presolved.GetConstrs().Length, presolved.GetVars().Length); } } } //Debug.WriteLine(" "); //Debug.WriteLine(" "); rootNr++; } //terminalCombinations.Add(new List<Vertex>(new[] { graph.Terminals[0] })); //for (int j = 1; j < graph.Terminals.Count - 1; j++) // terminalCombinations.AddRange(new Combinations<Vertex>(graph.Terminals.Skip(1), j).Select(combination => combination.Union(new[] { graph.Terminals[0] }).ToList())); //long nonTerminalSetsDone = 0; //long nonTerminalSets = 0; //for (int i = 0; i <= nonTerminals.Count; i++) // nonTerminalSets += Combinations<Vertex>.NumberOfCombinations(nonTerminals.Count, i); //for (int i = 0; i <= nonTerminals.Count; i++) //{ // foreach (var nonTerminalSet in new Combinations<Vertex>(nonTerminals, i)) // { // foreach (var nodes in (from a in terminalCombinations // select new HashSet<Vertex>(a.Union(nonTerminalSet)))) // { // var edges = nodes.SelectMany(graph.GetEdgesForVertex) // .Distinct() // .Where(x => x.WhereOne(y => !nodes.Contains(y))); // GRBLinExpr expression = 0; // foreach (var edge in edges) // expression.AddTerm(1, edgeVars[edge]); // model.AddConstr(expression >= 1.0, "subset_" + conNr); // conNr++; // } // nonTerminalSetsDone++; // if (nonTerminalSetsDone % 100 == 0) // Console.Write("\rRunning LP. Creating constraints... {0}/{1} ({2:0.000}%)\r", nonTerminalSetsDone, nonTerminalSets, nonTerminalSetsDone * 100.0 / nonTerminalSets); // } //} // Solve the LP model Console.Write("\rRunning LP. Creating objective & updating... \r"); GRBLinExpr objective = new GRBLinExpr(); for (int i = 0; i < graph.NumberOfEdges; i++) objective.AddTerm(graph.Edges[i].Cost, variables[i]); model.SetObjective(objective, GRB.MINIMIZE); Console.Write("\rRunning LP. Tuning... \r"); model.Tune(); Debug.WriteLine("Presolve called."); model.Presolve(); Console.Write("\rRunning LP. Solving... \r"); Debug.WriteLine("Optimize called."); model.Optimize(); Graph solution = graph.Clone(); HashSet<Edge> includedEdges = new HashSet<Edge>(); for (int i = 0; i < solution.NumberOfEdges; i++) { var value = variables[i].Get(GRB.DoubleAttr.X); if (value == 1) includedEdges.Add(solution.Edges[i]); } foreach (var edge in solution.Edges.ToList()) if (!includedEdges.Contains(edge)) solution.RemoveEdge(edge); Console.Write("\r \r"); return solution; }
public Dictionary <EdgePair, Double> Solve(FCTPGraph ti) { Dictionary <EdgePair, Double> edgeFlows = new Dictionary <EdgePair, Double>(); Dictionary <String, Edge> edgeMap = new Dictionary <String, Edge>(); Dictionary <String, GRBVar> varMap = new Dictionary <String, GRBVar>(); String currentVar = ""; try { //Model GRBEnv env = new GRBEnv(); env.Set(GRB.IntParam.LogToConsole, 0); GRBModel model = new GRBModel(env); model.Set(GRB.StringAttr.ModelName, "tranportation"); //edges foreach (Edge e in ti.Edges) { String xij = edgeVarName(e.Source, e.Sink); edgeMap.Add(xij, e); GRBVar var = model.AddVar(0, GRB.INFINITY, e.C, GRB.CONTINUOUS, xij); varMap.Add(xij, var); } //objective min Sum cij xij model.Set(GRB.IntAttr.ModelSense, GRB.MINIMIZE); //integrate variables model.Update(); //supply constraints foreach (Node source in ti.Sources) { GRBLinExpr sourceConstraint = new GRBLinExpr(); foreach (Node sink in ti.Sinks) { String name = edgeVarName(source.Id, sink.Id); sourceConstraint.AddTerm(1, varMap[name]); } model.AddConstr(sourceConstraint, GRB.EQUAL, source.Amount, ""); } //demand constraints foreach (Node sink in ti.Sinks) { GRBLinExpr sinkConstraint = new GRBLinExpr(); foreach (Node source in ti.Sources) { String name = edgeVarName(source.Id, sink.Id); sinkConstraint.AddTerm(1, varMap[name]); } model.AddConstr(sinkConstraint, GRB.EQUAL, sink.Amount, ""); } //update constraints model.Update(); model.Write("mipTranportationGurobi.lp"); env.Set(GRB.IntParam.Threads, 1); model.Optimize(); bool status = model.Get(GRB.IntAttr.Status) == GRB.Status.OPTIMAL; Console.WriteLine("Status: " + status); foreach (String s in edgeMap.Keys) { currentVar = s; double flow = varMap[s].Get(GRB.DoubleAttr.X); Edge e = edgeMap[s]; EdgePair ep = new EdgePair(e.Source, e.Sink); edgeFlows.Add(ep, flow); } model.Dispose(); } catch (GRBException e) { Console.Out.WriteLine(currentVar + " - is current var"); Console.Out.WriteLine(e.ErrorCode); Console.Out.WriteLine(e.ToString()); } return(edgeFlows); }
public static void build_initial_model(bool lp_relax) { env = new GRBEnv(true); env.Set("LogFile", "Recoloracao.log"); env.Set(GRB.IntParam.LogToConsole, 0); env.Start(); model = new GRBModel(env); model.ModelName = "Recoloracao"; model.Set(GRB.IntParam.Threads, 1); model.Set(GRB.IntParam.Method, 1); model.Set(GRB.DoubleParam.TimeLimit, 1800); X = new GRBVar[input.nVertices, input.nCores]; var obj = new GRBLinExpr(); for (int i = 0; i < input.nVertices; i++) { for (int j = 0; j < input.nCores; j++) { string nome = "X_ " + i.ToString() + "_c " + j.ToString(); if (lp_relax) { X[i, j] = model.AddVar(0.0, 1.0, 1.0, GRB.CONTINUOUS, nome); } else { X[i, j] = model.AddVar(0.0, 1.0, 1.0, GRB.BINARY, nome); } if (input.caminhoColorido[i] != j + 1) { obj.AddTerm(1, X[i, j]); } } } model.SetObjective(obj, GRB.MINIMIZE); // Restrição 1 for (int i = 0; i < input.nVertices; i++) { GRBLinExpr soma = new GRBLinExpr(); for (int c = 0; c < input.nCores; c++) { soma.AddTerm(1.0, X[i, c]); } model.AddConstr(soma, GRB.EQUAL, 1, "Cor vértice" + i); } // Restrição 2 for (int p = 0; p < input.nVertices - 2; p++) { for (int r = p + 2; r < input.nVertices; r++) { for (int q = p + 1; q < r; q++) { for (int k = 0; k < input.nCores; k++) { model.AddConstr(X[p, k] - X[q, k] + X[r, k], GRB.LESS_EQUAL, 1, "p" + p + "-q" + q + "-r" + r + "-k" + k); } } } } }
public Schedule Run() { Schedule schedule = new Schedule(100); try { GRBEnv env = new GRBEnv(true); env.Set("LogFile", "mip1.log"); env.Start(); GRBModel model = new GRBModel(env); // Create variables GRBVar[,] varInstructors = new GRBVar[ctx.Instructors.Length, 100]; GRBVar[,] varStudents = new GRBVar[ctx.Students.Length, 100]; GRBVar[,] varPresidentsSessions = new GRBVar[ctx.Presidents.Length, 20]; GRBVar[,] varSecretariesSessions = new GRBVar[ctx.Secretaries.Length, 20]; GRBVar[] varPresidentsTempP = new GRBVar[ctx.Presidents.Length]; GRBVar[] varPresidentsTempQ = new GRBVar[ctx.Presidents.Length]; GRBVar[] varSecretariesTempP = new GRBVar[ctx.Secretaries.Length]; GRBVar[] varSecretariesTempQ = new GRBVar[ctx.Secretaries.Length]; GRBVar[] varMembersTempP = new GRBVar[ctx.Members.Length]; GRBVar[] varMembersTempQ = new GRBVar[ctx.Members.Length]; for (int ts = 0; ts < 100; ts++) { for (int i = 0; i < ctx.Instructors.Length; i++) { varInstructors[i, ts] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, ctx.Instructors[i].Name + " " + ts); } for (int s = 0; s < ctx.Students.Length; s++) { varStudents[s, ts] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, ctx.Students[s].Name + " " + ts); } } for (int session = 0; session < 20; session++) { for (int president = 0; president < ctx.Presidents.Length; president++) { varPresidentsSessions[president, session] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, ctx.Presidents[president].Name + "_session_" + session); } for (int secretary = 0; secretary < ctx.Secretaries.Length; secretary++) { varSecretariesSessions[secretary, session] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, ctx.Secretaries[secretary].Name + "_session_" + session); } } for (int president = 0; president < ctx.Presidents.Length; president++) { varPresidentsTempP[president] = model.AddVar(0.0, GRB.INFINITY, 0.0, GRB.INTEGER, ctx.Presidents[president].Name + "_workload_P"); varPresidentsTempQ[president] = model.AddVar(0.0, GRB.INFINITY, 0.0, GRB.INTEGER, ctx.Presidents[president].Name + "_workload_Q"); } for (int secretary = 0; secretary < ctx.Secretaries.Length; secretary++) { varSecretariesTempP[secretary] = model.AddVar(0.0, GRB.INFINITY, 0.0, GRB.INTEGER, ctx.Secretaries[secretary].Name + "_workload_P"); varSecretariesTempQ[secretary] = model.AddVar(0.0, GRB.INFINITY, 0.0, GRB.INTEGER, ctx.Secretaries[secretary].Name + "_workload_Q"); } for (int member = 0; member < ctx.Members.Length; member++) { varMembersTempP[member] = model.AddVar(0.0, GRB.INFINITY, 0.0, GRB.INTEGER, ctx.Members[member].Name + "workload_P"); varMembersTempQ[member] = model.AddVar(0.0, GRB.INFINITY, 0.0, GRB.INTEGER, ctx.Members[member].Name + "workload_Q"); } // Set objective model.SetObjective(SumProduct(varInstructors, ctx.Instructors) + SumOfVars(varPresidentsTempP) + SumOfVars(varPresidentsTempQ) + SumOfVars(varSecretariesTempP) + SumOfVars(varSecretariesTempQ) + SumOfVars(varMembersTempP) + SumOfVars(varMembersTempQ) , GRB.MINIMIZE); char[] equalArray = Enumerable.Range(0, 100).Select(x => GRB.EQUAL).ToArray(); char[] greaterArray = Enumerable.Range(0, 100).Select(x => GRB.GREATER_EQUAL).ToArray(); char[] lessArray = Enumerable.Range(0, 100).Select(x => GRB.LESS_EQUAL).ToArray(); // Add constraint: max 5 instructors string[] nameOfMaxNrInstructorsConstrs = Enumerable.Range(0, 100).Select(x => "MaxInstructorsNr" + x).ToArray(); model.AddConstrs(SumOfPersonVarsPerTs(varInstructors), lessArray, NrArray(5.0), nameOfMaxNrInstructorsConstrs); // Add constraint: be min a president in every ts (enélkül több elnök is lehet 1 ts-ban) string[] nameOfPresidentsConstrs = Enumerable.Range(0, 100).Select(x => "President" + x).ToArray(); model.AddConstrs(SumOfPersonVarsPerTs(GetPresidentsVars(varInstructors)), equalArray, NrArray(1.0), nameOfPresidentsConstrs); // Add constraint: be min a secretary in every ts string[] nameOfSecretariesConstrs = Enumerable.Range(0, 100).Select(x => "Secretary" + x).ToArray(); model.AddConstrs(SumOfPersonVarsPerTs(GetSecretariesVars(varInstructors)), equalArray, NrArray(1.0), nameOfSecretariesConstrs); // Add constraint: be min a member in every ts string[] nameOfMembersConstrs = Enumerable.Range(0, 100).Select(x => "Member" + x).ToArray(); model.AddConstrs(SumOfPersonVarsPerTs(GetMembersVars(varInstructors)), greaterArray, NrArray(1.0), nameOfMembersConstrs); string[] nameOfMembersMaxConstrs = Enumerable.Range(0, 100).Select(x => "MemberMax" + x).ToArray(); model.AddConstrs(SumOfPersonVarsPerTs(GetMembersVars(varInstructors)), lessArray, NrArray(2.0), nameOfMembersMaxConstrs); // Add constraint: be a student in every ts string[] nameOfStudentsPerTsConstrs = Enumerable.Range(0, 100).Select(x => "Student" + x).ToArray(); model.AddConstrs(SumOfPersonVarsPerTs(varStudents), equalArray, NrArray(1.0), nameOfStudentsPerTsConstrs); string[] nameOfStudentsConstrs = Enumerable.Range(0, 100).Select(x => "Student" + x).ToArray(); model.AddConstrs(SumOfPersonVarsPerPerson(varStudents), equalArray, NrArray(1.0), nameOfStudentsConstrs); // Add constraint: be the supervisor of student there for (int ts = 0; ts < varStudents.GetLength(1); ts++) { for (int student = 0; student < varStudents.GetLength(0); student++) { int idOfSupervisor = ctx.Students[student].Supervisor.Id; model.AddConstr(varStudents[student, ts] - varInstructors[idOfSupervisor, ts] <= 0.0, "Supervisor" + ts + "_" + student); } } // Add constraint: be examiner there for (int student = 0; student < varStudents.GetLength(0); student++) { GRBLinExpr[] sumOfExaminersVarsPerTs = SumOfPersonVarsPerTs(GetExaminersVars(varInstructors, ctx.Students[student].ExamCourse)); for (int ts = 0; ts < varStudents.GetLength(1); ts++) { model.AddConstr(varStudents[student, ts] - sumOfExaminersVarsPerTs[ts] <= 0.0, "Examiner" + ts + "_" + student); } } // Add constraint: president not change GRBVar[,] presidentsVars = GetPresidentsVars(varInstructors); for (int session = 0; session < 20; session++) { for (int president = 0; president < ctx.Presidents.Length; president++) { GRBVar[] presidentsVarsInSession = new GRBVar[] { presidentsVars[president, session *5], presidentsVars[president, session *5 + 1], presidentsVars[president, session *5 + 2], presidentsVars[president, session *5 + 3], presidentsVars[president, session *5 + 4] }; model.AddGenConstrAnd(varPresidentsSessions[president, session], presidentsVarsInSession, "PresindentInSession" + president + "_" + session); } } string[] nameOfPresidentsSessionsConstraints = Enumerable.Range(0, 100).Select(x => "PresidentSession" + x).ToArray(); model.AddConstrs(SumOfPersonVarsPerTs(varPresidentsSessions), equalArray, NrArray(1.0), nameOfPresidentsSessionsConstraints); // Add constraint: secretary not change GRBVar[,] secretariesVars = GetSecretariesVars(varInstructors); for (int session = 0; session < 20; session++) { for (int secretary = 0; secretary < ctx.Secretaries.Length; secretary++) { GRBVar[] secretariesVarsInSession = new GRBVar[] { secretariesVars[secretary, session *5], secretariesVars[secretary, session *5 + 1], secretariesVars[secretary, session *5 + 2], secretariesVars[secretary, session *5 + 3], secretariesVars[secretary, session *5 + 4] }; model.AddGenConstrAnd(varSecretariesSessions[secretary, session], secretariesVarsInSession, "SecretaryInSession" + secretary + "_" + session); } } string[] nameOfSecretariesSessionsConstraints = Enumerable.Range(0, 100).Select(x => "SecretariesSession" + x).ToArray(); model.AddConstrs(SumOfPersonVarsPerTs(varSecretariesSessions), equalArray, NrArray(1.0), nameOfSecretariesSessionsConstraints); // Add constraint: presidents available model.AddConstr(SumProduct(GetPresidentsVars(varInstructors), ctx.Presidents) == 0.0, "PresindetsAvailable"); // Add constraint: secretaries available model.AddConstr(SumProduct(GetSecretariesVars(varInstructors), ctx.Secretaries) == 0.0, "SecretariesAvailable"); // Add constraint: workload of presidents string[] nameOfPresidentWorkloadMinConstrs = Enumerable.Range(0, 100).Select(x => "PresintWorkloadMin" + x).ToArray(); string[] nameOfPresidentWorkloadMaxConstrs = Enumerable.Range(0, 100).Select(x => "PresintWorkloadMax" + x).ToArray(); model.AddConstrs(SumOfPersonVarsPerPerson(varPresidentsSessions), greaterArray, NrArray(3.0), nameOfPresidentWorkloadMinConstrs); model.AddConstrs(SumOfPersonVarsPerPerson(varPresidentsSessions), lessArray, NrArray(7.0), nameOfPresidentWorkloadMaxConstrs); for (int president = 0; president < ctx.Presidents.Length; president++) { // for soft constraint: model.AddConstr(varPresidentsTempP[president] - varPresidentsTempQ[president] == SumOfPersonVarsPerPerson(varPresidentsSessions)[president] - 5.0, "workloadSoft" + president); } // Add constraint: workload of secretaries string[] nameOfSecretaryWorkloadMinConstrs = Enumerable.Range(0, 100).Select(x => "SecretaryWorkloadMin" + x).ToArray(); string[] nameOfSecretaryWorkloadMaxConstrs = Enumerable.Range(0, 100).Select(x => "SecretaryWorkloadMax" + x).ToArray(); model.AddConstrs(SumOfPersonVarsPerPerson(varSecretariesSessions), greaterArray, NrArray(1.0), nameOfSecretaryWorkloadMinConstrs); model.AddConstrs(SumOfPersonVarsPerPerson(varSecretariesSessions), lessArray, NrArray(3.0), nameOfSecretaryWorkloadMaxConstrs); for (int secretary = 0; secretary < ctx.Secretaries.Length; secretary++) { // for soft constraint: model.AddConstr(varSecretariesTempP[secretary] - varSecretariesTempQ[secretary] == SumOfPersonVarsPerPerson(varSecretariesSessions)[secretary] - 2.0, "workloadSoft" + secretary); } // Add constraint: workload of members string[] nameOfMemberWorkloadMinConstrs = Enumerable.Range(0, 100).Select(x => "MemberWorkloadMin" + x).ToArray(); string[] nameOfMemberWorkloadMaxConstrs = Enumerable.Range(0, 100).Select(x => "MemberWorkloadMax" + x).ToArray(); model.AddConstrs(SumOfPersonVarsPerPerson(GetMembersVars(varInstructors)), greaterArray, NrArray(7.0), nameOfMemberWorkloadMinConstrs); //model.AddConstrs(SumOfPersonVarsPerPerson(GetMembersVars(varInstructors)), lessArray, NrArray(12.0), nameOfMemberWorkloadMaxConstrs); for (int member = 0; member < ctx.Members.Length; member++) { // for soft constraint: model.AddConstr(varMembersTempP[member] - varMembersTempQ[member] == SumOfPersonVarsPerPerson(GetMembersVars(varInstructors))[member] - 10.0, "workloadSoft" + member); } // Optimize model model.Optimize(); for (int ts = 0; ts < 100; ts++) { List <Instructor> instructorsTS = new List <Instructor>(); for (int person = 0; person < ctx.Instructors.Length; person++) { if (varInstructors[person, ts].X == 1.0) { instructorsTS.Add(ctx.Instructors[person]); } } schedule.FinalExams[ts] = new FinalExam(); schedule.FinalExams[ts].Id = ts; //schedule.FinalExams[ts].President = instructorsTS.Find(i => i.Roles.HasFlag(Roles.President)); //schedule.FinalExams[ts].Secretary = instructorsTS.Find(i => i.Roles.HasFlag(Roles.Secretary)); schedule.FinalExams[ts].Member = instructorsTS.Find(i => i.Roles.HasFlag(Roles.Member)); for (int i = 0; i < ctx.Students.Length; i++) { if (varStudents[i, ts].X == 1.0) { schedule.FinalExams[ts].Student = ctx.Students[i]; } } if (instructorsTS.Contains(schedule.FinalExams[ts].Student.Supervisor)) { schedule.FinalExams[ts].Supervisor = schedule.FinalExams[ts].Student.Supervisor; } Course courseOfStudent = schedule.FinalExams[ts].Student.ExamCourse; for (int i = 0; i < courseOfStudent.Instructors.Length; i++) { if (instructorsTS.Contains(courseOfStudent.Instructors[i])) { schedule.FinalExams[ts].Examiner = courseOfStudent.Instructors[i]; } } } for (int session = 0; session < 20; session++) { Instructor presindetInSession = new Instructor(); Instructor secretaryInSession = new Instructor(); for (int president = 0; president < ctx.Presidents.Length; president++) { if (varPresidentsSessions[president, session].X == 1) { presindetInSession = ctx.Presidents[president]; } } for (int secretary = 0; secretary < ctx.Secretaries.Length; secretary++) { if (varSecretariesSessions[secretary, session].X == 1) { secretaryInSession = ctx.Secretaries[secretary]; } } for (int ts = session * 5 + 0; ts < session * 5 + 5; ts++) { schedule.FinalExams[ts].President = presindetInSession; schedule.FinalExams[ts].Secretary = secretaryInSession; } } Console.WriteLine("Obj: " + model.ObjVal); // Dispose of model and env model.Dispose(); env.Dispose(); } catch (GRBException e) { Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message); } return(schedule); }
public void RescheduleLotMachineAllocation() { //Console.WriteLine(GetTime); //Console.WriteLine("QueueLength:"); //Console.WriteLine(Queue.Length); int limitILPSolver = 2000; // maximum number of jobs put into the ILP solver // Clear scheduled lots foreach (Machine machine in LithographyArea.Machines) { ScheduledLotsPerMachine[machine.Name].Clear(); } // Set weight of each Lot for (int j = 0; j < Queue.Length; j++) { Lot peekLot = Queue.PeekAt(j); Queue.PeekAt(j).WeightDueDate = GetDueDateWeight(peekLot); Queue.PeekAt(j).WeightWIPBalance = GetWIPBalanceWeight(peekLot); } try { // Get lots which have to be scheduled List <Lot> lotsToBeScheduled = new List <Lot>(); if (Queue.Length <= limitILPSolver) // Small enough for ILP solver { for (int j = 0; j < Queue.Length; j++) { // Peek next lot in queue Lot peekLot = Queue.PeekAt(j); lotsToBeScheduled.Add(peekLot); } } else //Filter lots to get a set of 750 lots for ILP Solver { lotsToBeScheduled = DecreaseQueueLength(limitILPSolver); } double[] readyTimes = GetReadyTimes(); double[,] processingTimes = GetProcessingTimes(lotsToBeScheduled); List <Lot> lotsToBeScheduledNew = RemoveLotsCurrentlyNotEligible(lotsToBeScheduled, processingTimes); if (lotsToBeScheduledNew.Count < lotsToBeScheduled.Count) { processingTimes = GetProcessingTimes(lotsToBeScheduledNew); lotsToBeScheduled = lotsToBeScheduledNew; } // Number of jobs and machines int nMachines = processingTimes.GetLength(0); int nJobs = processingTimes.GetLength(1); Console.Write($"Time: {LithographyArea.StartDate.AddSeconds(GetTime)}, number of jobs scheduled: {nJobs}, "); // Create an empty environment, set options and start GRBEnv env = new GRBEnv(true); env.Set("LogFile", "mip1.log"); //env.OutputFlag = 0; env.Start(); // Create empty model GRBModel model = new GRBModel(env); // Decision Variable GRBVar[,,] x = new GRBVar[nMachines, nJobs, nJobs]; for (int i = 0; i < nMachines; ++i) { for (int j = 0; j < nJobs; ++j) { for (int k = 0; k < nJobs; ++k) { x[i, j, k] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, "x"); } } } // Set objective: GRBLinExpr exp1 = 0.0; for (int i = 0; i < nMachines; ++i) { for (int j = 0; j < nJobs; ++j) { for (int k = 0; k < nJobs; ++k) { GRBLinExpr exp2 = ((k + 1) * processingTimes[i, j] + readyTimes[i]) * x[i, j, k]; exp1.Add(exp2); } } } model.SetObjective(exp1, GRB.MINIMIZE); // Constraints for (int j = 0; j < nJobs; ++j) { GRBLinExpr exp3 = 0.0; for (int i = 0; i < nMachines; ++i) { for (int k = 0; k < nJobs; ++k) { exp3.AddTerm(1.0, x[i, j, k]); } } model.AddConstr(exp3 == 1.0, "c1"); } for (int i = 0; i < nMachines; ++i) { for (int k = 0; k < nJobs; ++k) { GRBLinExpr exp4 = 0.0; for (int j = 0; j < nJobs; ++j) { exp4.AddTerm(1.0, x[i, j, k]); } model.AddConstr(exp4 <= 1.0, "c2"); } } // Solve model.Optimize(); Console.WriteLine($"Status {model.Status}"); for (int i = 0; i < nMachines; ++i) { for (int j = 0; j < nJobs; j++) { Lot peekLot = lotsToBeScheduled[j]; for (int k = 0; k < nJobs; ++k) { if (x[i, j, k].X == 1.0) { //Console.WriteLine($"({i},{j},{k})" + "ProcessingTime: " + processingTimes[i, j]); //TODO: Schedule on machine ScheduledLotsPerMachine[LithographyArea.Machines[i].Name].Add(peekLot); } } } } Console.WriteLine("Objective: " + model.ObjVal); // Dispose of model and env model.Dispose(); env.Dispose(); } catch (GRBException e) { Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message); } }
public override Schedule Generate(Scenario scenario, FilledBaseline baseline = null) { var batches = scenario.Projects.SelectMany(p => p.Batches).OrderBy(b => b.Compatibility).ToArray(); var batchesBothLines = batches.Where(b => b.Compatibility == Batch.LineCompatibility.Both).ToArray(); var batchesLine1 = batches.Where(b => b.Compatibility == Batch.LineCompatibility.Line1).ToArray(); var batchesLine2 = batches.Where(b => b.Compatibility == Batch.LineCompatibility.Line2).ToArray(); int bBc = batchesBothLines.Count(); var earliestDate = scenario.Projects.Min(p => p.DeliveryDate); var lastDate = scenario.Projects.Max(p => p.DeliveryDate.AddDays(7 * p.Batches.Count())).AddDays(7 * 3); // add an additional buffer of 3 weeks. int maxWeek = (lastDate - earliestDate).Days / 7; // 0 to 200 const int weeksBuffer = 3; var delayPenaltyMultiplier = 0.01; // 1% of revenue per week var interestPenaltyMultiplier = (0.045 / 52.14); // 4.5% / 52.14 * (revenue – margin) var env = new GRBEnv(); env.Set(GRB.IntParam.LogToConsole, 1); //env.Set(GRB.DoubleParam.TimeLimit, _config.TimeLimit.Value); //env.Set(GRB.DoubleParam.MIPGap, 0.02); var m = new GRBModel(env); // decide: which batch is when, on which line // TODO: Batch can be over two weeks var varBatchWeekLine1 = m.AddVars(batchesBothLines.Count() + batchesLine1.Count(), maxWeek, 0, 1, GRB.BINARY, "batchWeekLine1"); var varBatchWeekLine2 = m.AddVars(batchesBothLines.Count() + batchesLine2.Count(), maxWeek, 0, 1, GRB.BINARY, "batchWeekLine2"); m.Update(); // line constraints: // assign batch only once (constraint for single lines then both lines) // if it's a fixed project though, then it must be allocated for (int l = 0; l < 2; l++) { var line = l == 0 ? varBatchWeekLine1 : varBatchWeekLine2; for (int bi = bBc; bi < line.GetLength(0); bi++) { var batchTotal = new GRBLinExpr(); for (int w = 0; w < maxWeek; w++) { batchTotal += line[bi, w]; } var batch = (l == 0 ? batchesLine1 : batchesLine2)[bi - bBc]; bool isFixedProject = scenario.Projects.First(p => p.Batches.Contains(batch)) is FixedProject; if (!isFixedProject) { m.AddConstr(batchTotal <= 1, "assign batch only once"); } else { m.AddConstr(batchTotal == 1, "assign batch exactly once"); } } } for (int bi = 0; bi < bBc; bi++) { var batchTotal = new GRBLinExpr(); for (int w = 0; w < maxWeek; w++) { batchTotal += varBatchWeekLine1[bi, w]; batchTotal += varBatchWeekLine2[bi, w]; } var batch = batchesBothLines[bi]; bool isFixedProject = scenario.Projects.First(p => p.Batches.Contains(batch)) is FixedProject; if (!isFixedProject) { m.AddConstr(batchTotal <= 1, "assign batch only once"); } else { m.AddConstr(batchTotal == 1, "assign batch exactly once"); } } // for all batches which aren't assigned to a line yet, limit the allocation of yet unassigned batches of a project to one line // TODO: If project has e.g. line1 and both lines, limit both lines to line 1? var varLineDecision = m.AddVars(scenario.Projects.Count(), 0, 1, GRB.BINARY, "varLineDecision"); m.Update(); for (int pi = 0; pi < scenario.Projects.Count(); pi++) { var p = scenario.Projects[pi]; if (!p.Batches.Any(b => b.Compatibility == Batch.LineCompatibility.Both)) { continue; } var sumBatchLine1 = new GRBLinExpr(); var sumBatchLine2 = new GRBLinExpr(); int i = batches.IndexOf(p.Batches.First()); for (int j = 0; j < p.Batches.Count(); j++) { if (p.Batches[j].Compatibility != Batch.LineCompatibility.Both) { continue; } for (int w = 0; w < maxWeek; w++) { sumBatchLine1 += varBatchWeekLine1[i + j, w]; sumBatchLine2 += varBatchWeekLine2[i + j, w]; } } m.AddConstr(sumBatchLine1 <= (1 - varLineDecision[pi]) * GRB.INFINITY); m.AddConstr(sumBatchLine2 <= varLineDecision[pi] * GRB.INFINITY); } // on each line the total of a week must not exceed 24*7 for (int l = 0; l < 2; l++) { var lineVars = l == 0 ? varBatchWeekLine1 : varBatchWeekLine2; var line = l == 0 ? batchesLine1 : batchesLine2; int bBC = batchesBothLines.Count(); for (int w = 0; w < maxWeek; w++) { var weekTotal = new GRBLinExpr(); for (int b = 0; b < lineVars.GetLength(0); b++) { var batch = b < bBC ? batchesBothLines[b] : line[b - bBC]; weekTotal += lineVars[b, w] * batch.UsedWorkHours; /*var test = new GRBLinExpr(); * test += (lineVars[b] == w) * 0.1; * (l[b] - w) * batch.UsedWorkHours*/ } m.AddConstr(weekTotal <= 24 * 7); } } // for each project, either all or no batches must be assigned var totalBatchesOfProject = new Dictionary <Project, List <GRBLinExpr> >(); foreach (var p in scenario.Projects) { var allBatches = new List <GRBLinExpr>(); // gather the total of all batches GRBLinExpr previousBatchTotal = null; for (int bi = 0; bi < p.Batches.Count(); bi++) { var b = p.Batches[bi]; var batchTotal = new GRBLinExpr(); if (b.Compatibility == Batch.LineCompatibility.Line1) { var bIndex = bBc + batchesLine1.IndexOf(b); for (int w = 0; w < maxWeek; w++) { batchTotal += varBatchWeekLine1[bIndex, w]; } } else if (b.Compatibility == Batch.LineCompatibility.Line2) { var bIndex = bBc + batchesLine2.IndexOf(b); for (int w = 0; w < maxWeek; w++) { batchTotal += varBatchWeekLine2[bIndex, w]; } } else { var bIndex = batchesBothLines.IndexOf(b); for (int w = 0; w < maxWeek; w++) { batchTotal += varBatchWeekLine1[bIndex, w]; batchTotal += varBatchWeekLine2[bIndex, w]; } } // the sum of this batch over all weeks (0 or 1) has to be the same as the sum of the previous one if (bi > 0) { m.AddConstr(previousBatchTotal == batchTotal); } previousBatchTotal = batchTotal; allBatches.Add(batchTotal); } totalBatchesOfProject.Add(p, allBatches); } // DEPRECATED: penalty/interest term adds this constraint with slack // in-between batches of the same project, 3 weeks distance should exist, otherwise there will be a penalty /*foreach (var p in scenario.Projects) * { * var bCompatibility = p.Batches.First().Compatibility; * * // for all batches of this project make sure that the previous one is at least (3 + 1) weeks away * // in other words: the total of batches within (3 + 1) weeks must be 4 at least. * for (int w = 0; w < maxWeek - weeksBuffer; w++) * { * var batchTotalOver4Weeks = new GRBLinExpr(); * // sum all batches over a 4 week period * for (int i = 0; i <= weeksBuffer; i++) * { * foreach (var b in p.Batches) * { * if (bCompatibility == Batch.LineCompatibility.Line1) * { * batchTotalOver4Weeks += varBatchWeekLine1[bBc + batchesLine1.IndexOf(b), w + i]; * } * else if (bCompatibility == Batch.LineCompatibility.Line2) * { * batchTotalOver4Weeks += varBatchWeekLine2[bBc + batchesLine2.IndexOf(b), w + i]; * } * else * { * var bIndex = batchesBothLines.IndexOf(b); * batchTotalOver4Weeks += varBatchWeekLine1[bIndex, w + i]; * batchTotalOver4Weeks += varBatchWeekLine2[bIndex, w + i]; * } * } * } * m.AddConstr(batchTotalOver4Weeks <= 1); * } * }*/ // Tbd: Only half of the batch slots of line 1 may be occupied. Sometimes existst as internal projects. // fill gap between 50% and internal projects, monthly resolution // Maximize the margin (including delay and interest penalties) and the workload // TODO: Only first one important? no delay otherwise? var margin = new GRBLinExpr(); foreach (var p in scenario.Projects) { // if the project is used add the margin margin += totalBatchesOfProject[p].First() * p.Margin; // deduct the delay penalty for each batch. int startWeekOfProject = (p.DeliveryDate - earliestDate).Days / 7; var varMaxedValue = m.AddVars(p.Batches.Count(), 0, maxWeek, GRB.CONTINUOUS, "penaltyIndicator" + p.Description); var varDecisionVar = m.AddVars(p.Batches.Count(), 0, 1, GRB.BINARY, "penaltyIndicator" + p.Description); m.Update(); GRBLinExpr previousWeekValue = new GRBLinExpr(); for (int bi = 0; bi < p.Batches.Count(); bi++) { var b = p.Batches[bi]; // compare the minimal batch time (3 + 1 weeks) to the actual delivery time var weekValue = new GRBLinExpr(); if (b.Compatibility == Batch.LineCompatibility.Line1) { var bIndex = bBc + batchesLine1.IndexOf(b); for (int w = 0; w < maxWeek; w++) { weekValue += varBatchWeekLine1[bIndex, w] * w; } } else if (b.Compatibility == Batch.LineCompatibility.Line2) { var bIndex = bBc + batchesLine2.IndexOf(b); for (int w = 0; w < maxWeek; w++) { weekValue += varBatchWeekLine2[bIndex, w] * w; } } else { var bIndex = batchesBothLines.IndexOf(b); for (int w = 0; w < maxWeek; w++) { weekValue += varBatchWeekLine1[bIndex, w] * w; weekValue += varBatchWeekLine2[bIndex, w] * w; } } if (bi < p.Batches.Count() - 1) { // for positive difference add delay penalty, for negative difference add interest penalty // var = max(0, x) // var * delay // (x - var) * interest var plannedWeek = startWeekOfProject + bi * (weeksBuffer + 1); var weekDiff = weekValue - plannedWeek * totalBatchesOfProject[p].First(); // here we multiply the planned week with the (0/1)-allocation-indicator to avoid penalties when the project is not assigned (and therefore weekValue = 0) m.AddConstr(varMaxedValue[bi] >= weekDiff); m.AddConstr(varMaxedValue[bi] >= 0); m.AddConstr(varMaxedValue[bi] <= weekDiff + maxWeek * (varDecisionVar[bi])); m.AddConstr(varMaxedValue[bi] <= 0 + maxWeek * (1 - varDecisionVar[bi])); double firstBatchImportance = bi == 0 ? 1 : 0.2; // mainly the first batch is important, the rest is meh margin += varMaxedValue[bi] * delayPenaltyMultiplier * firstBatchImportance; margin += (weekDiff - varMaxedValue[bi]) * interestPenaltyMultiplier * firstBatchImportance; } // constraint: batches of a project have to be in succession, i.e. batch2 can't come after batch3 chronologically if (bi > 0) { m.AddConstr(weekValue - previousWeekValue >= 0); } previousWeekValue = weekValue; } } m.SetObjective(margin, GRB.MAXIMIZE); m.Update(); m.Optimize(); //env.Set(GRB.IntParam.IISMethod, 0); // makes IIS computation fast but potentially inaccurate //m.ComputeIIS(); //m.Write("ilp.ilp"); // build the solution from the optimization var sol = new Schedule(); foreach (var p in scenario.Projects) { List <Schedule.BatchAllocation> allocatedBatches = new List <Schedule.BatchAllocation>(); for (int bi = 0; bi < p.Batches.Count(); bi++) { var b = p.Batches[bi]; // figure out on which week and which line the batch is allocated int weekLine1 = 0; int weekLine2 = 0; if (b.Compatibility == Batch.LineCompatibility.Line1) { for (int w = 1; w <= maxWeek; w++) { weekLine1 += (int)varBatchWeekLine1[bBc + batchesLine1.IndexOf(b), w - 1].Get(GRB.DoubleAttr.X) * w; } } else if (b.Compatibility == Batch.LineCompatibility.Line2) { for (int w = 1; w <= maxWeek; w++) { weekLine2 += (int)varBatchWeekLine2[bBc + batchesLine2.IndexOf(b), w - 1].Get(GRB.DoubleAttr.X) * w; } } else { for (int w = 1; w <= maxWeek; w++) { weekLine1 += (int)varBatchWeekLine1[batchesBothLines.IndexOf(b), w - 1].Get(GRB.DoubleAttr.X) * w; weekLine2 += (int)varBatchWeekLine2[batchesBothLines.IndexOf(b), w - 1].Get(GRB.DoubleAttr.X) * w; } } if (weekLine1 > 0 && weekLine2 > 0) { throw new InvalidOperationException(); } var alloc = Schedule.LineAllocation.None; if (weekLine1 > 0) { alloc = Schedule.LineAllocation.Line1; } if (weekLine2 > 0) { alloc = Schedule.LineAllocation.Line2; } allocatedBatches.Add(new Schedule.BatchAllocation(b, alloc, earliestDate.AddDays(weekLine1 + weekLine2 - 1))); } sol.Add(new Schedule.ProjectAllocation(p, allocatedBatches)); } return(sol); }
public override Schedule Generate(Scenario scenario, FilledBaseline fillerBaseline = null) { var projects = fillerBaseline == null ? scenario.Projects : fillerBaseline.Projects; // TODO: With fillerBaseline use ALL OF the projects var opportunities = projects.Where(p => p is Opportunity).ToArray(); var batches = projects.SelectMany(p => p.Batches).ToArray(); var batchesBothLines = batches.Where(b => b.Compatibility == Batch.LineCompatibility.Both).ToArray(); var batchesLine1 = batches.Where(b => b.Compatibility == Batch.LineCompatibility.Line1).ToArray(); var batchesLine2 = batches.Where(b => b.Compatibility == Batch.LineCompatibility.Line2).ToArray(); int bBc = batchesBothLines.Count(); var earliestDate = projects.Min(p => p.DeliveryDate); var lastDate = projects.Max(p => p.DeliveryDate.AddDays(7 * p.Batches.Count())).AddDays(7 * 3); // add an additional buffer of 3 weeks. double maxDays = (lastDate - earliestDate).Days; var maxWeek = maxDays / 7 + 1; const int weeksBuffer = 3; var delayPenaltyMultiplier = 0.01; // 1% of revenue per week var interestPenaltyMultiplier = (0.045 / 52.14); // 4.5% / 52.14 * (revenue – margin) var env = new GRBEnv(); env.Set(GRB.IntParam.UpdateMode, 1); env.Set(GRB.IntParam.LogToConsole, 1); //env.Set(GRB.IntParam.Threads, 1); //env.Set(GRB.IntParam.NumericFocus, 3); //env.Set(GRB.DoubleParam.TimeLimit, 30); env.Set(GRB.DoubleParam.MIPGap, 0.06); //env.Set(GRB.IntParam.Presolve,2); //env.Set(GRB.IntParam.ScaleFlag, 2); //env.Set(GRB.IntParam.Method, 2); var m = new GRBModel(env); // decide: which batch is when, on which line var varBatchTimeLine1 = m.AddVars(bBc + batchesLine1.Count(), 0, maxDays, GRB.CONTINUOUS, "varBatchTimeLine1"); var varBatchTimeLine2 = m.AddVars(bBc + batchesLine2.Count(), 0, maxDays, GRB.CONTINUOUS, "varBatchTimeLine2"); var varOpportunityIsUsed = m.AddVars(opportunities.Count(), 0, 1, GRB.BINARY, "varOpportunityIsUsed"); var varLineDecision = m.AddVars(bBc, 0, 1, GRB.BINARY, "varLineDecision"); // 0 = Line1, 1 = Line2 //m.Update(); //TODO: Start heuristic //m.Update(); //varMaxedValue[0].Set(GRB.DoubleAttr.Start, 5); // baseline constraint: // we want some opportunities to be used for sure /*if (baseline != null) * { * var mustWinOpportunitites = new List<Opportunity>(); * for (int oi = 0; oi < opportunities.Count(); oi++) * { * if (baseline.Projects.Contains(opportunities[oi])) * { * mustWinOpportunitites.Add((Opportunity)opportunities[oi]); * m.AddConstr(varOpportunityIsUsed[opportunities.IndexOf(opportunities[oi])] == 1); * } * } * * }*/ // line constraints: // if it's a fixed project though, then it must be allocated // for all batches which aren't assigned to a line yet, limit the allocation of yet unassigned batches of a project to one line // TODO: If project has e.g. line1 and both lines, limit both lines to line 1? for (int pi = 0; pi < projects.Count(); pi++) { var p = projects[pi]; if (!p.Batches.Any(b => b.Compatibility == Batch.LineCompatibility.Both)) { continue; } int lineRestriction = -1; if (p.Batches.Any(b => b.Compatibility != Batch.LineCompatibility.Both)) { lineRestriction = p.Batches.First(b => b.Compatibility != Batch.LineCompatibility.Both).Compatibility == Batch.LineCompatibility.Line1 ? 0 : 1; } int i_prev = -1; for (int j = 0; j < p.Batches.Count(); j++) { if (p.Batches[j].Compatibility != Batch.LineCompatibility.Both) { continue; } var i = batchesBothLines.IndexOf(p.Batches[j]); if (lineRestriction == -1) { if (i_prev != -1) { m.AddConstr(varLineDecision[i] == varLineDecision[i_prev]); } } else { // if there are other batches on this project which are already on a specific line, limit to the same line m.AddConstr(varLineDecision[i] == lineRestriction); } i_prev = i; } } // scheduling formulation for (int l = 0; l < 2; l++) { var lineVars = l == 0 ? varBatchTimeLine1 : varBatchTimeLine2; var line = batchesBothLines.ToList(); line.AddRange(l == 0 ? batchesLine1 : batchesLine2); for (int bi1 = 0; bi1 < line.Count() - 1; bi1++) { var batch1 = line[bi1]; var o1_exists = opportunities.FirstOrDefault(o => o.Batches.Contains(batch1)); var oi1 = o1_exists != null ? (1 - varOpportunityIsUsed[opportunities.IndexOf(o1_exists)]) : new GRBLinExpr(); var s1 = lineVars[bi1]; for (int bi2 = bi1 + 1; bi2 < line.Count(); bi2++) { var batch2 = line[bi2]; var o2_exists = opportunities.FirstOrDefault(o => o.Batches.Contains(batch2)); var oi2 = o2_exists != null ? (1 - varOpportunityIsUsed[opportunities.IndexOf(o2_exists)]) : new GRBLinExpr(); var s2 = lineVars[bi2]; // S1 - E2 >= 0 OR S2 - E1 >= 0 // IF both batches are used var decisionVar = m.AddVar(0, 1, GRB.BINARY, "schedulingORvar"); var opportunityNotUsedSlack = oi1 + oi2; m.AddConstr(s1 - (s2 + batch2.UsedWorkHours / 24d) >= -maxDays * (decisionVar + opportunityNotUsedSlack)); //TODO: varLineDecisionSlack for both lines batches m.AddConstr(s2 - (s1 + batch1.UsedWorkHours / 24d) >= -maxDays * (1 - decisionVar + opportunityNotUsedSlack)); } } } // Tbd: Only half of the batch slots of line 1 may be occupied. Sometimes existst as internal projects. // fill gap between 50% and internal projects, monthly resolution // Maximize the margin (including delay and interest penalties) and the workload // Only the delivery of the first batch is really important. The rest is less important. var margin = new GRBLinExpr(); var delays = new List <GRBLinExpr>(); var interests = new List <GRBLinExpr>(); var weekValues = new List <GRBLinExpr>(); var weekDiffs = new List <GRBLinExpr>(); foreach (var p in projects) { // if the project is used add the margin if (p is Opportunity) { margin += varOpportunityIsUsed[opportunities.IndexOf(p)] * p.Margin; } else { margin += p.Margin; } // deduct the delay penalty for each batch. var startDayOfProject = (p.DeliveryDate - earliestDate).TotalDays; var varMaxedValue = m.AddVars(p.Batches.Count(), 0, maxWeek, GRB.CONTINUOUS, "penaltyIndicator" + p.Description); var varDecisionVar = m.AddVars(p.Batches.Count(), 0, 1, GRB.BINARY, "penaltyIndicator" + p.Description); //m.Update(); GRBLinExpr previousWeekValue = new GRBLinExpr(); for (int pbi = 0; pbi < p.Batches.Count(); pbi++) { var b = p.Batches[pbi]; // compare the minimal batch time (3 + 1 weeks) to the actual delivery time GRBLinExpr weekValue; if (batchesLine1.Contains(b)) { var bi = batchesLine1.IndexOf(b) + bBc; weekValue = varBatchTimeLine1[bi] / 7d; } else if (batchesLine2.Contains(b)) { var bi = batchesLine2.IndexOf(b) + bBc; weekValue = varBatchTimeLine2[bi] / 7d; } else { var bi = batchesBothLines.IndexOf(b); // create a new var var bothLineWeekVar = m.AddVar(0, maxWeek, GRB.CONTINUOUS, "weekValueBothLines"); //m.Update(); m.AddConstr(bothLineWeekVar >= varBatchTimeLine1[bi] / 7d - (varLineDecision[bi]) * maxWeek); m.AddConstr(bothLineWeekVar <= varBatchTimeLine1[bi] / 7d + (varLineDecision[bi]) * maxWeek); m.AddConstr(bothLineWeekVar >= varBatchTimeLine2[bi] / 7d - (1 - varLineDecision[bi]) * maxWeek); m.AddConstr(bothLineWeekVar <= varBatchTimeLine2[bi] / 7d + (1 - varLineDecision[bi]) * maxWeek); weekValue = bothLineWeekVar; } if (true || pbi < p.Batches.Count() - 1) { // for positive difference add delay penalty, for negative difference add interest penalty // x = opportunity used ? x : 0 // var = max(0, x) // margin += var * delay // margin += (x - var) * interest var plannedWeek = startDayOfProject / 7d + pbi * (weeksBuffer + 1); GRBLinExpr weekDiff; if (p is Opportunity) { var weekDiffIfUsed = m.AddVar(0, maxWeek, GRB.CONTINUOUS, "weekValueBothLines"); //m.Update(); var wD = weekValue - plannedWeek; m.AddConstr(weekDiffIfUsed >= wD - (1 - varOpportunityIsUsed[opportunities.IndexOf(p)]) * maxWeek); m.AddConstr(weekDiffIfUsed <= wD + (1 - varOpportunityIsUsed[opportunities.IndexOf(p)]) * maxWeek); m.AddConstr(weekDiffIfUsed <= (varOpportunityIsUsed[opportunities.IndexOf(p)]) * maxWeek); m.AddConstr(weekDiffIfUsed >= -(varOpportunityIsUsed[opportunities.IndexOf(p)]) * maxWeek); weekDiff = weekDiffIfUsed; } else { weekDiff = weekValue - plannedWeek; } m.AddConstr(varMaxedValue[pbi] >= weekDiff); m.AddConstr(varMaxedValue[pbi] >= 0); m.AddConstr(varMaxedValue[pbi] <= weekDiff + maxWeek * (varDecisionVar[pbi])); m.AddConstr(varMaxedValue[pbi] <= 0 + maxWeek * (1 - varDecisionVar[pbi])); double firstBatchImportance = pbi == 0 ? 1 : 0.2; // mainly the first batch is important, the rest is meh weekValues.Add(weekValue); weekDiffs.Add(weekDiff); double revenueBase = p.Revenue == 0 ? 999999999 : p.Revenue; delays.Add(varMaxedValue[pbi] * delayPenaltyMultiplier * revenueBase * firstBatchImportance); interests.Add(-(weekDiff - varMaxedValue[pbi]) * interestPenaltyMultiplier * revenueBase * firstBatchImportance); margin -= delays.Last() + interests.Last(); } // constraint: batches of a project have to be in succession, i.e. batch2 can't come after batch3 chronologically if (pbi > 0) { m.AddConstr(weekValue - previousWeekValue >= 0); } previousWeekValue = weekValue; } } m.SetObjective(margin, GRB.MAXIMIZE); //m.Tune(); //m.GetTuneResult(0); m.Optimize(); //env.Set(GRB.IntParam.IISMethod, 0); // makes IIS computation fast but potentially inaccurate //m.ComputeIIS(); //m.Write("ilp.ilp"); // TODO: Max 5 weeks delay per project // build the solution from the optimization var sol = new Schedule(); int batchCount = 0; double cumulatedDelays = 0; double cumulatedInterests = 0; foreach (var p in projects) { List <Schedule.BatchAllocation> allocatedBatches = new List <Schedule.BatchAllocation>(); var data = new List <double[]>(); for (int bi = 0; bi < p.Batches.Count(); bi++) { var b = p.Batches[bi]; var delay = delays[batchCount].Value; var interest = interests[batchCount].Value; cumulatedDelays += delay; cumulatedInterests += interest; var weekValue = weekValues[batchCount].Value; var weekDiff = weekDiffs[batchCount].Value; // figure out on which week and which line the batch is allocated double dayLine1 = 0; double dayLine2 = 0; if (b.Compatibility == Batch.LineCompatibility.Line1) { dayLine1 = varBatchTimeLine1[bBc + batchesLine1.IndexOf(b)].Get(GRB.DoubleAttr.X); } else if (b.Compatibility == Batch.LineCompatibility.Line2) { dayLine2 = varBatchTimeLine2[bBc + batchesLine2.IndexOf(b)].Get(GRB.DoubleAttr.X); } else { var bbi = batchesBothLines.IndexOf(b); var lineDecision = varLineDecision[bbi].Get(GRB.DoubleAttr.X); dayLine1 = varBatchTimeLine1[bbi].Get(GRB.DoubleAttr.X) * (1 - lineDecision); dayLine2 = varBatchTimeLine2[bbi].Get(GRB.DoubleAttr.X) * lineDecision; } data.Add(new double[] { delay, interest, weekValue, weekDiff, dayLine1 + dayLine2 }); if (dayLine1 > 0 && dayLine2 > 0) { throw new InvalidOperationException(); } var alloc = Schedule.LineAllocation.None; if (p is FixedProject || (p is Opportunity && varOpportunityIsUsed[opportunities.IndexOf(p)].Get(GRB.DoubleAttr.X) > 0.5)) { if (b.Compatibility == Batch.LineCompatibility.Both) { alloc = varLineDecision[batchesBothLines.IndexOf(b)].Get(GRB.DoubleAttr.X) > 0.5 ? Schedule.LineAllocation.Line2 : Schedule.LineAllocation.Line1; } else { alloc = b.Compatibility == Batch.LineCompatibility.Line1 ? Schedule.LineAllocation.Line1 : Schedule.LineAllocation.Line2; } } //if (dayLine1 > 0) alloc = Solution.LineAllocation.Line1; //if (dayLine2 > 0) alloc = Solution.LineAllocation.Line2; allocatedBatches.Add(new Schedule.BatchAllocation(b, alloc, earliestDate.AddDays(dayLine1 + dayLine2))); batchCount++; } sol.Add(new Schedule.ProjectAllocation(p, allocatedBatches)); } return(sol); }
void BuildModel_RestrMaster() { _env = new GRBEnv("SolutionLog.log"); _env.Set(GRB.DoubleParam.MIPGap, 0.0); _env.Set(GRB.DoubleParam.TimeLimit, 500); _grbModel = new GRBModel(_env); //决策变量 foreach (Node n in Data.NodeSet) { n.Result_GenerateFlow = _grbModel.AddVar(0.0, M, 0.0, GRB.CONTINUOUS, "g_" + n.ID); } foreach (Dictionary <Node, int> scheme in SchemeSet) { _grbModel.AddVar(0.0, 1.0, 0.0, GRB.CONTINUOUS, "k_" + SchemeSet.IndexOf(scheme)); } foreach (Arc a in Data.ArcSet) { a.Result_FlowF = _grbModel.AddVar(0.0, M, 0.0, GRB.CONTINUOUS, "fF_" + a.FromNode.ID + "_" + a.ToNode.ID); a.Result_FlowR = _grbModel.AddVar(0.0, M, 0.0, GRB.CONTINUOUS, "fR_" + a.FromNode.ID + "_" + a.ToNode.ID); } _grbModel.Update(); //目标函数 GRBLinExpr expr1 = 0; foreach (Dictionary <Node, int> scheme in SchemeSet) { foreach (Node n in Data.NodeSet) { expr1 += scheme[n] * Data.ServerInstalationFee * _grbModel.GetVarByName("k_" + SchemeSet.IndexOf(scheme)); } } GRBLinExpr expr2 = 0; foreach (Arc a in Data.ArcSet) { expr2 += (a.Result_FlowF + a.Result_FlowR) * Data.FlowFeePerUnit; } _grbModel.SetObjective(expr1 + expr2, GRB.MINIMIZE); //约束条件 foreach (Node n in Data.NodeSet) { GRBLinExpr expr = 0; foreach (Dictionary <Node, int> scheme in SchemeSet) { expr += scheme[n] * _grbModel.GetVarByName("k_" + SchemeSet.IndexOf(scheme)) * M; } _grbModel.AddConstr(expr - n.Result_GenerateFlow >= 0, "ct1_" + n.ID); } foreach (Node n in Data.NodeSet) { GRBLinExpr sum1 = 0; GRBLinExpr sum2 = 0; GRBLinExpr sum3 = 0; GRBLinExpr sum4 = 0; foreach (Arc a in n.ArcSet) { if (a.ToNode == n)//进 { sum1 += a.Result_FlowF; sum3 += a.Result_FlowR; } else//出 { sum2 += a.Result_FlowR; sum4 += a.Result_FlowF; } } _grbModel.AddConstr(n.Result_GenerateFlow + sum1 + sum2 == n.Demand + sum3 + sum4, "ct2_" + n.ID); } foreach (Arc a in Data.ArcSet) { _grbModel.AddConstr(a.Result_FlowF + a.Result_FlowR <= a.Capacity, "ct3_" + a.FromNode.ID + "_" + a.ToNode.ID); } GRBLinExpr exprCtr = 0; foreach (Dictionary <Node, int> scheme in SchemeSet) { exprCtr += _grbModel.GetVarByName("k_" + SchemeSet.IndexOf(scheme)); } _grbModel.AddConstr(exprCtr == 1, "ct4"); }
void BuildModel_SubProblem2() { _env = new GRBEnv("SolutionLog.log"); _env.Set(GRB.DoubleParam.MIPGap, 0.0); _env.Set(GRB.DoubleParam.TimeLimit, 500); _grbModel = new GRBModel(_env); //决策变量 foreach (Node n in Data.NodeSet) { n.Result_GenerateFlow = _grbModel.AddVar(0.0, GRB.INFINITY, 0.0, GRB.CONTINUOUS, "g_" + n.ID); } foreach (Arc a in Data.ArcSet) { a.Result_FlowF = _grbModel.AddVar(0.0, GRB.INFINITY, 0.0, GRB.CONTINUOUS, "fF_" + a.FromNode.ID + "_" + a.ToNode.ID); a.Result_FlowR = _grbModel.AddVar(0.0, GRB.INFINITY, 0.0, GRB.CONTINUOUS, "fR_" + a.FromNode.ID + "_" + a.ToNode.ID); } _grbModel.Update(); //目标函数 GRBLinExpr expr1 = 0; foreach (Node n in Data.NodeSet) { expr1 += n.Result_GenerateFlow * multiplier_p[n]; } GRBLinExpr expr2 = 0; foreach (Arc a in Data.ArcSet) { expr2 += (a.Result_FlowF + a.Result_FlowR) * Data.FlowFeePerUnit; } _grbModel.SetObjective(expr1 + expr2, GRB.MINIMIZE); //约束条件 foreach (Node n in Data.NodeSet) { GRBLinExpr sum1 = 0; GRBLinExpr sum2 = 0; GRBLinExpr sum3 = 0; GRBLinExpr sum4 = 0; foreach (Arc a in n.ArcSet) { if (a.ToNode == n)//进 { sum1 += a.Result_FlowF; sum3 += a.Result_FlowR; } else//出 { sum2 += a.Result_FlowR; sum4 += a.Result_FlowF; } } _grbModel.AddConstr(n.Result_GenerateFlow + sum1 + sum2 == n.Demand + sum3 + sum4, "ct2_" + n.ID); } foreach (Arc a in Data.ArcSet) { _grbModel.AddConstr(a.Result_FlowF + a.Result_FlowR <= a.Capacity, "ct3_" + a.FromNode.ID + "_" + a.ToNode.ID); } }