/**
* This work is non-copyrightable
* @author Myriam Abramson
* myriam.abramson@nrl.navy.mil
*/
package iamrescue.util;
import java.text.DecimalFormat;
import java.util.Random;
/**
* Adapted from the website http://216.249.163.93/bob.pilgrim/445/munkres.html
* roles x agents find best role allocation the coefficient of the matrix are
* the role preferences of the agents
*/
public class Hungarian {
private static final int FORBIDDEN_VALUE = 9999;
private double[][] matrix;
private int[] rCov;
private int[] cCov;
private int[][] stars;
private int rows;
private int cols;
private int dim;
private int solutions;
private Random rand = new Random();
// columns = agents
// rows = roles
/**
* @param rows
* @param columns
*/
public Hungarian(int rows, int columns) {
this.rows = rows;
this.cols = columns;
dim = Math.max(rows, columns);
// solutions = Math.min(rows,columns);
solutions = dim;
matrix = new double[dim][dim];
stars = new int[dim][dim];
rCov = new int[dim];
cCov = new int[dim];
init(rows, columns);
}
/**
* converts x,y to one dimension
*/
/**
* @param x
* @param y
* @return
*/
public int two2one(int x, int y) {
return (x * dim) + y;
}
/**
* @param n
* @return
*/
/**
* @param n
* @return
*/
public int one2col(int n) {
return (n % dim);
}
/**
* @param n
* @return
*/
public int one2row(int n) {
return (n / dim);
}
// step 0 transform the matrix from maximization to minimization
/**
*
*/
public void max2min() {
double maxVal = Double.MIN_VALUE;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (matrix[i][j] > maxVal) {
maxVal = matrix[i][j];
}
}
}
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = maxVal - matrix[i][j];
}
}
// System.out.println ("after max2min");
// printIt();
}
// step1 find the minimum in each row and subtract it
/**
*
*/
public void rowMin() {
for (int i = 0; i < dim; i++) {
double minVal = matrix[i][0];
for (int j = 1; j < dim; j++) {
if (minVal > matrix[i][j]) {
minVal = matrix[i][j];
}
}
for (int j = 0; j < dim; j++) {
matrix[i][j] -= minVal;
}
}
// printIt();
// printStars();
}
/**
*
*/
public void colMin() {
for (int j = 0; j < dim; j++) {
double minVal = matrix[0][j];
for (int i = 1; i < dim; i++) {
if (minVal > matrix[i][j]) {
minVal = matrix[i][j];
}
}
for (int i = 0; i < dim; i++) {
matrix[i][j] -= minVal;
}
}
// printIt();
// printStars();
}
/**
*
*/
public void printStars() {
for (int i = 0; i < dim; i++) {
for (int j = 0; j < dim; j++) {
System.out.print(stars[i][j] + " ");
}
System.out.println(rCov[i]);
}
for (int j = 0; j < dim; j++) {
System.out.print(cCov[j] + " ");
}
System.out.println();
}
// step2 star the zeros
/**
*/
public void starZeros() {
for (int i = 0; i < dim; i++) {
for (int j = 0; j < dim; j++) {
if (matrix[i][j] == 0 && cCov[j] == 0 && rCov[i] == 0) {
stars[i][j] = 1;
cCov[j] = 1;
rCov[i] = 1;
}
}
}
clearCovers();
// printIt();
// printStars();
}
/**
* step 3 -- check for solutions
*/
/**
* @return
*/
public int coveredColumns() {
int k = 0;
for (int i = 0; i < dim; i++) {
for (int j = 0; j < dim; j++) {
if (stars[i][j] == 1) {
cCov[j] = 1;
}
}
}
for (int j = 0; j < dim; j++) {
k += cCov[j];
}
// printIt();
// printStars();
return k;
}
/**
* returns -1 if no uncovered zero is found a zero whose row or column is
* not covered
*/
/**
* @return
*/
public int findUncoveredZero() {
for (int i = 0; i < dim; i++) {
for (int j = 0; j < dim; j++) {
if (matrix[i][j] == 0 && rCov[i] == 0 && cCov[j] == 0) {
return two2one(i, j);
}
}
}
return -1;
}
/**
* returns -1 if not found returns the column if found
*/
/**
* @param zeroY
* @return
*/
public int foundStarInRow(int zeroY) {
for (int j = 0; j < dim; j++) {
if (stars[zeroY][j] == 1) {
return j;
}
}
return -1;
}
/**
* returns -1 if not found returns the row if found
*/
/**
* @param zeroX
* @return
*/
public int foundStarInCol(int zeroX) {
for (int i = 0; i < dim; i++) {
if (stars[i][zeroX] == 1) {
return i;
}
}
return -1;
}
/**
* step 4 Cover all the uncovered zeros one by one until no more cover the
* row and uncover the column
*/
/**
* @return
*/
public boolean coverZeros() {
int zero = findUncoveredZero();
while (zero >= 0) {
int zeroCol = one2col(zero);
int zeroRow = one2row(zero);
stars[zeroRow][zeroCol] = 2; // prime it
int starCol = foundStarInRow(zeroRow);
if (starCol >= 0) {
rCov[zeroRow] = 1;
cCov[starCol] = 0;
}
else {
// printStars();
starZeroInRow(zero); // step 5
return false;
}
zero = findUncoveredZero();
}
// printIt();
// printStars();
return true;
}
/**
* @param col
* @return
*/
public int findStarInCol(int col) {
if (col < 0) {
System.err.println("Invalid column index " + col);
}
for (int i = 0; i < dim; i++) {
if (stars[i][col] == 1) {
return i;
}
}
return -1;
}
/**
*
*/
public void clearCovers() {
for (int i = 0; i < dim; i++) {
rCov[i] = 0;
cCov[i] = 0;
}
}
/**
* unstar stars star primes
*/
/**
*/
public void erasePrimes() {
for (int i = 0; i < dim; i++) {
for (int j = 0; j < dim; j++) {
if (stars[i][j] == 2) {
// stars[i][j] = 1;
stars[i][j] = 0;
}
}
}
}
/**
* @param path
* @param kount
*/
public void convertPath(int[][] path, int kount) {
// printStars();
for (int i = 0; i <= kount; i++) {
int x = path[i][0];
int y = path[i][1];
if (stars[x][y] == 1) {
stars[x][y] = 0;
}
else if (stars[x][y] == 2) {
stars[x][y] = 1;
}
}
// printStars();
}
/**
* returns the column where a prime was found for a given row
*/
/**
* @param row
* @return
*/
public int findPrimeInRow(int row) {
for (int j = 0; j < dim; j++) {
if (stars[row][j] == 2) {
return j;
}
}
System.err.println("No prime in row " + row + " found");
forcePrint();
return -1;
}
/**
* step 5 augmenting path algorithm go back to step 3
*/
/**
* @param zero
*/
public void starZeroInRow(int zero) {
boolean done = false;
int zeroRow = one2row(zero); // row
int zeroCol = one2col(zero); // column
int kount = 0;
int[][] path = new int[100][2]; // how to dimension that?
path[kount][0] = zeroRow;
path[kount][1] = zeroCol;
while (!done) {
int r = findStarInCol(path[kount][1]);
if (r >= 0) {
kount++;
path[kount][0] = r;
path[kount][1] = path[kount - 1][1];
}
else {
done = true;
break;
}
int c = findPrimeInRow(path[kount][0]);
kount++;
path[kount][0] = path[kount - 1][0];
path[kount][1] = c;
}
convertPath(path, kount);
clearCovers();
erasePrimes();
// printIt();
// printStars();
// go to step 3
}
/**
*
*/
public void solve() {
// System.out.println ("in solve");
// forcePrint();
// printIt();
max2min();
rowMin(); // step 1
colMin();
starZeros(); // step 2
boolean done = false;
while (!done) {
int covCols = coveredColumns(); // step 3
// if (covCols == dim) {
if (covCols >= solutions) {
// printStarZeros();
break;
}
done = coverZeros(); // step 4 (calls step 5)
while (done) {
double smallest = findSmallestUncoveredVal();
uncoverSmallest(smallest); // step 6
done = coverZeros();
}
// System.out.println ("Continue(y/n)?");
// System.out.flush();
// char response = human.readChar();
// if (response == 'n')
// break;
}
}
/**
* @param row
* @param col
* @return
*/
boolean freeRow(int row, int col) {
for (int i = 0; i < dim; i++) {
if (i != row && stars[i][col] == 1) {
return false;
}
}
return true;
}
/**
* @param row
* @param col
* @return
*/
boolean freeCol(int row, int col) {
for (int j = 0; j < dim; j++) {
if (j != col && stars[row][j] == 1) {
return false;
}
}
return true;
}
// read from left to right:
// Role i is assigned to agent j
/**
*
*/
public void printStarZeros() {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
// check for independence
if (stars[i][j] == 1 && (freeRow(i, j) || freeCol(i, j))) {
System.out.println(i + " assigned to " + j
+ " is a solution");
}
}
}
}
// get the assignments for the agents
// the matrix is roles x agents
/**
* @return
*/
public int[] getSolutions() {
int[] solutions = new int[cols];
for (int j = 0; j < cols; j++) {
solutions[j] = -1;
for (int i = 0; i < rows; i++) {
// test for independence
// should not be necessary
if (stars[i][j] == 1 && (freeRow(i, j) || freeCol(i, j))) {
solutions[j] = i;
}
}
}
return solutions;
}
/**
* @return
*/
public double findSmallestUncoveredVal() {
double minVal = Double.MAX_VALUE;
for (int i = 0; i < dim; i++) {
for (int j = 0; j < dim; j++) {
if (rCov[i] == 0 && cCov[j] == 0) {
if (minVal > matrix[i][j]) {
minVal = matrix[i][j];
}
}
}
}
return minVal;
}
/**
* step 6 modify the matrix if the row is covered, add the smallest value if
* the column is not covered, subtract the smallest value
*/
/**
* @param smallest
*/
public void uncoverSmallest(double smallest) {
for (int i = 0; i < dim; i++) {
for (int j = 0; j < dim; j++) {
if (rCov[i] == 1) {
matrix[i][j] += smallest;
}
if (cCov[j] == 0) {
matrix[i][j] -= smallest;
}
}
// printIt();
// printStars();
}
}
/**
* @param rows
* @param cols
*/
public void init(int rows, int cols) {
for (int i = 0; i < dim; i++) {
for (int j = 0; j < dim; j++) {
if (i < rows && j < cols) {
matrix[i][j] = rand.nextDouble();
}
else {
matrix[i][j] = FORBIDDEN_VALUE;
}
}
// matrix[0][0] = 0.0;
// matrix[0][1] = 0.5;
// matrix[0][2] = 0.0;
// matrix[0][3] = 0.0;
// // matrix[0][4] = 0.17;
// // matrix[0][5] = 0.25;
// // matrix[0][6] = 0.12;
// // matrix[0][7] = 0.25;
// matrix[1][0] = 0.5;
// matrix[1][1] = 0.0;
// matrix[1][2] = 0.0;
// matrix[1][3] = 0.0;
// // matrix[1][4] = 0.17;
// // matrix[1][5] = 0.25;
// // matrix[1][6] = 0.10;
// // matrix[1][7] = 0.25;
// matrix[2][0] = 0.0;
// matrix[2][1] = 0.0;
// matrix[2][2] = 0.2;
// matrix[2][3] = 0.3;
// // matrix[2][4] = 0.12;
// // matrix[2][5] = 0.17;
// // matrix[2][6] = 0.10;
// // matrix[2][7] = 0.25;
}
}
/**
*
*/
public void forcePrint() {
DecimalFormat form = new DecimalFormat("0.00");
for (int i = 0; i < dim; i++) {
for (int j = 0; j < dim; j++) {
System.out.print(form.format(matrix[i][j]) + " ");
}
System.out.println();
}
}
/**
*
*/
public void printIt() {
DecimalFormat form = new DecimalFormat("0.00");
for (int i = 0; i < dim; i++) {
for (int j = 0; j < dim; j++) {
System.out.print(form.format(matrix[i][j]) + " ");
}
System.out.println();
}
}
/**
* @param index
* @param preferences
*/
public void addColumn(int index, double[] preferences) {
for (int i = 0; i < preferences.length; i++) {
matrix[i][index] = preferences[i];
}
}
/**
* @param index
* @param preferences
*/
public void addRow(int index, double[] preferences) {
for (int i = 0; i < preferences.length; i++) {
matrix[index][i] = preferences[i];
}
}
}