// replaceBland uses the Bland rule to find the indices to swap if the minimum // move is 0. The indices to be swapped are replace and minIdx (following the // nomenclature in the main routine). func replaceBland(A mat64.Matrix, ab *mat64.Dense, xb []float64, basicIdxs, nonBasicIdx []int, r, move []float64) (replace, minIdx int, err error) { // Use the traditional bland rule, except don't replace a constraint which // causes the new ab to be singular. for i, v := range r { if v > -blandNegTol { continue } minIdx = i err = computeMove(move, minIdx, A, ab, xb, nonBasicIdx) if err != nil { // Either unbounded or something went wrong. return -1, -1, err } replace = floats.MinIdx(move) if math.Abs(move[replace]) > blandZeroTol { // Large enough that it shouldn't be a problem return replace, minIdx, nil } // Find a zero index where replacement is non-singular. biCopy := make([]int, len(basicIdxs)) for replace, v := range move { if v > blandZeroTol { continue } copy(biCopy, basicIdxs) biCopy[replace] = nonBasicIdx[minIdx] abTmp := extractColumns(A, biCopy) // If the condition number is reasonable, use this index. if mat64.Cond(abTmp, 1) < 1e16 { return replace, minIdx, nil } } } return -1, -1, ErrBland }
// linearlyDependent returns whether the vector is linearly dependent // with the columns of A. It assumes that A is a full-rank matrix. func linearlyDependent(vec *mat64.Vector, A mat64.Matrix) bool { // Add vec to the columns of A, and see if the condition number is reasonable. m, n := A.Dims() aNew := mat64.NewDense(m, n+1, nil) aNew.Copy(A) col := mat64.Col(nil, 0, vec) aNew.SetCol(n, col) cond := mat64.Cond(aNew, 1) return cond > 1e12 }