Beispiel #1
0
func solve_problem(fnkey string, problem int) (opt *goga.Optimiser) {

	// GA parameters
	opt = new(goga.Optimiser)
	opt.Default()

	// options for report
	opt.RptFmtF = "%.4f"
	opt.RptFmtX = "%.3f"
	opt.RptFmtFdev = "%.1e"
	opt.RptWordF = "\\beta"
	opt.HistFmt = "%.2f"
	opt.HistNdig = 3
	opt.HistDelFmin = 0.005
	opt.HistDelFmax = 0.005

	// FORM data
	var lsft LSF_T
	var vars rnd.Variables

	// simple problem or FEM sim
	if fnkey == "simple" {
		opt.Read("ga-simple.json")
		opt.ProbNum = problem
		lsft, vars = get_simple_data(opt)
		fnkey += io.Sf("-%d", opt.ProbNum)
		io.Pf("\n----------------------------------- simple problem %d --------------------------------\n", opt.ProbNum)
	} else {
		opt.Read("ga-" + fnkey + ".json")
		lsft, vars = get_femsim_data(opt, fnkey)
		io.Pf("\n----------------------------------- femsim %s --------------------------------\n", fnkey)
	}

	// set limits
	nx := len(vars)
	opt.FltMin = make([]float64, nx)
	opt.FltMax = make([]float64, nx)
	for i, dat := range vars {
		opt.FltMin[i] = dat.Min
		opt.FltMax[i] = dat.Max
	}

	// log input
	var buf bytes.Buffer
	io.Ff(&buf, "%s", opt.LogParams())
	io.WriteFileVD("/tmp/gosl", fnkey+".log", &buf)

	// initialise distributions
	err := vars.Init()
	if err != nil {
		chk.Panic("cannot initialise distributions:\n%v", err)
	}

	// plot distributions
	if opt.PlotSet1 {
		io.Pf(". . . . . . . .  plot distributions  . . . . . . . .\n")
		np := 201
		for i, dat := range vars {
			plt.SetForEps(0.75, 250)
			dat.PlotPdf(np, "'b-',lw=2,zorder=1000")
			//plt.AxisXrange(dat.Min, dat.Max)
			plt.SetXnticks(15)
			plt.SaveD("/tmp/sims", io.Sf("distr-%s-%d.eps", fnkey, i))
		}
		return
	}

	// objective function
	nf := 1
	var ng, nh int
	var fcn goga.MinProb_t
	var obj goga.ObjFunc_t
	switch opt.Strategy {

	// argmin_x{ β(y(x)) | lsf(x) ≤ 0 }
	//  f ← sqrt(y dot y)
	//  g ← -lsf(x) ≥ 0
	//  h ← out-of-range in case Transform fails
	case 0:
		ng, nh = 1, 1
		fcn = func(f, g, h, x []float64, ξ []int, cpu int) {

			// original and normalised variables
			h[0] = 0
			y, invalid := vars.Transform(x)
			if invalid {
				h[0] = 1
				return
			}

			// objective value
			f[0] = math.Sqrt(la.VecDot(y, y)) // β

			// inequality constraint
			lsf, failed := lsft(x, cpu)
			g[0] = -lsf
			h[0] = failed
		}

	// argmin_x{ β(y(x)) | lsf(x) = 0 }
	//  f  ← sqrt(y dot y)
	//  h0 ← lsf(x)
	//  h1 ← out-of-range in case Transform fails
	case 1:
		ng, nh = 0, 2
		fcn = func(f, g, h, x []float64, ξ []int, cpu int) {

			// original and normalised variables
			h[0], h[1] = 0, 0
			y, invalid := vars.Transform(x)
			if invalid {
				h[0], h[1] = 1, 1
				return
			}

			// objective value
			f[0] = math.Sqrt(la.VecDot(y, y)) // β

			// equality constraint
			lsf, failed := lsft(x, cpu)
			h[0] = lsf
			h[1] = failed

			// induce minmisation of h0
			//f[0] += math.Abs(lsf)
		}

	case 2:
		opt.Nova = 1
		opt.Noor = 2
		obj = func(sol *goga.Solution, cpu int) {

			// clear out-of-range values
			sol.Oor[0] = 0 // invalid transformation or FEM failed
			sol.Oor[1] = 0 // g(x) ≤ 0 was violated

			// original and normalised variables
			x := sol.Flt
			y, invalid := vars.Transform(x)
			if invalid {
				sol.Oor[0] = goga.INF
				sol.Oor[1] = goga.INF
				return
			}

			// objective value
			sol.Ova[0] = math.Sqrt(la.VecDot(y, y)) // β

			// inequality constraint
			lsf, failed := lsft(x, cpu)
			sol.Oor[0] = failed
			sol.Oor[1] = fun.Ramp(lsf)
		}

	default:
		chk.Panic("strategy %d is not available", opt.Strategy)
	}

	// initialise optimiser
	opt.Init(goga.GenTrialSolutions, obj, fcn, nf, ng, nh)

	// solve
	io.Pf(". . . . . . . .  running  . . . . . . . .\n")
	opt.RunMany("", "")
	goga.StatF(opt, 0, true)
	io.Pfblue2("Tsys = %v\n", opt.SysTimeAve)

	// check
	goga.CheckFront0(opt, true)

	// results
	sols := goga.GetFeasible(opt.Solutions)
	if len(sols) > 0 {
		goga.SortByOva(sols, 0)
		best := sols[0]
		io.Pforan("x    = %.6f\n", best.Flt)
		io.Pforan("xref = %.6f\n", opt.RptXref)
		io.Pforan("β = %v  (%v)\n", best.Ova[0], opt.RptFref[0])
	}
	return
}
Beispiel #2
0
// Solve solves non-linear problem f(x) == 0
func (o *NlSolver) Solve(x []float64, silent bool) (err error) {

	// compute scaling vector
	la.VecScaleAbs(o.scal, o.atol, o.rtol, x) // scal := Atol + Rtol*abs(x)

	// evaluate function @ x
	err = o.Ffcn(o.fx, x) // fx := f(x)
	o.NFeval, o.NJeval = 1, 0
	if err != nil {
		return
	}

	// show message
	if !silent {
		o.msg("", 0, 0, 0, true, false)
	}

	// iterations
	var Ldx, Ldx_prev, Θ float64 // RMS norm of delta x, convergence rate
	var fx_max float64
	var nfv int
	for o.It = 0; o.It < o.MaxIt; o.It++ {

		// check convergence on f(x)
		fx_max = la.VecLargest(o.fx, 1.0) // den = 1.0
		if fx_max < o.ftol {
			if !silent {
				o.msg("fx_max(ini)", o.It, Ldx, fx_max, false, true)
			}
			break
		}

		// show message
		if !silent {
			o.msg("", o.It, Ldx, fx_max, false, false)
		}

		// output
		if o.Out != nil {
			o.Out(x)
		}

		// evaluate Jacobian @ x
		if o.It == 0 || !o.CteJac {
			if o.useDn {
				err = o.JfcnDn(o.J, x)
			} else {
				if o.numJ {
					err = Jacobian(&o.Jtri, o.Ffcn, x, o.fx, o.w, false)
					o.NFeval += o.neq
				} else {
					err = o.JfcnSp(&o.Jtri, x)
				}
			}
			o.NJeval += 1
			if err != nil {
				return
			}
		}

		// dense solution
		if o.useDn {

			// invert matrix
			err = la.MatInvG(o.Ji, o.J, 1e-10)
			if err != nil {
				return chk.Err(_nls_err1, err.Error())
			}

			// solve linear system (compute mdx) and compute lin-search data
			o.φ = 0.0
			for i := 0; i < o.neq; i++ {
				o.mdx[i], o.dφdx[i] = 0.0, 0.0
				for j := 0; j < o.neq; j++ {
					o.mdx[i] += o.Ji[i][j] * o.fx[j] // mdx  = inv(J) * fx
					o.dφdx[i] += o.J[j][i] * o.fx[j] // dφdx = tra(J) * fx
				}
				o.φ += o.fx[i] * o.fx[i]
			}
			o.φ *= 0.5

			// sparse solution
		} else {

			// init sparse solver
			if o.It == 0 {
				symmetric, verbose, timing := false, false, false
				err := o.lis.InitR(&o.Jtri, symmetric, verbose, timing)
				if err != nil {
					return chk.Err(_nls_err9, err.Error())
				}
			}

			// factorisation (must be done for all iterations)
			o.lis.Fact()

			// solve linear system => compute mdx
			o.lis.SolveR(o.mdx, o.fx, false) // mdx = inv(J) * fx   false => !sumToRoot

			// compute lin-search data
			if o.Lsearch {
				o.φ = 0.5 * la.VecDot(o.fx, o.fx)
				la.SpTriMatTrVecMul(o.dφdx, &o.Jtri, o.fx) // dφdx := transpose(J) * fx
			}
		}

		//io.Pforan("φ    = %v\n", o.φ)
		//io.Pforan("dφdx = %v\n", o.dφdx)

		// update x
		Ldx = 0.0
		for i := 0; i < o.neq; i++ {
			o.x0[i] = x[i]
			x[i] -= o.mdx[i]
			Ldx += (o.mdx[i] / o.scal[i]) * (o.mdx[i] / o.scal[i])
		}
		Ldx = math.Sqrt(Ldx / float64(o.neq))

		// calculate fx := f(x) @ update x
		err = o.Ffcn(o.fx, x)
		o.NFeval += 1
		if err != nil {
			return
		}

		// check convergence on f(x) => avoid line-search if converged already
		fx_max = la.VecLargest(o.fx, 1.0) // den = 1.0
		if fx_max < o.ftol {
			if !silent {
				o.msg("fx_max", o.It, Ldx, fx_max, false, true)
			}
			break
		}

		// check convergence on Ldx
		if Ldx < o.fnewt {
			if !silent {
				o.msg("Ldx", o.It, Ldx, fx_max, false, true)
			}
			break
		}

		// call line-search => update x and fx
		if o.Lsearch {
			nfv, err = LineSearch(x, o.fx, o.Ffcn, o.mdx, o.x0, o.dφdx, o.φ, o.LsMaxIt, true)
			o.NFeval += nfv
			if err != nil {
				return chk.Err(_nls_err2, err.Error())
			}
			Ldx = 0.0
			for i := 0; i < o.neq; i++ {
				Ldx += ((x[i] - o.x0[i]) / o.scal[i]) * ((x[i] - o.x0[i]) / o.scal[i])
			}
			Ldx = math.Sqrt(Ldx / float64(o.neq))
			fx_max = la.VecLargest(o.fx, 1.0) // den = 1.0
			if Ldx < o.fnewt {
				if !silent {
					o.msg("Ldx(linsrch)", o.It, Ldx, fx_max, false, true)
				}
				break
			}
		}

		// check convergence rate
		if o.It > 0 && o.ChkConv {
			Θ = Ldx / Ldx_prev
			if Θ > 0.99 {
				return chk.Err(_nls_err3, Θ, Ldx, Ldx_prev)
			}
		}
		Ldx_prev = Ldx
	}

	// output
	if o.Out != nil {
		o.Out(x)
	}

	// check convergence
	if o.It == o.MaxIt {
		err = chk.Err(_nls_err4, o.It)
	}
	return
}
Beispiel #3
0
func Test_geninvs01(tst *testing.T) {

	//verbose()
	chk.PrintTitle("geninvs01")

	// coefficients for smp invariants
	smp_a := -1.0
	smp_b := 0.5
	smp_β := 1e-1 // derivative values become too high with
	smp_ϵ := 1e-1 // small β and ϵ @ zero

	// constants for checking derivatives
	dver := chk.Verbose
	dtol := 1e-6
	dtol2 := 1e-6

	// run tests
	nd := test_nd
	for idxA := 0; idxA < len(test_nd); idxA++ {
		//for idxA := 10; idxA < 11; idxA++ {

		// tensor and eigenvalues
		A := test_AA[idxA]
		a := M_Alloc2(nd[idxA])
		Ten2Man(a, A)
		L := make([]float64, 3)
		M_EigenValsNum(L, a)

		// SMP derivs and SMP director
		dndL := la.MatAlloc(3, 3)
		dNdL := make([]float64, 3)
		d2ndLdL := utl.Deep3alloc(3, 3, 3)
		N := make([]float64, 3)
		F := make([]float64, 3)
		G := make([]float64, 3)
		m := SmpDerivs1(dndL, dNdL, N, F, G, L, smp_a, smp_b, smp_β, smp_ϵ)
		SmpDerivs2(d2ndLdL, L, smp_a, smp_b, smp_β, smp_ϵ, m, N, F, G, dNdL, dndL)
		n := make([]float64, 3)
		SmpUnitDirector(n, m, N)

		// SMP invariants
		p, q, err := GenInvs(L, n, smp_a)
		if err != nil {
			chk.Panic("SmpInvs failed:\n%v", err)
		}

		// output
		io.PfYel("\n\ntst # %d ###################################################################################\n", idxA)
		io.Pfblue2("L = %v\n", L)
		io.Pforan("n = %v\n", n)
		io.Pforan("p = %v\n", p)
		io.Pforan("q = %v\n", q)

		// check invariants
		tvec := make([]float64, 3)
		GenTvec(tvec, L, n)
		proj := make([]float64, 3) // projection of tvec along n
		tdn := la.VecDot(tvec, n)  // tvec dot n
		for i := 0; i < 3; i++ {
			proj[i] = tdn * n[i]
		}
		norm_proj := la.VecNorm(proj)
		norm_tvec := la.VecNorm(tvec)
		q_ := GENINVSQEPS + math.Sqrt(norm_tvec*norm_tvec-norm_proj*norm_proj)
		io.Pforan("proj = %v\n", proj)
		io.Pforan("norm(proj) = %v == p\n", norm_proj)
		chk.Scalar(tst, "p", 1e-14, math.Abs(p), norm_proj)
		chk.Scalar(tst, "q", 1e-13, q, q_)

		// dt/dL
		var tmp float64
		N_tmp := make([]float64, 3)
		n_tmp := make([]float64, 3)
		tvec_tmp := make([]float64, 3)
		dtdL := la.MatAlloc(3, 3)
		GenTvecDeriv1(dtdL, L, n, dndL)
		for i := 0; i < 3; i++ {
			for j := 0; j < 3; j++ {
				dnum, _ := num.DerivCentral(func(x float64, args ...interface{}) (res float64) {
					tmp, L[j] = L[j], x
					m_tmp := SmpDirector(N_tmp, L, smp_a, smp_b, smp_β, smp_ϵ)
					SmpUnitDirector(n_tmp, m_tmp, N_tmp)
					GenTvec(tvec_tmp, L, n_tmp)
					L[j] = tmp
					return tvec_tmp[i]
				}, L[j], 1e-6)
				chk.AnaNum(tst, io.Sf("dt/dL[%d][%d]", i, j), dtol, dtdL[i][j], dnum, dver)
			}
		}

		// d²t/dLdL
		io.Pfpink("\nd²t/dLdL\n")
		dNdL_tmp := make([]float64, 3)
		dndL_tmp := la.MatAlloc(3, 3)
		dtdL_tmp := la.MatAlloc(3, 3)
		for i := 0; i < 3; i++ {
			for j := 0; j < 3; j++ {
				for k := 0; k < 3; k++ {
					dnum, _ := num.DerivCentral(func(x float64, args ...interface{}) (res float64) {
						tmp, L[k] = L[k], x
						m_tmp := SmpDerivs1(dndL_tmp, dNdL_tmp, N_tmp, F, G, L, smp_a, smp_b, smp_β, smp_ϵ)
						SmpUnitDirector(n_tmp, m_tmp, N_tmp)
						GenTvecDeriv1(dtdL_tmp, L, n_tmp, dndL_tmp)
						L[k] = tmp
						return dtdL_tmp[i][j]
					}, L[k], 1e-6)
					dana := GenTvecDeriv2(i, j, k, L, dndL, d2ndLdL[i][j][k])
					chk.AnaNum(tst, io.Sf("d²t[%d]/dL[%d]dL[%d]", i, j, k), dtol2, dana, dnum, dver)
				}
			}
		}

		// change tolerance
		dtol_tmp := dtol
		switch idxA {
		case 5, 11:
			dtol = 1e-5
		case 12:
			dtol = 0.0013
		}

		// first order derivatives
		dpdL := make([]float64, 3)
		dqdL := make([]float64, 3)
		p_, q_, err := GenInvsDeriv1(dpdL, dqdL, L, n, dndL, smp_a)
		if err != nil {
			chk.Panic("%v", err)
		}
		chk.Scalar(tst, "p", 1e-17, p, p_)
		chk.Scalar(tst, "q", 1e-17, q, q_)
		var ptmp, qtmp float64
		io.Pfpink("\ndp/dL\n")
		for j := 0; j < 3; j++ {
			dnum, _ := num.DerivCentral(func(x float64, args ...interface{}) (res float64) {
				tmp, L[j] = L[j], x
				m_tmp := SmpDirector(N_tmp, L, smp_a, smp_b, smp_β, smp_ϵ)
				SmpUnitDirector(n_tmp, m_tmp, N_tmp)
				ptmp, _, err = GenInvs(L, n_tmp, smp_a)
				if err != nil {
					chk.Panic("DerivCentral: SmpInvs failed:\n%v", err)
				}
				L[j] = tmp
				return ptmp
			}, L[j], 1e-6)
			chk.AnaNum(tst, io.Sf("dp/dL[%d]", j), dtol, dpdL[j], dnum, dver)
		}
		io.Pfpink("\ndq/dL\n")
		for j := 0; j < 3; j++ {
			dnum, _ := num.DerivCentral(func(x float64, args ...interface{}) (res float64) {
				tmp, L[j] = L[j], x
				m_tmp := SmpDirector(N_tmp, L, smp_a, smp_b, smp_β, smp_ϵ)
				SmpUnitDirector(n_tmp, m_tmp, N_tmp)
				_, qtmp, err = GenInvs(L, n_tmp, smp_a)
				if err != nil {
					chk.Panic("DerivCentral: SmpInvs failed:\n%v", err)
				}
				L[j] = tmp
				return qtmp
			}, L[j], 1e-6)
			chk.AnaNum(tst, io.Sf("dq/dL[%d]", j), dtol, dqdL[j], dnum, dver)
		}

		// recover tolerance
		dtol = dtol_tmp

		// change tolerance
		io.Pforan("dtol2 = %v\n", dtol2)
		dtol2_tmp := dtol2
		switch idxA {
		case 5:
			dtol2 = 1e-5
		case 10:
			dtol2 = 0.72
		case 11:
			dtol2 = 1e-5
		case 12:
			dtol2 = 544
		}

		// second order derivatives
		dpdL_tmp := make([]float64, 3)
		dqdL_tmp := make([]float64, 3)
		d2pdLdL := la.MatAlloc(3, 3)
		d2qdLdL := la.MatAlloc(3, 3)
		GenInvsDeriv2(d2pdLdL, d2qdLdL, L, n, dpdL, dqdL, p, q, dndL, d2ndLdL, smp_a)
		io.Pfpink("\nd²p/dLdL\n")
		for i := 0; i < 3; i++ {
			for j := 0; j < 3; j++ {
				dnum, _ := num.DerivCentral(func(x float64, args ...interface{}) (res float64) {
					tmp, L[j] = L[j], x
					m_tmp := SmpDerivs1(dndL_tmp, dNdL_tmp, N_tmp, F, G, L, smp_a, smp_b, smp_β, smp_ϵ)
					SmpUnitDirector(n_tmp, m_tmp, N_tmp)
					GenInvsDeriv1(dpdL_tmp, dqdL_tmp, L, n_tmp, dndL_tmp, smp_a)
					L[j] = tmp
					return dpdL_tmp[i]
				}, L[j], 1e-6)
				chk.AnaNum(tst, io.Sf("d²p/dL[%d][%d]", i, j), dtol2, d2pdLdL[i][j], dnum, dver)
			}
		}
		io.Pfpink("\nd²q/dLdL\n")
		for i := 0; i < 3; i++ {
			for j := 0; j < 3; j++ {
				dnum, _ := num.DerivCentral(func(x float64, args ...interface{}) (res float64) {
					tmp, L[j] = L[j], x
					m_tmp := SmpDerivs1(dndL_tmp, dNdL_tmp, N_tmp, F, G, L, smp_a, smp_b, smp_β, smp_ϵ)
					SmpUnitDirector(n_tmp, m_tmp, N_tmp)
					GenInvsDeriv1(dpdL_tmp, dqdL_tmp, L, n_tmp, dndL_tmp, smp_a)
					L[j] = tmp
					return dqdL_tmp[i]
				}, L[j], 1e-6)
				chk.AnaNum(tst, io.Sf("d²q/dL[%d][%d]", i, j), dtol2, d2qdLdL[i][j], dnum, dver)
			}
		}

		// recover tolerance
		dtol2 = dtol2_tmp
	}
}
Beispiel #4
0
// Solve solves linear programming problem
func (o *LinIpm) Solve(verbose bool) (err error) {

	// starting point
	AAt := la.MatAlloc(o.Nl, o.Nl)         // A*Aᵀ
	d := make([]float64, o.Nl)             // inv(AAt) * b
	e := make([]float64, o.Nl)             // A * c
	la.SpMatMatTrMul(AAt, 1, o.A)          // AAt := A*Aᵀ
	la.SpMatVecMul(e, 1, o.A, o.C)         // e := A * c
	la.SPDsolve2(d, o.L, AAt, o.B, e)      // d := inv(AAt) * b  and  L := inv(AAt) * e
	la.SpMatTrVecMul(o.X, 1, o.A, d)       // x := Aᵀ * d
	la.VecCopy(o.S, 1, o.C)                // s := c
	la.SpMatTrVecMulAdd(o.S, -1, o.A, o.L) // s -= Aᵀλ
	xmin := o.X[0]
	smin := o.S[0]
	for i := 1; i < o.Nx; i++ {
		xmin = min(xmin, o.X[i])
		smin = min(smin, o.S[i])
	}
	δx := max(-1.5*xmin, 0)
	δs := max(-1.5*smin, 0)
	var xdots, xsum, ssum float64
	for i := 0; i < o.Nx; i++ {
		o.X[i] += δx
		o.S[i] += δs
		xdots += o.X[i] * o.S[i]
		xsum += o.X[i]
		ssum += o.S[i]
	}
	δx = 0.5 * xdots / ssum
	δs = 0.5 * xdots / xsum
	for i := 0; i < o.Nx; i++ {
		o.X[i] += δx
		o.S[i] += δs
	}

	// constants for linear solver
	symmetric := false
	timing := false

	// auxiliary
	I := o.Nx + o.Nl

	// control variables
	var μ, σ float64     // μ and σ
	var xrmin float64    // min{ x_i / (-Δx_i) } (x_ratio_minimum)
	var srmin float64    // min{ s_i / (-Δs_i) } (s_ratio_minimum)
	var αpa float64      // α_prime_affine
	var αda float64      // α_dual_affine
	var μaff float64     // μ_affine
	var ctx, btl float64 // cᵀx and bᵀl

	// message
	if verbose {
		io.Pf("%3s%16s%16s\n", "it", "f(x)", "error")
	}

	// perform iterations
	it := 0
	for it = 0; it < o.NmaxIt; it++ {

		// compute residual
		la.SpMatTrVecMul(o.Rx, 1, o.A, o.L) // rx := Aᵀλ
		la.SpMatVecMul(o.Rl, 1, o.A, o.X)   // rλ := A x
		ctx, btl, μ = 0, 0, 0
		for i := 0; i < o.Nx; i++ {
			o.Rx[i] += o.S[i] - o.C[i]
			o.Rs[i] = o.X[i] * o.S[i]
			ctx += o.C[i] * o.X[i]
			μ += o.X[i] * o.S[i]
		}
		for i := 0; i < o.Nl; i++ {
			o.Rl[i] -= o.B[i]
			btl += o.B[i] * o.L[i]
		}
		μ /= float64(o.Nx)

		// check convergence
		lerr := math.Abs(ctx-btl) / (1.0 + math.Abs(ctx))
		if verbose {
			fx := la.VecDot(o.C, o.X)
			io.Pf("%3d%16.8e%16.8e\n", it, fx, lerr)
		}
		if lerr < o.Tol {
			break
		}

		// assemble Jacobian
		o.J.Start()
		o.J.PutCCMatAndMatT(o.A)
		for i := 0; i < o.Nx; i++ {
			o.J.Put(i, I+i, 1.0)
			o.J.Put(I+i, i, o.S[i])
			o.J.Put(I+i, I+i, o.X[i])
		}

		// solve linear system
		if it == 0 {
			err = o.Lis.InitR(o.J, symmetric, false, timing)
			if err != nil {
				return
			}
		}
		err = o.Lis.Fact()
		if err != nil {
			return
		}
		err = o.Lis.SolveR(o.Mdy, o.R, false) // mdy := inv(J) * R
		if err != nil {
			return
		}

		// control variables
		xrmin, srmin = o.calc_min_ratios()
		αpa = min(1, xrmin)
		αda = min(1, srmin)
		μaff = 0
		for i := 0; i < o.Nx; i++ {
			μaff += (o.X[i] - αpa*o.Mdx[i]) * (o.S[i] - αda*o.Mds[i])
		}
		μaff /= float64(o.Nx)
		σ = math.Pow(μaff/μ, 3)

		// update residual
		for i := 0; i < o.Nx; i++ {
			o.Rs[i] += o.Mdx[i]*o.Mds[i] - σ*μ
		}

		// solve linear system again
		err = o.Lis.SolveR(o.Mdy, o.R, false) // mdy := inv(J) * R
		if err != nil {
			return
		}

		// step lengths
		xrmin, srmin = o.calc_min_ratios()
		αpa = min(1, 0.99*xrmin)
		αda = min(1, 0.99*srmin)

		// update
		for i := 0; i < o.Nx; i++ {
			o.X[i] -= αpa * o.Mdx[i]
			o.S[i] -= αda * o.Mds[i]
		}
		for i := 0; i < o.Nl; i++ {
			o.L[i] -= αda * o.Mdl[i]
		}
	}

	// check convergence
	if it == o.NmaxIt {
		err = chk.Err("iterations did not converge")
	}
	return
}