Exemple #1
0
    static void Main(string[] args)
    {
        if (args.Length < 1) {
          Console.Out.WriteLine("Usage: feasopt_cs filename");
          return;
        }

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

          // Create a copy to use FeasRelax feature later */
          GRBModel feasmodel1 = new GRBModel(feasmodel);

          // Clear objective
          feasmodel.SetObjective(new GRBLinExpr());

          // Add slack variables
          GRBConstr[] c = feasmodel.GetConstrs();
          for (int i = 0; i < c.Length; ++i) {
        char sense = c[i].Get(GRB.CharAttr.Sense);
        if (sense != '>') {
          GRBConstr[] constrs = new GRBConstr[] { c[i] };
          double[] coeffs = new double[] { -1 };
          feasmodel.AddVar(0.0, GRB.INFINITY, 1.0, GRB.CONTINUOUS, constrs,
                           coeffs, "ArtN_" + c[i].Get(GRB.StringAttr.ConstrName));
        }
        if (sense != '<') {
          GRBConstr[] constrs = new GRBConstr[] { c[i] };
          double[] coeffs = new double[] { 1 };
          feasmodel.AddVar(0.0, GRB.INFINITY, 1.0, GRB.CONTINUOUS, constrs,
                           coeffs, "ArtP_" +
                               c[i].Get(GRB.StringAttr.ConstrName));
        }
          }
          feasmodel.Update();

          // Optimize modified model
          feasmodel.Write("feasopt.lp");
          feasmodel.Optimize();

          // Use FeasRelax feature */
          feasmodel1.FeasRelax(GRB.FEASRELAX_LINEAR, true, false, true);
          feasmodel1.Write("feasopt1.lp");
          feasmodel1.Optimize();

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

        } catch (GRBException e) {
          Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message);
        }
    }
Exemple #2
0
    static void Main()
    {
        try {
          GRBEnv    env   = new GRBEnv("mip1.log");
          GRBModel  model = new GRBModel(env);

          // Create variables

          GRBVar x = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, "x");
          GRBVar y = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, "y");
          GRBVar z = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, "z");

          // Integrate new variables

          model.Update();

          // Set objective: maximize x + y + 2 z

          model.SetObjective(x + y + 2 * z, GRB.MAXIMIZE);

          // Add constraint: x + 2 y + 3 z <= 4

          model.AddConstr(x + 2 * y + 3 * z <= 4.0, "c0");

          // Add constraint: x + y >= 1

          model.AddConstr(x + y >= 1.0, "c1");

          // Optimize model

          model.Optimize();

          Console.WriteLine(x.Get(GRB.StringAttr.VarName)
                         + " " + x.Get(GRB.DoubleAttr.X));
          Console.WriteLine(y.Get(GRB.StringAttr.VarName)
                         + " " + y.Get(GRB.DoubleAttr.X));
          Console.WriteLine(z.Get(GRB.StringAttr.VarName)
                         + " " + z.Get(GRB.DoubleAttr.X));

          Console.WriteLine("Obj: " + model.Get(GRB.DoubleAttr.ObjVal));

          // Dispose of model and env

          model.Dispose();
          env.Dispose();

        } catch (GRBException e) {
          Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message);
        }
    }
        public HttpResponseMessage Optimize(string RunName)
        {
            using (var dbConn = new ApplicationDbContext())
            {
                //Variables for students, semesters, courses, and course/semester offerings
                students = dbConn.StudentPreferences.Where(m => m.IsActive == true).Include(m => m.Courses).Include(m => m.Student.CompletedCourses).OrderByDescending(m => m.Student.CompletedCourses.Count()).ToArray();
                crssems = dbConn.CourseSemesters.Where(m => m.IsActive == true).Include(m => m.Course).Include(m => m.Semester).ToArray();
                courses = crssems.Select(m => m.Course).Distinct().ToArray();
                sems = crssems.Select(m => m.Semester).Distinct().OrderBy(m => m.Type).OrderBy(m => m.Year).ToArray();

                var completed = dbConn.CompletedCourses.ToList();
                try
                {
                    GRBEnv env = new GRBEnv("mip1.log");
                    GRBModel model = new GRBModel(env);
                    model.Set(GRB.StringAttr.ModelName, "Course Optimizer");
                    GRBVar[,] slacks = new GRBVar[courses.Length, sems.Length];

                    //Assignment of student, course, and semester.  Student must have a desire to take the coure, has not taken the course, and the course is offered to be included
                    GRBVar[,,] CourseAllocation = new GRBVar[students.Length, courses.Length, sems.Length];
                    for (int i = 0; i < students.Length; i++)
                    {
                        for (int j = 0; j < courses.Length; j++)
                        {
                            for (int k = 0; k < sems.Length; k++)
                            {
                                if (students[i].Courses.Contains(courses[j]) && !completed.Any(m => m.GaTechId == students[i].GaTechId && courses[j].ID == m.Course_ID) && crssems.Contains(crssems.SingleOrDefault(m => m.Course == courses[j] && m.Semester == sems[k])))
                                    CourseAllocation[i, j, k] = model.AddVar(0, 1, 1, GRB.BINARY, "students." + (i + 1).ToString() + "_Course." + (j + 1).ToString() + "_Semester." + (k + 1).ToString());
                                else
                                    CourseAllocation[i, j, k] = model.AddVar(0, 0, 1, GRB.BINARY, "students." + (i + 1).ToString() + "_Course." + (j + 1).ToString() + "_Semester." + (k + 1).ToString());
                            }
                        }
                    }
                    model.Set(GRB.IntAttr.ModelSense, 1);
                    model.Update();

                    //MUST TAKE DESIRED COURSE ONLY ONCE
                    //Constrains the students to only take courses they desire once and for when the course is offered and does not allow a repeat of a course in another semester
                    for (int i = 0; i < students.Length; i++)
                    {
                        for (int j = 0; j < courses.Length; j++)
                        {
                            if (students[i].Courses.Contains(courses[j]) && !completed.Any(m => m.GaTechId == students[i].GaTechId && courses[j].ID == m.Course_ID))
                            {
                                GRBLinExpr constStudentDesiredCourses = 0.0;
                                for (int k = 0; k < sems.Length; k++)
                                {
                                    if (crssems.Any(m => m.Course.ID == courses[j].ID && m.Semester.Type == sems[k].Type && m.Semester.Year == sems[k].Year))
                                        constStudentDesiredCourses.AddTerm(1.0, CourseAllocation[i, j, k]);
                                }
                                String sStudentDesiredCourses = "DesiredCourse." + j + 1 + "_Student." + i + 1;
                                model.AddConstr(constStudentDesiredCourses == 1, sStudentDesiredCourses);
                            }
                        }

                        //MAX COURSES PER SEMESTER
                        //Constrains the students to only have a maximum number of 2 courses per semester.
                        for (int k = 0; k < sems.Length; k++)
                        {
                            GRBLinExpr constMaxPerSem = 0.0;
                            for (int j = 0; j < courses.Length; j++)
                            {
                                if (!completed.Any(m => m.GaTechId == students[i].GaTechId && courses[j].ID == m.Course_ID) && (crssems.Any(m => m.Course.ID == courses[j].ID && m.Semester.Type == sems[k].Type && m.Semester.Year == sems[k].Year)))
                                    constMaxPerSem.AddTerm(1, CourseAllocation[i, j, k]);
                            }
                            String sCourseSem = "maxCourseStudent." + i + 1 + "_Semester." + k + 1;
                            model.AddConstr(constMaxPerSem <= MAX_COURSES_PER_SEMESTER, sCourseSem);
                        }

                        //PREREQUISITES
                        //Constrains the students to take prerequisite courses prior to taking a course that needs the prerequisite
                        for (int j = 0; j < courses.Length; j++)
                        {
                            if (courses[j].Prerequisites.Any() && students[i].Courses.Contains(courses[j]) && !completed.Any(m => m.GaTechId == students[i].GaTechId && courses[j].ID == m.Course_ID))
                            {

                                foreach (var prereq in courses[j].Prerequisites)
                                {
                                    int prereqIndex = Array.IndexOf(courses, prereq);
                                    GRBLinExpr coursePrereqConst1 = 0.0;
                                    GRBLinExpr coursePrereqConst2 = 0.0;
                                    if (!completed.Any(m => m.GaTechId == students[i].GaTechId && m.Course.ID == prereq.ID))
                                    {

                                        for (int k = 0; k < sems.Length; k++)
                                        {

                                            if (prereqIndex >= 0)
                                            {
                                                coursePrereqConst1.AddTerm(k + 1, CourseAllocation[i, prereqIndex, k]);
                                                coursePrereqConst2.AddTerm(k, CourseAllocation[i, j, k]);
                                            }
                                        }
                                    }
                                    model.AddConstr(coursePrereqConst1, GRB.LESS_EQUAL, coursePrereqConst2, "PREREQ_Student" + i + "_Course+" + j + "_Prereq" + prereqIndex);

                                }

                            }
                        }
                    }

                    //SENIORITY
                    //Students are already ordered from dB query by seniority in descending order and puts a preference to senior students over the next student that desires that
                    //same course with less seniority.
                    for (int j = 0; j < courses.Length; j++)
                    {
                        for (int i = 0; i < students.Length - 1; i++)
                        {
                            if (students[i].Courses.Contains(courses[j]) && !completed.Any(m => m.GaTechId == students[i].GaTechId && courses[j].ID == m.Course_ID))
                            {
                                int SemsRemain = (students[i].Courses.Count - students[i].Student.CompletedCourses.Count) / 2 + (students[i].Courses.Count - students[i].Student.CompletedCourses.Count) % 2;
                                for (int n = i + 1; n < students.Length; n++)
                                {
                                    if (students[n].Courses.Contains(courses[j]) && !completed.Any(m => m.GaTechId == students[n].GaTechId && courses[j].ID == m.Course_ID))
                                    {
                                        GRBLinExpr​ seniority = 0.0;
                                        for (int k = 0; k < sems.Length; k++)
                                        {
                                            if (crssems.Any(m => m.Course.ID == courses[j].ID && m.Semester.Type == sems[k].Type && m.Semester.Year == sems[k].Year))
                                            {
                                                if (k <= SemsRemain)
                                                {
                                                    seniority​.AddTerm(1.0, CourseAllocation[i, j, k]);
                                                    seniority​.AddTerm(-1.0, CourseAllocation​[n, j, k]);
                                                }
                                                else
                                                {
                                                    seniority​.AddTerm(-1.0, CourseAllocation[i, j, k]);
                                                    seniority​.AddTerm(1.0, CourseAllocation​[n, j, k]);
                                                }
                                            }
                                        }
                                        model.AddConstr(seniority, GRB.GREATER_EQUAL, 0, "Seniority for Student." + students[i] + "_Course." + courses[j]);
                                        break;
                                    }
                                }
                            }
                        }
                    }

                    //Add the slack variable for all semester & course offerings then constrain the maximum number of students
                    //to take a couse in a semester.
                    for (int k = 0; k < sems.Length; k++)
                    {
                        for (int j = 0; j < courses.Length; j++)
                        {
                            if (crssems.Any(m => m.Course.ID == courses[j].ID && m.Semester.Type == sems[k].Type && m.Semester.Year == sems[k].Year))
                            {
                                slacks[j, k] = model.AddVar(0, GRB.INFINITY, 0, GRB.INTEGER, sems[k].Type.ToString() + "." + sems[k].Year.ToString() + "." + courses[j].Name + ".Slacks");
                                GRBLinExpr constMaxStudCrsSem = 0.0;
                                for (int i = 0; i < students.Length; i++)
                                {
                                    if (!completed.Any(m => m.GaTechId == students[i].GaTechId && courses[j].ID == m.Course_ID))
                                        constMaxStudCrsSem.AddTerm(1.0, CourseAllocation[i, j, k]);
                                }
                                model.Update();
                                constMaxStudCrsSem.AddTerm(-1.0, slacks[j, k]);
                                model.AddConstr(constMaxStudCrsSem <= crssems.Single(m => m.Course.ID == courses[j].ID && m.Semester.Type == sems[k].Type && m.Semester.Year == sems[k].Year).StudentLimit, sems[k].Type.ToString() + "." + sems[k].Year.ToString() + "." + courses[j].Name);
                            }
                        }
                    }

                    //Add total slack to the optimization model for all courses in the semesters they are offered.
                    GRBVar totSlack = model.AddVar(0, GRB.INFINITY, 0, GRB.INTEGER, "totSlack");
                    GRBLinExpr lhs = new GRBLinExpr();
                    lhs.AddTerm(-1.0, totSlack);
                    for (int j = 0; j < courses.Length; j++)
                    {
                        for (int k = 0; k < sems.Length; k++)
                        {
                            if (crssems.Any(m => m.Course.ID == courses[j].ID && m.Semester.Type == sems[k].Type && m.Semester.Year == sems[k].Year))
                                lhs.AddTerm(1.0, slacks[j, k]);
                        }
                    }
                    model.Update();
                    model.AddConstr(lhs, GRB.EQUAL, 0, "totSlack");

                    // Objective: minimize the total slack
                    GRBLinExpr obj = new GRBLinExpr();
                    obj.AddTerm(1.0, totSlack);
                    model.SetObjective(obj);

                    //Optimize the model to minimize the total slack and maximize students to course offerings based on input variables and constraints.
                    model.Optimize();

                    //Write Results optimization results to database
                    writeResults(CourseAllocation, students, courses, sems, crssems, dbConn, Convert.ToInt32(model.Get(GRB.DoubleAttr.ObjVal)), RunName);

                    //Clean-Up
                    model.Dispose();
                    env.Dispose();
            }
                catch (Exception e)
            {
                return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "An Error occured while running the optimization.");
            }
            }
            return Request.CreateResponse(HttpStatusCode.OK);
        }
