static void Main() { try { GRBEnv env = new GRBEnv("qp.log"); GRBModel model = new GRBModel(env); // Create variables GRBVar x = model.AddVar(0.0, 1.0, 0.0, GRB.CONTINUOUS, "x"); GRBVar y = model.AddVar(0.0, 1.0, 0.0, GRB.CONTINUOUS, "y"); GRBVar z = model.AddVar(0.0, 1.0, 0.0, GRB.CONTINUOUS, "z"); // Set objective GRBQuadExpr obj = x * x + x * y + y * y + y * z + z * z + 2 * x; model.SetObjective(obj); // Add constraint: x + 2 y + 3 z >= 4 model.AddConstr(x + 2 * y + 3 * z >= 4.0, "c0"); // Add constraint: x + y >= 1 model.AddConstr(x + y >= 1.0, "c1"); // Optimize model model.Optimize(); Console.WriteLine(x.VarName + " " + x.X); Console.WriteLine(y.VarName + " " + y.X); Console.WriteLine(z.VarName + " " + z.X); Console.WriteLine("Obj: " + model.ObjVal + " " + obj.Value); // Change variable types to integer x.VType = GRB.INTEGER; y.VType = GRB.INTEGER; z.VType = GRB.INTEGER; // Optimize model model.Optimize(); Console.WriteLine(x.VarName + " " + x.X); Console.WriteLine(y.VarName + " " + y.X); Console.WriteLine(z.VarName + " " + z.X); Console.WriteLine("Obj: " + model.ObjVal + " " + obj.Value); // Dispose of model and env model.Dispose(); env.Dispose(); } catch (GRBException e) { Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message); } }
public static GRBQuadExpr SumL2(this GRBVar[] _vars) { var res = new GRBQuadExpr(); foreach (var v in _vars) { res.Add(v * v); } return(res); }
public void GenerateBaseModel(string datFilePath) { _constraintModel = DatFileParser.ParseDatFile(datFilePath); ExampleName = new FileInfo(datFilePath).Name; try { var path = @"C:\IJCAI\Output\TestRuns\Logs\"; if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } env = new GRBEnv(path + ExampleName + ".log") { LogToConsole = 0, NodefileStart = 0.5 }; _model = new GRBModel(env); _k = _constraintModel.K; _b = _constraintModel.B; // Optimization values S = _model.AddVar(0.0, _k - 1, 0.0, GRB.CONTINUOUS, "S"); // Number of interrupted job pairs L = _model.AddVar(0.0, _k - 1, 0.0, GRB.CONTINUOUS, "L"); // Longest time a cable resides in storage M = _model.AddVar(0.0, _k - 1, 0.0, GRB.CONTINUOUS, "M"); // Maximum number of cables stored simultaneously N = _model.AddVar(0.0, _k - 1, 0.0, GRB.CONTINUOUS, "N"); // Number of violated soft atomic constraints pfc = CreatePermutationVariable(); // objective var objExpr = new GRBQuadExpr(); objExpr.AddTerm(Math.Pow(_k, 3), S); objExpr.AddTerm(Math.Pow(_k, 2), M); objExpr.AddTerm(Math.Pow(_k, 1), L); objExpr.AddTerm(Math.Pow(_k, 0), N); _model.SetObjective(objExpr); _model.Parameters.TimeLimit = 300; // 300 seconds } catch (Exception e) { Console.WriteLine(e.ToString()); } }
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" }; 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 }, { 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } }; // Model GRBEnv env = new GRBEnv(); GRBModel model = new GRBModel(env); model.ModelName = "assignment"; // Assignment 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, 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, Shifts[s] + "Slack"); } // 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, Workers[w] + "TotShifts"); } 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 == shiftRequirements[s], Shifts[s]); } // Constraint: set totSlack equal to the total slack lhs = new GRBLinExpr(); for (int s = 0; s < nShifts; ++s) { lhs.AddTerm(1.0, slacks[s]); } model.AddConstr(lhs == totSlack, "totSlack"); // Constraint: compute the total number of shifts for each worker for (int w = 0; w < nWorkers; ++w) { lhs = new GRBLinExpr(); for (int s = 0; s < nShifts; ++s) { lhs.AddTerm(1.0, x[w, s]); } model.AddConstr(lhs == totShifts[w], "totShifts" + Workers[w]); } // Objective: minimize the total slack model.SetObjective(1.0 * totSlack); // Optimize int status = solveAndPrint(model, totSlack, nWorkers, Workers, totShifts); if (status != GRB.Status.OPTIMAL) { return; } // Constrain the slack by setting its upper and lower bounds totSlack.UB = totSlack.X; totSlack.LB = totSlack.X; // Variable to count the average number of shifts worked GRBVar avgShifts = model.AddVar(0, GRB.INFINITY, 0, GRB.CONTINUOUS, "avgShifts"); // Variables to count the difference from average for each worker; // note that these variables can take negative values. GRBVar[] diffShifts = new GRBVar[nWorkers]; for (int w = 0; w < nWorkers; ++w) { diffShifts[w] = model.AddVar(-GRB.INFINITY, GRB.INFINITY, 0, GRB.CONTINUOUS, Workers[w] + "Diff"); } // Constraint: compute the average number of shifts worked lhs = new GRBLinExpr(); for (int w = 0; w < nWorkers; ++w) { lhs.AddTerm(1.0, totShifts[w]); } model.AddConstr(lhs == nWorkers * avgShifts, "avgShifts"); // Constraint: compute the difference from the average number of shifts for (int w = 0; w < nWorkers; ++w) { model.AddConstr(totShifts[w] - avgShifts == diffShifts[w], Workers[w] + "Diff"); } // Objective: minimize the sum of the square of the difference from the // average number of shifts worked GRBQuadExpr qobj = new GRBQuadExpr(); for (int w = 0; w < nWorkers; ++w) { qobj.AddTerm(1.0, diffShifts[w], diffShifts[w]); } model.SetObjective(qobj); // Optimize status = solveAndPrint(model, totSlack, nWorkers, Workers, totShifts); if (status != GRB.Status.OPTIMAL) { return; } // Dispose of model and env model.Dispose(); env.Dispose(); } catch (GRBException e) { Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message); } }
protected static bool dense_optimize(GRBEnv env, int rows, int cols, double[] c, // linear portion of objective function double[,] Q, // quadratic portion of objective function double[,] A, // constraint matrix char[] sense, // constraint senses double[] rhs, // RHS vector double[] lb, // variable lower bounds double[] ub, // variable upper bounds char[] vtype, // variable types (continuous, binary, etc.) double[] solution) { bool success = false; try { GRBModel model = new GRBModel(env); // Add variables to the model GRBVar[] vars = model.AddVars(lb, ub, null, vtype, null); model.Update(); // Populate A matrix for (int i = 0; i < rows; i++) { GRBLinExpr expr = new GRBLinExpr(); for (int j = 0; j < cols; j++) if (A[i,j] != 0) expr.AddTerm(A[i,j], vars[j]); // Note: '+=' would be much slower model.AddConstr(expr, sense[i], rhs[i], ""); } // Populate objective GRBQuadExpr obj = new GRBQuadExpr(); if (Q != null) { for (int i = 0; i < cols; i++) for (int j = 0; j < cols; j++) if (Q[i,j] != 0) obj.AddTerm(Q[i,j], vars[i], vars[j]); // Note: '+=' would be much slower for (int j = 0; j < cols; j++) if (c[j] != 0) obj.AddTerm(c[j], vars[j]); // Note: '+=' would be much slower model.SetObjective(obj); } // Solve model model.Optimize(); // Extract solution if (model.Get(GRB.IntAttr.Status) == GRB.Status.OPTIMAL) { success = true; for (int j = 0; j < cols; j++) solution[j] = vars[j].Get(GRB.DoubleAttr.X); } model.Dispose(); } catch (GRBException e) { Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message); } return success; }
public BalanceOutput BalanceGurobiForGLR(double[] x0, double[,] a, double[,] h, double[] d, double[] technologicLowerBound, double[] technologicUpperBound, double[] metrologicLowerBound, double[] metrologicUpperBound) { try { GRBEnv env = new GRBEnv(); GRBModel model = new GRBModel(env); double[] results = new double[x0.Length]; if (inputData.balanceSettings.balanceSettingsConstraints == BalanceSettings.BalanceSettingsConstraints.TECHNOLOGIC) { //Create variables GRBVar[] varsTechnologic = new GRBVar[a.Columns()]; for (int i = 0; i < varsTechnologic.Length; i++) { varsTechnologic[i] = model.AddVar(technologicLowerBound[i], technologicUpperBound[i], 0.0, GRB.CONTINUOUS, "x" + i); } //Set objective GRBQuadExpr objTechnologic = new GRBQuadExpr(); for (int i = 0; i < varsTechnologic.Length; i++) { objTechnologic.AddTerm(h[i, i] / 2.0, varsTechnologic[i], varsTechnologic[i]); } for (int i = 0; i < varsTechnologic.Length; i++) { objTechnologic.AddTerm(d[i], varsTechnologic[i]); } model.SetObjective(objTechnologic); //Add constraints GRBLinExpr expr; for (int i = 0; i < a.Rows(); i++) { expr = new GRBLinExpr(); for (int j = 0; j < a.Columns(); j++) { expr.AddTerm(a[i, j], varsTechnologic[j]); } model.AddConstr(expr, GRB.EQUAL, 0.0, "c" + i); } // Optimize model model.Optimize(); //results = new double[varsTechnologic.Length]; for (int i = 0; i < results.Length; i++) { results[i] = varsTechnologic[i].Get(GRB.DoubleAttr.X); } } else { //Create variables GRBVar[] varsMetrologic = new GRBVar[a.Columns()]; for (int i = 0; i < varsMetrologic.Length; i++) { varsMetrologic[i] = model.AddVar(metrologicLowerBound[i], metrologicUpperBound[i], 0.0, GRB.CONTINUOUS, "x" + i); } //Set objective GRBQuadExpr objMetrologic = new GRBQuadExpr(); for (int i = 0; i < varsMetrologic.Length; i++) { objMetrologic.AddTerm(h[i, i] / 2.0, varsMetrologic[i], varsMetrologic[i]); } for (int i = 0; i < varsMetrologic.Length; i++) { objMetrologic.AddTerm(d[i], varsMetrologic[i]); } model.SetObjective(objMetrologic); //Add constraints GRBLinExpr expr; for (int i = 0; i < a.Rows(); i++) { expr = new GRBLinExpr(); for (int j = 0; j < a.Columns(); j++) { expr.AddTerm(a[i, j], varsMetrologic[j]); } model.AddConstr(expr, GRB.EQUAL, 0.0, "c" + i); } // Optimize model model.Optimize(); //results = new double[varsMetrologic.Length]; for (int i = 0; i < results.Length; i++) { results[i] = varsMetrologic[i].Get(GRB.DoubleAttr.X); } } model.Dispose(); env.Dispose(); BalanceOutput outb = new BalanceOutput(); var balanceOutputVars = new List <OutputVariables>(); for (int i = 0; i < measuredValues.Count; i++) { InputVariables outputVariable = inputData.BalanceInputVariables[i]; balanceOutputVars.Add(new OutputVariables() { id = outputVariable.id, name = outputVariable.name, value = results[i], source = outputVariable.sourceId, target = outputVariable.destinationId }); } outb.balanceOutputVariables = balanceOutputVars; outb.GlobaltestValue = GTR; outb.Status = "Success"; return(outb); } catch (Exception e) { return(new BalanceOutput { Status = e.Message, }); } }
public void BalanceGurobi() { GRBEnv env = new GRBEnv(); GRBModel model = new GRBModel(env); DateTime CalculationTimeStart; DateTime CalculationTimeFinish; double[] results = new double[measuredValues.ToArray().Length]; if (inputData.balanceSettings.balanceSettingsConstraints == BalanceSettings.BalanceSettingsConstraints.TECHNOLOGIC) { //Create variables GRBVar[] varsTechnologic = new GRBVar[measuredValues.ToArray().Length]; for (int i = 0; i < varsTechnologic.Length; i++) { varsTechnologic[i] = model.AddVar(technologicRangeLowerBound[i], technologicRangeUpperBound[i], 0.0, GRB.CONTINUOUS, "x" + i); } //Set objective GRBQuadExpr objTechnologic = new GRBQuadExpr(); for (int i = 0; i < varsTechnologic.Length; i++) { objTechnologic.AddTerm(H[i, i] / 2.0, varsTechnologic[i], varsTechnologic[i]); } for (int i = 0; i < varsTechnologic.Length; i++) { objTechnologic.AddTerm(dVector[i], varsTechnologic[i]); } model.SetObjective(objTechnologic); //Add constraints GRBLinExpr expr; for (int i = 0; i < incidenceMatrix.RowCount; i++) { expr = new GRBLinExpr(); for (int j = 0; j < incidenceMatrix.ColumnCount; j++) { expr.AddTerm(incidenceMatrix[i, j], varsTechnologic[j]); } model.AddConstr(expr, GRB.EQUAL, 0.0, "c" + i); } // Optimize model CalculationTimeStart = DateTime.Now; model.Optimize(); CalculationTimeFinish = DateTime.Now; results = new double[varsTechnologic.Length]; for (int i = 0; i < results.Length; i++) { results[i] = varsTechnologic[i].Get(GRB.DoubleAttr.X); } } else { //Create variables GRBVar[] varsMetrologic = new GRBVar[measuredValues.ToArray().Length]; for (int i = 0; i < varsMetrologic.Length; i++) { if (measureIndicator[i, i] == 0) { varsMetrologic[i] = model.AddVar(technologicRangeLowerBound[i], technologicRangeUpperBound[i], 0.0, GRB.CONTINUOUS, "x" + i); } else { varsMetrologic[i] = model.AddVar(metrologicRangeLowerBound[i], metrologicRangeUpperBound[i], 0.0, GRB.CONTINUOUS, "x" + i); } } //Set objective GRBQuadExpr objMetroologic = new GRBQuadExpr(); for (int i = 0; i < varsMetrologic.Length; i++) { objMetroologic.AddTerm(H[i, i] / 2.0, varsMetrologic[i], varsMetrologic[i]); } for (int i = 0; i < varsMetrologic.Length; i++) { objMetroologic.AddTerm(dVector[i], varsMetrologic[i]); } model.SetObjective(objMetroologic); //Add constraints GRBLinExpr expr; for (int i = 0; i < incidenceMatrix.RowCount; i++) { expr = new GRBLinExpr(); for (int j = 0; j < incidenceMatrix.ColumnCount; j++) { expr.AddTerm(incidenceMatrix[i, j], varsMetrologic[j]); } model.AddConstr(expr, GRB.EQUAL, 0.0, "c" + i); } // Optimize model CalculationTimeStart = DateTime.Now; model.Optimize(); CalculationTimeFinish = DateTime.Now; results = new double[varsMetrologic.Length]; for (int i = 0; i < results.Length; i++) { results[i] = varsMetrologic[i].Get(GRB.DoubleAttr.X); } } model.Dispose(); env.Dispose(); double disbalanceOriginal = incidenceMatrix.Multiply(measuredValues).Subtract(reconciledValues).ToArray().Euclidean(); double disbalance = incidenceMatrix.Multiply(SparseVector.OfVector(new DenseVector(results))).Subtract(reconciledValues).ToArray().Euclidean(); balanceOutput = new BalanceOutput(); balanceOutputVariables = new List <OutputVariables>(); for (int i = 0; i < results.Length; i++) { InputVariables outputVariable = inputData.BalanceInputVariables[i]; balanceOutputVariables.Add(new OutputVariables() { id = outputVariable.id, name = outputVariable.name, value = results[i], source = outputVariable.sourceId, target = outputVariable.destinationId, upperBound = (inputData.balanceSettings.balanceSettingsConstraints == 0 || measureIndicator[i, i] == 0.0) ? technologicRangeUpperBound[i] : metrologicRangeUpperBound[i], lowerBound = (inputData.balanceSettings.balanceSettingsConstraints == 0 || measureIndicator[i, i] == 0.0) ? technologicRangeLowerBound[i] : metrologicRangeLowerBound[i] }); } balanceOutput.CalculationTime = (CalculationTimeFinish - CalculationTimeStart).TotalSeconds; balanceOutput.balanceOutputVariables = balanceOutputVariables; balanceOutput.DisbalanceOriginal = disbalanceOriginal; balanceOutput.Disbalance = disbalance; balanceOutput.GlobaltestValue = 0.0; balanceOutput.Status = "Success"; }
dense_optimize(GRBEnv env, int rows, int cols, double[] c, // linear portion of objective function double[,] Q, // quadratic portion of objective function double[,] A, // constraint matrix char[] sense, // constraint senses double[] rhs, // RHS vector double[] lb, // variable lower bounds double[] ub, // variable upper bounds char[] vtype, // variable types (continuous, binary, etc.) double[] solution) { bool success = false; try { GRBModel model = new GRBModel(env); // Add variables to the model GRBVar[] vars = model.AddVars(lb, ub, null, vtype, null); // Populate A matrix for (int i = 0; i < rows; i++) { GRBLinExpr expr = new GRBLinExpr(); for (int j = 0; j < cols; j++) { if (A[i, j] != 0) { expr.AddTerm(A[i, j], vars[j]); // Note: '+=' would be much slower } } model.AddConstr(expr, sense[i], rhs[i], ""); } // Populate objective GRBQuadExpr obj = new GRBQuadExpr(); if (Q != null) { for (int i = 0; i < cols; i++) { for (int j = 0; j < cols; j++) { if (Q[i, j] != 0) { obj.AddTerm(Q[i, j], vars[i], vars[j]); // Note: '+=' would be much slower } } } for (int j = 0; j < cols; j++) { if (c[j] != 0) { obj.AddTerm(c[j], vars[j]); // Note: '+=' would be much slower } } model.SetObjective(obj); } // Solve model model.Optimize(); // Extract solution if (model.Status == GRB.Status.OPTIMAL) { success = true; for (int j = 0; j < cols; j++) { solution[j] = vars[j].X; } } model.Dispose(); } catch (GRBException e) { Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message); } return(success); }
public GurobiSolver(Crossword crossword) { var wordLengthHistogram = new Dictionary <int, int>() { { 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); int amountQuestions = (int)Math.Round(0.22 * sizeX * sizeY); 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) var amountOfQuestionsRating = m.AddVar(0, sizeX * sizeY - amountQuestions, 0, GRB.INTEGER, "amountOfQuestionsRating"); var amountOfQuestionsAbsInput = m.AddVar(-amountQuestions, sizeX * sizeY - amountQuestions, 0, GRB.INTEGER, "amountOfQuestionsAbsInput"); m.AddConstr(amountOfQuestionsAbsInput == allFieldsSum - amountQuestions); m.AddGenConstrAbs(amountOfQuestionsRating, amountOfQuestionsAbsInput, "amountOfQuestionsAbs"); /*int tolerance = (int)(amountQuestions * 0.1); * 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 GRBQuadExpr(); foreach (var wl in wordLengthHistogram.Keys) { var diff = (wordLengthHistogram[wl] - lengths[wl]) * 2; wordHistogramDifferences += diff * diff; } // question field clusters var clusterPenalty = new GRBLinExpr(); for (int y = 0; y < sizeY - 2; y++) { for (int x = 0; x < sizeX - 2; x++) { var clusterTotal = new GRBLinExpr(); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { clusterTotal += fields[y + i, x + j]; } } var varClusterTotalPenalty = m.AddVar(0, 7, 0, GRB.INTEGER, "varClusterTotalPenalty" + y + "_" + x); m.AddConstr(varClusterTotalPenalty >= clusterTotal - 2); clusterPenalty += varClusterTotalPenalty; } } //amountOfQuestionsRating * (100d / sizeX / sizeY) + manyCrossedWords + + wordHistogramDifferences // clusterPenalty * 100 m.SetObjective(amountOfQuestionsRating * (100d / sizeX / sizeY) + clusterPenalty * 100 + wordHistogramDifferences + manyCrossedWords, GRB.MINIMIZE); m.SetCallback(new GRBMipSolCallback(crossword, fields, questionType, null)); m.Optimize(); m.ComputeIIS(); m.Write("model.ilp"); m.Dispose(); env.Dispose(); }
private static bool GurobiSolve(GRBEnv env, int rows, int cols, double[] c, // linear portion of objective function double[,] Q, // quadratic portion of objective function double[,] A, // constraint matrix double[] rhs, // RHS vector double[] lb, // variable lower bounds double[] ub, // variable upper bounds char[] vtype, // variable types (continuous, binary, etc.) double[] solution, ref double val) { bool success = false; try { GRBModel model = new GRBModel(env); // Add variables to the model GRBVar[] vars = model.AddVars(lb, ub, null, null, null); model.Update(); // Populate A matrix for (int i = 0; i < rows; i++) { GRBLinExpr expr = new GRBLinExpr(); for (int j = 0; j < cols; j++) { if (A[i, j] != 0) { expr.AddTerm(A[i, j], vars[j]); // Note: '+=' would be much slower } } model.AddConstr(expr, '>', rhs[i], ""); } // Populate objective GRBQuadExpr obj = new GRBQuadExpr(); if (Q != null) { for (int i = 0; i < cols; i++) { for (int j = 0; j < cols; j++) { if (Q[i, j] != 0) { obj.AddTerm(Q[i, j], vars[i], vars[j]); // Note: '+=' would be much slower } } } for (int j = 0; j < cols; j++) { if (c[j] != 0) { obj.AddTerm(c[j], vars[j]); // Note: '+=' would be much slower } } model.SetObjective(obj, GRB.MAXIMIZE); } // Solve model model.Optimize(); // Extract solution if (model.Get(GRB.IntAttr.Status) == GRB.Status.OPTIMAL) { success = true; for (int j = 0; j < cols; j++) { solution[j] = vars[j].Get(GRB.DoubleAttr.X); } } else { success = false; } val = obj.Value; model.Dispose(); } catch (GRBException e) { return(false); } return(success); }