static void MyContiguity(Solver solver, IntVar[] x)
    {
        // the DFA (for regular)
        int initial_state = 1;

        // all states are accepting states
        int[] accepting_states = { 1, 2, 3 };

        // The regular expression 0*1*0* {state, input, next state}
        long[][] transition_tuples =
        {
            new long[] { 1, 0, 1 },
            new long[] { 1, 1, 2 },
            new long[] { 2, 0, 3 },
            new long[] { 2, 1, 2 },
            new long[] { 3, 0, 3 }
        };

        IntTupleSet result = new IntTupleSet(3);

        result.InsertAll(transition_tuples);

        solver.Add(x.Transition(result,
                                initial_state,
                                accepting_states));
    }
  static void MyContiguity(Solver solver, IntVar[] x) {

    // the DFA (for regular)
    int initial_state = 1;

    // all states are accepting states
    int[] accepting_states = {1,2,3};

    // The regular expression 0*1*0* {state, input, next state}
    int[,] transition_tuples = { {1, 0, 1},
                                 {1, 1, 2},
                                 {2, 0, 3},
                                 {2, 1, 2},
                                 {3, 0, 3} };

    IntTupleSet result = new IntTupleSet(3);
    result.InsertAll(transition_tuples);

    solver.Add(x.Transition(result,
                            initial_state,
                            accepting_states));
  }
    /**
     *
     * Nurse rostering
     *
     * This is a simple nurse rostering model using a DFA and
     * the built-in TransitionConstraint.
     *
     * The DFA is from MiniZinc Tutorial, Nurse Rostering example:
     * - one day off every 4 days
     * - no 3 nights in a row.
     *
     * Also see:
     *  - http://www.hakank.org/or-tools/nurse_rostering.py
     *  - http://www.hakank.org/or-tools/nurse_rostering_regular.cs
     *    which use (a decomposition of) regular constraint
     *
     */
    private static void Solve(int nurse_multiplier, int week_multiplier)
    {
        Console.WriteLine("Starting Nurse Rostering");
        Console.WriteLine("  - {0} teams of 7 nurses", nurse_multiplier);
        Console.WriteLine("  - {0} blocks of 14 days", week_multiplier);

        Solver solver = new Solver("NurseRostering");

        //
        // Data
        //

        // Note: If you change num_nurses or num_days,
        //       please also change the constraints
        //       on nurse_stat and/or day_stat.
        int num_nurses = 7 * nurse_multiplier;
        int num_days   = 14 * week_multiplier;

        // Note: I had to add a dummy shift.
        int dummy_shift = 0;
        int day_shift   = 1;
        int night_shift = 2;
        int off_shift   = 3;

        int[] shifts       = { dummy_shift, day_shift, night_shift, off_shift };
        int[] valid_shifts = { day_shift, night_shift, off_shift };

        // the DFA (for regular)
        int initial_state = 1;

        int[] accepting_states = { 1, 2, 3, 4, 5, 6 };

        /*
         * // This is the transition function
         * // used in nurse_rostering_regular.cs
         * int[,] transition_fn = {
         * // d,n,o
         * {2,3,1}, // state 1
         * {4,4,1}, // state 2
         * {4,5,1}, // state 3
         * {6,6,1}, // state 4
         * {6,0,1}, // state 5
         * {0,0,1}  // state 6
         * };
         */

        // For TransitionConstraint
        IntTupleSet transition_tuples = new IntTupleSet(3);

        // state, input, next state
        transition_tuples.InsertAll(new long[][] {
            new long[] { 1, 1, 2 },
            new long[] { 1, 2, 3 },
            new long[] { 1, 3, 1 },
            new long[] { 2, 1, 4 },
            new long[] { 2, 2, 4 },
            new long[] { 2, 3, 1 },
            new long[] { 3, 1, 4 },
            new long[] { 3, 2, 5 },
            new long[] { 3, 3, 1 },
            new long[] { 4, 1, 6 },
            new long[] { 4, 2, 6 },
            new long[] { 4, 3, 1 },
            new long[] { 5, 1, 6 },
            new long[] { 5, 3, 1 },
            new long[] { 6, 3, 1 }
        });

        string[] days = { "d", "n", "o" }; // for presentation

        //
        // Decision variables
        //

        //
        // For TransitionConstraint
        //
        IntVar[,] x =
            solver.MakeIntVarMatrix(num_nurses, num_days, valid_shifts, "x");
        IntVar[] x_flat = x.Flatten();

        //
        // summary of the nurses
        //
        IntVar[] nurse_stat = new IntVar[num_nurses];

        //
        // summary of the shifts per day
        //
        int num_shifts = shifts.Length;

        IntVar[,] day_stat = new IntVar[num_days, num_shifts];
        for (int i = 0; i < num_days; i++)
        {
            for (int j = 0; j < num_shifts; j++)
            {
                day_stat[i, j] = solver.MakeIntVar(0, num_nurses, "day_stat");
            }
        }

        //
        // Constraints
        //
        for (int i = 0; i < num_nurses; i++)
        {
            IntVar[] reg_input = new IntVar[num_days];
            for (int j = 0; j < num_days; j++)
            {
                reg_input[j] = x[i, j];
            }

            solver.Add(reg_input.Transition(transition_tuples,
                                            initial_state,
                                            accepting_states));
        }

        //
        // Statistics and constraints for each nurse
        //
        for (int nurse = 0; nurse < num_nurses; nurse++)
        {
            // Number of worked days (either day or night shift)
            IntVar[] nurse_days = new IntVar[num_days];
            for (int day = 0; day < num_days; day++)
            {
                nurse_days[day] =
                    x[nurse, day].IsMember(new int[] { day_shift, night_shift });
            }
            nurse_stat[nurse] = nurse_days.Sum().Var();

            // Each nurse must work between 7 and 10
            // days/nights during this period
            solver.Add(nurse_stat[nurse] >= 7 * week_multiplier / nurse_multiplier);
            solver.Add(nurse_stat[nurse] <= 10 * week_multiplier / nurse_multiplier);
        }

        //
        // Statistics and constraints for each day
        //
        for (int day = 0; day < num_days; day++)
        {
            IntVar[] nurses = new IntVar[num_nurses];
            for (int nurse = 0; nurse < num_nurses; nurse++)
            {
                nurses[nurse] = x[nurse, day];
            }
            IntVar[] stats = new IntVar[num_shifts];
            for (int shift = 0; shift < num_shifts; ++shift)
            {
                stats[shift] = day_stat[day, shift];
            }
            solver.Add(nurses.Distribute(stats));

            //
            // Some constraints for each day:
            //
            // Note: We have a strict requirements of
            //       the number of shifts.
            //       Using atleast constraints is harder
            //       in this model.
            //
            if (day % 7 == 5 || day % 7 == 6)
            {
                // special constraints for the weekends
                solver.Add(day_stat[day, day_shift] == 2 * nurse_multiplier);
                solver.Add(day_stat[day, night_shift] == nurse_multiplier);
                solver.Add(day_stat[day, off_shift] == 4 * nurse_multiplier);
            }
            else
            {
                // for workdays:

                // - exactly 3 on day shift
                solver.Add(day_stat[day, day_shift] == 3 * nurse_multiplier);
                // - exactly 2 on night
                solver.Add(day_stat[day, night_shift] == 2 * nurse_multiplier);
                // - exactly 2 off duty
                solver.Add(day_stat[day, off_shift] == 2 * nurse_multiplier);
            }
        }

        //
        // Search
        //
        DecisionBuilder db = solver.MakePhase(x_flat,
                                              Solver.CHOOSE_FIRST_UNBOUND,
                                              Solver.ASSIGN_MIN_VALUE);

        SearchMonitor log = solver.MakeSearchLog(1000000);

        solver.NewSearch(db, log);

        int num_solutions = 0;

        while (solver.NextSolution())
        {
            num_solutions++;
            for (int i = 0; i < num_nurses; i++)
            {
                Console.Write("Nurse #{0,-2}: ", i);
                var occ = new Dictionary <int, int>();
                for (int j = 0; j < num_days; j++)
                {
                    int v = (int)x[i, j].Value() - 1;
                    if (!occ.ContainsKey(v))
                    {
                        occ[v] = 0;
                    }
                    occ[v]++;
                    Console.Write(days[v] + " ");
                }

                Console.Write(" #workdays: {0,2}", nurse_stat[i].Value());
                foreach (int s in valid_shifts)
                {
                    int v = 0;
                    if (occ.ContainsKey(s - 1))
                    {
                        v = occ[s - 1];
                    }
                    Console.Write("  {0}:{1}", days[s - 1], v);
                }
                Console.WriteLine();
            }
            Console.WriteLine();

            Console.WriteLine("Statistics per day:\nDay      d n o");
            for (int j = 0; j < num_days; j++)
            {
                Console.Write("Day #{0,2}: ", j);
                foreach (int t in valid_shifts)
                {
                    Console.Write(day_stat[j, t].Value() + " ");
                }
                Console.WriteLine();
            }
            Console.WriteLine();

            // We just show 2 solutions
            if (num_solutions > 1)
            {
                break;
            }
        }

        Console.WriteLine("\nSolutions: {0}", solver.Solutions());
        Console.WriteLine("WallTime: {0}ms", solver.WallTime());
        Console.WriteLine("Failures: {0}", solver.Failures());
        Console.WriteLine("Branches: {0} ", solver.Branches());

        solver.EndSearch();
    }
