Beispiel #1
0
    def __call__(self, mat, src, psi):
        verbose = g.default.is_verbose("mr")
        t0 = time()

        r, mmr = g.copy(src), g.copy(src)

        mat(psi, mmr)
        r @= src - mmr

        ssq = g.norm2(src)
        rsq = self.eps**2. * ssq

        for k in range(self.maxiter):
            mat(r, mmr)
            ip, mmr2 = g.innerProductNorm2(mmr, r)

            if mmr2 == 0.:
                continue

            alpha = ip.real / mmr2 * self.relax

            psi += alpha * r
            r2 = g.axpy_norm2(r, -alpha, mmr, r)

            if verbose:
                g.message("res^2[ %d ] = %g" % (k, r2))

            if r2 <= rsq:
                if verbose:
                    t1 = time()
                    g.message("Converged in %g s" % (t1 - t0))
                break
Beispiel #2
0
 def __call__(self, mat, src, psi):
     assert (src != psi)
     self.history = []
     verbose = g.default.is_verbose("cg")
     t0 = g.time()
     p, mmp, r = g.copy(src), g.copy(src), g.copy(src)
     guess = g.norm2(psi)
     mat(psi, mmp)  # in, out
     d = g.innerProduct(psi, mmp).real
     b = g.norm2(mmp)
     r @= src - mmp
     p @= r
     a = g.norm2(p)
     cp = a
     ssq = g.norm2(src)
     rsq = self.eps**2. * ssq
     for k in range(1, self.maxiter + 1):
         c = cp
         mat(p, mmp)
         dc = g.innerProduct(p, mmp)
         d = dc.real
         a = c / d
         cp = g.axpy_norm2(r, -a, mmp, r)
         b = cp / c
         psi += a * p
         p @= b * p + r
         self.history.append(cp)
         if verbose:
             g.message("res^2[ %d ] = %g" % (k, cp))
         if cp <= rsq:
             if verbose:
                 t1 = g.time()
                 g.message("Converged in %g s" % (t1 - t0))
             break
Beispiel #3
0
    def __call__(self, mat, src, psi):
        verbose = g.default.is_verbose("bicgstab")
        t0 = time()

        r, rhat, p, s = g.copy(src), g.copy(src), g.copy(src), g.copy(src)
        mmpsi, mmp, mms = g.copy(src), g.copy(src), g.copy(src)

        rho, rhoprev, alpha, omega = 1., 1., 1., 1.

        mat(psi, mmpsi)
        r @= src - mmpsi

        rhat @= r
        p @= r
        mmp @= r

        ssq = g.norm2(src)
        rsq = self.eps**2. * ssq

        for k in range(self.maxiter):
            rhoprev = rho
            rho = g.innerProduct(rhat, r).real

            beta = (rho / rhoprev) * (alpha / omega)

            p @= r + beta * p - beta * omega * mmp

            mat(p, mmp)
            alpha = rho / g.innerProduct(rhat, mmp).real

            s @= r - alpha * mmp

            mat(s, mms)
            ip, mms2 = g.innerProductNorm2(mms, s)

            if mms2 == 0.:
                continue

            omega = ip.real / mms2

            psi += alpha * p + omega * s

            r2 = g.axpy_norm2(r, -omega, mms, s)

            if verbose:
                g.message("res^2[ %d ] = %g" % (k, r2))

            if r2 <= rsq:
                if verbose:
                    t1 = time()
                    g.message("Converged in %g s" % (t1 - t0))
                break
Beispiel #4
0
        def inv(psi, src):
            self.history = []
            verbose = g.default.is_verbose("mr")

            t = g.timer("mr")
            t("setup")

            r, mmr = g.copy(src), g.copy(src)

            mat(mmr, psi)
            r @= src - mmr

            r2 = g.norm2(r)
            ssq = g.norm2(src)
            # if ssq == 0.0:
            # assert r2 != 0.0  # need either source or psi to not be zero
            # ssq = r2
            rsq = self.eps ** 2.0 * ssq

            for k in range(self.maxiter):
                t("mat")
                mat(mmr, r)

                t("inner")
                ip, mmr2 = g.inner_product_norm2(mmr, r)
                if mmr2 == 0.0:
                    continue

                t("linearcomb")
                alpha = ip.real / mmr2 * self.relax
                psi += alpha * r

                t("axpy_norm")
                r2 = g.axpy_norm2(r, -alpha, mmr, r)

                t("other")
                self.history.append(r2)

                if verbose:
                    g.message("mr: res^2[ %d ] = %g, target = %g" % (k, r2, rsq))

                if r2 <= rsq:
                    if verbose:
                        t()
                        g.message(
                            "mr: converged in %d iterations, took %g s"
                            % (k + 1, t.dt["total"])
                        )
                        g.message(t)
                    break