Exemple #4
0
    protected static bool dense_optimize(GRBEnv    env,
                   int       rows,
                   int       cols,
                   double[]  c,      // linear portion of objective function
                   double[,] Q,      // quadratic portion of objective function
                   double[,] A,      // constraint matrix
                   char[]    sense,  // constraint senses
                   double[]  rhs,    // RHS vector
                   double[]  lb,     // variable lower bounds
                   double[]  ub,     // variable upper bounds
                   char[]    vtype,  // variable types (continuous, binary, etc.)
                   double[]  solution)
    {
        bool success = false;

        try {
          GRBModel model = new GRBModel(env);

          // Add variables to the model

          GRBVar[] vars = model.AddVars(lb, ub, null, vtype, null);
          model.Update();

          // Populate A matrix

          for (int i = 0; i < rows; i++) {
        GRBLinExpr expr = new GRBLinExpr();
        for (int j = 0; j < cols; j++)
          if (A[i,j] != 0)
            expr.AddTerm(A[i,j], vars[j]); // Note: '+=' would be much slower
        model.AddConstr(expr, sense[i], rhs[i], "");
          }

          // Populate objective

          GRBQuadExpr obj = new GRBQuadExpr();
          if (Q != null) {
        for (int i = 0; i < cols; i++)
          for (int j = 0; j < cols; j++)
            if (Q[i,j] != 0)
              obj.AddTerm(Q[i,j], vars[i], vars[j]); // Note: '+=' would be much slower
        for (int j = 0; j < cols; j++)
          if (c[j] != 0)
            obj.AddTerm(c[j], vars[j]); // Note: '+=' would be much slower
        model.SetObjective(obj);
          }

          // Solve model

          model.Optimize();

          // Extract solution

          if (model.Get(GRB.IntAttr.Status) == GRB.Status.OPTIMAL) {
        success = true;

        for (int j = 0; j < cols; j++)
          solution[j] = vars[j].Get(GRB.DoubleAttr.X);
          }

          model.Dispose();

        } catch (GRBException e) {
          Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message);
        }

        return success;
    }
