static void SolveDual3(GRBEnv env, HashSet <string> nodes_set, List <Arc> arcs) { GRBModel dual = new GRBModel(env); Dictionary <string, GRBConstr> flow_balance = new Dictionary <string, GRBConstr>(); Dictionary <Arc, GRBVar> arc_traversed = new Dictionary <Arc, GRBVar>(); GRBConstr[] constrs = dual.AddConstrs(nodes_set.Count); dual.Update(); int i = 0; foreach (string s in nodes_set) { GRBConstr con = constrs[i]; con.Set(GRB.StringAttr.ConstrName, "flow_balance." + s); con.Set(GRB.CharAttr.Sense, GRB.EQUAL); flow_balance[s] = con; ++i; } flow_balance[ORIGIN].Set(GRB.DoubleAttr.RHS, -1); flow_balance[DESTINATION].Set(GRB.DoubleAttr.RHS, 1); foreach (Arc a in arcs) { GRBColumn col = new GRBColumn(); col.AddTerm(1, flow_balance[a.dest]); col.AddTerm(-1, flow_balance[a.source]); arc_traversed[a] = dual.AddVar(0, 1, a.length, GRB.CONTINUOUS, col, "arc_traversed." + a.source + "." + a.dest); } dual.Optimize(); dual.Write("dual3.lp"); dual.Write("dual3.sol"); dual.Dispose(); }
/// <summary> /// Exports the model to the specified path as an MPS-file. /// </summary> /// <param name="path">The path to the file (has to end with .mps).</param> public void ExportMPS(string path) { path = path.EndsWith(".mps") ? path : path + ".mps"; switch (Type) { case SolverType.CPLEX: CplexModel.ExportModel(path); break; case SolverType.Gurobi: GurobiModel.Update(); GurobiModel.Write(path); break; default: throw new ArgumentException("Unknown solver type: " + Type); } }
public bool TrySolve(out double prepS, out double execS) { _model.SetObjective(_value); _model.Parameters.DualReductions = 0; _model.Update(); _model.Write("debug.lp"); _model.Optimize(); if (_model.Status != GRB.Status.OPTIMAL) { Logger.Warn($"Didn't find solution to time conditions. Gurobi status: {_model.Status}"); prepS = -1; execS = -1; return(false); } try { prepS = _prepS.X; execS = _execS.X; } catch { Logger.Warn($"Exception. Gurobi status: {_model.Status}"); prepS = -1; execS = -1; return(false); } return(true); }
static void Main(string[] args) { GRBEnv env = new GRBEnv(); GRBModel m = new GRBModel(env); GRBVar x1 = m.AddVar(0, GRB.INFINITY, 20, GRB.CONTINUOUS, "food.1"); GRBVar x2 = m.AddVar(0, GRB.INFINITY, 10, GRB.CONTINUOUS, "food.2"); GRBVar x3 = m.AddVar(0, GRB.INFINITY, 31, GRB.CONTINUOUS, "food.3"); GRBVar x4 = m.AddVar(0, GRB.INFINITY, 11, GRB.CONTINUOUS, "food.4"); GRBVar x5 = m.AddVar(0, GRB.INFINITY, 12, GRB.CONTINUOUS, "food.5"); m.Update(); GRBConstr con1 = m.AddConstr(2 * x1 + 3 * x3 + 1 * x4 + 2 * x5 >= 21, "nutrient.iron"); GRBConstr con2 = m.AddConstr(1 * x2 + 2 * x3 + 2 * x4 + 1 * x5 >= 12, "nutrient.calcium"); m.Optimize(); m.Write("diet.lp"); foreach (GRBVar var in m.GetVars()) { Console.WriteLine("{0} = {1}, reduced cost = {2}", var.Get(GRB.StringAttr.VarName), var.Get(GRB.DoubleAttr.X), var.Get(GRB.DoubleAttr.RC)); } foreach (GRBConstr constr in m.GetConstrs()) { Console.WriteLine("{0}, slack = {1}, pi = {2}", constr.Get(GRB.StringAttr.ConstrName), constr.Get(GRB.DoubleAttr.Slack), constr.Get(GRB.DoubleAttr.Pi)); } m.Dispose(); env.Dispose(); }
/// <summary> /// 求解主问题 /// </summary> /// <returns></returns> public bool Solve() { _env = new GRBEnv(); _env.OutputFlag = 0; _env.LogToConsole = 0; _model = new GRBModel(_env); BuildVar(); BuildConst(); _model.Write("Benders_Master.lp"); _model.Optimize(); if (_model.Status == GRB.Status.OPTIMAL) { Frmk.SlaveObj = new SlaveObjCoef(); foreach (Node n in Frmk.Data.NodeSet) { double x = _model.GetVarByName("x_" + n.ID).X; Frmk.SlaveObj.x.Add(n, Convert.ToInt32(x)); } Output(); return(true); } else { throw new ApplicationException("没可行解!"); return(false); } }
static void Main(string[] args) { if (args.Length < 1) { Console.Out.WriteLine("Usage: feasopt_cs filename"); return; } try { GRBEnv env = new GRBEnv(); GRBModel feasmodel = new GRBModel(env, args[0]); // Create a copy to use FeasRelax feature later */ GRBModel feasmodel1 = new GRBModel(feasmodel); // Clear objective feasmodel.SetObjective(new GRBLinExpr()); // Add slack variables GRBConstr[] c = feasmodel.GetConstrs(); for (int i = 0; i < c.Length; ++i) { char sense = c[i].Get(GRB.CharAttr.Sense); if (sense != '>') { GRBConstr[] constrs = new GRBConstr[] { c[i] }; double[] coeffs = new double[] { -1 }; feasmodel.AddVar(0.0, GRB.INFINITY, 1.0, GRB.CONTINUOUS, constrs, coeffs, "ArtN_" + c[i].Get(GRB.StringAttr.ConstrName)); } if (sense != '<') { GRBConstr[] constrs = new GRBConstr[] { c[i] }; double[] coeffs = new double[] { 1 }; feasmodel.AddVar(0.0, GRB.INFINITY, 1.0, GRB.CONTINUOUS, constrs, coeffs, "ArtP_" + c[i].Get(GRB.StringAttr.ConstrName)); } } feasmodel.Update(); // Optimize modified model feasmodel.Write("feasopt.lp"); feasmodel.Optimize(); // Use FeasRelax feature */ feasmodel1.FeasRelax(GRB.FEASRELAX_LINEAR, true, false, true); feasmodel1.Write("feasopt1.lp"); feasmodel1.Optimize(); // Dispose of model and env feasmodel1.Dispose(); feasmodel.Dispose(); env.Dispose(); } catch (GRBException e) { Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message); } }
private void btnModelo2_Click(object sender, EventArgs e) { GRBEnv Ambinte = new GRBEnv(); GRBModel Modelo = new GRBModel(Ambinte); Random Aleatorio = new Random(4); int m = 1600; GRBVar[,] X = new GRBVar[m, m]; for (int i = 0; i < m; i++) { for (int j = 0; j < m; j++) { X[i, j] = Modelo.AddVar(0, 1, Aleatorio.Next(1, 20), GRB.BINARY, "x_" + i.ToString() + "_" + j.ToString()); } } Modelo.ModelSense = GRB.MAXIMIZE; GRBLinExpr Expressao = new GRBLinExpr(); for (int i = 0; i < m; i++) { Expressao.Clear(); for (int j = 0; j < m; j++) { Expressao.AddTerm(1, X[i, j]); } Modelo.AddConstr(Expressao == 1, "Vendedor_" + i); } for (int j = 0; j < m; j++) { Expressao.Clear(); for (int i = 0; i < m; i++) { Expressao.AddTerm(1, X[i, j]); } Modelo.AddConstr(Expressao == 1, "Regiao_" + j); } Stopwatch Cronometro = new Stopwatch(); Cronometro.Start(); Modelo.Optimize(); Cronometro.Stop(); MessageBox.Show("O valor do lucro é: " + Modelo.ObjVal.ToString()); MessageBox.Show("O tempo para resolver foi de: " + Cronometro.ElapsedMilliseconds.ToString() + " ms"); MessageBox.Show("Se quiser saber a alocação que gera esse lucro é só me pagar"); //for (int j = 0; j < m; j++) //{ // for (int i = 0; i < m; i++) // { // if(X[i,j].X>0) // { // MessageBox.Show("O vendedor " + i + " é alocado para a região " + j); // } // } //} Modelo.Write("C:\\Teste\\Modelo2.lp"); }
static void Main(string[] args) { if (args.Length < 1) { Console.Out.WriteLine("Usage: feasopt_cs filename"); return; } try { GRBEnv env = new GRBEnv(); GRBModel feasmodel = new GRBModel(env, args[0]); // Create a copy to use FeasRelax feature later */ GRBModel feasmodel1 = new GRBModel(feasmodel); // Clear objective feasmodel.SetObjective(new GRBLinExpr()); // Add slack variables GRBConstr[] c = feasmodel.GetConstrs(); for (int i = 0; i < c.Length; ++i) { char sense = c[i].Get(GRB.CharAttr.Sense); if (sense != '>') { GRBConstr[] constrs = new GRBConstr[] { c[i] }; double[] coeffs = new double[] { -1 }; feasmodel.AddVar(0.0, GRB.INFINITY, 1.0, GRB.CONTINUOUS, constrs, coeffs, "ArtN_" + c[i].Get(GRB.StringAttr.ConstrName)); } if (sense != '<') { GRBConstr[] constrs = new GRBConstr[] { c[i] }; double[] coeffs = new double[] { 1 }; feasmodel.AddVar(0.0, GRB.INFINITY, 1.0, GRB.CONTINUOUS, constrs, coeffs, "ArtP_" + c[i].Get(GRB.StringAttr.ConstrName)); } } feasmodel.Update(); // Optimize modified model feasmodel.Write("feasopt.lp"); feasmodel.Optimize(); // Use FeasRelax feature */ feasmodel1.FeasRelax(GRB.FEASRELAX_LINEAR, true, false, true); feasmodel1.Write("feasopt1.lp"); feasmodel1.Optimize(); // Dispose of model and env feasmodel1.Dispose(); feasmodel.Dispose(); env.Dispose(); } catch (GRBException e) { Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message); } }
static void Main(string[] args) { if (args.Length < 1) { Console.Out.WriteLine("Usage: lp_cs filename"); return; } try { GRBEnv env = new GRBEnv(); GRBModel model = new GRBModel(env, args[0]); model.Optimize(); int optimstatus = model.Status; if (optimstatus == GRB.Status.INF_OR_UNBD) { model.Parameters.Presolve = 0; model.Optimize(); optimstatus = model.Status; } if (optimstatus == GRB.Status.OPTIMAL) { double objval = model.ObjVal; Console.WriteLine("Optimal objective: " + objval); } else if (optimstatus == GRB.Status.INFEASIBLE) { Console.WriteLine("Model is infeasible"); // compute and write out IIS model.ComputeIIS(); model.Write("model.ilp"); } else if (optimstatus == GRB.Status.UNBOUNDED) { Console.WriteLine("Model is unbounded"); } else { Console.WriteLine("Optimization was stopped with status = " + optimstatus); } // Dispose of model and env model.Dispose(); env.Dispose(); } catch (GRBException e) { Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message); } }
public void Solve() { GenerateModel(); model.Optimize(); if (model.Status == GRB.Status.OPTIMAL) { model.Write("solution.sol"); InterpretSolution(); } }
private void GenerateModel() { environment = new GRBEnv("gurobi.log"); model = new GRBModel(environment); GenerateVariables(); model.Update(); GenerateObj(); GenerateConst(); model.Write("model.lp"); }
private void OptimizeFASub() { _env_FlowAssignmentSub = new GRBEnv("FASub.log"); _grbModel_FlowAssignmentSub = new GRBModel(_env_FlowAssignmentSub); BuildVar_FA(); BuildObj_FA(); BuildConst_FA(); _grbModel_FlowAssignmentSub.Write("FASub.lp"); _grbModel_FlowAssignmentSub.Optimize(); OutputValue(); }
bool Solve() { _grbModel.Write("OrgModel.lp"); _grbModel.Optimize(); int status = _grbModel.Get(GRB.IntAttr.Status); int solution = _grbModel.Get(GRB.IntAttr.SolCount); if (status == GRB.Status.OPTIMAL || (status == GRB.Status.TIME_LIMIT && solution > 0)) { return(true); } else { return(false); } }
private void btnOi_Click(object sender, EventArgs e) { GRBEnv Ambiente = new GRBEnv(); GRBModel Modelo = new GRBModel(Ambiente); GRBVar x = Modelo.AddVar(0, 1, 0, GRB.BINARY, "x"); GRBVar y = Modelo.AddVar(0, 1, 0, GRB.BINARY, "y"); GRBVar z = Modelo.AddVar(0, 1, 0, GRB.BINARY, "z"); Modelo.SetObjective(x + y + 2 * z, GRB.MAXIMIZE); Modelo.AddConstr(x + 2 * y + 3 * z <= 4.0, "c0"); Modelo.AddConstr(x + y >= 1.0, "c1"); Modelo.Write("C:\\Teste\\ModeloAula.lp"); Modelo.Optimize(); MessageBox.Show("O valor da variável x é: " + x.X.ToString()); MessageBox.Show("O valor da variável y é: " + y.X.ToString()); MessageBox.Show("O valor da variável z é: " + z.X.ToString()); }
private void OptimizeSchemeSub() { _env_SchemeGenerateSub = new GRBEnv("SchemeSub.log"); _grbModel_SchemeGenerateSub = new GRBModel(_env_SchemeGenerateSub); BuildVar_SG(); BuildObj_SG(); BuildConst_SG(); _grbModel_SchemeGenerateSub.Write("SchemeSub.lp"); _grbModel_SchemeGenerateSub.Optimize(); OutputSolution_SchemeSub(); //_grbModel_SchemeGenerateSub.Dispose(); //_env_SchemeGenerateSub.Dispose(); }
static void Main(string[] args) { if (args.Length < 1) { Console.Out.WriteLine("Usage: tune_cs filename"); return; } try { GRBEnv env = new GRBEnv(); // Read model from file GRBModel model = new GRBModel(env, args[0]); // Set the TuneResults parameter to 1 model.GetEnv().Set(GRB.IntParam.TuneResults, 1); // Tune the model model.Tune(); // Get the number of tuning results int resultcount = model.Get(GRB.IntAttr.TuneResultCount); if (resultcount > 0) { // Load the tuned parameters into the model's environment model.GetTuneResult(0); // Write the tuned parameters to a file model.Write("tune.prm"); // Solve the model using the tuned parameters model.Optimize(); } // Dispose of model and environment model.Dispose(); env.Dispose(); } catch (GRBException e) { Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message); } }
static void SolvePrimal(GRBEnv env, HashSet <string> nodes_set, List <Arc> arcs) { GRBModel m = new GRBModel(env); Dictionary <string, GRBVar> distances = new Dictionary <string, GRBVar>(); // pi foreach (string node in nodes_set) { distances[node] = m.AddVar(0, GRB.INFINITY, 0, GRB.CONTINUOUS, "distance." + node); } m.Update(); distances[ORIGIN].Set(GRB.DoubleAttr.Obj, -1); distances[DESTINATION].Set(GRB.DoubleAttr.Obj, 1); m.Set(GRB.IntAttr.ModelSense, -1); Dictionary <Arc, GRBConstr> constrs = new Dictionary <Arc, GRBConstr>(); foreach (Arc a in arcs) { constrs[a] = m.AddConstr(distances[a.dest] <= distances[a.source] + a.length, "distance_con." + a.source + "." + a.dest); } m.Update(); m.Write("shortest_path.lp"); m.Optimize(); foreach (var pair in distances) { Console.WriteLine("distance to {0} is {1}", pair.Key, pair.Value.Get(GRB.DoubleAttr.X)); } foreach (var pair in constrs) { GRBConstr con = pair.Value; if (con.Get(GRB.DoubleAttr.Pi) > 0.5) { Console.WriteLine("Arc {0}, {1} is in shortest path", pair.Key.source, pair.Key.dest); } } Console.WriteLine("Length of shortest path is {0}", m.Get(GRB.DoubleAttr.ObjVal)); m.Dispose(); }
public void Optimize() { Data.LoadData(); GenerateInitialFeasibleSolution(); while (true) { _envMaster = new GRBEnv("CG_Model.log"); _grbModelMaster = new GRBModel(_envMaster); BuildVar(); GenerateConst(); _grbModelMaster.Write("CG_Model.lp"); _grbModelMaster.Optimize(); GetDual(); OutputSolution(); OptimizeSchemeSub(); OptimizeFASub(); bool IsActive = AddColumn(); _grbModelMaster.Dispose(); _envMaster.Dispose(); _grbModel_SchemeGenerateSub.Dispose(); _env_SchemeGenerateSub.Dispose(); _grbModel_FlowAssignmentSub.Dispose(); _env_FlowAssignmentSub.Dispose(); if (!IsActive) { break; } } }
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()); }
static void Main() { try{ // Sample data int groundSetSize = 10; double[] objCoef = new double[] { 32, 32, 15, 15, 6, 6, 1, 1, 1, 1 }; double[] knapsackCoef = new double[] { 16, 16, 8, 8, 4, 4, 2, 2, 1, 1 }; double Budget = 33; int e, status, nSolutions; // Create environment GRBEnv env = new GRBEnv("poolsearch_cs.log"); // Create initial model GRBModel model = new GRBModel(env); model.ModelName = "poolsearch_cs"; // Initialize decision variables for ground set: // x[e] == 1 if element e is chosen GRBVar[] Elem = model.AddVars(groundSetSize, GRB.BINARY); model.Set(GRB.DoubleAttr.Obj, Elem, objCoef, 0, groundSetSize); for (e = 0; e < groundSetSize; e++) { Elem[e].VarName = string.Format("El{0}", e); } // Constraint: limit total number of elements to be picked to be at most // Budget GRBLinExpr lhs = new GRBLinExpr(); for (e = 0; e < groundSetSize; e++) { lhs.AddTerm(knapsackCoef[e], Elem[e]); } model.AddConstr(lhs, GRB.LESS_EQUAL, Budget, "Budget"); // set global sense for ALL objectives model.ModelSense = GRB.MAXIMIZE; // Limit how many solutions to collect model.Parameters.PoolSolutions = 1024; // Limit the search space by setting a gap for the worst possible solution that will be accepted model.Parameters.PoolGap = 0.10; // do a systematic search for the k-best solutions model.Parameters.PoolSearchMode = 2; // save problem model.Write("poolsearch_cs.lp"); // Optimize model.Optimize(); // Status checking status = model.Status; if (status == GRB.Status.INF_OR_UNBD || status == GRB.Status.INFEASIBLE || status == GRB.Status.UNBOUNDED) { Console.WriteLine("The model cannot be solved " + "because it is infeasible or unbounded"); return; } if (status != GRB.Status.OPTIMAL) { Console.WriteLine("Optimization was stopped with status {0}", status); return; } // Print best selected set Console.WriteLine("Selected elements in best solution:"); Console.Write("\t"); for (e = 0; e < groundSetSize; e++) { if (Elem[e].X < .9) { continue; } Console.Write("El{0} ", e); } Console.WriteLine(); // Print number of solutions stored nSolutions = model.SolCount; Console.WriteLine("Number of solutions found: {0}", nSolutions); // Print objective values of solutions for (e = 0; e < nSolutions; e++) { model.Parameters.SolutionNumber = e; Console.Write("{0} ", model.PoolObjVal); if (e % 15 == 14) { Console.WriteLine(); } } Console.WriteLine(); // Print fourth best set if available if (nSolutions >= 4) { model.Parameters.SolutionNumber = 3; Console.WriteLine("Selected elements in fourth best solution:"); Console.Write("\t"); for (e = 0; e < groundSetSize; e++) { if (Elem[e].Xn < .9) { continue; } Console.Write("El{0} ", e); } Console.WriteLine(); } model.Dispose(); env.Dispose(); } catch (GRBException e) { Console.WriteLine("Error code: {0}. {1}", e.ErrorCode, e.Message); } }
static void Main() { try { // Example data: // e.g. {0, n+1, 2} means clause (x0 or ~x1 or x2) int[,] Clauses = new int[, ] { { 0, n + 1, 2 }, { 1, n + 2, 3 }, { 2, n + 3, 0 }, { 3, n + 0, 1 }, { n + 0, n + 1, 2 }, { n + 1, n + 2, 3 }, { n + 2, n + 3, 0 }, { n + 3, n + 0, 1 } }; int i, status; // Create environment GRBEnv env = new GRBEnv("genconstr_cs.log"); // Create initial model GRBModel model = new GRBModel(env); model.ModelName = "genconstr_cs"; // Initialize decision variables and objective GRBVar[] Lit = new GRBVar[NLITERALS]; GRBVar[] NotLit = new GRBVar[NLITERALS]; for (i = 0; i < NLITERALS; i++) { Lit[i] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, string.Format("X{0}", i)); NotLit[i] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, string.Format("notX{0}", i)); } GRBVar[] Cla = new GRBVar[NCLAUSES]; for (i = 0; i < NCLAUSES; i++) { Cla[i] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, string.Format("Clause{0}", i)); } GRBVar[] Obj = new GRBVar[NOBJ]; for (i = 0; i < NOBJ; i++) { Obj[i] = model.AddVar(0.0, 1.0, 1.0, GRB.BINARY, string.Format("Obj{0}", i)); } // Link Xi and notXi GRBLinExpr lhs; for (i = 0; i < NLITERALS; i++) { lhs = new GRBLinExpr(); lhs.AddTerm(1.0, Lit[i]); lhs.AddTerm(1.0, NotLit[i]); model.AddConstr(lhs, GRB.EQUAL, 1.0, string.Format("CNSTR_X{0}", i)); } // Link clauses and literals for (i = 0; i < NCLAUSES; i++) { GRBVar[] clause = new GRBVar[3]; for (int j = 0; j < 3; j++) { if (Clauses[i, j] >= n) { clause[j] = NotLit[Clauses[i, j] - n]; } else { clause[j] = Lit[Clauses[i, j]]; } } model.AddGenConstrOr(Cla[i], clause, string.Format("CNSTR_Clause{0}", i)); } // Link objs with clauses model.AddGenConstrMin(Obj[0], Cla, GRB.INFINITY, "CNSTR_Obj0"); lhs = new GRBLinExpr(); for (i = 0; i < NCLAUSES; i++) { lhs.AddTerm(1.0, Cla[i]); } model.AddGenConstrIndicator(Obj[1], 1, lhs, GRB.GREATER_EQUAL, 4.0, "CNSTR_Obj1"); // Set global objective sense model.ModelSense = GRB.MAXIMIZE; // Save problem model.Write("genconstr_cs.mps"); model.Write("genconstr_cs.lp"); // Optimize model.Optimize(); // Status checking status = model.Status; if (status == GRB.Status.INF_OR_UNBD || status == GRB.Status.INFEASIBLE || status == GRB.Status.UNBOUNDED) { Console.WriteLine("The model cannot be solved " + "because it is infeasible or unbounded"); return; } if (status != GRB.Status.OPTIMAL) { Console.WriteLine("Optimization was stopped with status {0}", status); return; } // Print result double objval = model.ObjVal; if (objval > 1.9) { Console.WriteLine("Logical expression is satisfiable"); } else if (objval > 0.9) { Console.WriteLine("At least four clauses can be satisfied"); } else { Console.WriteLine("Not even three clauses can be satisfied"); } // Dispose of model and environment model.Dispose(); env.Dispose(); } catch (GRBException e) { Console.WriteLine("Error code: {0}. {1}", e.ErrorCode, e.Message); } }
public override void PrintLp(object solver) { GRBModel grbSolver = solver as GRBModel; grbSolver.Write(output_file_name); }
static void Main(string[] args) { int n = 9; int s = 3; if (args.Length < 1) { Console.Out.WriteLine("Usage: sudoku_cs filename"); return; } try { GRBEnv env = new GRBEnv(); GRBModel model = new GRBModel(env); // Create 3-D array of model variables GRBVar[,,] vars = new GRBVar[n, n, n]; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { for (int v = 0; v < n; v++) { string st = "G_" + i.ToString() + "_" + j.ToString() + "_" + v.ToString(); vars[i, j, v] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, st); } } } // Add constraints GRBLinExpr expr; // Each cell must take one value for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { expr = 0.0; for (int v = 0; v < n; v++) { expr.AddTerm(1.0, vars[i, j, v]); } string st = "V_" + i.ToString() + "_" + j.ToString(); model.AddConstr(expr == 1.0, st); } } // Each value appears once per row for (int i = 0; i < n; i++) { for (int v = 0; v < n; v++) { expr = 0.0; for (int j = 0; j < n; j++) { expr.AddTerm(1.0, vars[i, j, v]); } string st = "R_" + i.ToString() + "_" + v.ToString(); model.AddConstr(expr == 1.0, st); } } // Each value appears once per column for (int j = 0; j < n; j++) { for (int v = 0; v < n; v++) { expr = 0.0; for (int i = 0; i < n; i++) { expr.AddTerm(1.0, vars[i, j, v]); } string st = "C_" + j.ToString() + "_" + v.ToString(); model.AddConstr(expr == 1.0, st); } } // Each value appears once per sub-grid for (int v = 0; v < n; v++) { for (int i0 = 0; i0 < s; i0++) { for (int j0 = 0; j0 < s; j0++) { expr = 0.0; for (int i1 = 0; i1 < s; i1++) { for (int j1 = 0; j1 < s; j1++) { expr.AddTerm(1.0, vars[i0 * s + i1, j0 * s + j1, v]); } } string st = "Sub_" + v.ToString() + "_" + i0.ToString() + "_" + j0.ToString(); model.AddConstr(expr == 1.0, st); } } } // Fix variables associated with pre-specified cells StreamReader sr = File.OpenText(args[0]); for (int i = 0; i < n; i++) { string input = sr.ReadLine(); for (int j = 0; j < n; j++) { int val = (int)input[j] - 48 - 1; // 0-based if (val >= 0) { vars[i, j, val].LB = 1.0; } } } // Optimize model model.Optimize(); // Write model to file model.Write("sudoku.lp"); double[,,] x = model.Get(GRB.DoubleAttr.X, vars); Console.WriteLine(); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { for (int v = 0; v < n; v++) { if (x[i, j, v] > 0.5) { Console.Write(v + 1); } } } Console.WriteLine(); } // Dispose of model and env model.Dispose(); env.Dispose(); } catch (GRBException e) { Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message); } }
static void Main() { try { // Sample data // Sets of days and workers string[] Shifts = new string[] { "Mon1", "Tue2", "Wed3", "Thu4", "Fri5", "Sat6", "Sun7", "Mon8", "Tue9", "Wed10", "Thu11", "Fri12", "Sat13", "Sun14" }; string[] Workers = new string[] { "Amy", "Bob", "Cathy", "Dan", "Ed", "Fred", "Gu", "Tobi" }; int nShifts = Shifts.Length; int nWorkers = Workers.Length; // Number of workers required for each shift double[] shiftRequirements = new double[] { 3, 2, 4, 4, 5, 6, 5, 2, 2, 3, 4, 6, 7, 5 }; // Worker availability: 0 if the worker is unavailable for a shift double[,] availability = new double[, ] { { 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1 }, { 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0 }, { 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1 }, { 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1 }, { 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1 }, { 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1 }, { 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } }; // Create environment GRBEnv env = new GRBEnv(); // Create initial model GRBModel model = new GRBModel(env); model.ModelName = "workforce5_cs"; // Initialize assignment decision variables: // x[w][s] == 1 if worker w is assigned to shift s. // This is no longer a pure assignment model, so we must // use binary variables. GRBVar[,] x = new GRBVar[nWorkers, nShifts]; for (int w = 0; w < nWorkers; ++w) { for (int s = 0; s < nShifts; ++s) { x[w, s] = model.AddVar(0, availability[w, s], 0, GRB.BINARY, string.Format("{0}.{1}", Workers[w], Shifts[s])); } } // Slack variables for each shift constraint so that the shifts can // be satisfied GRBVar[] slacks = new GRBVar[nShifts]; for (int s = 0; s < nShifts; ++s) { slacks[s] = model.AddVar(0, GRB.INFINITY, 0, GRB.CONTINUOUS, string.Format("{0}Slack", Shifts[s])); } // Variable to represent the total slack GRBVar totSlack = model.AddVar(0, GRB.INFINITY, 0, GRB.CONTINUOUS, "totSlack"); // Variables to count the total shifts worked by each worker GRBVar[] totShifts = new GRBVar[nWorkers]; for (int w = 0; w < nWorkers; ++w) { totShifts[w] = model.AddVar(0, GRB.INFINITY, 0, GRB.CONTINUOUS, string.Format("{0}TotShifts", Workers[w])); } GRBLinExpr lhs; // Constraint: assign exactly shiftRequirements[s] workers // to each shift s, plus the slack for (int s = 0; s < nShifts; ++s) { lhs = new GRBLinExpr(); lhs.AddTerm(1.0, slacks[s]); for (int w = 0; w < nWorkers; ++w) { lhs.AddTerm(1.0, x[w, s]); } model.AddConstr(lhs, GRB.EQUAL, shiftRequirements[s], Shifts[s]); } // Constraint: set totSlack equal to the total slack lhs = new GRBLinExpr(); lhs.AddTerm(-1.0, totSlack); for (int s = 0; s < nShifts; ++s) { lhs.AddTerm(1.0, slacks[s]); } model.AddConstr(lhs, GRB.EQUAL, 0, "totSlack"); // Constraint: compute the total number of shifts for each worker for (int w = 0; w < nWorkers; ++w) { lhs = new GRBLinExpr(); lhs.AddTerm(-1.0, totShifts[w]); for (int s = 0; s < nShifts; ++s) { lhs.AddTerm(1.0, x[w, s]); } model.AddConstr(lhs, GRB.EQUAL, 0, string.Format("totShifts{0}", Workers[w])); } // Constraint: set minShift/maxShift variable to less <=/>= to the // number of shifts among all workers GRBVar minShift = model.AddVar(0, GRB.INFINITY, 0, GRB.CONTINUOUS, "minShift"); GRBVar maxShift = model.AddVar(0, GRB.INFINITY, 0, GRB.CONTINUOUS, "maxShift"); model.AddGenConstrMin(minShift, totShifts, GRB.INFINITY, "minShift"); model.AddGenConstrMax(maxShift, totShifts, -GRB.INFINITY, "maxShift"); // Set global sense for ALL objectives model.ModelSense = GRB.MINIMIZE; // Set primary objective model.SetObjectiveN(totSlack, 0, 2, 1.0, 2.0, 0.1, "TotalSlack"); // Set secondary objective model.SetObjectiveN(maxShift - minShift, 1, 1, 1.0, 0, 0, "Fairness"); // Save problem model.Write("workforce5_cs.lp"); // Optimize int status = solveAndPrint(model, totSlack, nWorkers, Workers, totShifts); if (status != GRB.Status.OPTIMAL) { return; } // Dispose of model and environment model.Dispose(); env.Dispose(); } catch (GRBException e) { Console.WriteLine("Error code: {0}. {1}", e.ErrorCode, e.Message); } }
public void OptimalPlacement(ref List <Station> stationList, ref List <OHCAEvent> eventList) { int[,] a = new int[J, I]; for (int j = 0; j < J; j++) { for (int i = 0; i < I; i++) { a[j, i] = (Utils.GetDistance(stationList[i].lat, stationList[i].lon, eventList[j].lat, eventList[j].lon) < Utils.GOLDEN_TIME - 1.0 / 6.0) ? 1 : 0; } } try { GRBEnv env = new GRBEnv("Boutilier.log"); GRBModel model = new GRBModel(env); GRBVar[] y = new GRBVar[I]; for (int i = 0; i < I; i++) { y[i] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, "y_" + i); } GRBVar[, ] z = new GRBVar[J, I]; for (int j = 0; j < J; j++) { for (int i = 0; i < I; i++) { z[j, i] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, "z_" + i + "," + j); } } GRBLinExpr obj_expr = 0.0; for (int i = 0; i < I; i++) { obj_expr.AddTerm(1.0, y[i]); } model.SetObjective(obj_expr, GRB.MINIMIZE); GRBLinExpr bigExpr = 0.0; for (int j = 0; j < J; j++) { GRBLinExpr expr = 0.0; for (int i = 0; i < I; i++) { expr.AddTerm(1, z[j, i]); } bigExpr.Add(expr); model.AddConstr(expr, GRB.LESS_EQUAL, 1.0, "c0_" + j); } model.AddConstr(bigExpr, GRB.GREATER_EQUAL, f / 100.0 * J, "c1"); for (int j = 0; j < J; j++) { for (int i = 0; i < I; i++) { model.AddConstr(z[j, i], GRB.LESS_EQUAL, a[j, i] * y[i], "c2_" + i + "," + j); } } model.Write("model_b.lp"); model.Optimize(); int optimstatus = model.Status; if (optimstatus == GRB.Status.INF_OR_UNBD) { model.Parameters.Presolve = 0; model.Optimize(); optimstatus = model.Status; } if (optimstatus == GRB.Status.OPTIMAL) { } else if (optimstatus == GRB.Status.INFEASIBLE) { Console.WriteLine("Model is unbounded"); } else { Console.WriteLine("Optimization was stopped with status = " + optimstatus); } List <Station> tempList = new List <Station>(); CloneList(stationList, tempList); stationList.Clear(); for (int i = 0; i < I; i++) { if (y[i].Get(GRB.DoubleAttr.X) > 0) { stationList.Add(tempList[i]); } } I = stationList.Count; coverList = new List <List <int> >(); for (int i = 0; i < I; i++) { coverList.Add(new List <int>()); } for (int j = 0; j < J; j++) { for (int i = 0; i < I; i++) { if (z[j, i].Get(GRB.DoubleAttr.X) > 0) { coverList[i].Add(j); } } } model.Dispose(); env.Dispose(); } catch (GRBException e) { Console.WriteLine("Error code : " + e.ErrorCode + ", " + e.Message); } }
public double OptimalCoverage(int p, ref List <Station> stationList) { double res = -1; try { GRBEnv env = new GRBEnv(); GRBModel model = new GRBModel(env); GRBVar[] X = new GRBVar[m]; // number of drones launched from site j for (int j = 0; j < m; j++) { X[j] = model.AddVar(0.0, Double.PositiveInfinity, 0.0, GRB.INTEGER, "X_" + j); } GRBVar[] Z = new GRBVar[n]; // amount of total overall coverage received by demand unit i for (int i = 0; i < n; i++) { Z[i] = model.AddVar(0.0, Double.PositiveInfinity, 0.0, GRB.CONTINUOUS, "Z_" + i); } GRBVar[] Y = new GRBVar[n]; // amount of backup coverage received by demand unit i for (int i = 0; i < n; i++) { Y[i] = model.AddVar(0.0, Double.PositiveInfinity, 0.0, GRB.CONTINUOUS, "Y_" + i); } GRBVar[] W = new GRBVar[n]; // amount of primary coverage received by demand unit i for (int i = 0; i < n; i++) { W[i] = model.AddVar(0.0, Double.PositiveInfinity, 0.0, GRB.CONTINUOUS, "W_" + i); } GRBLinExpr obj_expr = 0.0; for (int i = 0; i < n; i++) { obj_expr.AddTerm(w, Y[i]); } for (int i = 0; i < n; i++) { obj_expr.AddTerm(1 - w, W[i]); } model.SetObjective(obj_expr, GRB.MAXIMIZE); for (int i = 0; i < n; i++) // sum_{j} {b_i,j * X_j} >= Z_i for i = 0, ..., n - 1 { GRBLinExpr expr = 0.0; foreach (int j in N[i]) { expr.AddTerm(b[i, j], X[j]); } expr.AddTerm(-1.0, Z[i]); model.AddConstr(expr, GRB.GREATER_EQUAL, 0.0, "c0_" + i); } for (int i = 0; i < n; i++) // Y_i <= Z_i - d_i { GRBLinExpr expr = 0.0; expr.AddTerm(-1.0, Z[i]); expr.AddTerm(1.0, Y[i]); model.AddConstr(expr, GRB.LESS_EQUAL, -demandList[i], "c1_" + i); } for (int i = 0; i < n; i++) // W_i <= Z_i { GRBLinExpr expr = 0.0; expr.AddTerm(-1.0, Z[i]); expr.AddTerm(1.0, W[i]); model.AddConstr(expr, GRB.LESS_EQUAL, 0, "c2_" + i); } for (int i = 0; i < n; i++) // W_i <= d_i { GRBLinExpr expr = 0.0; expr.AddTerm(1.0, W[i]); model.AddConstr(expr, GRB.LESS_EQUAL, demandList[i], "c3_" + i); } for (int i = 0; i < n; i++) // Z_i <= h * d_i { GRBLinExpr expr = 0.0; expr.AddTerm(1.0, Z[i]); model.AddConstr(expr, GRB.LESS_EQUAL, h * demandList[i], "c4_" + i); } GRBLinExpr p_expr = 0.0; for (int j = 0; j < m; j++) // sum_{j} {X_j} <= p { p_expr.AddTerm(1.0, X[j]); } model.AddConstr(p_expr, GRB.LESS_EQUAL, p, "c_p"); model.Write("model.lp"); model.Optimize(); int optimstatus = model.Status; if (optimstatus == GRB.Status.INF_OR_UNBD) { model.Parameters.Presolve = 0; model.Optimize(); optimstatus = model.Status; } if (optimstatus == GRB.Status.OPTIMAL) { double objval = model.ObjVal; res = objval; } else if (optimstatus == GRB.Status.INFEASIBLE) { Console.WriteLine("Model is unbounded"); } else { Console.WriteLine("Optimization was stopped with status = " + optimstatus); } int sum = 0; for (int l = 0; l < m; l++) { int numDrone = (int)X[l].Get(GRB.DoubleAttr.X); sum += numDrone; for (int k = 0; k < numDrone; k++) { stationList[l].droneList.Add(new Drone(stationList[l].stationID)); } if (/*DEBUG && */ numDrone > 0) { Console.WriteLine(numDrone + " drones at station " + l + ", which is at (" + stationList[l].lat + ", " + stationList[l].lon + ")"); } } for (int l = stationList.Count - 1; l >= 0; l--) { if (stationList[l].droneList.Count == 0) { stationList.RemoveAt(l); } } Console.WriteLine("sum = " + sum); model.Dispose(); env.Dispose(); } catch (GRBException e) { Console.WriteLine("Error code : " + e.ErrorCode + ", " + e.Message); } return(res); }
public GurobiSolver4(Crossword crossword) { var wordLengthHistogram = new Dictionary <int, double>() { { 2, 0 }, { 3, 18 }, { 4, 24 }, { 5, 20 }, { 6, 18 }, { 7, 12 }, { 8, 4 }, { 9, 1 }, { 10, 1 }, { 11, 1 }, { 12, 1 }, { 13, 0 }, { 14, 0 }, { 15, 0 }, }; const int maxWordLength = 15; const int minWordLength = 2; int sizeY = crossword.Grid.GetLength(0); int sizeX = crossword.Grid.GetLength(1); var requiredAmountOfLetters = wordLengthHistogram.Sum(wl => wl.Key * wl.Value) / 1.8d; int totalFields = sizeX * sizeY; for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { totalFields -= crossword.Grid[y, x] is Blocked ? 1 : 0; } } int amountQuestions = (int)Math.Round(0.22 * totalFields); var wordLengthRatio = requiredAmountOfLetters / (totalFields - amountQuestions); GRBEnv env = new GRBEnv(); GRBModel m = new GRBModel(env); m.GetEnv().Method = 0; // 0 = letter, 1 = question GRBVar[,] fields = new GRBVar[sizeY, sizeX]; // 0 = right, 1 = down GRBVar[,] questionType = new GRBVar[sizeY, sizeX]; // 0 = Down, then right // 1 = Left, then down // 2 = Right, then down // 3 = Up, then right // Mostly null, except for places down and right of a blocked field or y==0 or x==0 GRBVar[,,] specialQuestionType = new GRBVar[sizeY, sizeX, 4]; var specialQuestionUsed = new GRBLinExpr[sizeY, sizeX]; for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { // create a var for every non-blocked field if (!(crossword.Grid[y, x] is Blocked)) { fields[y, x] = m.AddVar(0, 1, 0, GRB.BINARY, "Field" + y + "_" + x); questionType[y, x] = m.AddVar(0, 1, 0, GRB.BINARY, "QType" + y + "_" + x); // Is null except for places down and right of a blocked field or y==0 or x==0 if (y == 0 || x == 0 || crossword.Grid[y - 1, x] is Blocked || crossword.Grid[y, x - 1] is Blocked) { for (int t = 0; t < 4; t++) { specialQuestionType[y, x, t] = m.AddVar(0, 1, 0, GRB.BINARY, "SpecialQType" + t + "_" + y + "_" + x); } specialQuestionUsed[y, x] = specialQuestionType[y, x, 0] + specialQuestionType[y, x, 1] + specialQuestionType[y, x, 2] + specialQuestionType[y, x, 3]; // Max 1 special type, can also be no special question m.AddConstr(specialQuestionUsed[y, x] <= 1, "MaxOneSpecialQuestion" + y + "_" + x); } } } } // TEST //m.AddConstr(specialQuestionType[0, 0, 0] == 1); GRBLinExpr allFieldsSum = new GRBLinExpr(); for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { if (crossword.Grid[y, x] is Blocked) { continue; } allFieldsSum += fields[y, x]; bool noQuestionToTheRightAllowed = false; bool noQuestionTowardsDownAllowed = false; if (x + minWordLength < sizeX && !crossword.HasBlock(y, x, y, x + minWordLength)) { // for right: if [0,0] is question, [0,1..3] must not be question var totalQuestionsHorizontal = fields.SumRange(y, x + 1, y, x + minWordLength); var isQuestionAndPointsRight = fields[y, x] + (1 - questionType[y, x]) - 1; if ((object)specialQuestionUsed[y, x] != null) { isQuestionAndPointsRight += (1 - specialQuestionUsed[y, x]) - 1; } for (int len = 1; len <= minWordLength; len++) { m.AddConstr(isQuestionAndPointsRight <= 1 - fields[y, x + len], "MinWordLength3" + y + "_" + x + "_right" + len); } } else { noQuestionToTheRightAllowed = true; } // for down: if (y + minWordLength < sizeY && !crossword.HasBlock(y, x, y + minWordLength, x)) { var totalQuestionsVertical = fields.SumRange(y + 1, x, y + minWordLength, x); var isQuestionAndPointsDown = fields[y, x] + questionType[y, x] - 1; if ((object)specialQuestionUsed[y, x] != null) { isQuestionAndPointsDown += (1 - specialQuestionUsed[y, x]) - 1; } for (int len = 1; len <= minWordLength; len++) { m.AddConstr(isQuestionAndPointsDown <= 1 - fields[y + len, x], "MinWordLength3" + y + "_" + x + "_down" + len); } } else { noQuestionTowardsDownAllowed = true; } bool atLeastOneSpecialAllowed = false; if ((object)specialQuestionUsed[y, x] != null) { // down, then right if (y + 1 < sizeY && x + minWordLength - 1 < sizeX && !crossword.HasBlock(y + 1, x, y + 1, x + minWordLength - 1)) { for (int len = 1; len <= minWordLength; len++) { m.AddConstr(fields[y, x] + specialQuestionType[y, x, 0] - 1 <= 1 - fields[y + 1, x + len - 1], "MinWordLength3" + y + "_" + x + "_downRight" + len); } if (x > 0) { m.AddConstr(fields[y, x] + specialQuestionType[y, x, 0] - 1 <= fields[y + 1, x - 1], "QuestionBeforeSQ" + y + "_" + x + "_downRight"); } atLeastOneSpecialAllowed = true; } else { m.AddConstr(specialQuestionType[y, x, 0] == 0, "NoSpecialQuestionAllowed" + y + "_" + x + "_downRight"); } // left, then down if (y + minWordLength - 1 < sizeY && x - 1 >= 0 && !crossword.HasBlock(y, x - 1, y + minWordLength - 1, x - 1)) { for (int len = 1; len <= minWordLength; len++) { m.AddConstr(fields[y, x] + specialQuestionType[y, x, 1] - 1 <= 1 - fields[y + len - 1, x - 1], "MinWordLength3" + y + "_" + x + "_leftDown" + len); } if (y > 0) { m.AddConstr(fields[y, x] + specialQuestionType[y, x, 1] - 1 <= fields[y - 1, x - 1], "QuestionBeforeSQ" + y + "_" + x + "_leftDown"); } atLeastOneSpecialAllowed = true; } else { m.AddConstr(specialQuestionType[y, x, 1] == 0, "NoSpecialQuestionAllowed" + y + "_" + x + "_leftDown"); } // right, then down if (y + minWordLength - 1 < sizeY && x + 1 < sizeX && !crossword.HasBlock(y, x + 1, y + minWordLength - 1, x + 1)) { for (int len = 1; len <= minWordLength; len++) { m.AddConstr(fields[y, x] + specialQuestionType[y, x, 2] - 1 <= 1 - fields[y + len - 1, x + 1], "MinWordLength3" + y + "_" + x + "_rightDown" + len); } if (y > 0) { m.AddConstr(fields[y, x] + specialQuestionType[y, x, 2] - 1 <= fields[y - 1, x + 1], "QuestionBeforeSQ" + y + "_" + x + "_rightDown"); } atLeastOneSpecialAllowed = true; } else { m.AddConstr(specialQuestionType[y, x, 2] == 0, "NoSpecialQuestionAllowed" + y + "_" + x + "_rightDown"); } // up, then right if (y - 1 >= 0 && x + minWordLength - 1 < sizeX && !crossword.HasBlock(y - 1, x, y - 1, x + minWordLength - 1)) { for (int len = 1; len <= minWordLength; len++) { m.AddConstr(fields[y, x] + specialQuestionType[y, x, 3] - 1 <= 1 - fields[y - 1, x + len - 1], "MinWordLength3" + y + "_" + x + "_upRight" + len); } if (x > 0) { m.AddConstr(fields[y, x] + specialQuestionType[y, x, 3] - 1 <= fields[y - 1, x - 1], "QuestionBeforeSQ" + y + "_" + x + "_upRight"); } atLeastOneSpecialAllowed = true; } else { m.AddConstr(specialQuestionType[y, x, 3] == 0, "NoSpecialQuestionAllowed" + y + "_" + x + "_upRight"); } if (!atLeastOneSpecialAllowed) { m.AddConstr(specialQuestionUsed[y, x] == 0, "NoSpecialQuestionAllowedAtALl" + y + "_" + x); } } if (noQuestionToTheRightAllowed && noQuestionTowardsDownAllowed) { if (!atLeastOneSpecialAllowed) { m.AddConstr(fields[y, x] == 0, "NoQuestionAllowed" + y + "_" + x); } else { m.AddConstr(specialQuestionUsed[y, x] == 1, "OnlySpecialQuestionAllowed" + y + "_" + x); } } else { if (noQuestionToTheRightAllowed) { m.AddConstr(questionType[y, x] == 1, "QuestionCantPointRight" + y + "_" + x); } if (noQuestionTowardsDownAllowed) { m.AddConstr(questionType[y, x] == 0, "QuestionCantPointDown" + y + "_" + x); } } // max word length constraints // for right: if [0,0] is question, [0,1..maxLength+1] must have at least another question field if (x + maxWordLength + 1 < sizeX && !crossword.HasBlock(y, x, y, x + maxWordLength + 1)) { var allHorizontalFields = new GRBLinExpr(); for (int xi = 1; xi <= maxWordLength + 1; xi++) { allHorizontalFields += fields[y, x + xi]; } if ((object)specialQuestionUsed[y, x] != null) { m.AddConstr(fields[y, x] + (1 - questionType[y, x]) + (1 - specialQuestionUsed[y, x]) - 2 <= allHorizontalFields, "MaxLengthHorizontal" + y + "_" + x); } else { m.AddConstr(fields[y, x] + (1 - questionType[y, x]) - 1 <= allHorizontalFields, "MaxLengthHorizontal" + y + "_" + x); } } // for down: if (y + maxWordLength + 1 < sizeY && !crossword.HasBlock(y, x, y + maxWordLength + 1, x)) { var fieldsSum = fields.SumRange(y + 1, x, y + maxWordLength + 1, x); if ((object)specialQuestionUsed[y, x] != null) { m.AddConstr(fields[y, x] + questionType[y, x] + (1 - specialQuestionUsed[y, x]) - 2 <= fieldsSum, "MaxLengthVertical" + y + "_" + x); } else { m.AddConstr(fields[y, x] + questionType[y, x] - 1 <= fieldsSum, "MaxLengthVertical" + y + "_" + x); } } if ((object)specialQuestionUsed[y, x] != null) { // down, then right if (y + 1 < sizeY && x + maxWordLength < sizeX && !crossword.HasBlock(y + 1, x, y + 1, x + maxWordLength)) { var fieldsSum = fields.SumRange(y + 1, x, y + 1, x + maxWordLength); m.AddConstr(fields[y, x] + specialQuestionType[y, x, 0] - 1 <= fieldsSum, "MaxLengthSpecialQuestion0_" + y + "_" + x); // if there is a normal field to the left of the word, it has to be a question if (x - 1 >= 0 && !crossword.HasBlock(y + 1, x - 1)) { m.AddConstr(fields[y, x] + specialQuestionType[y, x, 0] - 1 <= fields[y + 1, x - 1], "QuestionRequiredBeforeSpecialQuestion0Word_" + y + "_" + x); } } // left, then down if (y + maxWordLength < sizeY && x - 1 >= 0 && !crossword.HasBlock(y, x - 1, y + maxWordLength, x - 1)) { var fieldsSum = fields.SumRange(y, x - 1, y + maxWordLength, x - 1); m.AddConstr(fields[y, x] + specialQuestionType[y, x, 1] - 1 <= fieldsSum, "MaxLengthSpecialQuestion1_" + y + "_" + x); if (y - 1 >= 0 && !crossword.HasBlock(y - 1, x - 1)) { m.AddConstr(fields[y, x] + specialQuestionType[y, x, 0] - 1 <= fields[y - 1, x - 1], "QuestionRequiredBeforeSpecialQuestion1Word_" + y + "_" + x); } } // right, then down if (y + maxWordLength < sizeY && x + 1 < sizeX && !crossword.HasBlock(y, x + 1, y + maxWordLength, x + 1)) { var fieldsSum = fields.SumRange(y, x + 1, y + maxWordLength, x + 1); m.AddConstr(fields[y, x] + specialQuestionType[y, x, 2] - 1 <= fieldsSum, "MaxLengthSpecialQuestion2_" + y + "_" + x); if (y - 1 >= 0 && !crossword.HasBlock(y - 1, x + 1)) { m.AddConstr(fields[y, x] + specialQuestionType[y, x, 0] - 1 <= fields[y - 1, x + 1], "QuestionRequiredBeforeSpecialQuestion2Word_" + y + "_" + x); } } // up, then right if (y - 1 >= 0 && x + maxWordLength < sizeX && !crossword.HasBlock(y - 1, x, y - 1, x + maxWordLength)) { var fieldsSum = fields.SumRange(y - 1, x, y - 1, x + maxWordLength); m.AddConstr(fields[y, x] + specialQuestionType[y, x, 3] - 1 <= fieldsSum, "MaxLengthSpecialQuestion3_" + y + "_" + x); if (x - 1 >= 0 && !crossword.HasBlock(y - 1, x - 1)) { m.AddConstr(fields[y, x] + specialQuestionType[y, x, 0] - 1 <= fields[y - 1, x - 1], "QuestionRequiredBeforeSpecialQuestion3Word_" + y + "_" + x); } } } } } // Does a field belong to zero, one or two questions? var partOfAWord = new GRBLinExpr[sizeY, sizeX, 6]; for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { // this constraint doesn't work for [0,0] if (crossword.HasBlock(y, x)) { continue; } // Is this field attached to a question on the left, pointing right? var attachedToHorizontalQuestion = m.AddVar(0, 1, 0, GRB.BINARY, "attachedToHorizontalQuestion" + y + "_" + x); for (int len = 1; len <= maxWordLength; len++) { if (x - len < 0 || crossword.HasBlock(y, x - len, y, x)) { continue; } var isQuestionAndPointsRight = fields[y, x - len] + (1 - questionType[y, x - len]); if ((object)specialQuestionUsed[y, x - len] != null) { isQuestionAndPointsRight += (1 - specialQuestionUsed[y, x - len]) - 1; } var questionsInbetween = fields.SumRange(y, x - len + 1, y, x); m.AddConstr(attachedToHorizontalQuestion >= isQuestionAndPointsRight - 1 - questionsInbetween, "attachedToHorizontalQuestionConstraint0_" + y + "_" + x); // 0 IF first question is not pointing right OR there is no question to the left // firstQuestion ==> total fields < 2 if ((object)specialQuestionUsed[y, x - len] != null) { m.AddConstr(attachedToHorizontalQuestion <= questionsInbetween + (1 - fields[y, x - len]) + 1 - (questionType[y, x - len] + specialQuestionUsed[y, x - len]) * 0.5, "attachedToHorizontalQuestionConstraint1_" + y + "_" + x); // the first question but DOESNT look right } else { m.AddConstr(attachedToHorizontalQuestion <= questionsInbetween + (1 - fields[y, x - len]) + 1 - questionType[y, x - len], "attachedToHorizontalQuestionConstraint2_" + y + "_" + x); // the first question but DOESNT look right } } var questionsToTheLeft = new GRBLinExpr(); int qlCount = 0; for (int len = 0; len <= maxWordLength; len++) { if (x - len < 0 || crossword.HasBlock(y, x - len, y, x)) { continue; } questionsToTheLeft += fields[y, x - len]; qlCount++; } if (qlCount > 0) { m.AddConstr(attachedToHorizontalQuestion <= questionsToTheLeft, "attachedToHorizontalQuestionConstraint4_" + y + "_" + x); } // Is this field attached to a question pointing towards down? var attachedToVerticalQuestion = m.AddVar(0, 1, 0, GRB.BINARY, "attachedToVerticalQuestion" + y + "_" + x); for (int len = 1; len <= maxWordLength; len++) { if (y - len < 0 || crossword.HasBlock(y - len, x, y, x)) { continue; } var isQuestionAndPointsDown = fields[y - len, x] + questionType[y - len, x]; if ((object)specialQuestionUsed[y - len, x] != null) { isQuestionAndPointsDown += (1 - specialQuestionUsed[y - len, x]) - 1; } var questionsInbetween = fields.SumRange(y - len + 1, x, y, x); m.AddConstr(attachedToVerticalQuestion >= isQuestionAndPointsDown - 1 - questionsInbetween, "attachedToVerticalQuestionConstraint0_" + y + "_" + x); if ((object)specialQuestionUsed[y - len, x] != null) { m.AddConstr(attachedToVerticalQuestion <= questionsInbetween + (1 - fields[y - len, x]) + 1 - (1 - questionType[y - len, x] + specialQuestionUsed[y - len, x]) * 0.5, "attachedToVerticalQuestionConstraint1_" + y + "_" + x); // the first question but DOESNT look down OR IS specialquestion } else { m.AddConstr(attachedToVerticalQuestion <= questionsInbetween + (1 - fields[y - len, x]) + 1 - (1 - questionType[y - len, x]), "attachedToVerticalQuestionConstraint2_" + y + "_" + x); // the first question but DOESNT look down OR IS specialquestion } } var questionsTowardsDown = new GRBLinExpr(); int qdCount = 0; for (int len = 0; len <= maxWordLength; len++) { if (y - len < 0 || crossword.HasBlock(y - len, x, y, x)) { continue; } questionsTowardsDown += fields[y - len, x]; qdCount++; } if (qdCount > 0) { m.AddConstr(attachedToVerticalQuestion <= questionsTowardsDown, "attachedToVerticalQuestionConstraint3_" + y + "_" + x); } var attachedToSpecialQuestions = new GRBLinExpr[4]; var spAll = new GRBLinExpr(); for (int type = 0; type < 4; type++) { attachedToSpecialQuestions[type] = AttachedToSpecialQuestion(y, x, type, crossword, m, sizeX, sizeY, maxWordLength, fields, specialQuestionUsed, specialQuestionType); if ((object)attachedToSpecialQuestions[type] != null) { spAll += attachedToSpecialQuestions[type]; } } // if attached to horizontal question, can't be attached to horizontal sq (0, 3) if ((object)attachedToSpecialQuestions[0] != null) { m.AddConstr((1 - attachedToHorizontalQuestion) >= attachedToSpecialQuestions[0], "noHorizontalOverlap1_" + y + "_" + x); } if ((object)attachedToSpecialQuestions[3] != null) { m.AddConstr((1 - attachedToHorizontalQuestion) >= attachedToSpecialQuestions[3], "noHorizontalOverlap2_" + y + "_" + x); } // give preference to one horizontal kind of sq if ((object)attachedToSpecialQuestions[0] != null && (object)attachedToSpecialQuestions[3] != null) { m.AddConstr((1 - attachedToSpecialQuestions[0]) >= attachedToSpecialQuestions[3], "noHorizontalOverlap3_" + y + "_" + x); } // if attached to vertical question, can't be attached to vertical sq (1, 2) if ((object)attachedToSpecialQuestions[1] != null) { m.AddConstr((1 - attachedToVerticalQuestion) >= attachedToSpecialQuestions[1], "noVerticalOverlap1_" + y + "_" + x); } if ((object)attachedToSpecialQuestions[2] != null) { m.AddConstr((1 - attachedToVerticalQuestion) >= attachedToSpecialQuestions[2], "noVerticalOverlap2_" + y + "_" + x); } // give preference to one horizontal kind of sq if ((object)attachedToSpecialQuestions[1] != null && (object)attachedToSpecialQuestions[2] != null) { m.AddConstr((1 - attachedToSpecialQuestions[1]) >= attachedToSpecialQuestions[2], "noVerticalOverlap3_" + y + "_" + x); } var c = m.AddConstr(attachedToHorizontalQuestion + attachedToVerticalQuestion + spAll >= 1 - fields[y, x], "AttachedToQuestionConstraint_" + y + "_" + x); //c.Lazy = 1; partOfAWord[y, x, 0] = attachedToHorizontalQuestion; partOfAWord[y, x, 1] = attachedToVerticalQuestion; partOfAWord[y, x, 2] = attachedToSpecialQuestions[0]; partOfAWord[y, x, 3] = attachedToSpecialQuestions[1]; partOfAWord[y, x, 4] = attachedToSpecialQuestions[2]; partOfAWord[y, x, 5] = attachedToSpecialQuestions[3]; } } // right now, [0,0] can only be a question //if (!crossword.HasBlock(0, 0)) m.AddConstr(fields[0, 0] == 1); // and similarly the bottom 3x3 can only be letters for (int y = sizeY - minWordLength; y < sizeY; y++) { for (int x = sizeX - minWordLength; x < sizeX; x++) { if (!crossword.HasBlock(y, x)) { m.AddConstr(fields[y, x] == 0, "BottomOnlyLetters_" + y + "_" + x); } } } // Objective: // questions should be around ~22% (allFieldsSum ~= amountQuestions) /*int tolerance = (int)(amountQuestions * 0.1); * m.AddConstr(allFieldsSum >= amountQuestions - tolerance, "amountOfQuestionsTolerance_1"); * m.AddConstr(allFieldsSum <= amountQuestions + tolerance, "amountOfQuestionsTolerance_2");*/ m.AddConstr(allFieldsSum == amountQuestions); // uncrossed var partOfWordTotals = new GRBLinExpr[sizeY, sizeX]; for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { if (!crossword.HasBlock(y, x)) //(x >= 1 || y >= 1) && { var partOfWordTotal = new GRBLinExpr(); for (int t = 0; t < 6; t++) { if ((object)partOfAWord[y, x, t] != null) { partOfWordTotal += partOfAWord[y, x, t]; } } partOfWordTotals[y, x] = partOfWordTotal; } } } for (int y = 0; y < sizeY - 1; y++) { for (int x = 0; x < sizeX - 1; x++) { if (!crossword.HasBlock(y, x)) //(x >= 1 || y >= 1) && { if (!crossword.HasBlock(y + 1, x)) { m.AddConstr(partOfWordTotals[y, x] + partOfWordTotals[y + 1, x] >= (1 - fields[y, x] - fields[y + 1, x]) * 3, "noUncrossedFields" + y + "_" + x); } if (!crossword.HasBlock(y, x + 1)) { m.AddConstr(partOfWordTotals[y, x] + partOfWordTotals[y, x + 1] >= (1 - fields[y, x] - fields[y, x + 1]) * 3, "noUncrossedFields" + y + "_" + x); } } } } // penalty for nearby uncrossed letters (dead fields) /*var deadFieldPenalty = new GRBLinExpr(); * for (int y = 0; y < sizeY; y++) * { * for (int x = 0; x < sizeX; x++) * { * var hby = y - 1 >= 0 && !crossword.HasBlock(y - 1, x); * var hbx = x - 1 >= 0 && !crossword.HasBlock(y, x - 1); * if (!crossword.HasBlock(y, x) && (hby || hbx)) * { * var isDeadArea = m.AddVar(0, 1, 0, GRB.BINARY, "isDeadArea" + y + "_" + x); * if (hby) m.AddConstr(isDeadArea >= uncrossedLetters[y, x] + uncrossedLetters[y - 1, x] - 1, "deadAreaConstr1" + y + "_" + x); * if (hbx) m.AddConstr(isDeadArea >= uncrossedLetters[y, x] + uncrossedLetters[y, x - 1] - 1, "deadAreaConstr2" + y + "_" + x); * m.AddConstr(isDeadArea <= uncrossedLetters[y, x]); * if (hby && hbx) * m.AddConstr(isDeadArea <= uncrossedLetters[y - 1, x] + uncrossedLetters[y, x - 1], "deadAreaConstr3" + y + "_" + x); * else if (hby) * m.AddConstr(isDeadArea <= uncrossedLetters[y - 1, x], "deadAreaConstr4" + y + "_" + x); * else if (hbx) * m.AddConstr(isDeadArea <= uncrossedLetters[y, x - 1], "deadAreaConstr5" + y + "_" + x); * deadFieldPenalty += isDeadArea; * } * } * }*/ // ideal histogram comparison //var wordHistogramDifferences = new GRBLinExpr(); var wlTotals = new Dictionary <int, GRBLinExpr>(); foreach (var wl in wordLengthHistogram.Keys) { var total = new GRBLinExpr(); for (int y = 0; y + wl - 1 < sizeY; y++) { for (int x = 0; x < sizeX; x++) { if (crossword.HasBlock(y, x, y + wl - 1, x)) { continue; } // true if field-1 is question or start AND field + wl (after word) is question or end var hasLength = m.AddVar(0, 1, 0, GRB.BINARY, "hasLenVert" + wl + "__" + y + "_" + x); var sum = fields.SumRange(y, x, y + wl - 1, x); // no questions inbetween for (int i = 0; i < wl; i++) { m.AddConstr(hasLength <= 1 - fields[y + i, x]); } // question at end if (y + wl < sizeY && !crossword.HasBlock(y + wl, x)) { sum += (1 - fields[y + wl, x]); m.AddConstr(hasLength <= fields[y + wl, x]); } // question at start if (y - 1 >= 0 && !crossword.HasBlock(y - 1, x)) { sum += (1 - fields[y - 1, x]); m.AddConstr(hasLength <= fields[y - 1, x]); } // counts if a letter is attached to a horizontal question var qsum = new GRBLinExpr(); if ((object)partOfAWord[y, x, 1] != null) { qsum += partOfAWord[y, x, 1]; } if ((object)partOfAWord[y, x, 3] != null) { qsum += partOfAWord[y, x, 3]; } if ((object)partOfAWord[y, x, 4] != null) { qsum += partOfAWord[y, x, 4]; } sum += 1 - qsum; m.AddConstr(hasLength <= qsum); m.AddConstr(hasLength >= 1 - sum); total += hasLength; } } for (int y = 0; y < sizeY; y++) { for (int x = 0; x + wl - 1 < sizeX; x++) { if (crossword.HasBlock(y, x, y, x + wl - 1)) { continue; } var hasLength = m.AddVar(0, 1, 0, GRB.BINARY, "hasLenHoriz" + wl + "__" + y + "_" + x); var sum = fields.SumRange(y, x, y, x + wl - 1); // no questions inbetween for (int i = 0; i < wl; i++) { m.AddConstr(hasLength <= 1 - fields[y, x + i]); } // question at end if (x + wl < sizeX && !crossword.HasBlock(y, x + wl)) { sum += (1 - fields[y, x + wl]); m.AddConstr(hasLength <= fields[y, x + wl]); } // question at start if (x - 1 >= 0 && !crossword.HasBlock(y, x - 1)) { sum += (1 - fields[y, x - 1]); m.AddConstr(hasLength <= fields[y, x - 1]); } // counts if a letter is attached to a horizontal question var qsum = new GRBLinExpr(); if ((object)partOfAWord[y, x, 0] != null) { qsum += partOfAWord[y, x, 0]; } if ((object)partOfAWord[y, x, 2] != null) { qsum += partOfAWord[y, x, 2]; } if ((object)partOfAWord[y, x, 5] != null) { qsum += partOfAWord[y, x, 5]; } sum += 1 - qsum; m.AddConstr(hasLength <= qsum); m.AddConstr(hasLength >= 1 - sum); total += hasLength; } } if (wl <= 9) { wlTotals.Add(wl, total); } else { wlTotals[9] += total; } } var wlPenalty = new GRBLinExpr(); var wordCounts = m.AddVars(8, 0, amountQuestions * 2, GRB.INTEGER, "amount"); foreach (var wl in wlTotals.Keys) { var input = wordCounts[wl - 2]; m.AddConstr(input == wlTotals[wl]); var absRes = m.AddVar(0, 100, 0, GRB.CONTINUOUS, "absRes"); Console.WriteLine(wl == 9 ? 4 : wordLengthHistogram[wl]); var percentageDiff = input * (100d / amountQuestions) - (wl == 9 ? 4 : wordLengthHistogram[wl]); m.AddConstr(percentageDiff <= absRes, "absPos"); m.AddConstr(-percentageDiff <= absRes, "absNeg"); wlPenalty += absRes; } wlPenalty *= (1d / 8); // question field clusters // in a field of 2x2, minimize the nr of fields where there are 2-4 questions resp. maximize 0-1 questions //var clusterPenalty = new GRBLinExpr(); int area = 3; for (int y = 0; y < sizeY - (area - 1); y++) { for (int x = 0; x < sizeX - (area - 1); x++) { var clusterTotal = new GRBLinExpr(); int ct = 0; for (int i = 0; i < area; i++) { for (int j = 0; j < area; j++) { if (crossword.HasBlock(y + i, x + j) || (i == 1 && j == 1)) { continue; } clusterTotal += fields[y + i, x + j]; ct++; } } if (ct >= 2 && !crossword.HasBlock(y + 1, x + 1)) { //var varClusterTotalPenalty = m.AddVar(0, 1, 0, GRB.BINARY, "varClusterTotalPenalty" + y + "_" + x); // 0-1 = good, 2-4 = bad m.AddConstr(clusterTotal - 1 <= (1 - fields[y + 1, x + 1]) * 8, "cluster" + y + "_" + x); /*m.AddConstr(varClusterTotalPenalty <= clusterTotal * 0.5, "clusterPenaltyConstr1_" + y + "_" + x); * m.AddConstr(varClusterTotalPenalty >= (clusterTotal - 1) * (1d / 3), "clusterPenaltyConstr2_" + y + "_" + x); * clusterPenalty += varClusterTotalPenalty;*/ } } } //m.AddConstr(deadFieldPenalty <= 30); //amountOfQuestionsRating * (100d / sizeX / sizeY) + manyCrossedWords + + wordHistogramDifferences // clusterPenalty * 100 m.SetObjective(wlPenalty, GRB.MINIMIZE); m.SetCallback(new GRBMipSolCallback(crossword, fields, questionType, specialQuestionType, true, wordCounts)); // Insert previous solution /*var cwdCheck = new Crossword(@"C:\Users\Roman Bolzern\Documents\GitHub\Crossword\docs\15x15_1_noDoubles.cwg"); * cwdCheck.Draw(); * for (int y = 0; y < cwdCheck.Grid.GetLength(0); y++) * { * for (int x = 0; x < cwdCheck.Grid.GetLength(1); x++) * { * if (cwdCheck.Grid[y, x] is Question) * { * m.AddConstr(fields[y, x] == 1); * var q = (Question)cwdCheck.Grid[y, x]; * if (q.Arrow == Question.ArrowType.Right) * { * m.AddConstr(questionType[y, x] == 0); * if ((object)specialQuestionType[y, x, 0] != null) * m.AddConstr(specialQuestionType[y, x, 0] + specialQuestionType[y, x, 1] + specialQuestionType[y, x, 2] + specialQuestionType[y, x, 3] == 0); * } * else if (q.Arrow == Question.ArrowType.Down) * { * m.AddConstr(questionType[y, x] == 1); * if ((object)specialQuestionType[y, x, 0] != null) * m.AddConstr(specialQuestionType[y, x, 0] + specialQuestionType[y, x, 1] + specialQuestionType[y, x, 2] + specialQuestionType[y, x, 3] == 0); * } * else if (q.Arrow == Question.ArrowType.DownRight) * { * m.AddConstr(specialQuestionType[y, x, 0] == 1); * } * else if (q.Arrow == Question.ArrowType.LeftDown) * { * m.AddConstr(specialQuestionType[y, x, 1] == 1); * } * else if (q.Arrow == Question.ArrowType.RightDown) * { * m.AddConstr(specialQuestionType[y, x, 2] == 1); * } * else if (q.Arrow == Question.ArrowType.UpRight) * { * m.AddConstr(specialQuestionType[y, x, 3] == 1); * } * } * else if (cwdCheck.Grid[y, x] is Letter) * { * m.AddConstr(fields[y, x] == 0); * } * } * }*/ m.Optimize(); m.ComputeIIS(); m.Write("model.ilp"); m.Dispose(); env.Dispose(); }
public GurobiSolver2(Crossword crossword) { var wordLengthHistogram = new Dictionary <int, double>() { { 3, 18 }, { 4, 24 }, { 5, 20 }, { 6, 18 }, { 7, 12 }, { 8, 4 }, { 9, 4 }, }; const int maxWordLength = 9; int sizeY = crossword.Grid.GetLength(0); int sizeX = crossword.Grid.GetLength(1); var requiredAmountOfLetters = wordLengthHistogram.Sum(wl => wl.Key * wl.Value) / 1.8d; int totalFields = sizeX * sizeY; for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { totalFields -= crossword.Grid[y, x] is Blocked ? 1 : 0; } } int amountQuestions = (int)Math.Round(0.22 * totalFields); var wordLengthRatio = requiredAmountOfLetters / (totalFields - amountQuestions); GRBEnv env = new GRBEnv(); GRBModel m = new GRBModel(env); // 0 = letter, 1 = question GRBVar[,] fields = new GRBVar[sizeY, sizeX]; // 0 = right, 1 = down GRBVar[,] questionType = new GRBVar[sizeY, sizeX]; for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { // create a var for every non-blocked field if (!(crossword.Grid[y, x] is Blocked)) { fields[y, x] = m.AddVar(0, 1, 0, GRB.BINARY, "Field" + x + "_" + y); questionType[y, x] = m.AddVar(0, 1, 0, GRB.BINARY, "QType" + x + "_" + y); } } } GRBLinExpr allFieldsSum = new GRBLinExpr(); // count word lengths (to compare with histogram) var lengths = new Dictionary <int, GRBLinExpr>(); foreach (var l in wordLengthHistogram.Keys) { lengths.Add(l, new GRBLinExpr()); } var partOfAWord = new GRBLinExpr[sizeY, sizeX]; for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { partOfAWord[y, x] = new GRBLinExpr(); } } for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { allFieldsSum += fields[y, x]; foreach (var l in wordLengthHistogram.Keys) { if (x + l < sizeX) { // + 1 if the following fields are all words and the last one is not a word (question or outside grid) var wordCount = new GRBLinExpr(); for (int i = 1; i <= l; i++) { wordCount += fields[y, x + i]; } // last field is question or outside? var lastFieldIsNoWord = m.AddVar(0, 1, 0, GRB.BINARY, "lastFieldIsNoWord" + y + "_" + x + "_" + l); if (x + l + 1 < sizeX) { m.AddConstr(lastFieldIsNoWord == fields[y, x + l + 1], "lastFieldIsInGridAndIsQuestion" + y + "_" + x + "_" + l); } else { m.AddConstr(lastFieldIsNoWord == 1, "lastFieldOutsideGrid" + y + "_" + x + "_" + l); } // https://cs.stackexchange.com/questions/51025/cast-to-boolean-for-integer-linear-programming var hasCorrectWordCount = m.AddVar(0, 1, 0, GRB.BINARY, "hasCorrect" + y + "_" + x + "_" + l); var hcTemp = m.AddVar(0, 1, 0, GRB.BINARY, "hasCorrectTemp" + y + "_" + x + "_" + l); var worddiff = wordCount - l; m.AddConstr(-sizeX * hasCorrectWordCount <= worddiff, "ToBoolean" + y + "_" + x + "_" + l + "_1"); m.AddConstr(worddiff <= sizeX * hasCorrectWordCount, "ToBoolean" + y + "_" + x + "_" + l + "_2"); m.AddConstr(0.001 * hasCorrectWordCount - (sizeX + 0.001) * hcTemp <= worddiff, "ToBoolean" + y + "_" + x + "_" + l + "_3"); m.AddConstr(worddiff <= -0.001 * hasCorrectWordCount + (sizeX + 0.001) * (1 - hcTemp), "ToBoolean" + y + "_" + x + "_" + l + "_4"); // firstFieldIsQuestion AND questionIsHorizontal AND hasCorrectWordCount AND lastFieldIsNoWord var allConditionsMet = m.AddVar(0, 1, 0, GRB.BINARY, "allCondsMet" + y + "_" + x + "_" + l); var condVal = fields[y, x] + (1 - questionType[y, x]) + hasCorrectWordCount + lastFieldIsNoWord - 3; m.AddConstr(allConditionsMet >= condVal, "AllConditionsAND" + y + "_" + x + "_" + l + "_1"); m.AddConstr(allConditionsMet <= fields[y, x], "AllConditionsAND" + y + "_" + x + "_" + l + "_2"); m.AddConstr(allConditionsMet <= (1 - questionType[y, x]), "AllConditionsAND" + y + "_" + x + "_" + l + "_3"); m.AddConstr(allConditionsMet <= hasCorrectWordCount, "AllConditionsAND" + y + "_" + x + "_" + l + "_4"); m.AddConstr(allConditionsMet <= lastFieldIsNoWord, "AllConditionsAND" + y + "_" + x + "_" + l + "_5"); lengths[l] += allConditionsMet; // If all conditions are met, all letters are part of a word for (int i = 1; i <= l; i++) { partOfAWord[y, x + i] += allConditionsMet; } } if (y + l < sizeY) { // + 1 if the following fields are all words and the last one is not a word (question or outside grid) var wordCount = new GRBLinExpr(); for (int i = 1; i <= l; i++) { wordCount += fields[y + i, x]; } // last field is question or outside? var lastFieldIsNoWord = m.AddVar(0, 1, 0, GRB.BINARY, "lastFieldIsNoWord" + y + "_" + x + "_" + l); if (y + l + 1 < sizeY) { m.AddConstr(lastFieldIsNoWord == fields[y + l + 1, x], "lastFieldIsInGridAndIsQuestion" + y + "_" + x + "_" + l); } else { m.AddConstr(lastFieldIsNoWord == 1, "lastFieldOutsideGrid" + y + "_" + x + "_" + l); } // https://cs.stackexchange.com/questions/51025/cast-to-boolean-for-integer-linear-programming var hasCorrectWordCount = m.AddVar(0, 1, 0, GRB.BINARY, "hasCorrect" + y + "_" + x + "_" + l); var hcTemp = m.AddVar(0, 1, 0, GRB.BINARY, "hasCorrectTemp" + y + "_" + x + "_" + l); var worddiff = wordCount - l; m.AddConstr(-sizeY * hasCorrectWordCount <= worddiff, "ToBoolean" + y + "_" + x + "_" + l + "_1"); m.AddConstr(worddiff <= sizeY * hasCorrectWordCount, "ToBoolean" + y + "_" + x + "_" + l + "_2"); m.AddConstr(0.001 * hasCorrectWordCount - (sizeY + 0.001) * hcTemp <= worddiff, "ToBoolean" + y + "_" + x + "_" + l + "_3"); m.AddConstr(worddiff <= -0.001 * hasCorrectWordCount + (sizeY + 0.001) * (1 - hcTemp), "ToBoolean" + y + "_" + x + "_" + l + "_4"); // firstFieldIsQuestion AND questionIsHorizontal AND hasCorrectWordCount AND lastFieldIsNoWord var allConditionsMet = m.AddVar(0, 1, 0, GRB.BINARY, "allCondsMet" + y + "_" + x + "_" + l); var condVal = fields[y, x] + (questionType[y, x]) + hasCorrectWordCount + lastFieldIsNoWord - 3; m.AddConstr(allConditionsMet >= condVal, "AllConditionsAND" + y + "_" + x + "_" + l + "_1"); m.AddConstr(allConditionsMet <= fields[y, x], "AllConditionsAND" + y + "_" + x + "_" + l + "_2"); m.AddConstr(allConditionsMet <= (questionType[y, x]), "AllConditionsAND" + y + "_" + x + "_" + l + "_3"); m.AddConstr(allConditionsMet <= hasCorrectWordCount, "AllConditionsAND" + y + "_" + x + "_" + l + "_4"); m.AddConstr(allConditionsMet <= lastFieldIsNoWord, "AllConditionsAND" + y + "_" + x + "_" + l + "_5"); lengths[l] += allConditionsMet; // If all conditions are met, all letters are part of a word for (int i = 1; i <= l; i++) { partOfAWord[y + i, x] += allConditionsMet; } } } // max word count if (x + maxWordLength < sizeX) { var maxWordCount = new GRBLinExpr(); var range = maxWordLength + 1; if (x + range == sizeX) { range--; } for (int i = 0; i < maxWordLength + 1; i++) { maxWordCount += 1 - fields[y, x + i]; } m.AddConstr(maxWordCount <= maxWordLength, "maxWordsX" + y + "_" + x); } if (y + maxWordLength < sizeY) { var maxWordCount = new GRBLinExpr(); var range = maxWordLength + 1; if (y + range == sizeY) { range--; } for (int i = 0; i < maxWordLength + 1; i++) { maxWordCount += 1 - fields[y + i, x]; } m.AddConstr(maxWordCount <= maxWordLength, "maxWordsY" + y + "_" + x); } } } // All non-question fields have to belong to a word for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { m.AddConstr(1 - fields[y, x] <= partOfAWord[y, x], "NonQuestionsToWords" + y + "_" + x + "_1"); m.AddConstr(1 - fields[y, x] >= partOfAWord[y, x] * 0.5, "NonQuestionsToWords" + y + "_" + x + "_2"); } } // after a question, no word of length 1 or 2 is allowed for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { if (x < sizeX - 3) { // if there's a question AND it's pointing right THEN sum(fields[y, x+1/2/3]) = 0 var minLetters = 3 - (fields[y, x + 1] + fields[y, x + 2] + fields[y, x + 3]); m.AddConstr(fields[y, x] + (1 - questionType[y, x]) - 1 <= minLetters * (1d / 3d), "AfterRightQuestionMin2Letters" + y + "_" + x); } else { m.AddConstr(questionType[y, x] == 1, "NoRightQuestionAtRightBorder" + y + "_" + x); } if (y < sizeY - 3) { // if there's a question AND it's pointing down THEN sum(fields[y+1/2/3, x]) = 0 var minLetters = 3 - (fields[y + 1, x] + fields[y + 2, x] + fields[y + 3, x]); m.AddConstr(fields[y, x] + questionType[y, x] - 1 <= minLetters * (1d / 3d), "AfterDownQuestionMin2Letters" + y + "_" + x); } else if (x < sizeX - 3) { m.AddConstr(questionType[y, x] == 0, "NoDownQuestionAtBottomBorder" + y + "_" + x); } else { m.AddConstr(fields[y, x] == 0, "OnlyWordsInBottomrightCorner" + y + "_" + x); } } } // Objective: // questions should be around ~22% (allFieldsSum ~= amountQuestions) int tolerance = (int)(amountQuestions * 0.2); m.AddConstr(allFieldsSum - amountQuestions >= -tolerance); m.AddConstr(allFieldsSum - amountQuestions <= tolerance); // as many partOfAWord == 2 as possible var manyCrossedWords = new GRBLinExpr(); for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { manyCrossedWords += partOfAWord[y, x]; } } // ideal histogram comparison //var wordHistogramDifferences = new GRBLinExpr(); foreach (var wl in wordLengthHistogram.Keys) { /*var varDiffInput = m.AddVar(-sizeX * sizeY / 3, sizeX * sizeY / 3, 0, GRB.INTEGER, "varDiffInput" + wl); * m.AddConstr(varDiffInput == (wordLengthHistogram[wl] - lengths[wl])); * var varDiffRes = m.AddVar(0, sizeX * sizeY / 3, 0, GRB.INTEGER, "varDiff" + wl); * m.AddGenConstrAbs(varDiffRes, varDiffInput, "diffGenConstr" + wl); * wordHistogramDifferences += varDiffRes;*/ int histogramTolerance = Math.Max(1, (int)(wordLengthHistogram[wl] * 0.2 * wordLengthRatio)); //m.AddConstr(wordLengthHistogram[wl] - lengths[wl] >= -histogramTolerance); //m.AddConstr(wordLengthHistogram[wl] - lengths[wl] <= histogramTolerance); } // question field clusters // in a field of 2x2, minimize the nr of fields where there are 2-4 questions resp. maximize 0-1 questions /*var clusterPenalty = new GRBLinExpr(); * int area = 2; * for (int y = 0; y < sizeY - (area - 1); y++) * { * for (int x = 0; x < sizeX - (area - 1); x++) * { * var clusterTotal = new GRBLinExpr(); * for (int i = 0; i < area; i++) * { * for (int j = 0; j < area; j++) * { * clusterTotal += fields[y + i, x + j]; * } * } * var varClusterTotalPenalty = m.AddVar(0, 1, 0, GRB.BINARY, "varClusterTotalPenalty" + y + "_" + x); * // 0-1 = good, 2-4 = bad * m.AddConstr(varClusterTotalPenalty <= clusterTotal * 0.5); * m.AddConstr(varClusterTotalPenalty >= (clusterTotal - 1) * (1d / 3)); * clusterPenalty += varClusterTotalPenalty; * } * }*/ //amountOfQuestionsRating * (100d / sizeX / sizeY) + manyCrossedWords + + wordHistogramDifferences // clusterPenalty * 100 m.SetObjective(manyCrossedWords, GRB.MAXIMIZE); m.SetCallback(new GRBMipSolCallback(crossword, fields, questionType, null)); m.Optimize(); m.ComputeIIS(); m.Write("model.ilp"); m.Dispose(); env.Dispose(); }
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); }
static void Main() { try { // Sample data int groundSetSize = 20; int nSubsets = 4; int Budget = 12; double[,] Set = new double[, ] { { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1 }, { 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0 }, { 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0 } }; int[] SetObjPriority = new int[] { 3, 2, 2, 1 }; double[] SetObjWeight = new double[] { 1.0, 0.25, 1.25, 1.0 }; int e, i, status, nSolutions; // Create environment GRBEnv env = new GRBEnv("multiobj_cs.log"); // Create initial model GRBModel model = new GRBModel(env); model.ModelName = "multiobj_cs"; // Initialize decision variables for ground set: // x[e] == 1 if element e is chosen for the covering. GRBVar[] Elem = model.AddVars(groundSetSize, GRB.BINARY); for (e = 0; e < groundSetSize; e++) { string vname = string.Format("El{0}", e); Elem[e].VarName = vname; } // Constraint: limit total number of elements to be picked to be at most // Budget GRBLinExpr lhs = new GRBLinExpr(); for (e = 0; e < groundSetSize; e++) { lhs.AddTerm(1.0, Elem[e]); } model.AddConstr(lhs, GRB.LESS_EQUAL, Budget, "Budget"); // Set global sense for ALL objectives model.ModelSense = GRB.MAXIMIZE; // Limit how many solutions to collect model.Parameters.PoolSolutions = 100; // Set and configure i-th objective for (i = 0; i < nSubsets; i++) { string vname = string.Format("Set{0}", i); GRBLinExpr objn = new GRBLinExpr(); for (e = 0; e < groundSetSize; e++) { objn.AddTerm(Set[i, e], Elem[e]); } model.SetObjectiveN(objn, i, SetObjPriority[i], SetObjWeight[i], 1.0 + i, 0.01, vname); } // Save problem model.Write("multiobj_cs.lp"); // Optimize model.Optimize(); // Status checking status = model.Status; if (status == GRB.Status.INF_OR_UNBD || status == GRB.Status.INFEASIBLE || status == GRB.Status.UNBOUNDED) { Console.WriteLine("The model cannot be solved " + "because it is infeasible or unbounded"); return; } if (status != GRB.Status.OPTIMAL) { Console.WriteLine("Optimization was stopped with status {0}", status); return; } // Print best selected set Console.WriteLine("Selected elements in best solution:"); Console.Write("\t"); for (e = 0; e < groundSetSize; e++) { if (Elem[e].X < .9) { continue; } Console.Write("El{0} ", e); } Console.WriteLine(); // Print number of solutions stored nSolutions = model.SolCount; Console.WriteLine("Number of solutions found: {0}", nSolutions); // Print objective values of solutions if (nSolutions > 10) { nSolutions = 10; } Console.WriteLine("Objective values for first {0} solutions:", nSolutions); for (i = 0; i < nSubsets; i++) { model.Parameters.ObjNumber = i; Console.Write("\tSet" + i); for (e = 0; e < nSolutions; e++) { model.Parameters.SolutionNumber = e; Console.Write("{0,8}", model.ObjNVal); } Console.WriteLine(); } model.Dispose(); env.Dispose(); } catch (GRBException e) { Console.WriteLine("Error code = {0}", e); Console.WriteLine(e.Message); } }
static void Main(string[] args) { int n = 9; int s = 3; if (args.Length < 1) { Console.Out.WriteLine("Usage: sudoku_cs filename"); return; } try { GRBEnv env = new GRBEnv(); GRBModel model = new GRBModel(env); // Create 3-D array of model variables GRBVar[,,] vars = new GRBVar[n,n,n]; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { for (int v = 0; v < n; v++) { string st = "G_" + i.ToString() + "_" + j.ToString() + "_" + v.ToString(); vars[i,j,v] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, st); } } } // Integrate variables into model model.Update(); // Add constraints GRBLinExpr expr; // Each cell must take one value for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { expr = 0.0; for (int v = 0; v < n; v++) expr += vars[i,j,v]; string st = "V_" + i.ToString() + "_" + j.ToString(); model.AddConstr(expr == 1.0, st); } } // Each value appears once per row for (int i = 0; i < n; i++) { for (int v = 0; v < n; v++) { expr = 0.0; for (int j = 0; j < n; j++) expr += vars[i,j,v]; string st = "R_" + i.ToString() + "_" + v.ToString(); model.AddConstr(expr == 1.0, st); } } // Each value appears once per column for (int j = 0; j < n; j++) { for (int v = 0; v < n; v++) { expr = 0.0; for (int i = 0; i < n; i++) expr += vars[i,j,v]; string st = "C_" + j.ToString() + "_" + v.ToString(); model.AddConstr(expr == 1.0, st); } } // Each value appears once per sub-grid for (int v = 0; v < n; v++) { for (int i0 = 0; i0 < s; i0++) { for (int j0 = 0; j0 < s; j0++) { expr = 0.0; for (int i1 = 0; i1 < s; i1++) { for (int j1 = 0; j1 < s; j1++) { expr += vars[i0*s+i1,j0*s+j1,v]; } } string st = "Sub_" + v.ToString() + "_" + i0.ToString() + "_" + j0.ToString(); model.AddConstr(expr == 1.0, st); } } } // Update model model.Update(); // Fix variables associated with pre-specified cells StreamReader sr = File.OpenText(args[0]); for (int i = 0; i < n; i++) { string input = sr.ReadLine(); for (int j = 0; j < n; j++) { int val = (int) input[j] - 48 - 1; // 0-based if (val >= 0) vars[i,j,val].Set(GRB.DoubleAttr.LB, 1.0); } } // Optimize model model.Optimize(); // Write model to file model.Write("sudoku.lp"); double[,,] x = model.Get(GRB.DoubleAttr.X, vars); Console.WriteLine(); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { for (int v = 0; v < n; v++) { if (x[i,j,v] > 0.5) { Console.Write(v+1); } } } Console.WriteLine(); } // 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 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); }