Beispiel #5
0
Datei: cg.py Projekt: krox/gpt
 def inv(psi, src):
     assert src != psi
     self.history = []
     verbose = g.default.is_verbose("cg")
     t = g.timer("cg")
     t("setup")
     p, mmp, r = g.copy(src), g.copy(src), g.copy(src)
     mat(mmp, psi)  # in, out
     d = g.inner_product(psi, mmp).real
     b = g.norm2(mmp)
     r @= src - mmp
     p @= r
     a = g.norm2(p)
     cp = a
     ssq = g.norm2(src)
     if ssq == 0.0:
         assert a != 0.0  # need either source or psi to not be zero
         ssq = a
     rsq = self.eps ** 2.0 * ssq
     for k in range(1, self.maxiter + 1):
         c = cp
         t("mat")
         mat(mmp, p)
         t("inner")
         dc = g.inner_product(p, mmp)
         d = dc.real
         a = c / d
         t("axpy_norm")
         cp = g.axpy_norm2(r, -a, mmp, r)
         t("linearcomb")
         b = cp / c
         psi += a * p
         p @= b * p + r
         t("other")
         self.history.append(cp)
         if verbose:
             g.message("cg: res^2[ %d ] = %g, target = %g" % (k, cp, rsq))
         if cp <= rsq:
             if verbose:
                 t()
                 g.message(
                     "cg: converged in %d iterations, took %g s"
                     % (k, t.dt["total"])
                 )
                 g.message(t)
             break
Beispiel #6
0
Datei: mr.py Projekt: wettig/gpt
        def inv(psi, src, t):

            t("setup")

            r, mmr = g.copy(src), g.copy(src)

            mat(mmr, psi)
            r @= src - mmr

            r2 = g.norm2(r)
            ssq = g.norm2(src)
            # if ssq == 0.0:
            # assert r2 != 0.0  # need either source or psi to not be zero
            # ssq = r2
            rsq = self.eps ** 2.0 * ssq

            for k in range(self.maxiter):
                t("mat")
                mat(mmr, r)

                t("inner")
                ip, mmr2 = g.inner_product_norm2(mmr, r)
                if mmr2 == 0.0:
                    continue

                t("linearcomb")
                alpha = ip.real / mmr2 * self.relax
                psi += alpha * r

                t("axpy_norm")
                r2 = g.axpy_norm2(r, -alpha, mmr, r)

                t("other")

                self.log_convergence(k, r2, rsq)

                if r2 <= rsq:
                    self.log(f"converged in {k+1} iterations")
                    return

            self.log(
                f"NOT converged in {k+1} iterations;  squared residual {r2:e} / {rsq:e}"
            )
Beispiel #7
0
Datei: cg.py Projekt: lehner/gpt
        def inv(psi, src, t):
            assert src != psi
            t("setup")
            p, mmp, r = g.copy(src), g.copy(src), g.copy(src)
            mat(mmp, psi)  # in, out
            d = g.inner_product(psi, mmp).real
            b = g.norm2(mmp)
            r @= src - mmp
            p @= r
            a = g.norm2(p)
            cp = a
            ssq = g.norm2(src)
            if ssq == 0.0:
                psi[:] = 0
                return
            rsq = self.eps ** 2.0 * ssq
            for k in range(self.maxiter):
                c = cp
                t("matrix")
                mat(mmp, p)
                t("inner_product")
                dc = g.inner_product(p, mmp)
                d = dc.real
                a = c / d
                t("axpy_norm2")
                cp = g.axpy_norm2(r, -a, mmp, r)
                t("linear combination")
                b = cp / c
                psi += a * p
                p @= b * p + r
                t("other")
                self.log_convergence(k, cp, rsq)
                if cp <= rsq:
                    self.log(f"converged in {k+1} iterations")
                    return

            self.log(
                f"NOT converged in {k+1} iterations;  squared residual {cp:e} / {rsq:e}"
            )
Beispiel #8
0
 def calc_res(self, mat, psi, mmpsi, src, r):
     mat(psi, mmpsi)
     return g.axpy_norm2(r, -1., mmpsi, src)
