github.com/gopherd/gonum@v0.0.4/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 "github.com/gopherd/gonum/floats" 9 "github.com/gopherd/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 // minimize cᵀ * x 23 // s.t G * x <= h 24 // A * x = b 25 // And the standard form is: 26 // minimize cNewᵀ * x 27 // s.t aNew * x = bNew 28 // x >= 0 29 // If there are no constraints of the given type, the inputs may be nil. 30 func Convert(c []float64, g mat.Matrix, h []float64, a mat.Matrix, b []float64) (cNew []float64, aNew *mat.Dense, bNew []float64) { 31 nVar := len(c) 32 nIneq := len(h) 33 34 // Check input sizes. 35 if g == nil { 36 if nIneq != 0 { 37 panic(badShape) 38 } 39 } else { 40 gr, gc := g.Dims() 41 if gr != nIneq { 42 panic(badShape) 43 } 44 if gc != nVar { 45 panic(badShape) 46 } 47 } 48 49 nEq := len(b) 50 if a == nil { 51 if nEq != 0 { 52 panic(badShape) 53 } 54 } else { 55 ar, ac := a.Dims() 56 if ar != nEq { 57 panic(badShape) 58 } 59 if ac != nVar { 60 panic(badShape) 61 } 62 } 63 64 // Convert the general form LP. 65 // Derivation: 66 // 0. Start with general form 67 // min. cᵀ * x 68 // s.t. G * x <= h 69 // A * x = b 70 // 1. Introduce slack variables for each constraint 71 // min. cᵀ * x 72 // s.t. G * x + s = h 73 // A * x = b 74 // s >= 0 75 // 2. Add non-negativity constraints for x by splitting x 76 // into positive and negative components. 77 // x = xp - xn 78 // xp >= 0, xn >= 0 79 // This makes the LP 80 // min. cᵀ * xp - cᵀ xn 81 // s.t. G * xp - G * xn + s = h 82 // A * xp - A * xn = b 83 // xp >= 0, xn >= 0, s >= 0 84 // 3. Write the above in standard form: 85 // xt = [xp 86 // xn 87 // s ] 88 // min. [cᵀ, -cᵀ, 0] xt 89 // s.t. [G, -G, I] xt = h 90 // [A, -A, 0] xt = b 91 // x >= 0 92 93 // In summary: 94 // Original LP: 95 // min. cᵀ * x 96 // s.t. G * x <= h 97 // A * x = b 98 // Standard Form: 99 // xt = [xp; xn; s] 100 // min. [cᵀ, -cᵀ, 0] xt 101 // s.t. [G, -G, I] xt = h 102 // [A, -A, 0] xt = b 103 // x >= 0 104 105 // New size of x is [xp, xn, s] 106 nNewVar := nVar + nVar + nIneq 107 108 // Construct cNew = [c; -c; 0] 109 cNew = make([]float64, nNewVar) 110 copy(cNew, c) 111 copy(cNew[nVar:], c) 112 floats.Scale(-1, cNew[nVar:2*nVar]) 113 114 // New number of equality constraints is the number of total constraints. 115 nNewEq := nIneq + nEq 116 117 // Construct bNew = [h, b]. 118 bNew = make([]float64, nNewEq) 119 copy(bNew, h) 120 copy(bNew[nIneq:], b) 121 122 // Construct aNew = [G, -G, I; A, -A, 0]. 123 aNew = mat.NewDense(nNewEq, nNewVar, nil) 124 if nIneq != 0 { 125 aNew.Slice(0, nIneq, 0, nVar).(*mat.Dense).Copy(g) 126 aNew.Slice(0, nIneq, nVar, 2*nVar).(*mat.Dense).Scale(-1, g) 127 aView := aNew.Slice(0, nIneq, 2*nVar, 2*nVar+nIneq).(*mat.Dense) 128 for i := 0; i < nIneq; i++ { 129 aView.Set(i, i, 1) 130 } 131 } 132 if nEq != 0 { 133 aNew.Slice(nIneq, nIneq+nEq, 0, nVar).(*mat.Dense).Copy(a) 134 aNew.Slice(nIneq, nIneq+nEq, nVar, 2*nVar).(*mat.Dense).Scale(-1, a) 135 } 136 return cNew, aNew, bNew 137 }