Exemple #5
0
    dense_optimize(GRBEnv env,
                   int rows,
                   int cols,
                   double[]  c,      // linear portion of objective function
                   double[,] Q,      // quadratic portion of objective function
                   double[,] A,      // constraint matrix
                   char[]    sense,  // constraint senses
                   double[]  rhs,    // RHS vector
                   double[]  lb,     // variable lower bounds
                   double[]  ub,     // variable upper bounds
                   char[]    vtype,  // variable types (continuous, binary, etc.)
                   double[]  solution)
    {
        bool success = false;

        try {
            GRBModel model = new GRBModel(env);

            // Add variables to the model

            GRBVar[] vars = model.AddVars(lb, ub, null, vtype, null);
            model.Update();

            // Populate A matrix

            for (int i = 0; i < rows; i++)
            {
                GRBLinExpr expr = new GRBLinExpr();
                for (int j = 0; j < cols; j++)
                {
                    if (A[i, j] != 0)
                    {
                        expr.AddTerm(A[i, j], vars[j]); // Note: '+=' would be much slower
                    }
                }
                model.AddConstr(expr, sense[i], rhs[i], "");
            }

            // Populate objective

            GRBQuadExpr obj = new GRBQuadExpr();
            if (Q != null)
            {
                for (int i = 0; i < cols; i++)
                {
                    for (int j = 0; j < cols; j++)
                    {
                        if (Q[i, j] != 0)
                        {
                            obj.AddTerm(Q[i, j], vars[i], vars[j]); // Note: '+=' would be much slower
                        }
                    }
                }
                for (int j = 0; j < cols; j++)
                {
                    if (c[j] != 0)
                    {
                        obj.AddTerm(c[j], vars[j]); // Note: '+=' would be much slower
                    }
                }
                model.SetObjective(obj);
            }

            // Solve model

            model.Optimize();

            // Extract solution

            if (model.Get(GRB.IntAttr.Status) == GRB.Status.OPTIMAL)
            {
                success = true;

                for (int j = 0; j < cols; j++)
                {
                    solution[j] = vars[j].Get(GRB.DoubleAttr.X);
                }
            }

            model.Dispose();
        } catch (GRBException e) {
            Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message);
        }

        return(success);
    }
        public OptimizationResult Solve(long timelimitMiliseconds, EventHandler <ProgressReport> progress,
                                        EventHandler <string> consoleProgress)
        {
            var sw     = Stopwatch.StartNew();
            var output = new OptimizationResult
            {
                OptimizationInput = input
            };

            var timeWindowIsRelevant = !input.Visits.All(visit =>
            {
                if (visit.Desired.Length > 0)
                {
                    return(false);
                }

                return(!visit.Unavailable.Any(unavailable =>
                {
                    var(unavailableFrom, unavailableTo) = unavailable;
                    foreach (var(dayFrom, dayTo) in input.Days)
                    {
                        if (IntersectionLength(unavailableFrom, unavailableTo, dayFrom, dayTo) > 0)
                        {
                            return true;
                        }
                    }
                    return false;
                }));
            });

            // first solve vrp, take result as initial solution.
            if (!timeWindowIsRelevant)
            {
                vrpTimeLimitFactor = 1;
            }

            var vrpSolution = input.Visits.Length > 75 ? FakeVRPSolution(input.Santas.Length * input.Days.Length) : new VRPCallbackSolver(input).SolveVRP((int)(timelimitMiliseconds * vrpTimeLimitFactor));

            consoleProgress?.Invoke(this, $"vrp needed {sw.ElapsedMilliseconds}ms, remaining {timelimitMiliseconds - sw.ElapsedMilliseconds}");

            if (!timeWindowIsRelevant)
            {
                BuildResultFromVRP(output, vrpSolution);
                output.TimeElapsed = sw.ElapsedMilliseconds / 1000;
                return(output);
            }

            timelimitMiliseconds -= sw.ElapsedMilliseconds;
            using (var env = new GRBEnv($"{DateTime.Now:yy-MM-dd-HH-mm-ss}_gurobi.log"))
                using (var model = 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]
                    var c = new GRBVar[numberOfRoutes][]; // [santa] visits visit at the end of [way]

                    var desiredDuration     = new GRBVar[numberOfRoutes][][];
                    var unavailableDuration = new GRBVar[numberOfRoutes][][];

                    var maxRoutes = new GRBVar[numberOfRoutes];
                    var minRoutes = new GRBVar[numberOfRoutes];

                    for (int s = 0; s < numberOfRoutes; s++)
                    {
                        v[s] = new GRBVar[visitDurations.Length];
                        c[s] = new GRBVar[visitDurations.Length];
                        desiredDuration[s]     = new GRBVar[visitDurations.Length][];
                        unavailableDuration[s] = new GRBVar[visitDurations.Length][];
                        var(dayStart, dayEnd)  = input.Days[s / input.Santas.Length];
                        var dayDuration = dayEnd - dayStart;
                        for (int i = 0; i < v[s].Length; i++)
                        {
                            v[s][i] = model.AddVar(0, 1, 0.0, GRB.BINARY, GurobiVarName($"v[{s}][{i}]"));
                            c[s][i] = model.AddVar(0, dayDuration, 0, GRB.CONTINUOUS, GurobiVarName($"c[{s}][{i}]"));

                            if (i > 0)
                            {
                                var visit = input.Visits[i - 1];
                                desiredDuration[s][i]     = new GRBVar[visit.Desired.Length];
                                unavailableDuration[s][i] = new GRBVar[visit.Unavailable.Length];

                                for (int d = 0; d < visit.Desired.Length; d++)
                                {
                                    var ub = Math.Max(0, Math.Min(visit.Duration, visit.Desired[d].to - visit.Desired[d].from));
                                    desiredDuration[s][i][d] = model.AddVar(0, ub, 0, GRB.CONTINUOUS, GurobiVarName($"desiredDuration[{s}][{i}][{d}]"));
                                }

                                for (int u = 0; u < visit.Unavailable.Length; u++)
                                {
                                    var ub = Math.Max(0, Math.Min(visit.Duration, visit.Unavailable[u].to - visit.Unavailable[u].from));
                                    unavailableDuration[s][i][u] = model.AddVar(0, ub, 0, GRB.CONTINUOUS, GurobiVarName($"unavailableDuration[{s}][{i}][{u}]"));
                                }
                            }
                        }

                        w[s]         = model.AddVars(distances.GetLength(0) * distances.GetLength(1), GRB.BINARY);
                        maxRoutes[s] = model.AddVar(0, dayEnd - dayStart, 0, GRB.CONTINUOUS, GurobiVarName($"santa{s} maxRoute"));
                        minRoutes[s] = model.AddVar(0, dayEnd - dayStart, 0, GRB.CONTINUOUS, GurobiVarName($"santa{s} minRoute"));
                    }

                    #endregion initialize Variables


                    #region add constraints
                    SelfieConstraint(model, numberOfRoutes, w);

                    // visit visited once
                    VisitVisitedOnce(model, numberOfRoutes, v);
                    // breaks
                    BreakHandling(model, numberOfRoutes, v);

                    // number of ways = number of visits + home
                    NumberOfWaysMatchForSanta(model, numberOfRoutes, v, w);

                    // incoming & outgoing constraint
                    IncomingOutgoingGlobal(model, numberOfRoutes, w);
                    IncomingOutgoingSanta(model, numberOfRoutes, v, w);

                    IncomingOutgoingSantaHome(model, numberOfRoutes, w, v);

                    IncreasingC(model, numberOfRoutes, w, c, v);

                    DesiredOverlap(model, numberOfRoutes, v, w, c, desiredDuration);

                    UnavailableOverlap(model, numberOfRoutes, v, w, c, unavailableDuration, true);

                    FillMaxRoute(model, maxRoutes, c, v);
                    FillMinRoutes(model, minRoutes, c, v);
                    MinRouteSmallerThanMaxRoute(model, minRoutes, maxRoutes);

                    // Symmetry breaking constraint if no breaks
                    if (!input.Visits.Any(visit => visit.IsBreak))
                    {
                        for (int d = 0; d < input.Days.Length; d++)
                        {
                            var dayOffset = d * input.Santas.Length;
                            for (int s = 1; s < input.Santas.Length; s++)
                            {
                                model.AddConstr(maxRoutes[dayOffset + s] - minRoutes[dayOffset + s] <= maxRoutes[dayOffset + s - 1] - minRoutes[dayOffset + s - 1], null);
                            }
                        }
                    }

                    #endregion add constraints

                    // TARGET FUNCTION
                    var totalWayTime = new GRBLinExpr(0);
                    var longestRoute = model.AddVar(0, input.Days.Max(d => d.to - d.from), 0, GRB.CONTINUOUS, "longestRoute");
                    for (int s = 0; s < numberOfRoutes; s++)
                    {
                        totalWayTime += (maxRoutes[s] - minRoutes[s]);
                        model.AddConstr(longestRoute >= maxRoutes[s] - minRoutes[s], $"longesRouteConstr{s}");
                    }

                    var desiredSum     = new GRBLinExpr(0);
                    var unavailableSum = new GRBLinExpr(0);
                    for (int s = 0; s < numberOfRoutes; s++)
                    {
                        for (int i = 1; i < visitDurations.Length; i++)
                        {
                            var visit = input.Visits[i - 1];
                            for (int d = 0; d < visit.Desired.Length; d++)
                            {
                                if (!(desiredDuration[s][i][d] is null))
                                {
                                    desiredSum += desiredDuration[s][i][d];
                                }
                            }

                            for (int u = 0; u < visit.Unavailable.Length; u++)
                            {
                                if (!(unavailableDuration[s][i][u] is null))
                                {
                                    unavailableSum += unavailableDuration[s][i][u];
                                }
                            }
                        }
                    }

                    LowerBoundTotalWaytime(model, totalWayTime);

                    model.SetObjective(
                        +(12d) * unavailableSum
                        + (4d) * totalWayTime
                        - (2d) * desiredSum
                        + (3d) * longestRoute
                        , GRB.MINIMIZE);
                    model.Parameters.TimeLimit = Math.Max(0, timelimitMiliseconds / 1000);
                    InitializeWithVRPSolution(vrpSolution, numberOfRoutes, model, v, w, c);

#if DEBUG
                    model.Write($"{name}_{visitDurations.Length}.mst");
                    model.Write($"{name}_{visitDurations.Length}.mps");
                    model.Write($"{name}_{visitDurations.Length}.lp");
#endif

                    model.Optimize();
                    output.TimeElapsed = sw.ElapsedMilliseconds / 1000;
                    try
                    {
                        if (model.SolCount == 0 && vrpSolution != null)
                        {
                            consoleProgress?.Invoke(this, "No solution found -> try with vrpsolution");
                            BuildResultFromVRP(output, vrpSolution);
                            return(output);
                        }
                        BuildResult(output, numberOfRoutes, w, c);
                        consoleProgress?.Invoke(this, $"longestRoute: {longestRoute.X}");
                        consoleProgress?.Invoke(this, $"totalWayTime: {totalWayTime.Value}");
                        consoleProgress?.Invoke(this, $"DesiredDuration: {desiredSum.Value}");
                        consoleProgress?.Invoke(this, $"UnavailableDuration: {unavailableSum.Value}");
                        for (int s = 0; s < numberOfRoutes; s++)
                        {
                            consoleProgress?.Invoke(this, $"maxRoutes[{s}]: {maxRoutes[s].X}, {minRoutes[s].X} , ->{maxRoutes[s].X - minRoutes[s].X}");
                            for (int visitIndex = 0; visitIndex < visitDurations.Length; visitIndex++)
                            {
                                consoleProgress?.Invoke(this, $"c[{s}][{visitIndex}] ({v[s][visitIndex].X}): {c[s][visitIndex].X}");
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        consoleProgress?.Invoke(this, $"ERROR: {e.Message}");
                        output.Routes = new Route[0];
                    }
                }

            return(output);
        }
    static void Main()
    {
        try {
            // Sample data
            // Sets of days and workers
            string[] Shifts =
                new string[] { "Mon1", "Tue2", "Wed3", "Thu4", "Fri5", "Sat6",
                               "Sun7", "Mon8", "Tue9", "Wed10", "Thu11", "Fri12", "Sat13",
                               "Sun14" };
            string[] Workers =
                new string[] { "Amy", "Bob", "Cathy", "Dan", "Ed", "Fred", "Gu" };

            int nShifts  = Shifts.Length;
            int nWorkers = Workers.Length;

            // Number of workers required for each shift
            double[] shiftRequirements =
                new double[] { 3, 2, 4, 4, 5, 6, 5, 2, 2, 3, 4, 6, 7, 5 };

            // Worker availability: 0 if the worker is unavailable for a shift
            double[,] availability =
                new double[, ] {
                { 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1 },
                { 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0 },
                { 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1 },
                { 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 },
                { 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1 },
                { 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1 },
                { 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
            };

            // Model
            GRBEnv   env   = new GRBEnv();
            GRBModel model = new GRBModel(env);

            model.ModelName = "assignment";

            // Assignment variables: x[w][s] == 1 if worker w is assigned
            // to shift s. This is no longer a pure assignment model, so we must
            // use binary variables.
            GRBVar[,] x = new GRBVar[nWorkers, nShifts];
            for (int w = 0; w < nWorkers; ++w)
            {
                for (int s = 0; s < nShifts; ++s)
                {
                    x[w, s] =
                        model.AddVar(0, availability[w, s], 0, GRB.BINARY,
                                     Workers[w] + "." + Shifts[s]);
                }
            }

            // Slack variables for each shift constraint so that the shifts can
            // be satisfied
            GRBVar[] slacks = new GRBVar[nShifts];
            for (int s = 0; s < nShifts; ++s)
            {
                slacks[s] =
                    model.AddVar(0, GRB.INFINITY, 0, GRB.CONTINUOUS,
                                 Shifts[s] + "Slack");
            }

            // Variable to represent the total slack
            GRBVar totSlack = model.AddVar(0, GRB.INFINITY, 0, GRB.CONTINUOUS,
                                           "totSlack");

            // Variables to count the total shifts worked by each worker
            GRBVar[] totShifts = new GRBVar[nWorkers];
            for (int w = 0; w < nWorkers; ++w)
            {
                totShifts[w] = model.AddVar(0, GRB.INFINITY, 0, GRB.CONTINUOUS,
                                            Workers[w] + "TotShifts");
            }

            GRBLinExpr lhs;

            // Constraint: assign exactly shiftRequirements[s] workers
            // to each shift s, plus the slack
            for (int s = 0; s < nShifts; ++s)
            {
                lhs = new GRBLinExpr();
                lhs.AddTerm(1.0, slacks[s]);
                for (int w = 0; w < nWorkers; ++w)
                {
                    lhs.AddTerm(1.0, x[w, s]);
                }
                model.AddConstr(lhs == shiftRequirements[s], Shifts[s]);
            }

            // Constraint: set totSlack equal to the total slack
            lhs = new GRBLinExpr();
            for (int s = 0; s < nShifts; ++s)
            {
                lhs.AddTerm(1.0, slacks[s]);
            }
            model.AddConstr(lhs == totSlack, "totSlack");

            // Constraint: compute the total number of shifts for each worker
            for (int w = 0; w < nWorkers; ++w)
            {
                lhs = new GRBLinExpr();
                for (int s = 0; s < nShifts; ++s)
                {
                    lhs.AddTerm(1.0, x[w, s]);
                }
                model.AddConstr(lhs == totShifts[w], "totShifts" + Workers[w]);
            }

            // Objective: minimize the total slack
            model.SetObjective(1.0 * totSlack);

            // Optimize
            int status = solveAndPrint(model, totSlack, nWorkers, Workers, totShifts);
            if (status != GRB.Status.OPTIMAL)
            {
                return;
            }

            // Constrain the slack by setting its upper and lower bounds
            totSlack.UB = totSlack.X;
            totSlack.LB = totSlack.X;

            // Variable to count the average number of shifts worked
            GRBVar avgShifts =
                model.AddVar(0, GRB.INFINITY, 0, GRB.CONTINUOUS, "avgShifts");

            // Variables to count the difference from average for each worker;
            // note that these variables can take negative values.
            GRBVar[] diffShifts = new GRBVar[nWorkers];
            for (int w = 0; w < nWorkers; ++w)
            {
                diffShifts[w] = model.AddVar(-GRB.INFINITY, GRB.INFINITY, 0,
                                             GRB.CONTINUOUS, Workers[w] + "Diff");
            }

            // Constraint: compute the average number of shifts worked
            lhs = new GRBLinExpr();
            for (int w = 0; w < nWorkers; ++w)
            {
                lhs.AddTerm(1.0, totShifts[w]);
            }
            model.AddConstr(lhs == nWorkers * avgShifts, "avgShifts");

            // Constraint: compute the difference from the average number of shifts
            for (int w = 0; w < nWorkers; ++w)
            {
                model.AddConstr(totShifts[w] - avgShifts == diffShifts[w],
                                Workers[w] + "Diff");
            }

            // Objective: minimize the sum of the square of the difference from the
            // average number of shifts worked
            GRBQuadExpr qobj = new GRBQuadExpr();
            for (int w = 0; w < nWorkers; ++w)
            {
                qobj.AddTerm(1.0, diffShifts[w], diffShifts[w]);
            }
            model.SetObjective(qobj);

            // Optimize
            status = solveAndPrint(model, totSlack, nWorkers, Workers, totShifts);
            if (status != GRB.Status.OPTIMAL)
            {
                return;
            }

            // Dispose of model and env
            model.Dispose();
            env.Dispose();
        } catch (GRBException e) {
            Console.WriteLine("Error code: " + e.ErrorCode + ". " +
                              e.Message);
        }
    }
Exemple #8
0
    static void Main()
    {
        try {

          // Sample data
          // Sets of days and workers
          string[] Shifts =
          new string[] { "Mon1", "Tue2", "Wed3", "Thu4", "Fri5", "Sat6",
              "Sun7", "Mon8", "Tue9", "Wed10", "Thu11", "Fri12", "Sat13",
              "Sun14" };
          string[] Workers =
          new string[] { "Amy", "Bob", "Cathy", "Dan", "Ed", "Fred", "Gu" };

          int nShifts = Shifts.Length;
          int nWorkers = Workers.Length;

          // Number of workers required for each shift
          double[] shiftRequirements =
          new double[] { 3, 2, 4, 4, 5, 6, 5, 2, 2, 3, 4, 6, 7, 5 };

          // Amount each worker is paid to work one shift
          double[] pay = new double[] { 10, 12, 10, 8, 8, 9, 11 };

          // Worker availability: 0 if the worker is unavailable for a shift
          double[,] availability =
          new double[,] { { 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1 },
              { 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0 },
              { 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1 },
              { 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 },
              { 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1 },
              { 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1 },
              { 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } };

          // Model
          GRBEnv env = new GRBEnv();
          GRBModel model = new GRBModel(env);
          model.Set(GRB.StringAttr.ModelName, "assignment");

          // Assignment variables: x[w][s] == 1 if worker w is assigned
          // to shift s. Since an assignment model always produces integer
          // solutions, we use continuous variables and solve as an LP.
          GRBVar[,] x = new GRBVar[nWorkers,nShifts];
          for (int w = 0; w < nWorkers; ++w) {
        for (int s = 0; s < nShifts; ++s) {
          x[w,s] =
              model.AddVar(0, availability[w,s], pay[w], GRB.CONTINUOUS,
                           Workers[w] + "." + Shifts[s]);
        }
          }

          // The objective is to minimize the total pay costs
          model.Set(GRB.IntAttr.ModelSense, 1);

          // Update model to integrate new variables
          model.Update();

          // Constraint: assign exactly shiftRequirements[s] workers
          // to each shift s
          LinkedList<GRBConstr> reqCts = new LinkedList<GRBConstr>();
          for (int s = 0; s < nShifts; ++s) {
        GRBLinExpr lhs = 0.0;
        for (int w = 0; w < nWorkers; ++w)
          lhs += x[w,s];
        GRBConstr newCt =
            model.AddConstr(lhs == shiftRequirements[s], Shifts[s]);
        reqCts.AddFirst(newCt);
          }

          // Optimize
          model.Optimize();
          int status = model.Get(GRB.IntAttr.Status);
          if (status == GRB.Status.UNBOUNDED) {
        Console.WriteLine("The model cannot be solved "
            + "because it is unbounded");
        return;
          }
          if (status == GRB.Status.OPTIMAL) {
        Console.WriteLine("The optimal objective is " +
            model.Get(GRB.DoubleAttr.ObjVal));
        return;
          }
          if ((status != GRB.Status.INF_OR_UNBD) &&
          (status != GRB.Status.INFEASIBLE)) {
        Console.WriteLine("Optimization was stopped with status " + status);
        return;
          }

          // Add slack variables to make the model feasible
          Console.WriteLine("The model is infeasible; adding slack variables");

          // Set original objective coefficients to zero
          model.SetObjective(new GRBLinExpr());

          // Add a new slack variable to each shift constraint so that the shifts
          // can be satisfied
          LinkedList<GRBVar> slacks = new LinkedList<GRBVar>();
          foreach (GRBConstr c in reqCts) {
        GRBColumn col = new GRBColumn();
        col.AddTerm(1.0, c);
        GRBVar newvar =
            model.AddVar(0, GRB.INFINITY, 1.0, GRB.CONTINUOUS, col,
                         c.Get(GRB.StringAttr.ConstrName) + "Slack");
        slacks.AddFirst(newvar);
          }

          // Solve the model with slacks
          model.Optimize();
          status = model.Get(GRB.IntAttr.Status);
          if ((status == GRB.Status.INF_OR_UNBD) ||
          (status == GRB.Status.INFEASIBLE) ||
          (status == GRB.Status.UNBOUNDED)) {
        Console.WriteLine("The model with slacks cannot be solved "
            + "because it is infeasible or unbounded");
        return;
          }
          if (status != GRB.Status.OPTIMAL) {
        Console.WriteLine("Optimization was stopped with status " + status);
        return;
          }

          Console.WriteLine("\nSlack values:");
          foreach (GRBVar sv in slacks) {
        if (sv.Get(GRB.DoubleAttr.X) > 1e-6) {
          Console.WriteLine(sv.Get(GRB.StringAttr.VarName) + " = " +
              sv.Get(GRB.DoubleAttr.X));
        }
          }

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

        } catch (GRBException e) {
          Console.WriteLine("Error code: " + e.ErrorCode + ". " +
          e.Message);
        }
    }
Exemple #9
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;
        }
    static void Main()
    {
        try {
            // Create environment

            GRBEnv env = new GRBEnv();

            // Create a new model

            GRBModel model = new GRBModel(env);

            // Create variables

            double lb = 0.0, ub = 1.0;

            GRBVar x = model.AddVar(lb, ub, 0.0, GRB.CONTINUOUS, "x");
            GRBVar y = model.AddVar(lb, ub, 0.0, GRB.CONTINUOUS, "y");
            GRBVar z = model.AddVar(lb, ub, 0.0, GRB.CONTINUOUS, "z");

            // Set objective for y

            model.SetObjective(-y);

            // Add piecewise-linear objective functions for x and z

            int      npts = 101;
            double[] ptu  = new double[npts];
            double[] ptf  = new double[npts];
            double[] ptg  = new double[npts];

            for (int i = 0; i < npts; i++)
            {
                ptu[i] = lb + (ub - lb) * i / (npts - 1);
                ptf[i] = f(ptu[i]);
                ptg[i] = g(ptu[i]);
            }

            model.SetPWLObj(x, ptu, ptf);
            model.SetPWLObj(z, ptu, ptg);

            // Add constraint: x + 2 y + 3 z <= 4

            model.AddConstr(x + 2 * y + 3 * z <= 4.0, "c0");

            // Add constraint: x + y >= 1

            model.AddConstr(x + y >= 1.0, "c1");

            // Optimize model as an LP

            model.Optimize();

            Console.WriteLine("IsMIP: " + model.IsMIP);

            Console.WriteLine(x.VarName + " " + x.X);
            Console.WriteLine(y.VarName + " " + y.X);
            Console.WriteLine(z.VarName + " " + z.X);

            Console.WriteLine("Obj: " + model.ObjVal);

            Console.WriteLine();

            // Negate piecewise-linear objective function for x

            for (int i = 0; i < npts; i++)
            {
                ptf[i] = -ptf[i];
            }

            model.SetPWLObj(x, ptu, ptf);

            // Optimize model as a MIP

            model.Optimize();

            Console.WriteLine("IsMIP: " + model.IsMIP);

            Console.WriteLine(x.VarName + " " + x.X);
            Console.WriteLine(y.VarName + " " + y.X);
            Console.WriteLine(z.VarName + " " + z.X);

            Console.WriteLine("Obj: " + model.ObjVal);

            // Dispose of model and environment

            model.Dispose();
            env.Dispose();
        } catch (GRBException e) {
            Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message);
        }
    }