Beispiel #9
0
        def inv(psi, src):
            self.history = []
            verbose = g.default.is_verbose("bicgstab")

            t = g.timer("bicgstab")
            t("setup")

            r, rhat, p, s = g.copy(src), g.copy(src), g.copy(src), g.copy(src)
            mmpsi, mmp, mms = g.copy(src), g.copy(src), g.copy(src)

            rho, rhoprev, alpha, omega = 1.0, 1.0, 1.0, 1.0

            mat(mmpsi, psi)
            r @= src - mmpsi

            rhat @= r
            p @= r
            mmp @= r

            r2 = g.norm2(r)
            ssq = g.norm2(src)
            if ssq == 0.0:
                assert r2 != 0.0  # need either source or psi to not be zero
                ssq = r2
            rsq = self.eps**2.0 * ssq

            for k in range(self.maxiter):
                t("inner")
                rhoprev = rho
                rho = g.inner_product(rhat, r).real

                t("linearcomb")
                beta = (rho / rhoprev) * (alpha / omega)
                p @= r + beta * p - beta * omega * mmp

                t("mat")
                mat(mmp, p)

                t("inner")
                alpha = rho / g.inner_product(rhat, mmp).real

                t("linearcomb")
                s @= r - alpha * mmp

                t("mat")
                mat(mms, s)

                t("inner")
                ip, mms2 = g.inner_product_norm2(mms, s)
                if mms2 == 0.0:
                    continue

                t("linearcomb")
                omega = ip.real / mms2
                psi += alpha * p + omega * s

                t("axpy_norm")
                r2 = g.axpy_norm2(r, -omega, mms, s)

                t("other")
                self.history.append(r2)

                if verbose:
                    g.message("bicgstab: res^2[ %d ] = %g, target = %g" %
                              (k, r2, rsq))

                if r2 <= rsq:
                    if verbose:
                        t()
                        g.message(
                            "bicgstab: converged in %d iterations, took %g s" %
                            (k + 1, t.dt["total"]))
                        g.message(t)
                    break
Beispiel #10
0
        def inv(psi, src, t):
            assert src != psi
            t("setup")
            p, mmp, r = g.lattice(src), g.lattice(src), g.lattice(src)
            if prec is not None:
                z = g.lattice(src)
            t("matrix")
            mat(mmp, psi)  # in, out
            t("setup")
            g.axpy(r, -1.0, mmp, src)
            if prec is not None:
                z[:] = 0
                prec(z, r)
                g.copy(p, z)
                cp = g.inner_product(r, z).real
            else:
                g.copy(p, r)
                cp = g.norm2(p)
            ssq = g.norm2(src)
            if ssq == 0.0:
                psi[:] = 0
                return
            rsq = self.eps**2.0 * ssq
            for k in range(self.maxiter):
                c = cp
                t("matrix")
                mat(mmp, p)
                t("inner_product")
                d = g.inner_product(p, mmp).real
                a = c / d
                t("axpy_norm2")
                if prec is not None:
                    # c = <r,z>, d = <p,A p>
                    g.axpy(r, -a, mmp, r)
                    t("prec")
                    z[:] = 0
                    prec(z, r)
                    t("axpy_norm2")
                    cp = g.inner_product(r, z).real
                else:
                    cp = g.axpy_norm2(r, -a, mmp, r)
                t("linear combination")
                b = cp / c
                psi += a * p
                if prec is not None:
                    g.axpy(p, b, p, z)
                else:
                    g.axpy(p, b, p, r)
                t("other")
                res = abs(cp)
                self.log_convergence(k, res, rsq)
                if k + 1 >= self.miniter:
                    if self.eps_abs is not None and res <= self.eps_abs**2.0:
                        self.log(
                            f"converged in {k+1} iterations (absolute criterion)"
                        )
                        return
                    if res <= rsq:
                        self.log(f"converged in {k+1} iterations")
                        return

            self.log(
                f"NOT converged in {k+1} iterations;  squared residual {res:e} / {rsq:e}"
            )
