Rewrote the matching algorithm which didnt work.

This commit is contained in:
Tom Vahlman 2012-02-24 20:58:35 +01:00
parent b7816cb7da
commit 3661061ed0
4 changed files with 111 additions and 198 deletions
src
main/java/se/su/dsv/scipro/match
test/java/se/su/dsv/scipro/match

@ -22,143 +22,83 @@ public class GreedyMatchingAlgorithm implements MatchingAlgorithm {
private Weights weights;
@Override
public Result match(List<Availability> supervisorAvailability,
List<ProjectIdea> unmatchedProjectIdeas, Weights weights) {
List<Match> matchList = new ArrayList<Match>();
this.weights = weights;
public Result match(List<Availability> supervisorAvailability, List<ProjectIdea> unmatchedProjectIdeas, Weights weights) {
this.weights = weights;
List<Pair> pairList = new ArrayList<Pair>();
List<Match> matchList = new ArrayList<Match>();
List<String> availableProjectClasses = getAvailableProjectClass(unmatchedProjectIdeas, supervisorAvailability);
HashMap<String, List<ProjectIdea>> sortedProjectIdeas = getSortedProjectIdeas(availableProjectClasses, unmatchedProjectIdeas);
HashMap<String, List<Availability>> sortedAvailability = getSortedAvailability(availableProjectClasses, supervisorAvailability);
logger.info("SortedAvail.size: " + sortedAvailability.size());
logger.info("sortedProjIdeas.size: " + sortedProjectIdeas.size());
for (String projectClassCode : availableProjectClasses) {
logger.info("Checking class:" + projectClassCode);
matchList.addAll(matchProjectIdeas(sortedProjectIdeas.get(projectClassCode), sortedAvailability.get(projectClassCode)));
}
logger.info("Matchlist.size: " + matchList.size());
while(!unmatchedProjectIdeas.isEmpty()) {
pairList.clear();
matchProjectIdeas(unmatchedProjectIdeas, supervisorAvailability, pairList);
Collections.sort(pairList);
if(!pairList.isEmpty()) {
Pair foundPair = pairList.get(0);
for(Availability availability : supervisorAvailability) {
if(availability.getSupervisor().equals(foundPair.getMatch().getSupervisor())) {
if(availability.getNumCapable() > availability.getNumMatched()) {
availability.setNumMatched(availability.getNumMatched() + 1);
matchList.add(foundPair.getMatch());
}
}
}
// If there are unmatched bachelor theses and unmatched master supervisors match them
List<ProjectIdea> projectIdeaList = sortedProjectIdeas.get(ProjectClass.BACHELOR);
List<Availability> availabilityList = sortedAvailability.get(ProjectClass.MASTER);
if (projectIdeaList != null && availabilityList != null) {
if (!projectIdeaList.isEmpty()
&& !availabilityList.isEmpty()) {
matchList.addAll(matchProjectIdeas(projectIdeaList, availabilityList));
}
}
unmatchedProjectIdeas = getUnmatchedProjectIdeas(matchList, unmatchedProjectIdeas);
return new Result(matchList, unmatchedProjectIdeas);
}
/**
* Take all ProjectIdeas and remove those who have been matched
* @param matchList the list
* @param unmatchedProjectIdeas unmatched ideas
* @return List<ProjectIdea>
*/
private List<ProjectIdea> getUnmatchedProjectIdeas(List<Match> matchList,
List<ProjectIdea> unmatchedProjectIdeas) {
for (Match match : matchList) {
ProjectIdea matchedProjectIdea = match.getProjectIdea();
Iterator<ProjectIdea> projectIdeaIterator = unmatchedProjectIdeas.iterator();
while (projectIdeaIterator.hasNext()) {
ProjectIdea unmatchedProjectIdea = projectIdeaIterator.next();
if (matchedProjectIdea.equals(unmatchedProjectIdea)) {
projectIdeaIterator.remove();
}
}
}
return unmatchedProjectIdeas;
Iterator<ProjectIdea> projectIdeaIterator = unmatchedProjectIdeas.iterator();
while(projectIdeaIterator.hasNext()) {
ProjectIdea projectIdea = projectIdeaIterator.next();
if(projectIdea.equals(foundPair.getMatch().getProjectIdea())) {
projectIdeaIterator.remove();
}
}
} else {
break;
}
}
return new Result(matchList, unmatchedProjectIdeas);
}
/**
* Match project ideas with supervisors (based on availability) and return the best match.
* @param projectIdeaList project ideas that should be matched
* @param availabilityList supervisors that should be matched
* @return List<Match> the best matches are returned
*/
private List<Match> matchProjectIdeas(List<ProjectIdea> projectIdeaList, List<Availability> availabilityList) {
List<Match> matchList = new ArrayList<Match>();
Iterator<ProjectIdea> projectIdeaIterator = projectIdeaList.iterator();
while (projectIdeaIterator.hasNext()) {
ProjectIdea projectIdea = projectIdeaIterator.next();
logger.info("Matching project idea:" + projectIdea);
Pair matchPair = null;
Iterator<Availability> availabilityIterator = availabilityList.iterator();
while (availabilityIterator.hasNext()) {
Availability availability = availabilityIterator.next();
* @param pairList the set
*/
private void matchProjectIdeas(List<ProjectIdea> projectIdeaList, List<Availability> availabilityList, List<Pair> pairList) {
for (ProjectIdea projectIdea : projectIdeaList) {
logger.info("Matching project idea:" + projectIdea);
for(Availability availability : availabilityList) {
logger.info("Matching project idea with supervisor:" + availability.getSupervisor());
if (!availability.isAvailable()) {
availabilityIterator.remove();
logger.info("Not an available supervisor");
continue;
}
if(availability.getProjectClass().toString().equalsIgnoreCase(ProjectClass.BACHELOR) &&
projectIdea.getProjectClass().toString().equalsIgnoreCase(ProjectClass.MASTER)) {
logger.info("A bachelor supervisor cannot handle master ideas.");
continue;
}
if (!isPossibleSupervisor(projectIdea, availability.getSupervisor())) {
logger.info("Not possible supervisor");
continue;
}
if(matchPair == null){
Match match = new Match(); // create a new match
match.setSupervisor(availability.getSupervisor());
match.setProjectIdea(projectIdea);
matchPair = new Pair(match, new Availability(null, 1L, 1, new ProjectClass()));
}
matchPair = getBestMatch(matchPair, availability);
pairList.add(getPair(projectIdea, availability));
}
if (matchPair != null && matchPair.getMatch().getPoints() != -1) {
matchList.add(matchPair.getMatch());
projectIdeaIterator.remove();
}
}
return matchList;
}
/**
* Return the best match, the old one or the new one with supervisor
* @param oldMatchPair the match pair
* @param projectIdea projectIdea
* @param availability the availability
* @return Pair
*/
private Pair getBestMatch(final Pair oldMatchPair, Availability availability) {
Match oldMatch = oldMatchPair.getMatch();
Availability oldAvailability = oldMatchPair.getAvailability();
private Pair getPair(ProjectIdea projectIdea, Availability availability) {
Match match = new Match();
match.setProjectIdea(oldMatch.getProjectIdea());
match.setProjectIdea(projectIdea);
match.setSupervisor(availability.getSupervisor());
match = calculateScore(match);
if (match.getPoints() > oldMatch.getPoints()) {
availability.setNumMatched(availability.getNumMatched() + 1);
oldAvailability.setNumMatched(oldAvailability.getNumMatched() - 1);
return new Pair(match, availability);
} else if (match.getPoints() == oldMatch.getPoints()) {
if (availability.getAvailability() > oldAvailability.getAvailability()) {
availability.setNumMatched(availability.getNumMatched() + 1);
oldAvailability.setNumMatched(oldAvailability.getNumMatched() - 1);
return new Pair(match, availability);
} else {
return oldMatchPair;
}
} else {
return oldMatchPair;
}
return new Pair(match, availability);
}
/**
@ -192,9 +132,7 @@ public class GreedyMatchingAlgorithm implements MatchingAlgorithm {
*/
private boolean isNotRejectedBySupervisor(ProjectIdea projectIdea, Employee supervisor) {
for (Match oldMatch : projectIdea.getMatchHistory()) {
if (
//oldMatch.getSupervisor() != null && oldMatch.getSupervisor().equals(supervisor) && we are not interested in who is the supervisor, we are interested in who is rejectedBy
oldMatch.getRejectedBy() != null && oldMatch.getRejectedBy().equals(supervisor.getUser()) &&
if (oldMatch.getRejectedBy() != null && oldMatch.getRejectedBy().equals(supervisor.getUser()) &&
oldMatch.getStatus() != null && oldMatch.getStatus().equals(Match.Status.REJECTED)) {
return false;
}
@ -252,91 +190,13 @@ public class GreedyMatchingAlgorithm implements MatchingAlgorithm {
match.setPoints(points);
return match;
}
/**
*
* @param availableProjectClasses the list with available project classes
* @param supervisorAvailability the list with available supervisors
* @return Supervisors availability sorted on ProjectClasses.code
*/
private HashMap<String, List<Availability>> getSortedAvailability(
List<String> availableProjectClasses,
List<Availability> supervisorAvailability) {
HashMap<String, List<Availability>> sortedAvailability = new HashMap<String, List<Availability>>();
for (String projectClassCode : availableProjectClasses) {
sortedAvailability.put(projectClassCode, new ArrayList<Availability>());
}
for (Availability availability : supervisorAvailability) {
sortedAvailability.get(availability.getProjectClass().getCode()).add(availability);
}
//start Tom+Anton test
//List<Availability> bachelorAvailabilities = sortedAvailability.get(ProjectClass.BACHELOR) != null ?
// sortedAvailability.get(ProjectClass.BACHELOR) : new ArrayList<Availability>();
List<Availability> bachelorAvailabilities = sortedAvailability.get(ProjectClass.BACHELOR);
if (bachelorAvailabilities != null)
for (Availability av : supervisorAvailability) {
if(!bachelorAvailabilities.contains(av))
bachelorAvailabilities.add(av);
}
//sortedAvailability.put(ProjectClass.BACHELOR, bachelorAvailabilities);
//end T+A
return sortedAvailability;
}
/**
* @param availableProjectClasses the project classes that exists, regardless of there is a match or not,
* that is ok because the List with project ideas will be empty in case no project class match
* @param unmatchedProjectIdeaList the unmatched project ideas
* @return unmatched ProjectIdeas sorted on ProjectClasses.code
*/
private HashMap<String, List<ProjectIdea>> getSortedProjectIdeas(
List<String> availableProjectClasses,
List<ProjectIdea> unmatchedProjectIdeaList) {
HashMap<String, List<ProjectIdea>> sortedProjectIdeas = new HashMap<String, List<ProjectIdea>>();
for (String projectClassCode : availableProjectClasses) {
sortedProjectIdeas.put(projectClassCode, new ArrayList<ProjectIdea>());
}
for (ProjectIdea projectIdea : unmatchedProjectIdeaList) {
sortedProjectIdeas.get(projectIdea.getProjectClass().getCode()).add(projectIdea);
}
return sortedProjectIdeas;
}
/**
*
* @param unmatchedProjectIdeas a list of unmatched project ideas
* @param supervisorAvailability list of available supervisors
* @return all available ProjectClass codes
*/
private List<String> getAvailableProjectClass(
List<ProjectIdea> unmatchedProjectIdeas, List<Availability> supervisorAvailability) {
List<String> availableProjectClasses = new ArrayList<String>();
for (ProjectIdea projectIdea : unmatchedProjectIdeas) {
if (!availableProjectClasses.contains(projectIdea.getProjectClass().getCode())) {
availableProjectClasses.add(projectIdea.getProjectClass().getCode());
}
}
for (Availability availability : supervisorAvailability) {
if (!availableProjectClasses.contains(availability.getProjectClass().getCode())) {
availableProjectClasses.add(availability.getProjectClass().getCode());
}
}
return availableProjectClasses;
}
private class Pair {
private class Pair implements Comparable<Pair> {
private Match match;
private Availability availability;
public Pair(Match match, Availability availability) {
public Pair(Match match, Availability availability
) {
this.match = match;
this.availability = availability;
}
@ -348,5 +208,17 @@ public class GreedyMatchingAlgorithm implements MatchingAlgorithm {
public Availability getAvailability() {
return availability;
}
@Override
public int compareTo(Pair otherPair) {
if(match.getPoints() > otherPair.getMatch().getPoints()) {
return -1;
} else if(otherPair.getMatch().getPoints() > match.getPoints()) {
return 1;
} else {
return availability.compareTo(otherPair.getAvailability());
}
}
}
}

@ -8,7 +8,7 @@ import se.su.dsv.scipro.data.dataobjects.ProjectClass;
/**
* A class that specifies how available a supervisor is(in terms of thesis supervision)
*/
public class Availability implements Serializable{
public class Availability implements Serializable, Comparable<Availability> {
private static final long serialVersionUID = 1L;
@ -53,6 +53,17 @@ public class Availability implements Serializable{
public void setNumMatched(Long num) {
numMatched = num;
}
@Override
public int compareTo(Availability availability) {
if(getAvailability() > availability.getAvailability()) {
return -1;
} else if(availability.getAvailability() > getAvailability()) {
return 1;
} else {
return 0;
}
}
@Override
public String toString() {

@ -21,7 +21,7 @@ import se.su.dsv.scipro.data.dataobjects.User;
@Cacheable(true)
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Table(name="matchings")
public class Match extends DomainObject {
public class Match extends DomainObject implements Comparable<Match> {
public static enum Type {
PRE_APPROVED,
@ -169,6 +169,18 @@ public class Match extends DomainObject {
", dateCreated=" + (getDateCreated() != null ? getDateCreated() :"") + "]";
}
@Override
public int compareTo(Match otherMatch) {
if(points > otherMatch.getPoints()) {
return -1;
} else if(otherMatch.getPoints() > points) {
return 1;
} else {
return 0;
}
}
@Override
public int hashCode() {
final int prime = 31;

@ -500,6 +500,7 @@ public class TestGreedyMatchingAlgorithm {
Employee bachelorSupervisor2 = createSupervisor("David", "Hallberg", languages);
supervisorAvailability.add(new Availability(masterSupervisor, 0L, 1, bachelorProjectClass));
supervisorAvailability.add(new Availability(bachelorSupervisor2, 0L, 1, bachelorProjectClass));
addKeyWords(bachelorSupervisor2, bachelorProjectIdea, createKeyword(keywordTypeWord, "Design", false));
unmatchedProjectIdeas.add(bachelorProjectIdea);
Result result = new GreedyMatchingAlgorithm().match(supervisorAvailability, unmatchedProjectIdeas, weights);
assertTrue(result.matches.size() > 0);
@ -560,7 +561,6 @@ public class TestGreedyMatchingAlgorithm {
@Rollback
/* test that a master which has filled up his slot for bachelor but has slots left for master can supervise a bachelor idea */
public void testIncreaseSlotForMasterSupervisor_v2() {
/*
Employee bachelorSupervisor2 = createSupervisor("David", "Hallberg", languages);
supervisorAvailability.add(new Availability(masterSupervisor, 3L, 3, bachelorProjectClass));
supervisorAvailability.add(new Availability(masterSupervisor, 3L, 4, masterProjectClass));
@ -571,11 +571,29 @@ public class TestGreedyMatchingAlgorithm {
masterProjectIdea.setPreferredSupervisor(masterSupervisor);
addKeyWords(masterSupervisor, masterProjectIdea, createKeyword(keywordTypeWord, "UML", false));
Result result = new GreedyMatchingAlgorithm().match(supervisorAvailability, unmatchedProjectIdeas, weights);
assertTrue(result.matches.size() > 0);
assertTrue(result.matches.get(0).getProjectIdea().equals(masterProjectIdea));
assertTrue(result.matches.get(0).getSupervisor().equals(masterSupervisor));
assertTrue(result.matches.size() > 0);
boolean foundMasterProjIdea = false;
boolean foundMasterSuperVisor= false;
boolean foundBachelorProjIdea = false;
boolean foundBachelorSuperVisor= false;
for(Match match : result.matches) {
if(match.getProjectIdea().equals(masterProjectIdea) && match.getSupervisor().equals(masterSupervisor)) {
foundMasterProjIdea = true;
foundMasterSuperVisor = true;
}
if(match.getProjectIdea().equals(bachelorProjectIdea) && match.getSupervisor().equals(bachelorSupervisor2)) {
foundBachelorProjIdea = true;
foundBachelorSuperVisor = true;
}
}
assertTrue(foundMasterProjIdea);
assertTrue(foundBachelorProjIdea);
assertTrue(foundBachelorSuperVisor);
assertTrue(foundMasterSuperVisor);
assertTrue(result.unmatched.size() == 0);
*/
}
}