Exemple #11
0
        // return false if not enough tick data
        public bool TryGetTickRecommendations()
        {
            var env = new GRBEnv();

            env.LogToConsole = 0;
            var model = new GRBModel(env);

            var userTickData = new List <UserTickData>();

            foreach (var history in _tickHistories)
            {
                var userNum = _tickHistories.IndexOf(history); // todo

                // todo time delta
                if (history.GetValidInbetweenCount < 5)
                {
                    Logger.Warn($"Not enough inbetweens for {userNum}, has {history.GetValidInbetweenCount}");
                    return(false);
                }

                history.GetStatistics(out float average, out float std);

                var userTick = new UserTickData
                {
                    LastTickSeconds = history.LastTickSeconds, // this needs to be close enough to a min tick time
                    AvgDelta        = average,
                    StdDelta        = std,
                    TimeFrame       = FindTicksInSeconds
                };

                userTickData.Add(userTick);

                GRBVar avgVar = model.AddVar(0, double.PositiveInfinity, 0, GRB.CONTINUOUS, $"avg_u{userNum}");
                userTick.RecommendedAvgVar = avgVar;
            }

            // variant 1: make them sync
            // variant 2: find a possible intersection point??? perhaps this is already handled by the prep time thing
            var stdOffsetFactor   = .5f;
            var meanDeltaDistance = .1f;

            GRBLinExpr minimizeValue = 0;

            // finde kleinsten gemeinsamen nenner
            for (int userNum = 0; userNum < userTickData.Count; userNum++)
            {
                var userInfo = userTickData[userNum];

                model.AddConstr(userInfo.RecommendedAvgVar <= userInfo.AvgDelta + userInfo.StdDelta * stdOffsetFactor, $"ubound_avg_u{userNum}");
                model.AddConstr(userInfo.RecommendedAvgVar >= userInfo.AvgDelta - userInfo.StdDelta * stdOffsetFactor, $"lbound_avg_u{userNum}");

                GRBVar absAvgDelta = model.AddVar(0, double.PositiveInfinity, 0, GRB.CONTINUOUS, $"abs_avg_delta_u{userNum}");

                model.AddConstr(userInfo.RecommendedAvgVar - userInfo.AvgDelta <= absAvgDelta, $"abs_avg_delta_r-a_u{userNum}");
                model.AddConstr(userInfo.AvgDelta - userInfo.RecommendedAvgVar <= absAvgDelta, $"abs_avg_delta_a-r_u{userNum}");

                minimizeValue += absAvgDelta;

                for (int otherNum = userNum + 1; otherNum < userTickData.Count; otherNum++)
                {
                    var otherInfo = userTickData[otherNum];

                    GRBVar interval = model.AddVar(0, double.PositiveInfinity, 0, GRB.CONTINUOUS, $"interval_u{userNum}_u{otherNum}");

                    GRBVar smaller;
                    GRBVar larger;
                    if (userInfo.AvgDelta <= otherInfo.AvgDelta)
                    {
                        smaller = userInfo.RecommendedAvgVar;
                        larger  = otherInfo.RecommendedAvgVar;
                    }
                    else
                    {
                        smaller = otherInfo.RecommendedAvgVar;
                        larger  = userInfo.RecommendedAvgVar;
                    }

                    model.AddConstr(interval * smaller <= larger + meanDeltaDistance,
                                    $"interval_u{userNum}_u{otherNum}_1");
                    model.AddConstr(interval * smaller >= larger - meanDeltaDistance,
                                    $"interval_u{userNum}_u{otherNum}_2");
                }
            }

            model.SetObjective(minimizeValue);
            model.ModelSense = GRB.MINIMIZE;
            model.Update();
            model.Optimize();

            // if didn't a solution, cancel
            if (model.Status != GRB.Status.OPTIMAL)
            {
                Logger.Warn($"Didn't find solution to time conditions. Gurobi status: {model.Status}");
                // todo
                return(false);
            }

            foreach (var userInfo in userTickData)
            {
                userInfo.RecommendedAvg = (float)userInfo.RecommendedAvgVar.X;
            }

            // (1)necessaryTick from a transition
            // or!: (3)the largest transition ==> this for now because it's easiest
            // or!: (2)the most convienient point everyone can convieniently hit
            var necessaryTick = 2 * userTickData.Max(userInfo => userInfo.RecommendedAvg);

            userTickData.ForEach(userTick => userTick.TimeFrame = necessaryTick);

            foreach (var userInfo in userTickData)
            {
                model.Reset();

                minimizeValue = 0;

                // do three times
                // check if any of the last hit the necessary constraint
                // or the one before
                // or the one before
                // last tick

                for (int i = 0; i < userInfo.AvgNumTicksInTimeFrame; i++)
                {
                    GRBVar tickVar = model.AddVar(0, double.PositiveInfinity, 0, GRB.CONTINUOUS, $"tick_t{i}");
                    userInfo.RecommendedTickVars.Add(tickVar);
                }

                for (int i = 0; i < userInfo.RecommendedTickVars.Count; i++)
                {
                    var tick = userInfo.RecommendedTickVars[i];

                    if (i == 0)
                    {
                        GRBVar interval = model.AddVar(0, double.PositiveInfinity, 0, GRB.CONTINUOUS,
                                                       $"interval_to_last_tick");

                        // soft constraint!
                        model.AddConstr(
                            tick - userInfo.LastTickSeconds <=
                            interval * (userInfo.RecommendedAvg + userInfo.StdDelta * stdOffsetFactor),
                            $"interval_to_last_tick_uconstraint");
                        model.AddConstr(
                            tick - userInfo.LastTickSeconds >=
                            interval * (userInfo.RecommendedAvg - userInfo.StdDelta * stdOffsetFactor),
                            $"interval_to_last_tick_lconstraint");
                    }
                    else if (i < userInfo.AvgNumTicksInTimeFrame - 1)
                    {
                        var nextTick = userInfo.RecommendedTickVars[i + 1];

                        model.AddConstr(
                            nextTick - tick <= userInfo.RecommendedAvg + userInfo.StdDelta * stdOffsetFactor,
                            $"delta{i}_uconstraint");
                        model.AddConstr(
                            nextTick - tick >= userInfo.RecommendedAvg - userInfo.StdDelta * stdOffsetFactor,
                            $"delta{i}_lconstraint");
                    }
                }

                // delta distance minimization
                minimizeValue = MinimizeDeltaDistance(model, minimizeValue, userInfo.RecommendedTickVars[userInfo.AvgNumTicksInTimeFrame - 1],
                                                      necessaryTick, "lastTickToNecessary");
                if (userInfo.AvgNumTicksInTimeFrame > 1)
                {
                    minimizeValue = MinimizeDeltaDistance(model, minimizeValue, userInfo.RecommendedTickVars[userInfo.AvgNumTicksInTimeFrame - 2],
                                                          necessaryTick, "preLastTickToNecessary");
                }
                if (userInfo.AvgNumTicksInTimeFrame > 2)
                {
                    minimizeValue = MinimizeDeltaDistance(model, minimizeValue, userInfo.RecommendedTickVars[userInfo.AvgNumTicksInTimeFrame - 3],
                                                          necessaryTick, "prePreLastTickToNecessary");
                }

                model.SetObjective(minimizeValue);
                model.ModelSense = GRB.MINIMIZE;
                model.Update();
                model.Optimize();

                if (model.Status != GRB.Status.OPTIMAL)
                {
                    Logger.Warn($"Didn't find solution for user. Gurobi status: {model.Status}");
                    return(false);
                }

                userInfo.RecommendedTicks = userInfo.RecommendedTickVars.Select(var => (float)var.X).ToList();
            }

            LastNecessaryTick       = necessaryTick;
            _lastCalculatedTickData = userTickData;

            return(true);
        }