Beispiel #11
0
        def inv(psi, src, t):
            t("setup")

            # parameters
            rlen = self.restartlen

            # tensors
            dtype_r, dtype_c = g.double.real_dtype, g.double.complex_dtype
            alpha = np.empty((rlen), dtype_c)
            beta = np.empty((rlen, rlen), dtype_c)
            gamma = np.empty((rlen), dtype_r)
            chi = np.empty((rlen), dtype_c)

            # fields
            r, mmpsi = g.copy(src), g.copy(src)
            p = [g.lattice(src) for i in range(rlen)]
            z = [g.lattice(src) for i in range(rlen)]

            # initial residual
            r2 = self.restart(mat, psi, mmpsi, src, r, p)

            # source
            ssq = g.norm2(src)
            if ssq == 0.0:
                assert r2 != 0.0  # need either source or psi to not be zero
                ssq = r2

            # target residual
            rsq = self.eps ** 2.0 * ssq

            for k in range(self.maxiter):
                # iteration within current krylov space
                i = k % rlen

                # iteration criteria
                need_restart = i + 1 == rlen

                t("prec")
                if prec is not None:
                    prec(p[i], r)
                else:
                    p[i] @= r

                t("mat")
                mat(z[i], p[i])

                t("ortho")
                g.orthogonalize(z[i], z[0:i], beta[:, i])

                t("linalg")
                ip, z2 = g.inner_product_norm2(z[i], r)
                gamma[i] = z2 ** 0.5
                if gamma[i] == 0.0:
                    self.debug(f"breakdown, gamma[{i:d}] = 0")
                    break
                z[i] /= gamma[i]
                alpha[i] = ip / gamma[i]
                r2 = g.axpy_norm2(r, -alpha[i], z[i], r)

                t("other")
                self.log_convergence((k, i), r2, rsq)

                if r2 <= rsq or need_restart:
                    t("update_psi")
                    self.update_psi(psi, alpha, beta, gamma, chi, p, i)

                if r2 <= rsq:
                    msg = f"converged in {k+1} iterations;  computed squared residual {r2:e} / {rsq:e}"
                    if self.checkres:
                        res = self.calc_res(mat, psi, mmpsi, src, r)
                        msg += f";  true squared residual {res:e} / {rsq:e}"
                    self.log(msg)
                    return

                if need_restart:
                    t("restart")
                    r2 = self.restart(mat, psi, mmpsi, src, r, p)
                    self.debug("performed restart")

            msg = f"NOT converged in {k+1} iterations;  computed squared residual {r2:e} / {rsq:e}"
            if self.checkres:
                res = self.calc_res(mat, psi, mmpsi, src, r)
                msg += f";  true squared residual {res:e} / {rsq:e}"
            self.log(msg)
Beispiel #12
0
 def calc_res(self, mat, psi, mmpsi, src, r, t):
     t("res - mat")
     mat(mmpsi, psi)
     t("res - axpy")
     return g.axpy_norm2(r, -1.0, mmpsi, src)
