func TestDaxpy(t *testing.T) { fmt.Printf("* L1 * test axpy: Y = alpha * X + Y\n") X := matrix.FloatVector([]float64{1, 1, 1}) Y := matrix.FloatVector([]float64{0, 0, 0}) fmt.Printf("before:\nX=\n%v\nY=\n%v\n", X, Y) Axpy(X, Y, matrix.FScalar(5.0)) fmt.Printf("after:\nX=\n%v\nY=\n%v\n", X, Y) }
func TestAcent(t *testing.T) { // matrix string in row order presentation Adata := [][]float64{ []float64{-7.44e-01, 1.11e-01, 1.29e+00, 2.62e+00, -1.82e+00}, []float64{4.59e-01, 7.06e-01, 3.16e-01, -1.06e-01, 7.80e-01}, []float64{-2.95e-02, -2.22e-01, -2.07e-01, -9.11e-01, -3.92e-01}, []float64{-7.75e-01, 1.03e-01, -1.22e+00, -5.74e-01, -3.32e-01}, []float64{-1.80e+00, 1.24e+00, -2.61e+00, -9.31e-01, -6.38e-01}} bdata := []float64{ 8.38e-01, 9.92e-01, 9.56e-01, 6.14e-01, 6.56e-01, 3.57e-01, 6.36e-01, 5.08e-01, 8.81e-03, 7.08e-02} // these are solution obtained from running cvxopt acent.py with above data solData := []float64{-11.59728373909344512, -1.35196389161339936, 7.21894899350256303, -3.29159917142051528, 4.90454147385329176} ntData := []float64{ 1.5163484265903457, 1.2433928210771914, 1.0562922103520955, 0.8816246051011607, 0.7271128861543598, 0.42725003346248974, 0.0816777301914883, 0.0005458037072843131, 1.6259980735305693e-10} b := matrix.FloatVector(bdata) Al := matrix.FloatMatrixFromTable(Adata, matrix.RowOrder) Au := matrix.Scale(Al, -1.0) A := matrix.FloatZeros(2*Al.Rows(), Al.Cols()) A.SetSubMatrix(0, 0, Al) A.SetSubMatrix(Al.Rows(), 0, Au) x, nt, err := acent(A, b, 10) if err != nil { t.Logf("Acent error: %s", err) t.Fail() } solref := matrix.FloatVector(solData) ntref := matrix.FloatVector(ntData) soldf := matrix.Minus(x, solref) ntdf := matrix.Minus(matrix.FloatVector(nt), ntref) solNrm := blas.Nrm2Float(soldf) ntNrm := blas.Nrm2Float(ntdf) t.Logf("x [diff=%.2e]:\n%v\n", solNrm, x) t.Logf("nt [diff=%.2e]:\n%v\n", ntNrm, nt) if solNrm > TOL { t.Log("solution deviates too much from expected\n") t.Fail() } }
// Dscal: X = alpha * X func TestDscal(t *testing.T) { fmt.Printf("* L1 * test scal: X = alpha * X\n") alpha := matrix.FScalar(2.0) A := matrix.FloatVector([]float64{1.0, 1.0, 1.0, 1.0, 1.0, 1.0}) Scal(A, alpha) fmt.Printf("Dscal 2.0 * A\n") fmt.Printf("%s\n", A) A = matrix.FloatVector([]float64{1.0, 1.0, 1.0, 1.0, 1.0, 1.0}) Scal(A, alpha, &linalg.IOpt{"offset", 3}) fmt.Printf("Dscal 2.0 * A[3:]\n") fmt.Printf("%s\n", A) A = matrix.FloatVector([]float64{1.0, 1.0, 1.0, 1.0, 1.0, 1.0}) fmt.Printf("Dscal 2.0* A[::2]\n") Scal(A, alpha, &linalg.IOpt{"inc", 2}) fmt.Printf("%s\n", A) }
func _TestRankSmall(t *testing.T) { bM := 5 bN := 5 //bP := 5 Adata := [][]float64{ []float64{1.0, 1.0, 1.0, 1.0, 1.0}, []float64{2.0, 2.0, 2.0, 2.0, 2.0}, []float64{3.0, 3.0, 3.0, 3.0, 3.0}, []float64{4.0, 4.0, 4.0, 4.0, 4.0}, []float64{5.0, 5.0, 5.0, 5.0, 5.0}} A := matrix.FloatMatrixFromTable(Adata, matrix.RowOrder) A0 := matrix.FloatMatrixFromTable(Adata, matrix.RowOrder) X := matrix.FloatVector([]float64{1.0, 2.0, 3.0, 4.0, 5.0}) Y := matrix.FloatWithValue(bN, 1, 2.0) Ar := A.FloatArray() Xr := X.FloatArray() Yr := Y.FloatArray() blas.GerFloat(X, Y, A0, 1.0) DRankMV(Ar, Xr, Yr, 1.0, A.LeadingIndex(), 1, 1, 0, bN, 0, bM, 4, 4) ok := A0.AllClose(A) t.Logf("A0 == A1: %v\n", ok) if !ok { t.Logf("blas ger:\n%v\n", A0) t.Logf("A1: \n%v\n", A) } }
func (gp *gpConvexProg) F1(x *matrix.FloatMatrix) (f, Df *matrix.FloatMatrix, err error) { f = nil Df = nil err = nil f = matrix.FloatZeros(gp.mnl+1, 1) Df = matrix.FloatZeros(gp.mnl+1, gp.n) y := gp.g.Copy() blas.GemvFloat(gp.F, x, y, 1.0, 1.0) for i, s := range gp.ind { start := s[0] stop := s[1] // yi := exp(yi) = exp(Fi*x+gi) ymax := maxvec(y.FloatArray()[start:stop]) // ynew = exp(y[start:stop] - ymax) ynew := matrix.Exp(matrix.FloatVector(y.FloatArray()[start:stop]).Add(-ymax)) y.SetIndexesFromArray(ynew.FloatArray(), matrix.Indexes(start, stop)...) // fi = log sum yi = log sum exp(Fi*x+gi) ysum := blas.AsumFloat(y, &la.IOpt{"n", stop - start}, &la.IOpt{"offset", start}) f.SetIndex(i, ymax+math.Log(ysum)) blas.ScalFloat(y, 1.0/ysum, &la.IOpt{"n", stop - start}, &la.IOpt{"offset", start}) blas.GemvFloat(gp.F, y, Df, 1.0, 0.0, la.OptTrans, &la.IOpt{"m", stop - start}, &la.IOpt{"incy", gp.mnl + 1}, &la.IOpt{"offseta", start}, &la.IOpt{"offsetx", start}, &la.IOpt{"offsety", i}) } return }
// v = X.T * Y func TestDdot(t *testing.T) { fmt.Printf("* L1 * test dot: X.T*Y\n") A := matrix.FloatVector([]float64{1.0, 1.0, 1.0, 1.0, 1.0, 1.0}) B := matrix.FloatVector([]float64{2.0, 2.0, 2.0, 2.0, 2.0, 2.0}) v1 := Dot(A, B) v2 := Dot(A, B, &linalg.IOpt{"offset", 3}) v3 := Dot(A, B, &linalg.IOpt{"inc", 2}) fmt.Printf("Ddot: X.T * Y\n") fmt.Printf("%.3f\n", v1.Float()) fmt.Printf("%.3f\n", v2.Float()) fmt.Printf("%.3f\n", v3.Float()) // Output: // 12.000 // 6.000 // 6.000 }
// Here problem is already translated to epigraph format except original convex problem. // We wrap it and create special CP epigraph kktsolver. func cp_problem(F ConvexProg, c MatrixVariable, G MatrixVarG, h *matrix.FloatMatrix, A MatrixVarA, b MatrixVariable, dims *sets.DimensionSet, kktsolver KKTCpSolver, solopts *SolverOptions, x0 *matrix.FloatMatrix, mnl int) (sol *Solution, err error) { err = nil F_e := &cpProg{F} //mx0 := newEpigraph(x0, 0.0) cdim := dims.Sum("l", "q") + dims.SumSquared("s") ux := x0.Copy() uz := matrix.FloatZeros(mnl+cdim, 1) kktsolver_e := func(W *sets.FloatMatrixSet, xa MatrixVariable, znl *matrix.FloatMatrix) (KKTFuncVar, error) { x, x_ok := xa.(*epigraph) _ = x_ok We := W.Copy() // dnl is matrix dnl := W.At("dnl")[0] dnli := W.At("dnli")[0] We.Set("dnl", matrix.FloatVector(dnl.FloatArray()[1:])) We.Set("dnli", matrix.FloatVector(dnli.FloatArray()[1:])) g, err := kktsolver(We, x.m(), znl) _, Df, _ := F.F1(x.m()) gradf0 := Df.GetRow(0, nil).Transpose() solve := func(xa, ya MatrixVariable, z *matrix.FloatMatrix) (err error) { x, x_ok := xa.(*epigraph) _ = x_ok // TODO: remove or use x_ok y := ya.Matrix() err = nil a := z.GetIndex(0) blas.Copy(x.m(), ux) blas.AxpyFloat(gradf0, ux, x.t()) blas.Copy(z, uz, &la.IOpt{"offsetx", 1}) err = g(ux, y, uz) z.SetIndex(0, -x.t()*dnl.GetIndex(0)) blas.Copy(uz, z, &la.IOpt{"offsety", 1}) blas.Copy(ux, x.m()) val := blas.DotFloat(gradf0, x.m()) + dnl.GetIndex(0)*dnl.GetIndex(0)*x.t() - a x.set(val) return } return solve, err } return cpl_solver(F_e, c, G, h, A, b, dims, kktsolver_e, solopts, nil, mnl) }
func TestDgemv(t *testing.T) { fmt.Printf("* L2 * test gemv: Y = alpha * A * X + beta * Y\n") A := matrix.FloatNew(3, 2, []float64{1, 1, 1, 2, 2, 2}) X := matrix.FloatVector([]float64{1, 1}) Y := matrix.FloatVector([]float64{0, 0, 0}) alpha := matrix.FScalar(1.0) beta := matrix.FScalar(0.0) fmt.Printf("before: alpha=1.0, beta=0.0\nA=\n%v\nX=\n%v\nY=\n%v\n", A, X, Y) err := Gemv(A, X, Y, alpha, beta) fmt.Printf("after:\nA=\n%v\nX=\n%v\nY=\n%v\n", A, X, Y) fmt.Printf("* L2 * test gemv: X = alpha * A.T * Y + beta * X\n") err = Gemv(A, Y, X, alpha, beta, linalg.OptTrans) if err != nil { fmt.Printf("error: %s\n", err) } fmt.Printf("after:\nA=\n%v\nX=\n%v\nY=\n%v\n", A, X, Y) }
func sinv(x, y *matrix.FloatMatrix, dims *sets.DimensionSet, mnl int) (err error) { /*DEBUGGED*/ err = nil // For the nonlinear and 'l' blocks: // // yk o\ xk = yk .\ xk. ind := mnl + dims.At("l")[0] blas.Tbsv(y, x, &la_.IOpt{"n", ind}, &la_.IOpt{"k", 0}, &la_.IOpt{"ldA", 1}) // For the 'q' blocks: // // [ l0 -l1' ] // yk o\ xk = 1/a^2 * [ ] * xk // [ -l1 (a*I + l1*l1')/l0 ] // // where yk = (l0, l1) and a = l0^2 - l1'*l1. for _, m := range dims.At("q") { aa := blas.Nrm2Float(y, &la_.IOpt{"n", m - 1}, &la_.IOpt{"offset", ind + 1}) ee := y.GetIndex(ind) aa = (ee + aa) * (ee - aa) cc := x.GetIndex(ind) dd := blas.DotFloat(x, y, &la_.IOpt{"n", m - 1}, &la_.IOpt{"offsetx", ind + 1}, &la_.IOpt{"offsety", ind + 1}) x.SetIndex(ind, cc*ee-dd) blas.ScalFloat(x, aa/ee, &la_.IOpt{"n", m - 1}, &la_.IOpt{"offset", ind + 1}) blas.AxpyFloat(y, x, dd/ee-cc, &la_.IOpt{"n", m - 1}, &la_.IOpt{"offsetx", ind + 1}, &la_.IOpt{"offsety", ind + 1}) blas.ScalFloat(x, 1.0/aa, &la_.IOpt{"n", m}, &la_.IOpt{"offset", ind}) ind += m } // For the 's' blocks: // // yk o\ xk = xk ./ gamma // // where gammaij = .5 * (yk_i + yk_j). ind2 := ind for _, m := range dims.At("s") { for j := 0; j < m; j++ { u := matrix.FloatVector(y.FloatArray()[ind2+j : ind2+m]) u.Add(y.GetIndex(ind2 + j)) u.Scale(0.5) blas.Tbsv(u, x, &la_.IOpt{"n", m - j}, &la_.IOpt{"k", 0}, &la_.IOpt{"lda", 1}, &la_.IOpt{"offsetx", ind + j*(m+1)}) } ind += m * m ind2 += m } return }
// The analytic centering with cone constraints example of section 9.1 // (Problems with nonlinear objectives). func TestCp(t *testing.T) { xref := []float64{0.41132359189354400, 0.55884774432611484, -0.72007090016957931} F := &acenterProg{3, 1} gdata := [][]float64{ []float64{0., -1., 0., 0., -21., -11., 0., -11., 10., 8., 0., 8., 5.}, []float64{0., 0., -1., 0., 0., 10., 16., 10., -10., -10., 16., -10., 3.}, []float64{0., 0., 0., -1., -5., 2., -17., 2., -6., 8., -17., -7., 6.}} G := matrix.FloatMatrixFromTable(gdata) h := matrix.FloatVector( []float64{1.0, 0.0, 0.0, 0.0, 20., 10., 40., 10., 80., 10., 40., 10., 15.}) var solopts SolverOptions solopts.MaxIter = 40 solopts.ShowProgress = false dims := sets.NewDimensionSet("l", "q", "s") dims.Set("l", []int{0}) dims.Set("q", []int{4}) dims.Set("s", []int{3}) sol, err := Cp(F, G, h, nil, nil, dims, &solopts) if err == nil && sol.Status == Optimal { x := sol.Result.At("x")[0] t.Logf("x = \n%v\n", x.ToString("%.9f")) xe, _ := nrmError(matrix.FloatVector(xref), x) if xe > TOL { t.Logf("x differs [%.3e] from exepted too much.", xe) t.Fail() } } else { t.Logf("result: %v\n", err) t.Fail() } }
// a = norm2(A) func TestDnrm2(t *testing.T) { fmt.Printf("* L1 * test sum: nrm2(X)\n") A := matrix.FloatVector([]float64{1.0, 1.0, 1.0, 1.0, 1.0, 1.0}) v1 := Nrm2(A, &linalg.IOpt{"offset", 0}) v2 := Nrm2(A, &linalg.IOpt{"offset", 3}) v3 := Nrm2(A, &linalg.IOpt{"inc", 2}) fmt.Printf("Ddnrm2\n") fmt.Printf("%.3f\n", v1.Float()) fmt.Printf("%.3f\n", v2.Float()) fmt.Printf("%.3f\n", v3.Float()) // Output: // 2.499 // 1.732 // 1.732 }
// a = sum(X) func TestDasum(t *testing.T) { fmt.Printf("* L1 * test sum: sum(X)\n") A := matrix.FloatVector([]float64{1.0, 1.0, 1.0, 1.0, 1.0, 1.0}) v1 := Asum(A, &linalg.IOpt{"offset", 0}) v2 := Asum(A, &linalg.IOpt{"offset", 3}) v3 := Asum(A, &linalg.IOpt{"inc", 2}) fmt.Printf("Dasum\n") fmt.Printf("%.3f\n", v1.Float()) fmt.Printf("%.3f\n", v2.Float()) fmt.Printf("%.3f\n", v3.Float()) // Output: // 6.000 // 3.000 // 3.000 }
// The small GP of section 9.3 (Geometric programming). func TestGp(t *testing.T) { xref := []float64{1.06032641296944741, 1.75347359157296845, 2.44603683900611868} aflr := 1000.0 awall := 100.0 alpha := 0.5 beta := 2.0 gamma := 0.5 delta := 2.0 fdata := [][]float64{ []float64{-1.0, 1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 0.0}, []float64{-1.0, 1.0, 0.0, 1.0, 1.0, -1.0, 1.0, -1.0}, []float64{-1.0, 0.0, 1.0, 1.0, 0.0, 0.0, -1.0, 1.0}} gdata := []float64{1.0, 2.0 / awall, 2.0 / awall, 1.0 / aflr, alpha, 1.0 / beta, gamma, 1.0 / delta} g := matrix.FloatNew(8, 1, gdata).Log() F := matrix.FloatMatrixFromTable(fdata) K := []int{1, 2, 1, 1, 1, 1, 1} var solopts SolverOptions solopts.MaxIter = 40 solopts.ShowProgress = false solopts.KKTSolverName = "ldl" sol, err := Gp(K, F, g, nil, nil, nil, nil, &solopts) if sol != nil && sol.Status == Optimal { x := sol.Result.At("x")[0] r := matrix.Exp(x) h := r.GetIndex(0) w := r.GetIndex(1) d := r.GetIndex(2) t.Logf("x=\n%v\n", x.ToString("%.9f")) t.Logf("h = %f, w = %f, d = %f.\n", h, w, d) xe, _ := nrmError(matrix.FloatVector(xref), x) if xe > TOL { t.Logf("x differs [%.3e] from exepted too much.", xe) t.Fail() } } else { t.Logf("status: %v\n", err) t.Fail() } }
func (gp *gpConvexProg) F2(x, z *matrix.FloatMatrix) (f, Df, H *matrix.FloatMatrix, err error) { err = nil f = matrix.FloatZeros(gp.mnl+1, 1) Df = matrix.FloatZeros(gp.mnl+1, gp.n) H = matrix.FloatZeros(gp.n, gp.n) y := gp.g.Copy() Fsc := matrix.FloatZeros(gp.maxK, gp.n) blas.GemvFloat(gp.F, x, y, 1.0, 1.0) //fmt.Printf("y=\n%v\n", y.ToString("%.3f")) for i, s := range gp.ind { start := s[0] stop := s[1] // yi := exp(yi) = exp(Fi*x+gi) ymax := maxvec(y.FloatArray()[start:stop]) ynew := matrix.Exp(matrix.FloatVector(y.FloatArray()[start:stop]).Add(-ymax)) y.SetIndexesFromArray(ynew.FloatArray(), matrix.Indexes(start, stop)...) // fi = log sum yi = log sum exp(Fi*x+gi) ysum := blas.AsumFloat(y, &la.IOpt{"n", stop - start}, &la.IOpt{"offset", start}) f.SetIndex(i, ymax+math.Log(ysum)) blas.ScalFloat(y, 1.0/ysum, &la.IOpt{"n", stop - start}, &la.IOpt{"offset", start}) blas.GemvFloat(gp.F, y, Df, 1.0, 0.0, la.OptTrans, &la.IOpt{"m", stop - start}, &la.IOpt{"incy", gp.mnl + 1}, &la.IOpt{"offseta", start}, &la.IOpt{"offsetx", start}, &la.IOpt{"offsety", i}) Fsc.SetSubMatrix(0, 0, gp.F.GetSubMatrix(start, 0, stop-start)) for k := start; k < stop; k++ { blas.AxpyFloat(Df, Fsc, -1.0, &la.IOpt{"n", gp.n}, &la.IOpt{"incx", gp.mnl + 1}, &la.IOpt{"incy", Fsc.Rows()}, &la.IOpt{"offsetx", i}, &la.IOpt{"offsety", k - start}) blas.ScalFloat(Fsc, math.Sqrt(y.GetIndex(k)), &la.IOpt{"inc", Fsc.Rows()}, &la.IOpt{"offset", k - start}) } // H += z[i]*Hi = z[i] *Fisc' * Fisc blas.SyrkFloat(Fsc, H, z.GetIndex(i), 1.0, la.OptTrans, &la.IOpt{"k", stop - start}) } return }
func _TestMultMVSmall(t *testing.T) { bM := 5 bN := 4 A := matrix.FloatNormal(bM, bN) X := matrix.FloatVector([]float64{1.0, 2.0, 3.0, 4.0}) Y1 := matrix.FloatZeros(bM, 1) Y0 := matrix.FloatZeros(bM, 1) Ar := A.FloatArray() Xr := X.FloatArray() Y1r := Y1.FloatArray() blas.GemvFloat(A, X, Y0, 1.0, 1.0) DMultMV(Y1r, Ar, Xr, 1.0, 1.0, NOTRANS, 1, A.LeadingIndex(), 1, 0, bN, 0, bM, 4, 4) ok := Y0.AllClose(Y1) t.Logf("Y0 == Y1: %v\n", ok) if !ok { t.Logf("blas: Y=A*X\n%v\n", Y0) t.Logf("Y1: Y1 = A*X\n%v\n", Y1) } }
// X <--> Y func TestDswap(t *testing.T) { fmt.Printf("* L1 * test swap: X <--> Y\n") A := matrix.FloatVector([]float64{1.0, 1.0, 1.0, 1.0, 1.0, 1.0}) B := matrix.FloatVector([]float64{2.0, 2.0, 2.0, 2.0, 2.0, 2.0}) Swap(A, B) fmt.Printf("Dswap A, B\n") fmt.Printf("%s\n", A) A = matrix.FloatVector([]float64{1.0, 1.0, 1.0, 1.0, 1.0, 1.0}) B = matrix.FloatVector([]float64{2.0, 2.0, 2.0, 2.0, 2.0, 2.0}) Swap(A, B, &linalg.IOpt{"offset", 3}) fmt.Printf("Dswap A[3:], B[3:]\n") fmt.Printf("%s\n", A) A = matrix.FloatVector([]float64{1.0, 1.0, 1.0, 1.0, 1.0, 1.0}) B = matrix.FloatVector([]float64{2.0, 2.0, 2.0, 2.0, 2.0, 2.0}) fmt.Printf("Dswap A[::2], B[::2]\n") Swap(A, B, &linalg.IOpt{"inc", 2}) fmt.Printf("%s\n", A) }
// Internal CPL solver for CP and CLP problems. Everything is wrapped to proper interfaces func cpl_solver(F ConvexVarProg, c MatrixVariable, G MatrixVarG, h *matrix.FloatMatrix, A MatrixVarA, b MatrixVariable, dims *sets.DimensionSet, kktsolver KKTCpSolverVar, solopts *SolverOptions, x0 MatrixVariable, mnl int) (sol *Solution, err error) { const ( STEP = 0.99 BETA = 0.5 ALPHA = 0.01 EXPON = 3 MAX_RELAXED_ITERS = 8 ) var refinement int sol = &Solution{Unknown, nil, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0} feasTolerance := FEASTOL absTolerance := ABSTOL relTolerance := RELTOL maxIter := MAXITERS if solopts.FeasTol > 0.0 { feasTolerance = solopts.FeasTol } if solopts.AbsTol > 0.0 { absTolerance = solopts.AbsTol } if solopts.RelTol > 0.0 { relTolerance = solopts.RelTol } if solopts.Refinement > 0 { refinement = solopts.Refinement } else { refinement = 1 } if solopts.MaxIter > 0 { maxIter = solopts.MaxIter } if x0 == nil { mnl, x0, err = F.F0() if err != nil { return } } if c == nil { err = errors.New("Must define objective.") return } if h == nil { h = matrix.FloatZeros(0, 1) } if dims == nil { err = errors.New("Problem dimensions not defined.") return } if err = checkConeLpDimensions(dims); err != nil { return } cdim := dims.Sum("l", "q") + dims.SumSquared("s") cdim_diag := dims.Sum("l", "q", "s") if h.Rows() != cdim { err = errors.New(fmt.Sprintf("'h' must be float matrix of size (%d,1)", cdim)) return } if G == nil { err = errors.New("'G' must be non-nil MatrixG interface.") return } fG := func(x, y MatrixVariable, alpha, beta float64, trans la.Option) error { return G.Gf(x, y, alpha, beta, trans) } // Check A and set defaults if it is nil if A == nil { err = errors.New("'A' must be non-nil MatrixA interface.") return } fA := func(x, y MatrixVariable, alpha, beta float64, trans la.Option) error { return A.Af(x, y, alpha, beta, trans) } if b == nil { err = errors.New("'b' must be non-nil MatrixVariable interface.") return } if kktsolver == nil { err = errors.New("nil kktsolver not allowed.") return } x := x0.Copy() y := b.Copy() y.Scal(0.0) z := matrix.FloatZeros(mnl+cdim, 1) s := matrix.FloatZeros(mnl+cdim, 1) ind := mnl + dims.At("l")[0] z.SetIndexes(1.0, matrix.MakeIndexSet(0, ind, 1)...) s.SetIndexes(1.0, matrix.MakeIndexSet(0, ind, 1)...) for _, m := range dims.At("q") { z.SetIndexes(1.0, ind) s.SetIndexes(1.0, ind) ind += m } for _, m := range dims.At("s") { iset := matrix.MakeIndexSet(ind, ind+m*m, m+1) z.SetIndexes(1.0, iset...) s.SetIndexes(1.0, iset...) ind += m * m } rx := x0.Copy() ry := b.Copy() dx := x.Copy() dy := y.Copy() rznl := matrix.FloatZeros(mnl, 1) rzl := matrix.FloatZeros(cdim, 1) dz := matrix.FloatZeros(mnl+cdim, 1) ds := matrix.FloatZeros(mnl+cdim, 1) lmbda := matrix.FloatZeros(mnl+cdim_diag, 1) lmbdasq := matrix.FloatZeros(mnl+cdim_diag, 1) sigs := matrix.FloatZeros(dims.Sum("s"), 1) sigz := matrix.FloatZeros(dims.Sum("s"), 1) dz2 := matrix.FloatZeros(mnl+cdim, 1) ds2 := matrix.FloatZeros(mnl+cdim, 1) newx := x.Copy() newy := y.Copy() newrx := x0.Copy() newz := matrix.FloatZeros(mnl+cdim, 1) news := matrix.FloatZeros(mnl+cdim, 1) newrznl := matrix.FloatZeros(mnl, 1) rx0 := rx.Copy() ry0 := ry.Copy() rznl0 := matrix.FloatZeros(mnl, 1) rzl0 := matrix.FloatZeros(cdim, 1) x0, dx0 := x.Copy(), dx.Copy() y0, dy0 := y.Copy(), dy.Copy() z0 := matrix.FloatZeros(mnl+cdim, 1) dz0 := matrix.FloatZeros(mnl+cdim, 1) dz20 := matrix.FloatZeros(mnl+cdim, 1) s0 := matrix.FloatZeros(mnl+cdim, 1) ds0 := matrix.FloatZeros(mnl+cdim, 1) ds20 := matrix.FloatZeros(mnl+cdim, 1) checkpnt.AddMatrixVar("z", z) checkpnt.AddMatrixVar("s", s) checkpnt.AddMatrixVar("dz", dz) checkpnt.AddMatrixVar("ds", ds) checkpnt.AddMatrixVar("rznl", rznl) checkpnt.AddMatrixVar("rzl", rzl) checkpnt.AddMatrixVar("lmbda", lmbda) checkpnt.AddMatrixVar("lmbdasq", lmbdasq) checkpnt.AddMatrixVar("z0", z0) checkpnt.AddMatrixVar("dz0", dz0) checkpnt.AddVerifiable("c", c) checkpnt.AddVerifiable("x", x) checkpnt.AddVerifiable("rx", rx) checkpnt.AddVerifiable("dx", dx) checkpnt.AddVerifiable("newrx", newrx) checkpnt.AddVerifiable("newx", newx) checkpnt.AddVerifiable("x0", x0) checkpnt.AddVerifiable("dx0", dx0) checkpnt.AddVerifiable("rx0", rx0) checkpnt.AddVerifiable("y", y) checkpnt.AddVerifiable("dy", dy) W0 := sets.NewFloatSet("d", "di", "dnl", "dnli", "v", "r", "rti", "beta") W0.Set("dnl", matrix.FloatZeros(mnl, 1)) W0.Set("dnli", matrix.FloatZeros(mnl, 1)) W0.Set("d", matrix.FloatZeros(dims.At("l")[0], 1)) W0.Set("di", matrix.FloatZeros(dims.At("l")[0], 1)) W0.Set("beta", matrix.FloatZeros(len(dims.At("q")), 1)) for _, n := range dims.At("q") { W0.Append("v", matrix.FloatZeros(n, 1)) } for _, n := range dims.At("s") { W0.Append("r", matrix.FloatZeros(n, n)) W0.Append("rti", matrix.FloatZeros(n, n)) } lmbda0 := matrix.FloatZeros(mnl+dims.Sum("l", "q", "s"), 1) lmbdasq0 := matrix.FloatZeros(mnl+dims.Sum("l", "q", "s"), 1) var f MatrixVariable = nil var Df MatrixVarDf = nil var H MatrixVarH = nil var ws3, wz3, wz2l, wz2nl *matrix.FloatMatrix var ws, wz, wz2, ws2 *matrix.FloatMatrix var wx, wx2, wy, wy2 MatrixVariable var gap, gap0, theta1, theta2, theta3, ts, tz, phi, phi0, mu, sigma, eta float64 var resx, resy, reszl, resznl, pcost, dcost, dres, pres, relgap float64 var resx0, resznl0, dres0, pres0 float64 var dsdz, dsdz0, step, step0, dphi, dphi0, sigma0, eta0 float64 var newresx, newresznl, newgap, newphi float64 var W *sets.FloatMatrixSet var f3 KKTFuncVar checkpnt.AddFloatVar("gap", &gap) checkpnt.AddFloatVar("pcost", &pcost) checkpnt.AddFloatVar("dcost", &dcost) checkpnt.AddFloatVar("pres", &pres) checkpnt.AddFloatVar("dres", &dres) checkpnt.AddFloatVar("relgap", &relgap) checkpnt.AddFloatVar("step", &step) checkpnt.AddFloatVar("dsdz", &dsdz) checkpnt.AddFloatVar("resx", &resx) checkpnt.AddFloatVar("resy", &resy) checkpnt.AddFloatVar("reszl", &reszl) checkpnt.AddFloatVar("resznl", &resznl) // Declare fDf and fH here, they bind to Df and H as they are already declared. // ??really?? var fDf func(u, v MatrixVariable, alpha, beta float64, trans la.Option) error = nil var fH func(u, v MatrixVariable, alpha, beta float64) error = nil relaxed_iters := 0 for iters := 0; iters <= maxIter+1; iters++ { checkpnt.MajorNext() checkpnt.Check("loopstart", 10) checkpnt.MinorPush(10) if refinement != 0 || solopts.Debug { f, Df, H, err = F.F2(x, matrix.FloatVector(z.FloatArray()[:mnl])) fDf = func(u, v MatrixVariable, alpha, beta float64, trans la.Option) error { return Df.Df(u, v, alpha, beta, trans) } fH = func(u, v MatrixVariable, alpha, beta float64) error { return H.Hf(u, v, alpha, beta) } } else { f, Df, err = F.F1(x) fDf = func(u, v MatrixVariable, alpha, beta float64, trans la.Option) error { return Df.Df(u, v, alpha, beta, trans) } } checkpnt.MinorPop() gap = sdot(s, z, dims, mnl) // these are helpers, copies of parts of z,s z_mnl := matrix.FloatVector(z.FloatArray()[:mnl]) z_mnl2 := matrix.FloatVector(z.FloatArray()[mnl:]) s_mnl := matrix.FloatVector(s.FloatArray()[:mnl]) s_mnl2 := matrix.FloatVector(s.FloatArray()[mnl:]) // rx = c + A'*y + Df'*z[:mnl] + G'*z[mnl:] // -- y, rx MatrixArg mCopy(c, rx) fA(y, rx, 1.0, 1.0, la.OptTrans) fDf(&matrixVar{z_mnl}, rx, 1.0, 1.0, la.OptTrans) fG(&matrixVar{z_mnl2}, rx, 1.0, 1.0, la.OptTrans) resx = math.Sqrt(rx.Dot(rx)) // rznl = s[:mnl] + f blas.Copy(s_mnl, rznl) blas.AxpyFloat(f.Matrix(), rznl, 1.0) resznl = blas.Nrm2Float(rznl) // rzl = s[mnl:] + G*x - h blas.Copy(s_mnl2, rzl) blas.AxpyFloat(h, rzl, -1.0) fG(x, &matrixVar{rzl}, 1.0, 1.0, la.OptNoTrans) reszl = snrm2(rzl, dims, 0) // Statistics for stopping criteria // pcost = c'*x // dcost = c'*x + y'*(A*x-b) + znl'*f(x) + zl'*(G*x-h) // = c'*x + y'*(A*x-b) + znl'*(f(x)+snl) + zl'*(G*x-h+sl) // - z'*s // = c'*x + y'*ry + znl'*rznl + zl'*rzl - gap //pcost = blas.DotFloat(c, x) pcost = c.Dot(x) dcost = pcost + blas.DotFloat(y.Matrix(), ry.Matrix()) + blas.DotFloat(z_mnl, rznl) dcost += sdot(z_mnl2, rzl, dims, 0) - gap if pcost < 0.0 { relgap = gap / -pcost } else if dcost > 0.0 { relgap = gap / dcost } else { relgap = math.NaN() } pres = math.Sqrt(resy*resy + resznl*resznl + reszl*reszl) dres = resx if iters == 0 { resx0 = math.Max(1.0, resx) resznl0 = math.Max(1.0, resznl) pres0 = math.Max(1.0, pres) dres0 = math.Max(1.0, dres) gap0 = gap theta1 = 1.0 / gap0 theta2 = 1.0 / resx0 theta3 = 1.0 / resznl0 } phi = theta1*gap + theta2*resx + theta3*resznl pres = pres / pres0 dres = dres / dres0 if solopts.ShowProgress { if iters == 0 { // some headers fmt.Printf("% 10s% 12s% 10s% 8s% 7s\n", "pcost", "dcost", "gap", "pres", "dres") } fmt.Printf("%2d: % 8.4e % 8.4e % 4.0e% 7.0e% 7.0e\n", iters, pcost, dcost, gap, pres, dres) } checkpnt.Check("checkgap", 50) // Stopping criteria if (pres <= feasTolerance && dres <= feasTolerance && (gap <= absTolerance || (!math.IsNaN(relgap) && relgap <= relTolerance))) || iters == maxIter { if iters == maxIter { s := "Terminated (maximum number of iterations reached)" if solopts.ShowProgress { fmt.Printf(s + "\n") } err = errors.New(s) sol.Status = Unknown } else { err = nil sol.Status = Optimal } sol.Result = sets.NewFloatSet("x", "y", "znl", "zl", "snl", "sl") sol.Result.Set("x", x.Matrix()) sol.Result.Set("y", y.Matrix()) sol.Result.Set("znl", matrix.FloatVector(z.FloatArray()[:mnl])) sol.Result.Set("zl", matrix.FloatVector(z.FloatArray()[mnl:])) sol.Result.Set("sl", matrix.FloatVector(s.FloatArray()[mnl:])) sol.Result.Set("snl", matrix.FloatVector(s.FloatArray()[:mnl])) sol.Gap = gap sol.RelativeGap = relgap sol.PrimalObjective = pcost sol.DualObjective = dcost sol.PrimalInfeasibility = pres sol.DualInfeasibility = dres sol.PrimalSlack = -ts sol.DualSlack = -tz return } // Compute initial scaling W: // // W * z = W^{-T} * s = lambda. // // lmbdasq = lambda o lambda if iters == 0 { W, _ = computeScaling(s, z, lmbda, dims, mnl) checkpnt.AddScaleVar(W) } ssqr(lmbdasq, lmbda, dims, mnl) checkpnt.Check("lmbdasq", 90) // f3(x, y, z) solves // // [ H A' GG'*W^{-1} ] [ ux ] [ bx ] // [ A 0 0 ] [ uy ] = [ by ]. // [ GG 0 -W' ] [ uz ] [ bz ] // // On entry, x, y, z contain bx, by, bz. // On exit, they contain ux, uy, uz. checkpnt.MinorPush(95) f3, err = kktsolver(W, x, z_mnl) checkpnt.MinorPop() checkpnt.Check("f3", 100) if err != nil { // ?? z_mnl is really copy of z[:mnl] ... should we copy here back to z?? singular_kkt_matrix := false if iters == 0 { err = errors.New("Rank(A) < p or Rank([H(x); A; Df(x); G] < n") return } else if relaxed_iters > 0 && relaxed_iters < MAX_RELAXED_ITERS { // The arithmetic error may be caused by a relaxed line // search in the previous iteration. Therefore we restore // the last saved state and require a standard line search. phi, gap = phi0, gap0 mu = gap / float64(mnl+dims.Sum("l", "s")+len(dims.At("q"))) blas.Copy(W0.At("dnl")[0], W.At("dnl")[0]) blas.Copy(W0.At("dnli")[0], W.At("dnli")[0]) blas.Copy(W0.At("d")[0], W.At("d")[0]) blas.Copy(W0.At("di")[0], W.At("di")[0]) blas.Copy(W0.At("beta")[0], W.At("beta")[0]) for k, _ := range dims.At("q") { blas.Copy(W0.At("v")[k], W.At("v")[k]) } for k, _ := range dims.At("s") { blas.Copy(W0.At("r")[k], W.At("r")[k]) blas.Copy(W0.At("rti")[k], W.At("rti")[k]) } //blas.Copy(x0, x) //x0.CopyTo(x) mCopy(x0, x) //blas.Copy(y0, y) mCopy(y0, y) blas.Copy(s0, s) blas.Copy(z0, z) blas.Copy(lmbda0, lmbda) blas.Copy(lmbdasq0, lmbdasq) // ??? //blas.Copy(rx0, rx) //rx0.CopyTo(rx) mCopy(rx0, rx) //blas.Copy(ry0, ry) mCopy(ry0, ry) //resx = math.Sqrt(blas.DotFloat(rx, rx)) resx = math.Sqrt(rx.Dot(rx)) blas.Copy(rznl0, rznl) blas.Copy(rzl0, rzl) resznl = blas.Nrm2Float(rznl) relaxed_iters = -1 // How about z_mnl here??? checkpnt.MinorPush(120) f3, err = kktsolver(W, x, z_mnl) checkpnt.MinorPop() if err != nil { singular_kkt_matrix = true } } else { singular_kkt_matrix = true } if singular_kkt_matrix { msg := "Terminated (singular KKT matrix)." if solopts.ShowProgress { fmt.Printf(msg + "\n") } zl := matrix.FloatVector(z.FloatArray()[mnl:]) sl := matrix.FloatVector(s.FloatArray()[mnl:]) ind := dims.Sum("l", "q") for _, m := range dims.At("s") { symm(sl, m, ind) symm(zl, m, ind) ind += m * m } ts, _ = maxStep(s, dims, mnl, nil) tz, _ = maxStep(z, dims, mnl, nil) err = errors.New(msg) sol.Status = Unknown sol.Result = sets.NewFloatSet("x", "y", "znl", "zl", "snl", "sl") sol.Result.Set("x", x.Matrix()) sol.Result.Set("y", y.Matrix()) sol.Result.Set("znl", matrix.FloatVector(z.FloatArray()[:mnl])) sol.Result.Set("zl", zl) sol.Result.Set("sl", sl) sol.Result.Set("snl", matrix.FloatVector(s.FloatArray()[:mnl])) sol.Gap = gap sol.RelativeGap = relgap sol.PrimalObjective = pcost sol.DualObjective = dcost sol.PrimalInfeasibility = pres sol.DualInfeasibility = dres sol.PrimalSlack = -ts sol.DualSlack = -tz return } } // f4_no_ir(x, y, z, s) solves // // [ 0 ] [ H A' GG' ] [ ux ] [ bx ] // [ 0 ] + [ A 0 0 ] [ uy ] = [ by ] // [ W'*us ] [ GG 0 0 ] [ W^{-1}*uz ] [ bz ] // // lmbda o (uz + us) = bs. // // On entry, x, y, z, x, contain bx, by, bz, bs. // On exit, they contain ux, uy, uz, us. if iters == 0 { ws3 = matrix.FloatZeros(mnl+cdim, 1) wz3 = matrix.FloatZeros(mnl+cdim, 1) checkpnt.AddMatrixVar("ws3", ws3) checkpnt.AddMatrixVar("wz3", wz3) } f4_no_ir := func(x, y MatrixVariable, z, s *matrix.FloatMatrix) (err error) { // Solve // // [ H A' GG' ] [ ux ] [ bx ] // [ A 0 0 ] [ uy ] = [ by ] // [ GG 0 -W'*W ] [ W^{-1}*uz ] [ bz - W'*(lmbda o\ bs) ] // // us = lmbda o\ bs - uz. err = nil // s := lmbda o\ s // = lmbda o\ bs sinv(s, lmbda, dims, mnl) // z := z - W'*s // = bz - W' * (lambda o\ bs) blas.Copy(s, ws3) scale(ws3, W, true, false) blas.AxpyFloat(ws3, z, -1.0) // Solve for ux, uy, uz err = f3(x, y, z) // s := s - z // = lambda o\ bs - z. blas.AxpyFloat(z, s, -1.0) return } if iters == 0 { wz2nl = matrix.FloatZeros(mnl, 1) wz2l = matrix.FloatZeros(cdim, 1) checkpnt.AddMatrixVar("wz2nl", wz2nl) checkpnt.AddMatrixVar("wz2l", wz2l) } res := func(ux, uy MatrixVariable, uz, us *matrix.FloatMatrix, vx, vy MatrixVariable, vz, vs *matrix.FloatMatrix) (err error) { // Evaluates residuals in Newton equations: // // [ vx ] [ 0 ] [ H A' GG' ] [ ux ] // [ vy ] -= [ 0 ] + [ A 0 0 ] [ uy ] // [ vz ] [ W'*us ] [ GG 0 0 ] [ W^{-1}*uz ] // // vs -= lmbda o (uz + us). err = nil minor := checkpnt.MinorTop() // vx := vx - H*ux - A'*uy - GG'*W^{-1}*uz fH(ux, vx, -1.0, 1.0) fA(uy, vx, -1.0, 1.0, la.OptTrans) blas.Copy(uz, wz3) scale(wz3, W, false, true) wz3_nl := matrix.FloatVector(wz3.FloatArray()[:mnl]) wz3_l := matrix.FloatVector(wz3.FloatArray()[mnl:]) fDf(&matrixVar{wz3_nl}, vx, -1.0, 1.0, la.OptTrans) fG(&matrixVar{wz3_l}, vx, -1.0, 1.0, la.OptTrans) checkpnt.Check("10res", minor+10) // vy := vy - A*ux fA(ux, vy, -1.0, 1.0, la.OptNoTrans) // vz := vz - W'*us - GG*ux err = fDf(ux, &matrixVar{wz2nl}, 1.0, 0.0, la.OptNoTrans) checkpnt.Check("15res", minor+10) blas.AxpyFloat(wz2nl, vz, -1.0) fG(ux, &matrixVar{wz2l}, 1.0, 0.0, la.OptNoTrans) checkpnt.Check("20res", minor+10) blas.AxpyFloat(wz2l, vz, -1.0, &la.IOpt{"offsety", mnl}) blas.Copy(us, ws3) scale(ws3, W, true, false) blas.AxpyFloat(ws3, vz, -1.0) checkpnt.Check("30res", minor+10) // vs -= lmbda o (uz + us) blas.Copy(us, ws3) blas.AxpyFloat(uz, ws3, 1.0) sprod(ws3, lmbda, dims, mnl, &la.SOpt{"diag", "D"}) blas.AxpyFloat(ws3, vs, -1.0) checkpnt.Check("90res", minor+10) return } // f4(x, y, z, s) solves the same system as f4_no_ir, but applies // iterative refinement. if iters == 0 { if refinement > 0 || solopts.Debug { wx = c.Copy() wy = b.Copy() wz = z.Copy() ws = s.Copy() checkpnt.AddVerifiable("wx", wx) checkpnt.AddMatrixVar("ws", ws) checkpnt.AddMatrixVar("wz", wz) } if refinement > 0 { wx2 = c.Copy() wy2 = b.Copy() wz2 = matrix.FloatZeros(mnl+cdim, 1) ws2 = matrix.FloatZeros(mnl+cdim, 1) checkpnt.AddVerifiable("wx2", wx2) checkpnt.AddMatrixVar("ws2", ws2) checkpnt.AddMatrixVar("wz2", wz2) } } f4 := func(x, y MatrixVariable, z, s *matrix.FloatMatrix) (err error) { if refinement > 0 || solopts.Debug { mCopy(x, wx) mCopy(y, wy) blas.Copy(z, wz) blas.Copy(s, ws) } minor := checkpnt.MinorTop() checkpnt.Check("0_f4", minor+100) checkpnt.MinorPush(minor + 100) err = f4_no_ir(x, y, z, s) checkpnt.MinorPop() checkpnt.Check("1_f4", minor+200) for i := 0; i < refinement; i++ { mCopy(wx, wx2) mCopy(wy, wy2) blas.Copy(wz, wz2) blas.Copy(ws, ws2) checkpnt.Check("2_f4", minor+(1+i)*200) checkpnt.MinorPush(minor + (1+i)*200) res(x, y, z, s, wx2, wy2, wz2, ws2) checkpnt.MinorPop() checkpnt.Check("3_f4", minor+(1+i)*200+100) err = f4_no_ir(wx2, wy2, wz2, ws2) checkpnt.MinorPop() checkpnt.Check("4_f4", minor+(1+i)*200+199) wx2.Axpy(x, 1.0) wy2.Axpy(y, 1.0) blas.AxpyFloat(wz2, z, 1.0) blas.AxpyFloat(ws2, s, 1.0) } if solopts.Debug { res(x, y, z, s, wx, wy, wz, ws) fmt.Printf("KKT residuals:\n") } return } sigma, eta = 0.0, 0.0 for i := 0; i < 2; i++ { minor := (i + 2) * 1000 checkpnt.MinorPush(minor) checkpnt.Check("loop01", minor) // Solve // // [ 0 ] [ H A' GG' ] [ dx ] // [ 0 ] + [ A 0 0 ] [ dy ] = -(1 - eta)*r // [ W'*ds ] [ GG 0 0 ] [ W^{-1}*dz ] // // lmbda o (dz + ds) = -lmbda o lmbda + sigma*mu*e. // mu = gap / float64(mnl+dims.Sum("l", "s")+len(dims.At("q"))) blas.ScalFloat(ds, 0.0) blas.AxpyFloat(lmbdasq, ds, -1.0, &la.IOpt{"n", mnl + dims.Sum("l", "q")}) ind = mnl + dims.At("l")[0] iset := matrix.MakeIndexSet(0, ind, 1) ds.Add(sigma*mu, iset...) for _, m := range dims.At("q") { ds.Add(sigma*mu, ind) ind += m } ind2 := ind for _, m := range dims.At("s") { blas.AxpyFloat(lmbdasq, ds, -1.0, &la.IOpt{"n", m}, &la.IOpt{"offsetx", ind2}, &la.IOpt{"offsety", ind}, &la.IOpt{"incy", m + 1}) ds.Add(sigma*mu, matrix.MakeIndexSet(ind, ind+m*m, m+1)...) ind += m * m ind2 += m } dx.Scal(0.0) rx.Axpy(dx, -1.0+eta) dy.Scal(0.0) ry.Axpy(dy, -1.0+eta) dz.Scale(0.0) blas.AxpyFloat(rznl, dz, -1.0+eta) blas.AxpyFloat(rzl, dz, -1.0+eta, &la.IOpt{"offsety", mnl}) //fmt.Printf("dx=\n%v\n", dx) //fmt.Printf("dz=\n%v\n", dz.ToString("%.7f")) //fmt.Printf("ds=\n%v\n", ds.ToString("%.7f")) checkpnt.Check("pref4", minor) checkpnt.MinorPush(minor) err = f4(dx, dy, dz, ds) if err != nil { if iters == 0 { s := fmt.Sprintf("Rank(A) < p or Rank([H(x); A; Df(x); G] < n (%s)", err) err = errors.New(s) return } msg := "Terminated (singular KKT matrix)." if solopts.ShowProgress { fmt.Printf(msg + "\n") } zl := matrix.FloatVector(z.FloatArray()[mnl:]) sl := matrix.FloatVector(s.FloatArray()[mnl:]) ind := dims.Sum("l", "q") for _, m := range dims.At("s") { symm(sl, m, ind) symm(zl, m, ind) ind += m * m } ts, _ = maxStep(s, dims, mnl, nil) tz, _ = maxStep(z, dims, mnl, nil) err = errors.New(msg) sol.Status = Unknown sol.Result = sets.NewFloatSet("x", "y", "znl", "zl", "snl", "sl") sol.Result.Set("x", x.Matrix()) sol.Result.Set("y", y.Matrix()) sol.Result.Set("znl", matrix.FloatVector(z.FloatArray()[:mnl])) sol.Result.Set("zl", zl) sol.Result.Set("sl", sl) sol.Result.Set("snl", matrix.FloatVector(s.FloatArray()[:mnl])) sol.Gap = gap sol.RelativeGap = relgap sol.PrimalObjective = pcost sol.DualObjective = dcost sol.PrimalInfeasibility = pres sol.DualInfeasibility = dres sol.PrimalSlack = -ts sol.DualSlack = -tz return } checkpnt.MinorPop() checkpnt.Check("postf4", minor+400) // Inner product ds'*dz and unscaled steps are needed in the // line search. dsdz = sdot(ds, dz, dims, mnl) blas.Copy(dz, dz2) scale(dz2, W, false, true) blas.Copy(ds, ds2) scale(ds2, W, true, false) checkpnt.Check("dsdz", minor+400) // Maximum steps to boundary. // // Also compute the eigenvalue decomposition of 's' blocks in // ds, dz. The eigenvectors Qs, Qz are stored in ds, dz. // The eigenvalues are stored in sigs, sigz. scale2(lmbda, ds, dims, mnl, false) ts, _ = maxStep(ds, dims, mnl, sigs) scale2(lmbda, dz, dims, mnl, false) tz, _ = maxStep(dz, dims, mnl, sigz) t := maxvec([]float64{0.0, ts, tz}) if t == 0 { step = 1.0 } else { step = math.Min(1.0, STEP/t) } checkpnt.Check("maxstep", minor+400) var newDf MatrixVarDf = nil var newf MatrixVariable = nil // Backtrack until newx is in domain of f. backtrack := true for backtrack { mCopy(x, newx) dx.Axpy(newx, step) newf, newDf, err = F.F1(newx) if newf != nil { backtrack = false } else { step *= BETA } } // Merit function // // phi = theta1 * gap + theta2 * norm(rx) + // theta3 * norm(rznl) // // and its directional derivative dphi. phi = theta1*gap + theta2*resx + theta3*resznl if i == 0 { dphi = -phi } else { dphi = -theta1*(1-sigma)*gap - theta2*(1-eta)*resx - theta3*(1-eta)*resznl } var newfDf func(x, y MatrixVariable, a, b float64, trans la.Option) error // Line search backtrack = true for backtrack { mCopy(x, newx) dx.Axpy(newx, step) mCopy(y, newy) dy.Axpy(newy, step) blas.Copy(z, newz) blas.AxpyFloat(dz2, newz, step) blas.Copy(s, news) blas.AxpyFloat(ds2, news, step) newf, newDf, err = F.F1(newx) newfDf = func(u, v MatrixVariable, a, b float64, trans la.Option) error { return newDf.Df(u, v, a, b, trans) } // newrx = c + A'*newy + newDf'*newz[:mnl] + G'*newz[mnl:] newz_mnl := matrix.FloatVector(newz.FloatArray()[:mnl]) newz_ml := matrix.FloatVector(newz.FloatArray()[mnl:]) //blas.Copy(c, newrx) //c.CopyTo(newrx) mCopy(c, newrx) fA(newy, newrx, 1.0, 1.0, la.OptTrans) newfDf(&matrixVar{newz_mnl}, newrx, 1.0, 1.0, la.OptTrans) fG(&matrixVar{newz_ml}, newrx, 1.0, 1.0, la.OptTrans) newresx = math.Sqrt(newrx.Dot(newrx)) // newrznl = news[:mnl] + newf news_mnl := matrix.FloatVector(news.FloatArray()[:mnl]) //news_ml := matrix.FloatVector(news.FloatArray()[mnl:]) blas.Copy(news_mnl, newrznl) blas.AxpyFloat(newf.Matrix(), newrznl, 1.0) newresznl = blas.Nrm2Float(newrznl) newgap = (1.0-(1.0-sigma)*step)*gap + step*step*dsdz newphi = theta1*newgap + theta2*newresx + theta3*newresznl if i == 0 { if newgap <= (1.0-ALPHA*step)*gap && (relaxed_iters > 0 && relaxed_iters < MAX_RELAXED_ITERS || newphi <= phi+ALPHA*step*dphi) { backtrack = false sigma = math.Min(newgap/gap, math.Pow((newgap/gap), EXPON)) //fmt.Printf("break 1: sigma=%.7f\n", sigma) eta = 0.0 } else { step *= BETA } } else { if relaxed_iters == -1 || (relaxed_iters == 0 && MAX_RELAXED_ITERS == 0) { // Do a standard line search. if newphi <= phi+ALPHA*step*dphi { relaxed_iters = 0 backtrack = false //fmt.Printf("break 2 : newphi=%.7f\n", newphi) } else { step *= BETA } } else if relaxed_iters == 0 && relaxed_iters < MAX_RELAXED_ITERS { if newphi <= phi+ALPHA*step*dphi { // Relaxed l.s. gives sufficient decrease. relaxed_iters = 0 } else { // Save state. phi0, dphi0, gap0 = phi, dphi, gap step0 = step blas.Copy(W.At("dnl")[0], W0.At("dnl")[0]) blas.Copy(W.At("dnli")[0], W0.At("dnli")[0]) blas.Copy(W.At("d")[0], W0.At("d")[0]) blas.Copy(W.At("di")[0], W0.At("di")[0]) blas.Copy(W.At("beta")[0], W0.At("beta")[0]) for k, _ := range dims.At("q") { blas.Copy(W.At("v")[k], W0.At("v")[k]) } for k, _ := range dims.At("s") { blas.Copy(W.At("r")[k], W0.At("r")[k]) blas.Copy(W.At("rti")[k], W0.At("rti")[k]) } mCopy(x, x0) mCopy(y, y0) mCopy(dx, dx0) mCopy(dy, dy0) blas.Copy(s, s0) blas.Copy(z, z0) blas.Copy(ds, ds0) blas.Copy(dz, dz0) blas.Copy(ds2, ds20) blas.Copy(dz2, dz20) blas.Copy(lmbda, lmbda0) blas.Copy(lmbdasq, lmbdasq0) // ??? mCopy(rx, rx0) mCopy(ry, ry0) blas.Copy(rznl, rznl0) blas.Copy(rzl, rzl0) dsdz0 = dsdz sigma0, eta0 = sigma, eta relaxed_iters = 1 } backtrack = false //fmt.Printf("break 3 : newphi=%.7f\n", newphi) } else if relaxed_iters >= 0 && relaxed_iters < MAX_RELAXED_ITERS && MAX_RELAXED_ITERS > 0 { if newphi <= phi0+ALPHA*step0*dphi0 { // Relaxed l.s. gives sufficient decrease. relaxed_iters = 0 } else { // Relaxed line search relaxed_iters += 1 } backtrack = false //fmt.Printf("break 4 : newphi=%.7f\n", newphi) } else if relaxed_iters == MAX_RELAXED_ITERS && MAX_RELAXED_ITERS > 0 { if newphi <= phi0+ALPHA*step0*dphi0 { // Series of relaxed line searches ends // with sufficient decrease w.r.t. phi0. backtrack = false relaxed_iters = 0 //fmt.Printf("break 5 : newphi=%.7f\n", newphi) } else if newphi >= phi0 { // Resume last saved line search phi, dphi, gap = phi0, dphi0, gap0 step = step0 blas.Copy(W0.At("dnl")[0], W.At("dnl")[0]) blas.Copy(W0.At("dnli")[0], W.At("dnli")[0]) blas.Copy(W0.At("d")[0], W.At("d")[0]) blas.Copy(W0.At("di")[0], W.At("di")[0]) blas.Copy(W0.At("beta")[0], W.At("beta")[0]) for k, _ := range dims.At("q") { blas.Copy(W0.At("v")[k], W.At("v")[k]) } for k, _ := range dims.At("s") { blas.Copy(W0.At("r")[k], W.At("r")[k]) blas.Copy(W0.At("rti")[k], W.At("rti")[k]) } mCopy(x, x0) mCopy(y, y0) mCopy(dx, dx0) mCopy(dy, dy0) blas.Copy(s, s0) blas.Copy(z, z0) blas.Copy(ds2, ds20) blas.Copy(dz2, dz20) blas.Copy(lmbda, lmbda0) blas.Copy(lmbdasq, lmbdasq0) // ??? mCopy(rx, rx0) mCopy(ry, ry0) blas.Copy(rznl, rznl0) blas.Copy(rzl, rzl0) dsdz = dsdz0 sigma, eta = sigma0, eta0 relaxed_iters = -1 } else if newphi <= phi+ALPHA*step*dphi { // Series of relaxed line searches ends // with sufficient decrease w.r.t. phi0. backtrack = false relaxed_iters = -1 //fmt.Printf("break 6 : newphi=%.7f\n", newphi) } } } } // end of line search checkpnt.Check("eol", minor+900) } // end for [0,1] // Update x, y dx.Axpy(x, step) dy.Axpy(y, step) checkpnt.Check("updatexy", 5000) // Replace nonlinear, 'l' and 'q' blocks of ds and dz with the // updated variables in the current scaling. // Replace 's' blocks of ds and dz with the factors Ls, Lz in a // factorization Ls*Ls', Lz*Lz' of the updated variables in the // current scaling. // ds := e + step*ds for nonlinear, 'l' and 'q' blocks. // dz := e + step*dz for nonlinear, 'l' and 'q' blocks. blas.ScalFloat(ds, step, &la.IOpt{"n", mnl + dims.Sum("l", "q")}) blas.ScalFloat(dz, step, &la.IOpt{"n", mnl + dims.Sum("l", "q")}) ind := mnl + dims.At("l")[0] is := matrix.MakeIndexSet(0, ind, 1) ds.Add(1.0, is...) dz.Add(1.0, is...) for _, m := range dims.At("q") { ds.SetIndex(ind, 1.0+ds.GetIndex(ind)) dz.SetIndex(ind, 1.0+dz.GetIndex(ind)) ind += m } checkpnt.Check("updatedsdz", 5100) // ds := H(lambda)^{-1/2} * ds and dz := H(lambda)^{-1/2} * dz. // // This replaces the 'l' and 'q' components of ds and dz with the // updated variables in the current scaling. // The 's' components of ds and dz are replaced with // // diag(lmbda_k)^{1/2} * Qs * diag(lmbda_k)^{1/2} // diag(lmbda_k)^{1/2} * Qz * diag(lmbda_k)^{1/2} scale2(lmbda, ds, dims, mnl, true) scale2(lmbda, dz, dims, mnl, true) checkpnt.Check("scale2", 5200) // sigs := ( e + step*sigs ) ./ lambda for 's' blocks. // sigz := ( e + step*sigz ) ./ lambda for 's' blocks. blas.ScalFloat(sigs, step) blas.ScalFloat(sigz, step) sigs.Add(1.0) sigz.Add(1.0) sdimsum := dims.Sum("s") qdimsum := dims.Sum("l", "q") blas.TbsvFloat(lmbda, sigs, &la.IOpt{"n", sdimsum}, &la.IOpt{"k", 0}, &la.IOpt{"lda", 1}, &la.IOpt{"offseta", mnl + qdimsum}) blas.TbsvFloat(lmbda, sigz, &la.IOpt{"n", sdimsum}, &la.IOpt{"k", 0}, &la.IOpt{"lda", 1}, &la.IOpt{"offseta", mnl + qdimsum}) checkpnt.Check("sigs", 5300) ind2 := mnl + qdimsum ind3 := 0 sdims := dims.At("s") for k := 0; k < len(sdims); k++ { m := sdims[k] for i := 0; i < m; i++ { a := math.Sqrt(sigs.GetIndex(ind3 + i)) blas.ScalFloat(ds, a, &la.IOpt{"offset", ind2 + m*i}, &la.IOpt{"n", m}) a = math.Sqrt(sigz.GetIndex(ind3 + i)) blas.ScalFloat(dz, a, &la.IOpt{"offset", ind2 + m*i}, &la.IOpt{"n", m}) } ind2 += m * m ind3 += m } checkpnt.Check("scaling", 5400) err = updateScaling(W, lmbda, ds, dz) checkpnt.Check("postscaling", 5500) // Unscale s, z, tau, kappa (unscaled variables are used only to // compute feasibility residuals). ind = mnl + dims.Sum("l", "q") ind2 = ind blas.Copy(lmbda, s, &la.IOpt{"n", ind}) for _, m := range dims.At("s") { blas.ScalFloat(s, 0.0, &la.IOpt{"offset", ind2}) blas.Copy(lmbda, s, &la.IOpt{"offsetx", ind}, &la.IOpt{"offsety", ind2}, &la.IOpt{"n", m}, &la.IOpt{"incy", m + 1}) ind += m ind2 += m * m } scale(s, W, true, false) checkpnt.Check("unscale_s", 5600) ind = mnl + dims.Sum("l", "q") ind2 = ind blas.Copy(lmbda, z, &la.IOpt{"n", ind}) for _, m := range dims.At("s") { blas.ScalFloat(z, 0.0, &la.IOpt{"offset", ind2}) blas.Copy(lmbda, z, &la.IOpt{"offsetx", ind}, &la.IOpt{"offsety", ind2}, &la.IOpt{"n", m}, &la.IOpt{"incy", m + 1}) ind += m ind2 += m * m } scale(z, W, false, true) checkpnt.Check("unscale_z", 5700) gap = blas.DotFloat(lmbda, lmbda) } return }
func TestConeLp(t *testing.T) { gdata := [][]float64{ []float64{16., 7., 24., -8., 8., -1., 0., -1., 0., 0., 7., -5., 1., -5., 1., -7., 1., -7., -4.}, []float64{-14., 2., 7., -13., -18., 3., 0., 0., -1., 0., 3., 13., -6., 13., 12., -10., -6., -10., -28.}, []float64{5., 0., -15., 12., -6., 17., 0., 0., 0., -1., 9., 6., -6., 6., -7., -7., -6., -7., -11.}} hdata := []float64{-3., 5., 12., -2., -14., -13., 10., 0., 0., 0., 68., -30., -19., -30., 99., 23., -19., 23., 10.} // these reference values obtained from running cvxopt conelp.py example xref := []float64{-1.22091525026262993, 0.09663323966626469, 3.57750155386611057} sref := []float64{ 0.00000172588537019, 13.35314040819201864, 94.28805677232460880, -53.44110853283719109, 18.97172963929198275, -75.32834138499130461, 10.00000013568614321, -1.22091525026262993, 0.09663323966626476, 3.57750155386611146, 44.05899318373081286, -58.82581769017131990, 4.26572401145687596, -58.82581769017131990, 124.10382738701650851, 40.46243652188705653, 4.26572401145687596, 40.46243652188705653, 47.17458693781828316} zref := []float64{ 0.09299833991484617, 0.00000001060210894, 0.23532251654806322, 0.13337937743566930, -0.04734875722474355, 0.18800192060450249, 0.00000001245876667, 0.00000000007816348, -0.00000000039584268, -0.00000000183463577, 0.12558704894101563, 0.08777794737598217, -0.08664401207348003, 0.08777794737598217, 0.06135161787371416, -0.06055906182304811, -0.08664401207348003, -0.06055906182304811, 0.05977675078191153} c := matrix.FloatVector([]float64{-6., -4., -5.}) G := matrix.FloatMatrixFromTable(gdata) h := matrix.FloatVector(hdata) dims := sets.DSetNew("l", "q", "s") dims.Set("l", []int{2}) dims.Set("q", []int{4, 4}) dims.Set("s", []int{3}) var solopts SolverOptions solopts.MaxIter = 30 solopts.ShowProgress = false sol, err := ConeLp(c, G, h, nil, nil, dims, &solopts, nil, nil) if err == nil { fail := false x := sol.Result.At("x")[0] s := sol.Result.At("s")[0] z := sol.Result.At("z")[0] t.Logf("Optimal\n") t.Logf("x=\n%v\n", x.ToString("%.9f")) t.Logf("s=\n%v\n", s.ToString("%.9f")) t.Logf("z=\n%v\n", z.ToString("%.9f")) xe, _ := nrmError(matrix.FloatVector(xref), x) if xe > TOL { t.Logf("x differs [%.3e] from exepted too much.", xe) fail = true } se, _ := nrmError(matrix.FloatVector(sref), s) if se > TOL { t.Logf("s differs [%.3e] from exepted too much.", se) fail = true } ze, _ := nrmError(matrix.FloatVector(zref), z) if ze > TOL { t.Logf("z differs [%.3e] from exepted too much.", ze) fail = true } if fail { t.Fail() } } else { t.Logf("status: %s\n", err) t.Fail() } }
func TestMcSdp(t *testing.T) { dataref := []float64{ 0.13391860811867590, -0.08810099183143839, 1.67440840625377385, 0.73364110729257948, 0.99752463160201232, -1.27750208100276641, -2.39671528273320744, -0.67928016472921104, -0.03909131843358723, 0.89355554552081806, -0.01764779458978329, -1.29655690877732033, -0.66798050600863512, 0.18170877830799323, 0.83105079034069296, -0.54824847139682842, -0.63803150269339892, 0.00708853169542829, -0.66985369437758802, -0.82776233138694755, 0.61250003036052025, -0.36758841015776106, -0.28949340911906524, -0.91687467081464402, -0.58380545879822354, 0.06461529015869494, 0.04674385977341894, -0.75072697866800864, -0.14423268736469502, 1.47556079692186803, -0.20647897491996936, -0.26069727887528138, -0.13821797189084586, -2.54339721705159461, 0.57741853670968823, -0.03610986310676272, -0.22617256815925429, 0.71500221712914425, 1.57726688654131597, -0.39227386148741172, 1.18277085579223740, 1.79311368741147459, -0.73134596730591273, -0.62981768127016369, 2.07069457080308439, -0.90300600743388137, 1.73554553761132668, 1.38308886012672638, -0.37536663987692281, -0.30431019951136606, 1.25357124882638882, -0.27384517924722629, -0.04632938142394107, 0.79898247010790735, 0.53995588924910287, 0.08585033894586751, 1.35529122074053832, 0.01385086083133875, 0.75136369502320788, -1.57490917820568810, -0.23737825305680132, 0.33649059262790887, -0.07959967279201274, -0.54087224229887154, -1.38043394277880926, 1.71655186027439033, 0.41811180400013570, 1.52495658744607199, 0.68647633127251673, -0.58855597085742983, 1.31674790938058739, -0.83298414482940919, 0.77396029737830319, -0.80351540646387032, -0.08543027502251639, 1.49619789524058855, 0.39776679351920241, 3.33842986686760090, 1.80093502635427316, 1.75627861060973078, 0.66747365962131211, -1.05016810484723888, 0.32937053624566975, 0.45508103845913089, -2.34700732048193306, -0.61389923742031549, -2.16180058868422442, -1.00134312023216387, -0.46226762826081341, -0.81756106137679618, 0.18439203820434547, -0.39491697602688680, -1.66955730897081911, -0.94514784362504667, 0.00356999348571242, 1.15573946640829073, 1.87743135406613315, 0.11219893028802071, 0.18121323774935597, 1.27830445029226136} xref := []float64{ 4.35021720027799841, 3.05093154317696458, 3.81967245723734372, 4.46546919954538968, 1.84870016923725555, 1.98581804497831516, 4.10446362983979540, 0.71734387468946914, 4.36803763631339592, 4.18804903479807233} zref := []float64{ 1.00000000000000022, 0.85303260971097450, -0.85020234203223444, -0.64630863726711674, -0.66211228030949409, 0.12515757728046226, 0.96725545921055622, -0.37698782755909727, 0.39233601196202428, 0.43202712322170089, 0.85303260971097450, 0.99999999999999956, -0.99998464240308171, -0.94953899113609297, -0.17372147858022127, 0.62451665675716073, 0.69265128272781040, -0.80493649049792482, 0.81469110300198433, 0.83917543899780545, -0.85020234203223444, -0.99998464240308171, 1.00000000000000089, 0.95121850038903877, 0.16840142730281188, -0.62872513058587987, -0.68874630140860726, 0.80812857148627615, -0.81781005005790097, -0.84210005596350634, -0.64630863726711674, -0.94953899113609297, 0.95121850038903877, 1.00000000000000000, -0.14392344681149352, -0.83796540396053343, -0.43147384025661650, 0.95042502132018603, -0.95546406276523821, -0.96741055070299309, -0.66211228030949409, -0.17372147858022127, 0.16840142730281188, -0.14392344681149352, 1.00000000000000067, 0.66064328265023031, -0.83063373455540346, -0.44450355615883147, 0.42954780821049410, 0.38980770530806869, 0.12515757728046226, 0.62451665675716073, -0.62872513058587987, -0.83796540396053343, 0.66064328265023031, 0.99999999999999978, -0.13074907207216546, -0.96611746250486030, 0.96169189357427165, 0.94884018282016180, 0.96725545921055622, 0.69265128272781040, -0.68874630140860726, -0.43147384025661650, -0.83063373455540346, -0.13074907207216546, 1.00000000000000044, -0.12956585354159514, 0.14603486320665809, 0.18898493355529175, -0.37698782755909727, -0.80493649049792482, 0.80812857148627615, 0.95042502132018603, -0.44450355615883147, -0.96611746250486030, -0.12956585354159514, 1.00000000000000067, -0.99986146360473316, -0.99818853872682889, 0.39233601196202428, 0.81469110300198433, -0.81781005005790097, -0.95546406276523821, 0.42954780821049410, 0.96169189357427165, 0.14603486320665809, -0.99986146360473316, 1.00000000000000022, 0.99905052020024310, 0.43202712322170089, 0.83917543899780545, -0.84210005596350634, -0.96741055070299309, 0.38980770530806869, 0.94884018282016180, 0.18898493355529175, -0.99818853872682889, 0.99905052020024310, 0.99999999999999933} data := matrix.FloatNew(10, 10, dataref) sol, err := mcsdp(data) if sol != nil && sol.Status == Optimal { fail := false x := sol.Result.At("x")[0] z := sol.Result.At("z")[0] //matrix.Reshape(z, data.Rows(), data.Rows()) t.Logf("x=\n%v\n", x.ToString("%.9f")) xe, _ := nrmError(matrix.FloatVector(xref), x) if xe > TOL { t.Logf("x differs [%.3e] from exepted too much.", xe) fail = true } ze, _ := nrmError(matrix.FloatVector(zref), z) if ze > TOL { t.Logf("z differs [%.3e] from exepted too much.", ze) fail = true } if fail { t.Fail() } } else { t.Logf("status: %v\n", err) t.Fail() } }
func updateScaling(W *sets.FloatMatrixSet, lmbda, s, z *matrix.FloatMatrix) (err error) { err = nil var stmp, ztmp *matrix.FloatMatrix /* Nonlinear and 'l' blocks d := d .* sqrt( s ./ z ) lmbda := lmbda .* sqrt(s) .* sqrt(z) */ mnl := 0 dnlset := W.At("dnl") dnliset := W.At("dnli") dset := W.At("d") diset := W.At("di") beta := W.At("beta")[0] if dnlset != nil && dnlset[0].NumElements() > 0 { mnl = dnlset[0].NumElements() } ml := dset[0].NumElements() m := mnl + ml //fmt.Printf("ml=%d, mnl=%d, m=%d'n", ml, mnl, m) stmp = matrix.FloatVector(s.FloatArray()[:m]) stmp.Apply(math.Sqrt) s.SetIndexesFromArray(stmp.FloatArray(), matrix.MakeIndexSet(0, m, 1)...) ztmp = matrix.FloatVector(z.FloatArray()[:m]) ztmp.Apply(math.Sqrt) z.SetIndexesFromArray(ztmp.FloatArray(), matrix.MakeIndexSet(0, m, 1)...) // d := d .* s .* z if len(dnlset) > 0 { blas.TbmvFloat(s, dnlset[0], &la_.IOpt{"n", mnl}, &la_.IOpt{"k", 0}, &la_.IOpt{"lda", 1}) blas.TbsvFloat(z, dnlset[0], &la_.IOpt{"n", mnl}, &la_.IOpt{"k", 0}, &la_.IOpt{"lda", 1}) //dnliset[0].Apply(dnlset[0], func(a float64)float64 { return 1.0/a}) //--dnliset[0] = matrix.Inv(dnlset[0]) matrix.Set(dnliset[0], dnlset[0]) dnliset[0].Inv() } blas.TbmvFloat(s, dset[0], &la_.IOpt{"n", ml}, &la_.IOpt{"k", 0}, &la_.IOpt{"lda", 1}, &la_.IOpt{"offseta", mnl}) blas.TbsvFloat(z, dset[0], &la_.IOpt{"n", ml}, &la_.IOpt{"k", 0}, &la_.IOpt{"lda", 1}, &la_.IOpt{"offseta", mnl}) //diset[0].Apply(dset[0], func(a float64)float64 { return 1.0/a}) //--diset[0] = matrix.Inv(dset[0]) matrix.Set(diset[0], dset[0]) diset[0].Inv() // lmbda := s .* z blas.CopyFloat(s, lmbda, &la_.IOpt{"n", m}) blas.TbmvFloat(z, lmbda, &la_.IOpt{"n", m}, &la_.IOpt{"k", 0}, &la_.IOpt{"lda", 1}) // 'q' blocks. // Let st and zt be the new variables in the old scaling: // // st = s_k, zt = z_k // // and a = sqrt(st' * J * st), b = sqrt(zt' * J * zt). // // 1. Compute the hyperbolic Householder transformation 2*q*q' - J // that maps st/a to zt/b. // // c = sqrt( (1 + st'*zt/(a*b)) / 2 ) // q = (st/a + J*zt/b) / (2*c). // // The new scaling point is // // wk := betak * sqrt(a/b) * (2*v[k]*v[k]' - J) * q // // with betak = W['beta'][k]. // // 3. The scaled variable: // // lambda_k0 = sqrt(a*b) * c // lambda_k1 = sqrt(a*b) * ( (2vk*vk' - J) * (-d*q + u/2) )_1 // // where // // u = st/a - J*zt/b // d = ( vk0 * (vk'*u) + u0/2 ) / (2*vk0 *(vk'*q) - q0 + 1). // // 4. Update scaling // // v[k] := wk^1/2 // = 1 / sqrt(2*(wk0 + 1)) * (wk + e). // beta[k] *= sqrt(a/b) ind := m for k, v := range W.At("v") { m = v.NumElements() // ln = sqrt( lambda_k' * J * lambda_k ) !! NOT USED!! jnrm2(lmbda, m, ind) // ?? NOT USED ?? // a = sqrt( sk' * J * sk ) = sqrt( st' * J * st ) // s := s / a = st / a aa := jnrm2(s, m, ind) blas.ScalFloat(s, 1.0/aa, &la_.IOpt{"n", m}, &la_.IOpt{"offset", ind}) // b = sqrt( zk' * J * zk ) = sqrt( zt' * J * zt ) // z := z / a = zt / b bb := jnrm2(z, m, ind) blas.ScalFloat(z, 1.0/bb, &la_.IOpt{"n", m}, &la_.IOpt{"offset", ind}) // c = sqrt( ( 1 + (st'*zt) / (a*b) ) / 2 ) cc := blas.DotFloat(s, z, &la_.IOpt{"offsetx", ind}, &la_.IOpt{"offsety", ind}, &la_.IOpt{"n", m}) cc = math.Sqrt((1.0 + cc) / 2.0) // vs = v' * st / a vs := blas.DotFloat(v, s, &la_.IOpt{"offsety", ind}, &la_.IOpt{"n", m}) // vz = v' * J *zt / b vz := jdot(v, z, m, 0, ind) // vq = v' * q where q = (st/a + J * zt/b) / (2 * c) vq := (vs + vz) / 2.0 / cc // vq = v' * q where q = (st/a + J * zt/b) / (2 * c) vu := vs - vz // lambda_k0 = c lmbda.SetIndex(ind, cc) // wk0 = 2 * vk0 * (vk' * q) - q0 wk0 := 2.0*v.GetIndex(0)*vq - (s.GetIndex(ind)+z.GetIndex(ind))/2.0/cc // d = (v[0] * (vk' * u) - u0/2) / (wk0 + 1) dd := (v.GetIndex(0)*vu - s.GetIndex(ind)/2.0 + z.GetIndex(ind)/2.0) / (wk0 + 1.0) // lambda_k1 = 2 * v_k1 * vk' * (-d*q + u/2) - d*q1 + u1/2 blas.CopyFloat(v, lmbda, &la_.IOpt{"offsetx", 1}, &la_.IOpt{"offsety", ind + 1}, &la_.IOpt{"n", m - 1}) blas.ScalFloat(lmbda, (2.0 * (-dd*vq + 0.5*vu)), &la_.IOpt{"offsetx", ind + 1}, &la_.IOpt{"offsety", ind + 1}, &la_.IOpt{"n", m - 1}) blas.AxpyFloat(s, lmbda, 0.5*(1.0-dd/cc), &la_.IOpt{"offsetx", ind + 1}, &la_.IOpt{"offsety", ind + 1}, &la_.IOpt{"n", m - 1}) blas.AxpyFloat(z, lmbda, 0.5*(1.0+dd/cc), &la_.IOpt{"offsetx", ind + 1}, &la_.IOpt{"offsety", ind + 1}, &la_.IOpt{"n", m - 1}) // Scale so that sqrt(lambda_k' * J * lambda_k) = sqrt(aa*bb). blas.ScalFloat(lmbda, math.Sqrt(aa*bb), &la_.IOpt{"offset", ind}, &la_.IOpt{"n", m}) // v := (2*v*v' - J) * q // = 2 * (v'*q) * v' - (J* st/a + zt/b) / (2*c) blas.ScalFloat(v, 2.0*vq) v.SetIndex(0, v.GetIndex(0)-(s.GetIndex(ind)/2.0/cc)) blas.AxpyFloat(s, v, 0.5/cc, &la_.IOpt{"offsetx", ind + 1}, &la_.IOpt{"offsety", 1}, &la_.IOpt{"n", m - 1}) blas.AxpyFloat(z, v, -0.5/cc, &la_.IOpt{"offsetx", ind}, &la_.IOpt{"n", m}) // v := v^{1/2} = 1/sqrt(2 * (v0 + 1)) * (v + e) v0 := v.GetIndex(0) + 1.0 v.SetIndex(0, v0) blas.ScalFloat(v, 1.0/math.Sqrt(2.0*v0)) // beta[k] *= ( aa / bb )**1/2 bk := beta.GetIndex(k) beta.SetIndex(k, bk*math.Sqrt(aa/bb)) ind += m } //fmt.Printf("-- end of q:\nz=\n%v\nlmbda=\n%v\n", z.ConvertToString(), lmbda.ConvertToString()) //fmt.Printf("beta=\n%v\n", beta.ConvertToString()) // 's' blocks // // Let st, zt be the updated variables in the old scaling: // // st = Ls * Ls', zt = Lz * Lz'. // // where Ls and Lz are the 's' components of s, z. // // 1. SVD Lz'*Ls = Uk * lambda_k^+ * Vk'. // // 2. New scaling is // // r[k] := r[k] * Ls * Vk * diag(lambda_k^+)^{-1/2} // rti[k] := r[k] * Lz * Uk * diag(lambda_k^+)^{-1/2}. // maxr := 0 for _, m := range W.At("r") { if m.Rows() > maxr { maxr = m.Rows() } } work := matrix.FloatZeros(maxr*maxr, 1) vlensum := 0 for _, m := range W.At("v") { vlensum += m.NumElements() } ind = mnl + ml + vlensum ind2 := ind ind3 := 0 rset := W.At("r") rtiset := W.At("rti") for k, _ := range rset { r := rset[k] rti := rtiset[k] m = r.Rows() //fmt.Printf("m=%d, r=\n%v\nrti=\n%v\n", m, r.ConvertToString(), rti.ConvertToString()) // r := r*sk = r*Ls blas.GemmFloat(r, s, work, 1.0, 0.0, &la_.IOpt{"m", m}, &la_.IOpt{"n", m}, &la_.IOpt{"k", m}, &la_.IOpt{"ldb", m}, &la_.IOpt{"ldc", m}, &la_.IOpt{"offsetb", ind2}) //fmt.Printf("1 work=\n%v\n", work.ConvertToString()) blas.CopyFloat(work, r, &la_.IOpt{"n", m * m}) // rti := rti*zk = rti*Lz blas.GemmFloat(rti, z, work, 1.0, 0.0, &la_.IOpt{"m", m}, &la_.IOpt{"n", m}, &la_.IOpt{"k", m}, &la_.IOpt{"ldb", m}, &la_.IOpt{"ldc", m}, &la_.IOpt{"offsetb", ind2}) //fmt.Printf("2 work=\n%v\n", work.ConvertToString()) blas.CopyFloat(work, rti, &la_.IOpt{"n", m * m}) // SVD Lz'*Ls = U * lmbds^+ * V'; store U in sk and V' in zk. ' blas.GemmFloat(z, s, work, 1.0, 0.0, la_.OptTransA, &la_.IOpt{"m", m}, &la_.IOpt{"n", m}, &la_.IOpt{"k", m}, &la_.IOpt{"lda", m}, &la_.IOpt{"ldb", m}, &la_.IOpt{"ldc", m}, &la_.IOpt{"offseta", ind2}, &la_.IOpt{"offsetb", ind2}) //fmt.Printf("3 work=\n%v\n", work.ConvertToString()) // U = s, Vt = z lapack.GesvdFloat(work, lmbda, s, z, la_.OptJobuAll, la_.OptJobvtAll, &la_.IOpt{"m", m}, &la_.IOpt{"n", m}, &la_.IOpt{"lda", m}, &la_.IOpt{"ldu", m}, &la_.IOpt{"ldvt", m}, &la_.IOpt{"offsets", ind}, &la_.IOpt{"offsetu", ind2}, &la_.IOpt{"offsetvt", ind2}) // r := r*V blas.GemmFloat(r, z, work, 1.0, 0.0, la_.OptTransB, &la_.IOpt{"m", m}, &la_.IOpt{"n", m}, &la_.IOpt{"k", m}, &la_.IOpt{"ldb", m}, &la_.IOpt{"ldc", m}, &la_.IOpt{"offsetb", ind2}) //fmt.Printf("4 work=\n%v\n", work.ConvertToString()) blas.CopyFloat(work, r, &la_.IOpt{"n", m * m}) // rti := rti*U blas.GemmFloat(rti, s, work, 1.0, 0.0, &la_.IOpt{"m", m}, &la_.IOpt{"n", m}, &la_.IOpt{"k", m}, &la_.IOpt{"ldb", m}, &la_.IOpt{"ldc", m}, &la_.IOpt{"offsetb", ind2}) //fmt.Printf("5 work=\n%v\n", work.ConvertToString()) blas.CopyFloat(work, rti, &la_.IOpt{"n", m * m}) for i := 0; i < m; i++ { a := 1.0 / math.Sqrt(lmbda.GetIndex(ind+i)) blas.ScalFloat(r, a, &la_.IOpt{"n", m}, &la_.IOpt{"offset", m * i}) blas.ScalFloat(rti, a, &la_.IOpt{"n", m}, &la_.IOpt{"offset", m * i}) } ind += m ind2 += m * m ind3 += m // !!NOT USED: ind3!! } //fmt.Printf("-- end of s:\nz=\n%v\nlmbda=\n%v\n", z.ConvertToString(), lmbda.ConvertToString()) return }
/* Evaluates x := H(lambda^{1/2}) * x (inverse is 'N') x := H(lambda^{-1/2}) * x (inverse is 'I'). H is the Hessian of the logarithmic barrier. */ func scale2(lmbda, x *matrix.FloatMatrix, dims *sets.DimensionSet, mnl int, inverse bool) (err error) { err = nil //var minor int = 0 //if ! checkpnt.MinorEmpty() { // minor = checkpnt.MinorTop() //} //fmt.Printf("\n%d.%04d scale2 x=\n%v\nlmbda=\n%v\n", checkpnt.Major(), minor, // x.ToString("%.17f"), lmbda.ToString("%.17f")) //if ! checkpnt.MinorEmpty() { // checkpnt.Check("000scale2", minor) //} // For the nonlinear and 'l' blocks, // // xk := xk ./ l (inverse is 'N') // xk := xk .* l (inverse is 'I') // // where l is lmbda[:mnl+dims['l']]. ind := mnl + dims.Sum("l") if !inverse { blas.TbsvFloat(lmbda, x, &la_.IOpt{"n", ind}, &la_.IOpt{"k", 0}, &la_.IOpt{"lda", 1}) } else { blas.TbmvFloat(lmbda, x, &la_.IOpt{"n", ind}, &la_.IOpt{"k", 0}, &la_.IOpt{"lda", 1}) } //if ! checkpnt.MinorEmpty() { // checkpnt.Check("010scale2", minor) //} // For 'q' blocks, if inverse is 'N', // // xk := 1/a * [ l'*J*xk; // xk[1:] - (xk[0] + l'*J*xk) / (l[0] + 1) * l[1:] ]. // // If inverse is 'I', // // xk := a * [ l'*xk; // xk[1:] + (xk[0] + l'*xk) / (l[0] + 1) * l[1:] ]. // // a = sqrt(lambda_k' * J * lambda_k), l = lambda_k / a. for _, m := range dims.At("q") { var lx, a, c, x0 float64 a = jnrm2(lmbda, m, ind) //&la_.IOpt{"n", m}, &la_.IOpt{"offset", ind}) if !inverse { lx = jdot(lmbda, x, m, ind, ind) //&la_.IOpt{"n", m}, &la_.IOpt{"offsetx", ind}, //&la_.IOpt{"offsety", ind}) lx /= a } else { lx = blas.DotFloat(lmbda, x, &la_.IOpt{"n", m}, &la_.IOpt{"offsetx", ind}, &la_.IOpt{"offsety", ind}) lx /= a } x0 = x.GetIndex(ind) x.SetIndex(ind, lx) c = (lx + x0) / (lmbda.GetIndex(ind)/a + 1.0) / a if !inverse { c *= -1.0 } blas.AxpyFloat(lmbda, x, c, &la_.IOpt{"n", m - 1}, &la_.IOpt{"offsetx", ind + 1}, &la_.IOpt{"offsety", ind + 1}) if !inverse { a = 1.0 / a } blas.ScalFloat(x, a, &la_.IOpt{"offset", ind}, &la_.IOpt{"n", m}) ind += m } //if ! checkpnt.MinorEmpty() { // checkpnt.Check("020scale2", minor) //} // For the 's' blocks, if inverse is 'N', // // xk := vec( diag(l)^{-1/2} * mat(xk) * diag(k)^{-1/2}). // // If inverse is true, // // xk := vec( diag(l)^{1/2} * mat(xk) * diag(k)^{1/2}). // // where l is kth block of lambda. // // We scale upper and lower triangular part of mat(xk) because the // inverse operation will be applied to nonsymmetric matrices. ind2 := ind sdims := dims.At("s") for k := 0; k < len(sdims); k++ { m := sdims[k] scaleF := func(v, x float64) float64 { return math.Sqrt(v) * math.Sqrt(x) } for j := 0; j < m; j++ { c := matrix.FloatVector(lmbda.FloatArray()[ind2 : ind2+m]) c.ApplyConst(lmbda.GetIndex(ind2+j), scaleF) if !inverse { blas.Tbsv(c, x, &la_.IOpt{"n", m}, &la_.IOpt{"k", 0}, &la_.IOpt{"lda", 1}, &la_.IOpt{"offsetx", ind + j*m}) } else { blas.Tbmv(c, x, &la_.IOpt{"n", m}, &la_.IOpt{"k", 0}, &la_.IOpt{"lda", 1}, &la_.IOpt{"offsetx", ind + j*m}) } } ind += m * m ind2 += m } //if ! checkpnt.MinorEmpty() { // checkpnt.Check("030scale2", minor) //} return }
// The product x := (y o x). If diag is 'D', the 's' part of y is // diagonal and only the diagonal is stored. func sprod(x, y *matrix.FloatMatrix, dims *sets.DimensionSet, mnl int, opts ...la_.Option) (err error) { err = nil diag := la_.GetStringOpt("diag", "N", opts...) // For the nonlinear and 'l' blocks: // // yk o xk = yk .* xk. ind := mnl + dims.At("l")[0] err = blas.Tbmv(y, x, &la_.IOpt{"n", ind}, &la_.IOpt{"k", 0}, &la_.IOpt{"lda", 1}) if err != nil { return } //fmt.Printf("Sprod l:x=\n%v\n", x) // For 'q' blocks: // // [ l0 l1' ] // yk o xk = [ ] * xk // [ l1 l0*I ] // // where yk = (l0, l1). for _, m := range dims.At("q") { dd := blas.DotFloat(x, y, &la_.IOpt{"offsetx", ind}, &la_.IOpt{"offsety", ind}, &la_.IOpt{"n", m}) //fmt.Printf("dd=%v\n", dd) alpha := y.GetIndex(ind) //fmt.Printf("scal=%v\n", alpha) blas.ScalFloat(x, alpha, &la_.IOpt{"offset", ind + 1}, &la_.IOpt{"n", m - 1}) alpha = x.GetIndex(ind) //fmt.Printf("axpy=%v\n", alpha) blas.AxpyFloat(y, x, alpha, &la_.IOpt{"offsetx", ind + 1}, &la_.IOpt{"offsety", ind + 1}, &la_.IOpt{"n", m - 1}) x.SetIndex(ind, dd) ind += m } //fmt.Printf("Sprod q :x=\n%v\n", x) // For the 's' blocks: // // yk o sk = .5 * ( Yk * mat(xk) + mat(xk) * Yk ) // // where Yk = mat(yk) if diag is 'N' and Yk = diag(yk) if diag is 'D'. if diag[0] == 'N' { // DEBUGGED maxm := maxdim(dims.At("s")) A := matrix.FloatZeros(maxm, maxm) for _, m := range dims.At("s") { blas.Copy(x, A, &la_.IOpt{"offsetx", ind}, &la_.IOpt{"n", m * m}) for i := 0; i < m-1; i++ { // i < m-1 --> i < m symm(A, m, 0) symm(y, m, ind) } err = blas.Syr2kFloat(A, y, x, 0.5, 0.0, &la_.IOpt{"n", m}, &la_.IOpt{"k", m}, &la_.IOpt{"lda", m}, &la_.IOpt{"ldb", m}, &la_.IOpt{"ldc", m}, &la_.IOpt{"offsetb", ind}, &la_.IOpt{"offsetc", ind}) if err != nil { return } ind += m * m } //fmt.Printf("Sprod diag=N s:x=\n%v\n", x) } else { ind2 := ind for _, m := range dims.At("s") { for i := 0; i < m; i++ { // original: u = 0.5 * ( y[ind2+i:ind2+m] + y[ind2+i] ) // creates matrix of elements: [ind2+i ... ind2+m] then // element wisely adds y[ind2+i] and scales by 0.5 iset := matrix.MakeIndexSet(ind2+i, ind2+m, 1) u := matrix.FloatVector(y.GetIndexes(iset...)) u.Add(y.GetIndex(ind2 + i)) u.Scale(0.5) err = blas.Tbmv(u, x, &la_.IOpt{"n", m - i}, &la_.IOpt{"k", 0}, &la_.IOpt{"lda", 1}, &la_.IOpt{"offsetx", ind + i*(m+1)}) if err != nil { return } } ind += m * m ind2 += m } //fmt.Printf("Sprod diag=T s:x=\n%v\n", x) } return }
func TestConeQp(t *testing.T) { adata := [][]float64{ []float64{0.3, -0.4, -0.2, -0.4, 1.3}, []float64{0.6, 1.2, -1.7, 0.3, -0.3}, []float64{-0.3, 0.0, 0.6, -1.2, -2.0}} // reference values from cvxopt coneqp.py xref := []float64{0.72558318685981904, 0.61806264311119252, 0.30253527966423444} sref := []float64{ 0.72558318685981904, 0.61806264311119263, 0.30253527966423449, 1.00000000000041678, -0.72558318686012169, -0.61806264311145032, -0.30253527966436067} zref := []float64{ 0.00000003332583626, 0.00000005116586239, 0.00000009993673262, 0.56869648433154019, 0.41264857754144563, 0.35149286573190930, 0.17201618570052318} A := matrix.FloatMatrixFromTable(adata, matrix.ColumnOrder) b := matrix.FloatVector([]float64{1.5, 0.0, -1.2, -0.7, 0.0}) _, n := A.Size() N := n + 1 + n h := matrix.FloatZeros(N, 1) h.SetIndex(n, 1.0) I0 := matrix.FloatDiagonal(n, -1.0) I1 := matrix.FloatIdentity(n) G, _ := matrix.FloatMatrixStacked(matrix.StackDown, I0, matrix.FloatZeros(1, n), I1) At := A.Transpose() P := matrix.Times(At, A) q := matrix.Times(At, b).Scale(-1.0) dims := sets.DSetNew("l", "q", "s") dims.Set("l", []int{n}) dims.Set("q", []int{n + 1}) var solopts SolverOptions solopts.MaxIter = 10 solopts.ShowProgress = false sol, err := ConeQp(P, q, G, h, nil, nil, dims, &solopts, nil) if err == nil { fail := false x := sol.Result.At("x")[0] s := sol.Result.At("s")[0] z := sol.Result.At("z")[0] t.Logf("Optimal\n") t.Logf("x=\n%v\n", x.ToString("%.9f")) t.Logf("s=\n%v\n", s.ToString("%.9f")) t.Logf("z=\n%v\n", z.ToString("%.9f")) xe, _ := nrmError(matrix.FloatVector(xref), x) if xe > TOL { t.Logf("x differs [%.3e] from exepted too much.", xe) fail = true } se, _ := nrmError(matrix.FloatVector(sref), s) if se > TOL { t.Logf("s differs [%.3e] from exepted too much.", se) fail = true } ze, _ := nrmError(matrix.FloatVector(zref), z) if ze > TOL { t.Logf("z differs [%.3e] from exepted too much.", ze) fail = true } if fail { t.Fail() } } }
// Solves a pair of primal and dual SOCPs // // minimize c'*x // subject to Gl*x + sl = hl // Gq[k]*x + sq[k] = hq[k], k = 0, ..., N-1 // A*x = b // sl >= 0, // sq[k] >= 0, k = 0, ..., N-1 // // maximize -hl'*z - sum_k hq[k]'*zq[k] - b'*y // subject to Gl'*zl + sum_k Gq[k]'*zq[k] + A'*y + c = 0 // zl >= 0, zq[k] >= 0, k = 0, ..., N-1. // // The inequalities sl >= 0 and zl >= 0 are elementwise vector // inequalities. The inequalities sq[k] >= 0, zq[k] >= 0 are second // order cone inequalities, i.e., equivalent to // // sq[k][0] >= || sq[k][1:] ||_2, zq[k][0] >= || zq[k][1:] ||_2. // func Socp(c, Gl, hl, A, b *matrix.FloatMatrix, Ghq *sets.FloatMatrixSet, solopts *SolverOptions, primalstart, dualstart *sets.FloatMatrixSet) (sol *Solution, err error) { if c == nil { err = errors.New("'c' must a column matrix") return } n := c.Rows() if n < 1 { err = errors.New("Number of variables must be at least 1") return } if Gl == nil { Gl = matrix.FloatZeros(0, n) } if Gl.Cols() != n { err = errors.New(fmt.Sprintf("'G' must be matrix with %d columns", n)) return } ml := Gl.Rows() if hl == nil { hl = matrix.FloatZeros(0, 1) } if !hl.SizeMatch(ml, 1) { err = errors.New(fmt.Sprintf("'hl' must be matrix of size (%d,1)", ml)) return } Gqset := Ghq.At("Gq") mq := make([]int, 0) for i, Gq := range Gqset { if Gq.Cols() != n { err = errors.New(fmt.Sprintf("'Gq' must be list of matrices with %d columns", n)) return } if Gq.Rows() == 0 { err = errors.New(fmt.Sprintf("the number of rows of 'Gq[%d]' is zero", i)) return } mq = append(mq, Gq.Rows()) } hqset := Ghq.At("hq") if len(Gqset) != len(hqset) { err = errors.New(fmt.Sprintf("'hq' must be a list of %d matrices", len(Gqset))) return } for i, hq := range hqset { if !hq.SizeMatch(Gqset[i].Rows(), 1) { s := fmt.Sprintf("hq[%d] has size (%d,%d). Expected size is (%d,1)", i, hq.Rows(), hq.Cols(), Gqset[i].Rows()) err = errors.New(s) return } } if A == nil { A = matrix.FloatZeros(0, n) } if A.Cols() != n { err = errors.New(fmt.Sprintf("'A' must be matrix with %d columns", n)) return } p := A.Rows() if b == nil { b = matrix.FloatZeros(0, 1) } if !b.SizeMatch(p, 1) { err = errors.New(fmt.Sprintf("'b' must be matrix of size (%d,1)", p)) return } dims := sets.NewDimensionSet("l", "q", "s") dims.Set("l", []int{ml}) dims.Set("q", mq) //N := dims.Sum("l", "q") hargs := make([]*matrix.FloatMatrix, 0, len(hqset)+1) hargs = append(hargs, hl) hargs = append(hargs, hqset...) h, indh := matrix.FloatMatrixStacked(matrix.StackDown, hargs...) Gargs := make([]*matrix.FloatMatrix, 0, len(Gqset)+1) Gargs = append(Gargs, Gl) Gargs = append(Gargs, Gqset...) G, indg := matrix.FloatMatrixStacked(matrix.StackDown, Gargs...) var pstart, dstart *sets.FloatMatrixSet = nil, nil if primalstart != nil { pstart = sets.NewFloatSet("x", "s") pstart.Set("x", primalstart.At("x")[0]) slset := primalstart.At("sl") margs := make([]*matrix.FloatMatrix, 0, len(slset)+1) margs = append(margs, primalstart.At("s")[0]) margs = append(margs, slset...) sl, _ := matrix.FloatMatrixStacked(matrix.StackDown, margs...) pstart.Set("s", sl) } if dualstart != nil { dstart = sets.NewFloatSet("y", "z") dstart.Set("y", dualstart.At("y")[0]) zlset := primalstart.At("zl") margs := make([]*matrix.FloatMatrix, 0, len(zlset)+1) margs = append(margs, dualstart.At("z")[0]) margs = append(margs, zlset...) zl, _ := matrix.FloatMatrixStacked(matrix.StackDown, margs...) dstart.Set("z", zl) } sol, err = ConeLp(c, G, h, A, b, dims, solopts, pstart, dstart) // unpack sol.Result if err == nil { s := sol.Result.At("s")[0] sl := matrix.FloatVector(s.FloatArray()[:ml]) sol.Result.Append("sl", sl) ind := ml for _, k := range indh[1:] { sk := matrix.FloatVector(s.FloatArray()[ind : ind+k]) sol.Result.Append("sq", sk) ind += k } z := sol.Result.At("z")[0] zl := matrix.FloatVector(z.FloatArray()[:ml]) sol.Result.Append("zl", zl) ind = ml for _, k := range indg[1:] { zk := matrix.FloatVector(z.FloatArray()[ind : ind+k]) sol.Result.Append("zq", zk) ind += k } } sol.Result.Remove("s") sol.Result.Remove("z") return }
// Solves a pair of primal and dual SDPs // // minimize c'*x // subject to Gl*x + sl = hl // mat(Gs[k]*x) + ss[k] = hs[k], k = 0, ..., N-1 // A*x = b // sl >= 0, ss[k] >= 0, k = 0, ..., N-1 // // maximize -hl'*z - sum_k trace(hs[k]*zs[k]) - b'*y // subject to Gl'*zl + sum_k Gs[k]'*vec(zs[k]) + A'*y + c = 0 // zl >= 0, zs[k] >= 0, k = 0, ..., N-1. // // The inequalities sl >= 0 and zl >= 0 are elementwise vector // inequalities. The inequalities ss[k] >= 0, zs[k] >= 0 are matrix // inequalities, i.e., the symmetric matrices ss[k] and zs[k] must be // positive semidefinite. mat(Gs[k]*x) is the symmetric matrix X with // X[:] = Gs[k]*x. For a symmetric matrix, zs[k], vec(zs[k]) is the // vector zs[k][:]. // func Sdp(c, Gl, hl, A, b *matrix.FloatMatrix, Ghs *sets.FloatMatrixSet, solopts *SolverOptions, primalstart, dualstart *sets.FloatMatrixSet) (sol *Solution, err error) { if c == nil { err = errors.New("'c' must a column matrix") return } n := c.Rows() if n < 1 { err = errors.New("Number of variables must be at least 1") return } if Gl == nil { Gl = matrix.FloatZeros(0, n) } if Gl.Cols() != n { err = errors.New(fmt.Sprintf("'G' must be matrix with %d columns", n)) return } ml := Gl.Rows() if hl == nil { hl = matrix.FloatZeros(0, 1) } if !hl.SizeMatch(ml, 1) { err = errors.New(fmt.Sprintf("'hl' must be matrix of size (%d,1)", ml)) return } Gsset := Ghs.At("Gs") ms := make([]int, 0) for i, Gs := range Gsset { if Gs.Cols() != n { err = errors.New(fmt.Sprintf("'Gs' must be list of matrices with %d columns", n)) return } sz := int(math.Sqrt(float64(Gs.Rows()))) if Gs.Rows() != sz*sz { err = errors.New(fmt.Sprintf("the squareroot of the number of rows of 'Gq[%d]' is not an integer", i)) return } ms = append(ms, sz) } hsset := Ghs.At("hs") if len(Gsset) != len(hsset) { err = errors.New(fmt.Sprintf("'hs' must be a list of %d matrices", len(Gsset))) return } for i, hs := range hsset { if !hs.SizeMatch(ms[i], ms[i]) { s := fmt.Sprintf("hq[%d] has size (%d,%d). Expected size is (%d,%d)", i, hs.Rows(), hs.Cols(), ms[i], ms[i]) err = errors.New(s) return } } if A == nil { A = matrix.FloatZeros(0, n) } if A.Cols() != n { err = errors.New(fmt.Sprintf("'A' must be matrix with %d columns", n)) return } p := A.Rows() if b == nil { b = matrix.FloatZeros(0, 1) } if !b.SizeMatch(p, 1) { err = errors.New(fmt.Sprintf("'b' must be matrix of size (%d,1)", p)) return } dims := sets.NewDimensionSet("l", "q", "s") dims.Set("l", []int{ml}) dims.Set("s", ms) N := dims.Sum("l") + dims.SumSquared("s") // Map hs matrices to h vector h := matrix.FloatZeros(N, 1) h.SetIndexesFromArray(hl.FloatArray()[:ml], matrix.MakeIndexSet(0, ml, 1)...) ind := ml for k, hs := range hsset { h.SetIndexesFromArray(hs.FloatArray(), matrix.MakeIndexSet(ind, ind+ms[k]*ms[k], 1)...) ind += ms[k] * ms[k] } Gargs := make([]*matrix.FloatMatrix, 0) Gargs = append(Gargs, Gl) Gargs = append(Gargs, Gsset...) G, sizeg := matrix.FloatMatrixStacked(matrix.StackDown, Gargs...) var pstart, dstart *sets.FloatMatrixSet = nil, nil if primalstart != nil { pstart = sets.NewFloatSet("x", "s") pstart.Set("x", primalstart.At("x")[0]) slset := primalstart.At("sl") margs := make([]*matrix.FloatMatrix, 0, len(slset)+1) margs = append(margs, primalstart.At("s")[0]) margs = append(margs, slset...) sl, _ := matrix.FloatMatrixStacked(matrix.StackDown, margs...) pstart.Set("s", sl) } if dualstart != nil { dstart = sets.NewFloatSet("y", "z") dstart.Set("y", dualstart.At("y")[0]) zlset := primalstart.At("zl") margs := make([]*matrix.FloatMatrix, 0, len(zlset)+1) margs = append(margs, dualstart.At("z")[0]) margs = append(margs, zlset...) zl, _ := matrix.FloatMatrixStacked(matrix.StackDown, margs...) dstart.Set("z", zl) } //fmt.Printf("h=\n%v\n", h.ToString("%.3f")) //fmt.Printf("G=\n%v\n", G.ToString("%.3f")) sol, err = ConeLp(c, G, h, A, b, dims, solopts, pstart, dstart) // unpack sol.Result if err == nil { s := sol.Result.At("s")[0] sl := matrix.FloatVector(s.FloatArray()[:ml]) sol.Result.Append("sl", sl) ind := ml for _, m := range ms { sk := matrix.FloatNew(m, m, s.FloatArray()[ind:ind+m*m]) sol.Result.Append("ss", sk) ind += m * m } z := sol.Result.At("z")[0] zl := matrix.FloatVector(s.FloatArray()[:ml]) sol.Result.Append("zl", zl) ind = ml for i, k := range sizeg[1:] { zk := matrix.FloatNew(ms[i], ms[i], z.FloatArray()[ind:ind+k]) sol.Result.Append("zs", zk) ind += k } } sol.Result.Remove("s") sol.Result.Remove("z") return }
/* Returns the Nesterov-Todd scaling W at points s and z, and stores the scaled variable in lmbda. W * z = W^{-T} * s = lmbda. W is a MatrixSet with entries: - W['dnl']: positive vector - W['dnli']: componentwise inverse of W['dnl'] - W['d']: positive vector - W['di']: componentwise inverse of W['d'] - W['v']: lists of 2nd order cone vectors with unit hyperbolic norms - W['beta']: list of positive numbers - W['r']: list of square matrices - W['rti']: list of square matrices. rti[k] is the inverse transpose of r[k]. */ func computeScaling(s, z, lmbda *matrix.FloatMatrix, dims *sets.DimensionSet, mnl int) (W *sets.FloatMatrixSet, err error) { /*DEBUGGED*/ err = nil W = sets.NewFloatSet("dnl", "dnli", "d", "di", "v", "beta", "r", "rti") // For the nonlinear block: // // W['dnl'] = sqrt( s[:mnl] ./ z[:mnl] ) // W['dnli'] = sqrt( z[:mnl] ./ s[:mnl] ) // lambda[:mnl] = sqrt( s[:mnl] .* z[:mnl] ) var stmp, ztmp, lmd *matrix.FloatMatrix if mnl > 0 { stmp = matrix.FloatVector(s.FloatArray()[:mnl]) ztmp = matrix.FloatVector(z.FloatArray()[:mnl]) //dnl := stmp.Div(ztmp) //dnl.Apply(dnl, math.Sqrt) dnl := matrix.Sqrt(matrix.Div(stmp, ztmp)) //dnli := dnl.Copy() //dnli.Apply(dnli, func(a float64)float64 { return 1.0/a }) dnli := matrix.Inv(dnl) W.Set("dnl", dnl) W.Set("dnli", dnli) //lmd = stmp.Mul(ztmp) //lmd.Apply(lmd, math.Sqrt) lmd = matrix.Sqrt(matrix.Mul(stmp, ztmp)) lmbda.SetIndexesFromArray(lmd.FloatArray(), matrix.MakeIndexSet(0, mnl, 1)...) } else { // set for empty matrices //W.Set("dnl", matrix.FloatZeros(0, 1)) //W.Set("dnli", matrix.FloatZeros(0, 1)) mnl = 0 } // For the 'l' block: // // W['d'] = sqrt( sk ./ zk ) // W['di'] = sqrt( zk ./ sk ) // lambdak = sqrt( sk .* zk ) // // where sk and zk are the first dims['l'] entries of s and z. // lambda_k is stored in the first dims['l'] positions of lmbda. m := dims.At("l")[0] //td := s.FloatArray() stmp = matrix.FloatVector(s.FloatArray()[mnl : mnl+m]) //zd := z.FloatArray() ztmp = matrix.FloatVector(z.FloatArray()[mnl : mnl+m]) //fmt.Printf(".Sqrt()=\n%v\n", matrix.Div(stmp, ztmp).Sqrt().ToString("%.17f")) //d := stmp.Div(ztmp) //d.Apply(d, math.Sqrt) d := matrix.Div(stmp, ztmp).Sqrt() //di := d.Copy() //di.Apply(di, func(a float64)float64 { return 1.0/a }) di := matrix.Inv(d) //fmt.Printf("d:\n%v\n", d) //fmt.Printf("di:\n%v\n", di) W.Set("d", d) W.Set("di", di) //lmd = stmp.Mul(ztmp) //lmd.Apply(lmd, math.Sqrt) lmd = matrix.Mul(stmp, ztmp).Sqrt() // lmd has indexes mnl:mnl+m and length of m lmbda.SetIndexesFromArray(lmd.FloatArray(), matrix.MakeIndexSet(mnl, mnl+m, 1)...) //fmt.Printf("after l:\n%v\n", lmbda) /* For the 'q' blocks, compute lists 'v', 'beta'. The vector v[k] has unit hyperbolic norm: (sqrt( v[k]' * J * v[k] ) = 1 with J = [1, 0; 0, -I]). beta[k] is a positive scalar. The hyperbolic Householder matrix H = 2*v[k]*v[k]' - J defined by v[k] satisfies (beta[k] * H) * zk = (beta[k] * H) \ sk = lambda_k where sk = s[indq[k]:indq[k+1]], zk = z[indq[k]:indq[k+1]]. lambda_k is stored in lmbda[indq[k]:indq[k+1]]. */ ind := mnl + dims.At("l")[0] var beta *matrix.FloatMatrix for _, k := range dims.At("q") { W.Append("v", matrix.FloatZeros(k, 1)) } beta = matrix.FloatZeros(len(dims.At("q")), 1) W.Set("beta", beta) vset := W.At("v") for k, m := range dims.At("q") { v := vset[k] // a = sqrt( sk' * J * sk ) where J = [1, 0; 0, -I] aa := jnrm2(s, m, ind) // b = sqrt( zk' * J * zk ) bb := jnrm2(z, m, ind) // beta[k] = ( a / b )**1/2 beta.SetIndex(k, math.Sqrt(aa/bb)) // c = sqrt( (sk/a)' * (zk/b) + 1 ) / sqrt(2) c0 := blas.DotFloat(s, z, &la_.IOpt{"n", m}, &la_.IOpt{"offsetx", ind}, &la_.IOpt{"offsety", ind}) cc := math.Sqrt((c0/aa/bb + 1.0) / 2.0) // vk = 1/(2*c) * ( (sk/a) + J * (zk/b) ) blas.CopyFloat(z, v, &la_.IOpt{"offsetx", ind}, &la_.IOpt{"n", m}) blas.ScalFloat(v, -1.0/bb) v.SetIndex(0, -1.0*v.GetIndex(0)) blas.AxpyFloat(s, v, 1.0/aa, &la_.IOpt{"offsetx", ind}, &la_.IOpt{"n", m}) blas.ScalFloat(v, 1.0/2.0/cc) // v[k] = 1/sqrt(2*(vk0 + 1)) * ( vk + e ), e = [1; 0] v.SetIndex(0, v.GetIndex(0)+1.0) blas.ScalFloat(v, (1.0 / math.Sqrt(2.0*v.GetIndex(0)))) /* To get the scaled variable lambda_k d = sk0/a + zk0/b + 2*c lambda_k = [ c; (c + zk0/b)/d * sk1/a + (c + sk0/a)/d * zk1/b ] lambda_k *= sqrt(a * b) */ lmbda.SetIndex(ind, cc) dd := 2*cc + s.GetIndex(ind)/aa + z.GetIndex(ind)/bb blas.CopyFloat(s, lmbda, &la_.IOpt{"offsetx", ind + 1}, &la_.IOpt{"offsety", ind + 1}, &la_.IOpt{"n", m - 1}) zz := (cc + z.GetIndex(ind)/bb) / dd / aa ss := (cc + s.GetIndex(ind)/aa) / dd / bb blas.ScalFloat(lmbda, zz, &la_.IOpt{"offset", ind + 1}, &la_.IOpt{"n", m - 1}) blas.AxpyFloat(z, lmbda, ss, &la_.IOpt{"offsetx", ind + 1}, &la_.IOpt{"offsety", ind + 1}, &la_.IOpt{"n", m - 1}) blas.ScalFloat(lmbda, math.Sqrt(aa*bb), &la_.IOpt{"offset", ind}, &la_.IOpt{"n", m}) ind += m //fmt.Printf("after q[%d]:\n%v\n", k, lmbda) } /* For the 's' blocks: compute two lists 'r' and 'rti'. r[k]' * sk^{-1} * r[k] = diag(lambda_k)^{-1} r[k]' * zk * r[k] = diag(lambda_k) where sk and zk are the entries inds[k] : inds[k+1] of s and z, reshaped into symmetric matrices. rti[k] is the inverse of r[k]', so rti[k]' * sk * rti[k] = diag(lambda_k)^{-1} rti[k]' * zk^{-1} * rti[k] = diag(lambda_k). The vectors lambda_k are stored in lmbda[ dims['l'] + sum(dims['q']) : -1 ] */ for _, k := range dims.At("s") { W.Append("r", matrix.FloatZeros(k, k)) W.Append("rti", matrix.FloatZeros(k, k)) } maxs := maxdim(dims.At("s")) work := matrix.FloatZeros(maxs*maxs, 1) Ls := matrix.FloatZeros(maxs*maxs, 1) Lz := matrix.FloatZeros(maxs*maxs, 1) ind2 := ind for k, m := range dims.At("s") { r := W.At("r")[k] rti := W.At("rti")[k] // Factor sk = Ls*Ls'; store Ls in ds[inds[k]:inds[k+1]]. blas.CopyFloat(s, Ls, &la_.IOpt{"offsetx", ind2}, &la_.IOpt{"n", m * m}) lapack.PotrfFloat(Ls, &la_.IOpt{"n", m}, &la_.IOpt{"lda", m}) // Factor zs[k] = Lz*Lz'; store Lz in dz[inds[k]:inds[k+1]]. blas.CopyFloat(z, Lz, &la_.IOpt{"offsetx", ind2}, &la_.IOpt{"n", m * m}) lapack.PotrfFloat(Lz, &la_.IOpt{"n", m}, &la_.IOpt{"lda", m}) // SVD Lz'*Ls = U*diag(lambda_k)*V'. Keep U in work. for i := 0; i < m; i++ { blas.ScalFloat(Ls, 0.0, &la_.IOpt{"offset", i * m}, &la_.IOpt{"n", i}) } blas.CopyFloat(Ls, work, &la_.IOpt{"n", m * m}) blas.TrmmFloat(Lz, work, 1.0, la_.OptTransA, &la_.IOpt{"lda", m}, &la_.IOpt{"ldb", m}, &la_.IOpt{"n", m}, &la_.IOpt{"m", m}) lapack.GesvdFloat(work, lmbda, nil, nil, la_.OptJobuO, &la_.IOpt{"lda", m}, &la_.IOpt{"offsetS", ind}, &la_.IOpt{"n", m}, &la_.IOpt{"m", m}) // r = Lz^{-T} * U blas.CopyFloat(work, r, &la_.IOpt{"n", m * m}) blas.TrsmFloat(Lz, r, 1.0, la_.OptTransA, &la_.IOpt{"lda", m}, &la_.IOpt{"n", m}, &la_.IOpt{"m", m}) // rti = Lz * U blas.CopyFloat(work, rti, &la_.IOpt{"n", m * m}) blas.TrmmFloat(Lz, rti, 1.0, &la_.IOpt{"lda", m}, &la_.IOpt{"n", m}, &la_.IOpt{"m", m}) // r := r * diag(sqrt(lambda_k)) // rti := rti * diag(1 ./ sqrt(lambda_k)) for i := 0; i < m; i++ { a := math.Sqrt(lmbda.GetIndex(ind + i)) blas.ScalFloat(r, a, &la_.IOpt{"offset", m * i}, &la_.IOpt{"n", m}) blas.ScalFloat(rti, 1.0/a, &la_.IOpt{"offset", m * i}, &la_.IOpt{"n", m}) } ind += m ind2 += m * m } return }