Exemple #12
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();
        }
Exemple #13
0
        public static void build_initial_model(bool lp_relax)
        {
            env = new GRBEnv(true);
            env.Set("LogFile", "Recoloracao.log");
            env.Set(GRB.IntParam.LogToConsole, 0);
            env.Start();

            model           = new GRBModel(env);
            model.ModelName = "Recoloracao";
            model.Set(GRB.IntParam.Threads, 1);
            model.Set(GRB.IntParam.Method, 1);
            model.Set(GRB.DoubleParam.TimeLimit, 1800);

            X = new GRBVar[input.nVertices, input.nCores];
            var obj = new GRBLinExpr();

            for (int i = 0; i < input.nVertices; i++)
            {
                for (int j = 0; j < input.nCores; j++)
                {
                    string nome = "X_ " + i.ToString() + "_c " + j.ToString();

                    if (lp_relax)
                    {
                        X[i, j] = model.AddVar(0.0, 1.0, 1.0, GRB.CONTINUOUS, nome);
                    }
                    else
                    {
                        X[i, j] = model.AddVar(0.0, 1.0, 1.0, GRB.BINARY, nome);
                    }

                    if (input.caminhoColorido[i] != j + 1)
                    {
                        obj.AddTerm(1, X[i, j]);
                    }
                }
            }

            model.SetObjective(obj, GRB.MINIMIZE);

            // Restrição 1
            for (int i = 0; i < input.nVertices; i++)
            {
                GRBLinExpr soma = new GRBLinExpr();

                for (int c = 0; c < input.nCores; c++)
                {
                    soma.AddTerm(1.0, X[i, c]);
                }

                model.AddConstr(soma, GRB.EQUAL, 1, "Cor vértice" + i);
            }

            // Restrição 2
            for (int p = 0; p < input.nVertices - 2; p++)
            {
                for (int r = p + 2; r < input.nVertices; r++)
                {
                    for (int q = p + 1; q < r; q++)
                    {
                        for (int k = 0; k < input.nCores; k++)
                        {
                            model.AddConstr(X[p, k] - X[q, k] + X[r, k], GRB.LESS_EQUAL, 1, "p" + p + "-q" + q + "-r" + r + "-k" + k);
                        }
                    }
                }
            }
        }
        void BuildFeasibleSolutionModel()
        {
            _env = new GRBEnv("SolutionLog.log");
            _env.Set(GRB.DoubleParam.MIPGap, 0.0);
            _env.Set(GRB.DoubleParam.TimeLimit, 500);
            _grbModel = new GRBModel(_env);

            //决策变量
            foreach (Node n in Data.NodeSet)
            {
                n.Result_GenerateFlow = _grbModel.AddVar(0.0, GRB.INFINITY, 0.0, GRB.CONTINUOUS, "g_" + n.ID);
            }
            foreach (Arc a in Data.ArcSet)
            {
                a.Result_FlowF = _grbModel.AddVar(0.0, GRB.INFINITY, 0.0, GRB.CONTINUOUS, "fF_" + a.FromNode.ID + "_" + a.ToNode.ID);
                a.Result_FlowR = _grbModel.AddVar(0.0, GRB.INFINITY, 0.0, GRB.CONTINUOUS, "fR_" + a.FromNode.ID + "_" + a.ToNode.ID);
            }
            _grbModel.Update();

            //目标函数
            GRBLinExpr expr1 = 0;

            foreach (Node n in Data.NodeSet)
            {
                expr1 += n.IsServerLocationSelected * Data.ServerInstalationFee;
            }
            GRBLinExpr expr2 = 0;

            foreach (Arc a in Data.ArcSet)
            {
                expr2 += (a.Result_FlowF + a.Result_FlowR) * Data.FlowFeePerUnit;
            }

            _grbModel.SetObjective(expr1 + expr2, GRB.MINIMIZE);


            //约束条件
            foreach (Node n in Data.NodeSet)
            {
                _grbModel.AddConstr(n.Result_GenerateFlow <= n.IsServerLocationSelected * Data.M, "ct1_" + n.ID);
            }
            foreach (Node n in Data.NodeSet)
            {
                GRBLinExpr sum1 = 0;
                GRBLinExpr sum2 = 0;
                GRBLinExpr sum3 = 0;
                GRBLinExpr sum4 = 0;
                foreach (Arc a in n.ArcSet)
                {
                    if (a.ToNode == n)//进
                    {
                        sum1 += a.Result_FlowF;
                        sum3 += a.Result_FlowR;
                    }
                    else//出
                    {
                        sum2 += a.Result_FlowR;
                        sum4 += a.Result_FlowF;
                    }
                }
                _grbModel.AddConstr(n.Result_GenerateFlow + sum1 + sum2 == n.Demand + sum3 + sum4, "ct2_" + n.ID);
            }
            foreach (Arc a in Data.ArcSet)
            {
                _grbModel.AddConstr(a.Result_FlowF + a.Result_FlowR <= a.Capacity, "ct3_" + a.FromNode.ID + "_" + a.ToNode.ID);
            }
        }
        /// <summary>
        /// Method called when a process token executes the step.
        /// </summary>
        public ExitType Execute(IStepExecutionContext context)
        {
            IState _timenow = _propTimenow.GetState(context);
            double timenow  = Convert.ToDouble(_timenow.StateValue);
            IState _salida  = _propSalida.GetState(context);
            double salida   = Convert.ToDouble(_salida.StateValue);
            IState _aux     = _propAux.GetState(context);
            double aux      = Convert.ToDouble(_aux.StateValue);

            SerializarElement sr = new SerializarElement();

            if (File.Exists(sr.SerializationFile))
            {
                Vect v = sr.deserializa();
                vectores = sr.deserializa();

                vectores.MipYc = v.MipYc;
                vectores.MipYv = v.MipYv;
                vectores.MipTc = v.MipTc;
                vectores.MipTv = v.MipTv;

                //Dinamico
                vectores.Din1   = v.Din1;
                vectores.DesCam = v.DesCam;
                vectores.Fila   = v.Fila;

                //LP
                vectores.TCV           = v.TCV;
                vectores.TCC           = v.TCC;
                vectores.TCj           = v.TCj;
                vectores.TDi           = v.TDi;
                vectores.Destij        = v.Destij;
                vectores.Dj            = v.Dj;
                vectores.Uj            = v.Uj;
                vectores.Ui            = v.Ui;
                vectores.RLi           = v.RLi;
                vectores.RUi           = v.RUi;
                vectores.PlYc          = v.PlYc;
                vectores.PlYv          = v.PlYv;
                vectores.PlTc          = v.PlTc;
                vectores.PlTv          = v.PlTv;
                vectores.PlTcmalo      = v.PlTcmalo;
                vectores.Mine          = v.Mine;
                vectores.Mineralocado  = v.Mineralocado;
                vectores.Lastrealocado = v.Lastrealocado;
            }

            //i Botaderos. j Palas. k Camiones.

            double[,] Din12 = (double[, ])vectores.Din1.Clone();

            double[,] Tij = (double[, ])vectores.PlTc.Clone();//I Tonelaje por turno


            double[,] xij = new double[30, 20]; //I Tonelaje cumplido hasta el momento de activación del dinámico
            double[,] yij = new double[30, 20]; //Tonelaje requerido para alcazar R
            double[,] Rij = new double[30, 20]; //Porcentaje de cumplimienteo del tonelaje
            double[,] dij = new double[30, 20]; //Desviación del porcentaje de cumplimiento ideal en el momento de activación
            double[,] Mij = new double[30, 20]; //Demanda de camiones por ruta
            double[,] rij = new double[30, 20]; //I Matriz de tiempos de viajes

            double[] tk = new double[81];       //I Tiempo que le toma al camión k llegar al botadero/chancador al que va (si ya está en un botadero este tiempo es 0)
            double[] dk = new double[81];       //I Tiempo esperado de espera del camión k en el botadero/chancador al que va
            double[] ej = new double[30];       //I Tiempo promedio de descarga en el botadero j
            double[] Di = new double[20];       //Demanda de camiones por pala (int?)
            double[] Li = new double[20];       //I Tiempo promedio de carga en la pala i
            double[] Ni = new double[20];       //I Número de camiones en la pala i (int?)
            double[] Ei = new double[20];       //I Camiones en ruta a la pala i (int?)
            double[] Sk = new double[81];       //I Arreglo de variables binarias que indican si el camión tiene una tarea asignada
            int[]    Bk = new int[81];          //Botadero al que está llendo el camión k

            int    numcam       = 81;           //Número total de camiones
            double capacidad    = 300;          //Capacidad de los camiones
            int    numPalas     = 20;           //Palas activas?
            int    numBotaderos = 30;           //Botaderos o chancador activos?
            double R            = 0;

            //Se cálcula el porcentaje de cumplimiento de cuota
            for (int i = 0; i < numBotaderos; i++)
            {
                for (int j = 0; j < numPalas; j++)
                {
                    if (Tij[i, j] != 0)
                    {
                        Rij[i, j] = xij[i, j] / Tij[i, j];
                        R         = R + Rij[i, j];
                    }
                }
            }

            //Se calcula de déficit o superávit
            R = R / (numPalas * numBotaderos);
            for (int i = 0; i < numBotaderos; i++)
            {
                for (int j = 0; j < numPalas; j++)
                {
                    dij[i, j] = Rij[i, j] - R;
                }
            }
            //Se calcula la demanda adeudada para alcanzar R
            double mij_aux;
            int    mij;

            for (int i = 0; i < numBotaderos; i++)
            {
                for (int j = 0; j < numPalas; j++)
                {
                    yij[i, j] = R * Tij[i, j] - xij[i, j];
                    mij_aux   = yij[i, j] / capacidad;
                    mij       = Convert.ToInt32(mij_aux);
                    Mij[i, j] = mij + 1;
                }
            }
            double DTotal = 0;
            double di;

            for (int j = 0; j < numPalas; j++)
            {
                di = 0;
                for (int i = 0; i < numPalas; i++)
                {
                    di = Mij[i, j] + di;
                }

                Di[j]  = di;
                DTotal = DTotal + Di[j];
            }
            //En teoría se debería hacer un chequeo para que la demanda total de camiones no supere
            //el total disponible. De ocurrir, se deben limitar los flujos del PL (*).

            //Criterio de asignación de camiones
            //Se crea el arreglo Wik que contiene los tiempos que demora al camion k llegar a ser
            //atendido por la pala i. El tamaño de este arreglo depende de los camiones disponibles.
            double[,] Wik = new double[numPalas, numcam];//Matriz de tiempos esperados del camión k para ser atendido por la pala i

            for (int k = 0; k < numcam; k++)
            {
                if (Sk[k] != 0) //Sólo para los camiones sin tareas asignadas
                {
                    for (int i = 0; i < numPalas; i++)
                    {
                        Wik[i, k] = Li[i] * (Ni[i] + Ei[i]) - (tk[k] + dk[k] + ej[Bk[k]] + rij[Bk[k], i]);
                    }
                }
            }

            //Modelo
            GRBEnv   env   = new GRBEnv();
            GRBModel model = new GRBModel(env);

            model.ModelName = "Asignación Dinámica";

            GRBVar[,] X = new GRBVar[numPalas, numcam]; //Binaria que define si el camión k se asigna a la pala i

            for (int i = 0; i < numPalas; i++)
            {
                for (int k = 0; k < numcam; k++)
                {
                    X[i, k] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, "X" + i + "_" + k);
                }
            }

            model.Update();

            //Restricciones

            for (int k = 0; k < numcam; k++)
            {
                GRBLinExpr suma_Xik = 0;

                for (int i = 0; i < numPalas; i++)
                {
                    suma_Xik = suma_Xik + X[i, k];
                }
                model.AddConstr(suma_Xik, GRB.LESS_EQUAL, Sk[k], "S" + k);
            }

            for (int i = 0; i < numPalas; i++)
            {
                GRBLinExpr suma_Xik = 0;

                for (int k = 0; k < numcam; k++)
                {
                    suma_Xik = suma_Xik + X[i, k];
                }
                model.AddConstr(suma_Xik, GRB.GREATER_EQUAL, Di[i], "D" + i);
            }

            model.Update();

            GRBLinExpr FO = 0;

            for (int i = 0; i < numPalas; i++)
            {
                for (int k = 0; k < numcam; k++)
                {
                    FO = FO + Wik[i, k] * X[i, k];
                }
            }
            model.SetObjective(FO, GRB.MINIMIZE);

            model.Update();

            model.Optimize();

            sr.serializa(vectores);

            sr.closeStream();
            return(ExitType.FirstExit);
        }