Beispiel #13
0
        def inv(psi, src):
            self.history = []
            # verbosity
            verbose = g.default.is_verbose("fgcr")

            # timing
            t = g.timer("fgcr")
            t("setup")

            # parameters
            rlen = self.restartlen

            # tensors
            dtype_r, dtype_c = g.double.real_dtype, g.double.complex_dtype
            alpha = np.empty((rlen), dtype_c)
            beta = np.empty((rlen, rlen), dtype_c)
            gamma = np.empty((rlen), dtype_r)
            chi = np.empty((rlen), dtype_c)

            # fields
            r, mmpsi = g.copy(src), g.copy(src)
            p = [g.lattice(src) for i in range(rlen)]
            z = [g.lattice(src) for i in range(rlen)]

            # initial residual
            r2 = self.restart(mat, psi, mmpsi, src, r, p)

            # source
            ssq = g.norm2(src)
            if ssq == 0.0:
                assert r2 != 0.0  # need either source or psi to not be zero
                ssq = r2

            # target residual
            rsq = self.eps**2.0 * ssq

            for k in range(self.maxiter):
                # iteration within current krylov space
                i = k % rlen

                # iteration criteria
                reached_maxiter = k + 1 == self.maxiter
                need_restart = i + 1 == rlen

                t("prec")
                if prec is not None:
                    prec(p[i], r)
                else:
                    p[i] @= r

                t("mat")
                mat(z[i], p[i])

                t("ortho")
                g.default.push_verbose("orthogonalize", False)
                g.orthogonalize(z[i], z[0:i], beta[:, i])
                g.default.pop_verbose()

                t("linalg")
                ip, z2 = g.inner_product_norm2(z[i], r)
                gamma[i] = z2**0.5
                if gamma[i] == 0.0:
                    g.message("fgcr: breakdown, gamma[%d] = 0" % (i))
                    break
                z[i] /= gamma[i]
                alpha[i] = ip / gamma[i]
                r2 = g.axpy_norm2(r, -alpha[i], z[i], r)

                t("other")
                self.history.append(r2)

                if verbose:
                    g.message("fgcr: res^2[ %d, %d ] = %g, target = %g" %
                              (k, i, r2, rsq))

                if r2 <= rsq or need_restart or reached_maxiter:
                    t("update_psi")
                    self.update_psi(psi, alpha, beta, gamma, chi, p, i)
                    comp_res = r2 / ssq

                    if r2 <= rsq:
                        if verbose:
                            t()
                            g.message(
                                "fgcr: converged in %d iterations, took %g s" %
                                (k + 1, t.total))
                            g.message(t)
                            if self.checkres:
                                res = self.calc_res(mat, psi, mmpsi, src,
                                                    r) / ssq
                                g.message(
                                    "fgcr: computed res = %g, true res = %g, target = %g"
                                    % (comp_res**0.5, res**0.5, self.eps))
                            else:
                                g.message(
                                    "fgcr: computed res = %g, target = %g" %
                                    (comp_res**0.5, self.eps))
                        break

                    if reached_maxiter:
                        if verbose:
                            t()
                            g.message(
                                "fgcr: did NOT converge in %d iterations, took %g s"
                                % (k + 1, t.dt["total"]))
                            g.message(t)
                            if self.checkres:
                                res = self.calc_res(mat, psi, mmpsi, src,
                                                    r) / ssq
                                g.message(
                                    "fgcr: computed res = %g, true res = %g, target = %g"
                                    % (comp_res**0.5, res**0.5, self.eps))
                            else:
                                g.message(
                                    "fgcr: computed res = %g, target = %g" %
                                    (comp_res**0.5, self.eps))
                        break

                    if need_restart:
                        t("restart")
                        r2 = self.restart(mat, psi, mmpsi, src, r, p)
                        if verbose:
                            g.message("fgcr: performed restart")
Beispiel #14
0
        def inv(psi, src, t):

            t("setup")

            r, rhat, p, s = g.copy(src), g.copy(src), g.copy(src), g.copy(src)
            mmpsi, mmp, mms = g.copy(src), g.copy(src), g.copy(src)

            rho, rhoprev, alpha, omega = 1.0, 1.0, 1.0, 1.0

            mat(mmpsi, psi)
            r @= src - mmpsi

            rhat @= r
            p @= r
            mmp @= r

            r2 = g.norm2(r)
            ssq = g.norm2(src)
            if ssq == 0.0:
                assert r2 != 0.0  # need either source or psi to not be zero
                ssq = r2
            rsq = self.eps**2.0 * ssq

            for k in range(self.maxiter):
                t("inner")
                rhoprev = rho
                rho = g.inner_product(rhat, r).real

                t("linearcomb")
                beta = (rho / rhoprev) * (alpha / omega)
                p @= r + beta * p - beta * omega * mmp

                t("mat")
                mat(mmp, p)

                t("inner")
                alpha = rho / g.inner_product(rhat, mmp).real

                t("linearcomb")
                s @= r - alpha * mmp

                t("mat")
                mat(mms, s)

                t("inner")
                ip, mms2 = g.inner_product_norm2(mms, s)
                if mms2 == 0.0:
                    continue

                t("linearcomb")
                omega = ip.real / mms2
                psi += alpha * p + omega * s

                t("axpy_norm")
                r2 = g.axpy_norm2(r, -omega, mms, s)

                t("other")

                self.log_convergence(k, r2, rsq)

                if r2 <= rsq:
                    self.log(f"converged in {k+1} iterations")
                    return

            self.log(
                f"NOT converged in {k+1} iterations;  squared residual {r2:e} / {rsq:e}"
            )
