Add Team functions
This commit is contained in:
@@ -57,7 +57,6 @@ namespace Core.Calculation
|
||||
_twoTeams = events;
|
||||
}
|
||||
|
||||
|
||||
public async Task<EventAssignmentSolution> Solve()
|
||||
{
|
||||
Debug.WriteLine(_parameters);
|
||||
@@ -78,21 +77,25 @@ namespace Core.Calculation
|
||||
LimitStudentAssignment(model, x);
|
||||
|
||||
// set the range for level of effort
|
||||
var eventEffortCoefficients = GetEventEffortCoefficients(_events);
|
||||
SetLevelOfEffort(model, x, eventEffortCoefficients);
|
||||
SetLevelOfEffort(model, x);
|
||||
|
||||
// each student should be assigned at least one on site activity eventDefinition
|
||||
// each student should be assigned at least one on site activity
|
||||
if (_parameters.RequireOnSite)
|
||||
RequireOnSiteActivity(model, x);
|
||||
|
||||
// students should have at maximum one individual eventDefinition
|
||||
LimitIndividualEvent(model, x);
|
||||
|
||||
|
||||
// students should have at least one regional event
|
||||
if (_parameters.RequireRegional)
|
||||
RequireRegionalEvent(model, x);
|
||||
|
||||
OptimizeStudentEventRankings(model, x);
|
||||
// students should have at maximum one individual event
|
||||
AtMostOneIndividualEvent(model, x);
|
||||
|
||||
//EventHasInterestedStudent(model, x);
|
||||
|
||||
IndividualEventsMustBeRanked(model, x);
|
||||
|
||||
OptimizeStudentEventRankings(model, x);
|
||||
|
||||
|
||||
Debug.WriteLine("Starting optimization");
|
||||
var solver = new CpSolver();
|
||||
@@ -114,36 +117,34 @@ namespace Core.Calculation
|
||||
return eventAssignmentSolution;
|
||||
}
|
||||
|
||||
// Take the solution and map it back to the entities
|
||||
private List<Team> GetEventAssignments(BoolVar[,] x, CpSolver solver, CpSolverStatus cpSolverStatus)
|
||||
{
|
||||
var eventAssignmentsList = new List<Team>();
|
||||
if (cpSolverStatus is not (CpSolverStatus.Optimal or CpSolverStatus.Feasible))
|
||||
return [];
|
||||
|
||||
if (cpSolverStatus == CpSolverStatus.Optimal || cpSolverStatus == CpSolverStatus.Feasible)
|
||||
{
|
||||
foreach (var e in _allEvents)
|
||||
var eventAssignments =
|
||||
from e in _allEvents
|
||||
let students =
|
||||
from s in _allStudents
|
||||
where solver.BooleanValue(x[e, s])
|
||||
select _students[s]
|
||||
where students.Any()
|
||||
select new Team
|
||||
{
|
||||
var students = new List<Student>();
|
||||
foreach (var s in _allStudents)
|
||||
{
|
||||
if (solver.BooleanValue(x[e, s]))
|
||||
{
|
||||
students.Add(_students[s]);
|
||||
|
||||
}
|
||||
}
|
||||
if (students.Count > 0)
|
||||
eventAssignmentsList.Add(new Team { TeamId = _events[e].Name, Event = _events[e], Students = students});
|
||||
}
|
||||
}
|
||||
TeamId = _events[e].Name,
|
||||
Event = _events[e],
|
||||
Students = students.ToList()
|
||||
};
|
||||
|
||||
return eventAssignmentsList;
|
||||
return eventAssignments.ToList();
|
||||
}
|
||||
|
||||
// Maximize student event rankings
|
||||
private void OptimizeStudentEventRankings(CpModel model, BoolVar[,] x)
|
||||
{
|
||||
var maximizePicks = LinearExpr.NewBuilder();
|
||||
|
||||
// optimize student event rankings
|
||||
|
||||
foreach (var s in _allStudents)
|
||||
{
|
||||
var eventPickCoefficients = GetEventPickCoefficients(_events, _students[s].EventRankings);
|
||||
@@ -167,13 +168,13 @@ namespace Core.Calculation
|
||||
regionalEvent.Add(x[e, s]);
|
||||
}
|
||||
|
||||
//if (_parameters.RequireRegional)
|
||||
// between 1 and 2 regional events
|
||||
model.AddLinearConstraint(LinearExpr.Sum(regionalEvent), 1, 2);
|
||||
regionalEvent.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void LimitIndividualEvent(CpModel model, BoolVar[,] x)
|
||||
private void AtMostOneIndividualEvent(CpModel model, BoolVar[,] x)
|
||||
{
|
||||
foreach (var s in _allStudents)
|
||||
{
|
||||
@@ -206,8 +207,30 @@ namespace Core.Calculation
|
||||
}
|
||||
}
|
||||
|
||||
private void SetLevelOfEffort(CpModel model, BoolVar[,] x, long[] eventEffortCoefficients)
|
||||
private void IndividualEventsMustBeRanked(CpModel model, BoolVar[,] x)
|
||||
{
|
||||
foreach (var s in _allStudents)
|
||||
{
|
||||
var student = _students[s];
|
||||
foreach (var e in _allEvents)
|
||||
{
|
||||
var evt = _events[e];
|
||||
|
||||
var prohibitVar = new List<IntVar> { x[e, s] };
|
||||
if (evt.EventFormat == EventFormat.Individual
|
||||
&& student.EventRankings.Find(er => er.EventDefinition == evt) == null)
|
||||
model.AddLinearConstraint(LinearExpr.Sum(prohibitVar), 0, 0);
|
||||
prohibitVar.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetLevelOfEffort(CpModel model, BoolVar[,] x)
|
||||
{
|
||||
long[] eventEffortCoefficients = _events.Select(
|
||||
e => e.LevelOfEffort ?? 1L
|
||||
).ToArray();
|
||||
|
||||
foreach (var s in _allStudents)
|
||||
{
|
||||
var effortVar = new BoolVar[_allEvents.Length];
|
||||
@@ -234,9 +257,9 @@ namespace Core.Calculation
|
||||
}
|
||||
}
|
||||
|
||||
// Limit the number of events a student is assigned
|
||||
private void LimitStudentAssignment(CpModel model, BoolVar[,] x)
|
||||
{
|
||||
// Limit the number of events a student is assigned
|
||||
foreach (var s in _allStudents)
|
||||
{
|
||||
var studentCapacity = new List<IntVar>();
|
||||
@@ -251,6 +274,25 @@ namespace Core.Calculation
|
||||
}
|
||||
}
|
||||
|
||||
private void EventHasInterestedStudent(CpModel model, BoolVar[,] x)
|
||||
{
|
||||
foreach (var e in _allEvents)
|
||||
{
|
||||
var evt = _events[e];
|
||||
var studentInterest = new List<IntVar>();
|
||||
|
||||
foreach (var s in _allStudents)
|
||||
{
|
||||
var student = _students[s];
|
||||
if (student.EventRankings.Find(er => er.EventDefinition == evt) != null)
|
||||
studentInterest.Add(x[e, s]);
|
||||
}
|
||||
model.AddLinearConstraint(
|
||||
LinearExpr.Sum(studentInterest), 1, 10);
|
||||
studentInterest.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private List<EventAssignmentThresholds> AddEventAssignmentThresholds(CpModel model, BoolVar[,] x)
|
||||
{
|
||||
var assignmentThresholdsList = new List<EventAssignmentThresholds>();
|
||||
@@ -280,6 +322,9 @@ namespace Core.Calculation
|
||||
)
|
||||
teamCount = 1;
|
||||
|
||||
if (_twoTeams.Contains(evt))
|
||||
teamCount = 2;
|
||||
|
||||
if (evt.Name == "Tech Bowl")
|
||||
teamCount = 1;
|
||||
|
||||
@@ -292,6 +337,9 @@ namespace Core.Calculation
|
||||
if (_droppedEvents != null && _droppedEvents.Contains(evt))
|
||||
teamCount = 0;
|
||||
|
||||
if (evt.EventFormat == EventFormat.Individual)
|
||||
evtMinTeamSize = 0;
|
||||
|
||||
var lb = evtMinTeamSize * teamCount;
|
||||
var ub = Math.Min(evtMaxTeamSize * teamCount, _parameters.TeamSizeLimit * teamCount);
|
||||
|
||||
@@ -347,22 +395,11 @@ namespace Core.Calculation
|
||||
return
|
||||
eventRank == null
|
||||
? 0L
|
||||
// TODO: MaxRank can be calculated
|
||||
: StudentEventRanking.MaxRank - eventRank.Rank; // inverse
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
private long[] GetEventEffortCoefficients(IEnumerable<EventDefinition> events)
|
||||
{
|
||||
return
|
||||
events.Select(
|
||||
e =>
|
||||
//e.Name == "Tech Bowl"
|
||||
// ? 0 // Tech bowl gets requires no effort?
|
||||
//:
|
||||
e.LevelOfEffort ?? 10L
|
||||
).ToArray();
|
||||
}
|
||||
|
||||
public class SolutionPrinter : CpSolverSolutionCallback
|
||||
{
|
||||
private readonly IList<EventDefinition> _events;
|
||||
|
||||
Reference in New Issue
Block a user