Exemple #16
0
    static void Main()
    {
        try {
          GRBEnv    env   = new GRBEnv("qp.log");
          GRBModel  model = new GRBModel(env);

          // Create variables

          GRBVar x = model.AddVar(0.0, 1.0, 0.0, GRB.CONTINUOUS, "x");
          GRBVar y = model.AddVar(0.0, 1.0, 0.0, GRB.CONTINUOUS, "y");
          GRBVar z = model.AddVar(0.0, 1.0, 0.0, GRB.CONTINUOUS, "z");

          // Integrate new variables

          model.Update();

          // Set objective

          GRBQuadExpr obj = x*x + x*y + y*y + y*z + z*z;
          model.SetObjective(obj);

          // Add constraint: x + 2 y + 3 z >= 4

          model.AddConstr(x + 2 * y + 3 * z >= 4.0, "c0");

          // Add constraint: x + y >= 1

          model.AddConstr(x + y >= 1.0, "c1");

          // Optimize model

          model.Optimize();

          Console.WriteLine(x.Get(GRB.StringAttr.VarName)
                         + " " + x.Get(GRB.DoubleAttr.X));
          Console.WriteLine(y.Get(GRB.StringAttr.VarName)
                         + " " + y.Get(GRB.DoubleAttr.X));
          Console.WriteLine(z.Get(GRB.StringAttr.VarName)
                         + " " + z.Get(GRB.DoubleAttr.X));

          Console.WriteLine("Obj: " + model.Get(GRB.DoubleAttr.ObjVal) + " " +
                        obj.Value);

          // Change variable types to integer

          x.Set(GRB.CharAttr.VType, GRB.INTEGER);
          y.Set(GRB.CharAttr.VType, GRB.INTEGER);
          z.Set(GRB.CharAttr.VType, GRB.INTEGER);

          // Optimize model

          model.Optimize();

          Console.WriteLine(x.Get(GRB.StringAttr.VarName)
                         + " " + x.Get(GRB.DoubleAttr.X));
          Console.WriteLine(y.Get(GRB.StringAttr.VarName)
                         + " " + y.Get(GRB.DoubleAttr.X));
          Console.WriteLine(z.Get(GRB.StringAttr.VarName)
                         + " " + z.Get(GRB.DoubleAttr.X));

          Console.WriteLine("Obj: " + model.Get(GRB.DoubleAttr.ObjVal) + " " +
                        obj.Value);

          // Dispose of model and env

          model.Dispose();
          env.Dispose();

        } catch (GRBException e) {
          Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message);
        }
    }
