Example #1
0
        private void BuildMIPModel()
        {
            _env   = new GRBEnv();
            _model = new GRBModel(_env);


            //room used.
            varR = new Dictionary <Room, GRBVar>();
            foreach (var room in Data.Rooms)
            {
                varR[room] = _model.AddVar(0, GRB.INFINITY, room.Cost, GRB.INTEGER,
                                           $"r_{room}");
            }

            _model.Update();



            _model.AddConstr(Data.TimeSlots.Count * varR.Values.Sumx(), GRB.GREATER_EQUAL, Data.Courses.Sum(c => c.Lectures), "");

            foreach (var roomCapacity in Data.RoomCapacities)
            {
                var overCap = OverCapacity * ((double)Data.Courses.Where(c => c.NumberOfStudents > roomCapacity.Value).Sum(c => c.Lectures) / Data.TimeSlots.Count);
                _model.AddConstr(varR.Where(r => r.Key.Capacity > roomCapacity.Value).Sumx(r => r.Value) * Data.TimeSlots.Count, GRB.GREATER_EQUAL,
                                 Data.Courses.Where(c => c.NumberOfStudents > roomCapacity.Value).Sum(c => c.Lectures) + overCap,
                                 $"roomcapcut_{roomCapacity.Value}");
            }

            _model.SetCallback(new OutputCallback());
        }
        /// <summary>
        ///     Creates a cancellable task that runs Gurobi on the given instance.
        /// </summary>
        /// <param name="instance">Instance to run on.</param>
        /// <param name="cancellationToken">
        ///     Token that is regurlarly checked for cancellation.
        ///     If cancellation is detected, the task will be stopped.
        /// </param>
        /// <returns>
        ///     A task that returns the run's runtime, gap, feasibility and completion status onto return.
        /// </returns>
        public Task <GurobiResult> Run(InstanceSeedFile instance, CancellationToken cancellationToken)
        {
            // Check if the runner has already been disposed.
            if (this._hasAlreadyBeenDisposed)
            {
                throw new ObjectDisposedException("GurobiRunner", "Called Run on a disposed GurobiRunner.");
            }

            // Continue if it hasn't.
            var solveTask = Task.Run(
                () =>
            {
                // Prepare Gurobi model: Use configured _environment and the given instance,
                // then add a callback for cancellation.
                var instanceFile = new FileInfo(instance.Path);
                if (!File.Exists(instanceFile.FullName))
                {
                    throw new Exception(string.Format("Instance {0} not found!", instance.Path));
                }

                LoggingHelper.WriteLine(VerbosityLevel.Debug, $"Setting MIPGap to {this._runnerConfiguration.TerminationMipGap}");
                this._environment.MIPGap = this._runnerConfiguration.TerminationMipGap;
                LoggingHelper.WriteLine(VerbosityLevel.Debug, $"Current Seed: {instance.Seed}");
                this._environment.Seed      = instance.Seed;
                this._environment.TimeLimit = this._runnerConfiguration.CpuTimeout.TotalSeconds;

                var fileName = Path.GetFileNameWithoutExtension(instance.Path);
                if (!Directory.Exists("GurobiLog"))
                {
                    Directory.CreateDirectory("GurobiLog");
                }

                this._environment.LogFile = $"GurobiLog/GurobiRunner_{DateTime.Now:yy-MM-dd_HH-mm-ss-ffff}_" + fileName + ".log";

                var model = new GRBModel(this._environment, instance.Path)
                {
                    ModelName = instanceFile.Name
                };
                var mstfileName = instance.Path.Substring(0, instance.Path.Length - instanceFile.Extension.Length) + ".mst";
                if (File.Exists(mstfileName))
                {
                    model.Read(mstfileName);
                }

                model.SetCallback(new GurobiCallback(cancellationToken));

                // Optimize. This step may be aborted in the callback.
                model.Optimize();
                var result = this.CreateGurobiResult(model);
                // Before returning, dispose of Gurobi model.
                model.Dispose();
                return(result);
            },
                cancellationToken);

            return(solveTask);
        }
Example #3
0
        public LinearModel(SolverType type, Action <string> logger, int threadCount = 0)
        {
            Type    = type;
            _logger = logger;
            switch (type)
            {
            case SolverType.CPLEX:
            {
                CplexModel           = new Cplex();
                _cplexStatusCallback = new CplexStatusCallback(this)
                {
                };
                CplexModel.SetOut(new CplexOutputHandler(logger));
                CplexModel.Use(_cplexStatusCallback);
                if (threadCount > 0)
                {
                    CplexModel.SetParam(Cplex.IntParam.Threads, threadCount);
                }
            }
            break;

            case SolverType.Gurobi:
            {
                GRBEnv gurobiEnvironment = new GRBEnv();
                GurobiModel = new GRBModel(gurobiEnvironment);
                GurobiModel.GetEnv().Set(GRB.IntParam.UpdateMode, 1);         // Enable immediate updates to better support the lazy initialization of the wrappers (at minor performance and memory costs)
                GurobiModel.GetEnv().Set(GRB.IntParam.OutputFlag, 0);
                if (threadCount > 0)
                {
                    GurobiModel.GetEnv().Set(GRB.IntParam.Threads, threadCount);
                }
                _gurobiStatusCallback = new GurobiStatusCallback(this)
                {
                    Logger = logger
                };
                GurobiModel.SetCallback(_gurobiStatusCallback);
            }
            break;

            default: throw new ArgumentException("Unknown solver type: " + Type);
            }
        }
