/*
* This file is part of the TimeFinder project.
* Visit http://www.timefinder.de for more information.
* Copyright 2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.timefinder.algo.roomassignment;
import de.timefinder.algo.graph.WeightedGraph;
import de.timefinder.algo.graph.Matching;
import javolution.util.FastMap;
import java.util.Map;
import java.util.Map.Entry;
/**
* This class implements the path growing algorithm described from
* Drake and Hougardy in their paper: "A Simple Approximation Algorithm
* for the Weighted Matching Problem"
*
* @author Peter Karich, peat_hal 'at' users 'dot' sourceforge 'dot' net
*/
public class PathGrowingAlgorithm implements AssignmentAlgorithm {
private float[][] costMatrix;
@Override
public int[][] computeAssignments(float[][] costMatrixArg) {
AssignmentArray matrix = new AssignmentArray();
matrix.init(costMatrixArg);
costMatrix = matrix.getCosts();
Matching[] matching = new Matching[2];
matching[0] = new Matching();
matching[1] = new Matching();
int i = 0;
final int NO_X = costMatrix[0].length; // COLUMNS
int NO_Y = costMatrix.length; // ROWS
float floatMax = AssignmentHelper.getFloatMax(Math.max(costMatrix.length, costMatrix[0].length));
matching[0].setFloatMax(floatMax);
matching[1].setFloatMax(floatMax);
WeightedGraph graph = new WeightedGraph(NO_X + NO_Y);
int ALL_NODES = NO_X + NO_Y;
for (int x = 0; x < NO_X; x++) {
int tmpY = 0;
for (int y = NO_X; y < ALL_NODES; y++, tmpY++) {
if (costMatrix[tmpY][x] < floatMax) {
graph.addEdge(x, y, costMatrix[tmpY][x]);
} else {
// we would not need this line, if we would have a better graph.getOneNodeWithAnEdge()
// see within the while(true) loop
graph.addEdge(x, y, floatMax);
}
}
}
// x is somewhat missleading, because the x and y nodes swap
// in every iteration (in the while(true) loop)
int x = graph.getOneNodeWithAnEdge();
int saveFirstNode = x;
Map<Integer, Float> saveNeighbors =
new FastMap<Integer, Float>(graph.getNeighbors(x));
int yForExtremalWeight = -1;
while (graph.getNoOfEdges() > 0) {
while (true) {
if (graph.getNeighbors(x).size() == 0) {
// EITHER return only x node if they are required (or y) via:
//x = graph.getOneNodeWithAnEdge();
//if (x < 0) { break; }
// OR assume a complete graph and simply break (no while(graph.getNoOfEdges() > 0) would be required)
break;
}
float minWeight = floatMax + 1;
yForExtremalWeight = -1;
for (Entry<Integer, Float> entry : graph.getNeighbors(x).entrySet()) {
if (entry.getValue() < minWeight) {
yForExtremalWeight = entry.getKey();
minWeight = entry.getValue();
}
}
assert yForExtremalWeight != -1;
// even add it if float is floatMax, because otherwise we will
// will have a wrong totalSum
matching[i].addEdge(x, yForExtremalWeight, minWeight);
i = 1 - i;
boolean ret = graph.removeNodeAndNeighbors(x);
assert ret;
x = yForExtremalWeight;
}
}
// determine the last egde for matching[1] and add it
float lastWeight = saveNeighbors.get(yForExtremalWeight);
// we don't need the two cases: saveFirstNode < NO_X and the other
// because it is the same for both!
matching[1].addEdge(yForExtremalWeight, saveFirstNode, lastWeight);
if (matching[0].getTotalSum() < matching[1].getTotalSum()) {
if (saveFirstNode < NO_X) {
// first is matching[0] with x, y => y %= NO_X
return matching[0].toBipartiteArrayModY(NO_X);
} else {
// first is matching[0] with y, x => x %= NO_X
return matching[0].toBipartiteArrayModX(NO_X);
}
} else {
if (saveFirstNode < NO_X) {
// first is matching[0] with x, y => matching[1] with y, x
return matching[1].toBipartiteArrayModX(NO_X);
} else {
// first is matching[0] with y, x => matching[1] with x, y
return matching[1].toBipartiteArrayModY(NO_X);
}
}
}
}