public Assignment Solve(SparseMatrixDouble cost) => throw new NotImplementedException("The pseudoflow solver can only be used with integer costs");
public Assignment Solve(SparseMatrixDouble cost) { var nr = cost.NumRows; var nc = cost.NumColumns; var v = new double[nc]; var x = Enumerable.Repeat(-1, nr).ToArray(); var y = Enumerable.Repeat(-1, nc).ToArray(); var u = new double[nr]; var d = new double[nc]; var ok = new bool[nc]; var xinv = new bool[nr]; var free = Enumerable.Repeat(-1, nr).ToArray(); var todo = Enumerable.Repeat(-1, nc).ToArray(); var lab = new int[nc]; var first = cost.IA; var kk = cost.CA; var cc = cost.A; int l0; // The initialization steps of LAPJVsp only make sense for square matrices if (nr == nc) { for (var jp = 0; jp < nc; jp++) { v[jp] = double.PositiveInfinity; } for (var i = 0; i < nr; i++) { for (var t = first[i]; t < first[i + 1]; t++) { var jp = kk[t]; if (cc[t] < v[jp]) { v[jp] = cc[t]; y[jp] = i; } } } for (var jp = nc - 1; jp >= 0; jp--) { var i = y[jp]; if (x[i] == -1) { x[i] = jp; } else { y[jp] = -1; // Here, the original Pascal code simply inverts the sign of x; as that // doesn't play too well with zero-indexing, we explicitly keep track of // uniqueness instead. xinv[i] = true; } } var lp = 0; for (var i = 0; i < nr; i++) { if (xinv[i]) { continue; } if (x[i] != -1) { var min = double.PositiveInfinity; var j1 = x[i]; for (var t = first[i]; t < first[i + 1]; t++) { var jp = kk[t]; if (jp != j1) { if (cc[t] - v[jp] < min) { min = cc[t] - v[jp]; } } } u[i] = min; var tp = first[i]; while (kk[tp] != j1) { tp++; } v[j1] = cc[tp] - min; } else { free[lp++] = i; } } for (var tel = 0; tel < 2; tel++) { var h = 0; var l0p = lp; lp = 0; while (h < l0p) { // Note: In the original Pascal code, the indices of the lowest // and second-lowest reduced costs are never reset. This can // cause issues for infeasible problems; see https://stackoverflow.com/q/62875232/5085211 var i = free[h++]; var j0p = -1; var j1p = -1; var v0 = double.PositiveInfinity; var vj = double.PositiveInfinity; for (var t = first[i]; t < first[i + 1]; t++) { var jp = kk[t]; var dj = cc[t] - v[jp]; if (dj < vj) { if (dj >= v0) { vj = dj; j1p = jp; } else { vj = v0; v0 = dj; j1p = j0p; j0p = jp; } } } // If the index of the column with the largest reduced cost has not been // set, no assignment is possible for this row. if (j0p < 0) { throw new InvalidOperationException("No feasible solution."); } var i0 = y[j0p]; u[i] = vj; if (v0 < vj) { v[j0p] += v0 - vj; } else if (i0 != -1) { j0p = j1p; i0 = y[j0p]; } x[i] = j0p; y[j0p] = i; if (i0 != -1) { if (v0 < vj) { free[--h] = i0; } else { free[lp++] = i0; } } } } l0 = lp; } else { l0 = nr; for (int i = 0; i < nr; i++) { free[i] = i; } } var td1 = -1; for (var l = 0; l < l0; l++) { td1 = SolveForOneL(l, nc, d, ok, free, first, kk, cc, v, lab, todo, y, x, td1); } return(new Assignment(x, y)); }
public SparseMatrixDouble(SparseMatrixDouble matrix) : this(new List <double>(matrix.A), new List <int>(matrix.IA), new List <int>(matrix.CA), matrix.NumColumns) { }
/// <summary> /// Solves a sparse instance of the linear assignment problem with floating point costs. /// </summary> /// <param name="cost">The weights of the edges of the bipartite graph representing the problem.</param> /// <param name="maximize">Whether or not to maximize total cost rather than minimize it.</param> /// <param name="solver">The solver to use. If not given, this defaults to <see cref="ShortestPathSolver"/>.</param> /// <param name="allowOverwrite">Allows the entries <paramref name="cost"/> to be changed; setting this to /// <see langword="true" /> can give performance improvements in certain cases.</param> /// <returns>An <see cref="Assignment"/> representing the solution.</returns> public static Assignment Solve(SparseMatrixDouble cost, bool maximize = false, ISolver solver = null, bool allowOverwrite = false) { var transpose = false; if (cost.NumRows > cost.NumColumns) { cost = cost.Transpose(); allowOverwrite = true; transpose = true; } var nr = cost.NumRows; var nc = cost.NumColumns; if (nr == 0 || nc == 0) { return(AssignmentWithDuals.Empty); } // We handle maximization by changing all signs in the given cost, then // minimizing the result. At the end of the day, we also make sure to // update the dual variables accordingly. if (maximize) { if (!allowOverwrite) { cost = new SparseMatrixDouble(cost); allowOverwrite = true; } for (var i = 0; i < cost.A.Count; i++) { cost.A[i] = -cost.A[i]; } } // Ensure that all values are positive var min = cost.A.Any() ? cost.A.Min() : double.PositiveInfinity; if (min < 0) { if (!allowOverwrite) { cost = new SparseMatrixDouble(cost); } for (var i = 0; i < cost.A.Count; i++) { cost.A[i] -= min; } } else { min = 0; } solver ??= new ShortestPathSolver(); var solution = solver.Solve(cost); if (solution is AssignmentWithDuals solutionWithDuals) { if (min != 0) { for (var ip = 0; ip < nr; ip++) { solutionWithDuals.DualU[ip] += min; } } if (maximize) { FlipDualSigns(solutionWithDuals.DualU, solutionWithDuals.DualV); } } if (transpose) { solution = solution.Transpose(); } return(solution); }