Example #4
0
    static void Main(string[] args)
    {
        if (args.Length < 1)
        {
            Console.Out.WriteLine("Usage: callback_cs filename");
            return;
        }

        try {
            GRBEnv   env   = new GRBEnv();
            GRBModel model = new GRBModel(env, args[0]);

            GRBVar[] vars = model.GetVars();

            // Create a callback object and associate it with the model
            model.SetCallback(new callback_cs(vars));
            model.Optimize();

            double[] x      = model.Get(GRB.DoubleAttr.X, vars);
            string[] vnames = model.Get(GRB.StringAttr.VarName, vars);

            for (int j = 0; j < vars.Length; j++)
            {
                if (x[j] != 0.0)
                {
                    Console.WriteLine(vnames[j] + " " + x[j]);
                }
            }

            // Dispose of model and env
            model.Dispose();
            env.Dispose();
        } catch (GRBException e) {
            Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message);
            Console.WriteLine(e.StackTrace);
        }
    }
Example #5
0
        private void BuildMIPModel()
        {
            _env   = new GRBEnv();
            _model = new GRBModel(_env);



            _varTimeslotUsed = new Dictionary <TimeSlot, GRBVar>();
            foreach (var timeSlot in Data.TimeSlots)
            {
                _varTimeslotUsed[timeSlot] = _model.AddVar(0, 1, 1, GRB.BINARY, $"t_{timeSlot}");
            }

            _model.Update();

            _model.AddConstr(Data.Rooms.Count * _varTimeslotUsed.Values.Sumx(), GRB.GREATER_EQUAL, Data.Courses.Sum(c => c.Lectures), "");

            foreach (var roomCapacity in Data.RoomCapacities)

            {
                _model.AddConstr(Data.Rooms.Count(r => r.Capacity > roomCapacity.Value) * Data.TimeSlots.Count, GRB.GREATER_EQUAL,
                                 Data.Courses.Where(c => c.NumberOfStudents > roomCapacity.Value).Sum(c => c.Lectures),
                                 $"timecapcapcut_{roomCapacity.Value}");
            }

            //Consecutive timeslots
            var timeslotlist = Data.TimeSlots.OrderBy(ts => ts.Period).ThenBy(ts => ts.Day).ToList();

            for (var i = 0; i < timeslotlist.Count - 1; i++)
            {
                _model.AddConstr(_varTimeslotUsed[timeslotlist[i + 1]] - _varTimeslotUsed[timeslotlist[i]], GRB.LESS_EQUAL, 0,
                                 $"timeslotconsec_{i}");
            }

            _model.SetCallback(new OutputCallback());
        }
Example #6
0
    static void Main(string[] args)
    {
        if (args.Length < 1)
        {
            Console.Out.WriteLine("Usage: callback_cs filename");
            return;
        }

        StreamWriter logfile = null;

        try {
            // Create environment
            GRBEnv env = new GRBEnv();

            // Read model from file
            GRBModel model = new GRBModel(env, args[0]);

            // Turn off display and heuristics
            model.Parameters.OutputFlag = 0;
            model.Parameters.Heuristics = 0.0;

            // Open log file
            logfile = new StreamWriter("cb.log");

            // Create a callback object and associate it with the model
            GRBVar[]    vars = model.GetVars();
            callback_cs cb   = new callback_cs(vars, logfile);

            model.SetCallback(cb);

            // Solve model and capture solution information
            model.Optimize();

            Console.WriteLine("");
            Console.WriteLine("Optimization complete");
            if (model.SolCount == 0)
            {
                Console.WriteLine("No solution found, optimization status = "
                                  + model.Status);
            }
            else
            {
                Console.WriteLine("Solution found, objective = " + model.ObjVal);

                string[] vnames = model.Get(GRB.StringAttr.VarName, vars);
                double[] x      = model.Get(GRB.DoubleAttr.X, vars);

                for (int j = 0; j < vars.Length; j++)
                {
                    if (x[j] != 0.0)
                    {
                        Console.WriteLine(vnames[j] + " " + x[j]);
                    }
                }
            }

            // Dispose of model and environment
            model.Dispose();
            env.Dispose();
        } catch (GRBException e) {
            Console.WriteLine("Error code: " + e.ErrorCode);
            Console.WriteLine(e.Message);
            Console.WriteLine(e.StackTrace);
        } catch (Exception e) {
            Console.WriteLine("Error during optimization");
            Console.WriteLine(e.Message);
            Console.WriteLine(e.StackTrace);
        } finally {
            // Close log file
            if (logfile != null)
            {
                logfile.Close();
            }
        }
    }