Exemple #17
0
    static void Main()
    {
        try {
            GRBEnv   env   = new GRBEnv("qp.log");
            GRBModel model = new GRBModel(env);

            // Create variables

            GRBVar x = model.AddVar(0.0, 1.0, 0.0, GRB.CONTINUOUS, "x");
            GRBVar y = model.AddVar(0.0, 1.0, 0.0, GRB.CONTINUOUS, "y");
            GRBVar z = model.AddVar(0.0, 1.0, 0.0, GRB.CONTINUOUS, "z");

            // Integrate new variables

            model.Update();

            // Set objective

            GRBQuadExpr obj = x * x + x * y + y * y + y * z + z * z + 2 * x;
            model.SetObjective(obj);

            // Add constraint: x + 2 y + 3 z >= 4

            model.AddConstr(x + 2 * y + 3 * z >= 4.0, "c0");

            // Add constraint: x + y >= 1

            model.AddConstr(x + y >= 1.0, "c1");

            // Optimize model

            model.Optimize();

            Console.WriteLine(x.Get(GRB.StringAttr.VarName)
                              + " " + x.Get(GRB.DoubleAttr.X));
            Console.WriteLine(y.Get(GRB.StringAttr.VarName)
                              + " " + y.Get(GRB.DoubleAttr.X));
            Console.WriteLine(z.Get(GRB.StringAttr.VarName)
                              + " " + z.Get(GRB.DoubleAttr.X));

            Console.WriteLine("Obj: " + model.Get(GRB.DoubleAttr.ObjVal) + " " +
                              obj.Value);


            // Change variable types to integer

            x.Set(GRB.CharAttr.VType, GRB.INTEGER);
            y.Set(GRB.CharAttr.VType, GRB.INTEGER);
            z.Set(GRB.CharAttr.VType, GRB.INTEGER);

            // Optimize model

            model.Optimize();

            Console.WriteLine(x.Get(GRB.StringAttr.VarName)
                              + " " + x.Get(GRB.DoubleAttr.X));
            Console.WriteLine(y.Get(GRB.StringAttr.VarName)
                              + " " + y.Get(GRB.DoubleAttr.X));
            Console.WriteLine(z.Get(GRB.StringAttr.VarName)
                              + " " + z.Get(GRB.DoubleAttr.X));

            Console.WriteLine("Obj: " + model.Get(GRB.DoubleAttr.ObjVal) + " " +
                              obj.Value);

            // Dispose of model and env

            model.Dispose();
            env.Dispose();
        } catch (GRBException e) {
            Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message);
        }
    }
