func kktChol2(G *matrix.FloatMatrix, dims *sets.DimensionSet, A *matrix.FloatMatrix, mnl int) (kktFactor, error) { if len(dims.At("q")) > 0 || len(dims.At("s")) > 0 { return nil, errors.New("'chol2' solver only for problems with no second-order or " + "semidefinite cone constraints") } p, n := A.Size() ml := dims.At("l")[0] F := &chol2Data{firstcall: true, singular: false, A: A, G: G, dims: dims} factor := func(W *sets.FloatMatrixSet, H, Df *matrix.FloatMatrix) (KKTFunc, error) { var err error = nil minor := 0 if !checkpnt.MinorEmpty() { minor = checkpnt.MinorTop() } if F.firstcall { F.Gs = matrix.FloatZeros(F.G.Size()) if mnl > 0 { F.Dfs = matrix.FloatZeros(Df.Size()) } F.S = matrix.FloatZeros(n, n) F.K = matrix.FloatZeros(p, p) checkpnt.AddMatrixVar("Gs", F.Gs) checkpnt.AddMatrixVar("Dfs", F.Dfs) checkpnt.AddMatrixVar("S", F.S) checkpnt.AddMatrixVar("K", F.K) } if mnl > 0 { dnli := matrix.FloatZeros(mnl, mnl) dnli.SetIndexesFromArray(W.At("dnli")[0].FloatArray(), matrix.DiagonalIndexes(dnli)...) blas.GemmFloat(dnli, Df, F.Dfs, 1.0, 0.0) } checkpnt.Check("02factor_chol2", minor) di := matrix.FloatZeros(ml, ml) di.SetIndexesFromArray(W.At("di")[0].FloatArray(), matrix.DiagonalIndexes(di)...) err = blas.GemmFloat(di, G, F.Gs, 1.0, 0.0) checkpnt.Check("06factor_chol2", minor) if F.firstcall { blas.SyrkFloat(F.Gs, F.S, 1.0, 0.0, la.OptTrans) if mnl > 0 { blas.SyrkFloat(F.Dfs, F.S, 1.0, 1.0, la.OptTrans) } if H != nil { F.S.Plus(H) } checkpnt.Check("10factor_chol2", minor) err = lapack.Potrf(F.S) if err != nil { err = nil // reset error F.singular = true // original code recreates F.S as dense if it is sparse and // A is dense, we don't do it as currently no sparse matrices //F.S = matrix.FloatZeros(n, n) //checkpnt.AddMatrixVar("S", F.S) blas.SyrkFloat(F.Gs, F.S, 1.0, 0.0, la.OptTrans) if mnl > 0 { blas.SyrkFloat(F.Dfs, F.S, 1.0, 1.0, la.OptTrans) } checkpnt.Check("14factor_chol2", minor) blas.SyrkFloat(F.A, F.S, 1.0, 1.0, la.OptTrans) if H != nil { F.S.Plus(H) } lapack.Potrf(F.S) } F.firstcall = false checkpnt.Check("20factor_chol2", minor) } else { blas.SyrkFloat(F.Gs, F.S, 1.0, 0.0, la.OptTrans) if mnl > 0 { blas.SyrkFloat(F.Dfs, F.S, 1.0, 1.0, la.OptTrans) } if H != nil { F.S.Plus(H) } checkpnt.Check("40factor_chol2", minor) if F.singular { blas.SyrkFloat(F.A, F.S, 1.0, 1.0, la.OptTrans) } lapack.Potrf(F.S) checkpnt.Check("50factor_chol2", minor) } // Asct := L^{-1}*A'. Factor K = Asct'*Asct. Asct := F.A.Transpose() blas.TrsmFloat(F.S, Asct, 1.0) blas.SyrkFloat(Asct, F.K, 1.0, 0.0, la.OptTrans) lapack.Potrf(F.K) checkpnt.Check("90factor_chol2", minor) solve := func(x, y, z *matrix.FloatMatrix) (err error) { // Solve // // [ H A' GG'*W^{-1} ] [ ux ] [ bx ] // [ A 0 0 ] * [ uy ] = [ by ] // [ W^{-T}*GG 0 -I ] [ W*uz ] [ W^{-T}*bz ] // // and return ux, uy, W*uz. // // If not F['singular']: // // K*uy = A * S^{-1} * ( bx + GG'*W^{-1}*W^{-T}*bz ) - by // S*ux = bx + GG'*W^{-1}*W^{-T}*bz - A'*uy // W*uz = W^{-T} * ( GG*ux - bz ). // // If F['singular']: // // K*uy = A * S^{-1} * ( bx + GG'*W^{-1}*W^{-T}*bz + A'*by ) // - by // S*ux = bx + GG'*W^{-1}*W^{-T}*bz + A'*by - A'*y. // W*uz = W^{-T} * ( GG*ux - bz ). minor := 0 if !checkpnt.MinorEmpty() { minor = checkpnt.MinorTop() } // z := W^{-1} * z = W^{-1} * bz scale(z, W, true, true) checkpnt.Check("10solve_chol2", minor) // If not F['singular']: // x := L^{-1} * P * (x + GGs'*z) // = L^{-1} * P * (x + GG'*W^{-1}*W^{-T}*bz) // // If F['singular']: // x := L^{-1} * P * (x + GGs'*z + A'*y)) // = L^{-1} * P * (x + GG'*W^{-1}*W^{-T}*bz + A'*y) if mnl > 0 { blas.GemvFloat(F.Dfs, z, x, 1.0, 1.0, la.OptTrans) } blas.GemvFloat(F.Gs, z, x, 1.0, 1.0, la.OptTrans, &la.IOpt{"offsetx", mnl}) //checkpnt.Check("20solve_chol2", minor) if F.singular { blas.GemvFloat(F.A, y, x, 1.0, 1.0, la.OptTrans) } checkpnt.Check("30solve_chol2", minor) blas.TrsvFloat(F.S, x) //checkpnt.Check("50solve_chol2", minor) // y := K^{-1} * (Asc*x - y) // = K^{-1} * (A * S^{-1} * (bx + GG'*W^{-1}*W^{-T}*bz) - by) // (if not F['singular']) // = K^{-1} * (A * S^{-1} * (bx + GG'*W^{-1}*W^{-T}*bz + // A'*by) - by) // (if F['singular']). blas.GemvFloat(Asct, x, y, 1.0, -1.0, la.OptTrans) //checkpnt.Check("55solve_chol2", minor) lapack.Potrs(F.K, y) //checkpnt.Check("60solve_chol2", minor) // x := P' * L^{-T} * (x - Asc'*y) // = S^{-1} * (bx + GG'*W^{-1}*W^{-T}*bz - A'*y) // (if not F['singular']) // = S^{-1} * (bx + GG'*W^{-1}*W^{-T}*bz + A'*by - A'*y) // (if F['singular']) blas.GemvFloat(Asct, y, x, -1.0, 1.0) blas.TrsvFloat(F.S, x, la.OptTrans) checkpnt.Check("70solve_chol2", minor) // W*z := GGs*x - z = W^{-T} * (GG*x - bz) if mnl > 0 { blas.GemvFloat(F.Dfs, x, z, 1.0, -1.0) } blas.GemvFloat(F.Gs, x, z, 1.0, -1.0, &la.IOpt{"offsety", mnl}) checkpnt.Check("90solve_chol2", minor) return nil } return solve, err } return factor, nil }
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) }
// Solution of KKT equations by reduction to a 2 x 2 system, a QR // factorization to eliminate the equality constraints, and a dense // Cholesky factorization of order n-p. // // Computes the QR factorization // // A' = [Q1, Q2] * [R; 0] // // and returns a function that (1) computes the Cholesky factorization // // Q_2^T * (H + GG^T * W^{-1} * W^{-T} * GG) * Q2 = L * L^T, // // given H, Df, W, where GG = [Df; G], and (2) returns a function for // solving // // [ H A' GG' ] [ ux ] [ bx ] // [ A 0 0 ] * [ uy ] = [ by ]. // [ GG 0 -W'*W ] [ uz ] [ bz ] // // H is n x n, A is p x n, Df is mnl x n, G is N x n where // N = dims['l'] + sum(dims['q']) + sum( k**2 for k in dims['s'] ). // func kktChol(G *matrix.FloatMatrix, dims *sets.DimensionSet, A *matrix.FloatMatrix, mnl int) (kktFactor, error) { p, n := A.Size() cdim := mnl + dims.Sum("l", "q") + dims.SumSquared("s") cdim_pckd := mnl + dims.Sum("l", "q") + dims.SumPacked("s") QA := A.Transpose() tauA := matrix.FloatZeros(p, 1) lapack.Geqrf(QA, tauA) Gs := matrix.FloatZeros(cdim, n) K := matrix.FloatZeros(n, n) bzp := matrix.FloatZeros(cdim_pckd, 1) yy := matrix.FloatZeros(p, 1) checkpnt.AddMatrixVar("tauA", tauA) checkpnt.AddMatrixVar("Gs", Gs) checkpnt.AddMatrixVar("K", K) factor := func(W *sets.FloatMatrixSet, H, Df *matrix.FloatMatrix) (KKTFunc, error) { // Compute // // K = [Q1, Q2]' * (H + GG' * W^{-1} * W^{-T} * GG) * [Q1, Q2] // // and take the Cholesky factorization of the 2,2 block // // Q_2' * (H + GG^T * W^{-1} * W^{-T} * GG) * Q2. var err error = nil minor := 0 if !checkpnt.MinorEmpty() { minor = checkpnt.MinorTop() } // Gs = W^{-T} * GG in packed storage. if mnl > 0 { Gs.SetSubMatrix(0, 0, Df) } Gs.SetSubMatrix(mnl, 0, G) checkpnt.Check("00factor_chol", minor) scale(Gs, W, true, true) pack2(Gs, dims, mnl) //checkpnt.Check("10factor_chol", minor) // K = [Q1, Q2]' * (H + Gs' * Gs) * [Q1, Q2]. blas.SyrkFloat(Gs, K, 1.0, 0.0, la.OptTrans, &la.IOpt{"k", cdim_pckd}) if H != nil { K.SetSubMatrix(0, 0, matrix.Plus(H, K.GetSubMatrix(0, 0, H.Rows(), H.Cols()))) } //checkpnt.Check("20factor_chol", minor) symm(K, n, 0) lapack.Ormqr(QA, tauA, K, la.OptLeft, la.OptTrans) lapack.Ormqr(QA, tauA, K, la.OptRight) //checkpnt.Check("30factor_chol", minor) // Cholesky factorization of 2,2 block of K. lapack.Potrf(K, &la.IOpt{"n", n - p}, &la.IOpt{"offseta", p * (n + 1)}) checkpnt.Check("40factor_chol", minor) solve := func(x, y, z *matrix.FloatMatrix) (err error) { // Solve // // [ 0 A' GG'*W^{-1} ] [ ux ] [ bx ] // [ A 0 0 ] * [ uy ] = [ by ] // [ W^{-T}*GG 0 -I ] [ W*uz ] [ W^{-T}*bz ] // // and return ux, uy, W*uz. // // On entry, x, y, z contain bx, by, bz. On exit, they contain // the solution ux, uy, W*uz. // // If we change variables ux = Q1*v + Q2*w, the system becomes // // [ K11 K12 R ] [ v ] [Q1'*(bx+GG'*W^{-1}*W^{-T}*bz)] // [ K21 K22 0 ] * [ w ] = [Q2'*(bx+GG'*W^{-1}*W^{-T}*bz)] // [ R^T 0 0 ] [ uy ] [by ] // // W*uz = W^{-T} * ( GG*ux - bz ). minor := 0 if !checkpnt.MinorEmpty() { minor = checkpnt.MinorTop() } // bzp := W^{-T} * bz in packed storage scale(z, W, true, true) pack(z, bzp, dims, &la.IOpt{"mnl", mnl}) // x := [Q1, Q2]' * (x + Gs' * bzp) // = [Q1, Q2]' * (bx + Gs' * W^{-T} * bz) blas.GemvFloat(Gs, bzp, x, 1.0, 1.0, la.OptTrans, &la.IOpt{"m", cdim_pckd}) lapack.Ormqr(QA, tauA, x, la.OptLeft, la.OptTrans) // y := x[:p] // = Q1' * (bx + Gs' * W^{-T} * bz) blas.Copy(y, yy) blas.Copy(x, y, &la.IOpt{"n", p}) // x[:p] := v = R^{-T} * by blas.Copy(yy, x) lapack.Trtrs(QA, x, la.OptUpper, la.OptTrans, &la.IOpt{"n", p}) // x[p:] := K22^{-1} * (x[p:] - K21*x[:p]) // = K22^{-1} * (Q2' * (bx + Gs' * W^{-T} * bz) - K21*v) blas.GemvFloat(K, x, x, -1.0, 1.0, &la.IOpt{"m", n - p}, &la.IOpt{"n", p}, &la.IOpt{"offseta", p}, &la.IOpt{"offsety", p}) lapack.Potrs(K, x, &la.IOpt{"n", n - p}, &la.IOpt{"offseta", p * (n + 1)}, &la.IOpt{"offsetb", p}) // y := y - [K11, K12] * x // = Q1' * (bx + Gs' * W^{-T} * bz) - K11*v - K12*w blas.GemvFloat(K, x, y, -1.0, 1.0, &la.IOpt{"m", p}, &la.IOpt{"n", n}) // y := R^{-1}*y // = R^{-1} * (Q1' * (bx + Gs' * W^{-T} * bz) - K11*v // - K12*w) lapack.Trtrs(QA, y, la.OptUpper, &la.IOpt{"n", p}) // x := [Q1, Q2] * x lapack.Ormqr(QA, tauA, x, la.OptLeft) // bzp := Gs * x - bzp. // = W^{-T} * ( GG*ux - bz ) in packed storage. // Unpack and copy to z. blas.GemvFloat(Gs, x, bzp, 1.0, -1.0, &la.IOpt{"m", cdim_pckd}) unpack(bzp, z, dims, &la.IOpt{"mnl", mnl}) checkpnt.Check("90solve_chol", minor) return nil } return solve, err } return factor, nil }