Example #7
0
    public static void Main(String[] args)
    {
        if (args.Length < 1)
        {
            Console.WriteLine("Usage: tsp_cs nnodes");
            return;
        }

        int n = Convert.ToInt32(args[0]);

        try {
            GRBEnv   env   = new GRBEnv();
            GRBModel model = new GRBModel(env);

            // Must set LazyConstraints parameter when using lazy constraints

            model.Parameters.LazyConstraints = 1;

            double[] x = new double[n];
            double[] y = new double[n];

            Random r = new Random();
            for (int i = 0; i < n; i++)
            {
                x[i] = r.NextDouble();
                y[i] = r.NextDouble();
            }

            // Create variables

            GRBVar[,] vars = new GRBVar[n, n];

            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j <= i; j++)
                {
                    vars[i, j] = model.AddVar(0.0, 1.0, distance(x, y, i, j),
                                              GRB.BINARY, "x" + i + "_" + j);
                    vars[j, i] = vars[i, j];
                }
            }

            // Degree-2 constraints

            for (int i = 0; i < n; i++)
            {
                GRBLinExpr expr = 0;
                for (int j = 0; j < n; j++)
                {
                    expr.AddTerm(1.0, vars[i, j]);
                }
                model.AddConstr(expr == 2.0, "deg2_" + i);
            }

            // Forbid edge from node back to itself

            for (int i = 0; i < n; i++)
            {
                vars[i, i].UB = 0.0;
            }

            model.SetCallback(new tsp_cs(vars));
            model.Optimize();

            if (model.SolCount > 0)
            {
                int[] tour = findsubtour(model.Get(GRB.DoubleAttr.X, vars));

                Console.Write("Tour: ");
                for (int i = 0; i < tour.Length; i++)
                {
                    Console.Write(tour[i] + " ");
                }
                Console.WriteLine();
            }

            // Dispose of model and environment
            model.Dispose();
            env.Dispose();
        } catch (GRBException e) {
            Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message);
            Console.WriteLine(e.StackTrace);
        }
    }
Example #8
0
        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();
        }
Example #9
0
    static void Main(string[] args)
    {
        if (args.Length < 1) {
          Console.Out.WriteLine("Usage: callback_cs filename");
          return;
        }

        try {
          GRBEnv    env   = new GRBEnv();
          GRBModel  model = new GRBModel(env, args[0]);

          GRBVar[] vars   = model.GetVars();

          model.SetCallback(new callback_cs(vars));
          model.Optimize();

          double[] x      = model.Get(GRB.DoubleAttr.X, vars);
          string[] vnames = model.Get(GRB.StringAttr.VarName, vars);

          for (int j = 0; j < vars.Length; j++) {
        if (x[j] != 0.0) Console.WriteLine(vnames[j] + " " + x[j]);
          }

          for (int j = 0; j < vars.Length; j++) {
        if (x[j] != 0.0) Console.WriteLine(vnames[j] + " " + x[j]);
          }

          // Dispose of model and env
          model.Dispose();
          env.Dispose();

        } catch (GRBException e) {
          Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message);
          Console.WriteLine(e.StackTrace);
        }
    }
Example #10
0
        /// <summary>
        /// Creates a task that runs Gurobi on the given instance.
        /// </summary>
        /// <param name="instance">
        /// Instance to run on.
        /// </param>
        /// <param name="cancellationToken">
        /// The cancellation token given to the task. NOTE: In this implementation, we ignore this cancellation token, since Gurobi uses its own cancellation token in the callback.
        /// </param>
        /// <returns>
        /// A task that returns the run's runtime, gap, feasibility and completion status onto return.
        /// </returns>
        public Task <GurobiResult> Run(InstanceSeedFile instance, CancellationToken cancellationToken)
        {
            // Check if the runner has already been disposed.
            if (this._hasAlreadyBeenDisposed)
            {
                throw new ObjectDisposedException("GurobiRunner", "Called Run on a disposed GurobiRunner.");
            }

            // Continue if it hasn't.
            // ReSharper disable once MethodSupportsCancellation
            var solveTask = Task.Run(
                () =>
            {
                this._recordTimer = null;
                try
                {
                    // Prepare Gurobi model: Use configured _environment and the given instance,
                    // then add a cancellation token source and the Gurobi callback for cancellation.
                    var instanceFile = new FileInfo(instance.Path);
                    if (!File.Exists(instanceFile.FullName))
                    {
                        throw new FileNotFoundException($"Instance {instanceFile.FullName} not found!");
                    }

                    LoggingHelper.WriteLine(VerbosityLevel.Debug, $"Setting MIPGap to {this._runnerConfiguration.TerminationMipGap}");
                    this._environment.MIPGap = this._runnerConfiguration.TerminationMipGap;
                    LoggingHelper.WriteLine(VerbosityLevel.Debug, $"Current Seed: {instance.Seed}");
                    this._environment.Seed      = instance.Seed;
                    this._environment.TimeLimit = this._runnerConfiguration.CpuTimeout.TotalSeconds;

                    if (!Directory.Exists("GurobiLog"))
                    {
                        Directory.CreateDirectory("GurobiLog");
                    }

                    var instanceFileNameWithoutExtension = GurobiUtils.GetFileNameWithoutGurobiExtension(instanceFile);

                    this._environment.LogFile = "GurobiLog" + Path.DirectorySeparatorChar
                                                + $"GurobiRunner_{DateTime.Now:yy-MM-dd_HH-mm-ss-ffff}_"
                                                + instanceFileNameWithoutExtension + $"_{Guid.NewGuid()}.log";

                    var model = new GRBModel(this._environment, instanceFile.FullName)
                    {
                        ModelName = instanceFileNameWithoutExtension
                    };

                    if (GurobiRunner.TryToGetMstFile(instanceFile.DirectoryName, instanceFileNameWithoutExtension, out var mstFileFullName))
                    {
                        model.Read(mstFileFullName);
                    }

                    this._startTimeStamp        = DateTime.Now;
                    this._targetAlgorithmStatus = TargetAlgorithmStatus.Running;

                    this._innerCancellationTokenSource = new CancellationTokenSource(this._runnerConfiguration.CpuTimeout);

                    this._grbCallback = new GurobiCallback(
                        this._innerCancellationTokenSource.Token,
                        this._tunerConfiguration.EnableDataRecording,
                        this._startTimeStamp);
                    model.SetCallback(this._grbCallback);

                    if (this._tunerConfiguration.EnableDataRecording)
                    {
                        this._lastRuntimeFeatures = null;
                        this.SetGurobiInstanceFeatures(model);

                        // Start record timer.
                        var autoResetEvent = new AutoResetEvent(false);
                        this._recordTimer  = new Timer(
                            (timerCallback) =>
                        {
                            var currentTimeStamp = DateTime.Now;
                            if (currentTimeStamp - this._startTimeStamp < this._runnerConfiguration.CpuTimeout)
                            {
                                // Write current line to data log.
                                var currentAdapterData = this.CreateCurrentAdapterDataRecord(currentTimeStamp);
                                this.OnNewDataRecord?.Invoke(this, currentAdapterData);
                            }
                        },
                            autoResetEvent,
                            TimeSpan.FromSeconds(0),
                            this._tunerConfiguration.DataRecordUpdateInterval);
                    }

                    // Optimize. This step may be aborted in the callback.
                    model.Optimize();

                    if (this._targetAlgorithmStatus != TargetAlgorithmStatus.CancelledByGrayBox)
                    {
                        this._recordTimer?.Dispose();
                        this._targetAlgorithmStatus = this.GetIsRunInterrupted(model)
                                                                  ? TargetAlgorithmStatus.CancelledByTimeout
                                                                  : TargetAlgorithmStatus.Finished;
                    }

                    var finalTimeStamp = DateTime.Now;
                    var result         = this.CreateGurobiResult(finalTimeStamp, model);

                    if (this._tunerConfiguration.EnableDataRecording)
                    {
                        // Write last line to data log.
                        var lastAdapterData = this.CreateFinalAdapterDataRecord(finalTimeStamp, model, result);
                        this.OnNewDataRecord?.Invoke(this, lastAdapterData);
                    }

                    // Before returning, dispose of Gurobi model.
                    model.Dispose();

                    return(result);
                }
                finally
                {
                    this._recordTimer?.Dispose();
                }
            });

            return(solveTask);
        }
