gonum.org/v1/gonum@v0.14.0/optimize/convex/lp/convert.go (about) 1 // Copyright ©2016 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package lp 6 7 import ( 8 "gonum.org/v1/gonum/floats" 9 "gonum.org/v1/gonum/mat" 10 ) 11 12 // TODO(btracey): Have some sort of preprocessing step for helping to fix A to make it 13 // full rank? 14 // TODO(btracey): Reduce rows? Get rid of all zeros, places where only one variable 15 // is there, etc. Could be implemented with a Reduce function. 16 // TODO(btracey): Provide method of artificial variables for help when problem 17 // is infeasible? 18 // TODO(btracey): Add an lp.Solve that solves an LP in non-standard form. 19 20 // Convert converts a General-form LP into a standard form LP. 21 // The general form of an LP is: 22 // 23 // minimize cᵀ * x 24 // s.t G * x <= h 25 // A * x = b 26 // 27 // And the standard form is: 28 // 29 // minimize cNewᵀ * x 30 // s.t aNew * x = bNew 31 // x >= 0 32 // 33 // If there are no constraints of the given type, the inputs may be nil. 34 func Convert(c []float64, g mat.Matrix, h []float64, a mat.Matrix, b []float64) (cNew []float64, aNew *mat.Dense, bNew []float64) { 35 nVar := len(c) 36 nIneq := len(h) 37 38 // Check input sizes. 39 if g == nil { 40 if nIneq != 0 { 41 panic(badShape) 42 } 43 } else { 44 gr, gc := g.Dims() 45 if gr != nIneq { 46 panic(badShape) 47 } 48 if gc != nVar { 49 panic(badShape) 50 } 51 } 52 53 nEq := len(b) 54 if a == nil { 55 if nEq != 0 { 56 panic(badShape) 57 } 58 } else { 59 ar, ac := a.Dims() 60 if ar != nEq { 61 panic(badShape) 62 } 63 if ac != nVar { 64 panic(badShape) 65 } 66 } 67 68 // Convert the general form LP. 69 // Derivation: 70 // 0. Start with general form 71 // min. cᵀ * x 72 // s.t. G * x <= h 73 // A * x = b 74 // 1. Introduce slack variables for each constraint 75 // min. cᵀ * x 76 // s.t. G * x + s = h 77 // A * x = b 78 // s >= 0 79 // 2. Add non-negativity constraints for x by splitting x 80 // into positive and negative components. 81 // x = xp - xn 82 // xp >= 0, xn >= 0 83 // This makes the LP 84 // min. cᵀ * xp - cᵀ xn 85 // s.t. G * xp - G * xn + s = h 86 // A * xp - A * xn = b 87 // xp >= 0, xn >= 0, s >= 0 88 // 3. Write the above in standard form: 89 // xt = [xp 90 // xn 91 // s ] 92 // min. [cᵀ, -cᵀ, 0] xt 93 // s.t. [G, -G, I] xt = h 94 // [A, -A, 0] xt = b 95 // x >= 0 96 97 // In summary: 98 // Original LP: 99 // min. cᵀ * x 100 // s.t. G * x <= h 101 // A * x = b 102 // Standard Form: 103 // xt = [xp; xn; s] 104 // min. [cᵀ, -cᵀ, 0] xt 105 // s.t. [G, -G, I] xt = h 106 // [A, -A, 0] xt = b 107 // x >= 0 108 109 // New size of x is [xp, xn, s] 110 nNewVar := nVar + nVar + nIneq 111 112 // Construct cNew = [c; -c; 0] 113 cNew = make([]float64, nNewVar) 114 copy(cNew, c) 115 copy(cNew[nVar:], c) 116 floats.Scale(-1, cNew[nVar:2*nVar]) 117 118 // New number of equality constraints is the number of total constraints. 119 nNewEq := nIneq + nEq 120 121 // Construct bNew = [h, b]. 122 bNew = make([]float64, nNewEq) 123 copy(bNew, h) 124 copy(bNew[nIneq:], b) 125 126 // Construct aNew = [G, -G, I; A, -A, 0]. 127 aNew = mat.NewDense(nNewEq, nNewVar, nil) 128 if nIneq != 0 { 129 aNew.Slice(0, nIneq, 0, nVar).(*mat.Dense).Copy(g) 130 aNew.Slice(0, nIneq, nVar, 2*nVar).(*mat.Dense).Scale(-1, g) 131 aView := aNew.Slice(0, nIneq, 2*nVar, 2*nVar+nIneq).(*mat.Dense) 132 for i := 0; i < nIneq; i++ { 133 aView.Set(i, i, 1) 134 } 135 } 136 if nEq != 0 { 137 aNew.Slice(nIneq, nIneq+nEq, 0, nVar).(*mat.Dense).Copy(a) 138 aNew.Slice(nIneq, nIneq+nEq, nVar, 2*nVar).(*mat.Dense).Scale(-1, a) 139 } 140 return cNew, aNew, bNew 141 }