Beispiel #15
0
    def __call__(self, mat, src, psi, prec=None):
        # verbosity
        verbose = g.default.is_verbose("fgcr")
        checkres = True  # for now

        # total time
        tt0 = time()

        # parameters
        rlen = self.restartlen

        # tensors
        dtype_r, dtype_c = np.float64, np.complex128
        alpha = np.empty((rlen), dtype_c)
        beta = np.empty((rlen, rlen), dtype_c)
        gamma = np.empty((rlen), dtype_r)
        delta = np.empty((rlen), dtype_c)

        # fields
        r, mmr, mmpsi = g.copy(src), g.copy(src), g.copy(src)
        p = [g.lattice(src) for i in range(rlen)]
        mmp = [g.lattice(src) for i in range(rlen)]

        # residual target
        ssq = g.norm2(src)
        rsq = self.eps**2. * ssq

        # initial values
        r2 = self.restart(mat, psi, mmpsi, src, r)

        for k in range(self.maxiter):
            # iteration within current krylov space
            i = k % rlen

            # iteration criteria
            reached_maxiter = k + 1 == self.maxiter
            need_restart = i + 1 == rlen

            t0 = time()
            if not prec is None:
                prec(r, p[i])
            else:
                p[i] @= r
            t1 = time()

            t2 = time()
            mat(p[i], mmp[i])
            t3 = time()

            t4 = time()
            g.orthogonalize(mmp[i], mmp[0:i], beta[:, i])
            t5 = time()

            t6 = time()
            ip, mmp2 = g.innerProductNorm2(mmp[i], r)
            gamma[i] = mmp2**0.5

            if gamma[i] == 0.:
                g.message("fgcr breakdown, gamma[%d] = 0" % (i))
                break

            mmp[i] /= gamma[i]
            alpha[i] = ip / gamma[i]
            r2 = g.axpy_norm2(r, -alpha[i], mmp[i], r)
            t7 = time()

            if verbose:
                g.message(
                    "Timing[s]: Prec = %g, Matrix = %g, Orthog = %g, Rest = %g"
                    % (t1 - t0, t3 - t2, t5 - t4, t7 - t6))
                g.message("res^2[ %d, %d ] = %g" % (k, i, r2))

            if r2 <= rsq or need_restart or reached_maxiter:
                self.update_psi(psi, alpha, beta, gamma, delta, p, i)

                if r2 <= rsq:
                    if verbose:
                        tt1 = time()
                        g.message("Converged in %g s" % (tt1 - tt0))
                        if checkres:
                            res = self.calc_res(mat, psi, mmpsi, src, r) / ssq
                            g.message(
                                "Computed res = %g, true res = %g, target = %g"
                                % (r2**0.5, res**0.5, self.eps))
                    break

                if reached_maxiter:
                    if verbose:
                        tt1 = time()
                        g.message("Did NOT converge in %g s" % (tt1 - tt0))
                        if checkres:
                            res = self.calc_res(mat, psi, mmpsi, src, r) / ssq
                            g.message(
                                "Computed res = %g, true res = %g, target = %g"
                                % (r2**0.5, res**0.5, self.eps))

                if need_restart:
                    r2 = self.restart(mat, psi, mmpsi, src, r)
                    if verbose:
                        g.message("Performed restart")
Beispiel #16
0
        def inv(psi, src, t):

            if len(src) > 1:
                n = len(src)
                # do different sources separately
                for idx in range(n):
                    inv(psi[idx::n], [src[idx]])
                return

            src = src[0]
            scgs = []
            for j, s in enumerate(self.shifts):
                scgs += [shifted_cg(psi[j], src, s)]

            t("setup")
            p, mmp, r = g.copy(src), g.copy(src), g.copy(src)
            x = g.copy(src)
            x[:] = 0

            b = 0.0
            a = g.norm2(p)
            cp = a
            assert a != 0.0  # need either source or psi to not be zero
            rsq = self.eps**2.0 * a
            for k in range(self.maxiter):
                c = cp
                t("matrix")
                mat(mmp, p)

                t("inner_product")
                dc = g.inner_product(p, mmp)
                d = dc.real
                om = b / a
                a = c / d
                om *= a

                t("axpy_norm2")
                cp = g.axpy_norm2(r, -a, mmp, r)

                t("linear combination")
                b = cp / c
                for cg in scgs:
                    if not cg.converged:
                        cg.step1(a, b, om)

                x += a * p
                p @= b * p + r
                for cg in scgs:
                    if not cg.converged:
                        cg.step2(r)

                t("other")
                for cg in scgs:
                    msg = cg.check(cp, rsq)
                    if msg:
                        self.log(f"{msg} at iteration {k+1}")
                if sum([cg.converged for cg in scgs]) == ns:
                    return

            self.log(
                f"NOT converged in {k+1} iterations;  squared residual {cp:e} / {rsq:e}"
            )