Example #11
0
        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();
        }
Example #12
0
    public static void Main(String[] args)
    {
        if (args.Length < 1) {
          Console.WriteLine("Usage: tsp_cs nnodes");
          return;
        }

        int n = Convert.ToInt32(args[0]);

        try {
          GRBEnv   env   = new GRBEnv();
          GRBModel model = new GRBModel(env);

          // Must disable dual reductions when using lazy constraints

          model.GetEnv().Set(GRB.IntParam.DualReductions, 0);

          double[] x = new double[n];
          double[] y = new double[n];

          Random r = new Random();
          for (int i = 0; i < n; i++) {
        x[i] = r.NextDouble();
        y[i] = r.NextDouble();
          }

          // Create variables

          GRBVar[,] vars = new GRBVar[n, n];

          for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
          vars[i, j] = model.AddVar(0.0, 1.0, distance(x, y, i, j),
                                    GRB.BINARY, "x"+i+"_"+j);

          // Integrate variables

          model.Update();

          // Degree-2 constraints

          for (int i = 0; i < n; i++) {
        GRBLinExpr expr = 0;
        for (int j = 0; j < n; j++)
          expr += vars[i, j];
        model.AddConstr(expr == 2.0, "deg2_"+i);
          }

          // Forbid edge from node back to itself

          for (int i = 0; i < n; i++)
        vars[i, i].Set(GRB.DoubleAttr.UB, 0.0);

          // Symmetric TSP

          for (int i = 0; i < n; i++)
        for (int j = 0; j < i; j++)
          model.AddConstr(vars[i, j]== vars[j, i], "");

          model.SetCallback(new tsp_cs(vars));
          model.Optimize();

          if (model.Get(GRB.IntAttr.SolCount) > 0) {
        int[] tour = findsubtour(model.Get(GRB.DoubleAttr.X, vars));

        Console.Write("Tour: ");
        for (int i = 0; i < tour.Length; i++)
          Console.Write(tour[i] + " ");
        Console.WriteLine();
          }

          // Dispose of model and environment
          model.Dispose();
          env.Dispose();

        } catch (GRBException e) {
          Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message);
          Console.WriteLine(e.StackTrace);
        }
    }
