Search code examples
prologschedulingclpfd

Schedule planning strategy


I am going to attempt solving a scheduling problem with Prolog. I have a set of requests from people in the format

req_id, user_id, start_date, end_date, state

each of these people are in one or more groups

group_id, user_id

and each group are defined by

group_id, loose, strict

where the loose and strict are integers >= 0

The initial state of each request is unknown, and can be changed to either rejected or approved.

To explain loose and strict on the groups consider the following example

Group A - Loose 1 - Strict 1
Group B - Loose 1 - Strict 0
Group C - Loose 0 - Strict 1

Given these data at any given time we need 3 people at work as 1 person can solve both A and B loose (given he is in both of those groups) and 1 person is needed for A strict and 1 person for C strict.

The date span will be limited to 5-6 weeks - never open ended searches.

Problem 1: how to I represent the available personnel at any given time so no group rules can be broken

Problem 2: how do I "force" that prolog searches for the maximum possible accept actions instead of just rejecting all requests


Edit

Sorry for the late followup, I hurt my leg and haven't had the time to do this.

My current code is:

updateGroupStrict(
    [ group(Group,N,Loose) | GroupsRest ],
    [ usergroup(User,Group) | _ ],
    User,
    [ group(Group,M,Loose) | GroupsRest ]
) :- N > 0,
    M is N - 1.

% Reduce strict helper, try next group
updateGroupStrict( [ GroupHead | GroupsRest ], UserGroups, User, [ GroupHead | UpdatedGroups ] ) :- updateGroupStrict(GroupsRest,UserGroups,User,UpdatedGroups).

% Reduce strict helper, try next usergroup
updateGroupStrict(Groups,[ _ | UserGroupTail ],User,UpdatedGroups) :- updateGroupStrict(Groups,UserGroupTail,User,UpdatedGroups).

handle(Groups, _, userrequest( _, [ request(_, accept) | _ ]), Groups).

% to reject a request, the groups should be updated in the following way
% - A strict accept should only update 1 strict group
handle(
    Groups,
    UserGroups,
    userrequest( User, [ request(_, reject) | _ ]),
    UpdatedGroups
) :- updateGroupStrict(Groups, UserGroups, User, UpdatedGroups).

% Success when all requests have been processed - meaning the list is empty, and all groups are 0,0.
planned([],_,[]).

% If a group do not have more unsatisfied then just reduce it
planned( [ group(_,0,0) | GroupsTail ], UserGroups, Requests ) :- planned( GroupsTail, UserGroups, Requests ).

% To reduce the list of requests, handle the head, and continue on the rest
planned( Groups, UserGroups, [ RequestHead | RequestTail] ) :-
    handle( Groups, UserGroups, RequestHead, UpdatedGroups ),
    planned( UpdatedGroups, UserGroups, RequestTail ).

The assumptions made in this code is that there is only strict rules, all requests are same length (completely overlapping), and all users have made an request.

planned([group(group1,1,0)],[usergroup(user1,group1)],[userrequest(user1,[request(request1,RequestAction1)]),userrequest(user2,[request(request2,RequestAction2)])]).


Solution

  • So I finally managed to find a solution. Instead of all these intermediate representations I now have two

    1. a list of requests where start and end date is represented as an integer offset (request(User,FirstDay,LastDay,Mode))
    2. a list of groups where they represent each day with the users that can work that day in them (group(Day,Limit,[Users])

    This way I am able to remove the user from a group's users when a request is approved. And just leave him if rejected. And finally just check if there is enough users in a group accordingly to the limit.