// 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 }
func coneqp_solver(P MatrixVarP, q MatrixVariable, G MatrixVarG, h *matrix.FloatMatrix, A MatrixVarA, b MatrixVariable, dims *sets.DimensionSet, kktsolver KKTConeSolverVar, solopts *SolverOptions, initvals *sets.FloatMatrixSet) (sol *Solution, err error) { err = nil EXPON := 3 STEP := 0.99 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} //var kktsolver func(*sets.FloatMatrixSet)(KKTFunc, error) = nil var refinement int var correction bool = true 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.MaxIter > 0 { maxIter = solopts.MaxIter } if q == nil { err = errors.New("'q' must be non-nil MatrixVariable with one column") return } if h == nil { h = matrix.FloatZeros(0, 1) } if h.Cols() != 1 { err = errors.New("'h' must be non-nil matrix with one column") return } if dims == nil { dims = sets.NewDimensionSet("l", "q", "s") dims.Set("l", []int{h.Rows()}) } err = checkConeQpDimensions(dims) if err != nil { return } cdim := dims.Sum("l", "q") + dims.SumSquared("s") //cdim_pckd := dims.Sum("l", "q") + dims.SumPacked("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 } // Data for kth 'q' constraint are found in rows indq[k]:indq[k+1] of G. indq := make([]int, 0) indq = append(indq, dims.At("l")[0]) for _, k := range dims.At("q") { indq = append(indq, indq[len(indq)-1]+k) } // Data for kth 's' constraint are found in rows inds[k]:inds[k+1] of G. inds := make([]int, 0) inds = append(inds, indq[len(indq)-1]) for _, k := range dims.At("s") { inds = append(inds, inds[len(inds)-1]+k*k) } if P == nil { err = errors.New("'P' must be non-nil MatrixVarP interface.") return } fP := func(u, v MatrixVariable, alpha, beta float64) error { return P.Pf(u, v, alpha, beta) } 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 fA := func(x, y MatrixVariable, alpha, beta float64, trans la.Option) error { return A.Af(x, y, alpha, beta, trans) } // Check b and set defaults if it is nil if b == nil { err = errors.New("'b' must be non-nil MatrixVariable interface.") return } // kktsolver(W) returns a routine for solving 3x3 block KKT system // // [ 0 A' G'*W^{-1} ] [ ux ] [ bx ] // [ A 0 0 ] [ uy ] = [ by ]. // [ G 0 -W' ] [ uz ] [ bz ] if kktsolver == nil { err = errors.New("nil kktsolver not allowed.") return } ws3 := matrix.FloatZeros(cdim, 1) wz3 := matrix.FloatZeros(cdim, 1) checkpnt.AddMatrixVar("ws3", ws3) checkpnt.AddMatrixVar("wz3", wz3) // res := func(ux, uy MatrixVariable, uz, us *matrix.FloatMatrix, vx, vy MatrixVariable, vz, vs *matrix.FloatMatrix, W *sets.FloatMatrixSet, lmbda *matrix.FloatMatrix) (err error) { // Evaluates residual in Newton equations: // // [ vx ] [ vx ] [ 0 ] [ P A' G' ] [ ux ] // [ vy ] := [ vy ] - [ 0 ] - [ A 0 0 ] * [ uy ] // [ vz ] [ vz ] [ W'*us ] [ G 0 0 ] [ W^{-1}*uz ] // // vs := vs - lmbda o (uz + us). // vx := vx - P*ux - A'*uy - G'*W^{-1}*uz minor := checkpnt.MinorTop() checkpnt.Check("00res", minor) fP(ux, vx, -1.0, 1.0) fA(uy, vx, -1.0, 1.0, la.OptTrans) blas.Copy(uz, wz3) scale(wz3, W, true, false) fG(&matrixVar{wz3}, vx, -1.0, 1.0, la.OptTrans) // vy := vy - A*ux fA(ux, vy, -1.0, 1.0, la.OptNoTrans) checkpnt.Check("50res", minor) // vz := vz - G*ux - W'*us fG(ux, &matrixVar{vz}, -1.0, 1.0, la.OptNoTrans) blas.Copy(us, ws3) scale(ws3, W, true, false) blas.AxpyFloat(ws3, vz, -1.0) // vs := vs - lmbda o (uz + us) blas.Copy(us, ws3) blas.AxpyFloat(uz, ws3, 1.0) sprod(ws3, lmbda, dims, 0, la.OptDiag) blas.AxpyFloat(ws3, vs, -1.0) checkpnt.Check("90res", minor) return } resx0 := math.Max(1.0, math.Sqrt(q.Dot(q))) resy0 := math.Max(1.0, math.Sqrt(b.Dot(b))) resz0 := math.Max(1.0, snrm2(h, dims, 0)) //fmt.Printf("resx0: %.17f, resy0: %.17f, resz0: %.17f\n", resx0, resy0, resz0) var x, y, dx, dy, rx, ry MatrixVariable var z, s, ds, dz, rz *matrix.FloatMatrix var lmbda, lmbdasq, sigs, sigz *matrix.FloatMatrix var W *sets.FloatMatrixSet var f, f3 KKTFuncVar var resx, resy, resz, step, sigma, mu, eta float64 var gap, pcost, dcost, relgap, pres, dres, f0 float64 if cdim == 0 { // Solve // // [ P A' ] [ x ] [ -q ] // [ ] [ ] = [ ]. // [ A 0 ] [ y ] [ b ] // Wtmp := sets.NewFloatSet("d", "di", "beta", "v", "r", "rti") Wtmp.Set("d", matrix.FloatZeros(0, 1)) Wtmp.Set("di", matrix.FloatZeros(0, 1)) f3, err = kktsolver(Wtmp) if err != nil { s := fmt.Sprintf("kkt error: %s", err) err = errors.New("2: Rank(A) < p or Rank(([P; A; G;]) < n : " + s) return } x = q.Copy() x.Scal(0.0) y = b.Copy() f3(x, y, matrix.FloatZeros(0, 1)) // dres = || P*x + q + A'*y || / resx0 rx = q.Copy() fP(x, rx, 1.0, 1.0) pcost = 0.5 * (x.Dot(rx) + x.Dot(q)) fA(y, rx, 1.0, 1.0, la.OptTrans) dres = math.Sqrt(rx.Dot(rx) / resx0) ry = b.Copy() fA(x, ry, 1.0, -1.0, la.OptNoTrans) pres = math.Sqrt(ry.Dot(ry) / resy0) relgap = 0.0 if pcost == 0.0 { relgap = math.NaN() } sol.Result = sets.NewFloatSet("x", "y", "s", "z") sol.Result.Set("x", x.Matrix()) sol.Result.Set("y", y.Matrix()) sol.Result.Set("s", matrix.FloatZeros(0, 1)) sol.Result.Set("z", matrix.FloatZeros(0, 1)) sol.Status = Optimal sol.Gap = 0.0 sol.RelativeGap = relgap sol.PrimalObjective = pcost sol.DualObjective = pcost sol.PrimalInfeasibility = pres sol.DualInfeasibility = dres sol.PrimalSlack = 0.0 sol.DualSlack = 0.0 return } x = q.Copy() y = b.Copy() s = matrix.FloatZeros(cdim, 1) z = matrix.FloatZeros(cdim, 1) checkpnt.AddVerifiable("x", x) checkpnt.AddVerifiable("y", y) checkpnt.AddMatrixVar("s", s) checkpnt.AddMatrixVar("z", z) var ts, tz, nrms, nrmz float64 if initvals == nil { // Factor // // [ 0 A' G' ] // [ A 0 0 ]. // [ G 0 -I ] // W = sets.NewFloatSet("d", "di", "v", "beta", "r", "rti") W.Set("d", matrix.FloatOnes(dims.At("l")[0], 1)) W.Set("di", matrix.FloatOnes(dims.At("l")[0], 1)) W.Set("beta", matrix.FloatOnes(len(dims.At("q")), 1)) for _, n := range dims.At("q") { vm := matrix.FloatZeros(n, 1) vm.SetIndex(0, 1.0) W.Append("v", vm) } for _, n := range dims.At("s") { W.Append("r", matrix.FloatIdentity(n)) W.Append("rti", matrix.FloatIdentity(n)) } checkpnt.AddScaleVar(W) f, err = kktsolver(W) if err != nil { s := fmt.Sprintf("kkt error: %s", err) err = errors.New("3: Rank(A) < p or Rank([P; G; A]) < n : " + s) return } // Solve // // [ P A' G' ] [ x ] [ -q ] // [ A 0 0 ] * [ y ] = [ b ]. // [ G 0 -I ] [ z ] [ h ] mCopy(q, x) x.Scal(-1.0) mCopy(b, y) blas.Copy(h, z) checkpnt.Check("00init", 1) err = f(x, y, z) if err != nil { s := fmt.Sprintf("kkt error: %s", err) err = errors.New("4: Rank(A) < p or Rank([P; G; A]) < n : " + s) return } blas.Copy(z, s) blas.ScalFloat(s, -1.0) checkpnt.Check("05init", 1) nrms = snrm2(s, dims, 0) ts, _ = maxStep(s, dims, 0, nil) //fmt.Printf("nrms = %.7f, ts = %.7f\n", nrms, ts) if ts >= -1e-8*math.Max(nrms, 1.0) { // a = 1.0 + ts a := 1.0 + ts is := make([]int, 0) // indexes s[:dims['l']] is = append(is, matrix.MakeIndexSet(0, dims.At("l")[0], 1)...) // indexes s[indq[:-1]] is = append(is, indq[:len(indq)-1]...) ind := dims.Sum("l", "q") // indexes s[ind:ind+m*m:m+1] (diagonal) for _, m := range dims.At("s") { is = append(is, matrix.MakeIndexSet(ind, ind+m*m, m+1)...) ind += m * m } for _, k := range is { s.SetIndex(k, a+s.GetIndex(k)) } } nrmz = snrm2(z, dims, 0) tz, _ = maxStep(z, dims, 0, nil) //fmt.Printf("nrmz = %.7f, tz = %.7f\n", nrmz, tz) if tz >= -1e-8*math.Max(nrmz, 1.0) { a := 1.0 + tz is := make([]int, 0) is = append(is, matrix.MakeIndexSet(0, dims.At("l")[0], 1)...) is = append(is, indq[:len(indq)-1]...) ind := dims.Sum("l", "q") for _, m := range dims.At("s") { is = append(is, matrix.MakeIndexSet(ind, ind+m*m, m+1)...) ind += m * m } for _, k := range is { z.SetIndex(k, a+z.GetIndex(k)) } } } else { ix := initvals.At("x")[0] if ix != nil { mCopy(&matrixVar{ix}, x) } else { x.Scal(0.0) } is := initvals.At("s")[0] if is != nil { blas.Copy(is, s) } else { iset := make([]int, 0) iset = append(iset, matrix.MakeIndexSet(0, dims.At("l")[0], 1)...) iset = append(iset, indq[:len(indq)-1]...) ind := dims.Sum("l", "q") for _, m := range dims.At("s") { iset = append(iset, matrix.MakeIndexSet(ind, ind+m*m, m+1)...) ind += m * m } for _, k := range iset { s.SetIndex(k, 1.0) } } iy := initvals.At("y")[0] if iy != nil { mCopy(&matrixVar{iy}, y) } else { y.Scal(0.0) } iz := initvals.At("z")[0] if iz != nil { blas.Copy(iz, z) } else { iset := make([]int, 0) iset = append(iset, matrix.MakeIndexSet(0, dims.At("l")[0], 1)...) iset = append(iset, indq[:len(indq)-1]...) ind := dims.Sum("l", "q") for _, m := range dims.At("s") { iset = append(iset, matrix.MakeIndexSet(ind, ind+m*m, m+1)...) ind += m * m } for _, k := range iset { z.SetIndex(k, 1.0) } } } rx = q.Copy() ry = b.Copy() rz = matrix.FloatZeros(cdim, 1) dx = x.Copy() dy = y.Copy() dz = matrix.FloatZeros(cdim, 1) ds = matrix.FloatZeros(cdim, 1) lmbda = matrix.FloatZeros(cdim_diag, 1) lmbdasq = matrix.FloatZeros(cdim_diag, 1) sigs = matrix.FloatZeros(dims.Sum("s"), 1) sigz = matrix.FloatZeros(dims.Sum("s"), 1) checkpnt.AddVerifiable("rx", rx) checkpnt.AddVerifiable("ry", ry) checkpnt.AddVerifiable("dx", dx) checkpnt.AddVerifiable("dy", dy) //checkpnt.AddMatrixVar("rs", rs) checkpnt.AddMatrixVar("rz", rz) checkpnt.AddMatrixVar("ds", ds) checkpnt.AddMatrixVar("dz", dz) checkpnt.AddMatrixVar("lmbda", lmbda) checkpnt.AddMatrixVar("lmbdasq", lmbdasq) //var resx, resy, resz, step, sigma, mu, eta float64 //var gap, pcost, dcost, relgap, pres, dres, f0 float64 checkpnt.AddFloatVar("resx", &resx) checkpnt.AddFloatVar("resy", &resy) checkpnt.AddFloatVar("resz", &resz) checkpnt.AddFloatVar("step", &step) checkpnt.AddFloatVar("gap", &gap) checkpnt.AddFloatVar("dcost", &dcost) checkpnt.AddFloatVar("pcost", &pcost) checkpnt.AddFloatVar("dres", &dres) checkpnt.AddFloatVar("pres", &pres) checkpnt.AddFloatVar("relgap", &relgap) checkpnt.AddFloatVar("sigma", &sigma) var WS fVarClosure gap = sdot(s, z, dims, 0) for iter := 0; iter < maxIter+1; iter++ { checkpnt.MajorNext() checkpnt.Check("loopstart", 10) // f0 = (1/2)*x'*P*x + q'*x + r and rx = P*x + q + A'*y + G'*z. mCopy(q, rx) fP(x, rx, 1.0, 1.0) f0 = 0.5 * (x.Dot(rx) + x.Dot(q)) fA(y, rx, 1.0, 1.0, la.OptTrans) fG(&matrixVar{z}, rx, 1.0, 1.0, la.OptTrans) resx = math.Sqrt(rx.Dot(rx)) // ry = A*x - b mCopy(b, ry) fA(x, ry, 1.0, -1.0, la.OptNoTrans) resy = math.Sqrt(ry.Dot(ry)) // rz = s + G*x - h blas.Copy(s, rz) blas.AxpyFloat(h, rz, -1.0) fG(x, &matrixVar{rz}, 1.0, 1.0, la.OptNoTrans) resz = snrm2(rz, dims, 0) //fmt.Printf("resx: %.17f, resy: %.17f, resz: %.17f\n", resx, resy, resz) // Statistics for stopping criteria. // pcost = (1/2)*x'*P*x + q'*x // dcost = (1/2)*x'*P*x + q'*x + y'*(A*x-b) + z'*(G*x-h) ' // = (1/2)*x'*P*x + q'*x + y'*(A*x-b) + z'*(G*x-h+s) - z'*s // = (1/2)*x'*P*x + q'*x + y'*ry + z'*rz - gap pcost = f0 dcost = f0 + y.Dot(ry) + sdot(z, rz, dims, 0) - gap if pcost < 0.0 { relgap = gap / -pcost } else if dcost > 0.0 { relgap = gap / dcost } else { relgap = math.NaN() } pres = math.Max(resy/resy0, resz/resz0) dres = resx / resx0 if solopts.ShowProgress { if iter == 0 { // show headers of something fmt.Printf("% 10s% 12s% 10s% 8s% 7s\n", "pcost", "dcost", "gap", "pres", "dres") } // show something fmt.Printf("%2d: % 8.4e % 8.4e % 4.0e% 7.0e% 7.0e\n", iter, pcost, dcost, gap, pres, dres) } checkpnt.Check("stoptest", 100) if pres <= feasTolerance && dres <= feasTolerance && (gap <= absTolerance || (!math.IsNaN(relgap) && relgap <= relTolerance)) || iter == maxIter { ind := dims.Sum("l", "q") for _, m := range dims.At("s") { symm(s, m, ind) symm(z, m, ind) ind += m * m } ts, _ = maxStep(s, dims, 0, nil) tz, _ = maxStep(z, dims, 0, nil) if iter == maxIter { // terminated on max iterations. sol.Status = Unknown err = errors.New("Terminated (maximum iterations reached)") fmt.Printf("Terminated (maximum iterations reached)\n") return } // optimal solution found //fmt.Print("Optimal solution.\n") err = nil sol.Result = sets.NewFloatSet("x", "y", "s", "z") sol.Result.Set("x", x.Matrix()) sol.Result.Set("y", y.Matrix()) sol.Result.Set("s", s) sol.Result.Set("z", z) sol.Status = Optimal sol.Gap = gap sol.RelativeGap = relgap sol.PrimalObjective = pcost sol.DualObjective = dcost sol.PrimalInfeasibility = pres sol.DualInfeasibility = dres sol.PrimalSlack = -ts sol.DualSlack = -tz sol.PrimalResidualCert = math.NaN() sol.DualResidualCert = math.NaN() sol.Iterations = iter return } // Compute initial scaling W and scaled iterates: // // W * z = W^{-T} * s = lambda. // // lmbdasq = lambda o lambda. if iter == 0 { W, err = computeScaling(s, z, lmbda, dims, 0) checkpnt.AddScaleVar(W) } ssqr(lmbdasq, lmbda, dims, 0) f3, err = kktsolver(W) if err != nil { if iter == 0 { s := fmt.Sprintf("kkt error: %s", err) err = errors.New("5: Rank(A) < p or Rank([P; A; G]) < n : " + s) return } else { ind := dims.Sum("l", "q") for _, m := range dims.At("s") { symm(s, m, ind) symm(z, m, ind) ind += m * m } ts, _ = maxStep(s, dims, 0, nil) tz, _ = maxStep(z, dims, 0, nil) // terminated (singular KKT matrix) fmt.Printf("Terminated (singular KKT matrix).\n") err = errors.New("Terminated (singular KKT matrix).") sol.Result = sets.NewFloatSet("x", "y", "s", "z") sol.Result.Set("x", x.Matrix()) sol.Result.Set("y", y.Matrix()) sol.Result.Set("s", s) sol.Result.Set("z", z) sol.Status = Unknown sol.RelativeGap = relgap sol.PrimalObjective = pcost sol.DualObjective = dcost sol.PrimalInfeasibility = pres sol.DualInfeasibility = dres sol.PrimalSlack = -ts sol.DualSlack = -tz sol.Iterations = iter return } } // f4_no_ir(x, y, z, s) solves // // [ 0 ] [ P A' G' ] [ ux ] [ bx ] // [ 0 ] + [ A 0 0 ] * [ uy ] = [ by ] // [ W'*us ] [ G 0 0 ] [ W^{-1}*uz ] [ bz ] // // lmbda o (uz + us) = bs. // // On entry, x, y, z, s contain bx, by, bz, bs. // On exit, they contain ux, uy, uz, us. f4_no_ir := func(x, y MatrixVariable, z, s *matrix.FloatMatrix) error { // Solve // // [ P A' G' ] [ ux ] [ bx ] // [ A 0 0 ] [ uy ] = [ by ] // [ G 0 -W'*W ] [ W^{-1}*uz ] [ bz - W'*(lmbda o\ bs) ] // // us = lmbda o\ bs - uz. // // On entry, x, y, z, s contains bx, by, bz, bs. // On exit they contain x, y, z, s. minor := checkpnt.MinorTop() checkpnt.Check("f4_no_ir_start", minor) // s := lmbda o\ s // = lmbda o\ bs sinv(s, lmbda, dims, 0) // z := z - W'*s // = bz - W'*(lambda o\ bs) blas.Copy(s, ws3) scale(ws3, W, true, false) blas.AxpyFloat(ws3, z, -1.0) checkpnt.Check("f4_no_ir_f3", minor+50) err := f3(x, y, z) if err != nil { return err } checkpnt.Check("f4_no_ir_f3", minor+60) // s := s - z // = lambda o\ bs - uz. blas.AxpyFloat(z, s, -1.0) checkpnt.Check("f4_no_ir_f3", minor+90) return nil } if iter == 0 { if refinement > 0 || solopts.Debug { WS.wx = q.Copy() WS.wy = y.Copy() WS.ws = matrix.FloatZeros(cdim, 1) WS.wz = matrix.FloatZeros(cdim, 1) checkpnt.AddVerifiable("wx", WS.wx) checkpnt.AddVerifiable("wy", WS.wy) checkpnt.AddMatrixVar("ws", WS.ws) checkpnt.AddMatrixVar("wz", WS.wz) } if refinement > 0 { WS.wx2 = q.Copy() WS.wy2 = y.Copy() WS.ws2 = matrix.FloatZeros(cdim, 1) WS.wz2 = matrix.FloatZeros(cdim, 1) checkpnt.AddVerifiable("wx2", WS.wx2) checkpnt.AddVerifiable("wy2", WS.wy2) checkpnt.AddMatrixVar("ws2", WS.ws2) checkpnt.AddMatrixVar("wz2", WS.wz2) } } f4 := func(x, y MatrixVariable, z, s *matrix.FloatMatrix) (err error) { minor := checkpnt.MinorTop() checkpnt.Check("f4start", minor) err = nil if refinement > 0 || solopts.Debug { mCopy(x, WS.wx) mCopy(y, WS.wy) blas.Copy(z, WS.wz) blas.Copy(s, WS.ws) } checkpnt.MinorPush(minor + 100) err = f4_no_ir(x, y, z, s) checkpnt.MinorPop() for i := 0; i < refinement; i++ { mCopy(WS.wx, WS.wx2) mCopy(WS.wy, WS.wy2) blas.Copy(WS.wz, WS.wz2) blas.Copy(WS.ws, WS.ws2) checkpnt.MinorPush(minor + (i+1)*300) res(x, y, z, s, WS.wx2, WS.wy2, WS.wz2, WS.ws2, W, lmbda) checkpnt.MinorPop() checkpnt.MinorPush(minor + (i+1)*500) f4_no_ir(WS.wx2, WS.wy2, WS.wz2, WS.ws2) checkpnt.MinorPop() WS.wx2.Axpy(x, 1.0) WS.wy2.Axpy(y, 1.0) blas.AxpyFloat(WS.wz2, z, 1.0) blas.AxpyFloat(WS.ws2, s, 1.0) } checkpnt.Check("f4end", minor+1500) return } //var mu, sigma, eta float64 mu = gap / float64(dims.Sum("l", "s")+len(dims.At("q"))) sigma, eta = 0.0, 0.0 for i := 0; i < 2; i++ { // Solve // // [ 0 ] [ P A' G' ] [ dx ] // [ 0 ] + [ A 0 0 ] * [ dy ] = -(1 - eta) * r // [ W'*ds ] [ G 0 0 ] [ W^{-1}*dz ] // // lmbda o (dz + ds) = -lmbda o lmbda + sigma*mu*e (i=0) // lmbda o (dz + ds) = -lmbda o lmbda - dsa o dza // + sigma*mu*e (i=1) where dsa, dza // are the solution for i=0. minor_base := (i + 1) * 2000 // ds = -lmbdasq + sigma * mu * e (if i is 0) // = -lmbdasq - dsa o dza + sigma * mu * e (if i is 1), // where ds, dz are solution for i is 0. blas.ScalFloat(ds, 0.0) if correction && i == 1 { blas.AxpyFloat(ws3, ds, -1.0) } blas.AxpyFloat(lmbdasq, ds, -1.0, &la.IOpt{"n", dims.Sum("l", "q")}) ind := dims.At("l")[0] ds.Add(sigma*mu, matrix.MakeIndexSet(0, ind, 1)...) for _, m := range dims.At("q") { ds.SetIndex(ind, sigma*mu+ds.GetIndex(ind)) ind += m } ind2 := ind for _, m := range dims.At("s") { blas.AxpyFloat(lmbdasq, ds, -1.0, &la.IOpt{"n", m}, &la.IOpt{"incy", m + 1}, &la.IOpt{"offsetx", ind2}, &la.IOpt{"offsety", ind}) ds.Add(sigma*mu, matrix.MakeIndexSet(ind, ind+m*m, m+1)...) ind += m * m ind2 += m } checkpnt.Check("00loop01", minor_base) // (dx, dy, dz) := -(1 - eta) * (rx, ry, rz) //blas.ScalFloat(dx, 0.0) //blas.AxpyFloat(rx, dx, -1.0+eta) dx.Scal(0.0) rx.Axpy(dx, -1.0+eta) dy.Scal(0.0) ry.Axpy(dy, -1.0+eta) blas.ScalFloat(dz, 0.0) blas.AxpyFloat(rz, dz, -1.0+eta) //fmt.Printf("== Calling f4 %d\n", i) //fmt.Printf("dx=\n%v\n", dx.ToString("%.17f")) //fmt.Printf("ds=\n%v\n", ds.ToString("%.17f")) //fmt.Printf("dz=\n%v\n", dz.ToString("%.17f")) //fmt.Printf("== Entering f4 %d\n", i) checkpnt.MinorPush(minor_base) err = f4(dx, dy, dz, ds) checkpnt.MinorPop() if err != nil { if iter == 0 { s := fmt.Sprintf("kkt error: %s", err) err = errors.New("6: Rank(A) < p or Rank([P; A; G]) < n : " + s) return } else { ind = dims.Sum("l", "q") for _, m := range dims.At("s") { symm(s, m, ind) symm(z, m, ind) ind += m * m } ts, _ = maxStep(s, dims, 0, nil) tz, _ = maxStep(z, dims, 0, nil) return } } dsdz := sdot(ds, dz, dims, 0) if correction && i == 0 { blas.Copy(ds, ws3) sprod(ws3, dz, dims, 0) } // Maximum step to boundary. // // If i is 1, also compute eigenvalue decomposition of the 's' // blocks in ds, dz. The eigenvectors Qs, Qz are stored in // dsk, dzk. The eigenvalues are stored in sigs, sigz. scale2(lmbda, ds, dims, 0, false) scale2(lmbda, dz, dims, 0, false) checkpnt.Check("maxstep", minor_base+1500) if i == 0 { ts, _ = maxStep(ds, dims, 0, nil) tz, _ = maxStep(dz, dims, 0, nil) } else { ts, _ = maxStep(ds, dims, 0, sigs) tz, _ = maxStep(dz, dims, 0, sigz) } t := maxvec([]float64{0.0, ts, tz}) //fmt.Printf("== t=%.17f from %v\n", t, []float64{ts, tz}) if t == 0.0 { step = 1.0 } else { if i == 0 { step = math.Min(1.0, 1.0/t) } else { step = math.Min(1.0, STEP/t) } } if i == 0 { m := math.Max(0.0, 1.0-step+dsdz/gap*(step*step)) sigma = math.Pow(math.Min(1.0, m), float64(EXPON)) eta = 0.0 } //fmt.Printf("== step=%.17f sigma=%.17f dsdz=%.17f\n", step, sigma, dsdz) } checkpnt.Check("updatexy", 8000) dx.Axpy(x, step) dy.Axpy(y, step) //fmt.Printf("x=\n%v\n", x.ConvertToString()) //fmt.Printf("y=\n%v\n", y.ConvertToString()) //fmt.Printf("ds=\n%v\n", ds.ConvertToString()) //fmt.Printf("dz=\n%v\n", dz.ConvertToString()) // We will now replace the 'l' and 'q' blocks of ds and dz with // the updated iterates in the current scaling. // We also replace the '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", dims.Sum("l", "q")}) blas.ScalFloat(dz, step, &la.IOpt{"n", dims.Sum("l", "q")}) ind := 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", 8010) // 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, 0, true) scale2(lmbda, dz, dims, 0, true) checkpnt.Check("scale2", 8030) // 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", qdimsum}) blas.TbsvFloat(lmbda, sigz, &la.IOpt{"n", sdimsum}, &la.IOpt{"k", 0}, &la.IOpt{"lda", 1}, &la.IOpt{"offseta", qdimsum}) ind2 := 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("updatescaling", 8050) err = updateScaling(W, lmbda, ds, dz) checkpnt.Check("afterscaling", 8060) // Unscale s, z, tau, kappa (unscaled variables are used only to // compute feasibility residuals). ind = 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) ind = 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) gap = blas.DotFloat(lmbda, lmbda) checkpnt.Check("eol", 8900) //fmt.Printf("== gap = %.17f\n", gap) } return }
// 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 }
func conelp_solver(c MatrixVariable, G MatrixVarG, h *matrix.FloatMatrix, A MatrixVarA, b MatrixVariable, dims *sets.DimensionSet, kktsolver KKTConeSolverVar, solopts *SolverOptions, primalstart, dualstart *sets.FloatMatrixSet) (sol *Solution, err error) { err = nil const EXPON = 3 const STEP = 0.99 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} var refinement int if solopts.Refinement > 0 { refinement = solopts.Refinement } else { refinement = 0 if len(dims.At("q")) > 0 || len(dims.At("s")) > 0 { refinement = 1 } } 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.MaxIter > 0 { maxIter = solopts.MaxIter } if err = checkConeLpDimensions(dims); err != nil { return } cdim := dims.Sum("l", "q") + dims.SumSquared("s") //cdim_pckd := dims.Sum("l", "q") + dims.SumPacked("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 } // Data for kth 'q' constraint are found in rows indq[k]:indq[k+1] of G. indq := make([]int, 0) indq = append(indq, dims.At("l")[0]) for _, k := range dims.At("q") { indq = append(indq, indq[len(indq)-1]+k) } // Data for kth 's' constraint are found in rows inds[k]:inds[k+1] of G. inds := make([]int, 0) inds = append(inds, indq[len(indq)-1]) for _, k := range dims.At("s") { inds = append(inds, inds[len(inds)-1]+k*k) } Gf := func(x, y MatrixVariable, alpha, beta float64, trans la.Option) error { return G.Gf(x, y, alpha, beta, trans) } Af := func(x, y MatrixVariable, alpha, beta float64, trans la.Option) error { return A.Af(x, y, alpha, beta, trans) } // kktsolver(W) returns a routine for solving 3x3 block KKT system // // [ 0 A' G'*W^{-1} ] [ ux ] [ bx ] // [ A 0 0 ] [ uy ] = [ by ]. // [ G 0 -W' ] [ uz ] [ bz ] if kktsolver == nil { err = errors.New("nil kktsolver not allowed.") return } // res() evaluates residual in 5x5 block KKT system // // [ vx ] [ 0 ] [ 0 A' G' c ] [ ux ] // [ vy ] [ 0 ] [-A 0 0 b ] [ uy ] // [ vz ] += [ W'*us ] - [-G 0 0 h ] [ W^{-1}*uz ] // [ vtau ] [ dg*ukappa ] [-c' -b' -h' 0 ] [ utau/dg ] // // vs += lmbda o (dz + ds) // vkappa += lmbdg * (dtau + dkappa). ws3 := matrix.FloatZeros(cdim, 1) wz3 := matrix.FloatZeros(cdim, 1) checkpnt.AddMatrixVar("ws3", ws3) checkpnt.AddMatrixVar("wz3", wz3) // res := func(ux, uy MatrixVariable, uz, utau, us, ukappa *matrix.FloatMatrix, vx, vy MatrixVariable, vz, vtau, vs, vkappa *matrix.FloatMatrix, W *sets.FloatMatrixSet, dg float64, lmbda *matrix.FloatMatrix) (err error) { err = nil // vx := vx - A'*uy - G'*W^{-1}*uz - c*utau/dg Af(uy, vx, -1.0, 1.0, la.OptTrans) //fmt.Printf("post-Af vx=\n%v\n", vx) blas.Copy(uz, wz3) scale(wz3, W, false, true) Gf(&matrixVar{wz3}, vx, -1.0, 1.0, la.OptTrans) //blas.AxpyFloat(c, vx, -utau.Float()/dg) c.Axpy(vx, -utau.Float()/dg) // vy := vy + A*ux - b*utau/dg Af(ux, vy, 1.0, 1.0, la.OptNoTrans) //blas.AxpyFloat(b, vy, -utau.Float()/dg) b.Axpy(vy, -utau.Float()/dg) // vz := vz + G*ux - h*utau/dg + W'*us Gf(ux, &matrixVar{vz}, 1.0, 1.0, la.OptNoTrans) blas.AxpyFloat(h, vz, -utau.Float()/dg) blas.Copy(us, ws3) scale(ws3, W, true, false) blas.AxpyFloat(ws3, vz, 1.0) // vtau := vtau + c'*ux + b'*uy + h'*W^{-1}*uz + dg*ukappa var vtauplus float64 = dg*ukappa.Float() + c.Dot(ux) + b.Dot(uy) + sdot(h, wz3, dims, 0) vtau.SetValue(vtau.Float() + vtauplus) // vs := vs + lmbda o (uz + us) blas.Copy(us, ws3) blas.AxpyFloat(uz, ws3, 1.0) sprod(ws3, lmbda, dims, 0, &la.SOpt{"diag", "D"}) blas.AxpyFloat(ws3, vs, 1.0) // vkappa += vkappa + lmbdag * (utau + ukappa) lscale := lmbda.GetIndex(-1) var vkplus float64 = lscale * (utau.Float() + ukappa.Float()) vkappa.SetValue(vkappa.Float() + vkplus) return } resx0 := math.Max(1.0, math.Sqrt(c.Dot(c))) resy0 := math.Max(1.0, math.Sqrt(b.Dot(b))) resz0 := math.Max(1.0, snrm2(h, dims, 0)) // select initial points //fmt.Printf("** initial resx0=%.4f, resy0=%.4f, resz0=%.4f \n", resx0, resy0, resz0) x := c.Copy() //blas.ScalFloat(x, 0.0) x.Scal(0.0) y := b.Copy() //blas.ScalFloat(y, 0.0) y.Scal(0.0) s := matrix.FloatZeros(cdim, 1) z := matrix.FloatZeros(cdim, 1) dx := c.Copy() dy := b.Copy() ds := matrix.FloatZeros(cdim, 1) dz := matrix.FloatZeros(cdim, 1) // these are singleton matrix dkappa := matrix.FloatValue(0.0) dtau := matrix.FloatValue(0.0) checkpnt.AddVerifiable("x", x) checkpnt.AddMatrixVar("s", s) checkpnt.AddMatrixVar("z", z) checkpnt.AddVerifiable("dx", dx) checkpnt.AddMatrixVar("ds", ds) checkpnt.AddMatrixVar("dz", dz) checkpnt.Check("00init", 1) var W *sets.FloatMatrixSet var f KKTFuncVar if primalstart == nil || dualstart == nil { // Factor // // [ 0 A' G' ] // [ A 0 0 ]. // [ G 0 -I ] // W = sets.NewFloatSet("d", "di", "v", "beta", "r", "rti") dd := dims.At("l")[0] mat := matrix.FloatOnes(dd, 1) W.Set("d", mat) mat = matrix.FloatOnes(dd, 1) W.Set("di", mat) dq := len(dims.At("q")) W.Set("beta", matrix.FloatOnes(dq, 1)) for _, n := range dims.At("q") { vm := matrix.FloatZeros(n, 1) vm.SetIndex(0, 1.0) W.Append("v", vm) } for _, n := range dims.At("s") { W.Append("r", matrix.FloatIdentity(n)) W.Append("rti", matrix.FloatIdentity(n)) } f, err = kktsolver(W) if err != nil { fmt.Printf("kktsolver error: %s\n", err) return } checkpnt.AddScaleVar(W) } checkpnt.Check("05init", 5) if primalstart == nil { // minimize || G * x - h ||^2 // subject to A * x = b // // by solving // // [ 0 A' G' ] [ x ] [ 0 ] // [ A 0 0 ] * [ dy ] = [ b ]. // [ G 0 -I ] [ -s ] [ h ] //blas.ScalFloat(x, 0.0) //blas.CopyFloat(b, dy) checkpnt.MinorPush(5) x.Scal(0.0) mCopy(b, dy) blas.CopyFloat(h, s) err = f(x, dy, s) if err != nil { fmt.Printf("f(x,dy,s): %s\n", err) return } blas.ScalFloat(s, -1.0) //fmt.Printf("initial s=\n%v\n", s.ToString("%.5f")) checkpnt.MinorPop() } else { mCopy(&matrixVar{primalstart.At("x")[0]}, x) blas.Copy(primalstart.At("s")[0], s) } // ts = min{ t | s + t*e >= 0 } ts, _ := maxStep(s, dims, 0, nil) if ts >= 0 && primalstart != nil { err = errors.New("initial s is not positive") return } //fmt.Printf("initial ts=%.5f\n", ts) checkpnt.Check("10init", 10) if dualstart == nil { // minimize || z ||^2 // subject to G'*z + A'*y + c = 0 // // by solving // // [ 0 A' G' ] [ dx ] [ -c ] // [ A 0 0 ] [ y ] = [ 0 ]. // [ G 0 -I ] [ z ] [ 0 ] //blas.Copy(c, dx) //blas.ScalFloat(dx, -1.0) //blas.ScalFloat(y, 0.0) checkpnt.MinorPush(10) mCopy(c, dx) dx.Scal(-1.0) y.Scal(0.0) blas.ScalFloat(z, 0.0) err = f(dx, y, z) if err != nil { fmt.Printf("f(dx,y,z): %s\n", err) return } //fmt.Printf("initial z=\n%v\n", z.ToString("%.5f")) checkpnt.MinorPop() } else { if len(dualstart.At("y")) > 0 { mCopy(&matrixVar{dualstart.At("y")[0]}, y) } blas.Copy(dualstart.At("z")[0], z) } // ts = min{ t | z + t*e >= 0 } tz, _ := maxStep(z, dims, 0, nil) if tz >= 0 && dualstart != nil { err = errors.New("initial z is not positive") return } //fmt.Printf("initial tz=%.5f\n", tz) nrms := snrm2(s, dims, 0) nrmz := snrm2(z, dims, 0) gap := 0.0 pcost := 0.0 dcost := 0.0 relgap := 0.0 checkpnt.Check("20init", 0) if primalstart == nil && dualstart == nil { gap = sdot(s, z, dims, 0) pcost = c.Dot(x) dcost = -b.Dot(y) - sdot(h, z, dims, 0) if pcost < 0.0 { relgap = gap / -pcost } else if dcost > 0.0 { relgap = gap / dcost } else { relgap = math.NaN() } if ts <= 0 && tz < 0 && (gap <= absTolerance || (!math.IsNaN(relgap) && relgap <= relTolerance)) { // Constructed initial points happen to be feasible and optimal ind := dims.At("l")[0] + dims.Sum("q") for _, m := range dims.At("s") { symm(s, m, ind) symm(z, m, ind) ind += m * m } // rx = A'*y + G'*z + c rx := c.Copy() Af(y, rx, 1.0, 1.0, la.OptTrans) Gf(&matrixVar{z}, rx, 1.0, 1.0, la.OptTrans) resx := math.Sqrt(rx.Dot(rx)) // ry = b - A*x ry := b.Copy() Af(x, ry, -1.0, -1.0, la.OptNoTrans) resy := math.Sqrt(ry.Dot(ry)) // rz = s + G*x - h rz := matrix.FloatZeros(cdim, 1) Gf(x, &matrixVar{rz}, 1.0, 0.0, la.OptNoTrans) blas.AxpyFloat(s, rz, 1.0) blas.AxpyFloat(h, rz, -1.0) resz := snrm2(rz, dims, 0) pres := math.Max(resy/resy0, resz/resz0) dres := resx / resx0 cx := c.Dot(x) by := b.Dot(y) hz := sdot(h, z, dims, 0) //sol.X = x; sol.Y = y; sol.S = s; sol.Z = z sol.Result = sets.NewFloatSet("x", "y", "s", "x") sol.Result.Append("x", x.Matrix()) sol.Result.Append("y", y.Matrix()) sol.Result.Append("s", s) sol.Result.Append("z", z) sol.Status = Optimal sol.Gap = gap sol.RelativeGap = relgap sol.PrimalObjective = cx sol.DualObjective = -(by + hz) sol.PrimalInfeasibility = pres sol.DualInfeasibility = dres sol.PrimalSlack = -ts sol.DualSlack = -tz return } if ts >= -1e-8*math.Max(nrms, 1.0) { a := 1.0 + ts is := make([]int, 0) // indexes s[:dims['l']] if dims.At("l")[0] > 0 { is = append(is, matrix.MakeIndexSet(0, dims.At("l")[0], 1)...) } // indexes s[indq[:-1]] if len(indq) > 1 { is = append(is, indq[:len(indq)-1]...) } // indexes s[ind:ind+m*m:m+1] (diagonal) ind := dims.Sum("l", "q") for _, m := range dims.At("s") { is = append(is, matrix.MakeIndexSet(ind, ind+m*m, m+1)...) ind += m * m } for _, k := range is { s.SetIndex(k, a+s.GetIndex(k)) } } if tz >= -1e-8*math.Max(nrmz, 1.0) { a := 1.0 + tz is := make([]int, 0) // indexes z[:dims['l']] if dims.At("l")[0] > 0 { is = append(is, matrix.MakeIndexSet(0, dims.At("l")[0], 1)...) } // indexes z[indq[:-1]] if len(indq) > 1 { is = append(is, indq[:len(indq)-1]...) } // indexes z[ind:ind+m*m:m+1] (diagonal) ind := dims.Sum("l", "q") for _, m := range dims.At("s") { is = append(is, matrix.MakeIndexSet(ind, ind+m*m, m+1)...) ind += m * m } for _, k := range is { z.SetIndex(k, a+z.GetIndex(k)) } } } else if primalstart == nil && dualstart != nil { if ts >= -1e-8*math.Max(nrms, 1.0) { a := 1.0 + ts is := make([]int, 0) if dims.At("l")[0] > 0 { is = append(is, matrix.MakeIndexSet(0, dims.At("l")[0], 1)...) } if len(indq) > 1 { is = append(is, indq[:len(indq)-1]...) } ind := dims.Sum("l", "q") for _, m := range dims.At("s") { is = append(is, matrix.MakeIndexSet(ind, ind+m*m, m+1)...) ind += m * m } for _, k := range is { s.SetIndex(k, a+s.GetIndex(k)) } } } else if primalstart != nil && dualstart == nil { if tz >= -1e-8*math.Max(nrmz, 1.0) { a := 1.0 + tz is := make([]int, 0) if dims.At("l")[0] > 0 { is = append(is, matrix.MakeIndexSet(0, dims.At("l")[0], 1)...) } if len(indq) > 1 { is = append(is, indq[:len(indq)-1]...) } ind := dims.Sum("l", "q") for _, m := range dims.At("s") { is = append(is, matrix.MakeIndexSet(ind, ind+m*m, m+1)...) ind += m * m } for _, k := range is { z.SetIndex(k, a+z.GetIndex(k)) } } } tau := matrix.FloatValue(1.0) kappa := matrix.FloatValue(1.0) wkappa3 := matrix.FloatValue(0.0) rx := c.Copy() hrx := c.Copy() ry := b.Copy() hry := b.Copy() rz := matrix.FloatZeros(cdim, 1) hrz := matrix.FloatZeros(cdim, 1) sigs := matrix.FloatZeros(dims.Sum("s"), 1) sigz := matrix.FloatZeros(dims.Sum("s"), 1) lmbda := matrix.FloatZeros(cdim_diag+1, 1) lmbdasq := matrix.FloatZeros(cdim_diag+1, 1) gap = sdot(s, z, dims, 0) var x1, y1 MatrixVariable var z1 *matrix.FloatMatrix var dg, dgi float64 var th *matrix.FloatMatrix var WS fVarClosure var f3 KKTFuncVar var cx, by, hz, rt float64 var hresx, resx, hresy, resy, hresz, resz float64 var dres, pres, dinfres, pinfres float64 // check point variables checkpnt.AddMatrixVar("lmbda", lmbda) checkpnt.AddMatrixVar("lmbdasq", lmbdasq) checkpnt.AddVerifiable("rx", rx) checkpnt.AddVerifiable("ry", ry) checkpnt.AddMatrixVar("rz", rz) checkpnt.AddFloatVar("resx", &resx) checkpnt.AddFloatVar("resy", &resy) checkpnt.AddFloatVar("resz", &resz) checkpnt.AddFloatVar("hresx", &hresx) checkpnt.AddFloatVar("hresy", &hresy) checkpnt.AddFloatVar("hresz", &hresz) checkpnt.AddFloatVar("cx", &cx) checkpnt.AddFloatVar("by", &by) checkpnt.AddFloatVar("hz", &hz) checkpnt.AddFloatVar("gap", &gap) checkpnt.AddFloatVar("pres", &pres) checkpnt.AddFloatVar("dres", &dres) for iter := 0; iter < maxIter+1; iter++ { checkpnt.MajorNext() checkpnt.Check("loop-start", 100) // hrx = -A'*y - G'*z Af(y, hrx, -1.0, 0.0, la.OptTrans) Gf(&matrixVar{z}, hrx, -1.0, 1.0, la.OptTrans) hresx = math.Sqrt(hrx.Dot(hrx)) // rx = hrx - c*tau // = -A'*y - G'*z - c*tau mCopy(hrx, rx) c.Axpy(rx, -tau.Float()) resx = math.Sqrt(rx.Dot(rx)) / tau.Float() // hry = A*x Af(x, hry, 1.0, 0.0, la.OptNoTrans) hresy = math.Sqrt(hry.Dot(hry)) // ry = hry - b*tau // = A*x - b*tau mCopy(hry, ry) b.Axpy(ry, -tau.Float()) resy = math.Sqrt(ry.Dot(ry)) / tau.Float() // hrz = s + G*x Gf(x, &matrixVar{hrz}, 1.0, 0.0, la.OptNoTrans) blas.AxpyFloat(s, hrz, 1.0) hresz = snrm2(hrz, dims, 0) // rz = hrz - h*tau // = s + G*x - h*tau blas.ScalFloat(rz, 0.0) blas.AxpyFloat(hrz, rz, 1.0) blas.AxpyFloat(h, rz, -tau.Float()) resz = snrm2(rz, dims, 0) / tau.Float() // rt = kappa + c'*x + b'*y + h'*z ' cx = c.Dot(x) by = b.Dot(y) hz = sdot(h, z, dims, 0) rt = kappa.Float() + cx + by + hz // Statistics for stopping criteria pcost = cx / tau.Float() dcost = -(by + hz) / tau.Float() if pcost < 0.0 { relgap = gap / -pcost } else if dcost > 0.0 { relgap = gap / dcost } else { relgap = math.NaN() } pres = math.Max(resy/resy0, resz/resz0) dres = resx / resx0 pinfres = math.NaN() if hz+by < 0.0 { pinfres = hresx / resx0 / (-hz - by) } dinfres = math.NaN() if cx < 0.0 { dinfres = math.Max(hresy/resy0, hresz/resz0) / (-cx) } if solopts.ShowProgress { if iter == 0 { // show headers of something fmt.Printf("% 10s% 12s% 10s% 8s% 7s % 5s\n", "pcost", "dcost", "gap", "pres", "dres", "k/t") } // show something fmt.Printf("%2d: % 8.4e % 8.4e % 4.0e% 7.0e% 7.0e% 7.0e\n", iter, pcost, dcost, gap, pres, dres, kappa.GetIndex(0)/tau.GetIndex(0)) } checkpnt.Check("isready", 200) if (pres <= feasTolerance && dres <= feasTolerance && (gap <= absTolerance || (!math.IsNaN(relgap) && relgap <= relTolerance))) || iter == maxIter { // done x.Scal(1.0 / tau.Float()) y.Scal(1.0 / tau.Float()) blas.ScalFloat(s, 1.0/tau.Float()) blas.ScalFloat(z, 1.0/tau.Float()) ind := dims.Sum("l", "q") for _, m := range dims.At("s") { symm(s, m, ind) symm(z, m, ind) ind += m * m } ts, _ = maxStep(s, dims, 0, nil) tz, _ = maxStep(z, dims, 0, nil) if iter == maxIter { // MaxIterations exceeded if solopts.ShowProgress { fmt.Printf("No solution. Max iterations exceeded\n") } err = errors.New("No solution. Max iterations exceeded") //sol.X = x; sol.Y = y; sol.S = s; sol.Z = z sol.Result = sets.NewFloatSet("x", "y", "s", "x") sol.Result.Append("x", x.Matrix()) sol.Result.Append("y", y.Matrix()) sol.Result.Append("s", s) sol.Result.Append("z", z) sol.Status = Unknown sol.Gap = gap sol.RelativeGap = relgap sol.PrimalObjective = pcost sol.DualObjective = dcost sol.PrimalInfeasibility = pres sol.DualInfeasibility = dres sol.PrimalSlack = -ts sol.DualSlack = -tz sol.PrimalResidualCert = pinfres sol.DualResidualCert = dinfres sol.Iterations = iter return } else { // Optimal if solopts.ShowProgress { fmt.Printf("Optimal solution.\n") } err = nil //sol.X = x; sol.Y = y; sol.S = s; sol.Z = z sol.Result = sets.NewFloatSet("x", "y", "s", "x") sol.Result.Append("x", x.Matrix()) sol.Result.Append("y", y.Matrix()) sol.Result.Append("s", s) sol.Result.Append("z", z) sol.Status = Optimal sol.Gap = gap sol.RelativeGap = relgap sol.PrimalObjective = pcost sol.DualObjective = dcost sol.PrimalInfeasibility = pres sol.DualInfeasibility = dres sol.PrimalSlack = -ts sol.DualSlack = -tz sol.PrimalResidualCert = math.NaN() sol.DualResidualCert = math.NaN() sol.Iterations = iter return } } else if !math.IsNaN(pinfres) && pinfres <= feasTolerance { // Primal Infeasible if solopts.ShowProgress { fmt.Printf("Primal infeasible.\n") } err = errors.New("Primal infeasible") y.Scal(1.0 / (-hz - by)) blas.ScalFloat(z, 1.0/(-hz-by)) //sol.X = nil; sol.Y = nil; sol.S = nil; sol.Z = nil ind := dims.Sum("l", "q") for _, m := range dims.At("s") { symm(z, m, ind) ind += m * m } tz, _ = maxStep(z, dims, 0, nil) sol.Status = PrimalInfeasible sol.Result = sets.NewFloatSet("x", "y", "s", "x") sol.Result.Append("x", nil) sol.Result.Append("y", nil) sol.Result.Append("s", nil) sol.Result.Append("z", nil) sol.Gap = math.NaN() sol.RelativeGap = math.NaN() sol.PrimalObjective = math.NaN() sol.DualObjective = 1.0 sol.PrimalInfeasibility = math.NaN() sol.DualInfeasibility = math.NaN() sol.PrimalSlack = math.NaN() sol.DualSlack = -tz sol.PrimalResidualCert = pinfres sol.DualResidualCert = math.NaN() sol.Iterations = iter return } else if !math.IsNaN(dinfres) && dinfres <= feasTolerance { // Dual Infeasible if solopts.ShowProgress { fmt.Printf("Dual infeasible.\n") } err = errors.New("Primal infeasible") x.Scal(1.0 / (-cx)) blas.ScalFloat(s, 1.0/(-cx)) //sol.X = nil; sol.Y = nil; sol.S = nil; sol.Z = nil ind := dims.Sum("l", "q") for _, m := range dims.At("s") { symm(s, m, ind) ind += m * m } ts, _ = maxStep(s, dims, 0, nil) sol.Status = PrimalInfeasible sol.Result = sets.NewFloatSet("x", "y", "s", "x") sol.Result.Append("x", nil) sol.Result.Append("y", nil) sol.Result.Append("s", nil) sol.Result.Append("z", nil) sol.Gap = math.NaN() sol.RelativeGap = math.NaN() sol.PrimalObjective = 1.0 sol.DualObjective = math.NaN() sol.PrimalInfeasibility = math.NaN() sol.DualInfeasibility = math.NaN() sol.PrimalSlack = -ts sol.DualSlack = math.NaN() sol.PrimalResidualCert = math.NaN() sol.DualResidualCert = dinfres sol.Iterations = iter return } // Compute initial scaling W: // // W * z = W^{-T} * s = lambda // dg * tau = 1/dg * kappa = lambdag. if iter == 0 { //fmt.Printf("compute scaling: lmbda=\n%v\n", lmbda.ToString("%.17f")) //fmt.Printf("s=\n%v\n", s.ToString("%.17f")) //fmt.Printf("z=\n%v\n", z.ToString("%.17f")) W, err = computeScaling(s, z, lmbda, dims, 0) checkpnt.AddScaleVar(W) // dg = sqrt( kappa / tau ) // dgi = sqrt( tau / kappa ) // lambda_g = sqrt( tau * kappa ) // // lambda_g is stored in the last position of lmbda. dg = math.Sqrt(kappa.Float() / tau.Float()) dgi = math.Sqrt(float64(tau.Float() / kappa.Float())) lmbda.SetIndex(-1, math.Sqrt(float64(tau.Float()*kappa.Float()))) //fmt.Printf("lmbda=\n%v\n", lmbda.ToString("%.17f")) //W.Print() checkpnt.Check("compute_scaling", 300) } // lmbdasq := lmbda o lmbda ssqr(lmbdasq, lmbda, dims, 0) lmbdasq.SetIndex(-1, lmbda.GetIndex(-1)*lmbda.GetIndex(-1)) // f3(x, y, z) solves // // [ 0 A' G' ] [ ux ] [ bx ] // [ A 0 0 ] [ uy ] = [ by ]. // [ G 0 -W'*W ] [ W^{-1}*uz ] [ bz ] // // On entry, x, y, z contain bx, by, bz. // On exit, they contain ux, uy, uz. // // Also solve // // [ 0 A' G' ] [ x1 ] [ c ] // [-A 0 0 ]*[ y1 ] = -dgi * [ b ]. // [-G 0 W'*W ] [ W^{-1}*z1 ] [ h ] f3, err = kktsolver(W) if err != nil { fmt.Printf("kktsolver error=%v\n", err) return } if iter == 0 { x1 = c.Copy() y1 = b.Copy() z1 = matrix.FloatZeros(cdim, 1) checkpnt.AddVerifiable("x1", x1) checkpnt.AddMatrixVar("z1", z1) } mCopy(c, x1) x1.Scal(-1.0) mCopy(b, y1) blas.Copy(h, z1) err = f3(x1, y1, z1) //fmt.Printf("f3 result: x1=\n%v\nf3 result: z1=\n%v\n", x1, z1) x1.Scal(dgi) y1.Scal(dgi) blas.ScalFloat(z1, dgi) if err != nil { if iter == 0 && primalstart != nil && dualstart != nil { err = errors.New("Rank(A) < p or Rank([G; A]) < n") return } else { t_ := 1.0 / tau.Float() x.Scal(t_) y.Scal(t_) blas.ScalFloat(s, t_) blas.ScalFloat(z, t_) ind := dims.Sum("l", "q") for _, m := range dims.At("s") { symm(s, m, ind) symm(z, m, ind) ind += m * m } ts, _ = maxStep(s, dims, 0, nil) tz, _ = maxStep(z, dims, 0, nil) err = errors.New("Terminated (singular KKT matrix).") //sol.X = x; sol.Y = y; sol.S = s; sol.Z = z sol.Result = sets.NewFloatSet("x", "y", "s", "x") sol.Result.Append("x", x.Matrix()) sol.Result.Append("y", y.Matrix()) sol.Result.Append("s", s) sol.Result.Append("z", z) sol.Status = Unknown sol.RelativeGap = relgap sol.PrimalObjective = pcost sol.DualObjective = dcost sol.PrimalInfeasibility = pres sol.DualInfeasibility = dres sol.PrimalSlack = -ts sol.DualSlack = -tz sol.Iterations = iter return } } // f6_no_ir(x, y, z, tau, s, kappa) solves // // [ 0 ] [ 0 A' G' c ] [ ux ] [ bx ] // [ 0 ] [ -A 0 0 b ] [ uy ] [ by ] // [ W'*us ] - [ -G 0 0 h ] [ W^{-1}*uz ] = -[ bz ] // [ dg*ukappa ] [ -c' -b' -h' 0 ] [ utau/dg ] [ btau ] // // lmbda o (uz + us) = -bs // lmbdag * (utau + ukappa) = -bkappa. // // On entry, x, y, z, tau, s, kappa contain bx, by, bz, btau, // bkappa. On exit, they contain ux, uy, uz, utau, ukappa. // th = W^{-T} * h if iter == 0 { th = matrix.FloatZeros(cdim, 1) checkpnt.AddMatrixVar("th", th) } blas.Copy(h, th) scale(th, W, true, true) f6_no_ir := func(x, y MatrixVariable, z, tau, s, kappa *matrix.FloatMatrix) (err error) { // Solve // // [ 0 A' G' 0 ] [ ux ] // [ -A 0 0 b ] [ uy ] // [ -G 0 W'*W h ] [ W^{-1}*uz ] // [ -c' -b' -h' k/t ] [ utau/dg ] // // [ bx ] // [ by ] // = [ bz - W'*(lmbda o\ bs) ] // [ btau - bkappa/tau ] // // us = -lmbda o\ bs - uz // ukappa = -bkappa/lmbdag - utau. // First solve // // [ 0 A' G' ] [ ux ] [ bx ] // [ A 0 0 ] [ uy ] = [ -by ] // [ G 0 -W'*W ] [ W^{-1}*uz ] [ -bz + W'*(lmbda o\ bs) ] minor := checkpnt.MinorTop() err = nil // y := -y = -by y.Scal(-1.0) // s := -lmbda o\ s = -lmbda o\ bs err = sinv(s, lmbda, dims, 0) blas.ScalFloat(s, -1.0) // z := -(z + W'*s) = -bz + W'*(lambda o\ bs) blas.Copy(s, ws3) checkpnt.Check("prescale", minor+5) checkpnt.MinorPush(minor + 5) err = scale(ws3, W, true, false) checkpnt.MinorPop() if err != nil { fmt.Printf("scale error: %s\n", err) } blas.AxpyFloat(ws3, z, 1.0) blas.ScalFloat(z, -1.0) checkpnt.Check("f3-call", minor+20) checkpnt.MinorPush(minor + 20) err = f3(x, y, z) checkpnt.MinorPop() checkpnt.Check("f3-return", minor+40) // Combine with solution of // // [ 0 A' G' ] [ x1 ] [ c ] // [-A 0 0 ] [ y1 ] = -dgi * [ b ] // [-G 0 W'*W ] [ W^{-1}*dzl ] [ h ] // // to satisfy // // -c'*x - b'*y - h'*W^{-1}*z + dg*tau = btau - bkappa/tau. ' // , kappa[0] := -kappa[0] / lmbd[-1] = -bkappa / lmbdag kap_ := kappa.Float() tau_ := tau.Float() kap_ = -kap_ / lmbda.GetIndex(-1) // tau[0] = tau[0] + kappa[0] / dgi = btau[0] - bkappa / tau tau_ = tau_ + kap_/dgi //tau[0] = dgi * ( tau[0] + xdot(c,x) + ydot(b,y) + // misc.sdot(th, z, dims) ) / (1.0 + misc.sdot(z1, z1, dims)) //tau_ = tau_ + blas.DotFloat(c, x) + blas.DotFloat(b, y) + sdot(th, z, dims, 0) tau_ += c.Dot(x) tau_ += b.Dot(y) tau_ += sdot(th, z, dims, 0) tau_ = dgi * tau_ / (1.0 + sdot(z1, z1, dims, 0)) tau.SetValue(tau_) x1.Axpy(x, tau_) y1.Axpy(y, tau_) blas.AxpyFloat(z1, z, tau_) blas.AxpyFloat(z, s, -1.0) kap_ = kap_ - tau_ kappa.SetValue(kap_) return } // f6(x, y, z, tau, s, kappa) solves the same system as f6_no_ir, // but applies iterative refinement. Following variables part of f6-closure // and ~ 12 is the limit. We wrap them to a structure. if iter == 0 { if refinement > 0 || solopts.Debug { WS.wx = c.Copy() WS.wy = b.Copy() WS.wz = matrix.FloatZeros(cdim, 1) WS.ws = matrix.FloatZeros(cdim, 1) WS.wtau = matrix.FloatValue(0.0) WS.wkappa = matrix.FloatValue(0.0) checkpnt.AddVerifiable("wx", WS.wx) checkpnt.AddMatrixVar("wz", WS.wz) checkpnt.AddMatrixVar("ws", WS.ws) } if refinement > 0 { WS.wx2 = c.Copy() WS.wy2 = b.Copy() WS.wz2 = matrix.FloatZeros(cdim, 1) WS.ws2 = matrix.FloatZeros(cdim, 1) WS.wtau2 = matrix.FloatValue(0.0) WS.wkappa2 = matrix.FloatValue(0.0) checkpnt.AddVerifiable("wx2", WS.wx2) checkpnt.AddMatrixVar("wz2", WS.wz2) checkpnt.AddMatrixVar("ws2", WS.ws2) } } f6 := func(x, y MatrixVariable, z, tau, s, kappa *matrix.FloatMatrix) error { var err error = nil minor := checkpnt.MinorTop() checkpnt.Check("startf6", minor+100) if refinement > 0 || solopts.Debug { mCopy(x, WS.wx) mCopy(y, WS.wy) blas.Copy(z, WS.wz) blas.Copy(s, WS.ws) WS.wtau.SetValue(tau.Float()) WS.wkappa.SetValue(kappa.Float()) } checkpnt.Check("pref6_no_ir", minor+200) err = f6_no_ir(x, y, z, tau, s, kappa) checkpnt.Check("postf6_no_ir", minor+399) for i := 0; i < refinement; i++ { mCopy(WS.wx, WS.wx2) mCopy(WS.wy, WS.wy2) blas.Copy(WS.wz, WS.wz2) blas.Copy(WS.ws, WS.ws2) WS.wtau2.SetValue(WS.wtau.Float()) WS.wkappa2.SetValue(WS.wkappa.Float()) checkpnt.Check("res-call", minor+400) checkpnt.MinorPush(minor + 400) err = res(x, y, z, tau, s, kappa, WS.wx2, WS.wy2, WS.wz2, WS.wtau2, WS.ws2, WS.wkappa2, W, dg, lmbda) checkpnt.MinorPop() checkpnt.Check("refine_pref6_no_ir", minor+500) checkpnt.MinorPush(minor + 500) err = f6_no_ir(WS.wx2, WS.wy2, WS.wz2, WS.wtau2, WS.ws2, WS.wkappa2) checkpnt.MinorPop() checkpnt.Check("refine_postf6_no_ir", minor+600) WS.wx2.Axpy(x, 1.0) WS.wy2.Axpy(y, 1.0) blas.AxpyFloat(WS.wz2, z, 1.0) blas.AxpyFloat(WS.ws2, s, 1.0) tau.SetValue(tau.Float() + WS.wtau2.Float()) kappa.SetValue(kappa.Float() + WS.wkappa2.Float()) } if solopts.Debug { checkpnt.MinorPush(minor + 700) res(x, y, z, tau, s, kappa, WS.wx, WS.wy, WS.wz, WS.wtau, WS.ws, WS.wkappa, W, dg, lmbda) checkpnt.MinorPop() fmt.Printf("KKT residuals\n") fmt.Printf(" 'x' : %.6e\n", math.Sqrt(WS.wx.Dot(WS.wx))) fmt.Printf(" 'y' : %.6e\n", math.Sqrt(WS.wy.Dot(WS.wy))) fmt.Printf(" 'z' : %.6e\n", snrm2(WS.wz, dims, 0)) fmt.Printf(" 'tau' : %.6e\n", math.Abs(WS.wtau.Float())) fmt.Printf(" 's' : %.6e\n", snrm2(WS.ws, dims, 0)) fmt.Printf(" 'kappa': %.6e\n", math.Abs(WS.wkappa.Float())) } return err } var nrm float64 = blas.Nrm2Float(lmbda) mu := math.Pow(nrm, 2.0) / (1.0 + float64(cdim_diag)) sigma := 0.0 var step, tt, tk float64 for i := 0; i < 2; i++ { // Solve // // [ 0 ] [ 0 A' G' c ] [ dx ] // [ 0 ] [ -A 0 0 b ] [ dy ] // [ W'*ds ] - [ -G 0 0 h ] [ W^{-1}*dz ] // [ dg*dkappa ] [ -c' -b' -h' 0 ] [ dtau/dg ] // // [ rx ] // [ ry ] // = - (1-sigma) [ rz ] // [ rtau ] // // lmbda o (dz + ds) = -lmbda o lmbda + sigma*mu*e // lmbdag * (dtau + dkappa) = - kappa * tau + sigma*mu // // ds = -lmbdasq if i is 0 // = -lmbdasq - dsa o dza + sigma*mu*e if i is 1 // dkappa = -lambdasq[-1] if i is 0 // = -lambdasq[-1] - dkappaa*dtaua + sigma*mu if i is 1. ind := dims.Sum("l", "q") ind2 := ind blas.Copy(lmbdasq, ds, &la.IOpt{"n", ind}) blas.ScalFloat(ds, 0.0, &la.IOpt{"offset", ind}) for _, m := range dims.At("s") { blas.Copy(lmbdasq, ds, &la.IOpt{"n", m}, &la.IOpt{"offsetx", ind2}, &la.IOpt{"offsety", ind}, &la.IOpt{"incy", m + 1}) ind += m * m ind2 += m } // dkappa[0] = lmbdasq[-1] dkappa.SetValue(lmbdasq.GetIndex(-1)) if i == 1 { blas.AxpyFloat(ws3, ds, 1.0) ind = dims.Sum("l", "q") is := make([]int, 0) // indexes: [:dims['l']] if dims.At("l")[0] > 0 { is = append(is, matrix.MakeIndexSet(0, dims.At("l")[0], 1)...) } // ...[indq[:-1]] if len(indq) > 1 { is = append(is, indq[:len(indq)-1]...) } // ...[ind : ind+m*m : m+1] (diagonal) for _, m := range dims.At("s") { is = append(is, matrix.MakeIndexSet(ind, ind+m*m, m+1)...) ind += m * m } //ds.Add(-sigma*mu, is...) for _, k := range is { ds.SetIndex(k, ds.GetIndex(k)-sigma*mu) } dk_ := dkappa.Float() wk_ := wkappa3.Float() dkappa.SetValue(dk_ + wk_ - sigma*mu) } // (dx, dy, dz, dtau) = (1-sigma)*(rx, ry, rz, rt) mCopy(rx, dx) dx.Scal(1.0 - sigma) mCopy(ry, dy) dy.Scal(1.0 - sigma) blas.Copy(rz, dz) blas.ScalFloat(dz, 1.0-sigma) // dtau[0] = (1.0 - sigma) * rt dtau.SetValue((1.0 - sigma) * rt) checkpnt.Check("pref6", (1+i)*1000) checkpnt.MinorPush((1 + i) * 1000) err = f6(dx, dy, dz, dtau, ds, dkappa) checkpnt.MinorPop() checkpnt.Check("postf6", (1+i)*1000+800) // Save ds o dz and dkappa * dtau for Mehrotra correction if i == 0 { blas.Copy(ds, ws3) sprod(ws3, dz, dims, 0) wkappa3.SetValue(dtau.Float() * dkappa.Float()) } // Maximum step to boundary. // // If i is 1, also compute eigenvalue decomposition of the 's' // blocks in ds, dz. The eigenvectors Qs, Qz are stored in // dsk, dzk. The eigenvalues are stored in sigs, sigz. var ts, tz float64 checkpnt.MinorPush((1+i)*1000 + 900) scale2(lmbda, ds, dims, 0, false) scale2(lmbda, dz, dims, 0, false) checkpnt.MinorPop() checkpnt.Check("post-scale2", (1+i)*1000+990) if i == 0 { ts, _ = maxStep(ds, dims, 0, nil) tz, _ = maxStep(dz, dims, 0, nil) } else { ts, _ = maxStep(ds, dims, 0, sigs) tz, _ = maxStep(dz, dims, 0, sigz) } dt_ := dtau.Float() dk_ := dkappa.Float() tt = -dt_ / lmbda.GetIndex(-1) tk = -dk_ / lmbda.GetIndex(-1) t := maxvec([]float64{0.0, ts, tz, tt, tk}) if t == 0.0 { step = 1.0 } else { if i == 0 { step = math.Min(1.0, 1.0/t) } else { step = math.Min(1.0, STEP/t) } } if i == 0 { // sigma = (1 - step)^3 sigma = (1.0 - step) * (1.0 - step) * (1.0 - step) //sigma = math.Pow((1.0 - step), EXPON) } } //fmt.Printf("** tau = %.17f, kappa = %.17f\n", tau.Float(), kappa.Float()) //fmt.Printf("** step = %.17f, sigma = %.17f\n", step, sigma) checkpnt.Check("update-xy", 7000) // Update x, y dx.Axpy(x, step) dy.Axpy(y, step) // Replace '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 'l' and 'q' blocks. // dz := e + step*dz for 'l' and 'q' blocks. blas.ScalFloat(ds, step, &la.IOpt{"n", dims.Sum("l", "q")}) blas.ScalFloat(dz, step, &la.IOpt{"n", dims.Sum("l", "q")}) is := make([]int, 0) is = append(is, matrix.MakeIndexSet(0, dims.At("l")[0], 1)...) is = append(is, indq[:len(indq)-1]...) for _, k := range is { ds.SetIndex(k, 1.0+ds.GetIndex(k)) dz.SetIndex(k, 1.0+dz.GetIndex(k)) } checkpnt.Check("update-dsdz", 7500) // 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} checkpnt.MinorPush(7500) scale2(lmbda, ds, dims, 0, true) scale2(lmbda, dz, dims, 0, true) checkpnt.MinorPop() // 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", qdimsum}) blas.TbsvFloat(lmbda, sigz, &la.IOpt{"n", sdimsum}, &la.IOpt{"k", 0}, &la.IOpt{"lda", 1}, &la.IOpt{"offseta", qdimsum}) ind2 := 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("pre-update-scaling", 7700) err = updateScaling(W, lmbda, ds, dz) checkpnt.Check("post-update-scaling", 7800) // For kappa, tau block: // // dg := sqrt( (kappa + step*dkappa) / (tau + step*dtau) ) // = dg * sqrt( (1 - step*tk) / (1 - step*tt) ) // // lmbda[-1] := sqrt((tau + step*dtau) * (kappa + step*dkappa)) // = lmbda[-1] * sqrt(( 1 - step*tt) * (1 - step*tk)) dg *= math.Sqrt(1.0-step*tk) / math.Sqrt(1.0-step*tt) dgi = 1.0 / dg a := math.Sqrt(1.0-step*tk) * math.Sqrt(1.0-step*tt) lmbda.SetIndex(-1, a*lmbda.GetIndex(-1)) // Unscale s, z, tau, kappa (unscaled variables are used only to // compute feasibility residuals). ind := 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) ind = 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) kappa.SetValue(lmbda.GetIndex(-1) / dgi) tau.SetValue(lmbda.GetIndex(-1) * dgi) g := blas.Nrm2Float(lmbda, &la.IOpt{"n", lmbda.Rows() - 1}) / tau.Float() gap = g * g checkpnt.Check("end-of-loop", 8000) //fmt.Printf(" ** kappa=%.10f, tau=%.10f, gap=%.10f\n", kappa.Float(), tau.Float(), gap) } return }