Example #13
0
        public List <int[]> SolveVRP(int timeLimitMilliseconds)
        {
            using (var env = new GRBEnv($"{DateTime.Now:yy-MM-dd-HH-mm-ss}_vrp_gurobi.log"))
                using (var vrpModel = new GRBModel(env))
                {
                    #region initialize Variables
                    var numberOfRoutes = input.Santas.Length * input.Days.Length;
                    var v = new GRBVar[numberOfRoutes][]; // [santa] visits [visit]
                    var w = new GRBVar[numberOfRoutes][]; // [santa] uses [way]


                    for (int s = 0; s < numberOfRoutes; s++)
                    {
                        v[s] = new GRBVar[visitDurations.Length];
                        for (int i = 0; i < v[s].Length; i++)
                        {
                            v[s][i] = vrpModel.AddVar(0, 1, 0.0, GRB.BINARY, $"v[{s}][{i}]");
                        }

                        w[s] = vrpModel.AddVars(distances.GetLength(0) * distances.GetLength(1), GRB.BINARY);
                    }
                    #endregion initialize Variables

                    #region add constraints
                    SelfieConstraint(vrpModel, numberOfRoutes, w);
                    VisitVisitedOnce(vrpModel, numberOfRoutes, v);
                    IncomingOutgoingWaysConstraints(vrpModel, numberOfRoutes, w, v);
                    IncomingOutgoingSanta(vrpModel, numberOfRoutes, v, w);
                    IncomingOutgoingSantaHome(vrpModel, numberOfRoutes, w, v);
                    NumberOfWaysMatchForSanta(vrpModel, numberOfRoutes, v, w);
                    BreakHandling(vrpModel, numberOfRoutes, v);
                    #endregion add constraints

                    var totalWayTime = new GRBLinExpr(0);
                    var longestRoute = vrpModel.AddVar(0, input.Days.Max(d => d.to - d.from), 0, GRB.CONTINUOUS,
                                                       "longestRoute");
                    for (int s = 0; s < numberOfRoutes; s++)
                    {
                        var routeTime = new GRBLinExpr(0);
                        for (int i = 0; i < visitDurations.Length; i++)
                        {
                            routeTime += v[s][i] * visitDurations[i];
                            for (int j = 0; j < visitDurations.Length; j++)
                            {
                                routeTime += AccessW(w[s], i, j) * distances[i, j];
                            }
                        }

                        totalWayTime += routeTime;
                        vrpModel.AddConstr(longestRoute >= routeTime, $"longesRouteConstr{s}");
                    }

                    vrpModel.SetObjective(
                        +(40d / 3600d) * totalWayTime
                        + (30d / 3600d) * longestRoute
                        , GRB.MINIMIZE);

                    vrpModel.Parameters.LazyConstraints = 1;
                    vrpModel.SetCallback(new VRPCallbackSolverCallback(w, AccessW, ConvertBack));
                    vrpModel.Parameters.TimeLimit = timeLimitMilliseconds / 1000;
                    InitializeModel(w, v, numberOfRoutes, input.Visits, input.Santas.Length);
                    vrpModel.Optimize();
                    if (vrpModel.SolCount == 0)
                    {
                        return(null);
                    }

                    var routes = new List <int[]>();
                    for (int s = 0; s < numberOfRoutes; s++)
                    {
                        var route     = new List <int>();
                        var currVisit = 0;
                        do
                        {
                            route.Add(currVisit);
                            for (int i = 0; i < visitDurations.Length; i++)
                            {
                                if (AccessW(w[s], currVisit, i).X > 0.5)
                                {
                                    currVisit = i;
                                    break;
                                }
                            }
                        } while (currVisit != 0);
                        routes.Add(route.ToArray());
                    }

                    vrpModel.Reset();
                    return(routes);
                }
        }
