107 lines
3.5 KiB
C#
107 lines
3.5 KiB
C#
using System.Diagnostics;
|
|
using Core.Entities;
|
|
using Google.OrTools.Sat;
|
|
|
|
namespace Core.Calculation;
|
|
public class TeamScheduler
|
|
{
|
|
private readonly IList<Student> _studentObjects;
|
|
private readonly IList<Team> _teamObjects;
|
|
|
|
private readonly int[] _students;
|
|
private readonly int[] _teams;
|
|
private readonly int[] _timeSlots;
|
|
|
|
private readonly List<Tuple<int,int>> _scheduleSeparateTeams = [];
|
|
|
|
public TeamScheduler(IEnumerable<Team> teams, int numTimeSlots)
|
|
{
|
|
_teamObjects = teams.ToArray();
|
|
_studentObjects = teams.SelectMany(t => t.Students).Distinct().ToList();
|
|
|
|
_students = Enumerable.Range(0, _studentObjects.Count).ToArray();
|
|
_teams = Enumerable.Range(0, _teamObjects.Count).ToArray();
|
|
_timeSlots = Enumerable.Range(0, numTimeSlots).ToArray();
|
|
}
|
|
|
|
public void ScheduleSeparate(Team team1, Team team2)
|
|
{
|
|
var one = _teamObjects.IndexOf(team1);
|
|
var two = _teamObjects.IndexOf(team2);
|
|
_scheduleSeparateTeams.Add(Tuple.Create(one,two));
|
|
}
|
|
|
|
public static TeamScheduler CreateInstance(IEnumerable<Team> teams, int numTimeSlots)
|
|
{
|
|
return new TeamScheduler(teams, numTimeSlots);
|
|
}
|
|
|
|
public TeamSchedulerSolution Solve()
|
|
{
|
|
// Model.
|
|
var model = new CpModel();
|
|
|
|
// Data
|
|
var m = new int[_students.Length,_teams.Length];
|
|
foreach (var i in _students)
|
|
foreach (var t in _teams)
|
|
m[i, t] = _studentObjects[i].Teams.Contains(_teamObjects[t]) ? 1 : 0;
|
|
|
|
// Variables.
|
|
// x - 1 if meeting of team t takes place at time slot s, else 0
|
|
var x = new IntVar[_teams.Length, _timeSlots.Length];
|
|
foreach (var t in _teams)
|
|
foreach (var s in _timeSlots)
|
|
x[t, s] = model.NewIntVar(0, 1,$"team time slots[{t},{s}]");
|
|
|
|
// y - 1 if individual i has meetings at time slot s, 0 otherwise
|
|
var y = new IntVar[_students.Length, _timeSlots.Length];
|
|
foreach (var i in _students)
|
|
foreach (var s in _timeSlots)
|
|
y[i, s] = model.NewIntVar(0, 1, $"individual time slots[{i},{s}]");
|
|
|
|
// each team meets exactly one time
|
|
foreach (var t in _teams)
|
|
model.AddLinearConstraint(LinearExpr.Sum(_timeSlots.Select(s => x[t, s])), 1L, 1L);
|
|
|
|
// individual must have at least one team meeting at the given time slot to attend
|
|
foreach (var i in _students)
|
|
foreach (var s in _timeSlots)
|
|
model.Add(
|
|
new BoundedLinearExpression(
|
|
y[i, s],
|
|
LinearExpr.Sum(_teams.Select(t => m[i, t] * x[t, s])),
|
|
false));
|
|
|
|
// maximize number of times individuals meet
|
|
var indTimeSlotVars = LinearExpr.NewBuilder();
|
|
foreach (var i in _students)
|
|
foreach (var s in _timeSlots)
|
|
indTimeSlotVars.Add(y[i, s]);
|
|
model.Minimize(indTimeSlotVars);
|
|
|
|
foreach (var ts in _scheduleSeparateTeams)
|
|
foreach (var s in _timeSlots)
|
|
model.Add(x[ts.Item1, s] != x[ts.Item2, s]);
|
|
|
|
var solver = new CpSolver();
|
|
solver.StringParameters = "max_time_in_seconds:2.0";
|
|
|
|
var cpSolverStatus = solver.Solve(model);
|
|
Debug.WriteLine($"Solver status: {cpSolverStatus}");
|
|
var timeSlotTeams = new Team[_timeSlots.Length][];
|
|
|
|
if (cpSolverStatus is not (CpSolverStatus.Optimal or CpSolverStatus.Feasible))
|
|
return new TeamSchedulerSolution(timeSlotTeams, cpSolverStatus.ToString());
|
|
|
|
Debug.WriteLine($"Total cost: {solver.ObjectiveValue}\n");
|
|
|
|
foreach (var s in _timeSlots)
|
|
{
|
|
var teams = (from t in _teams where solver.Value(x[t, s]) > 0 select _teamObjects[t]).ToArray();
|
|
timeSlotTeams[s] = teams;
|
|
}
|
|
//Debug.WriteLine("No solution found.");
|
|
return new TeamSchedulerSolution(timeSlotTeams, cpSolverStatus.ToString());
|
|
}
|
|
} |