func mcsdp(w *matrix.FloatMatrix) (*Solution, error) { // // Returns solution x, z to // // (primal) minimize sum(x) // subject to w + diag(x) >= 0 // // (dual) maximize -tr(w*z) // subject to diag(z) = 1 // z >= 0. // n := w.Rows() G := &matrixFs{n} cngrnc := func(r, x *matrix.FloatMatrix, alpha float64) (err error) { // Congruence transformation // // x := alpha * r'*x*r. // // r and x are square matrices. // err = nil // tx = matrix(x, (n,n)) is copying and reshaping // scale diagonal of x by 1/2, (x is (n,n)) tx := x.Copy() matrix.Reshape(tx, n, n) tx.Diag().Scale(0.5) // a := tril(x)*r // (python: a = +r is really making a copy of r) a := r.Copy() err = blas.TrmmFloat(tx, a, 1.0, linalg.OptLeft) // x := alpha*(a*r' + r*a') err = blas.Syr2kFloat(r, a, tx, alpha, 0.0, linalg.OptTrans) // x[:] = tx[:] tx.CopyTo(x) return } Fkkt := func(W *sets.FloatMatrixSet) (KKTFunc, error) { // Solve // -diag(z) = bx // -diag(x) - inv(rti*rti') * z * inv(rti*rti') = bs // // On entry, x and z contain bx and bs. // On exit, they contain the solution, with z scaled // (inv(rti)'*z*inv(rti) is returned instead of z). // // We first solve // // ((rti*rti') .* (rti*rti')) * x = bx - diag(t*bs*t) // // and take z = -rti' * (diag(x) + bs) * rti. var err error = nil rti := W.At("rti")[0] // t = rti*rti' as a nonsymmetric matrix. t := matrix.FloatZeros(n, n) err = blas.GemmFloat(rti, rti, t, 1.0, 0.0, linalg.OptTransB) if err != nil { return nil, err } // Cholesky factorization of tsq = t.*t. tsq := matrix.Mul(t, t) err = lapack.Potrf(tsq) if err != nil { return nil, err } f := func(x, y, z *matrix.FloatMatrix) (err error) { // tbst := t * zs * t = t * bs * t tbst := z.Copy() matrix.Reshape(tbst, n, n) cngrnc(t, tbst, 1.0) // x := x - diag(tbst) = bx - diag(rti*rti' * bs * rti*rti') diag := tbst.Diag().Transpose() x.Minus(diag) // x := (t.*t)^{-1} * x = (t.*t)^{-1} * (bx - diag(t*bs*t)) err = lapack.Potrs(tsq, x) // z := z + diag(x) = bs + diag(x) // z, x are really column vectors here z.AddIndexes(matrix.MakeIndexSet(0, n*n, n+1), x.FloatArray()) // z := -rti' * z * rti = -rti' * (diag(x) + bs) * rti cngrnc(rti, z, -1.0) return nil } return f, nil } c := matrix.FloatWithValue(n, 1, 1.0) // initial feasible x: x = 1.0 - min(lmbda(w)) lmbda := matrix.FloatZeros(n, 1) wp := w.Copy() lapack.Syevx(wp, lmbda, nil, 0.0, nil, []int{1, 1}, linalg.OptRangeInt) x0 := matrix.FloatZeros(n, 1).Add(-lmbda.GetAt(0, 0) + 1.0) s0 := w.Copy() s0.Diag().Plus(x0.Transpose()) matrix.Reshape(s0, n*n, 1) // initial feasible z is identity z0 := matrix.FloatIdentity(n) matrix.Reshape(z0, n*n, 1) dims := sets.DSetNew("l", "q", "s") dims.Set("s", []int{n}) primalstart := sets.FloatSetNew("x", "s") dualstart := sets.FloatSetNew("z") primalstart.Set("x", x0) primalstart.Set("s", s0) dualstart.Set("z", z0) var solopts SolverOptions solopts.MaxIter = 30 solopts.ShowProgress = false h := w.Copy() matrix.Reshape(h, h.NumElements(), 1) return ConeLpCustomMatrix(c, G, h, nil, nil, dims, Fkkt, &solopts, primalstart, dualstart) }
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 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() } } }