Example #14
0
        public static Graph RunSolver(Graph graph)
        {
            GRBEnv env = new GRBEnv();
            env.Set(GRB.IntParam.OutputFlag, 0);
            env.Set(GRB.IntParam.LogToConsole, 0);
            env.Set(GRB.IntParam.Presolve, 2);
            env.Set(GRB.DoubleParam.Heuristics, 0.0);
            GRBModel model = new GRBModel(env);
            GRBVar[] variables = new GRBVar[graph.NumberOfEdges];
            model.SetCallback(new LPSolverCallback());
            Dictionary<Edge, GRBVar> edgeVars = new Dictionary<Edge, GRBVar>();

            // Add variables to the LP model
            for (int i = 0; i < graph.NumberOfEdges; i++)
            {
                variables[i] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, "x_" + i);
                edgeVars.Add(graph.Edges[i], variables[i]);
            }
            model.Update();

            // Add constraints to the LP model
            Console.Write("\rRunning LP. Creating constraints...\r");
            //var nonTerminals = graph.Vertices.Except(graph.Terminals).ToList();
            ulong conNr = 0;
            //var terminalCombinations = new List<List<Vertex>>();

            // Assume, without loss of generality, that Terminals[0] is the root, and thus is always included
            int rootNr = 1;
            foreach (var rootTerminal in graph.Terminals)
            //var rootTerminal = graph.Terminals[0];
            {
                Console.Write("\rRunning LP. Creating constraints... {0}/{1}\r", rootNr, graph.Terminals.Count);
                foreach (var combination in GetBFS(graph, rootTerminal))
                {
                    var nodes = combination.ToList(); //new HashSet<Vertex>(combination);
                    if (nodes.Count == graph.NumberOfVertices || graph.Terminals.All(nodes.Contains))
                        continue;
                    //Debug.WriteLine("Combination: {0}", string.Join(" ", nodes));
                    //for (int i = 1; i <= nodes.Count; i++)
                    {
                        var edges = nodes//.Take(i)
                                         .SelectMany(graph.GetEdgesForVertex)
                                         .Distinct()
                                         .Where(x => x.WhereOne(y => !nodes.Contains(y)));
                        GRBLinExpr expression = 0;
                        foreach (var edge in edges)
                            expression.AddTerm(1, edgeVars[edge]);
                        model.AddConstr(expression >= 1.0, "subset_" + conNr);
                        conNr++;

                        if (conNr % 100000 == 0)
                        {
                            //model = model.Presolve(); //Pre-solve the model every 1000 constraints.
                            int constrBefore = model.GetConstrs().Length, varsBefore = model.GetVars().Length;
                            Debug.WriteLine("Presolve called.");
                            var presolved = model.Presolve();
                            Debug.WriteLine("Model has {0} constraints, {1} variables. Presolve has {2} constraints, {3} variables",
                                constrBefore, varsBefore, presolved.GetConstrs().Length, presolved.GetVars().Length);
                        }
                    }
                }

                //Debug.WriteLine("   ");
                //Debug.WriteLine("   ");
                rootNr++;
            }

            //terminalCombinations.Add(new List<Vertex>(new[] { graph.Terminals[0] }));
            //for (int j = 1; j < graph.Terminals.Count - 1; j++)
            //    terminalCombinations.AddRange(new Combinations<Vertex>(graph.Terminals.Skip(1), j).Select(combination => combination.Union(new[] { graph.Terminals[0] }).ToList()));

            //long nonTerminalSetsDone = 0;
            //long nonTerminalSets = 0;
            //for (int i = 0; i <= nonTerminals.Count; i++)
            //    nonTerminalSets += Combinations<Vertex>.NumberOfCombinations(nonTerminals.Count, i);

            //for (int i = 0; i <= nonTerminals.Count; i++)
            //{
            //    foreach (var nonTerminalSet in new Combinations<Vertex>(nonTerminals, i))
            //    {
            //        foreach (var nodes in (from a in terminalCombinations
            //                               select new HashSet<Vertex>(a.Union(nonTerminalSet))))
            //        {
            //            var edges = nodes.SelectMany(graph.GetEdgesForVertex)
            //                             .Distinct()
            //                             .Where(x => x.WhereOne(y => !nodes.Contains(y)));
            //            GRBLinExpr expression = 0;
            //            foreach (var edge in edges)
            //                expression.AddTerm(1, edgeVars[edge]);
            //            model.AddConstr(expression >= 1.0, "subset_" + conNr);
            //            conNr++;
            //        }
            //        nonTerminalSetsDone++;
            //        if (nonTerminalSetsDone % 100 == 0)
            //            Console.Write("\rRunning LP. Creating constraints... {0}/{1} ({2:0.000}%)\r", nonTerminalSetsDone, nonTerminalSets, nonTerminalSetsDone * 100.0 / nonTerminalSets);
            //    }
            //}

            // Solve the LP model
            Console.Write("\rRunning LP. Creating objective & updating...                                   \r");
            GRBLinExpr objective = new GRBLinExpr();
            for (int i = 0; i < graph.NumberOfEdges; i++)
                objective.AddTerm(graph.Edges[i].Cost, variables[i]);
            model.SetObjective(objective, GRB.MINIMIZE);
            Console.Write("\rRunning LP. Tuning...                                   \r");
            model.Tune();
            Debug.WriteLine("Presolve called.");
            model.Presolve();
            Console.Write("\rRunning LP. Solving...                               \r");
            Debug.WriteLine("Optimize called.");
            model.Optimize();

            Graph solution = graph.Clone();
            HashSet<Edge> includedEdges = new HashSet<Edge>();
            for (int i = 0; i < solution.NumberOfEdges; i++)
            {
                var value = variables[i].Get(GRB.DoubleAttr.X);
                if (value == 1)
                    includedEdges.Add(solution.Edges[i]);
            }

            foreach (var edge in solution.Edges.ToList())
                if (!includedEdges.Contains(edge))
                    solution.RemoveEdge(edge);

            Console.Write("\r                                                  \r");

            return solution;
        }