示例#4
0
    /**
     *
     * Traffic lights problem.
     *
     * CSPLib problem 16
     * http://www.cs.st-andrews.ac.uk/~ianm/CSPLib/prob/prob016/index.html
     * """
     * Specification:
     * Consider a four way traffic junction with eight traffic lights. Four of the traffic
     * lights are for the vehicles and can be represented by the variables V1 to V4 with domains
     * {r,ry,g,y} (for red, red-yellow, green and yellow). The other four traffic lights are
     * for the pedestrians and can be represented by the variables P1 to P4 with domains {r,g}.
     *
     * The constraints on these variables can be modelled by quaternary constraints on
     * (Vi, Pi, Vj, Pj ) for 1<=i<=4, j=(1+i)mod 4 which allow just the tuples
     * {(r,r,g,g), (ry,r,y,r), (g,g,r,r), (y,r,ry,r)}.
     *
     * It would be interesting to consider other types of junction (e.g. five roads
     * intersecting) as well as modelling the evolution over time of the traffic light sequence.
     * ...
     *
     * Results
     * Only 2^2 out of the 2^12 possible assignments are solutions.
     *
     * (V1,P1,V2,P2,V3,P3,V4,P4) =
     * {(r,r,g,g,r,r,g,g), (ry,r,y,r,ry,r,y,r), (g,g,r,r,g,g,r,r), (y,r,ry,r,y,r,ry,r)}
     * [(1,1,3,3,1,1,3,3), ( 2,1,4,1, 2,1,4,1), (3,3,1,1,3,3,1,1), (4,1, 2,1,4,1, 2,1)}
     * The problem has relative few constraints, but each is very
     * tight. Local propagation appears to be rather ineffective on this
     * problem.
     *
     * """
     * Note: In this model we use only the constraint
     *  solver.AllowedAssignments().
     *
     *
     * See http://www.hakank.org/or-tools/traffic_lights.py
     *
     */
    private static void Solve()
    {
        Solver solver = new Solver("TrafficLights");

        //
        // data
        //
        int n = 4;

        int r  = 0;
        int ry = 1;
        int g  = 2;
        int y  = 3;

        string[] lights = { "r", "ry", "g", "y" };

        // The allowed combinations
        IntTupleSet allowed = new IntTupleSet(4);

        allowed.InsertAll(new int[, ] {
            { r, r, g, g },
            { ry, r, y, r },
            { g, g, r, r },
            { y, r, ry, r }
        });
        //
        // Decision variables
        //
        IntVar[] V = solver.MakeIntVarArray(n, 0, n - 1, "V");
        IntVar[] P = solver.MakeIntVarArray(n, 0, n - 1, "P");

        // for search
        IntVar[] VP = new IntVar[2 * n];
        for (int i = 0; i < n; i++)
        {
            VP[i]     = V[i];
            VP[i + n] = P[i];
        }

        //
        // Constraints
        //
        for (int i = 0; i < n; i++)
        {
            int      j   = (1 + i) % n;
            IntVar[] tmp = new IntVar[] { V[i], P[i], V[j], P[j] };
            solver.Add(tmp.AllowedAssignments(allowed));
        }

        //
        // Search
        //
        DecisionBuilder db = solver.MakePhase(VP,
                                              Solver.CHOOSE_FIRST_UNBOUND,
                                              Solver.ASSIGN_MIN_VALUE);


        solver.NewSearch(db);

        while (solver.NextSolution())
        {
            for (int i = 0; i < n; i++)
            {
                Console.Write("{0,2} {1,2} ",
                              lights[V[i].Value()],
                              lights[P[i].Value()]);
            }
            Console.WriteLine();
        }

        Console.WriteLine("\nSolutions: {0}", solver.Solutions());
        Console.WriteLine("WallTime: {0}ms", solver.WallTime());
        Console.WriteLine("Failures: {0}", solver.Failures());
        Console.WriteLine("Branches: {0} ", solver.Branches());

        solver.EndSearch();
    }
    /**
       *
       * Traffic lights problem.
       *
       * CSPLib problem 16
       * http://www.csplib.org/Problems/prob016
       * """
       * Specification:
       * Consider a four way traffic junction with eight traffic lights. Four of the traffic
       * lights are for the vehicles and can be represented by the variables V1 to V4 with domains
       * {r,ry,g,y} (for red, red-yellow, green and yellow). The other four traffic lights are
       * for the pedestrians and can be represented by the variables P1 to P4 with domains {r,g}.
       *
       * The constraints on these variables can be modelled by quaternary constraints on
       * (Vi, Pi, Vj, Pj ) for 1<=i<=4, j=(1+i)mod 4 which allow just the tuples
       * {(r,r,g,g), (ry,r,y,r), (g,g,r,r), (y,r,ry,r)}.
       *
       * It would be interesting to consider other types of junction (e.g. five roads
       * intersecting) as well as modelling the evolution over time of the traffic light sequence.
       * ...
       *
       * Results
       * Only 2^2 out of the 2^12 possible assignments are solutions.
       *
       * (V1,P1,V2,P2,V3,P3,V4,P4) =
       * {(r,r,g,g,r,r,g,g), (ry,r,y,r,ry,r,y,r), (g,g,r,r,g,g,r,r), (y,r,ry,r,y,r,ry,r)}
       * [(1,1,3,3,1,1,3,3), ( 2,1,4,1, 2,1,4,1), (3,3,1,1,3,3,1,1), (4,1, 2,1,4,1, 2,1)}
       * The problem has relative few constraints, but each is very
       * tight. Local propagation appears to be rather ineffective on this
       * problem.
       *
       * """
       * Note: In this model we use only the constraint
       *  solver.AllowedAssignments().
       *
       *
       * See http://www.hakank.org/or-tools/traffic_lights.py
       *
       */
    private static void Solve()
    {
        Solver solver = new Solver("TrafficLights");

        //
        // data
        //
        int n = 4;

        int r = 0;
        int ry = 1;
        int g = 2;
        int y = 3;

        string[] lights = {"r", "ry", "g", "y"};

        // The allowed combinations
        IntTupleSet allowed = new IntTupleSet(4);
        allowed.InsertAll(new int[,] {{r,r,g,g},
                                  {ry,r,y,r},
                                  {g,g,r,r},
                                  {y,r,ry,r}});
        //
        // Decision variables
        //
        IntVar[] V = solver.MakeIntVarArray(n, 0, n-1, "V");
        IntVar[] P = solver.MakeIntVarArray(n, 0, n-1, "P");

        // for search
        IntVar[] VP = new IntVar[2 * n];
        for(int i = 0; i < n; i++) {
          VP[i] = V[i];
          VP[i+n] = P[i];
        }

        //
        // Constraints
        //
        for(int i = 0; i < n; i++) {
          int j = (1+i) % n;
          IntVar[] tmp = new IntVar[] {V[i],P[i],V[j],P[j]};
          solver.Add(tmp.AllowedAssignments(allowed));
        }

        //
        // Search
        //
        DecisionBuilder db = solver.MakePhase(VP,
                                          Solver.CHOOSE_FIRST_UNBOUND,
                                          Solver.ASSIGN_MIN_VALUE);

        solver.NewSearch(db);

        while (solver.NextSolution()) {
          for(int i = 0; i < n; i++) {
        Console.Write("{0,2} {1,2} ",
                      lights[V[i].Value()],
                      lights[P[i].Value()]);
          }
          Console.WriteLine();
        }

        Console.WriteLine("\nSolutions: {0}", solver.Solutions());
        Console.WriteLine("WallTime: {0}ms", solver.WallTime());
        Console.WriteLine("Failures: {0}", solver.Failures());
        Console.WriteLine("Branches: {0} ", solver.Branches());

        solver.EndSearch();
    }
        /// <summary>
        ///     Get a new schedule based on number of teams and days.
        ///     Provide a rule set for transitions and day and employee constraints
        /// </summary>
        /// <param name="ruleSet">Rule set to use for transitions</param>
        /// <param name="shiftEmployees">Participating employees for the shifts</param>
        /// <param name="startDate">Schedule start date</param>
        /// <param name="numberOfDays">Number of days in each cycle</param>
        /// <param name="teamSize">Number of employees in each team</param>
        /// <param name="minShiftsPerCycle">Required number of shifts per cycle</param>
        /// <param name="startHour">Starting hour of the day shift</param>
        /// <param name="shiftHours">Hours in every shift, up to 12</param>
        /// <param name="maxSolutions">Max returned results</param>
        /// <returns></returns>
        public Task <List <Schedule> > CreateNewScheduleAsync(
            RuleSet ruleSet,
            IList <Employee> shiftEmployees,
            DateTime startDate,
            int numberOfDays,
            int teamSize,
            int minShiftsPerCycle,
            int startHour,
            int shiftHours,
            int maxSolutions = 1)
        {
            // Some sanity checks
            if (ruleSet == null)
            {
                throw new ArgumentOutOfRangeException($"Rule set is empty.");
            }
            if (shiftEmployees == null || shiftEmployees.Count < 1)
            {
                throw new ArgumentOutOfRangeException($"Employee collection is empty.");
            }
            if (numberOfDays < 1)
            {
                throw new ArgumentOutOfRangeException($"Invalid number for days in a cycle.");
            }
            if (teamSize < 1)
            {
                throw new ArgumentOutOfRangeException($"Invalid number for employees in each shift.");
            }
            if (minShiftsPerCycle < 0)
            {
                throw new ArgumentOutOfRangeException($"Invalid number for minimum shifts per cycle.");
            }
            if (startHour > 12)
            {
                throw new ArgumentOutOfRangeException(
                          $"Starting hour is bigger than expected. Please provide a number between 0-12");
            }
            if (shiftHours > 12)
            {
                throw new ArgumentOutOfRangeException(
                          $"Shift hours cannot be bigger than twelve. Please provide a number between 1-12");
            }

            var numberOfEmployees = shiftEmployees.Count;

            LastError = null;

            AddDiagnostics("Starting to solve a new schedule\n\n");
            AddDiagnostics("This is a schedule for {0} employees in {1} days\n", numberOfEmployees, numberOfDays);
            AddDiagnostics("Shift team size: {0}, minimum shifts per employee: {1}\n\n", teamSize, minShiftsPerCycle);

            /*
             * Solver
             */

            // Initiate a new solver
            var solver = new Solver("Schedule");

            int[] shifts      = { ShiftConsts.None, ShiftConsts.Day, ShiftConsts.Night, ShiftConsts.Off };
            int[] validShifts = { ShiftConsts.Day, ShiftConsts.Night, ShiftConsts.Off };

            /*
             * DFA and Transitions
             */
            var initialState = ruleSet.InitialState; // Everybody starts at this state

            int[] acceptingStates = ruleSet.AcceptingStates;

            // Transition tuples For TransitionConstraint
            var transitionTuples = new IntTupleSet(3);

            // Every tuple contains { state, input, next state }
            transitionTuples.InsertAll(ruleSet.Tuples);

            // Just for presentation in stats
            string[] days = { "d", "n", "o" };

            /*
             * Decision variables
             */

            // TransitionConstraint
            var x =
                solver.MakeIntVarMatrix(numberOfEmployees, numberOfDays, validShifts, "x");

            var flattenedX = x.Flatten();

            // Shift count
            var shiftCount = shifts.Length;

            // Shifts per day statistics
            var dayStats = new IntVar[numberOfDays, shiftCount];

            for (var i = 0; i < numberOfDays; i++)
            {
                for (var j = 0; j < shiftCount; j++)
                {
                    dayStats[i, j] = solver.MakeIntVar(0, numberOfEmployees, "dayStats");
                }
            }

            // Team statistics
            var teamStats = new IntVar[numberOfEmployees];

            /*
             * Constraints
             */
            for (var i = 0; i < numberOfEmployees; i++)
            {
                var regInput = new IntVar[numberOfDays];
                for (var j = 0; j < numberOfDays; j++)
                {
                    regInput[j] = x[i, j];
                }

                solver.Add(regInput.Transition(transitionTuples, initialState, acceptingStates));
            }

            // Statistics and constraints for each team
            for (var team = 0; team < numberOfEmployees; team++)
            {
                // Number of worked days (either day or night shift)
                var teamDays = new IntVar[numberOfDays];
                for (var day = 0; day < numberOfDays; day++)
                {
                    teamDays[day] = x[team, day].IsMember(new[] { ShiftConsts.Day, ShiftConsts.Night });
                }

                teamStats[team] = teamDays.Sum().Var();

                // At least two shifts per cycle
                solver.Add(teamStats[team] >= minShiftsPerCycle);
            }

            // Statistics and constraints for each day
            for (var day = 0; day < numberOfDays; day++)
            {
                var teams = new IntVar[numberOfEmployees];
                for (var team = 0; team < numberOfEmployees; team++)
                {
                    teams[team] = x[team, day];
                }

                var stats = new IntVar[shiftCount];
                for (var shift = 0; shift < shiftCount; ++shift)
                {
                    stats[shift] = dayStats[day, shift];
                }

                solver.Add(teams.Distribute(stats));

                // Constraints for each day

                // - exactly teamSize on day shift
                solver.Add(dayStats[day, ShiftConsts.Day] == teamSize);
                // - exactly teamSize on night shift
                solver.Add(dayStats[day, ShiftConsts.Night] == teamSize);
                // - The rest of the employees are off duty
                solver.Add(dayStats[day, ShiftConsts.Off] == numberOfEmployees - teamSize * 2);

                /* We can customize constraints even further
                 * For example, a special constraints for weekends(1 employee each shift as weekends are quiet):
                 * if (day % 7 == 5 || day % 7 == 6)
                 * {
                 *  solver.Add(dayStats[day, ShiftConsts.Day] == weekendTeamSize);
                 *  solver.Add(dayStats[day, ShiftConsts.Night] == weekendTeamSize);
                 *  solver.Add(dayStats[day, ShiftConsts.Off] == numberOfEmployees - weekendTeamSize * 2);
                 * }
                 */
            }

            /*
             * Decision Builder and Solution Search
             */

            // A simple random selection
            var db = solver.MakePhase(flattenedX, Solver.CHOOSE_DYNAMIC_GLOBAL_BEST, Solver.ASSIGN_RANDOM_VALUE);

            var log = solver.MakeSearchLog(1000000);

            // Don't search after a certain miliseconds
            var timeLimit = solver.MakeTimeLimit(1000); // a second

            // Start the search
            solver.NewSearch(db, log, timeLimit);

            // Return solutions as result
            var schedules = new List <Schedule>();

            var numSolutions = 0;

            while (solver.NextSolution())
            {
                numSolutions++;

                // A new schedule for the time period
                var schedule = new Schedule
                {
                    Id   = numSolutions,
                    Name = string.Format("Schedule for {0}-{1} for {2} employees, team size {3}",
                                         startDate.Date.ToShortDateString(), startDate.Date.AddDays(numberOfDays).ToShortDateString(),
                                         numberOfEmployees, teamSize),
                    StartDate = startDate.Date,
                    EndDate   = startDate.Date.AddDays(numberOfDays),
                    Shifts    = new List <Shift>()
                };

                var idCounter = 1;
                for (var i = 0; i < numberOfEmployees; i++)
                {
                    AddDiagnostics("Employee #{0,-2}: ", i + 1, shiftEmployees[i].ToString());

                    var occ = new Dictionary <int, int>();
                    for (var j = 0; j < numberOfDays; j++)
                    {
                        var shiftVal = (int)x[i, j].Value() - 1;
                        if (!occ.ContainsKey(shiftVal))
                        {
                            occ[shiftVal] = 0;
                        }
                        occ[shiftVal]++;

                        // Add a shift
                        var shiftType  = (ShiftType)shiftVal + 1;
                        var shiftStart = startDate.Date
                                         .AddDays(j)
                                         .AddHours(shiftType == ShiftType.Off
                                ? 0
                                : (shiftType == ShiftType.Day
                                    ? startHour
                                    : startHour + shiftHours)); // i.e Day shift starts at 07:00, night shift at 19:00

                        schedule.Shifts.Add(new Shift
                        {
                            Id        = idCounter,
                            Employee  = shiftEmployees[i],
                            Type      = shiftType,
                            StartDate = shiftStart,
                            EndDate   = shiftType == ShiftType.Off ? shiftStart : shiftStart.AddHours(shiftHours)
                        });
                        idCounter++;
                        AddDiagnostics(days[shiftVal] + " ");
                    }

                    AddDiagnostics(" #Total days: {0,2}", teamStats[i].Value());
                    foreach (var s in validShifts)
                    {
                        var v = 0;
                        if (occ.ContainsKey(s - 1))
                        {
                            v = occ[s - 1];
                        }
                        AddDiagnostics("  {0}:{1}", days[s - 1], v);
                    }

                    AddDiagnostics("\t- {0}\n", shiftEmployees[i].ToString());
                }

                AddDiagnostics("\n");

                AddDiagnostics("Daily Statistics\nDay\t\td n o\n");
                for (var j = 0; j < numberOfDays; j++)
                {
                    AddDiagnostics("Day #{0,2}: \t", j + 1);
                    foreach (var t in validShifts)
                    {
                        AddDiagnostics(dayStats[j, t].Value() + " ");
                    }
                    AddDiagnostics("\n");
                }

                AddDiagnostics("\n");

                // Add this schedule to list
                schedules.Add(schedule);

                // defaults to just the first one
                if (numSolutions >= maxSolutions)
                {
                    break;
                }
            }

            AddDiagnostics("\nSolutions: {0}", solver.Solutions());
            AddDiagnostics("\nFailures: {0}", solver.Failures());
            AddDiagnostics("\nBranches: {0} ", solver.Branches());
            AddDiagnostics("\nWallTime: {0}ms", solver.WallTime());

            solver.EndSearch();

            AddDiagnostics("\n\nFinished solving the schedule.");

            if (schedules.Count < 1)
            {
                LastError = "There's no solution in the model for your input.";
                // We reached the limit and there's no solution
                AddDiagnostics("\n\nThere's no solution in the model for your input.");
            }

            return(Task.FromResult(schedules));
        }
  /**
   *
   * Nurse rostering
   *
   * This is a simple nurse rostering model using a DFA and
   * the built-in TransitionConstraint.
   *
   * The DFA is from MiniZinc Tutorial, Nurse Rostering example:
   * - one day off every 4 days
   * - no 3 nights in a row.
   *
   * Also see:
   *  - http://www.hakank.org/or-tools/nurse_rostering.py
   *  - http://www.hakank.org/or-tools/nurse_rostering_regular.cs
   *    which use (a decomposition of) regular constraint
   *
   */
  private static void Solve(int nurse_multiplier, int week_multiplier)
  {
    Console.WriteLine("Starting Nurse Rostering");
    Console.WriteLine("  - {0} teams of 7 nurses", nurse_multiplier);
    Console.WriteLine("  - {0} blocks of 14 days", week_multiplier);

    Solver solver = new Solver("NurseRostering");

    //
    // Data
    //

    // Note: If you change num_nurses or num_days,
    //       please also change the constraints
    //       on nurse_stat and/or day_stat.
    int num_nurses = 7 * nurse_multiplier;
    int num_days = 14 * week_multiplier;

    // Note: I had to add a dummy shift.
    int dummy_shift = 0;
    int day_shift = 1;
    int night_shift = 2;
    int off_shift = 3;
    int[] shifts = {dummy_shift, day_shift, night_shift, off_shift};
    int[] valid_shifts = {day_shift, night_shift, off_shift};

    // the DFA (for regular)
    int initial_state = 1;
    int[] accepting_states = {1,2,3,4,5,6};

    /*
      // This is the transition function
      // used in nurse_rostering_regular.cs
    int[,] transition_fn = {
      // d,n,o
      {2,3,1}, // state 1
      {4,4,1}, // state 2
      {4,5,1}, // state 3
      {6,6,1}, // state 4
      {6,0,1}, // state 5
      {0,0,1}  // state 6
    };
    */

    // For TransitionConstraint
    IntTupleSet transition_tuples = new IntTupleSet(3);
    // state, input, next state
    transition_tuples.InsertAll(new int[,] { {1,1,2},
                                             {1,2,3},
                                             {1,3,1},
                                             {2,1,4},
                                             {2,2,4},
                                             {2,3,1},
                                             {3,1,4},
                                             {3,2,5},
                                             {3,3,1},
                                             {4,1,6},
                                             {4,2,6},
                                             {4,3,1},
                                             {5,1,6},
                                             {5,3,1},
                                             {6,3,1} });

    string[] days = {"d","n","o"}; // for presentation

    //
    // Decision variables
    //

    //
    // For TransitionConstraint
    //
    IntVar[,] x =
        solver.MakeIntVarMatrix(num_nurses, num_days, valid_shifts, "x");
    IntVar[] x_flat = x.Flatten();

    //
    // summary of the nurses
    //
    IntVar[] nurse_stat = new IntVar[num_nurses];

    //
    // summary of the shifts per day
    //
    int num_shifts = shifts.Length;
    IntVar[,] day_stat = new IntVar[num_days, num_shifts];
    for(int i = 0; i < num_days; i++) {
      for(int j = 0; j < num_shifts; j++) {
        day_stat[i,j] = solver.MakeIntVar(0, num_nurses, "day_stat");
      }
    }

    //
    // Constraints
    //
    for(int i = 0; i < num_nurses; i++) {
      IntVar[] reg_input = new IntVar[num_days];
      for(int j = 0; j < num_days; j++) {
        reg_input[j] = x[i,j];
      }

      solver.Add(reg_input.Transition(transition_tuples,
                                      initial_state,
                                      accepting_states));
    }

    //
    // Statistics and constraints for each nurse
    //
    for(int nurse = 0; nurse < num_nurses; nurse++) {

      // Number of worked days (either day or night shift)
      IntVar[] nurse_days = new IntVar[num_days];
      for(int day = 0; day < num_days; day++) {
        nurse_days[day] =
            x[nurse, day].IsMember(new int[] { day_shift, night_shift });
      }
      nurse_stat[nurse] = nurse_days.Sum().Var();

      // Each nurse must work between 7 and 10
      // days/nights during this period
      solver.Add(nurse_stat[nurse] >= 7 * week_multiplier / nurse_multiplier);
      solver.Add(nurse_stat[nurse] <= 10 * week_multiplier / nurse_multiplier);

    }

    //
    // Statistics and constraints for each day
    //
    for(int day = 0; day < num_days; day++) {
      IntVar[] nurses = new IntVar[num_nurses];
      for(int nurse = 0; nurse < num_nurses; nurse++) {
        nurses[nurse] = x[nurse, day];
      }
      IntVar[] stats = new IntVar[num_shifts];
      for (int shift = 0; shift < num_shifts; ++shift)
      {
        stats[shift] = day_stat[day, shift];
      }
      solver.Add(nurses.Distribute(stats));

      //
      // Some constraints for each day:
      //
      // Note: We have a strict requirements of
      //       the number of shifts.
      //       Using atleast constraints is harder
      //       in this model.
      //
      if (day % 7 == 5 || day % 7 == 6) {
        // special constraints for the weekends
        solver.Add(day_stat[day, day_shift] == 2 * nurse_multiplier);
        solver.Add(day_stat[day, night_shift] == nurse_multiplier);
        solver.Add(day_stat[day, off_shift] == 4 * nurse_multiplier);
      } else {
        // for workdays:

        // - exactly 3 on day shift
        solver.Add(day_stat[day, day_shift] == 3 * nurse_multiplier);
        // - exactly 2 on night
        solver.Add(day_stat[day, night_shift] == 2 * nurse_multiplier);
        // - exactly 2 off duty
        solver.Add(day_stat[day, off_shift] == 2 * nurse_multiplier);
      }
    }

    //
    // Search
    //
    DecisionBuilder db = solver.MakePhase(x_flat,
                                          Solver.CHOOSE_FIRST_UNBOUND,
                                          Solver.ASSIGN_MIN_VALUE);

    SearchMonitor log = solver.MakeSearchLog(1000000);

    solver.NewSearch(db, log);

    int num_solutions = 0;
    while (solver.NextSolution()) {
      num_solutions++;
      for(int i = 0; i < num_nurses; i++) {
        Console.Write("Nurse #{0,-2}: ", i);
        var occ = new Dictionary<int, int>();
        for(int j = 0; j < num_days; j++) {
          int v = (int)x[i,j].Value()-1;
          if (!occ.ContainsKey(v)) {
            occ[v] = 0;
          }
          occ[v]++;
          Console.Write(days[v] + " ");
        }

        Console.Write(" #workdays: {0,2}", nurse_stat[i].Value());
        foreach(int s in valid_shifts) {
          int v = 0;
          if (occ.ContainsKey(s-1)) {
            v = occ[s-1];
          }
          Console.Write("  {0}:{1}", days[s-1], v);
        }
        Console.WriteLine();

      }
      Console.WriteLine();

      Console.WriteLine("Statistics per day:\nDay      d n o");
      for(int j = 0; j < num_days; j++) {
        Console.Write("Day #{0,2}: ", j);
        foreach(int t in valid_shifts) {
          Console.Write(day_stat[j,t].Value() + " ");
        }
        Console.WriteLine();
      }
      Console.WriteLine();

      // We just show 2 solutions
      if (num_solutions > 1) {
        break;
      }
    }

    Console.WriteLine("\nSolutions: {0}", solver.Solutions());
    Console.WriteLine("WallTime: {0}ms", solver.WallTime());
    Console.WriteLine("Failures: {0}", solver.Failures());
    Console.WriteLine("Branches: {0} ", solver.Branches());

    solver.EndSearch();

  }