0

I started studying Google OR-Tools and decided to try to make a task from examples, adding my own restrictions. And it’s impossible to make one of them, which determines that after the worker leaves for a shift, it should remain for a random number of days, after which one day off follows, and the next shift is issued, which should also remain for a random number of days.

The code itself:

using System;
using System.Collections.Generic;
using System.Linq;
using Google.OrTools.Sat;

public class WorkerSolutionObserver : CpSolverSolutionCallback
{
    public WorkerSolutionObserver(BoolVar[,,] shifts, int num_workers, int num_days, int num_shifts,
                                 HashSet<int> to_print, int last_solution_explored)
    {
        shifts_ = shifts;
        num_workers_ = num_workers;
        num_days_ = num_days;
        num_shifts_ = num_shifts;
        to_print_ = to_print;
        last_solution_explored_ = last_solution_explored;
    }

    public override void OnSolutionCallback()
    {
        solution_count_++;
        if (to_print_.Contains(solution_count_))
        {
            Console.WriteLine(String.Format("Solution #{0}: time = {1:.02} s", solution_count_, WallTime()));
            for (int d = 0; d < num_days_; ++d)
            {
                Console.WriteLine(String.Format("Day #{0}", d+1));
                for (int w = 0; w < num_workers_; ++w)
                {
                    for (int s = 0; s < num_shifts_; ++s)
                    {
                        if (BooleanValue(shifts_[w, d, s]))
                        {
                            Console.WriteLine(String.Format("  Worker #{0} is working shift #{1}", w, s));
                        }
                    }
                }
            }
        }
        if (solution_count_ >= last_solution_explored_)
        {
            StopSearch();
        }
    }

    public int SolutionCount()
    {
        return solution_count_;
    }

    private int solution_count_;
    private BoolVar[,,] shifts_;
    private int num_workers_;
    private int num_days_;
    private int num_shifts_;
    private HashSet<int> to_print_;
    private int last_solution_explored_;
}

public class WorkerSat
{
    static void Main()
    {
        int num_workers = 5;
        int num_shifts = 5;
        int num_days = 10;

        var all_workers = Enumerable.Range(0, num_workers);
        var all_shifts = Enumerable.Range(0, num_shifts);
        var all_working_shifts = Enumerable.Range(1, num_shifts - 1);
        var all_days = Enumerable.Range(0, num_days);

        CpModel model = new CpModel();

        // Создает переменные расписания
        // shift[w, d, s]: работник "w" работает в смену "s" в день"d".
        BoolVar[,,] shift = new BoolVar[num_workers, num_days, num_shifts];
        foreach (int w in all_workers)
        {
            foreach (int d in all_days)
            {
                foreach (int s in all_shifts)
                {
                    shift[w, d, s] = model.NewBoolVar(String.Format("shift_w{0}d{1}s{2}", w, d, s));
                }
            }
        }

        // Делает задания разными на каждый день на каждую смену(гарантировано, что на все смены в d день будет назначен работник)
        foreach (int d in all_days)
        {
            foreach (int s in all_shifts)
            {
                List<ILiteral> workers = new List<ILiteral>();
                foreach (int w in all_workers)
                {
                    workers.Add(shift[w, d, s]);
                }
                model.AddExactlyOne(workers);
            }
        }

        //Рабочий работает 1 смену в день.
        foreach (int w in all_workers)
        {
            foreach (int d in all_days)
            {
                List<ILiteral> worked = new List<ILiteral>();
                foreach (int s in all_shifts)
                {
                    worked.Add(shift[w, d, s]);
                }
                model.AddExactlyOne(worked);
            }
        }

        BoolVar[,] works_shift = new BoolVar[num_workers, num_shifts];
        foreach (int w in all_workers)
        {
            foreach (int s in all_shifts)
            {
                works_shift[w, s] = model.NewBoolVar(String.Format("works_shift_n{0}s{1}", w, s));
                BoolVar[] worked = new BoolVar[num_days];
                foreach (int d in all_days)
                {
                    worked[d] = shift[w, d, s];
                }
                model.AddMaxEquality(works_shift[w, s], worked);
            }
        }
        CpSolver solver = new CpSolver();
        HashSet<int> to_print = new HashSet<int>();
        to_print.Add(100);
        to_print.Add(200);
        WorkerSolutionObserver cb = new WorkerSolutionObserver(shift, num_workers, num_days, num_shifts, to_print, 200);
        solver.StringParameters = "linearization_level:1 enumerate_all_solutions:true";
        CpSolverStatus status = solver.Solve(model, cb);

        Console.WriteLine("Statistics");
        Console.WriteLine(String.Format("- solve status: {0}", status));
        Console.WriteLine("- conflicts: " + solver.NumConflicts());
        Console.WriteLine("- branches: " + solver.NumBranches());
        Console.WriteLine("- wall time: " + solver.WallTime() + " ms");
        Console.WriteLine("- #solutions: " + cb.SolutionCount());
    }
}

Thanks in advance.

Qumel
  • 1

1 Answers1

0

I suggest you look at the following example that contains a lot of usual constraints that appear workforce scheduling.

https://github.com/google/or-tools/blob/stable/examples/dotnet/ShiftSchedulingSat.cs

Laurent Perron
  • 8,594
  • 1
  • 8
  • 22
  • I already saw it, tried to do it, but nothing came of it, the result of the calculations is not what I need. – Qumel Oct 25 '22 at 16:49