Example #15
0
        public GurobiSolver3(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();

            // All non-question fields have to belong to a word
            // E.g. if a question points right, only lengths 3 to 9 are allowed
            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 + 3 < sizeX && !crossword.HasBlock(y, x, y, x + 3))
                    {
                        // for right: if [0,0] is question, [0,1..3] must not be question
                        var totalQuestionsHorizontal = fields[y, x + 1] + fields[y, x + 2] + fields[y, x + 3];
                        m.AddConstr(fields[y, x] + (1 - questionType[y, x]) - 1 <= 1 - fields[y, x + 1], "MinWordLength3" + y + "_" + x + "_right1");
                        m.AddConstr(fields[y, x] + (1 - questionType[y, x]) - 1 <= 1 - fields[y, x + 2], "MinWordLength3" + y + "_" + x + "_right2");
                        m.AddConstr(fields[y, x] + (1 - questionType[y, x]) - 1 <= 1 - fields[y, x + 3], "MinWordLength3" + y + "_" + x + "_right3");
                    }
                    else
                    {
                        noQuestionToTheRightAllowed = true;
                    }

                    // for down:
                    if (y + 3 < sizeY && !crossword.HasBlock(y, x, y + 3, x))
                    {
                        var totalQuestionsVertical = fields[y + 1, x] + fields[y + 2, x] + fields[y + 3, x];
                        m.AddConstr(fields[y, x] + questionType[y, x] - 1 <= 1 - fields[y + 1, x], "MinWordLength3" + y + "_" + x + "_down1");
                        m.AddConstr(fields[y, x] + questionType[y, x] - 1 <= 1 - fields[y + 2, x], "MinWordLength3" + y + "_" + x + "_down2");
                        m.AddConstr(fields[y, x] + questionType[y, x] - 1 <= 1 - fields[y + 3, x], "MinWordLength3" + y + "_" + x + "_down3");
                    }
                    else
                    {
                        noQuestionTowardsDownAllowed = true;
                    }

                    if (noQuestionToTheRightAllowed && noQuestionTowardsDownAllowed)
                    {
                        m.AddConstr(fields[y, x] == 0, "NoQuestionAllowed" + 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
                    if (x + maxWordLength + 1 < sizeX && !crossword.HasBlock(y, x, y, x + maxWordLength + 1))
                    {
                        // for right: if [0,0] is question, [0,1..maxLength+1] must have at least another question field
                        var allHorizontalFields = new GRBLinExpr();
                        for (int xi = 1; xi <= maxWordLength + 1; xi++)
                        {
                            allHorizontalFields += fields[y, x + xi];
                        }
                        m.AddConstr(fields[y, x] + (1 - questionType[y, x]) - 1 <= allHorizontalFields, "MaxLengthHorizontal" + y + "_" + x);
                    }
                    if (y + maxWordLength + 1 < sizeY && !crossword.HasBlock(y, x, y + maxWordLength + 1, x))
                    {
                        // for down:
                        var allVerticalFields = new GRBLinExpr();
                        for (int yi = 1; yi <= maxWordLength + 1; yi++)
                        {
                            allVerticalFields += fields[y + yi, x];
                        }
                        m.AddConstr(fields[y, x] + questionType[y, x] - 1 <= allVerticalFields, "MaxLengthVertical" + y + "_" + x);
                    }
                }
            }

            // Does a field belong to zero, one or two questions?
            var partOfAWord = new GRBLinExpr[sizeY, sizeX, 2];

            for (int y = 0; y < sizeY; y++)
            {
                for (int x = 0; x < sizeX; x++)
                {
                    // this constraint doesn't work for [0,0]
                    if ((x == 0 && y == 0) || crossword.HasBlock(y, x))
                    {
                        continue;
                    }

                    // does this field have a question to the left?

                    /*var attachedToHorizontalQuestion = new GRBLinExpr();
                     * for (int l = 0; l < maxWordLength; l++)
                     * {
                     *  if (x - l - 1 < 0) continue;
                     *  var attachedToHorizontalQuestionSpecificLength = m.AddVar(0, 1, 0, GRB.BINARY, "varAttachedToHorizontalQuestionLength" + l + "_" + y + "_" + x);
                     *  // If the first field is a question and points to the right, and no letters inbetween are questions
                     *  var isQuestionAndPointsRight = fields[y, x - l - 1] + (1 - questionType[y, x - l - 1]);
                     *  var allHorizontalFields = new GRBLinExpr();
                     *  for (int xi = 0; xi <= l; xi++)
                     *      allHorizontalFields += fields[y, x - xi];
                     *  m.AddConstr(attachedToHorizontalQuestionSpecificLength >= isQuestionAndPointsRight - 1 - allHorizontalFields);
                     *  for (int xi = 0; xi <= l; xi++) m.AddConstr(attachedToHorizontalQuestionSpecificLength <= 1 - fields[y, x - xi]);
                     *  m.AddConstr(attachedToHorizontalQuestionSpecificLength <= fields[y, x - l - 1]);
                     *  m.AddConstr(attachedToHorizontalQuestionSpecificLength <= 1 - questionType[y, x - l - 1]);
                     *  //m.AddConstr(attachedToHorizontalQuestionSpecificLength <= isQuestionAndPointsRight * 0.5);
                     *  //m.AddConstr(attachedToHorizontalQuestionSpecificLength <= 1 - allHorizontalFields * (1d / (l + 1)));
                     *  attachedToHorizontalQuestion += attachedToHorizontalQuestionSpecificLength;
                     * }*/
                    // RETRY
                    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]);
                        var questionsInbetween       = new GRBLinExpr();
                        for (int xi = 0; xi < len; xi++)
                        {
                            questionsInbetween += fields[y, x - xi];
                        }
                        m.AddConstr(attachedToHorizontalQuestion >= isQuestionAndPointsRight - 1 - questionsInbetween);

                        // 0 IF first question is not pointing right OR there is no question to the left
                        // firstQuestion ==> total fields < 2
                        m.AddConstr(attachedToHorizontalQuestion <= questionsInbetween + (1 - fields[y, x - len]) + 1 - questionType[y, x - len]); // 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);
                    }

                    // does this field have a question towards down?

                    /*var attachedToVerticalQuestion = new GRBLinExpr();
                     * for (int l = 0; l < maxWordLength; l++)
                     * {
                     *  if (y - l - 1 < 0) continue;
                     *  var attachedToVerticalQuestionSpecificLength = m.AddVar(0, 1, 0, GRB.BINARY, "varAttachedToVerticalQuestionLength" + l + "_" + y + "_" + x);
                     *  // If the first field is a question and points to the right, and no letters inbetween are questions
                     *  var isQuestionAndPointsDown = fields[y - l - 1, x] + questionType[y - l - 1, x];
                     *  var allVerticalFields = new GRBLinExpr();
                     *  for (int yi = 0; yi <= l; yi++)
                     *      allVerticalFields += fields[y - yi, x];
                     *  m.AddConstr(attachedToVerticalQuestionSpecificLength >= isQuestionAndPointsDown - 1 - allVerticalFields);
                     *  for (int yi = 0; yi <= l; yi++) m.AddConstr(attachedToVerticalQuestionSpecificLength <= 1 - fields[y - yi, x]);
                     *  m.AddConstr(attachedToVerticalQuestionSpecificLength <= fields[y - l - 1, x]);
                     *  m.AddConstr(attachedToVerticalQuestionSpecificLength <= questionType[y - l - 1, x]);
                     *  //m.AddConstr(attachedToVerticalQuestionSpecificLength <= isQuestionAndPointsDown * 0.5);
                     *  //m.AddConstr(attachedToVerticalQuestionSpecificLength <= 1 - allVerticalFields * (1d / (l + 1)));
                     *  attachedToVerticalQuestion += attachedToVerticalQuestionSpecificLength;
                     * }*/
                    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];
                        var questionsInbetween      = new GRBLinExpr();
                        for (int yi = 0; yi < len; yi++)
                        {
                            questionsInbetween += fields[y - yi, x];
                        }
                        m.AddConstr(attachedToVerticalQuestion >= isQuestionAndPointsDown - 1 - questionsInbetween);

                        m.AddConstr(attachedToVerticalQuestion <= questionsInbetween + (1 - fields[y - len, x]) + 1 - (1 - questionType[y - len, x])); // the first question but DOESNT look down
                    }
                    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);
                    }

                    var c = m.AddConstr(attachedToHorizontalQuestion + attachedToVerticalQuestion >= 1 - fields[y, x], "AttachedToQuestionConstraint_" + y + "_" + x);
                    //c.Lazy = 1;
                    partOfAWord[y, x, 0] = attachedToHorizontalQuestion;
                    partOfAWord[y, x, 1] = attachedToVerticalQuestion;
                }
            }

            // 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 - 3; y < sizeY; y++)
            {
                for (int x = sizeX - 3; x < sizeX; x++)
                {
                    if (!crossword.HasBlock(y, x))
                    {
                        m.AddConstr(fields[y, x] == 0);
                    }
                }
            }

            // 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");

            // dead fields
            var uncrossedLetters        = new GRBVar[sizeY, sizeX];
            var uncrossedLettersPenalty = new GRBLinExpr();

            for (int y = 0; y < sizeY; y++)
            {
                for (int x = 0; x < sizeX; x++)
                {
                    if ((x >= 1 || y >= 1) && !crossword.HasBlock(y, x))
                    {
                        uncrossedLetters[y, x] = m.AddVar(0, 1, 0, GRB.BINARY, "isUncrossedLetter" + y + "_" + x);
                        m.AddConstr(uncrossedLetters[y, x] <= partOfAWord[y, x, 0] + partOfAWord[y, x, 1]);                // if 0 ==> 0 NECESSARY?
                        m.AddConstr(uncrossedLetters[y, x] <= 2 - partOfAWord[y, x, 0] - partOfAWord[y, x, 1]);            // if 2 ==> 0
                        m.AddConstr(uncrossedLetters[y, x] <= 1 - fields[y, x]);                                           // if it's a question it can't be a dead field

                        m.AddConstr(uncrossedLetters[y, x] >= partOfAWord[y, x, 0] - partOfAWord[y, x, 1] - fields[y, x]); // horizontal XOR vertical
                        m.AddConstr(uncrossedLetters[y, x] >= partOfAWord[y, x, 1] - partOfAWord[y, x, 0] - fields[y, x]);
                        uncrossedLettersPenalty += uncrossedLetters[y, x];
                    }
                }
            }
            // penalty for nearby uncrossed letters
            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 (x >= 1 && y >= 1 && !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);
                        }
                        if (hbx)
                        {
                            m.AddConstr(isDeadArea >= uncrossedLetters[y, x] + uncrossedLetters[y, x - 1] - 1);
                        }
                        m.AddConstr(isDeadArea <= uncrossedLetters[y, x]);
                        if (hby && hbx)
                        {
                            m.AddConstr(isDeadArea <= uncrossedLetters[y - 1, x] + uncrossedLetters[y, x - 1]);
                        }
                        else if (hby)
                        {
                            m.AddConstr(isDeadArea <= uncrossedLetters[y - 1, x]);
                        }
                        else if (hbx)
                        {
                            m.AddConstr(isDeadArea <= uncrossedLetters[y, x - 1]);
                        }
                        deadFieldPenalty += isDeadArea;
                    }
                }
            }


            // 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();
                    int ct           = 0;
                    for (int i = 0; i < area; i++)
                    {
                        for (int j = 0; j < area; j++)
                        {
                            if (crossword.HasBlock(y + i, x + j))
                            {
                                continue;
                            }
                            clusterTotal += fields[y + i, x + j];
                            ct++;
                        }
                    }
                    if (ct >= 3)
                    {
                        var varClusterTotalPenalty = m.AddVar(0, 1, 0, GRB.BINARY, "varClusterTotalPenalty" + y + "_" + x);
                        // 0-1 = good, 2-4 = bad
                        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(deadFieldPenalty + clusterPenalty, GRB.MINIMIZE);

            m.SetCallback(new GRBMipSolCallback(crossword, fields, questionType, null));

            m.Optimize();
            m.ComputeIIS();
            m.Write("model.ilp");

            m.Dispose();
            env.Dispose();
        }