Exemple #18
0
    static void Main()
    {
        try {
            // Create environment

            GRBEnv env = new GRBEnv();

            // Create a new m

            GRBModel m = new GRBModel(env);

            double lb = 0.0, ub = GRB.INFINITY;

            GRBVar x = m.AddVar(lb, ub, 0.0, GRB.CONTINUOUS, "x");
            GRBVar y = m.AddVar(lb, ub, 0.0, GRB.CONTINUOUS, "y");
            GRBVar u = m.AddVar(lb, ub, 0.0, GRB.CONTINUOUS, "u");
            GRBVar v = m.AddVar(lb, ub, 0.0, GRB.CONTINUOUS, "v");

            // Set objective

            m.SetObjective(2 * x + y, GRB.MAXIMIZE);

            // Add linear constraint

            m.AddConstr(u + 4 * v <= 9, "l1");

            // Approach 1) PWL constraint approach

            double   intv = 1e-3;
            double   xmax = Math.Log(9.0);
            int      len  = (int)Math.Ceiling(xmax / intv) + 1;
            double[] xpts = new double[len];
            double[] upts = new double[len];
            for (int i = 0; i < len; i++)
            {
                xpts[i] = i * intv;
                upts[i] = f(i * intv);
            }
            GRBGenConstr gc1 = m.AddGenConstrPWL(x, u, xpts, upts, "gc1");

            double ymax = (9.0 / 4.0) * (9.0 / 4.0);
            len = (int)Math.Ceiling(ymax / intv) + 1;
            double[] ypts = new double[len];
            double[] vpts = new double[len];
            for (int i = 0; i < len; i++)
            {
                ypts[i] = i * intv;
                vpts[i] = g(i * intv);
            }
            GRBGenConstr gc2 = m.AddGenConstrPWL(y, v, ypts, vpts, "gc2");

            // Optimize the model and print solution

            m.Optimize();
            printsol(m, x, y, u, v);

            // Approach 2) General function constraint approach with auto PWL
            //             translation by Gurobi

            // restore unsolved state and get rid of PWL constraints
            m.Reset();
            m.Remove(gc1);
            m.Remove(gc2);
            m.Update();

            GRBGenConstr gcf1 = m.AddGenConstrExp(x, u, "gcf1", "");
            GRBGenConstr gcf2 = m.AddGenConstrPow(y, v, 0.5, "gcf2", "");

            m.Parameters.FuncPieceLength = 1e-3;

            // Optimize the model and print solution

            m.Optimize();
            printsol(m, x, y, u, v);

            // Zoom in, use optimal solution to reduce the ranges and use a smaller
            // pclen=1e-5 to solve it

            x.LB = Math.Max(x.LB, x.X - 0.01);
            x.UB = Math.Min(x.UB, x.X + 0.01);
            y.LB = Math.Max(y.LB, y.X - 0.01);
            y.UB = Math.Min(y.UB, y.X + 0.01);
            m.Update();
            m.Reset();

            m.Parameters.FuncPieceLength = 1e-5;

            // Optimize the model and print solution

            m.Optimize();
            printsol(m, x, y, u, v);

            // Dispose of model and environment

            m.Dispose();
            env.Dispose();
        } catch (GRBException e) {
            Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message);
        }
    }