public Assignment Solve(SparseMatrixDouble cost) =>
 throw new NotImplementedException("The pseudoflow solver can only be used with integer costs");
示例#2
0
        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)
 {
 }
示例#4
0
        /// <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);
        }